Skip to main content

sequoia_view/
icon.rs

1use sequoia_assets::{AssetRegistry, icon_key};
2use sequoia_core::{Color, Edges, Length, Rect, Size};
3use sequoia_layout::{Indents, LayoutCtx, LayoutStyle};
4
5use crate::view::{PaintCtx, View};
6use crate::views::Unit;
7
8const DEFAULT_SIZE: f32 = 16.0;
9
10pub struct Icon {
11    name: String,
12    size: Option<f32>,
13    color: Option<Color>,
14    style: LayoutStyle,
15}
16
17impl Icon {
18    pub fn new(name: impl Into<String>) -> Self {
19        Self {
20            name: name.into(),
21            size: None,
22            color: None,
23            style: LayoutStyle {
24                width: Length::Fit,
25                height: Length::Fit,
26                ..LayoutStyle::default()
27            },
28        }
29    }
30
31    pub fn size(mut self, px: f32) -> Self {
32        self.size = Some(px);
33        self
34    }
35
36    pub fn color(mut self, color: Color) -> Self {
37        self.color = Some(color);
38        self
39    }
40
41    pub fn width(mut self, width: impl Into<Length>) -> Self {
42        self.style = self.style.width(width.into());
43        self
44    }
45
46    pub fn height(mut self, height: impl Into<Length>) -> Self {
47        self.style = self.style.height(height.into());
48        self
49    }
50
51    pub fn min_width(mut self, v: f32) -> Self {
52        self.style = self.style.min_width(v);
53        self
54    }
55
56    pub fn max_width(mut self, v: f32) -> Self {
57        self.style = self.style.max_width(v);
58        self
59    }
60
61    pub fn min_height(mut self, v: f32) -> Self {
62        self.style = self.style.min_height(v);
63        self
64    }
65
66    pub fn max_height(mut self, v: f32) -> Self {
67        self.style = self.style.max_height(v);
68        self
69    }
70
71    pub fn flex(mut self, weight: f32) -> Self {
72        self.style = self.style.flex(weight);
73        self
74    }
75
76    pub fn expand(self) -> Self {
77        self.flex(1.0)
78    }
79
80    pub fn fill_width(mut self) -> Self {
81        self.style = self.style.width(Length::Fill(1.0));
82        self
83    }
84
85    pub fn fill_height(mut self) -> Self {
86        self.style = self.style.height(Length::Fill(1.0));
87        self
88    }
89
90    pub fn fill_size(self) -> Self {
91        self.fill_width().fill_height()
92    }
93
94    pub fn padding(mut self, padding: impl Into<Edges>) -> Self {
95        self.style = self.style.padding(padding);
96        self
97    }
98
99    pub fn padding_xy(mut self, x: f32, y: f32) -> Self {
100        self.style = self.style.padding_xy(x, y);
101        self
102    }
103
104    pub fn padding_x(mut self, x: f32) -> Self {
105        self.style = self.style.padding_x(x);
106        self
107    }
108
109    pub fn padding_y(mut self, y: f32) -> Self {
110        self.style = self.style.padding_y(y);
111        self
112    }
113
114    pub fn margin(mut self, margin: impl Into<Edges>) -> Self {
115        self.style = self.style.margin(margin);
116        self
117    }
118
119    pub fn margin_xy(mut self, x: f32, y: f32) -> Self {
120        self.style = self.style.margin_xy(x, y);
121        self
122    }
123
124    pub fn margin_x(mut self, x: f32) -> Self {
125        self.style = self.style.margin_x(x);
126        self
127    }
128
129    pub fn margin_y(mut self, y: f32) -> Self {
130        self.style = self.style.margin_y(y);
131        self
132    }
133
134    pub fn margin_left(mut self, v: f32) -> Self {
135        self.style = self.style.margin_left(v);
136        self
137    }
138
139    pub fn margin_right(mut self, v: f32) -> Self {
140        self.style = self.style.margin_right(v);
141        self
142    }
143
144    pub fn margin_top(mut self, v: f32) -> Self {
145        self.style = self.style.margin_top(v);
146        self
147    }
148
149    pub fn margin_bottom(mut self, v: f32) -> Self {
150        self.style = self.style.margin_bottom(v);
151        self
152    }
153
154    pub fn indent_top(mut self, v: f32) -> Self {
155        self.style = self.style.indent_top(v);
156        self
157    }
158
159    pub fn indent_bottom(mut self, v: f32) -> Self {
160        self.style = self.style.indent_bottom(v);
161        self
162    }
163
164    pub fn indent_left(mut self, v: f32) -> Self {
165        self.style = self.style.indent_left(v);
166        self
167    }
168
169    pub fn indent_right(mut self, v: f32) -> Self {
170        self.style = self.style.indent_right(v);
171        self
172    }
173
174    pub fn clear_indents(mut self) -> Self {
175        self.style = self.style.clear_indents();
176        self
177    }
178
179    pub fn indent(mut self, indents: Indents) -> Self {
180        self.style = self.style.indent(indents);
181        self
182    }
183
184    pub fn indent_all(mut self, v: f32) -> Self {
185        self.style = self.style.indent_all(v);
186        self
187    }
188
189    pub fn indent_xy(mut self, x: f32, y: f32) -> Self {
190        self.style = self.style.indent_xy(x, y);
191        self
192    }
193
194    pub fn indent_axes(mut self, horizontal: f32, vertical: f32) -> Self {
195        self.style = self.style.indent_axes(horizontal, vertical);
196        self
197    }
198
199    pub fn hit_testable(mut self, on: bool) -> Self {
200        self.style = self.style.hit_testable(on);
201        self
202    }
203
204    fn px(&self) -> f32 {
205        self.size.unwrap_or(DEFAULT_SIZE)
206    }
207}
208
209impl View for Icon {
210    fn body(&self) -> impl View + 'static {
211        Unit
212    }
213    fn is_leaf(&self) -> bool {
214        true
215    }
216    fn layout_style(&self) -> LayoutStyle {
217        self.style
218    }
219    fn intrinsic_size(&self, _ctx: &mut LayoutCtx) -> Option<Size> {
220        let px = self.px();
221        let pad = self.style.padding;
222        Some(Size::new(
223            px + pad.horizontal_sum(),
224            px + pad.vertical_sum(),
225        ))
226    }
227    fn paint_self(&self, ctx: &mut PaintCtx) {
228        let Some(assets) = ctx.environment.get::<AssetRegistry>() else {
229            return;
230        };
231        let px = self.px();
232
233        let raster_px = ((px * ctx.text.scale_factor()).round() as u32).max(1);
234        let Ok(sprite) = assets.rasterize_icon(&self.name, raster_px) else {
235            return;
236        };
237        let pad = self.style.padding;
238        let r = Rect::from_xywh(
239            ctx.rect.left() + pad.left,
240            ctx.rect.top() + pad.top,
241            (ctx.rect.size.width - pad.horizontal_sum()).max(0.0),
242            (ctx.rect.size.height - pad.vertical_sum()).max(0.0),
243        );
244        let dst = Rect::from_xywh(
245            r.left() + (r.size.width - px) * 0.5,
246            r.top() + (r.size.height - px) * 0.5,
247            px,
248            px,
249        );
250        let color = self.color.unwrap_or_else(|| ctx.theme().text);
251        ctx.draw_sprite(
252            icon_key(&self.name, raster_px),
253            sprite.width,
254            sprite.height,
255            &sprite.alpha,
256            dst,
257            color,
258        );
259    }
260}