use crate::{ Icon, IconButtonShape, IconName, IconSize, KeyBinding, Label, List, ListItem, ListSeparator, ListSubHeader, h_flex, prelude::*, utils::WithRemSize, v_flex,};use gpui::{ Action, AnyElement, App, AppContext as _, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, Render, Subscription, px,};use menu::{SelectFirst, SelectLast, SelectNext, SelectPrevious};use settings::Settings;use std::{rc::Rc, time::Duration};use theme::ThemeSettings; use super::Tooltip; pub enum ContextMenuItem { Separator, Header(SharedString), /// title, link_label, link_url HeaderWithLink(SharedString, SharedString, SharedString), // This could be folded into header Label(SharedString), Entry(ContextMenuEntry), CustomEntry { entry_render: Box<dyn Fn(&mut Window, &mut App) -> AnyElement>, handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>, selectable: bool, documentation_aside: Option<DocumentationAside>, },} impl ContextMenuItem { pub fn custom_entry( entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static, handler: impl Fn(&mut Window, &mut App) + 'static, documentation_aside: Option<DocumentationAside>, ) -> Self { Self::CustomEntry { entry_render: Box::new(entry_render), handler: Rc::new(move |_, window, cx| handler(window, cx)), selectable: true, documentation_aside, } }} pub struct ContextMenuEntry { toggle: Option<(IconPosition, bool)>, label: SharedString, icon: Option<IconName>, custom_icon_path: Option<SharedString>, icon_position: IconPosition, icon_size: IconSize, icon_color: Option<Color>, handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>, action: Option<Box<dyn Action>>, disabled: bool, documentation_aside: Option<DocumentationAside>, end_slot_icon: Option<IconName>, end_slot_title: Option<SharedString>, end_slot_handler: Option<Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>>, show_end_slot_on_hover: bool,} impl ContextMenuEntry { pub fn new(label: impl Into<SharedString>) -> Self { ContextMenuEntry { toggle: None, label: label.into(), icon: None, custom_icon_path: None, icon_position: IconPosition::Start, icon_size: IconSize::Small, icon_color: None, handler: Rc::new(|_, _, _| {}), action: None, disabled: false, documentation_aside: None, end_slot_icon: None, end_slot_title: None, end_slot_handler: None, show_end_slot_on_hover: false, } } pub fn toggleable(mut self, toggle_position: IconPosition, toggled: bool) -> Self { self.toggle = Some((toggle_position, toggled)); self } pub fn icon(mut self, icon: IconName) -> Self { self.icon = Some(icon); self } pub fn custom_icon_path(mut self, path: impl Into<SharedString>) -> Self { self.custom_icon_path = Some(path.into()); self.icon = None; // Clear IconName if custom path is set self } pub fn icon_position(mut self, position: IconPosition) -> Self { self.icon_position = position; self } pub fn icon_size(mut self, icon_size: IconSize) -> Self { self.icon_size = icon_size; self } pub fn icon_color(mut self, icon_color: Color) -> Self { self.icon_color = Some(icon_color); self } pub fn toggle(mut self, toggle_position: IconPosition, toggled: bool) -> Self { self.toggle = Some((toggle_position, toggled)); self } pub fn action(mut self, action: Box<dyn Action>) -> Self { self.action = Some(action); self } pub fn handler(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self { self.handler = Rc::new(move |_, window, cx| handler(window, cx)); self } pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } pub fn documentation_aside( mut self, side: DocumentationSide, edge: DocumentationEdge, render: impl Fn(&mut App) -> AnyElement + 'static, ) -> Self { self.documentation_aside = Some(DocumentationAside { side, edge, render: Rc::new(render), }); self }} impl FluentBuilder for ContextMenuEntry {} impl From<ContextMenuEntry> for ContextMenuItem { fn from(entry: ContextMenuEntry) -> Self { ContextMenuItem::Entry(entry) }} pub struct ContextMenu { builder: Option<Rc<dyn Fn(Self, &mut Window, &mut Context<Self>) -> Self>>, items: Vec<ContextMenuItem>, focus_handle: FocusHandle, action_context: Option<FocusHandle>, selected_index: Option<usize>, delayed: bool, clicked: bool, end_slot_action: Option<Box<dyn Action>>, key_context: SharedString, _on_blur_subscription: Subscription, keep_open_on_confirm: bool, documentation_aside: Option<(usize, DocumentationAside)>, fixed_width: Option<DefiniteLength>,} #[derive(Copy, Clone, PartialEq, Eq)]pub enum DocumentationSide { Left, Right,} #[derive(Copy, Default, Clone, PartialEq, Eq)]pub enum DocumentationEdge { #[default] Top, Bottom,} #[derive(Clone)]pub struct DocumentationAside { pub side: DocumentationSide, pub edge: DocumentationEdge, pub render: Rc<dyn Fn(&mut App) -> AnyElement>,} impl DocumentationAside { pub fn new( side: DocumentationSide, edge: DocumentationEdge, render: Rc<dyn Fn(&mut App) -> AnyElement>, ) -> Self { Self { side, edge, render } }} impl Focusable for ContextMenu { fn focus_handle(&self, _cx: &App) -> FocusHandle { self.focus_handle.clone() }} impl EventEmitter<DismissEvent> for ContextMenu {} impl FluentBuilder for ContextMenu {} impl ContextMenu { pub fn new( window: &mut Window, cx: &mut Context<Self>, f: impl FnOnce(Self, &mut Window, &mut Context<Self>) -> Self, ) -> Self { let focus_handle = cx.focus_handle(); let _on_blur_subscription = cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ); window.refresh(); f( Self { builder: None, items: Default::default(), focus_handle, action_context: None, selected_index: None, delayed: false, clicked: false, key_context: "menu".into(), _on_blur_subscription, keep_open_on_confirm: false, documentation_aside: None, fixed_width: None, end_slot_action: None, }, window, cx, ) } pub fn build( window: &mut Window, cx: &mut App, f: impl FnOnce(Self, &mut Window, &mut Context<Self>) -> Self, ) -> Entity<Self> { cx.new(|cx| Self::new(window, cx, f)) } /// Builds a [`ContextMenu`] that will stay open when making changes instead of closing after each confirmation. /// /// The main difference from [`ContextMenu::build`] is the type of the `builder`, as we need to be able to hold onto /// it to call it again. pub fn build_persistent( window: &mut Window, cx: &mut App, builder: impl Fn(Self, &mut Window, &mut Context<Self>) -> Self + 'static, ) -> Entity<Self> { cx.new(|cx| { let builder = Rc::new(builder); let focus_handle = cx.focus_handle(); let _on_blur_subscription = cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ); window.refresh(); (builder.clone())( Self { builder: Some(builder), items: Default::default(), focus_handle, action_context: None, selected_index: None, delayed: false, clicked: false, key_context: "menu".into(), _on_blur_subscription, keep_open_on_confirm: true, documentation_aside: None, fixed_width: None, end_slot_action: None, }, window, cx, ) }) } /// Rebuilds the menu. /// /// This is used to refresh the menu entries when entries are toggled when the menu is configured with /// `keep_open_on_confirm = true`. /// /// This only works if the [`ContextMenu`] was constructed using [`ContextMenu::build_persistent`]. Otherwise it is /// a no-op. pub fn rebuild(&mut self, window: &mut Window, cx: &mut Context<Self>) { let Some(builder) = self.builder.clone() else { return; }; // The way we rebuild the menu is a bit of a hack. let focus_handle = cx.focus_handle(); let new_menu = (builder.clone())( Self { builder: Some(builder), items: Default::default(), focus_handle: focus_handle.clone(), action_context: None, selected_index: None, delayed: false, clicked: false, key_context: "menu".into(), _on_blur_subscription: cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ), keep_open_on_confirm: false, documentation_aside: None, fixed_width: None, end_slot_action: None, }, window, cx, ); self.items = new_menu.items; cx.notify(); } pub fn context(mut self, focus: FocusHandle) -> Self { self.action_context = Some(focus); self } pub fn header(mut self, title: impl Into<SharedString>) -> Self { self.items.push(ContextMenuItem::Header(title.into())); self } pub fn header_with_link( mut self, title: impl Into<SharedString>, link_label: impl Into<SharedString>, link_url: impl Into<SharedString>, ) -> Self { self.items.push(ContextMenuItem::HeaderWithLink( title.into(), link_label.into(), link_url.into(), )); self } pub fn separator(mut self) -> Self { self.items.push(ContextMenuItem::Separator); self } pub fn extend<I: Into<ContextMenuItem>>(mut self, items: impl IntoIterator<Item = I>) -> Self { self.items.extend(items.into_iter().map(Into::into)); self } pub fn item(mut self, item: impl Into<ContextMenuItem>) -> Self { self.items.push(item.into()); self } pub fn push_item(&mut self, item: impl Into<ContextMenuItem>) { self.items.push(item.into()); } pub fn entry( mut self, label: impl Into<SharedString>, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, custom_icon_path: None, icon_position: IconPosition::End, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, end_slot_icon: None, end_slot_title: None, end_slot_handler: None, show_end_slot_on_hover: false, })); self } pub fn entry_with_end_slot( mut self, label: impl Into<SharedString>, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, end_slot_icon: IconName, end_slot_title: SharedString, end_slot_handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, custom_icon_path: None, icon_position: IconPosition::End, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, end_slot_icon: Some(end_slot_icon), end_slot_title: Some(end_slot_title), end_slot_handler: Some(Rc::new(move |_, window, cx| end_slot_handler(window, cx))), show_end_slot_on_hover: false, })); self } pub fn entry_with_end_slot_on_hover( mut self, label: impl Into<SharedString>, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, end_slot_icon: IconName, end_slot_title: SharedString, end_slot_handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, custom_icon_path: None, icon_position: IconPosition::End, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, end_slot_icon: Some(end_slot_icon), end_slot_title: Some(end_slot_title), end_slot_handler: Some(Rc::new(move |_, window, cx| end_slot_handler(window, cx))), show_end_slot_on_hover: true, })); self } pub fn toggleable_entry( mut self, label: impl Into<SharedString>, toggled: bool, position: IconPosition, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: Some((position, toggled)), label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, custom_icon_path: None, icon_position: position, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, end_slot_icon: None, end_slot_title: None, end_slot_handler: None, show_end_slot_on_hover: false, })); self } pub fn custom_row( mut self,Lines 1–500 of 1339. Load more
use crate::{ Icon, IconButtonShape, IconName, IconSize, KeyBinding, Label, List, ListItem, ListSeparator, ListSubHeader, h_flex, prelude::*, utils::WithRemSize, v_flex, }; use gpui::{ Action, AnyElement, App, AppContext as _, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, IntoElement, Render, Subscription, px, }; use menu::{SelectFirst, SelectLast, SelectNext, SelectPrevious}; use settings::Settings; use std::{rc::Rc, time::Duration}; use theme::ThemeSettings; use super::Tooltip; pub enum ContextMenuItem { Separator, Header(SharedString), /// title, link_label, link_url HeaderWithLink(SharedString, SharedString, SharedString), // This could be folded into header Label(SharedString), Entry(ContextMenuEntry), CustomEntry { entry_render: Box<dyn Fn(&mut Window, &mut App) -> AnyElement>, handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>, selectable: bool, documentation_aside: Option<DocumentationAside>, }, } impl ContextMenuItem { pub fn custom_entry( entry_render: impl Fn(&mut Window, &mut App) -> AnyElement + 'static, handler: impl Fn(&mut Window, &mut App) + 'static, documentation_aside: Option<DocumentationAside>, ) -> Self { Self::CustomEntry { entry_render: Box::new(entry_render), handler: Rc::new(move |_, window, cx| handler(window, cx)), selectable: true, documentation_aside, } } } pub struct ContextMenuEntry { toggle: Option<(IconPosition, bool)>, label: SharedString, icon: Option<IconName>, custom_icon_path: Option<SharedString>, icon_position: IconPosition, icon_size: IconSize, icon_color: Option<Color>, handler: Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>, action: Option<Box<dyn Action>>, disabled: bool, documentation_aside: Option<DocumentationAside>, end_slot_icon: Option<IconName>, end_slot_title: Option<SharedString>, end_slot_handler: Option<Rc<dyn Fn(Option<&FocusHandle>, &mut Window, &mut App)>>, show_end_slot_on_hover: bool, } impl ContextMenuEntry { pub fn new(label: impl Into<SharedString>) -> Self { ContextMenuEntry { toggle: None, label: label.into(), icon: None, custom_icon_path: None, icon_position: IconPosition::Start, icon_size: IconSize::Small, icon_color: None, handler: Rc::new(|_, _, _| {}), action: None, disabled: false, documentation_aside: None, end_slot_icon: None, end_slot_title: None, end_slot_handler: None, show_end_slot_on_hover: false, } } pub fn toggleable(mut self, toggle_position: IconPosition, toggled: bool) -> Self { self.toggle = Some((toggle_position, toggled)); self } pub fn icon(mut self, icon: IconName) -> Self { self.icon = Some(icon); self } pub fn custom_icon_path(mut self, path: impl Into<SharedString>) -> Self { self.custom_icon_path = Some(path.into()); self.icon = None; // Clear IconName if custom path is set self } pub fn icon_position(mut self, position: IconPosition) -> Self { self.icon_position = position; self } pub fn icon_size(mut self, icon_size: IconSize) -> Self { self.icon_size = icon_size; self } pub fn icon_color(mut self, icon_color: Color) -> Self { self.icon_color = Some(icon_color); self } pub fn toggle(mut self, toggle_position: IconPosition, toggled: bool) -> Self { self.toggle = Some((toggle_position, toggled)); self } pub fn action(mut self, action: Box<dyn Action>) -> Self { self.action = Some(action); self } pub fn handler(mut self, handler: impl Fn(&mut Window, &mut App) + 'static) -> Self { self.handler = Rc::new(move |_, window, cx| handler(window, cx)); self } pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } pub fn documentation_aside( mut self, side: DocumentationSide, edge: DocumentationEdge, render: impl Fn(&mut App) -> AnyElement + 'static, ) -> Self { self.documentation_aside = Some(DocumentationAside { side, edge, render: Rc::new(render), }); self } } impl FluentBuilder for ContextMenuEntry {} impl From<ContextMenuEntry> for ContextMenuItem { fn from(entry: ContextMenuEntry) -> Self { ContextMenuItem::Entry(entry) } } pub struct ContextMenu { builder: Option<Rc<dyn Fn(Self, &mut Window, &mut Context<Self>) -> Self>>, items: Vec<ContextMenuItem>, focus_handle: FocusHandle, action_context: Option<FocusHandle>, selected_index: Option<usize>, delayed: bool, clicked: bool, end_slot_action: Option<Box<dyn Action>>, key_context: SharedString, _on_blur_subscription: Subscription, keep_open_on_confirm: bool, documentation_aside: Option<(usize, DocumentationAside)>, fixed_width: Option<DefiniteLength>, } #[derive(Copy, Clone, PartialEq, Eq)] pub enum DocumentationSide { Left, Right, } #[derive(Copy, Default, Clone, PartialEq, Eq)] pub enum DocumentationEdge { #[default] Top, Bottom, } #[derive(Clone)] pub struct DocumentationAside { pub side: DocumentationSide, pub edge: DocumentationEdge, pub render: Rc<dyn Fn(&mut App) -> AnyElement>, } impl DocumentationAside { pub fn new( side: DocumentationSide, edge: DocumentationEdge, render: Rc<dyn Fn(&mut App) -> AnyElement>, ) -> Self { Self { side, edge, render } } } impl Focusable for ContextMenu { fn focus_handle(&self, _cx: &App) -> FocusHandle { self.focus_handle.clone() } } impl EventEmitter<DismissEvent> for ContextMenu {} impl FluentBuilder for ContextMenu {} impl ContextMenu { pub fn new( window: &mut Window, cx: &mut Context<Self>, f: impl FnOnce(Self, &mut Window, &mut Context<Self>) -> Self, ) -> Self { let focus_handle = cx.focus_handle(); let _on_blur_subscription = cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ); window.refresh(); f( Self { builder: None, items: Default::default(), focus_handle, action_context: None, selected_index: None, delayed: false, clicked: false, key_context: "menu".into(), _on_blur_subscription, keep_open_on_confirm: false, documentation_aside: None, fixed_width: None, end_slot_action: None, }, window, cx, ) } pub fn build( window: &mut Window, cx: &mut App, f: impl FnOnce(Self, &mut Window, &mut Context<Self>) -> Self, ) -> Entity<Self> { cx.new(|cx| Self::new(window, cx, f)) } /// Builds a [`ContextMenu`] that will stay open when making changes instead of closing after each confirmation. /// /// The main difference from [`ContextMenu::build`] is the type of the `builder`, as we need to be able to hold onto /// it to call it again. pub fn build_persistent( window: &mut Window, cx: &mut App, builder: impl Fn(Self, &mut Window, &mut Context<Self>) -> Self + 'static, ) -> Entity<Self> { cx.new(|cx| { let builder = Rc::new(builder); let focus_handle = cx.focus_handle(); let _on_blur_subscription = cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ); window.refresh(); (builder.clone())( Self { builder: Some(builder), items: Default::default(), focus_handle, action_context: None, selected_index: None, delayed: false, clicked: false, key_context: "menu".into(), _on_blur_subscription, keep_open_on_confirm: true, documentation_aside: None, fixed_width: None, end_slot_action: None, }, window, cx, ) }) } /// Rebuilds the menu. /// /// This is used to refresh the menu entries when entries are toggled when the menu is configured with /// `keep_open_on_confirm = true`. /// /// This only works if the [`ContextMenu`] was constructed using [`ContextMenu::build_persistent`]. Otherwise it is /// a no-op. pub fn rebuild(&mut self, window: &mut Window, cx: &mut Context<Self>) { let Some(builder) = self.builder.clone() else { return; }; // The way we rebuild the menu is a bit of a hack. let focus_handle = cx.focus_handle(); let new_menu = (builder.clone())( Self { builder: Some(builder), items: Default::default(), focus_handle: focus_handle.clone(), action_context: None, selected_index: None, delayed: false, clicked: false, key_context: "menu".into(), _on_blur_subscription: cx.on_blur( &focus_handle, window, |this: &mut ContextMenu, window, cx| this.cancel(&menu::Cancel, window, cx), ), keep_open_on_confirm: false, documentation_aside: None, fixed_width: None, end_slot_action: None, }, window, cx, ); self.items = new_menu.items; cx.notify(); } pub fn context(mut self, focus: FocusHandle) -> Self { self.action_context = Some(focus); self } pub fn header(mut self, title: impl Into<SharedString>) -> Self { self.items.push(ContextMenuItem::Header(title.into())); self } pub fn header_with_link( mut self, title: impl Into<SharedString>, link_label: impl Into<SharedString>, link_url: impl Into<SharedString>, ) -> Self { self.items.push(ContextMenuItem::HeaderWithLink( title.into(), link_label.into(), link_url.into(), )); self } pub fn separator(mut self) -> Self { self.items.push(ContextMenuItem::Separator); self } pub fn extend<I: Into<ContextMenuItem>>(mut self, items: impl IntoIterator<Item = I>) -> Self { self.items.extend(items.into_iter().map(Into::into)); self } pub fn item(mut self, item: impl Into<ContextMenuItem>) -> Self { self.items.push(item.into()); self } pub fn push_item(&mut self, item: impl Into<ContextMenuItem>) { self.items.push(item.into()); } pub fn entry( mut self, label: impl Into<SharedString>, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, custom_icon_path: None, icon_position: IconPosition::End, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, end_slot_icon: None, end_slot_title: None, end_slot_handler: None, show_end_slot_on_hover: false, })); self } pub fn entry_with_end_slot( mut self, label: impl Into<SharedString>, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, end_slot_icon: IconName, end_slot_title: SharedString, end_slot_handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, custom_icon_path: None, icon_position: IconPosition::End, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, end_slot_icon: Some(end_slot_icon), end_slot_title: Some(end_slot_title), end_slot_handler: Some(Rc::new(move |_, window, cx| end_slot_handler(window, cx))), show_end_slot_on_hover: false, })); self } pub fn entry_with_end_slot_on_hover( mut self, label: impl Into<SharedString>, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, end_slot_icon: IconName, end_slot_title: SharedString, end_slot_handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: None, label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, custom_icon_path: None, icon_position: IconPosition::End, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, end_slot_icon: Some(end_slot_icon), end_slot_title: Some(end_slot_title), end_slot_handler: Some(Rc::new(move |_, window, cx| end_slot_handler(window, cx))), show_end_slot_on_hover: true, })); self } pub fn toggleable_entry( mut self, label: impl Into<SharedString>, toggled: bool, position: IconPosition, action: Option<Box<dyn Action>>, handler: impl Fn(&mut Window, &mut App) + 'static, ) -> Self { self.items.push(ContextMenuItem::Entry(ContextMenuEntry { toggle: Some((position, toggled)), label: label.into(), handler: Rc::new(move |_, window, cx| handler(window, cx)), icon: None, custom_icon_path: None, icon_position: position, icon_size: IconSize::Small, icon_color: None, action, disabled: false, documentation_aside: None, end_slot_icon: None, end_slot_title: None, end_slot_handler: None, show_end_slot_on_hover: false, })); self } pub fn custom_row( mut self,