From 2ce336895c3452671c4cfa29c8411991697e3119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Tue, 6 Oct 2026 09:17:56 -0400 Subject: [PATCH] =?UTF-8?q?Menus=20=C2=B7=20ContextMenu:=20styles=20size?= =?UTF-8?q?=20its=20host,=20so=20a=20list=20inside=20can=20fill=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/menus/hosts.rs | 16 ++++++++++++---- src/menus/tests/more.rs | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/menus/hosts.rs b/src/menus/hosts.rs index 4534f5c..0fd16d0 100644 --- a/src/menus/hosts.rs +++ b/src/menus/hosts.rs @@ -2,8 +2,8 @@ use std::rc::Rc; use gpui::{ AnyElement, App, ClickEvent, Div, ElementId, Entity, InteractiveElement, IntoElement, - MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString, Stateful, Styled, Window, - div, prelude::*, + MouseButton, ParentElement, Pixels, Point, RenderOnce, SharedString, Stateful, StyleRefinement, + Styled, Window, div, prelude::*, }; use smallvec::SmallVec; @@ -221,13 +221,14 @@ impl RenderOnce for SplitButton { } } -/// Its children, and a menu that opens at the pointer on a right click. +/// Its children, and a menu that opens at the pointer on a right click. Styles size the host, so a list inside can fill it. #[derive(IntoElement)] pub struct ContextMenu { id: ElementId, menu: Menu, request: Option)>>, children: SmallVec<[AnyElement; 2]>, + base: Div, } impl ContextMenu { @@ -237,6 +238,7 @@ impl ContextMenu { menu, request: None, children: SmallVec::new(), + base: div(), } } @@ -253,6 +255,12 @@ impl ParentElement for ContextMenu { } } +impl Styled for ContextMenu { + fn style(&mut self) -> &mut StyleRefinement { + self.base.style() + } +} + impl RenderOnce for ContextMenu { fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { let state = state(&self.id, window, cx); @@ -265,7 +273,7 @@ impl RenderOnce for ContextMenu { } } let (open, shut, menu) = (state.clone(), state.clone(), self.menu.clone()); - div() + self.base .id(self.id.clone()) .relative() .when(self.request.is_none(), |host| { diff --git a/src/menus/tests/more.rs b/src/menus/tests/more.rs index b01b8e4..8fbc129 100644 --- a/src/menus/tests/more.rs +++ b/src/menus/tests/more.rs @@ -246,3 +246,36 @@ fn pie_confirm_keys_stay_inside_and_ignore_modifiers(cx: &mut TestAppContext) { assert_eq!(view.read_with(cx, |wheel, _| wheel.ran.clone()), ["copy"]); assert_eq!(view.read_with(cx, |wheel, _| wheel.enters_above), 0); } + +/// A sized context menu over a child that fills it. +struct Filled { + seen: std::rc::Rc>, +} + +impl Render for Filled { + fn render(&mut self, _: &mut Window, _: &mut Context) -> impl IntoElement { + let seen = self.seen.clone(); + let fill = gpui::canvas( + move |bounds, _, _| seen.set(f32::from(bounds.size.height)), + |_, _, _, _| {}, + ) + .size_full(); + div().h(px(200.)).w(px(100.)).child( + crate::menus::ContextMenu::new("filled", crate::menus::Menu::new()) + .size_full() + .child(fill), + ) + } +} + +#[gpui::test] +fn a_sized_context_menu_lets_its_child_fill_it(cx: &mut TestAppContext) { + cx.update(Theme::init); + let seen = std::rc::Rc::new(std::cell::Cell::new(0.0)); + let (_, cx) = cx.add_window_view({ + let seen = seen.clone(); + move |_, _| Filled { seen } + }); + settle(cx); + assert_eq!(seen.get(), 200.0); +}