aetna_core/tree/content.rs
1//! Content-related [`El`] modifiers: text runs, icon source, and raster image source.
2
3use crate::image::{Image, ImageFit};
4
5use super::color::Color;
6use super::layout_types::Size;
7use super::node::El;
8use super::text_types::{FontFamily, FontWeight, TextAlign, TextOverflow, TextRole, TextWrap};
9
10impl El {
11 // ---- Text-bearing ----
12 pub fn text(mut self, t: impl Into<String>) -> Self {
13 self.text = Some(t.into());
14 self
15 }
16
17 pub fn text_color(mut self, c: Color) -> Self {
18 self.text_color = Some(c);
19 self
20 }
21
22 pub fn text_align(mut self, align: TextAlign) -> Self {
23 self.text_align = align;
24 self
25 }
26
27 pub fn center_text(self) -> Self {
28 self.text_align(TextAlign::Center)
29 }
30
31 pub fn end_text(self) -> Self {
32 self.text_align(TextAlign::End)
33 }
34
35 pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
36 self.text_wrap = wrap;
37 self
38 }
39
40 pub fn wrap_text(self) -> Self {
41 self.text_wrap(TextWrap::Wrap)
42 }
43
44 pub fn nowrap_text(self) -> Self {
45 self.text_wrap(TextWrap::NoWrap)
46 }
47
48 pub fn text_overflow(mut self, overflow: TextOverflow) -> Self {
49 self.text_overflow = overflow;
50 self
51 }
52
53 pub fn ellipsis(self) -> Self {
54 self.text_overflow(TextOverflow::Ellipsis)
55 }
56
57 pub fn max_lines(mut self, lines: usize) -> Self {
58 self.text_max_lines = Some(lines.max(1));
59 self
60 }
61
62 pub fn font_size(mut self, s: f32) -> Self {
63 self.font_size = s;
64 self.line_height = crate::tokens::line_height_for_size(s);
65 self
66 }
67
68 pub fn line_height(mut self, h: f32) -> Self {
69 self.line_height = h.max(1.0);
70 self
71 }
72
73 pub fn font_weight(mut self, w: FontWeight) -> Self {
74 self.font_weight = w;
75 self
76 }
77
78 pub fn font_family(mut self, family: FontFamily) -> Self {
79 self.font_family = family;
80 self.explicit_font_family = true;
81 self
82 }
83
84 pub fn inter(self) -> Self {
85 self.font_family(FontFamily::Inter)
86 }
87
88 pub fn roboto(self) -> Self {
89 self.font_family(FontFamily::Roboto)
90 }
91
92 /// Override the monospace face used when this node renders as code
93 /// (`font_mono = true`, `TextRole::Code`, or any descendant that
94 /// inherits the value through theme propagation). Setting this
95 /// pins the node — theme `with_mono_font_family(...)` no longer
96 /// stamps over it.
97 pub fn mono_font_family(mut self, family: FontFamily) -> Self {
98 self.mono_font_family = family;
99 self.explicit_mono_font_family = true;
100 self
101 }
102
103 /// Pin this node's monospace face to JetBrains Mono. Convenience
104 /// shorthand for `.mono_font_family(FontFamily::JetBrainsMono)`.
105 pub fn jetbrains_mono(self) -> Self {
106 self.mono_font_family(FontFamily::JetBrainsMono)
107 }
108
109 /// Set the icon for this element to either a built-in [`crate::IconName`],
110 /// an app-supplied [`crate::SvgIcon`], or a string-typed name from
111 /// the built-in vocabulary.
112 pub fn icon_source(mut self, source: impl crate::svg_icon::IntoIconSource) -> Self {
113 self.icon = Some(source.into_icon_source());
114 self
115 }
116
117 /// Convenience alias for [`Self::icon_source`] preserved for call
118 /// sites that want the historical name.
119 pub fn icon_name(self, source: impl crate::svg_icon::IntoIconSource) -> Self {
120 self.icon_source(source)
121 }
122
123 pub fn icon_stroke_width(mut self, width: f32) -> Self {
124 self.icon_stroke_width = width.max(0.25);
125 self
126 }
127
128 pub fn icon_size(mut self, size: f32) -> Self {
129 let size = size.max(1.0);
130 self.font_size = size;
131 self.line_height = size;
132 self.width = Size::Fixed(size);
133 self.height = Size::Fixed(size);
134 self.explicit_width = true;
135 self.explicit_height = true;
136 self
137 }
138
139 /// Attach a raster image. Usually you'll want the [`crate::image`]
140 /// free builder instead, which sets [`crate::Kind::Image`] for you; this
141 /// method exists for cases where you've already constructed an El
142 /// (e.g. through a stock widget) and want to swap in pixel art.
143 pub fn image(mut self, image: impl Into<Image>) -> Self {
144 self.image = Some(image.into());
145 self
146 }
147
148 pub fn image_fit(mut self, fit: ImageFit) -> Self {
149 self.image_fit = fit;
150 self
151 }
152
153 pub fn image_tint(mut self, c: Color) -> Self {
154 self.image_tint = Some(c);
155 self
156 }
157
158 /// Attach an app-owned GPU texture source. Typically set via the
159 /// [`crate::tree::surface`] builder (which also sets
160 /// [`crate::Kind::Surface`]); reach for this method on a stock
161 /// widget El whose Kind you want to keep.
162 pub fn surface_source(mut self, source: crate::surface::SurfaceSource) -> Self {
163 self.surface_source = Some(source);
164 self
165 }
166
167 /// How a [`crate::Kind::Surface`] El composes with widgets below
168 /// it. Default is [`crate::surface::SurfaceAlpha::Premultiplied`].
169 pub fn surface_alpha(mut self, alpha: crate::surface::SurfaceAlpha) -> Self {
170 self.surface_alpha = alpha;
171 self
172 }
173
174 /// How a [`crate::Kind::Surface`] El's texture projects into its
175 /// resolved rect. Defaults to [`crate::image::ImageFit::Fill`] —
176 /// stretch to the rect — for parity with the pre-`surface_fit`
177 /// behaviour. `Contain` / `Cover` / `None` mirror the modes on
178 /// [`crate::El::image_fit`].
179 pub fn surface_fit(mut self, fit: crate::image::ImageFit) -> Self {
180 self.surface_fit = fit;
181 self
182 }
183
184 /// Affine applied to the texture quad in destination space, around
185 /// the centre of the post-[`Self::surface_fit`] rect. Defaults to
186 /// identity. Use this for rotation, mirroring, source-dimension-
187 /// independent zoom/pan, or any combination thereof. The El's
188 /// auto-clip scissor still clamps the rendered content to the
189 /// resolved rect.
190 pub fn surface_transform(mut self, transform: crate::affine::Affine2) -> Self {
191 self.surface_transform = transform;
192 self
193 }
194
195 /// Attach a vector asset source. Typically set via the
196 /// [`crate::tree::vector`] builder (which also sets
197 /// [`crate::Kind::Vector`]); reach for this method on a stock
198 /// widget El whose Kind you want to keep.
199 pub fn vector_source(
200 mut self,
201 asset: impl Into<std::sync::Arc<crate::vector::VectorAsset>>,
202 ) -> Self {
203 self.vector_source = Some(asset.into());
204 self
205 }
206
207 /// Select how a vector asset should render. The default is
208 /// [`crate::vector::VectorRenderMode::Painted`], which preserves
209 /// authored fills/strokes/gradients. Use [`Self::vector_mask`] when
210 /// the asset is intended as one-colour coverage geometry.
211 pub fn vector_render_mode(mut self, mode: crate::vector::VectorRenderMode) -> Self {
212 self.vector_render_mode = mode;
213 self
214 }
215
216 /// Treat this vector as coverage geometry and paint it with one
217 /// colour. Backends can render this through their MSDF path.
218 pub fn vector_mask(self, color: Color) -> Self {
219 self.vector_render_mode(crate::vector::VectorRenderMode::Mask { color })
220 }
221
222 /// Preserve authored vector paint. This is the default for
223 /// [`crate::tree::vector`].
224 pub fn vector_painted(self) -> Self {
225 self.vector_render_mode(crate::vector::VectorRenderMode::Painted)
226 }
227
228 /// Inside-out redraw deadline. While this El is visible (rect
229 /// intersects the viewport), Aetna asks the host to drive the next
230 /// frame within `deadline`. Aggregated across the tree via `min`,
231 /// so the host gets a single signal regardless of how many widgets
232 /// are asking. Use `Duration::ZERO` for "next frame ASAP";
233 /// non-zero values pace the redraw loop below the display rate.
234 ///
235 /// Apps that pause / resume animation (e.g. GIF playback) just
236 /// stop calling this method on the relevant El — Aetna re-runs
237 /// the aggregation each frame, so the redraw scheduler quiets
238 /// automatically when no visible widget is asking.
239 pub fn redraw_within(mut self, deadline: std::time::Duration) -> Self {
240 self.redraw_within = Some(deadline);
241 self
242 }
243
244 /// Opt this node into the monospace face. Setting this flag also
245 /// sets [`El::explicit_mono`] so a subsequent role modifier
246 /// (`.caption()` / `.label()` / `.body()` / `.title()` /
247 /// `.heading()` / `.display()`) won't silently reset `font_mono`
248 /// when the role's default is non-mono. The natural reading order
249 /// `text(s).mono().caption()` therefore renders in mono.
250 pub fn mono(mut self) -> Self {
251 self.font_mono = true;
252 self.explicit_mono = true;
253 self
254 }
255
256 /// Italic styling for a text run. Honoured by the
257 /// [`crate::Kind::Inlines`] layout pass and (best-effort) on
258 /// standalone text Els.
259 pub fn italic(mut self) -> Self {
260 self.text_italic = true;
261 self
262 }
263
264 /// Inline-run background. Honoured when this El is a styled text
265 /// leaf inside an [`crate::Kind::Inlines`] parent: the shaped span
266 /// paints a solid quad behind its glyphs (per-line if the span
267 /// wraps). Mirrors HTML's `<mark>` / inline `background`; the rect
268 /// tracks the glyph extent rather than the El's layout box, so a
269 /// wrapped highlight follows the prose. No effect on standalone
270 /// text Els.
271 pub fn background(mut self, color: Color) -> Self {
272 self.text_bg = Some(color);
273 self
274 }
275
276 /// Underline styling for a text run.
277 pub fn underline(mut self) -> Self {
278 self.text_underline = true;
279 self
280 }
281
282 /// Strikethrough styling for a text run.
283 pub fn strikethrough(mut self) -> Self {
284 self.text_strikethrough = true;
285 self
286 }
287
288 /// Markdown-flavoured inline-code styling. Currently `mono`-styled;
289 /// a tinted background per the theme is a future addition. Authors
290 /// who want raw mono without code chrome should use [`Self::mono`]
291 /// instead.
292 pub fn code(self) -> Self {
293 self.text_role(TextRole::Code)
294 }
295
296 /// Mark this run as a link to `url`. Inside an
297 /// [`crate::Kind::Inlines`] parent the run paints with a
298 /// link-themed color; runs sharing the same URL group together for
299 /// hit-test.
300 pub fn link(mut self, url: impl Into<String>) -> Self {
301 self.text_link = Some(url.into());
302 self
303 }
304
305 pub fn math_expr(mut self, expr: impl Into<std::sync::Arc<crate::math::MathExpr>>) -> Self {
306 self.math = Some(expr.into());
307 self
308 }
309
310 pub fn math_display(mut self, display: crate::math::MathDisplay) -> Self {
311 self.math_display = display;
312 self
313 }
314}