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}