1use gpui::{AnyView, ClickEvent};
2
3use crate::{prelude::*, ButtonLike, ButtonLikeRounding, ElevationIndex};
4
5/// The position of a [`ToggleButton`] within a group of buttons.
6#[derive(Debug, PartialEq, Eq, Clone, Copy)]
7pub enum ToggleButtonPosition {
8 /// The toggle button is first in the group.
9 First,
10
11 /// The toggle button is in the middle of the group (i.e., it is not the first or last toggle button).
12 Middle,
13
14 /// The toggle button is last in the group.
15 Last,
16}
17
18#[derive(IntoElement)]
19pub struct ToggleButton {
20 base: ButtonLike,
21 position_in_group: Option<ToggleButtonPosition>,
22 label: SharedString,
23 label_color: Option<Color>,
24}
25
26impl ToggleButton {
27 pub fn new(id: impl Into<ElementId>, label: impl Into<SharedString>) -> Self {
28 Self {
29 base: ButtonLike::new(id),
30 position_in_group: None,
31 label: label.into(),
32 label_color: None,
33 }
34 }
35
36 pub fn color(mut self, label_color: impl Into<Option<Color>>) -> Self {
37 self.label_color = label_color.into();
38 self
39 }
40
41 pub fn position_in_group(mut self, position: ToggleButtonPosition) -> Self {
42 self.position_in_group = Some(position);
43 self
44 }
45
46 pub fn first(self) -> Self {
47 self.position_in_group(ToggleButtonPosition::First)
48 }
49
50 pub fn middle(self) -> Self {
51 self.position_in_group(ToggleButtonPosition::Middle)
52 }
53
54 pub fn last(self) -> Self {
55 self.position_in_group(ToggleButtonPosition::Last)
56 }
57}
58
59impl Selectable for ToggleButton {
60 fn selected(mut self, selected: bool) -> Self {
61 self.base = self.base.selected(selected);
62 self
63 }
64}
65
66impl SelectableButton for ToggleButton {
67 fn selected_style(mut self, style: ButtonStyle) -> Self {
68 self.base.selected_style = Some(style);
69 self
70 }
71}
72
73impl Disableable for ToggleButton {
74 fn disabled(mut self, disabled: bool) -> Self {
75 self.base = self.base.disabled(disabled);
76 self
77 }
78}
79
80impl Clickable for ToggleButton {
81 fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
82 self.base = self.base.on_click(handler);
83 self
84 }
85
86 fn cursor_style(mut self, cursor_style: gpui::CursorStyle) -> Self {
87 self.base = self.base.cursor_style(cursor_style);
88 self
89 }
90}
91
92impl ButtonCommon for ToggleButton {
93 fn id(&self) -> &ElementId {
94 self.base.id()
95 }
96
97 fn style(mut self, style: ButtonStyle) -> Self {
98 self.base = self.base.style(style);
99 self
100 }
101
102 fn size(mut self, size: ButtonSize) -> Self {
103 self.base = self.base.size(size);
104 self
105 }
106
107 fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
108 self.base = self.base.tooltip(tooltip);
109 self
110 }
111
112 fn layer(mut self, elevation: ElevationIndex) -> Self {
113 self.base = self.base.layer(elevation);
114 self
115 }
116}
117
118impl RenderOnce for ToggleButton {
119 fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
120 let is_disabled = self.base.disabled;
121 let is_selected = self.base.selected;
122
123 let label_color = if is_disabled {
124 Color::Disabled
125 } else if is_selected {
126 Color::Selected
127 } else {
128 self.label_color.unwrap_or_default()
129 };
130
131 self.base
132 .when_some(self.position_in_group, |this, position| match position {
133 ToggleButtonPosition::First => this.rounding(ButtonLikeRounding::Left),
134 ToggleButtonPosition::Middle => this.rounding(None),
135 ToggleButtonPosition::Last => this.rounding(ButtonLikeRounding::Right),
136 })
137 .child(
138 Label::new(self.label)
139 .color(label_color)
140 .line_height_style(LineHeightStyle::UiLabel),
141 )
142 }
143}