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:
ZacharyZhang-NY committed 2026-09-25 13:03:23 -04:00
1 parent a923def708
commit f990586abd
20 files changed
+1695 -52

No files matched your search

+90
View File
@@ -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);
}