Skip to main content

aetna_core/tree/
visual_modifiers.rs

1//! Visual, cursor, and paint-transform modifiers for [`El`].
2
3use crate::anim::Timing;
4use crate::shader::ShaderBinding;
5use crate::style::StyleProfile;
6
7use super::color::Color;
8use super::geometry::{Corners, Sides};
9use super::node::El;
10use super::semantics::SurfaceRole;
11
12impl El {
13    // ---- Visual ----
14    pub fn fill(mut self, c: Color) -> Self {
15        self.fill = Some(c);
16        self
17    }
18
19    /// Fill applied when the nearest focusable ancestor isn't focused;
20    /// the painter lerps from `dim_fill` toward `fill` as the focus
21    /// envelope rises from 0 to 1. See [`Self::dim_fill`] field doc.
22    pub fn dim_fill(mut self, c: Color) -> Self {
23        self.dim_fill = Some(c);
24        self
25    }
26
27    pub fn stroke(mut self, c: Color) -> Self {
28        self.stroke = Some(c);
29        if self.stroke_width == 0.0 {
30            self.stroke_width = 1.0;
31        }
32        self
33    }
34
35    pub fn stroke_width(mut self, w: f32) -> Self {
36        self.stroke_width = w;
37        self
38    }
39
40    /// Set the element's corner radii. A scalar (e.g.
41    /// `.radius(tokens::RADIUS_MD)`) sets all four corners uniformly
42    /// via [`Corners::from`]; pass [`Corners::top`] / [`Corners::bottom`]
43    /// / [`Corners::left`] / [`Corners::right`], or a directly-built
44    /// [`Corners`], to round only a subset of corners.
45    pub fn radius(mut self, r: impl Into<Corners>) -> Self {
46        self.radius = r.into();
47        self.explicit_radius = true;
48        self
49    }
50
51    pub fn shadow(mut self, s: f32) -> Self {
52        self.shadow = s;
53        self
54    }
55
56    /// Tag this node with a semantic [`SurfaceRole`] so the theme can
57    /// route it through the appropriate paint recipe. Most app code
58    /// should not call this directly: the catalog widgets (`card()`,
59    /// `sidebar()`, `dialog()`, `popover()`, `tabs_list()`, etc.) set
60    /// the right role *and* the matching fill / stroke / radius /
61    /// shadow together, while the `.selected()` and `.current()`
62    /// chainables wrap the corresponding state recipes.
63    ///
64    /// Reach for the raw chainable when authoring a new widget or when
65    /// composing a custom container that the catalog doesn't cover —
66    /// and remember that decorative roles (`Panel`, `Raised`, `Popover`,
67    /// `Danger`) require you to supply a fill yourself; see the
68    /// [`SurfaceRole`] doc for the per-variant contract. The bundle
69    /// lint pass flags `Panel` without a fill as
70    /// [`crate::bundle::lint::FindingKind::MissingSurfaceFill`].
71    pub fn surface_role(mut self, role: SurfaceRole) -> Self {
72        self.surface_role = role;
73        self
74    }
75
76    /// Permit paint to extend beyond this element's layout bounds by
77    /// `outset` on each side. Layout-neutral; siblings don't move and
78    /// hit-testing still uses the layout rect.
79    pub fn paint_overflow(mut self, outset: impl Into<Sides>) -> Self {
80        self.paint_overflow = outset.into();
81        self
82    }
83
84    /// Attach a hover tooltip to this element. The runtime synthesizes
85    /// a floating tooltip layer when the pointer rests on the node for
86    /// the configured delay.
87    ///
88    /// **The node must also have a [`key`](Self::key).** Tooltips fire
89    /// through the hit-test pipeline, and `crate::hit_test` only
90    /// returns keyed nodes — an unkeyed leaf with `.tooltip()` is
91    /// silently dead, because hover skips past it to the nearest
92    /// keyed ancestor (which has a different `computed_id` and a
93    /// different tooltip). The bundle lint flags this case as
94    /// [`crate::bundle::lint::FindingKind::DeadTooltip`].
95    ///
96    /// For info-only chrome inside list rows (sha cells, timestamps,
97    /// chips, identicon avatars) the usual key is a synthetic one
98    /// like `"row:{idx}.<part>"` — its only purpose is to make the
99    /// tooltip's hover land. The tooltip text is snapshotted onto the
100    /// hit target at hit-test time, so tooltips fire correctly even
101    /// on `virtual_list_dyn` rows whose children are realized only
102    /// during layout.
103    pub fn tooltip(mut self, text: impl Into<String>) -> Self {
104        self.tooltip = Some(text.into());
105        self
106    }
107
108    /// Declare the pointer cursor when the pointer is over this
109    /// element.
110    pub fn cursor(mut self, cursor: crate::cursor::Cursor) -> Self {
111        self.cursor = Some(cursor);
112        self
113    }
114
115    /// Declare the cursor shown only while a press is captured at this
116    /// exact node.
117    pub fn cursor_pressed(mut self, cursor: crate::cursor::Cursor) -> Self {
118        self.cursor_pressed = Some(cursor);
119        self
120    }
121
122    // ---- Paint-time transforms (animatable via `.animate()`) ----
123    /// Multiply this element's paint alpha by `v` (clamped to `[0, 1]`).
124    pub fn opacity(mut self, v: f32) -> Self {
125        self.opacity = v.clamp(0.0, 1.0);
126        self
127    }
128
129    /// Offset this element's paint and its descendants by `(x, y)` in
130    /// logical pixels.
131    pub fn translate(mut self, x: f32, y: f32) -> Self {
132        self.translate = (x, y);
133        self
134    }
135
136    /// Uniformly scale this element's paint around its rect centre.
137    pub fn scale(mut self, v: f32) -> Self {
138        self.scale = v.max(0.0);
139        self
140    }
141
142    /// Opt this element into app-driven prop interpolation.
143    pub fn animate(mut self, timing: Timing) -> Self {
144        self.animate = Some(timing);
145        self
146    }
147
148    /// Bind a shader for the surface paint, replacing the implicit
149    /// `stock::rounded_rect`.
150    pub fn shader(mut self, binding: ShaderBinding) -> Self {
151        self.shader_override = Some(binding);
152        self
153    }
154
155    // ---- Internal: style profile ----
156    pub fn style_profile(mut self, p: StyleProfile) -> Self {
157        self.style_profile = p;
158        self
159    }
160
161    pub(crate) fn default_radius(mut self, r: impl Into<Corners>) -> Self {
162        self.radius = r.into();
163        self.explicit_radius = false;
164        self
165    }
166}