dropdown_menu.rs

  1use gpui::{ClickEvent, Corner, CursorStyle, Entity, MouseButton};
  2
  3use crate::{prelude::*, ContextMenu, PopoverMenu};
  4
  5#[derive(IntoElement)]
  6pub struct DropdownMenu {
  7    id: ElementId,
  8    label: SharedString,
  9    menu: Entity<ContextMenu>,
 10    full_width: bool,
 11    disabled: bool,
 12}
 13
 14impl DropdownMenu {
 15    pub fn new(
 16        id: impl Into<ElementId>,
 17        label: impl Into<SharedString>,
 18        menu: Entity<ContextMenu>,
 19    ) -> Self {
 20        Self {
 21            id: id.into(),
 22            label: label.into(),
 23            menu,
 24            full_width: false,
 25            disabled: false,
 26        }
 27    }
 28
 29    pub fn full_width(mut self, full_width: bool) -> Self {
 30        self.full_width = full_width;
 31        self
 32    }
 33}
 34
 35impl Disableable for DropdownMenu {
 36    fn disabled(mut self, disabled: bool) -> Self {
 37        self.disabled = disabled;
 38        self
 39    }
 40}
 41
 42impl RenderOnce for DropdownMenu {
 43    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
 44        PopoverMenu::new(self.id)
 45            .full_width(self.full_width)
 46            .menu(move |_window, _cx| Some(self.menu.clone()))
 47            .trigger(DropdownMenuTrigger::new(self.label).full_width(self.full_width))
 48            .attach(Corner::BottomLeft)
 49    }
 50}
 51
 52#[derive(IntoElement)]
 53struct DropdownMenuTrigger {
 54    label: SharedString,
 55    full_width: bool,
 56    selected: bool,
 57    disabled: bool,
 58    cursor_style: CursorStyle,
 59    on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
 60}
 61
 62impl DropdownMenuTrigger {
 63    pub fn new(label: impl Into<SharedString>) -> Self {
 64        Self {
 65            label: label.into(),
 66            full_width: false,
 67            selected: false,
 68            disabled: false,
 69            cursor_style: CursorStyle::default(),
 70            on_click: None,
 71        }
 72    }
 73
 74    pub fn full_width(mut self, full_width: bool) -> Self {
 75        self.full_width = full_width;
 76        self
 77    }
 78}
 79
 80impl Disableable for DropdownMenuTrigger {
 81    fn disabled(mut self, disabled: bool) -> Self {
 82        self.disabled = disabled;
 83        self
 84    }
 85}
 86
 87impl Toggleable for DropdownMenuTrigger {
 88    fn toggle_state(mut self, selected: bool) -> Self {
 89        self.selected = selected;
 90        self
 91    }
 92}
 93
 94impl Clickable for DropdownMenuTrigger {
 95    fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static) -> Self {
 96        self.on_click = Some(Box::new(handler));
 97        self
 98    }
 99
100    fn cursor_style(mut self, cursor_style: CursorStyle) -> Self {
101        self.cursor_style = cursor_style;
102        self
103    }
104}
105
106impl RenderOnce for DropdownMenuTrigger {
107    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
108        let disabled = self.disabled;
109
110        h_flex()
111            .id("dropdown-menu-trigger")
112            .justify_between()
113            .rounded_sm()
114            .bg(cx.theme().colors().editor_background)
115            .pl_2()
116            .pr_1p5()
117            .py_0p5()
118            .gap_2()
119            .min_w_20()
120            .map(|el| {
121                if self.full_width {
122                    el.w_full()
123                } else {
124                    el.flex_none().w_auto()
125                }
126            })
127            .map(|el| {
128                if disabled {
129                    el.cursor_not_allowed()
130                } else {
131                    el.cursor_pointer()
132                }
133            })
134            .child(Label::new(self.label).color(if disabled {
135                Color::Disabled
136            } else {
137                Color::Default
138            }))
139            .child(
140                Icon::new(IconName::ChevronUpDown)
141                    .size(IconSize::XSmall)
142                    .color(if disabled {
143                        Color::Disabled
144                    } else {
145                        Color::Muted
146                    }),
147            )
148            .when_some(self.on_click.filter(|_| !disabled), |el, on_click| {
149                el.on_mouse_down(MouseButton::Left, |_, window, _| window.prevent_default())
150                    .on_click(move |event, window, cx| {
151                        cx.stop_propagation();
152                        (on_click)(event, window, cx)
153                    })
154            })
155    }
156}