1use gpui::{AnyView, DefiniteLength};
2
3use super::button_like::{ButtonCommon, ButtonLike, ButtonSize, ButtonStyle};
4use crate::{prelude::*, ElevationIndex, SelectableButton};
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)]
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}
25
26impl IconButton {
27 pub fn new(id: impl Into<ElementId>, icon: IconName) -> Self {
28 let mut this = Self {
29 base: ButtonLike::new(id),
30 shape: IconButtonShape::Wide,
31 icon,
32 icon_size: IconSize::default(),
33 icon_color: Color::Default,
34 selected_icon: None,
35 };
36 this.base.base = this.base.base.debug_selector(|| format!("ICON-{:?}", icon));
37 this
38 }
39
40 pub fn shape(mut self, shape: IconButtonShape) -> Self {
41 self.shape = shape;
42 self
43 }
44
45 pub fn icon_size(mut self, icon_size: IconSize) -> Self {
46 self.icon_size = icon_size;
47 self
48 }
49
50 pub fn icon_color(mut self, icon_color: Color) -> Self {
51 self.icon_color = icon_color;
52 self
53 }
54
55 pub fn selected_icon(mut self, icon: impl Into<Option<IconName>>) -> Self {
56 self.selected_icon = icon.into();
57 self
58 }
59}
60
61impl Disableable for IconButton {
62 fn disabled(mut self, disabled: bool) -> Self {
63 self.base = self.base.disabled(disabled);
64 self
65 }
66}
67
68impl Selectable for IconButton {
69 fn selected(mut self, selected: bool) -> Self {
70 self.base = self.base.selected(selected);
71 self
72 }
73}
74
75impl SelectableButton for IconButton {
76 fn selected_style(mut self, style: ButtonStyle) -> Self {
77 self.base = self.base.selected_style(style);
78 self
79 }
80}
81
82impl Clickable for IconButton {
83 fn on_click(
84 mut self,
85 handler: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
86 ) -> Self {
87 self.base = self.base.on_click(handler);
88 self
89 }
90
91 fn cursor_style(mut self, cursor_style: gpui::CursorStyle) -> Self {
92 self.base = self.base.cursor_style(cursor_style);
93 self
94 }
95}
96
97impl FixedWidth for IconButton {
98 fn width(mut self, width: DefiniteLength) -> Self {
99 self.base = self.base.width(width);
100 self
101 }
102
103 fn full_width(mut self) -> Self {
104 self.base = self.base.full_width();
105 self
106 }
107}
108
109impl ButtonCommon for IconButton {
110 fn id(&self) -> &ElementId {
111 self.base.id()
112 }
113
114 fn style(mut self, style: ButtonStyle) -> Self {
115 self.base = self.base.style(style);
116 self
117 }
118
119 fn size(mut self, size: ButtonSize) -> Self {
120 self.base = self.base.size(size);
121 self
122 }
123
124 fn tooltip(mut self, tooltip: impl Fn(&mut WindowContext) -> AnyView + 'static) -> Self {
125 self.base = self.base.tooltip(tooltip);
126 self
127 }
128
129 fn layer(mut self, elevation: ElevationIndex) -> Self {
130 self.base = self.base.layer(elevation);
131 self
132 }
133}
134
135impl VisibleOnHover for IconButton {
136 fn visible_on_hover(mut self, group_name: impl Into<SharedString>) -> Self {
137 self.base = self.base.visible_on_hover(group_name);
138 self
139 }
140}
141
142impl RenderOnce for IconButton {
143 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
144 let is_disabled = self.base.disabled;
145 let is_selected = self.base.selected;
146 let selected_style = self.base.selected_style;
147
148 self.base
149 .map(|this| match self.shape {
150 IconButtonShape::Square => {
151 let size = self.icon_size.square(cx);
152 this.width(size.into()).height(size.into())
153 }
154 IconButtonShape::Wide => this,
155 })
156 .child(
157 ButtonIcon::new(self.icon)
158 .disabled(is_disabled)
159 .selected(is_selected)
160 .selected_icon(self.selected_icon)
161 .when_some(selected_style, |this, style| this.selected_style(style))
162 .size(self.icon_size)
163 .color(self.icon_color),
164 )
165 }
166}