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    fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
182        let is_disabled = self.base.disabled;
183        let is_selected = self.base.selected;
184        let selected_style = self.base.selected_style;
185
186        let color = self.icon_color.color(cx).opacity(self.alpha.unwrap_or(1.0));
187        self.base
188            .map(|this| match self.shape {
189                IconButtonShape::Square => {
190                    let size = self.icon_size.square(window, cx);
191                    this.width(size.into()).height(size.into())
192                }
193                IconButtonShape::Wide => this,
194            })
195            .child(
196                ButtonIcon::new(self.icon)
197                    .disabled(is_disabled)
198                    .toggle_state(is_selected)
199                    .selected_icon(self.selected_icon)
200                    .selected_icon_color(self.selected_icon_color)
201                    .when_some(selected_style, |this, style| this.selected_style(style))
202                    .when_some(self.indicator, |this, indicator| {
203                        this.indicator(indicator)
204                            .indicator_border_color(self.indicator_border_color)
205                    })
206                    .size(self.icon_size)
207                    .color(Color::Custom(color)),
208            )
209    }
210}
211
212impl Component for IconButton {
213    fn scope() -> ComponentScope {
214        ComponentScope::Input
215    }
216
217    fn sort_name() -> &'static str {
218        "ButtonB"
219    }
220
221    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
222        Some(
223            v_flex()
224                .gap_6()
225                .children(vec![
226                    example_group_with_title(
227                        "Icon Button Styles",
228                        vec![
229                            single_example(
230                                "Default",
231                                IconButton::new("default", IconName::Check)
232                                    .layer(ElevationIndex::Background)
233                                    .into_any_element(),
234                            ),
235                            single_example(
236                                "Filled",
237                                IconButton::new("filled", IconName::Check)
238                                    .layer(ElevationIndex::Background)
239                                    .style(ButtonStyle::Filled)
240                                    .into_any_element(),
241                            ),
242                            single_example(
243                                "Subtle",
244                                IconButton::new("subtle", IconName::Check)
245                                    .layer(ElevationIndex::Background)
246                                    .style(ButtonStyle::Subtle)
247                                    .into_any_element(),
248                            ),
249                            single_example(
250                                "Tinted",
251                                IconButton::new("tinted", IconName::Check)
252                                    .layer(ElevationIndex::Background)
253                                    .style(ButtonStyle::Tinted(TintColor::Accent))
254                                    .into_any_element(),
255                            ),
256                            single_example(
257                                "Transparent",
258                                IconButton::new("transparent", IconName::Check)
259                                    .layer(ElevationIndex::Background)
260                                    .style(ButtonStyle::Transparent)
261                                    .into_any_element(),
262                            ),
263                        ],
264                    ),
265                    example_group_with_title(
266                        "Icon Button Shapes",
267                        vec![
268                            single_example(
269                                "Square",
270                                IconButton::new("square", IconName::Check)
271                                    .shape(IconButtonShape::Square)
272                                    .style(ButtonStyle::Filled)
273                                    .layer(ElevationIndex::Background)
274                                    .into_any_element(),
275                            ),
276                            single_example(
277                                "Wide",
278                                IconButton::new("wide", IconName::Check)
279                                    .shape(IconButtonShape::Wide)
280                                    .style(ButtonStyle::Filled)
281                                    .layer(ElevationIndex::Background)
282                                    .into_any_element(),
283                            ),
284                        ],
285                    ),
286                    example_group_with_title(
287                        "Icon Button Sizes",
288                        vec![
289                            single_example(
290                                "XSmall",
291                                IconButton::new("xsmall", IconName::Check)
292                                    .icon_size(IconSize::XSmall)
293                                    .style(ButtonStyle::Filled)
294                                    .layer(ElevationIndex::Background)
295                                    .into_any_element(),
296                            ),
297                            single_example(
298                                "Small",
299                                IconButton::new("small", IconName::Check)
300                                    .icon_size(IconSize::Small)
301                                    .style(ButtonStyle::Filled)
302                                    .layer(ElevationIndex::Background)
303                                    .into_any_element(),
304                            ),
305                            single_example(
306                                "Medium",
307                                IconButton::new("medium", IconName::Check)
308                                    .icon_size(IconSize::Medium)
309                                    .style(ButtonStyle::Filled)
310                                    .layer(ElevationIndex::Background)
311                                    .into_any_element(),
312                            ),
313                            single_example(
314                                "XLarge",
315                                IconButton::new("xlarge", IconName::Check)
316                                    .icon_size(IconSize::XLarge)
317                                    .style(ButtonStyle::Filled)
318                                    .layer(ElevationIndex::Background)
319                                    .into_any_element(),
320                            ),
321                        ],
322                    ),
323                    example_group_with_title(
324                        "Special States",
325                        vec![
326                            single_example(
327                                "Disabled",
328                                IconButton::new("disabled", IconName::Check)
329                                    .disabled(true)
330                                    .style(ButtonStyle::Filled)
331                                    .layer(ElevationIndex::Background)
332                                    .into_any_element(),
333                            ),
334                            single_example(
335                                "Selected",
336                                IconButton::new("selected", IconName::Check)
337                                    .toggle_state(true)
338                                    .style(ButtonStyle::Filled)
339                                    .layer(ElevationIndex::Background)
340                                    .into_any_element(),
341                            ),
342                            single_example(
343                                "With Indicator",
344                                IconButton::new("indicator", IconName::Check)
345                                    .indicator(Indicator::dot().color(Color::Success))
346                                    .style(ButtonStyle::Filled)
347                                    .layer(ElevationIndex::Background)
348                                    .into_any_element(),
349                            ),
350                        ],
351                    ),
352                    example_group_with_title(
353                        "Custom Colors",
354                        vec![
355                            single_example(
356                                "Custom Icon Color",
357                                IconButton::new("custom_color", IconName::Check)
358                                    .icon_color(Color::Accent)
359                                    .style(ButtonStyle::Filled)
360                                    .layer(ElevationIndex::Background)
361                                    .into_any_element(),
362                            ),
363                            single_example(
364                                "With Alpha",
365                                IconButton::new("alpha", IconName::Check)
366                                    .alpha(0.5)
367                                    .style(ButtonStyle::Filled)
368                                    .layer(ElevationIndex::Background)
369                                    .into_any_element(),
370                            ),
371                        ],
372                    ),
373                ])
374                .into_any_element(),
375        )
376    }
377}