armas_basic/components/
icon_button.rs1use crate::components::ButtonVariant;
6use crate::ext::ArmasContextExt;
7use crate::icon::{render_icon_data, IconData, OwnedIconData};
8use egui::{Color32, Response, Sense, Ui, Vec2};
9
10pub struct IconButton<'a> {
37 vertices: &'a [(f32, f32)],
38 indices: &'a [u32],
39 viewbox_width: f32,
40 viewbox_height: f32,
41 variant: ButtonVariant,
42 size: f32,
43 padding: f32,
44 enabled: bool,
45 icon_color: Option<Color32>,
46 hover_icon_color: Option<Color32>,
47}
48
49impl<'a> IconButton<'a> {
50 #[must_use]
52 pub const fn new(icon_data: &'a IconData) -> Self {
53 Self {
54 vertices: icon_data.vertices,
55 indices: icon_data.indices,
56 viewbox_width: icon_data.viewbox_width,
57 viewbox_height: icon_data.viewbox_height,
58 variant: ButtonVariant::Default,
59 size: 24.0,
60 padding: 8.0,
61 enabled: true,
62 icon_color: None,
63 hover_icon_color: None,
64 }
65 }
66
67 #[must_use]
69 pub fn from_owned(data: &'a OwnedIconData) -> Self {
70 Self {
71 vertices: &data.vertices,
72 indices: &data.indices,
73 viewbox_width: data.viewbox_width,
74 viewbox_height: data.viewbox_height,
75 variant: ButtonVariant::Default,
76 size: 24.0,
77 padding: 8.0,
78 enabled: true,
79 icon_color: None,
80 hover_icon_color: None,
81 }
82 }
83
84 #[must_use]
86 pub const fn variant(mut self, variant: ButtonVariant) -> Self {
87 self.variant = variant;
88 self
89 }
90
91 #[must_use]
93 pub const fn size(mut self, size: f32) -> Self {
94 self.size = size;
95 self
96 }
97
98 #[must_use]
100 pub const fn padding(mut self, padding: f32) -> Self {
101 self.padding = padding;
102 self
103 }
104
105 #[must_use]
107 pub const fn enabled(mut self, enabled: bool) -> Self {
108 self.enabled = enabled;
109 self
110 }
111
112 #[must_use]
114 pub const fn icon_color(mut self, color: Color32) -> Self {
115 self.icon_color = Some(color);
116 self
117 }
118
119 #[must_use]
121 pub const fn hover_icon_color(mut self, color: Color32) -> Self {
122 self.hover_icon_color = Some(color);
123 self
124 }
125
126 pub fn show(self, ui: &mut Ui) -> Response {
128 let theme = ui.ctx().armas_theme();
129 let total_size = Vec2::splat(self.size + self.padding * 2.0);
130
131 let sense = if self.enabled {
132 Sense::click()
133 } else {
134 Sense::hover()
135 };
136
137 let (rect, response) = ui.allocate_exact_size(total_size, sense);
138
139 if ui.is_rect_visible(rect) {
140 let (bg_color, mut icon_color) = match self.variant {
142 ButtonVariant::Default => {
143 let bg = if response.is_pointer_button_down_on() {
144 theme.primary().linear_multiply(0.9)
145 } else if response.hovered() {
146 theme.primary().linear_multiply(1.08)
147 } else {
148 theme.primary()
149 };
150 (Some(bg), theme.primary_foreground())
151 }
152 ButtonVariant::Secondary => {
153 let bg = if response.is_pointer_button_down_on() {
154 theme.secondary()
155 } else if response.hovered() {
156 theme.secondary().linear_multiply(1.08)
157 } else {
158 theme.secondary()
159 };
160 (Some(bg), theme.secondary_foreground())
161 }
162 ButtonVariant::Outline | ButtonVariant::Ghost | ButtonVariant::Link => {
163 let bg = if response.hovered() {
164 Some(theme.accent())
165 } else {
166 None
167 };
168 let icon = if response.hovered() {
169 theme.accent_foreground()
170 } else {
171 theme.foreground()
172 };
173 (bg, icon)
174 }
175 };
176
177 if response.hovered() {
179 if let Some(custom_hover_color) = self.hover_icon_color {
180 icon_color = custom_hover_color;
181 }
182 } else if let Some(custom_color) = self.icon_color {
183 icon_color = custom_color;
184 }
185
186 if !self.enabled {
188 icon_color = icon_color.linear_multiply(0.5);
189 }
190
191 if let Some(bg) = bg_color {
193 let rounding = match self.variant {
194 ButtonVariant::Default | ButtonVariant::Secondary => total_size.x / 2.0, _ => 6.0, };
197 let final_bg = if self.enabled {
198 bg
199 } else {
200 bg.linear_multiply(0.5)
201 };
202 ui.painter().rect_filled(rect, rounding, final_bg);
203 }
204
205 if self.variant == ButtonVariant::Outline {
207 let stroke = egui::Stroke::new(1.0, theme.border());
208 ui.painter()
209 .rect_stroke(rect, 6.0, stroke, egui::epaint::StrokeKind::Inside);
210 }
211
212 let icon_rect = egui::Rect::from_center_size(rect.center(), Vec2::splat(self.size));
214 render_icon_data(
215 ui.painter(),
216 icon_rect,
217 self.vertices,
218 self.indices,
219 self.viewbox_width,
220 self.viewbox_height,
221 icon_color,
222 );
223 }
224
225 response
226 }
227}