menu: Add menu_element_with_icon, menu_element_with_check to PopupMenu. (#704)

- Added `menu_element_with_icon` and `menu_element_with_check` method to
PopupMenu.

<img width="317" alt="image"
src="https://github.com/user-attachments/assets/d32575e3-25fc-4d6f-bde2-ec5bc5f238dd"
/>


## Break changes

- menu: Renamed `menu_with_element` method to `menu_element`.
This commit is contained in:
Jason Lee 2025-03-11 11:18:43 +08:00 committed by GitHub
parent 6a00cfcc4a
commit d451ce5c2d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 67 additions and 14 deletions

View file

@ -262,17 +262,30 @@ impl Render for PopupStory {
.separator() .separator()
.menu_with_icon("Search", IconName::Search, Box::new(SearchAll)) .menu_with_icon("Search", IconName::Search, Box::new(SearchAll))
.separator() .separator()
.menu_with_element( .menu_element(Box::new(Info(0)), |_, cx| {
|_, cx| { v_flex().child("Custom Element").child(
v_flex().gap_1().child("Custom Element").child( div()
div() .text_xs()
.text_sm() .text_color(cx.theme().muted_foreground)
.text_color(cx.theme().muted_foreground) .child("THis is sub-title"),
.child("THis is sub-title"), )
) })
}, .menu_element_with_check(checked, Box::new(Info(0)), |_, cx| {
Box::new(Info(0)), h_flex().gap_1().child("Custom Element").child(
) div()
.text_xs()
.text_color(cx.theme().muted_foreground)
.child("checked"),
)
})
.menu_element_with_icon(IconName::Info, Box::new(Info(0)), |_, cx| {
h_flex().gap_1().child("Custom").child(
div()
.text_sm()
.text_color(cx.theme().muted_foreground)
.child("element"),
)
})
.separator() .separator()
.submenu("Links", window, cx, |menu, _, _| { .submenu("Links", window, cx, |menu, _, _| {
menu.link_with_icon( menu.link_with_icon(

View file

@ -68,6 +68,7 @@ enum PopupMenuItem {
handler: Rc<dyn Fn(&mut Window, &mut App)>, handler: Rc<dyn Fn(&mut Window, &mut App)>,
}, },
ElementItem { ElementItem {
icon: Option<Icon>,
render: Box<dyn Fn(&mut Window, &mut App) -> AnyElement + 'static>, render: Box<dyn Fn(&mut Window, &mut App) -> AnyElement + 'static>,
handler: Rc<dyn Fn(&mut Window, &mut App)>, handler: Rc<dyn Fn(&mut Window, &mut App)>,
}, },
@ -238,7 +239,16 @@ impl PopupMenu {
} }
/// Add Menu Item with custom element render. /// Add Menu Item with custom element render.
pub fn menu_with_element<F, E>(mut self, builder: F, action: Box<dyn Action>) -> Self pub fn menu_element<F, E>(self, action: Box<dyn Action>, builder: F) -> Self
where
F: Fn(&mut Window, &mut App) -> E + 'static,
E: IntoElement,
{
self.menu_element_with_check(false, action, builder)
}
/// Add Menu Item with custom element render with icon.
pub fn menu_element_with_icon<F, E>(mut self, icon: impl Into<Icon>, action: Box<dyn Action>, builder: F) -> Self
where where
F: Fn(&mut Window, &mut App) -> E + 'static, F: Fn(&mut Window, &mut App) -> E + 'static,
E: IntoElement, E: IntoElement,
@ -246,7 +256,37 @@ impl PopupMenu {
self.menu_items.push(PopupMenuItem::ElementItem { self.menu_items.push(PopupMenuItem::ElementItem {
render: Box::new(move |window, cx| builder(window, cx).into_any_element()), render: Box::new(move |window, cx| builder(window, cx).into_any_element()),
handler: self.wrap_handler(action), handler: self.wrap_handler(action),
icon: Some(icon.into())
}); });
self.has_icon = true;
self
}
/// Add Menu Item with custom element render with check state
pub fn menu_element_with_check<F, E>(
mut self,
checked: bool,
action: Box<dyn Action>,
builder: F,
) -> Self
where
F: Fn(&mut Window, &mut App) -> E + 'static,
E: IntoElement,
{
if checked {
self.menu_items.push(PopupMenuItem::ElementItem {
render: Box::new(move |window, cx| builder(window, cx).into_any_element()),
handler: self.wrap_handler(action),
icon: Some(IconName::Check.into())
});
self.has_icon = true;
} else {
self.menu_items.push(PopupMenuItem::ElementItem {
render: Box::new(move |window, cx| builder(window, cx).into_any_element()),
handler: self.wrap_handler(action),
icon: None,
});
}
self self
} }
@ -515,14 +555,14 @@ impl PopupMenu {
.my_0p5() .my_0p5()
.bg(cx.theme().muted), .bg(cx.theme().muted),
), ),
PopupMenuItem::ElementItem { render, .. } => this PopupMenuItem::ElementItem { render, icon, .. } => this
.on_click(cx.listener(move |this, _, window, cx| this.on_click(ix, window, cx))) .on_click(cx.listener(move |this, _, window, cx| this.on_click(ix, window, cx)))
.child( .child(
h_flex() h_flex()
.min_h(ITEM_HEIGHT) .min_h(ITEM_HEIGHT)
.items_center() .items_center()
.gap_x_1() .gap_x_1()
.children(Self::render_icon(has_icon, None, window, cx)) .children(Self::render_icon(has_icon, icon.clone(), window, cx))
.child((render)(window, cx)), .child((render)(window, cx)),
), ),
PopupMenuItem::Item { PopupMenuItem::Item {