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 {
205 fn id(&self) -> &ElementId;
206 fn style(self, style: ButtonStyle2) -> Self;
207 fn disabled(self, disabled: bool) -> Self;
208 fn size(self, size: ButtonSize2) -> Self;
209 fn tooltip(self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self;
210 // fn width(&mut self, width: DefiniteLength) -> &mut Self;
211}
212
213// pub struct LabelButton {
214// // Base properties...
215// id: ElementId,
216// appearance: ButtonAppearance,
217// state: InteractionState,
218// disabled: bool,
219// size: ButtonSize,
220// tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
221// width: Option<DefiniteLength>,
222// // Button-specific properties...
223// label: Option<SharedString>,
224// label_color: Option<Color>,
225// icon: Option<Icon>,
226// icon_color: Option<Color>,
227// icon_position: Option<IconPosition>,
228// // Define more fields for additional properties as needed
229// }
230
231// impl ButtonCommon for LabelButton {
232// fn id(&self) -> &ElementId {
233// &self.id
234// }
235
236// fn appearance(&mut self, appearance: ButtonAppearance) -> &mut Self {
237// self.style= style;
238// self
239// }
240// // implement methods from ButtonCommon trait...
241// }
242
243// impl LabelButton {
244// pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
245// Self {
246// id: id.into(),
247// label: Some(label.into()),
248// // initialize other fields with default values...
249// }
250// }
251
252// // ... Define other builder methods specific to Button type...
253// }
254
255// TODO: Icon Button
256
257#[derive(IntoElement)]
258pub struct ButtonLike {
259 id: ElementId,
260 style: ButtonStyle2,
261 disabled: bool,
262 size: ButtonSize2,
263 tooltip: Option<Box<dyn Fn(&mut WindowContext) -> AnyView>>,
264 on_click: Option<Box<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
265 children: SmallVec<[AnyElement; 2]>,
266}
267
268impl ButtonLike {
269 pub fn children(
270 &mut self,
271 children: impl IntoIterator<Item = impl Into<AnyElement>>,
272 ) -> &mut Self {
273 self.children = children.into_iter().map(Into::into).collect();
274 self
275 }
276
277 pub fn new(id: impl Into<ElementId>) -> Self {
278 Self {
279 id: id.into(),
280 style: ButtonStyle2::default(),
281 disabled: false,
282 size: ButtonSize2::Default,
283 tooltip: None,
284 children: SmallVec::new(),
285 on_click: None,
286 }
287 }
288}
289
290impl Clickable for ButtonLike {
291 fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
292 self.on_click = Some(Box::new(handler));
293 self
294 }
295}
296
297// impl Selectable for ButtonLike {
298// fn selected(&mut self, selected: bool) -> &mut Self {
299// todo!()
300// }
301
302// fn selected_tooltip(
303// &mut self,
304// tooltip: Box<dyn Fn(&mut WindowContext) -> AnyView + 'static>,
305// ) -> &mut Self {
306// todo!()
307// }
308// }
309
310impl ButtonCommon for ButtonLike {
311 fn id(&self) -> &ElementId {
312 &self.id
313 }
314
315 fn style(mut self, style: ButtonStyle2) -> Self {
316 self.style = style;
317 self
318 }
319
320 fn disabled(mut self, disabled: bool) -> Self {
321 self.disabled = disabled;
322 self
323 }
324
325 fn size(mut self, size: ButtonSize2) -> Self {
326 self.size = size;
327 self
328 }
329
330 fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
331 self.tooltip = Some(Box::new(tooltip));
332 self
333 }
334}
335
336impl RenderOnce for ButtonLike {
337 type Rendered = Stateful<Div>;
338
339 fn render(self, cx: &mut WindowContext) -> Self::Rendered {
340 h_stack()
341 .id(self.id.clone())
342 .h(self.size.height())
343 .rounded_md()
344 .cursor_pointer()
345 .gap_1()
346 .px_1()
347 .bg(self.style.enabled(cx).background)
348 .hover(|hover| hover.bg(self.style.hovered(cx).background))
349 .active(|active| active.bg(self.style.active(cx).background))
350 .when_some(
351 self.on_click.filter(|_| !self.disabled),
352 |this, on_click| this.on_click(move |event, cx| (on_click)(event, cx)),
353 )
354 .when_some(self.tooltip, |this, tooltip| {
355 this.tooltip(move |cx| tooltip(cx))
356 })
357 .children(self.children)
358 }
359}
360
361impl ParentElement for ButtonLike {
362 fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> {
363 &mut self.children
364 }
365}
366
367// pub struct ToggleButton {
368// // based on either IconButton2 or Button, with additional 'selected: bool' property
369// }
370
371// impl ButtonCommon for ToggleButton {
372// fn id(&self) -> &ElementId {
373// &self.id
374// }
375// // ... Implement other methods from ButtonCommon trait with builder patterns...
376// }
377
378// impl ToggleButton {
379// pub fn new() -> Self {
380// // Initialize with default values
381// Self {
382// // ... initialize fields, possibly with defaults or required parameters...
383// }
384// }
385
386// // ... Define other builder methods specific to ToggleButton type...
387// }
388
389// pub struct SplitButton {
390// // Base properties...
391// id: ElementId,
392// // Button-specific properties, possibly including a DefaultButton
393// secondary_action: Option<Box<dyn Fn(&MouseDownEvent, &mut WindowContext)>>,
394// // More fields as necessary...
395// }
396
397// impl ButtonCommon for SplitButton {
398// fn id(&self) -> &ElementId {
399// &self.id
400// }
401// // ... Implement other methods from ButtonCommon trait with builder patterns...
402// }
403
404// impl SplitButton {
405// pub fn new(id: impl Into<ElementId>) -> Self {
406// Self {
407// id: id.into(),
408// // ... initialize other fields with default values...
409// }
410// }
411
412// // ... Define other builder methods specific to SplitButton type...
413// }