1use gpui::{rems, AnyElement, AnyView, ClickEvent, Div, Hsla, Rems, Stateful};
2use smallvec::SmallVec;
3
4use crate::h_stack;
5use crate::prelude::*;
6
7pub trait ButtonCommon: Clickable + Disableable {
8 fn id(&self) -> &ElementId;
9 fn style(self, style: ButtonStyle2) -> Self;
10 fn size(self, size: ButtonSize2) -> Self;
11 fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
12}
13
14#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
15pub enum ButtonStyle2 {
16 #[default]
17 Filled,
18 // Tinted,
19 Subtle,
20 Transparent,
21}
22
23#[derive(Debug, Clone)]
24pub struct ButtonStyle {
25 pub background: Hsla,
26 pub border_color: Hsla,
27 pub label_color: Hsla,
28 pub icon_color: Hsla,
29}
30
31impl ButtonStyle2 {
32 pub fn enabled(self, cx: &mut WindowContext) -> ButtonStyle {
33 match self {
34 ButtonStyle2::Filled => ButtonStyle {
35 background: cx.theme().colors().element_background,
36 border_color: gpui::transparent_black(),
37 label_color: Color::Default.color(cx),
38 icon_color: Color::Default.color(cx),
39 },
40 ButtonStyle2::Subtle => ButtonStyle {
41 background: cx.theme().colors().ghost_element_background,
42 border_color: gpui::transparent_black(),
43 label_color: Color::Default.color(cx),
44 icon_color: Color::Default.color(cx),
45 },
46 ButtonStyle2::Transparent => ButtonStyle {
47 background: gpui::transparent_black(),
48 border_color: gpui::transparent_black(),
49 label_color: Color::Default.color(cx),
50 icon_color: Color::Default.color(cx),
51 },
52 }
53 }
54
55 pub fn hovered(self, cx: &mut WindowContext) -> ButtonStyle {
56 match self {
57 ButtonStyle2::Filled => ButtonStyle {
58 background: cx.theme().colors().element_hover,
59 border_color: gpui::transparent_black(),
60 label_color: Color::Default.color(cx),
61 icon_color: Color::Default.color(cx),
62 },
63 ButtonStyle2::Subtle => ButtonStyle {
64 background: cx.theme().colors().ghost_element_hover,
65 border_color: gpui::transparent_black(),
66 label_color: Color::Default.color(cx),
67 icon_color: Color::Default.color(cx),
68 },
69 ButtonStyle2::Transparent => ButtonStyle {
70 background: gpui::transparent_black(),
71 border_color: gpui::transparent_black(),
72 // TODO: These are not great
73 label_color: Color::Muted.color(cx),
74 // TODO: These are not great
75 icon_color: Color::Muted.color(cx),
76 },
77 }
78 }
79
80 pub fn active(self, cx: &mut WindowContext) -> ButtonStyle {
81 match self {
82 ButtonStyle2::Filled => ButtonStyle {
83 background: cx.theme().colors().element_active,
84 border_color: gpui::transparent_black(),
85 label_color: Color::Default.color(cx),
86 icon_color: Color::Default.color(cx),
87 },
88 ButtonStyle2::Subtle => ButtonStyle {
89 background: cx.theme().colors().ghost_element_active,
90 border_color: gpui::transparent_black(),
91 label_color: Color::Default.color(cx),
92 icon_color: Color::Default.color(cx),
93 },
94 ButtonStyle2::Transparent => ButtonStyle {
95 background: gpui::transparent_black(),
96 border_color: gpui::transparent_black(),
97 // TODO: These are not great
98 label_color: Color::Muted.color(cx),
99 // TODO: These are not great
100 icon_color: Color::Muted.color(cx),
101 },
102 }
103 }
104
105 pub fn focused(self, cx: &mut WindowContext) -> ButtonStyle {
106 match self {
107 ButtonStyle2::Filled => ButtonStyle {
108 background: cx.theme().colors().element_background,
109 border_color: cx.theme().colors().border_focused,
110 label_color: Color::Default.color(cx),
111 icon_color: Color::Default.color(cx),
112 },
113 ButtonStyle2::Subtle => ButtonStyle {
114 background: cx.theme().colors().ghost_element_background,
115 border_color: cx.theme().colors().border_focused,
116 label_color: Color::Default.color(cx),
117 icon_color: Color::Default.color(cx),
118 },
119 ButtonStyle2::Transparent => ButtonStyle {
120 background: gpui::transparent_black(),
121 border_color: cx.theme().colors().border_focused,
122 label_color: Color::Accent.color(cx),
123 icon_color: Color::Accent.color(cx),
124 },
125 }
126 }
127
128 pub fn disabled(self, cx: &mut WindowContext) -> ButtonStyle {
129 match self {
130 ButtonStyle2::Filled => ButtonStyle {
131 background: cx.theme().colors().element_disabled,
132 border_color: cx.theme().colors().border_disabled,
133 label_color: Color::Disabled.color(cx),
134 icon_color: Color::Disabled.color(cx),
135 },
136 ButtonStyle2::Subtle => ButtonStyle {
137 background: cx.theme().colors().ghost_element_disabled,
138 border_color: cx.theme().colors().border_disabled,
139 label_color: Color::Disabled.color(cx),
140 icon_color: Color::Disabled.color(cx),
141 },
142 ButtonStyle2::Transparent => ButtonStyle {
143 background: gpui::transparent_black(),
144 border_color: gpui::transparent_black(),
145 label_color: Color::Disabled.color(cx),
146 icon_color: Color::Disabled.color(cx),
147 },
148 }
149 }
150}
151
152#[derive(Default, PartialEq, Clone, Copy)]
153pub enum ButtonSize2 {
154 #[default]
155 Default,
156 Compact,
157 None,
158}
159
160impl ButtonSize2 {
161 fn height(self) -> Rems {
162 match self {
163 ButtonSize2::Default => rems(22. / 16.),
164 ButtonSize2::Compact => rems(18. / 16.),
165 ButtonSize2::None => rems(16. / 16.),
166 }
167 }
168}
169
170#[derive(IntoElement)]
171pub struct ButtonLike {
172 id: ElementId,
173 pub(super) style: ButtonStyle2,
174 pub(super) disabled: bool,
175 size: ButtonSize2,
176 tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
177 on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
178 children: SmallVec<[AnyElement; 2]>,
179}
180
181impl ButtonLike {
182 pub fn new(id: impl Into<ElementId>) -> Self {
183 Self {
184 id: id.into(),
185 style: ButtonStyle2::default(),
186 disabled: false,
187 size: ButtonSize2::Default,
188 tooltip: None,
189 children: SmallVec::new(),
190 on_click: None,
191 }
192 }
193}
194
195impl Disableable for ButtonLike {
196 fn disabled(mut self, disabled: bool) -> Self {
197 self.disabled = disabled;
198 self
199 }
200}
201
202impl Clickable for ButtonLike {
203 fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
204 self.on_click = Some(Box::new(handler));
205 self
206 }
207}
208
209// impl Selectable for ButtonLike {
210// fn selected(&mut self, selected: bool) -> &mut Self {
211// todo!()
212// }
213
214// fn selected_tooltip(
215// &mut self,
216// tooltip: Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>,
217// ) -> &mut Self {
218// todo!()
219// }
220// }
221
222impl ButtonCommon for ButtonLike {
223 fn id(&self) -> &ElementId {
224 &self.id
225 }
226
227 fn style(mut self, style: ButtonStyle2) -> Self {
228 self.style = style;
229 self
230 }
231
232 fn size(mut self, size: ButtonSize2) -> Self {
233 self.size = size;
234 self
235 }
236
237 fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
238 self.tooltip = Some(Box::new(tooltip));
239 self
240 }
241}
242
243impl ParentElement for ButtonLike {
244 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
245 &mut self.children
246 }
247}
248
249impl RenderOnce for ButtonLike {
250 type Rendered = Stateful<Div>;
251
252 fn render(self, cx: &mut WindowContext) -> Self::Rendered {
253 h_stack()
254 .id(self.id.clone())
255 .h(self.size.height())
256 .rounded_md()
257 .cursor_pointer()
258 .gap_1()
259 .px_1()
260 .bg(self.style.enabled(cx).background)
261 .hover(|hover| hover.bg(self.style.hovered(cx).background))
262 .active(|active| active.bg(self.style.active(cx).background))
263 .when_some(
264 self.on_click.filter(|_| !self.disabled),
265 |this, on_click| this.on_click(move |event, cx| (on_click)(event, cx)),
266 )
267 .when_some(self.tooltip, |this, tooltip| {
268 this.tooltip(move |cx| tooltip(cx))
269 })
270 .children(self.children)
271 }
272}