gpui_component/button/
dropdown_button.rs

1use gpui::{
2    div, prelude::FluentBuilder, App, Context, Corner, Corners, Edges, ElementId,
3    InteractiveElement as _, IntoElement, ParentElement, RenderOnce, StyleRefinement, Styled,
4    Window,
5};
6
7use crate::{
8    popup_menu::{PopupMenu, PopupMenuExt},
9    IconName, Selectable, Sizable, Size, StyledExt as _,
10};
11
12use super::{Button, ButtonRounded, ButtonVariant, ButtonVariants};
13
14#[derive(IntoElement)]
15pub struct DropdownButton {
16    id: ElementId,
17    style: StyleRefinement,
18    button: Option<Button>,
19    popup_menu:
20        Option<Box<dyn Fn(PopupMenu, &mut Window, &mut Context<PopupMenu>) -> PopupMenu + 'static>>,
21    selected: bool,
22    // The button props
23    compact: Option<bool>,
24    outline: Option<bool>,
25    variant: Option<ButtonVariant>,
26    size: Option<Size>,
27    rounded: ButtonRounded,
28}
29
30impl DropdownButton {
31    pub fn new(id: impl Into<ElementId>) -> Self {
32        Self {
33            id: id.into(),
34            style: StyleRefinement::default(),
35            button: None,
36            popup_menu: None,
37            selected: false,
38            compact: None,
39            outline: None,
40            variant: None,
41            size: None,
42            rounded: ButtonRounded::default(),
43        }
44    }
45
46    pub fn button(mut self, button: Button) -> Self {
47        self.button = Some(button);
48        self
49    }
50
51    pub fn popup_menu(
52        mut self,
53        popup_menu: impl Fn(PopupMenu, &mut Window, &mut Context<PopupMenu>) -> PopupMenu + 'static,
54    ) -> Self {
55        self.popup_menu = Some(Box::new(popup_menu));
56        self
57    }
58
59    pub fn rounded(mut self, rounded: impl Into<ButtonRounded>) -> Self {
60        self.rounded = rounded.into();
61        self
62    }
63
64    pub fn compact(mut self) -> Self {
65        self.compact = Some(true);
66        self
67    }
68
69    pub fn outline(mut self) -> Self {
70        self.outline = Some(true);
71        self
72    }
73}
74
75impl Styled for DropdownButton {
76    fn style(&mut self) -> &mut gpui::StyleRefinement {
77        &mut self.style
78    }
79}
80
81impl Sizable for DropdownButton {
82    fn with_size(mut self, size: impl Into<Size>) -> Self {
83        self.size = Some(size.into());
84        self
85    }
86}
87
88impl ButtonVariants for DropdownButton {
89    fn with_variant(mut self, variant: ButtonVariant) -> Self {
90        self.variant = Some(variant);
91        self
92    }
93}
94
95impl Selectable for DropdownButton {
96    fn selected(mut self, selected: bool) -> Self {
97        self.selected = selected;
98        self
99    }
100
101    fn is_selected(&self) -> bool {
102        self.selected
103    }
104}
105
106impl RenderOnce for DropdownButton {
107    fn render(self, _: &mut Window, _: &mut App) -> impl IntoElement {
108        let rounded = self
109            .variant
110            .map(|variant| variant.is_ghost() && !self.selected)
111            .unwrap_or(false);
112
113        div()
114            .id(self.id)
115            .h_flex()
116            .refine_style(&self.style)
117            .when_some(self.button, |this, button| {
118                this.child(
119                    button
120                        .rounded(self.rounded)
121                        .border_corners(Corners {
122                            top_left: true,
123                            top_right: rounded,
124                            bottom_left: true,
125                            bottom_right: rounded,
126                        })
127                        .border_edges(Edges {
128                            left: true,
129                            top: true,
130                            right: true,
131                            bottom: true,
132                        })
133                        .selected(self.selected)
134                        .when_some(self.compact, |this, _| this.compact())
135                        .when_some(self.outline, |this, _| this.outline())
136                        .when_some(self.size, |this, size| this.with_size(size))
137                        .when_some(self.variant, |this, variant| this.with_variant(variant)),
138                )
139                .when_some(self.popup_menu, |this, popup_menu| {
140                    this.child(
141                        Button::new("popup")
142                            .icon(IconName::ChevronDown)
143                            .rounded(self.rounded)
144                            .border_edges(Edges {
145                                left: rounded,
146                                top: true,
147                                right: true,
148                                bottom: true,
149                            })
150                            .border_corners(Corners {
151                                top_left: rounded,
152                                top_right: true,
153                                bottom_left: rounded,
154                                bottom_right: true,
155                            })
156                            .selected(self.selected)
157                            .when_some(self.compact, |this, _| this.compact())
158                            .when_some(self.outline, |this, _| this.outline())
159                            .when_some(self.size, |this, size| this.with_size(size))
160                            .when_some(self.variant, |this, variant| this.with_variant(variant))
161                            .popup_menu_with_anchor(Corner::TopRight, move |this, window, cx| {
162                                popup_menu(this, window, cx)
163                            }),
164                    )
165                })
166            })
167    }
168}