1#![allow(missing_docs)]
2use gpui::{AnyView, DefiniteLength, Hsla};
3
4use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle};
5use crate::{prelude::*, ElevationIndex, Indicator, SelectableButton};
6use crate::{IconName, IconSize};
7
8use super::button_icon::ButtonIcon;
9
10/// The shape of an [`IconButton`].
11#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
12pub enum IconButtonShape {
13 Square,
14 Wide,
15}
16
17#[derive(IntoElement)]
18pub struct IconButton {
19 base: ButtonLike,
20 shape: IconButtonShape,
21 icon: IconName,
22 icon_size: IconSize,
23 icon_color: Color,
24 selected_icon: Option<IconName>,
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 indicator: None,
40 indicator_border_color: None,
41 alpha: None,
42 };
43 this.base.base = this.base.base.debug_selector(|| format!("ICON-{:?}", icon));
44 this
45 }
46
47 pub fn shape(mut self, shape: IconButtonShape) -> Self {
48 self.shape = shape;
49 self
50 }
51
52 pub fn icon_size(mut self, icon_size: IconSize) -> Self {
53 self.icon_size = icon_size;
54 self
55 }
56
57 pub fn icon_color(mut self, icon_color: Color) -> Self {
58 self.icon_color = icon_color;
59 self
60 }
61
62 pub fn alpha(mut self, alpha: f32) -> Self {
63 self.alpha = Some(alpha);
64 self
65 }
66
67 pub fn selected_icon(mut self, icon: impl Into<Option<IconName>>) -> Self {
68 self.selected_icon = icon.into();
69 self
70 }
71
72 pub fn indicator(mut self, indicator: Indicator) -> Self {
73 self.indicator = Some(indicator);
74 self
75 }
76
77 pub fn indicator_border_color(mut self, color: Option<Hsla>) -> Self {
78 self.indicator_border_color = color;
79 self
80 }
81}
82
83impl Disableable for IconButton {
84 fn disabled(mut self, disabled: bool) -> Self {
85 self.base = self.base.disabled(disabled);
86 self
87 }
88}
89
90impl Toggleable for IconButton {
91 fn toggle_state(mut self, selected: bool) -> Self {
92 self.base = self.base.toggle_state(selected);
93 self
94 }
95}
96
97impl SelectableButton for IconButton {
98 fn selected_style(mut self, style: ButtonStyle) -> Self {
99 self.base = self.base.selected_style(style);
100 self
101 }
102}
103
104impl Clickable for IconButton {
105 fn on_click(
106 mut self,
107 handler: impl Fn(&gpui::ClickEvent, &mut Window, &mut App) + 'static,
108 ) -> Self {
109 self.base = self.base.on_click(handler);
110 self
111 }
112
113 fn cursor_style(mut self, cursor_style: gpui::CursorStyle) -> Self {
114 self.base = self.base.cursor_style(cursor_style);
115 self
116 }
117}
118
119impl FixedWidth for IconButton {
120 fn width(mut self, width: DefiniteLength) -> Self {
121 self.base = self.base.width(width);
122 self
123 }
124
125 fn full_width(mut self) -> Self {
126 self.base = self.base.full_width();
127 self
128 }
129}
130
131impl ButtonCommon for IconButton {
132 fn id(&self) -> &ElementId {
133 self.base.id()
134 }
135
136 fn style(mut self, style: ButtonStyle) -> Self {
137 self.base = self.base.style(style);
138 self
139 }
140
141 fn size(mut self, size: ButtonSize) -> Self {
142 self.base = self.base.size(size);
143 self
144 }
145
146 fn tooltip(mut self, tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static) -> Self {
147 self.base = self.base.tooltip(tooltip);
148 self
149 }
150
151 fn layer(mut self, elevation: ElevationIndex) -> Self {
152 self.base = self.base.layer(elevation);
153 self
154 }
155}
156
157impl VisibleOnHover for IconButton {
158 fn visible_on_hover(mut self, group_name: impl Into<SharedString>) -> Self {
159 self.base = self.base.visible_on_hover(group_name);
160 self
161 }
162}
163
164impl RenderOnce for IconButton {
165 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
166 let is_disabled = self.base.disabled;
167 let is_selected = self.base.selected;
168 let selected_style = self.base.selected_style;
169
170 let color = self.icon_color.color(cx).opacity(self.alpha.unwrap_or(1.0));
171 self.base
172 .map(|this| match self.shape {
173 IconButtonShape::Square => {
174 let size = self.icon_size.square(window, cx);
175 this.width(size.into()).height(size.into())
176 }
177 IconButtonShape::Wide => this,
178 })
179 .child(
180 ButtonIcon::new(self.icon)
181 .disabled(is_disabled)
182 .toggle_state(is_selected)
183 .selected_icon(self.selected_icon)
184 .when_some(selected_style, |this, style| this.selected_style(style))
185 .when_some(self.indicator, |this, indicator| {
186 this.indicator(indicator)
187 .indicator_border_color(self.indicator_border_color)
188 })
189 .size(self.icon_size)
190 .color(Color::Custom(color)),
191 )
192 }
193}