icon_button.rs

  1use gpui::{AnyView, DefiniteLength, Hsla};
  2
  3use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle};
  4use crate::{ElevationIndex, Indicator, SelectableButton, TintColor, prelude::*};
  5use crate::{IconName, IconSize};
  6
  7use super::button_icon::ButtonIcon;
  8
  9/// The shape of an [`IconButton`].
 10#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
 11pub enum IconButtonShape {
 12    Square,
 13    Wide,
 14}
 15
 16#[derive(IntoElement, RegisterComponent)]
 17pub struct IconButton {
 18    base: ButtonLike,
 19    shape: IconButtonShape,
 20    icon: IconName,
 21    icon_size: IconSize,
 22    icon_color: Color,
 23    selected_icon: Option<IconName>,
 24    selected_icon_color: Option<Color>,
 25    indicator: Option<Indicator>,
 26    indicator_border_color: Option<Hsla>,
 27    alpha: Option<f32>,
 28}
 29
 30impl IconButton {
 31    pub fn new(id: impl Into<ElementId>, icon: IconName) -> Self {
 32        let mut this = Self {
 33            base: ButtonLike::new(id),
 34            shape: IconButtonShape::Wide,
 35            icon,
 36            icon_size: IconSize::default(),
 37            icon_color: Color::Default,
 38            selected_icon: None,
 39            selected_icon_color: None,
 40            indicator: None,
 41            indicator_border_color: None,
 42            alpha: None,
 43        };
 44        this.base.base = this.base.base.debug_selector(|| format!("ICON-{:?}", icon));
 45        this
 46    }
 47
 48    pub fn shape(mut self, shape: IconButtonShape) -> Self {
 49        self.shape = shape;
 50        self
 51    }
 52
 53    pub fn icon_size(mut self, icon_size: IconSize) -> Self {
 54        self.icon_size = icon_size;
 55        self
 56    }
 57
 58    pub fn icon_color(mut self, icon_color: Color) -> Self {
 59        self.icon_color = icon_color;
 60        self
 61    }
 62
 63    pub fn alpha(mut self, alpha: f32) -> Self {
 64        self.alpha = Some(alpha);
 65        self
 66    }
 67
 68    pub fn selected_icon(mut self, icon: impl Into<Option<IconName>>) -> Self {
 69        self.selected_icon = icon.into();
 70        self
 71    }
 72
 73    pub fn on_right_click(
 74        mut self,
 75        handler: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static,
 76    ) -> Self {
 77        self.base = self.base.on_right_click(handler);
 78        self
 79    }
 80
 81    /// Sets the icon color used when the button is in a selected state.
 82    pub fn selected_icon_color(mut self, color: impl Into<Option<Color>>) -> Self {
 83        self.selected_icon_color = color.into();
 84        self
 85    }
 86
 87    pub fn indicator(mut self, indicator: Indicator) -> Self {
 88        self.indicator = Some(indicator);
 89        self
 90    }
 91
 92    pub fn indicator_border_color(mut self, color: Option<Hsla>) -> Self {
 93        self.indicator_border_color = color;
 94
 95        self
 96    }
 97}
 98
 99impl Disableable for IconButton {
100    fn disabled(mut self, disabled: bool) -> Self {
101        self.base = self.base.disabled(disabled);
102        self
103    }
104}
105
106impl Toggleable for IconButton {
107    fn toggle_state(mut self, selected: bool) -> Self {
108        self.base = self.base.toggle_state(selected);
109        self
110    }
111}
112
113impl SelectableButton for IconButton {
114    fn selected_style(mut self, style: ButtonStyle) -> Self {
115        self.base = self.base.selected_style(style);
116        self
117    }
118}
119
120impl Clickable for IconButton {
121    fn on_click(
122        mut self,
123        handler: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static,
124    ) -> Self {
125        self.base = self.base.on_click(handler);
126        self
127    }
128
129    fn cursor_style(mut self, cursor_style: gpui::CursorStyle) -> Self {
130        self.base = self.base.cursor_style(cursor_style);
131        self
132    }
133}
134
135impl FixedWidth for IconButton {
136    fn width(mut self, width: DefiniteLength) -> Self {
137        self.base = self.base.width(width);
138        self
139    }
140
141    fn full_width(mut self) -> Self {
142        self.base = self.base.full_width();
143        self
144    }
145}
146
147impl ButtonCommon for IconButton {
148    fn id(&self) -> &ElementId {
149        self.base.id()
150    }
151
152    fn style(mut self, style: ButtonStyle) -> Self {
153        self.base = self.base.style(style);
154        self
155    }
156
157    fn size(mut self, size: ButtonSize) -> Self {
158        self.base = self.base.size(size);
159        self
160    }
161
162    fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
163        self.base = self.base.tooltip(tooltip);
164        self
165    }
166
167    fn layer(mut self, elevation: ElevationIndex) -> Self {
168        self.base = self.base.layer(elevation);
169        self
170    }
171}
172
173impl VisibleOnHover for IconButton {
174    fn visible_on_hover(mut self, group_name: impl Into<SharedString>) -> Self {
175        self.base = self.base.visible_on_hover(group_name);
176        self
177    }
178}
179
180impl RenderOnce for IconButton {
181    #[allow(refining_impl_trait)]
182    fn render(self, window: &mut Window, cx: &mut App) -> ButtonLike {
183        let is_disabled = self.base.disabled;
184        let is_selected = self.base.selected;
185        let selected_style = self.base.selected_style;
186
187        let color = self.icon_color.color(cx).opacity(self.alpha.unwrap_or(1.0));
188        self.base
189            .map(|this| match self.shape {
190                IconButtonShape::Square => {
191                    let size = self.icon_size.square(window, cx);
192                    this.width(size.into()).height(size.into())
193                }
194                IconButtonShape::Wide => this,
195            })
196            .child(
197                ButtonIcon::new(self.icon)
198                    .disabled(is_disabled)
199                    .toggle_state(is_selected)
200                    .selected_icon(self.selected_icon)
201                    .selected_icon_color(self.selected_icon_color)
202                    .when_some(selected_style, |this, style| this.selected_style(style))
203                    .when_some(self.indicator, |this, indicator| {
204                        this.indicator(indicator)
205                            .indicator_border_color(self.indicator_border_color)
206                    })
207                    .size(self.icon_size)
208                    .color(Color::Custom(color)),
209            )
210    }
211}
212
213impl Component for IconButton {
214    fn scope() -> ComponentScope {
215        ComponentScope::Input
216    }
217
218    fn sort_name() -> &'static str {
219        "ButtonB"
220    }
221
222    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
223        Some(
224            v_flex()
225                .gap_6()
226                .children(vec![
227                    example_group_with_title(
228                        "Icon Button Styles",
229                        vec![
230                            single_example(
231                                "Default",
232                                IconButton::new("default", IconName::Check)
233                                    .layer(ElevationIndex::Background)
234                                    .into_any_element(),
235                            ),
236                            single_example(
237                                "Filled",
238                                IconButton::new("filled", IconName::Check)
239                                    .layer(ElevationIndex::Background)
240                                    .style(ButtonStyle::Filled)
241                                    .into_any_element(),
242                            ),
243                            single_example(
244                                "Subtle",
245                                IconButton::new("subtle", IconName::Check)
246                                    .layer(ElevationIndex::Background)
247                                    .style(ButtonStyle::Subtle)
248                                    .into_any_element(),
249                            ),
250                            single_example(
251                                "Tinted",
252                                IconButton::new("tinted", IconName::Check)
253                                    .layer(ElevationIndex::Background)
254                                    .style(ButtonStyle::Tinted(TintColor::Accent))
255                                    .into_any_element(),
256                            ),
257                            single_example(
258                                "Transparent",
259                                IconButton::new("transparent", IconName::Check)
260                                    .layer(ElevationIndex::Background)
261                                    .style(ButtonStyle::Transparent)
262                                    .into_any_element(),
263                            ),
264                        ],
265                    ),
266                    example_group_with_title(
267                        "Icon Button Shapes",
268                        vec![
269                            single_example(
270                                "Square",
271                                IconButton::new("square", IconName::Check)
272                                    .shape(IconButtonShape::Square)
273                                    .style(ButtonStyle::Filled)
274                                    .layer(ElevationIndex::Background)
275                                    .into_any_element(),
276                            ),
277                            single_example(
278                                "Wide",
279                                IconButton::new("wide", IconName::Check)
280                                    .shape(IconButtonShape::Wide)
281                                    .style(ButtonStyle::Filled)
282                                    .layer(ElevationIndex::Background)
283                                    .into_any_element(),
284                            ),
285                        ],
286                    ),
287                    example_group_with_title(
288                        "Icon Button Sizes",
289                        vec![
290                            single_example(
291                                "XSmall",
292                                IconButton::new("xsmall", IconName::Check)
293                                    .icon_size(IconSize::XSmall)
294                                    .style(ButtonStyle::Filled)
295                                    .layer(ElevationIndex::Background)
296                                    .into_any_element(),
297                            ),
298                            single_example(
299                                "Small",
300                                IconButton::new("small", IconName::Check)
301                                    .icon_size(IconSize::Small)
302                                    .style(ButtonStyle::Filled)
303                                    .layer(ElevationIndex::Background)
304                                    .into_any_element(),
305                            ),
306                            single_example(
307                                "Medium",
308                                IconButton::new("medium", IconName::Check)
309                                    .icon_size(IconSize::Medium)
310                                    .style(ButtonStyle::Filled)
311                                    .layer(ElevationIndex::Background)
312                                    .into_any_element(),
313                            ),
314                            single_example(
315                                "XLarge",
316                                IconButton::new("xlarge", IconName::Check)
317                                    .icon_size(IconSize::XLarge)
318                                    .style(ButtonStyle::Filled)
319                                    .layer(ElevationIndex::Background)
320                                    .into_any_element(),
321                            ),
322                        ],
323                    ),
324                    example_group_with_title(
325                        "Special States",
326                        vec![
327                            single_example(
328                                "Disabled",
329                                IconButton::new("disabled", IconName::Check)
330                                    .disabled(true)
331                                    .style(ButtonStyle::Filled)
332                                    .layer(ElevationIndex::Background)
333                                    .into_any_element(),
334                            ),
335                            single_example(
336                                "Selected",
337                                IconButton::new("selected", IconName::Check)
338                                    .toggle_state(true)
339                                    .style(ButtonStyle::Filled)
340                                    .layer(ElevationIndex::Background)
341                                    .into_any_element(),
342                            ),
343                            single_example(
344                                "With Indicator",
345                                IconButton::new("indicator", IconName::Check)
346                                    .indicator(Indicator::dot().color(Color::Success))
347                                    .style(ButtonStyle::Filled)
348                                    .layer(ElevationIndex::Background)
349                                    .into_any_element(),
350                            ),
351                        ],
352                    ),
353                    example_group_with_title(
354                        "Custom Colors",
355                        vec![
356                            single_example(
357                                "Custom Icon Color",
358                                IconButton::new("custom_color", IconName::Check)
359                                    .icon_color(Color::Accent)
360                                    .style(ButtonStyle::Filled)
361                                    .layer(ElevationIndex::Background)
362                                    .into_any_element(),
363                            ),
364                            single_example(
365                                "With Alpha",
366                                IconButton::new("alpha", IconName::Check)
367                                    .alpha(0.5)
368                                    .style(ButtonStyle::Filled)
369                                    .layer(ElevationIndex::Background)
370                                    .into_any_element(),
371                            ),
372                        ],
373                    ),
374                ])
375                .into_any_element(),
376        )
377    }
378}