1use gpui::{
2 rems, AnyElement, AnyView, ClickEvent, Div, Hsla, IntoElement, Rems, Stateful,
3 StatefulInteractiveElement, WindowContext,
4};
5use smallvec::SmallVec;
6
7use crate::{h_stack, prelude::*};
8
9// 🚧 Heavily WIP 🚧
10
11// #[derive(Default, PartialEq, Clone, Copy)]
12// pub enum ButtonType2 {
13// #[default]
14// DefaultButton,
15// IconButton,
16// ButtonLike,
17// SplitButton,
18// ToggleButton,
19// }
20
21#[derive(Default, PartialEq, Clone, Copy)]
22pub enum IconPosition2 {
23 #[default]
24 Before,
25 After,
26}
27
28#[derive(Default, PartialEq, Clone, Copy)]
29pub enum ButtonStyle2 {
30 #[default]
31 Filled,
32 // Tinted,
33 Subtle,
34 Transparent,
35}
36
37#[derive(Debug, Clone, Copy)]
38pub struct ButtonStyle {
39 pub background: Hsla,
40 pub border_color: Hsla,
41 pub label_color: Hsla,
42 pub icon_color: Hsla,
43}
44
45impl ButtonStyle2 {
46 pub fn enabled(self, cx: &mut WindowContext) -> ButtonStyle {
47 match self {
48 ButtonStyle2::Filled => ButtonStyle {
49 background: cx.theme().colors().element_background,
50 border_color: gpui::transparent_black(),
51 label_color: Color::Default.color(cx),
52 icon_color: Color::Default.color(cx),
53 },
54 ButtonStyle2::Subtle => ButtonStyle {
55 background: cx.theme().colors().ghost_element_background,
56 border_color: gpui::transparent_black(),
57 label_color: Color::Default.color(cx),
58 icon_color: Color::Default.color(cx),
59 },
60 ButtonStyle2::Transparent => ButtonStyle {
61 background: gpui::transparent_black(),
62 border_color: gpui::transparent_black(),
63 label_color: Color::Default.color(cx),
64 icon_color: Color::Default.color(cx),
65 },
66 }
67 }
68
69 pub fn hovered(self, cx: &mut WindowContext) -> ButtonStyle {
70 match self {
71 ButtonStyle2::Filled => ButtonStyle {
72 background: cx.theme().colors().element_hover,
73 border_color: gpui::transparent_black(),
74 label_color: Color::Default.color(cx),
75 icon_color: Color::Default.color(cx),
76 },
77 ButtonStyle2::Subtle => ButtonStyle {
78 background: cx.theme().colors().ghost_element_hover,
79 border_color: gpui::transparent_black(),
80 label_color: Color::Default.color(cx),
81 icon_color: Color::Default.color(cx),
82 },
83 ButtonStyle2::Transparent => ButtonStyle {
84 background: gpui::transparent_black(),
85 border_color: gpui::transparent_black(),
86 // TODO: These are not great
87 label_color: Color::Muted.color(cx),
88 // TODO: These are not great
89 icon_color: Color::Muted.color(cx),
90 },
91 }
92 }
93
94 pub fn active(self, cx: &mut WindowContext) -> ButtonStyle {
95 match self {
96 ButtonStyle2::Filled => ButtonStyle {
97 background: cx.theme().colors().element_active,
98 border_color: gpui::transparent_black(),
99 label_color: Color::Default.color(cx),
100 icon_color: Color::Default.color(cx),
101 },
102 ButtonStyle2::Subtle => ButtonStyle {
103 background: cx.theme().colors().ghost_element_active,
104 border_color: gpui::transparent_black(),
105 label_color: Color::Default.color(cx),
106 icon_color: Color::Default.color(cx),
107 },
108 ButtonStyle2::Transparent => ButtonStyle {
109 background: gpui::transparent_black(),
110 border_color: gpui::transparent_black(),
111 // TODO: These are not great
112 label_color: Color::Muted.color(cx),
113 // TODO: These are not great
114 icon_color: Color::Muted.color(cx),
115 },
116 }
117 }
118
119 pub fn focused(self, cx: &mut WindowContext) -> ButtonStyle {
120 match self {
121 ButtonStyle2::Filled => ButtonStyle {
122 background: cx.theme().colors().element_background,
123 border_color: cx.theme().colors().border_focused,
124 label_color: Color::Default.color(cx),
125 icon_color: Color::Default.color(cx),
126 },
127 ButtonStyle2::Subtle => ButtonStyle {
128 background: cx.theme().colors().ghost_element_background,
129 border_color: cx.theme().colors().border_focused,
130 label_color: Color::Default.color(cx),
131 icon_color: Color::Default.color(cx),
132 },
133 ButtonStyle2::Transparent => ButtonStyle {
134 background: gpui::transparent_black(),
135 border_color: cx.theme().colors().border_focused,
136 label_color: Color::Accent.color(cx),
137 icon_color: Color::Accent.color(cx),
138 },
139 }
140 }
141
142 pub fn disabled(self, cx: &mut WindowContext) -> ButtonStyle {
143 match self {
144 ButtonStyle2::Filled => ButtonStyle {
145 background: cx.theme().colors().element_disabled,
146 border_color: cx.theme().colors().border_disabled,
147 label_color: Color::Disabled.color(cx),
148 icon_color: Color::Disabled.color(cx),
149 },
150 ButtonStyle2::Subtle => ButtonStyle {
151 background: cx.theme().colors().ghost_element_disabled,
152 border_color: cx.theme().colors().border_disabled,
153 label_color: Color::Disabled.color(cx),
154 icon_color: Color::Disabled.color(cx),
155 },
156 ButtonStyle2::Transparent => ButtonStyle {
157 background: gpui::transparent_black(),
158 border_color: gpui::transparent_black(),
159 label_color: Color::Disabled.color(cx),
160 icon_color: Color::Disabled.color(cx),
161 },
162 }
163 }
164}
165
166#[derive(Default, PartialEq, Clone, Copy)]
167pub enum ButtonSize2 {
168 #[default]
169 Default,
170 Compact,
171 None,
172}
173
174impl ButtonSize2 {
175 fn height(self) -> Rems {
176 match self {
177 ButtonSize2::Default => rems(22. / 16.),
178 ButtonSize2::Compact => rems(18. / 16.),
179 ButtonSize2::None => rems(16. / 16.),
180 }
181 }
182}
183
184// pub struct Button {
185// id: ElementId,
186// icon: Option<Icon>,
187// icon_color: Option<Color>,
188// icon_position: Option<IconPosition2>,
189// label: Option<Label>,
190// label_color: Option<Color>,
191// appearance: ButtonAppearance2,
192// state: InteractionState,
193// selected: bool,
194// disabled: bool,
195// tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
196// width: Option<DefiniteLength>,
197// action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
198// secondary_action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
199// /// Used to pass down some content to the button
200// /// to enable creating custom buttons.
201// children: SmallVec<[AnyElement; 2]>,
202// }
203
204pub trait ButtonCommon: Clickable + Disableable {
205 fn id(&self) -> &ElementId;
206 fn style(self, style: ButtonStyle2) -> Self;
207 fn size(self, size: ButtonSize2) -> Self;
208 fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
209}
210
211// pub struct LabelButton {
212// // Base properties...
213// id: ElementId,
214// appearance: ButtonAppearance,
215// state: InteractionState,
216// disabled: bool,
217// size: ButtonSize,
218// tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
219// width: Option<DefiniteLength>,
220// // Button-specific properties...
221// label: Option<SharedString>,
222// label_color: Option<Color>,
223// icon: Option<Icon>,
224// icon_color: Option<Color>,
225// icon_position: Option<IconPosition>,
226// // Define more fields for additional properties as needed
227// }
228
229// impl ButtonCommon for LabelButton {
230// fn id(&self) -> &ElementId {
231// &self.id
232// }
233
234// fn appearance(&mut self, appearance: ButtonAppearance) -> &mut Self {
235// self.style= style;
236// self
237// }
238// // implement methods from ButtonCommon trait...
239// }
240
241// impl LabelButton {
242// pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
243// Self {
244// id: id.into(),
245// label: Some(label.into()),
246// // initialize other fields with default values...
247// }
248// }
249
250// // ... Define other builder methods specific to Button type...
251// }
252
253// TODO: Icon Button
254
255#[derive(IntoElement)]
256pub struct ButtonLike {
257 id: ElementId,
258 style: ButtonStyle2,
259 disabled: bool,
260 size: ButtonSize2,
261 tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
262 on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
263 children: SmallVec<[AnyElement; 2]>,
264}
265
266impl ButtonLike {
267 pub fn new(id: impl Into<ElementId>) -> Self {
268 Self {
269 id: id.into(),
270 style: ButtonStyle2::default(),
271 disabled: false,
272 size: ButtonSize2::Default,
273 tooltip: None,
274 children: SmallVec::new(),
275 on_click: None,
276 }
277 }
278}
279
280impl Disableable for ButtonLike {
281 fn disabled(mut self, disabled: bool) -> Self {
282 self.disabled = disabled;
283 self
284 }
285}
286
287impl Clickable for ButtonLike {
288 fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
289 self.on_click = Some(Box::new(handler));
290 self
291 }
292}
293
294// impl Selectable for ButtonLike {
295// fn selected(&mut self, selected: bool) -> &mut Self {
296// todo!()
297// }
298
299// fn selected_tooltip(
300// &mut self,
301// tooltip: Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>,
302// ) -> &mut Self {
303// todo!()
304// }
305// }
306
307impl ButtonCommon for ButtonLike {
308 fn id(&self) -> &ElementId {
309 &self.id
310 }
311
312 fn style(mut self, style: ButtonStyle2) -> Self {
313 self.style = style;
314 self
315 }
316
317 fn size(mut self, size: ButtonSize2) -> Self {
318 self.size = size;
319 self
320 }
321
322 fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
323 self.tooltip = Some(Box::new(tooltip));
324 self
325 }
326}
327
328impl RenderOnce for ButtonLike {
329 type Rendered = Stateful<Div>;
330
331 fn render(self, cx: &mut WindowContext) -> Self::Rendered {
332 h_stack()
333 .id(self.id.clone())
334 .h(self.size.height())
335 .rounded_md()
336 .cursor_pointer()
337 .gap_1()
338 .px_1()
339 .bg(self.style.enabled(cx).background)
340 .hover(|hover| hover.bg(self.style.hovered(cx).background))
341 .active(|active| active.bg(self.style.active(cx).background))
342 .when_some(
343 self.on_click.filter(|_| !self.disabled),
344 |this, on_click| this.on_click(move |event, cx| (on_click)(event, cx)),
345 )
346 .when_some(self.tooltip, |this, tooltip| {
347 this.tooltip(move |cx| tooltip(cx))
348 })
349 .children(self.children)
350 }
351}
352
353impl ParentElement for ButtonLike {
354 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
355 &mut self.children
356 }
357}
358
359// pub struct ToggleButton {
360// // based on either IconButton2 or Button, with additional 'selected: bool' property
361// }
362
363// impl ButtonCommon for ToggleButton {
364// fn id(&self) -> &ElementId {
365// &self.id
366// }
367// // ... Implement other methods from ButtonCommon trait with builder patterns...
368// }
369
370// impl ToggleButton {
371// pub fn new() -> Self {
372// // Initialize with default values
373// Self {
374// // ... initialize fields, possibly with defaults or required parameters...
375// }
376// }
377
378// // ... Define other builder methods specific to ToggleButton type...
379// }
380
381// pub struct SplitButton {
382// // Base properties...
383// id: ElementId,
384// // Button-specific properties, possibly including a DefaultButton
385// secondary_action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext)>>,
386// // More fields as necessary...
387// }
388
389// impl ButtonCommon for SplitButton {
390// fn id(&self) -> &ElementId {
391// &self.id
392// }
393// // ... Implement other methods from ButtonCommon trait with builder patterns...
394// }
395
396// impl SplitButton {
397// pub fn new(id: impl Into<ElementId>) -> Self {
398// Self {
399// id: id.into(),
400// // ... initialize other fields with default values...
401// }
402// }
403
404// // ... Define other builder methods specific to SplitButton type...
405// }