T08a menus: menu, items, groups, separators, submenus, check and radio rows, key hints, icons; dropdown, split, overflow and context hosts; overlays pick on key release
This commit is contained in:
20 files changed
+1695
-52
No files matched your search
@@ -8,6 +8,8 @@ mod native;
|
||||
mod status;
|
||||
mod switcher;
|
||||
mod tabs;
|
||||
#[cfg(all(test, feature = "test-support"))]
|
||||
mod tests;
|
||||
mod update;
|
||||
mod windows;
|
||||
|
||||
|
||||
+13
-7
@@ -95,8 +95,9 @@ impl RenderOnce for WindowSwitcher {
|
||||
})
|
||||
.collect();
|
||||
let handles: Vec<AnyWindowHandle> = windows.iter().map(|(handle, _)| *handle).collect();
|
||||
let (next, prev, keys) = (state.clone(), state.clone(), state.clone());
|
||||
let (close_keys, close_scrim) = (close.clone(), close);
|
||||
let (next, prev, keys, chosen) =
|
||||
(state.clone(), state.clone(), state.clone(), state.clone());
|
||||
let (close_keys, close_enter, close_scrim) = (close.clone(), close.clone(), close);
|
||||
let focus = takeover.read(cx).focus.clone();
|
||||
let panel = div()
|
||||
.id("window-switcher")
|
||||
@@ -117,16 +118,21 @@ impl RenderOnce for WindowSwitcher {
|
||||
match event.keystroke.key.as_str() {
|
||||
"down" => step(&keys, 1, count, cx),
|
||||
"up" => step(&keys, -1, count, cx),
|
||||
"enter" => {
|
||||
if let Some(handle) = handles.get(keys.read(cx).selected) {
|
||||
switch_to(*handle, &close_keys, window, cx);
|
||||
}
|
||||
}
|
||||
"escape" => close_keys(window, cx),
|
||||
"enter" if !event.keystroke.modifiers.modified() => {}
|
||||
_ => return,
|
||||
}
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_key_up(move |event, window, cx| {
|
||||
let stroke = &event.keystroke;
|
||||
if stroke.key == "enter" && !stroke.modifiers.modified() {
|
||||
cx.stop_propagation();
|
||||
if let Some(handle) = handles.get(chosen.read(cx).selected) {
|
||||
switch_to(*handle, &close_enter, window, cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
.children(rows)
|
||||
.when(count == 0, |panel| {
|
||||
panel.child(
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
use gpui::{
|
||||
AppContext as _, Context, FocusHandle, InteractiveElement, IntoElement, KeyUpEvent, Keystroke,
|
||||
ParentElement, Render, TestAppContext, VisualTestContext, Window, div, prelude::*, px, size,
|
||||
};
|
||||
|
||||
use super::{WindowManager, WindowSwitcher};
|
||||
use crate::{buttons::Button, primitives::FocusScope, theme::Theme};
|
||||
|
||||
struct Blank;
|
||||
|
||||
impl Render for Blank {
|
||||
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||
div()
|
||||
}
|
||||
}
|
||||
|
||||
struct Desk {
|
||||
root: FocusHandle,
|
||||
open: bool,
|
||||
enters_above: usize,
|
||||
}
|
||||
|
||||
impl Render for Desk {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let (opener, closer, above) = (cx.entity(), cx.entity(), cx.entity());
|
||||
let scope = FocusScope::new(&self.root)
|
||||
.size_full()
|
||||
.child(Button::new("opener", "Windows").on_click(move |_, _, cx| {
|
||||
opener.update(cx, |desk, cx| {
|
||||
desk.open = true;
|
||||
cx.notify();
|
||||
})
|
||||
}))
|
||||
.when(self.open, |desk| {
|
||||
desk.child(WindowSwitcher::new("switcher", move |_, cx| {
|
||||
closer.update(cx, |desk, cx| {
|
||||
desk.open = false;
|
||||
cx.notify();
|
||||
})
|
||||
}))
|
||||
});
|
||||
div()
|
||||
.size_full()
|
||||
.on_key_down(move |event, _, cx| {
|
||||
if event.keystroke.key == "enter" {
|
||||
above.update(cx, |desk, _| desk.enters_above += 1);
|
||||
}
|
||||
})
|
||||
.child(scope)
|
||||
}
|
||||
}
|
||||
|
||||
fn settle(cx: &mut VisualTestContext) {
|
||||
cx.run_until_parked();
|
||||
cx.update(|window, _| window.refresh());
|
||||
cx.run_until_parked();
|
||||
}
|
||||
|
||||
fn press(key: &str, cx: &mut VisualTestContext) {
|
||||
cx.simulate_keystrokes(key);
|
||||
settle(cx);
|
||||
cx.simulate_event(KeyUpEvent {
|
||||
keystroke: Keystroke::parse(key).expect("a key"),
|
||||
});
|
||||
settle(cx);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn the_switcher_switches_on_release_so_its_opener_stays_shut(cx: &mut TestAppContext) {
|
||||
cx.update(Theme::init);
|
||||
let (desk, cx) = cx.add_window_view(|_, cx| Desk {
|
||||
root: cx.focus_handle(),
|
||||
open: false,
|
||||
enters_above: 0,
|
||||
});
|
||||
cx.update(|_, cx| {
|
||||
WindowManager::open("Notes", size(px(320.0), px(240.0)), cx, |_, cx| {
|
||||
cx.new(|_| Blank)
|
||||
})
|
||||
.expect("a managed window");
|
||||
});
|
||||
settle(cx);
|
||||
cx.update(|window, _| window.focus_next());
|
||||
press("enter", cx);
|
||||
assert!(desk.read_with(cx, |desk, _| desk.open));
|
||||
let above = desk.read_with(cx, |desk, _| desk.enters_above);
|
||||
press("enter", cx);
|
||||
assert!(!desk.read_with(cx, |desk, _| desk.open));
|
||||
assert_eq!(desk.read_with(cx, |desk, _| desk.enters_above), above);
|
||||
}
|
||||
Reference in new issue
Block a user