Skip to main content

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}