Add some necessary context menu (submenu) improvements

This commit is contained in:
Danilo Leal
2025-12-30 18:38:17 -03:00
parent 963cb2c200
commit 066e27aa20

View File

@@ -27,7 +27,6 @@ struct OpenSubmenu {
item_index: usize,
entity: Entity<ContextMenu>,
trigger_bounds: Option<Bounds<Pixels>>,
// Capture the submenu's vertical offset once and keep it stable while the submenu is open.
offset: Option<Pixels>,
_dismiss_subscription: Subscription,
}
@@ -61,6 +60,7 @@ pub enum ContextMenuItem {
Submenu {
label: SharedString,
icon: Option<IconName>,
icon_color: Option<Color>,
builder: Rc<dyn Fn(ContextMenu, &mut Window, &mut Context<ContextMenu>) -> ContextMenu>,
},
}
@@ -763,6 +763,7 @@ impl ContextMenu {
self.items.push(ContextMenuItem::Submenu {
label: label.into(),
icon: None,
icon_color: None,
builder: Rc::new(builder),
});
self
@@ -777,6 +778,23 @@ impl ContextMenu {
self.items.push(ContextMenuItem::Submenu {
label: label.into(),
icon: Some(icon),
icon_color: None,
builder: Rc::new(builder),
});
self
}
pub fn submenu_with_colored_icon(
mut self,
label: impl Into<SharedString>,
icon: IconName,
icon_color: Color,
builder: impl Fn(ContextMenu, &mut Window, &mut Context<ContextMenu>) -> ContextMenu + 'static,
) -> Self {
self.items.push(ContextMenuItem::Submenu {
label: label.into(),
icon: Some(icon),
icon_color: Some(icon_color),
builder: Rc::new(builder),
});
self
@@ -1319,8 +1337,13 @@ impl ContextMenu {
)
.into_any_element()
}
ContextMenuItem::Submenu { label, icon, .. } => self
.render_submenu_item_trigger(ix, label.clone(), *icon, cx)
ContextMenuItem::Submenu {
label,
icon,
icon_color,
..
} => self
.render_submenu_item_trigger(ix, label.clone(), *icon, *icon_color, cx)
.into_any_element(),
}
}
@@ -1330,6 +1353,7 @@ impl ContextMenu {
ix: usize,
label: SharedString,
icon: Option<IconName>,
icon_color: Option<Color>,
cx: &mut Context<Self>,
) -> impl IntoElement {
let toggle_state = Some(ix) == self.selected_index
@@ -1413,9 +1437,17 @@ impl ContextMenu {
SubmenuState::Open(open_submenu) if open_submenu.item_index == ix
);
if is_open_for_this_item && this.hover_target != HoverTarget::Submenu {
let mouse_in_submenu_zone = this
.padded_submenu_bounds()
.is_some_and(|bounds| bounds.contains(&window.mouse_position()));
if is_open_for_this_item
&& this.hover_target != HoverTarget::Submenu
&& !mouse_in_submenu_zone
{
this.close_submenu(false, cx);
this.clear_selected();
window.focus(&this.focus_handle.clone(), cx);
cx.notify();
}
}
@@ -1441,6 +1473,7 @@ impl ContextMenu {
.child(
h_flex()
.w_full()
.gap_2()
.justify_between()
.child(
h_flex()
@@ -1449,7 +1482,7 @@ impl ContextMenu {
this.child(
Icon::new(icon_name)
.size(IconSize::Small)
.color(Color::Default),
.color(icon_color.unwrap_or(Color::Muted)),
)
})
.child(Label::new(label).color(Color::Default)),
@@ -1973,9 +2006,14 @@ impl Render for ContextMenu {
.on_action(cx.listener(ContextMenu::select_submenu_parent))
.on_action(cx.listener(ContextMenu::confirm))
.on_action(cx.listener(ContextMenu::cancel))
.on_hover(cx.listener(|this, hovered: &bool, _, _| {
.on_hover(cx.listener(|this, hovered: &bool, _, cx| {
if *hovered {
this.hover_target = HoverTarget::MainMenu;
if let Some(parent) = &this.main_menu {
parent.update(cx, |parent, _| {
parent.hover_target = HoverTarget::Submenu;
});
}
}
}))
.on_mouse_down_out(cx.listener(