zng_wgt_fill/
lib.rs

1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3//!
4//! Properties that fill the widget inner bounds and nodes that fill the available space.
5//!
6//! # Crate
7//!
8#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12use zng_wgt::prelude::gradient::{GradientRadius, GradientStops, LinearGradientAxis, stops};
13use zng_wgt::{HitTestMode, hit_test_mode, node::interactive_node, prelude::*};
14
15pub mod node;
16
17/// Custom background. Allows using any other UI node as a background.
18///
19/// The `background` is not interactive, it is hit-testable only as a visual of the widget. The background
20/// is layout to fill the widget, it does not affect the size of the widget.
21///
22/// Note that nodes can only exist in a single place in the UI tree at a time, so if you set this property
23/// in a style the background node will only appear in the last widget that uses the style, the
24/// [`background_fn`](fn@background_fn) property does not have this issue.
25#[property(FILL)]
26pub fn background(child: impl UiNode, background: impl UiNode) -> impl UiNode {
27    let background = interactive_node(background, false);
28    let background = fill_node(background);
29
30    match_node_list(ui_vec![background, child], |children, op| match op {
31        UiNodeOp::Measure { wm, desired_size } => {
32            *desired_size = children.with_node(1, |n| n.measure(wm));
33        }
34        UiNodeOp::Layout { wl, final_size } => {
35            let size = children.with_node(1, |n| n.layout(wl));
36
37            LAYOUT.with_constraints(PxConstraints2d::new_exact_size(size), || {
38                children.with_node(0, |n| n.layout(wl));
39            });
40            *final_size = size;
41        }
42        _ => {}
43    })
44}
45
46/// Custom background generated using a [`WidgetFn<()>`].
47///
48/// This is the equivalent of setting [`background`] to the [`presenter`] node, but if the property is
49/// set in a style that is used by multiple widgets at the same time the `wgt_fn` will be called for each widget
50/// to create duplicates of the background nodes instead of moving the node to the last widget.
51///
52/// [`WidgetFn<()>`]: WidgetFn
53/// [`background`]: fn@background
54/// [`presenter`]: zng_wgt::prelude::presenter
55#[property(FILL, default(WidgetFn::nil()))]
56pub fn background_fn(child: impl UiNode, wgt_fn: impl IntoVar<WidgetFn<()>>) -> impl UiNode {
57    background(child, presenter((), wgt_fn))
58}
59
60/// Fill color background.
61///
62/// This property applies a [`node::flood`] as [`background`].
63///
64/// [`background`]: fn@background
65#[property(FILL, default(colors::BLACK.transparent()))]
66pub fn background_color(child: impl UiNode, color: impl IntoVar<Rgba>) -> impl UiNode {
67    background(child, node::flood(color))
68}
69
70/// Linear gradient background.
71///
72/// This property applies a [`node::linear_gradient`] as [`background`].
73///
74/// [`background`]: fn@background
75#[property(FILL, default(0.deg(), {
76    let c = colors::BLACK.transparent();
77    stops![c, c]
78}))]
79pub fn background_gradient(child: impl UiNode, axis: impl IntoVar<LinearGradientAxis>, stops: impl IntoVar<GradientStops>) -> impl UiNode {
80    background(child, node::linear_gradient(axis, stops))
81}
82
83/// Radial gradient background.
84///
85/// This property applies a [`node::radial_gradient`] as [`background`].
86///
87/// [`background`]: fn@background
88#[property(FILL, default((50.pct(), 50.pct()), 100.pct(), {
89    let c = colors::BLACK.transparent();
90    stops![c, c]
91}))]
92pub fn background_radial(
93    child: impl UiNode,
94    center: impl IntoVar<Point>,
95    radius: impl IntoVar<GradientRadius>,
96    stops: impl IntoVar<GradientStops>,
97) -> impl UiNode {
98    background(child, node::radial_gradient(center, radius, stops))
99}
100
101/// Conic gradient background.
102///
103/// This property applies a [`node::conic_gradient`] as [`background`].
104///
105/// [`background`]: fn@background
106#[property(FILL, default((50.pct(), 50.pct()), 0.deg(), {
107    let c = colors::BLACK.transparent();
108    stops![c, c]
109}))]
110pub fn background_conic(
111    child: impl UiNode,
112    center: impl IntoVar<Point>,
113    angle: impl IntoVar<AngleRadian>,
114    stops: impl IntoVar<GradientStops>,
115) -> impl UiNode {
116    background(child, node::conic_gradient(center, angle, stops))
117}
118
119/// Custom foreground fill. Allows using any other UI node as a foreground overlay.
120///
121/// The `foreground` is not interactive and not hit-testable.
122/// The foreground is layout to fill the widget, it does not affect the size of the widget. It is rendered over
123/// the widget child and background, it is rendered under borders by default.
124///
125/// Note that nodes can only exist in a single place in the UI tree at a time, so if you set this property in a style
126/// the foreground node will only appear in the last widget that uses the style, the [`foreground_fn`] property does not have this issue.
127///
128/// [`foreground_fn`]: fn@foreground_fn
129#[property(FILL, default(NilUiNode))]
130pub fn foreground(child: impl UiNode, foreground: impl UiNode) -> impl UiNode {
131    let foreground = interactive_node(foreground, false);
132    let foreground = fill_node(foreground);
133    let foreground = hit_test_mode(foreground, HitTestMode::Disabled);
134
135    match_node_list(ui_vec![child, foreground], |children, op| match op {
136        UiNodeOp::Measure { wm, desired_size } => {
137            *desired_size = children.with_node(0, |n| n.measure(wm));
138        }
139        UiNodeOp::Layout { wl, final_size } => {
140            let size = children.with_node(0, |n| n.layout(wl));
141            LAYOUT.with_constraints(PxConstraints2d::new_exact_size(size), || {
142                children.with_node(1, |n| n.layout(wl));
143            });
144            *final_size = size;
145        }
146        _ => {}
147    })
148}
149
150/// Custom foreground generated using a [`WidgetFn<()>`].
151///
152/// This is the equivalent of setting [`foreground`] to the [`presenter`] node, but if the property is set in a style that is used
153/// by multiple widgets at the same time the `wgt_fn` will be called for each widget to create duplicates of the foreground nodes
154/// instead of moving the node to the last widget.
155///
156/// [`WidgetFn<()>`]: WidgetFn
157/// [`foreground`]: fn@foreground
158/// [`presenter`]: zng_wgt::prelude::presenter
159#[property(FILL, default(WidgetFn::nil()))]
160pub fn foreground_fn(child: impl UiNode, wgt_fn: impl IntoVar<WidgetFn<()>>) -> impl UiNode {
161    foreground(child, presenter((), wgt_fn))
162}
163
164/// Foreground highlight border overlay.
165///
166/// This property draws a border contour overlay that can be positioned using `offsets`.
167#[property(FILL, default(0, 0, BorderStyle::Hidden))]
168pub fn foreground_highlight(
169    child: impl UiNode,
170    offsets: impl IntoVar<SideOffsets>,
171    widths: impl IntoVar<SideOffsets>,
172    sides: impl IntoVar<BorderSides>,
173) -> impl UiNode {
174    let offsets = offsets.into_var();
175    let widths = widths.into_var();
176    let sides = sides.into_var();
177
178    let mut render_bounds = PxRect::zero();
179    let mut render_widths = PxSideOffsets::zero();
180    let mut render_radius = PxCornerRadius::zero();
181
182    match_node(child, move |child, op| match op {
183        UiNodeOp::Init => {
184            WIDGET.sub_var_layout(&offsets).sub_var_layout(&widths).sub_var_render(&sides);
185        }
186        UiNodeOp::Layout { wl, final_size } => {
187            let size = child.layout(wl);
188
189            let radius = BORDER.inner_radius();
190            let offsets = offsets.layout();
191            let radius = radius.deflate(offsets);
192
193            let mut bounds = PxRect::zero();
194            if let Some(inline) = wl.inline() {
195                if let Some(first) = inline.rows.iter().find(|r| !r.size.is_empty()) {
196                    bounds = *first;
197                }
198            }
199            if bounds.size.is_empty() {
200                let border_offsets = BORDER.inner_offsets();
201
202                bounds = PxRect::new(
203                    PxPoint::new(offsets.left + border_offsets.left, offsets.top + border_offsets.top),
204                    size - PxSize::new(offsets.horizontal(), offsets.vertical()),
205                );
206            }
207
208            let widths = LAYOUT.with_constraints(PxConstraints2d::new_exact_size(size), || widths.layout());
209
210            if render_bounds != bounds || render_widths != widths || render_radius != radius {
211                render_bounds = bounds;
212                render_widths = widths;
213                render_radius = radius;
214                WIDGET.render();
215            }
216
217            *final_size = size;
218        }
219        UiNodeOp::Render { frame } => {
220            child.render(frame);
221            frame.push_border(render_bounds, render_widths, sides.get(), render_radius);
222        }
223        _ => {}
224    })
225}
226
227/// Fill color overlay.
228///
229/// This property applies a [`node::flood`] as [`foreground`].
230///
231/// [`foreground`]: fn@foreground
232#[property(FILL, default(colors::BLACK.transparent()))]
233pub fn foreground_color(child: impl UiNode, color: impl IntoVar<Rgba>) -> impl UiNode {
234    foreground(child, node::flood(color))
235}
236
237/// Linear gradient overlay.
238///
239/// This property applies a [`node::linear_gradient`] as [`foreground`].
240///
241/// [`foreground`]: fn@foreground
242#[property(FILL, default(0.deg(), {
243    let c = colors::BLACK.transparent();
244    stops![c, c]
245}))]
246pub fn foreground_gradient(child: impl UiNode, axis: impl IntoVar<LinearGradientAxis>, stops: impl IntoVar<GradientStops>) -> impl UiNode {
247    foreground(child, node::linear_gradient(axis, stops))
248}
249
250/// Radial gradient foreground.
251///
252/// This property applies a [`node::radial_gradient`] as [`foreground`].
253///
254/// [`foreground`]: fn@foreground
255#[property(FILL, default((50.pct(), 50.pct()), 100.pct(), {
256    let c = colors::BLACK.transparent();
257    stops![c, c]
258}))]
259pub fn foreground_radial(
260    child: impl UiNode,
261    center: impl IntoVar<Point>,
262    radius: impl IntoVar<GradientRadius>,
263    stops: impl IntoVar<GradientStops>,
264) -> impl UiNode {
265    foreground(child, node::radial_gradient(center, radius, stops))
266}
267
268/// Conic gradient foreground.
269///
270/// This property applies a [`node::conic_gradient`] as [`foreground`].
271///
272/// [`foreground`]: fn@foreground
273#[property(FILL, default((50.pct(), 50.pct()), 0.deg(), {
274    let c = colors::BLACK.transparent();
275    stops![c, c]
276}))]
277pub fn foreground_conic(
278    child: impl UiNode,
279    center: impl IntoVar<Point>,
280    angle: impl IntoVar<AngleRadian>,
281    stops: impl IntoVar<GradientStops>,
282) -> impl UiNode {
283    foreground(child, node::conic_gradient(center, angle, stops))
284}