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