damascene-core 0.4.4

Damascene — backend-agnostic UI library core
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
//! Content-related [`El`] modifiers: text runs, icon source, and raster image source.

// Lock in full per-item documentation for this module (issue #73).
#![warn(missing_docs)]

use crate::image::{DynamicRangeLimit, Image, ImageFit};

use super::layout_types::Size;
use super::node::El;
use super::semantics::Kind;
use super::text_types::{FontFamily, FontWeight, TextAlign, TextOverflow, TextRole, TextWrap};
use crate::color::Color;

impl El {
    // ---- Text-bearing ----
    /// Set this element's text content.
    pub fn text(mut self, t: impl Into<String>) -> Self {
        self.text = Some(t.into());
        self
    }

    /// Override the themed text color.
    pub fn text_color(mut self, c: Color) -> Self {
        self.text_color = Some(c);
        self
    }

    /// Horizontal alignment of the text within its layout box.
    pub fn text_align(mut self, align: TextAlign) -> Self {
        self.text_align = align;
        self
    }

    /// Shorthand for `.text_align(TextAlign::Center)`.
    pub fn center_text(self) -> Self {
        self.text_align(TextAlign::Center)
    }

    /// Shorthand for `.text_align(TextAlign::End)`.
    pub fn end_text(self) -> Self {
        self.text_align(TextAlign::End)
    }

    /// Set whether the text wraps onto multiple lines.
    pub fn text_wrap(mut self, wrap: TextWrap) -> Self {
        self.text_wrap = wrap;
        self
    }

    /// Shorthand for `.text_wrap(TextWrap::Wrap)`.
    pub fn wrap_text(self) -> Self {
        self.text_wrap(TextWrap::Wrap)
    }

    /// Shorthand for `.text_wrap(TextWrap::NoWrap)`.
    pub fn nowrap_text(self) -> Self {
        self.text_wrap(TextWrap::NoWrap)
    }

    /// Set how text that exceeds its box is treated (clip vs. ellipsis).
    pub fn text_overflow(mut self, overflow: TextOverflow) -> Self {
        self.text_overflow = overflow;
        self
    }

    /// Shorthand for `.text_overflow(TextOverflow::Ellipsis)`.
    pub fn ellipsis(self) -> Self {
        self.text_overflow(TextOverflow::Ellipsis)
    }

    /// Cap wrapped text at `lines` lines (clamped to at least 1).
    pub fn max_lines(mut self, lines: usize) -> Self {
        self.text_max_lines = Some(lines.max(1));
        self
    }

    /// Font size in logical px. Also re-derives the line height from
    /// the size→line-height token curve; chain [`Self::line_height`]
    /// afterwards to override it.
    pub fn font_size(mut self, s: f32) -> Self {
        self.font_size = s;
        self.line_height = crate::tokens::line_height_for_size(s);
        self
    }

    /// Explicit line height in logical px (clamped to at least 1).
    pub fn line_height(mut self, h: f32) -> Self {
        self.line_height = h.max(1.0);
        self
    }

    /// Set the font weight.
    pub fn font_weight(mut self, w: FontWeight) -> Self {
        self.font_weight = w;
        self
    }

    /// Set the proportional font family. Setting this pins the node —
    /// theme font-family propagation no longer stamps over it.
    pub fn font_family(mut self, family: FontFamily) -> Self {
        self.font_family = family;
        self.explicit_font_family = true;
        self
    }

    /// Shorthand for `.font_family(FontFamily::Inter)`.
    pub fn inter(self) -> Self {
        self.font_family(FontFamily::Inter)
    }

    /// Shorthand for `.font_family(FontFamily::Roboto)`.
    pub fn roboto(self) -> Self {
        self.font_family(FontFamily::Roboto)
    }

    /// Override the monospace face used when this node renders as code
    /// (`font_mono = true`, `TextRole::Code`, or any descendant that
    /// inherits the value through theme propagation). Setting this
    /// pins the node — theme `with_mono_font_family(...)` no longer
    /// stamps over it.
    pub fn mono_font_family(mut self, family: FontFamily) -> Self {
        self.mono_font_family = family;
        self.explicit_mono_font_family = true;
        self
    }

    /// Pin this node's monospace face to JetBrains Mono. Convenience
    /// shorthand for `.mono_font_family(FontFamily::JetBrainsMono)`.
    pub fn jetbrains_mono(self) -> Self {
        self.mono_font_family(FontFamily::JetBrainsMono)
    }

    /// Set the icon for this element to either a built-in [`crate::IconName`],
    /// an app-supplied [`crate::SvgIcon`], or a string-typed name from
    /// the built-in vocabulary.
    pub fn icon_source(mut self, source: impl crate::icons::svg::IntoIconSource) -> Self {
        self.icon = Some(source.into_icon_source());
        self
    }

    /// Convenience alias for [`Self::icon_source`] preserved for call
    /// sites that want the historical name.
    pub fn icon_name(self, source: impl crate::icons::svg::IntoIconSource) -> Self {
        self.icon_source(source)
    }

    /// Stroke width for the icon's outline geometry, in the icon's
    /// 24-unit design space (clamped to at least 0.25). Default 2.0.
    pub fn icon_stroke_width(mut self, width: f32) -> Self {
        self.icon_stroke_width = width.max(0.25);
        self
    }

    /// Set the icon glyph metric (`font_size` + `line_height`) for this
    /// element, and — when this element *is* an icon — its layout box.
    ///
    /// Kind-aware: width/height are only assigned when `self.kind` is
    /// `Kind::Custom("icon")`. On container kinds like
    /// [`crate::button_with_icon`] or `Kind::Custom("icon_button")`,
    /// only `font_size` / `line_height` are set — chaining
    /// `.icon_size(...)` no longer collapses the outer rect.
    ///
    /// Propagates to direct children whose `kind` is `Kind::Custom("icon")`
    /// so a caller's `.icon_size(...)` on a container widget resizes the
    /// inner icon child too. Containers that hold the icon deeper than
    /// one level (or behind a layout wrapper) need to set the icon size
    /// at the icon site directly.
    pub fn icon_size(mut self, size: f32) -> Self {
        let size = size.max(1.0);
        self.font_size = size;
        self.line_height = size;
        if matches!(&self.kind, Kind::Custom(name) if *name == "icon") {
            self.width = Size::Fixed(size);
            self.height = Size::Fixed(size);
            self.explicit_width = true;
            self.explicit_height = true;
        }
        for child in self.children.iter_mut() {
            if matches!(&child.kind, Kind::Custom(name) if *name == "icon") {
                child.font_size = size;
                child.line_height = size;
                child.width = Size::Fixed(size);
                child.height = Size::Fixed(size);
                child.explicit_width = true;
                child.explicit_height = true;
            }
        }
        self
    }

    /// Attach a raster image. Usually you'll want the [`crate::image`]
    /// free builder instead, which sets [`crate::Kind::Image`] for you; this
    /// method exists for cases where you've already constructed an El
    /// (e.g. through a stock widget) and want to swap in pixel art.
    pub fn image(mut self, image: impl Into<Image>) -> Self {
        self.image = Some(image.into());
        self
    }

    /// How the raster image projects into the El's rect (mirrors CSS
    /// `object-fit`). Defaults to [`ImageFit::Contain`].
    pub fn image_fit(mut self, fit: ImageFit) -> Self {
        self.image_fit = fit;
        self
    }

    /// How much of the output's HDR headroom this image may use
    /// (mirrors CSS `dynamic-range-limit`). Defaults to
    /// [`DynamicRangeLimit::NoLimit`] — the image uses the panel's full
    /// headroom, remastered (hue-preserving BT.2390 roll-off) when its
    /// content peaks brighter than the panel can show. `ConstrainedHigh`
    /// bounds HDR brights for grids/feeds; `Standard` tonemaps to SDR.
    pub fn dynamic_range_limit(mut self, limit: DynamicRangeLimit) -> Self {
        self.image_range_limit = limit;
        self
    }

    /// Tint color carried on the image draw op (combined with the El's
    /// resolved opacity) for backends to apply when sampling.
    pub fn image_tint(mut self, c: Color) -> Self {
        self.image_tint = Some(c);
        self
    }

    /// Attach an app-owned GPU texture source. Typically set via the
    /// [`crate::tree::surface`] builder (which also sets
    /// [`crate::Kind::Surface`]); reach for this method on a stock
    /// widget El whose Kind you want to keep.
    pub fn surface_source(mut self, source: crate::surface::SurfaceSource) -> Self {
        self.surface_source = Some(source);
        self
    }

    /// Attach a 3D scene specification. Typically set via the
    /// [`crate::tree::chart3d`] builder (which also sets
    /// [`crate::Kind::Scene3D`]).
    pub fn scene_source(mut self, scene: crate::scene::SceneSpec) -> Self {
        self.scene_source = Some(Box::new(scene));
        self
    }

    /// Attach a 2D plot specification. Typically set via the
    /// [`crate::tree::plot`] builder (which also sets [`crate::Kind::Plot`]).
    pub fn plot_source(mut self, plot: crate::plot::PlotSpec) -> Self {
        self.plot_source = Some(Box::new(plot));
        self
    }

    /// How a [`crate::Kind::Surface`] El composes with widgets below
    /// it. Default is [`crate::surface::SurfaceAlpha::Premultiplied`].
    pub fn surface_alpha(mut self, alpha: crate::surface::SurfaceAlpha) -> Self {
        self.surface_alpha = alpha;
        self
    }

    /// How a [`crate::Kind::Surface`] El's texture projects into its
    /// resolved rect. Defaults to [`crate::image::ImageFit::Fill`] —
    /// stretch to the rect — for parity with the pre-`surface_fit`
    /// behaviour. `Contain` / `Cover` / `None` mirror the modes on
    /// [`crate::El::image_fit`].
    pub fn surface_fit(mut self, fit: crate::image::ImageFit) -> Self {
        self.surface_fit = fit;
        self
    }

    /// Affine applied to the texture quad in destination space, around
    /// the centre of the post-[`Self::surface_fit`] rect. Defaults to
    /// identity. Use this for rotation, mirroring, source-dimension-
    /// independent zoom/pan, or any combination thereof. The El's
    /// auto-clip scissor still clamps the rendered content to the
    /// resolved rect.
    pub fn surface_transform(mut self, transform: crate::affine::Affine2) -> Self {
        self.surface_transform = transform;
        self
    }

    /// Attach a vector asset source. Typically set via the
    /// [`crate::tree::vector`] builder (which also sets
    /// [`crate::Kind::Vector`]); reach for this method on a stock
    /// widget El whose Kind you want to keep.
    pub fn vector_source(
        mut self,
        asset: impl Into<std::sync::Arc<crate::vector::VectorAsset>>,
    ) -> Self {
        self.vector_source = Some(asset.into());
        self
    }

    /// Select how a vector asset should render. The default is
    /// [`crate::vector::VectorRenderMode::Painted`], which preserves
    /// authored fills/strokes/gradients. Use [`Self::vector_mask`] when
    /// the asset is intended as one-colour coverage geometry.
    pub fn vector_render_mode(mut self, mode: crate::vector::VectorRenderMode) -> Self {
        self.vector_render_mode = mode;
        self
    }

    /// Treat this vector as coverage geometry and paint it with one
    /// colour. Backends can render this through their MSDF path.
    pub fn vector_mask(self, color: Color) -> Self {
        self.vector_render_mode(crate::vector::VectorRenderMode::Mask { color })
    }

    /// Preserve authored vector paint. This is the default for
    /// [`crate::tree::vector`].
    pub fn vector_painted(self) -> Self {
        self.vector_render_mode(crate::vector::VectorRenderMode::Painted)
    }

    /// Inside-out redraw deadline. While this El is visible (rect
    /// intersects the viewport), Damascene asks the host to drive the next
    /// frame within `deadline`. Aggregated across the tree via `min`,
    /// so the host gets a single signal regardless of how many widgets
    /// are asking. Use `Duration::ZERO` for "next frame ASAP";
    /// non-zero values pace the redraw loop below the display rate.
    ///
    /// Apps that pause / resume animation (e.g. GIF playback) just
    /// stop calling this method on the relevant El — Damascene re-runs
    /// the aggregation each frame, so the redraw scheduler quiets
    /// automatically when no visible widget is asking.
    pub fn redraw_within(mut self, deadline: std::time::Duration) -> Self {
        self.redraw_within = Some(deadline);
        self
    }

    /// Opt this node into the monospace face. Setting this flag also
    /// sets [`El::explicit_mono`] so a subsequent role modifier
    /// (`.caption()` / `.label()` / `.body()` / `.title()` /
    /// `.heading()` / `.display()`) won't silently reset `font_mono`
    /// when the role's default is non-mono. The natural reading order
    /// `text(s).mono().caption()` therefore renders in mono.
    pub fn mono(mut self) -> Self {
        self.font_mono = true;
        self.explicit_mono = true;
        self
    }

    /// Italic styling for a text run. Honoured by the
    /// [`crate::Kind::Inlines`] layout pass and (best-effort) on
    /// standalone text Els.
    pub fn italic(mut self) -> Self {
        self.text_italic = true;
        self
    }

    /// Inline-run background. Honoured when this El is a styled text
    /// leaf inside an [`crate::Kind::Inlines`] parent: the shaped span
    /// paints a solid quad behind its glyphs (per-line if the span
    /// wraps). Mirrors HTML's `<mark>` / inline `background`; the rect
    /// tracks the glyph extent rather than the El's layout box, so a
    /// wrapped highlight follows the prose. No effect on standalone
    /// text Els.
    pub fn background(mut self, color: Color) -> Self {
        self.text_bg = Some(color);
        self
    }

    /// Underline styling for a text run.
    pub fn underline(mut self) -> Self {
        self.text_underline = true;
        self
    }

    /// Strikethrough styling for a text run.
    pub fn strikethrough(mut self) -> Self {
        self.text_strikethrough = true;
        self
    }

    /// Shape digits with tabular figures (OpenType `tnum`) so every
    /// digit takes the same advance — the CSS `font-variant-numeric:
    /// tabular-nums` shape. Use on clocks, counters, and numeric table
    /// columns so values don't jitter horizontally as digits change.
    /// Honoured by fonts that carry the feature (the bundled Inter
    /// does); a graceful no-op otherwise. Applies to layout
    /// measurement and paint consistently.
    pub fn tabular_numerals(mut self) -> Self {
        self.text_tabular_numerals = true;
        self
    }

    /// Markdown-flavoured inline-code styling. Currently `mono`-styled;
    /// a tinted background per the theme is a future addition. Authors
    /// who want raw mono without code chrome should use [`Self::mono`]
    /// instead.
    pub fn code(self) -> Self {
        self.text_role(TextRole::Code)
    }

    /// Mark this run as a link to `url`. Inside an
    /// [`crate::Kind::Inlines`] parent the run paints with a
    /// link-themed color; runs sharing the same URL group together for
    /// hit-test.
    pub fn link(mut self, url: impl Into<String>) -> Self {
        self.text_link = Some(url.into());
        self
    }

    /// Attach a math expression. Typically set via the math builders
    /// (which also set [`crate::Kind::Math`]).
    pub fn math_expr(mut self, expr: impl Into<std::sync::Arc<crate::math::MathExpr>>) -> Self {
        self.math = Some(expr.into());
        self
    }

    /// Inline vs. block (display-style) math layout. Defaults to
    /// [`crate::math::MathDisplay::Inline`].
    pub fn math_display(mut self, display: crate::math::MathDisplay) -> Self {
        self.math_display = display;
        self
    }
}