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