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}
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}