1use crate::{Icon, IconName, IconSize, IconWithIndicator, Indicator, prelude::*};
2use gpui::Hsla;
3
4/// An icon that appears within a button.
5///
6/// Can be used as either an icon alongside a label, like in [`Button`](crate::Button),
7/// or as a standalone icon, like in [`IconButton`](crate::IconButton).
8#[derive(IntoElement, RegisterComponent)]
9pub(super) struct ButtonIcon {
10 icon: IconName,
11 size: IconSize,
12 color: Color,
13 disabled: bool,
14 selected: bool,
15 selected_icon: Option<IconName>,
16 selected_icon_color: Option<Color>,
17 selected_style: Option<ButtonStyle>,
18 indicator: Option<Indicator>,
19 indicator_border_color: Option<Hsla>,
20}
21
22impl ButtonIcon {
23 pub fn new(icon: IconName) -> Self {
24 Self {
25 icon,
26 size: IconSize::default(),
27 color: Color::default(),
28 disabled: false,
29 selected: false,
30 selected_icon: None,
31 selected_icon_color: None,
32 selected_style: None,
33 indicator: None,
34 indicator_border_color: None,
35 }
36 }
37
38 pub fn size(mut self, size: impl Into<Option<IconSize>>) -> Self {
39 if let Some(size) = size.into() {
40 self.size = size;
41 }
42 self
43 }
44
45 pub fn color(mut self, color: impl Into<Option<Color>>) -> Self {
46 if let Some(color) = color.into() {
47 self.color = color;
48 }
49 self
50 }
51
52 pub fn selected_icon(mut self, icon: impl Into<Option<IconName>>) -> Self {
53 self.selected_icon = icon.into();
54 self
55 }
56
57 pub fn selected_icon_color(mut self, color: impl Into<Option<Color>>) -> Self {
58 self.selected_icon_color = color.into();
59 self
60 }
61
62 pub fn indicator(mut self, indicator: Indicator) -> Self {
63 self.indicator = Some(indicator);
64 self
65 }
66
67 pub fn indicator_border_color(mut self, color: Option<Hsla>) -> Self {
68 self.indicator_border_color = color;
69 self
70 }
71}
72
73impl Disableable for ButtonIcon {
74 fn disabled(mut self, disabled: bool) -> Self {
75 self.disabled = disabled;
76 self
77 }
78}
79
80impl Toggleable for ButtonIcon {
81 fn toggle_state(mut self, selected: bool) -> Self {
82 self.selected = selected;
83 self
84 }
85}
86
87impl SelectableButton for ButtonIcon {
88 fn selected_style(mut self, style: ButtonStyle) -> Self {
89 self.selected_style = Some(style);
90 self
91 }
92}
93
94impl RenderOnce for ButtonIcon {
95 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
96 let icon = self
97 .selected_icon
98 .filter(|_| self.selected)
99 .unwrap_or(self.icon);
100
101 let icon_color = if self.disabled {
102 Color::Disabled
103 } else if self.selected_style.is_some() && self.selected {
104 self.selected_style.unwrap().into()
105 } else if self.selected {
106 self.selected_icon_color.unwrap_or(Color::Selected)
107 } else {
108 self.color
109 };
110
111 let icon = Icon::new(icon).size(self.size).color(icon_color);
112
113 match self.indicator {
114 Some(indicator) => IconWithIndicator::new(icon, Some(indicator))
115 .indicator_border_color(self.indicator_border_color)
116 .into_any_element(),
117 None => icon.into_any_element(),
118 }
119 }
120}
121
122impl Component for ButtonIcon {
123 fn scope() -> ComponentScope {
124 ComponentScope::Input
125 }
126
127 fn name() -> &'static str {
128 "ButtonIcon"
129 }
130
131 fn description() -> Option<&'static str> {
132 Some("An icon component specifically designed for use within buttons.")
133 }
134
135 fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
136 Some(
137 v_flex()
138 .gap_6()
139 .children(vec![
140 example_group_with_title(
141 "Basic Usage",
142 vec![
143 single_example(
144 "Default",
145 ButtonIcon::new(IconName::Star).into_any_element(),
146 ),
147 single_example(
148 "Custom Size",
149 ButtonIcon::new(IconName::Star)
150 .size(IconSize::Medium)
151 .into_any_element(),
152 ),
153 single_example(
154 "Custom Color",
155 ButtonIcon::new(IconName::Star)
156 .color(Color::Accent)
157 .into_any_element(),
158 ),
159 ],
160 ),
161 example_group_with_title(
162 "States",
163 vec![
164 single_example(
165 "Selected",
166 ButtonIcon::new(IconName::Star)
167 .toggle_state(true)
168 .into_any_element(),
169 ),
170 single_example(
171 "Disabled",
172 ButtonIcon::new(IconName::Star)
173 .disabled(true)
174 .into_any_element(),
175 ),
176 ],
177 ),
178 example_group_with_title(
179 "With Indicator",
180 vec![
181 single_example(
182 "Default Indicator",
183 ButtonIcon::new(IconName::Star)
184 .indicator(Indicator::dot())
185 .into_any_element(),
186 ),
187 single_example(
188 "Custom Indicator",
189 ButtonIcon::new(IconName::Star)
190 .indicator(Indicator::dot().color(Color::Error))
191 .into_any_element(),
192 ),
193 ],
194 ),
195 ])
196 .into_any_element(),
197 )
198 }
199}