dropdown_menu.rs

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