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