zng_wgt_size_offset/lib.rs
1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/res/zng-logo.png")]
3//!
4//! Exact size constraints and exact positioning properties.
5//!
6//! # Crate
7//!
8#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12mod offset_impl;
13pub use offset_impl::*;
14mod min;
15pub use min::*;
16mod max;
17pub use max::*;
18mod size_impl;
19pub use size_impl::*;
20mod force;
21pub use force::*;
22mod actual;
23pub use actual::*;
24mod sticky;
25pub use sticky::*;
26
27use zng_wgt::prelude::*;
28
29/// Set or overwrite the baseline of the widget.
30///
31/// The `baseline` is a vertical offset from the bottom edge of the widget's inner bounds up, it defines the
32/// line where the widget naturally *sits*, some widgets like [Text!` have a non-zero default baseline, most others leave it at zero.
33///
34/// Relative values are computed from the widget's height.
35#[property(BORDER, default(Length::Default))]
36pub fn baseline(child: impl IntoUiNode, baseline: impl IntoVar<Length>) -> UiNode {
37 let baseline = baseline.into_var();
38 match_node(child, move |child, op| match op {
39 UiNodeOp::Init => {
40 WIDGET.sub_var_layout(&baseline);
41 }
42 UiNodeOp::Layout { wl, final_size } => {
43 let size = child.layout(wl);
44
45 let bounds = WIDGET.bounds();
46 let inner_size = bounds.inner_size();
47 let default = bounds.baseline();
48
49 let baseline = LAYOUT.with_constraints(LAYOUT.constraints().with_max_size(inner_size).with_fill(true, true), || {
50 baseline.layout_dft_y(default)
51 });
52 wl.set_baseline(baseline);
53
54 *final_size = size;
55 }
56 _ => {}
57 })
58}
59
60/// Exact size property info.
61///
62/// Properties like [`size`], [`width`] and [`height`] set this metadata in the widget state.
63/// Panels can use this info to implement [`Length::Leftover`] support.
64///
65/// [`size`]: fn@size
66/// [`width`]: fn@width
67/// [`height`]: fn@height
68/// [`Length::Leftover`]: zng_wgt::prelude::Length::Leftover
69#[expect(non_camel_case_types)]
70pub struct WIDGET_SIZE;
71impl WIDGET_SIZE {
72 /// Set the width state.
73 pub fn set_width(&self, width: &Length) {
74 WIDGET.with_state_mut(|mut state| {
75 let width = width.into();
76 match state.entry(*WIDGET_SIZE_ID) {
77 state_map::StateMapEntry::Occupied(mut e) => e.get_mut().width = width,
78 state_map::StateMapEntry::Vacant(e) => {
79 e.insert(euclid::size2(width, WidgetLength::Default));
80 }
81 }
82 });
83 }
84
85 /// Set the height state.
86 pub fn set_height(&self, height: &Length) {
87 WIDGET.with_state_mut(|mut state| {
88 let height = height.into();
89 match state.entry(*WIDGET_SIZE_ID) {
90 state_map::StateMapEntry::Occupied(mut e) => e.get_mut().height = height,
91 state_map::StateMapEntry::Vacant(e) => {
92 e.insert(euclid::size2(WidgetLength::Default, height));
93 }
94 }
95 })
96 }
97
98 /// Set the size state.
99 pub fn set(&self, size: &Size) {
100 WIDGET.set_state(*WIDGET_SIZE_ID, euclid::size2((&size.width).into(), (&size.height).into()));
101 }
102
103 /// Get the size set in the state.
104 pub fn get(&self) -> euclid::Size2D<WidgetLength, ()> {
105 WIDGET.get_state(*WIDGET_SIZE_ID).unwrap_or_default()
106 }
107
108 /// Get the size set in the widget state.
109 pub fn get_wgt(&self, wgt: &mut UiNode) -> euclid::Size2D<WidgetLength, ()> {
110 match wgt.as_widget() {
111 Some(mut wgt) => wgt.with_context(WidgetUpdateMode::Ignore, || self.get()),
112 None => Default::default(),
113 }
114 }
115}
116
117static_id! {
118 static ref WIDGET_SIZE_ID: StateId<euclid::Size2D<WidgetLength, ()>>;
119}
120
121/// Represents the width or height property value set on a widget.
122///
123/// Properties like [`size`], [`width`] and [`height`] set the [`WIDGET_SIZE`]
124/// metadata in the widget state. Panels can use this info to implement [`Length::Leftover`] support.
125///
126/// [`size`]: fn@size
127/// [`width`]: fn@width
128/// [`height`]: fn@height
129/// [`Length::Leftover`]: zng_wgt::prelude::Length::Leftover
130#[derive(Debug, Clone, Copy, PartialEq, Default, serde::Serialize, serde::Deserialize)]
131pub enum WidgetLength {
132 /// Evaluates to [`PxConstraints2d::fill_size`] when measured, can serve as a request for *size-to-fit*.
133 ///
134 /// The `Grid!` widget uses this to fit the column and row widgets to *their* cells, as they don't
135 /// logically own the cells, this fit needs to be computed by the parent panel.
136 ///
137 /// [`PxConstraints2d::fill_size`]: zng_wgt::prelude::PxConstraints2d::fill_size
138 #[default]
139 Default,
140 /// The [`Length::Leftover`] value. Evaluates to the [`LayoutMetrics::leftover`] value when measured, if
141 /// a leftover value is not provided evaluates like a [`Length::Factor`].
142 ///
143 /// The *leftover* length needs to be computed by the parent panel, as it depends on the length of the sibling widgets,
144 /// not just the panel constraints. Panels that support this, compute the value for each widget and measure/layout each using
145 /// [`LAYOUT.with_leftover`] to inject the computed value.
146 ///
147 /// [`LAYOUT.with_leftover`]: zng_wgt::prelude::LAYOUT::with_leftover
148 /// [`Length::Leftover`]: zng_wgt::prelude::Length::Leftover
149 /// [`Length::Factor`]: zng_wgt::prelude::Length::Factor
150 /// [`LayoutMetrics::leftover`]: zng_wgt::prelude::LayoutMetrics::leftover
151 Leftover(Factor),
152 /// Any of the other [`Length`] kinds. All contextual metrics needed to compute these values is already available
153 /// in the [`LayoutMetrics`], panels that support [`Length::Leftover`] can layout this widget first to compute the
154 /// leftover length.
155 ///
156 /// [`Length::Leftover`]: zng_wgt::prelude::Length::Leftover
157 /// [`LayoutMetrics`]: zng_wgt::prelude::LayoutMetrics
158 /// [`Length`]: zng_wgt::prelude::Length
159 Exact,
160}
161
162impl From<&Length> for WidgetLength {
163 fn from(value: &Length) -> Self {
164 match value {
165 Length::Default => WidgetLength::Default,
166 Length::Leftover(f) => WidgetLength::Leftover(*f),
167 _ => WidgetLength::Exact,
168 }
169 }
170}