dropdown_menu.rs

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