use crate::{h_flex, ActiveTheme, Disableable, Selectable, StyledExt}; use gpui::{ div, prelude::FluentBuilder as _, AnyElement, App, ClickEvent, ElementId, InteractiveElement, IntoElement, MouseButton, MouseMoveEvent, ParentElement, RenderOnce, StatefulInteractiveElement as _, StyleRefinement, Styled, Window, }; use smallvec::SmallVec; #[derive(IntoElement)] pub struct MenuItem { id: ElementId, style: StyleRefinement, disabled: bool, selected: bool, on_click: Option>, on_mouse_enter: Option>, children: SmallVec<[AnyElement; 2]>, } impl MenuItem { pub fn new(id: impl Into) -> Self { let id: ElementId = id.into(); Self { id: id.clone(), style: StyleRefinement::default(), disabled: false, selected: false, on_click: None, on_mouse_enter: None, children: SmallVec::new(), } } /// Set ListItem as the selected item style. pub fn selected(mut self, selected: bool) -> Self { self.selected = selected; self } pub fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } pub fn on_click( mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static, ) -> Self { self.on_click = Some(Box::new(handler)); self } pub fn on_mouse_enter( mut self, handler: impl Fn(&MouseMoveEvent, &mut Window, &mut App) + 'static, ) -> Self { self.on_mouse_enter = Some(Box::new(handler)); self } } impl Disableable for MenuItem { fn disabled(mut self, disabled: bool) -> Self { self.disabled = disabled; self } } impl Selectable for MenuItem { fn selected(mut self, selected: bool) -> Self { self.selected = selected; self } fn is_selected(&self) -> bool { self.selected } } impl Styled for MenuItem { fn style(&mut self) -> &mut gpui::StyleRefinement { &mut self.style } } impl ParentElement for MenuItem { fn extend(&mut self, elements: impl IntoIterator) { self.children.extend(elements); } } impl RenderOnce for MenuItem { fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement { h_flex() .id(self.id) .gap_x_1() .py_1() .px_2() .text_base() .text_color(cx.theme().foreground) .relative() .items_center() .justify_between() .refine_style(&self.style) .when(!self.disabled, |this| { this.when(!self.selected, |this| { this.hover(|this| { this.bg(cx.theme().accent) .text_color(cx.theme().accent_foreground) }) }) .when_some(self.on_mouse_enter, |this, on_mouse_enter| { this.on_mouse_move(move |ev, window, cx| (on_mouse_enter)(ev, window, cx)) }) .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().muted_foreground) }) .child( h_flex() .w_full() .items_center() .justify_between() .gap_x_1() .child(div().w_full().children(self.children)), ) } }