use gpui::prelude::FluentBuilder as _; use gpui::{ AnyElement, App, ClickEvent, ElementId, InteractiveElement, IntoElement, MouseButton, ParentElement, RenderOnce, SharedString, StatefulInteractiveElement as _, StyleRefinement, Styled, Window, }; use smallvec::SmallVec; use theme::ActiveTheme; use crate::{h_flex, Disableable, StyledExt}; #[derive(IntoElement)] pub(crate) struct MenuItemElement { id: ElementId, group_name: SharedString, style: StyleRefinement, disabled: bool, selected: bool, #[allow(clippy::type_complexity)] on_click: Option>, #[allow(clippy::type_complexity)] on_hover: Option>, children: SmallVec<[AnyElement; 2]>, } impl MenuItemElement { /// Create a new MenuItem with the given ID and group name. pub(crate) fn new(id: impl Into, group_name: impl Into) -> Self { let id: ElementId = id.into(); Self { id: id.clone(), group_name: group_name.into(), style: StyleRefinement::default(), disabled: false, selected: false, on_click: None, on_hover: None, children: SmallVec::new(), } } /// Set ListItem as the selected item style. pub(crate) fn selected(mut self, selected: bool) -> Self { self.selected = selected; self } /// Set the disabled state of the MenuItem. pub(crate) fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } /// Set a handler for when the MenuItem is clicked. pub(crate) fn on_click( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, ) -> Self { self.on_click = Some(Box::new(handler)); self } /// Set a handler for when the mouse enters the MenuItem. #[allow(unused)] pub fn on_hover(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self { self.on_hover = Some(Box::new(handler)); self } } impl Disableable for MenuItemElement { fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } } impl Styled for MenuItemElement { fn style(&mut self) -> &mut gpui::StyleRefinement { &mut self.style } } impl ParentElement for MenuItemElement { fn extend(&mut self, elements: impl IntoIterator) { self.children.extend(elements); } } impl RenderOnce for MenuItemElement { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { h_flex() .id(self.id) .group(&self.group_name) .gap_x_1() .p_1() .text_sm() .text_color(cx.theme().text) .relative() .items_center() .justify_between() .refine_style(&self.style) .when_some(self.on_hover, |this, on_hover| { this.on_hover(move |hovered, window, cx| (on_hover)(hovered, window, cx)) }) .when(!self.disabled, |this| { this.group_hover(self.group_name, |this| { this.bg(cx.theme().secondary_background) .text_color(cx.theme().secondary_foreground) }) .when(self.selected, |this| { this.bg(cx.theme().secondary_background) .text_color(cx.theme().secondary_foreground) }) .when_some(self.on_click, |this, on_click| { this.on_mouse_down(MouseButton::Left, move |_, _, cx| { cx.stop_propagation(); }) .on_click(on_click) }) }) .when(self.disabled, |this| this.text_color(cx.theme().text_muted)) .children(self.children) } }