Added some more layout documentation.

This commit is contained in:
Samuel Guerra 2024-01-20 15:08:05 -03:00
parent 5b2d5bd0d9
commit 5377b7e8eb
8 changed files with 263 additions and 47 deletions

View File

@ -549,8 +549,8 @@ fn center_viewport(msg: impl UiNode) -> impl UiNode {
// the large images can take a moment to decode in debug builds, but the size
// is already known after read, so the "loading.." message ends-up off-screen
// because it is centered on the image.
layout::x = zero_ui::scroll::SCROLL.horizontal_offset().map(|&fct| Length::Relative(fct) - 1.vw() * fct);
layout::y = zero_ui::scroll::SCROLL.vertical_offset().map(|&fct| Length::Relative(fct) - 1.vh() * fct);
layout::x = zero_ui::scroll::SCROLL.horizontal_offset().map(|&fct| Length::Factor(fct) - 1.vw() * fct);
layout::y = zero_ui::scroll::SCROLL.vertical_offset().map(|&fct| Length::Factor(fct) - 1.vh() * fct);
widget::can_auto_hide = false;
layout::max_size = (1.vw(), 1.vh());
child_align = Align::CENTER;

View File

@ -242,13 +242,13 @@ impl_from_and_into_var! {
GradientRadius::farthest_corner(radii)
}
/// Conversion to [`Length::Relative`] and to radius.
/// Conversion to [`Length::Factor`] and to radius.
fn from(percent: FactorPercent) -> GradientRadius {
Length::Relative(percent.into()).into()
Length::Factor(percent.into()).into()
}
/// Conversion to [`Length::Relative`] and to radius.
/// Conversion to [`Length::Factor`] and to radius.
fn from(norm: Factor) -> GradientRadius {
Length::Relative(norm).into()
Length::Factor(norm).into()
}
/// Conversion to [`Length::DipF32`] and to radius.
fn from(f: f32) -> GradientRadius {
@ -539,12 +539,12 @@ impl_from_and_into_var! {
GradientStop::ColorHint(color_hint)
}
/// Conversion to [`Length::Relative`] color hint.
/// Conversion to [`Length::Factor`] color hint.
fn from(color_hint: FactorPercent) -> GradientStop {
GradientStop::ColorHint(color_hint.into())
}
/// Conversion to [`Length::Relative`] color hint.
/// Conversion to [`Length::Factor`] color hint.
fn from(color_hint: Factor) -> GradientStop {
GradientStop::ColorHint(color_hint.into())
}
@ -738,11 +738,11 @@ impl GradientStops {
middle: vec![
GradientStop::Color(ColorStop {
color,
offset: Length::Relative(Factor(0.5 - tran)),
offset: Length::Factor(Factor(0.5 - tran)),
}),
GradientStop::Color(ColorStop {
color: end.color,
offset: Length::Relative(Factor(0.5 + tran)),
offset: Length::Factor(Factor(0.5 + tran)),
}),
],
end,
@ -784,7 +784,7 @@ impl GradientStops {
let end = ColorStop {
color: colors[last].into(),
offset: Length::Relative(Factor(1.0)),
offset: Length::Factor(Factor(1.0)),
};
middle.push(
ColorStop {

View File

@ -25,8 +25,6 @@ use crate::context::LAYOUT;
#[derive(Clone, serde::Serialize, serde::Deserialize)]
pub enum Length {
/// The default (initial) value.
///
/// This is usually `0.px()`, unless the property redefines it.
Default,
/// The exact length in device independent units.
Dip(Dip),
@ -35,7 +33,7 @@ pub enum Length {
/// The exact length in font points.
Pt(f32),
/// Relative to the fill length.
Relative(Factor),
Factor(Factor),
/// Relative to the leftover fill length.
Leftover(Factor),
/// Relative to the font-size of the widget.
@ -83,7 +81,7 @@ impl<L: Into<Length>> ops::Add<L> for Length {
(Dip(a), Dip(b)) => Dip(a + b),
(Px(a), Px(b)) => Px(a + b),
(Pt(a), Pt(b)) => Pt(a + b),
(Relative(a), Relative(b)) => Relative(a + b),
(Factor(a), Factor(b)) => Factor(a + b),
(Leftover(a), Leftover(b)) => Leftover(a + b),
(Em(a), Em(b)) => Em(a + b),
(RootEm(a), RootEm(b)) => RootEm(a + b),
@ -123,7 +121,7 @@ impl<L: Into<Length>> ops::Sub<L> for Length {
(Dip(a), Dip(b)) => Dip(a - b),
(Px(a), Px(b)) => Px(a - b),
(Pt(a), Pt(b)) => Pt(a - b),
(Relative(a), Relative(b)) => Relative(a - b),
(Factor(a), Factor(b)) => Factor(a - b),
(Leftover(a), Leftover(b)) => Leftover(a - b),
(Em(a), Em(b)) => Em(a - b),
(RootEm(a), RootEm(b)) => RootEm(a - b),
@ -164,7 +162,7 @@ impl<F: Into<Factor>> ops::Mul<F> for Length {
Dip(e) => DipF32(e.to_f32() * rhs.0),
Px(e) => PxF32(e.0 as f32 * rhs.0),
Pt(e) => Pt(e * rhs.0),
Relative(r) => Relative(r * rhs),
Factor(r) => Factor(r * rhs),
Leftover(r) => Leftover(r * rhs),
Em(e) => Em(e * rhs),
RootEm(e) => RootEm(e * rhs),
@ -200,7 +198,7 @@ impl<F: Into<Factor>> ops::Div<F> for Length {
Dip(e) => DipF32(e.to_f32() / rhs.0),
Px(e) => PxF32(e.0 as f32 / rhs.0),
Pt(e) => Pt(e / rhs.0),
Relative(r) => Relative(r / rhs),
Factor(r) => Factor(r / rhs),
Leftover(r) => Leftover(r / rhs),
Em(e) => Em(e / rhs),
RootEm(e) => RootEm(e / rhs),
@ -235,7 +233,7 @@ impl Transitionable for Length {
(Dip(a), Dip(b)) => Dip(a.lerp(b, step)),
(Px(a), Px(b)) => Px(a.lerp(b, step)),
(Pt(a), Pt(b)) => Pt(a.lerp(b, step)),
(Relative(a), Relative(b)) => Relative(a.lerp(b, step)),
(Factor(a), Factor(b)) => Factor(a.lerp(b, step)),
(Leftover(a), Leftover(b)) => Leftover(a.lerp(b, step)),
(Em(a), Em(b)) => Em(a.lerp(b, step)),
(RootEm(a), RootEm(b)) => RootEm(a.lerp(b, step)),
@ -262,7 +260,7 @@ impl ops::Neg for Length {
Length::Dip(e) => Length::Dip(-e),
Length::Px(e) => Length::Px(-e),
Length::Pt(e) => Length::Pt(-e),
Length::Relative(e) => Length::Relative(-e),
Length::Factor(e) => Length::Factor(-e),
Length::Leftover(e) => Length::Leftover(-e),
Length::Em(e) => Length::Em(-e),
Length::RootEm(e) => Length::RootEm(-e),
@ -294,7 +292,7 @@ impl PartialEq for Length {
(DipF32(a), DipF32(b)) | (PxF32(a), PxF32(b)) => about_eq(*a, *b, EQ_EPSILON_100),
(Relative(a), Relative(b)) | (Em(a), Em(b)) | (RootEm(a), RootEm(b)) | (Leftover(a), Leftover(b)) => a == b,
(Factor(a), Factor(b)) | (Em(a), Em(b)) | (RootEm(a), RootEm(b)) | (Leftover(a), Leftover(b)) => a == b,
(ViewportWidth(a), ViewportWidth(b))
| (ViewportHeight(a), ViewportHeight(b))
@ -319,7 +317,7 @@ impl fmt::Debug for Length {
Dip(e) => f.debug_tuple("Length::Dip").field(e).finish(),
Px(e) => f.debug_tuple("Length::Px").field(e).finish(),
Pt(e) => f.debug_tuple("Length::Pt").field(e).finish(),
Relative(e) => f.debug_tuple("Length::Relative").field(e).finish(),
Factor(e) => f.debug_tuple("Length::Factor").field(e).finish(),
Leftover(e) => f.debug_tuple("Length::Leftover").field(e).finish(),
Em(e) => f.debug_tuple("Length::Em").field(e).finish(),
RootEm(e) => f.debug_tuple("Length::RootEm").field(e).finish(),
@ -337,7 +335,7 @@ impl fmt::Debug for Length {
Dip(e) => write!(f, "{}.dip()", e.to_f32()),
Px(e) => write!(f, "{}.px()", e.0),
Pt(e) => write!(f, "{e}.pt()"),
Relative(e) => write!(f, "{}.pct()", e.0 * 100.0),
Factor(e) => write!(f, "{}.pct()", e.0 * 100.0),
Leftover(e) => write!(f, "{}.lft()", e.0),
Em(e) => write!(f, "{}.em()", e.0),
RootEm(e) => write!(f, "{}.rem()", e.0),
@ -360,7 +358,7 @@ impl fmt::Display for Length {
Dip(l) => write!(f, "{l}"),
Px(l) => write!(f, "{l}"),
Pt(l) => write!(f, "{l}pt"),
Relative(n) => write!(f, "{:.*}%", f.precision().unwrap_or(0), n.0 * 100.0),
Factor(n) => write!(f, "{:.*}%", f.precision().unwrap_or(0), n.0 * 100.0),
Leftover(l) => write!(f, "{l}lft"),
Em(e) => write!(f, "{e}em"),
RootEm(re) => write!(f, "{re}rem"),
@ -375,14 +373,14 @@ impl fmt::Display for Length {
}
}
impl_from_and_into_var! {
/// Conversion to [`Length::Relative`]
/// Conversion to [`Length::Factor`]
fn from(percent: FactorPercent) -> Length {
Length::Relative(percent.into())
Length::Factor(percent.into())
}
/// Conversion to [`Length::Relative`]
/// Conversion to [`Length::Factor`]
fn from(norm: Factor) -> Length {
Length::Relative(norm)
Length::Factor(norm)
}
/// Conversion to [`Length::DipF32`]
@ -413,12 +411,12 @@ impl Length {
/// Length that fills the available space.
pub const fn fill() -> Length {
Length::Relative(Factor(1.0))
Length::Factor(Factor(1.0))
}
/// Length that fills 50% of the available space.
pub const fn half() -> Length {
Length::Relative(Factor(0.5))
Length::Factor(Factor(0.5))
}
/// Returns a length that resolves to the maximum layout length between `self` and `other`.
@ -429,7 +427,7 @@ impl Length {
(Dip(a), Dip(b)) => Dip(a.max(b)),
(Px(a), Px(b)) => Px(a.max(b)),
(Pt(a), Pt(b)) => Pt(a.max(b)),
(Relative(a), Relative(b)) => Relative(a.max(b)),
(Factor(a), Factor(b)) => Factor(a.max(b)),
(Leftover(a), Leftover(b)) => Leftover(a.max(b)),
(Em(a), Em(b)) => Em(a.max(b)),
(RootEm(a), RootEm(b)) => RootEm(a.max(b)),
@ -453,7 +451,7 @@ impl Length {
(Dip(a), Dip(b)) => Dip(a.min(b)),
(Px(a), Px(b)) => Px(a.min(b)),
(Pt(a), Pt(b)) => Pt(a.min(b)),
(Relative(a), Relative(b)) => Relative(a.min(b)),
(Factor(a), Factor(b)) => Factor(a.min(b)),
(Leftover(a), Leftover(b)) => Leftover(a.min(b)),
(Em(a), Em(b)) => Em(a.min(b)),
(RootEm(a), RootEm(b)) => RootEm(a.min(b)),
@ -482,7 +480,7 @@ impl Length {
Dip(e) => Dip(e.abs()),
Px(e) => Px(e.abs()),
Pt(e) => Pt(e.abs()),
Relative(r) => Relative(r.abs()),
Factor(r) => Factor(r.abs()),
Leftover(r) => Leftover(r.abs()),
Em(e) => Em(e.abs()),
RootEm(r) => RootEm(r.abs()),
@ -508,7 +506,7 @@ impl Length {
Dip(l) => Some(*l == self::Dip::new(0)),
Px(l) => Some(*l == self::Px(0)),
Pt(l) => Some(l.abs() < EQ_EPSILON),
Relative(f) => Some(f.0.abs() < EQ_EPSILON),
Factor(f) => Some(f.0.abs() < EQ_EPSILON),
Leftover(f) => Some(f.0.abs() < EQ_EPSILON),
Em(f) => Some(f.0.abs() < EQ_EPSILON),
RootEm(f) => Some(f.0.abs() < EQ_EPSILON),
@ -596,7 +594,7 @@ impl super::Layout1d for Length {
Dip(l) => l.to_px(LAYOUT.scale_factor()),
Px(l) => *l,
Pt(l) => Self::pt_to_px(*l, LAYOUT.scale_factor()),
Relative(f) => LAYOUT.constraints_for(axis).fill() * f.0,
Factor(f) => LAYOUT.constraints_for(axis).fill() * f.0,
Leftover(f) => {
if let Some(l) = LAYOUT.leftover_for(axis) {
l
@ -624,7 +622,7 @@ impl super::Layout1d for Length {
Dip(l) => l.to_f32() * LAYOUT.scale_factor().0,
Px(l) => l.0 as f32,
Pt(l) => Self::pt_to_px_f32(*l, LAYOUT.scale_factor()),
Relative(f) => LAYOUT.constraints_for(axis).fill().0 as f32 * f.0,
Factor(f) => LAYOUT.constraints_for(axis).fill().0 as f32 * f.0,
Leftover(f) => {
if let Some(l) = LAYOUT.leftover_for(axis) {
l.0 as f32
@ -652,7 +650,7 @@ impl super::Layout1d for Length {
Dip(_) => LayoutMask::SCALE_FACTOR,
Px(_) => LayoutMask::empty(),
Pt(_) => LayoutMask::SCALE_FACTOR,
Relative(_) => LayoutMask::CONSTRAINTS,
Factor(_) => LayoutMask::CONSTRAINTS,
Leftover(_) => LayoutMask::LEFTOVER,
Em(_) => LayoutMask::FONT_SIZE,
RootEm(_) => LayoutMask::ROOT_FONT_SIZE,
@ -884,6 +882,22 @@ pub trait LengthUnits {
/// Returns [`Length::Pt`].
fn pt(self) -> Length;
/// Factor of the fill length.
///
/// This is the same as [`FactorUnits::fct`], but produces a [`Length`] directly. This might be needed
/// in places that don't automatically convert [`Factor`] to [`Length`].
///
/// Returns [`Length::Factor`].
fn fct_l(self) -> Length;
/// Percentage of the fill length.
///
/// This is the same as [`FactorUnits::pct`], but produces a [`Length`] directly. This might be needed
/// in places that don't automatically convert [`FactorPercent`] to [`Length`].
///
/// Returns [`Length::Factor`].
fn pct_l(self) -> Length;
/// Factor of the font-size of the widget.
///
/// Returns [`Length::Em`].
@ -965,6 +979,14 @@ impl LengthUnits for f32 {
Length::Pt(self)
}
fn fct_l(self) -> Length {
Length::Factor(self.fct())
}
fn pct_l(self) -> Length {
Length::Factor(self.pct().fct())
}
fn em(self) -> Length {
Length::Em(self.into())
}
@ -1030,6 +1052,14 @@ impl LengthUnits for i32 {
Length::Pt(self as f32)
}
fn fct_l(self) -> Length {
Length::Factor(self.fct())
}
fn pct_l(self) -> Length {
Length::Factor(self.pct().fct())
}
fn em(self) -> Length {
Length::Em(self.fct())
}

View File

@ -147,12 +147,12 @@ impl_from_and_into_var! {
Vector::splat(length)
}
/// Conversion to [`Length::Relative`] then to vector.
/// Conversion to [`Length::Factor`] then to vector.
fn from(percent: FactorPercent) -> Vector {
Length::from(percent).into()
}
/// Conversion to [`Length::Relative`] then to vector.
/// Conversion to [`Length::Factor`] then to vector.
fn from(norm: Factor) -> Vector {
Length::from(norm).into()
}

View File

@ -1477,7 +1477,7 @@ impl GridLayout {
}
}
// individual factors under `1.0` behave like `Length::Relative`.
// individual factors under `1.0` behave like `Length::Factor`.
if total_factor < Factor(1.0) {
total_factor = Factor(1.0);
}
@ -1606,7 +1606,7 @@ impl GridLayout {
}
}
// individual factors under `1.0` behave like `Length::Relative`.
// individual factors under `1.0` behave like `Length::Factor`.
if total_factor < Factor(1.0) {
total_factor = Factor(1.0);
}

View File

@ -990,7 +990,7 @@ pub enum WidgetLength {
#[default]
Default,
/// The [`Length::Leftover`] value. Evaluates to the [`LayoutMetrics::leftover`] value when measured, if
/// a leftover value is not provided evaluates like a [`Length::Relative`].
/// a leftover value is not provided evaluates like a [`Length::Factor`].
///
/// The *leftover* length needs to be computed by the parent panel, as it depends on the length of the sibling widgets,
/// not just the panel constraints. Panels that support this, compute the value for each widget and measure/layout each using

View File

@ -146,8 +146,8 @@ pub fn is_collapsed(child: impl UiNode, state: impl IntoVar<bool>) -> impl UiNod
/// Container! {
/// zero_ui::core::widget_base::can_auto_hide = false;
///
/// x = zero_ui::widgets::scroll::SCROLL_HORIZONTAL_OFFSET_VAR.map(|&fct| Length::Relative(fct) - 1.vw() * fct);
/// y = zero_ui::widgets::scroll::SCROLL_VERTICAL_OFFSET_VAR.map(|&fct| Length::Relative(fct) - 1.vh() * fct);
/// x = zero_ui::widgets::scroll::SCROLL_HORIZONTAL_OFFSET_VAR.map(|&fct| Length::Factor(fct) - 1.vw() * fct);
/// y = zero_ui::widgets::scroll::SCROLL_VERTICAL_OFFSET_VAR.map(|&fct| Length::Factor(fct) - 1.vh() * fct);
/// max_size = (1.vw(), 1.vh());
/// content_align = Align::CENTER;
///

View File

@ -1,10 +1,98 @@
//! Layout service, units and other types.
//!
//! # Measure & Layout
//! A widget final size and position is influenced by the widget and all ancestor widgets, the properties
//! and nodes that influence the size and position can be grouped into [widget intrinsics](#widget-intrinsics),
//! [widget properties](#widget-properties), [layout properties](#layout-properties) and [transform properties](#transform-properties).
//!
//! TODO !!:
//! Internally this is split into two passes [`UiNode::layout`] and [`UiNode::render`], transform properties are only applied
//! during render and only influence the size and position of the widget and descendants, the other properties are true layout
//! and influence the size and position of the parent widget and siblings too.
//!
//! # Exact Size & Units
//! [`UiNode::Layout`]: crate::widget::node::UiNode::layout
//! [`UiNode::render`]: crate::widget::node::UiNode::render
//!
//! ## Widget Intrinsics
//!
//! Each widget defines a size preference, the default widget has no minimum nor maximum size, it fills available space and collapses
//! to zero when aligned, most widgets override this and have a minimum size preference.
//! The `Text!` prefers a size that fits the entire text without introducing wrap line breaks,
//! the `Stack!` widget prefers a size that fits all its children positioned in a given direction.
//!
//! ### Widget Properties
//!
//! Widget size can be influenced by properties widget specific properties, the `Text!` widget is affected by the font properties
//! for example, as different fonts have different sizes. The `Stack!` widget is affected by the `direction` property that changes
//! position of children widgets and so changes the size that best fits the children.
//!
//! ## Layout Properties
//!
//! Widget size and position can be more directly configured using the standalone layout properties defined in this module,
//! as an example the [`min_size`](fn@min_size) property influences the widget size and the [`align`](fn@align) property
//! influences the widget position, the [`margin`](fn@margin) property potentially influences both size and position.
//!
//! ```
//! use zero_ui::prelude::*;
//! # let _scope = APP.defaults();
//!
//! # let _ =
//! Window! {
//! child = Wgt! {
//! layout::min_size = 40;
//! layout::align = layout::Align::CENTER;
//! widget::background_color = colors::AZURE;
//! };
//! }
//! # ;
//! ```
//!
//! ## Transform Properties
//!
//! Widget size and position can be affected during render only, the standalone [`transform`](fn@transform) property
//! and derived properties like [`scale`](fn@scale) change the final size and position of the widget by transforming
//! the final layout size and position, this affects only the widget and descendants, widget interactions like clicks
//! will *see* the widget at its final transformed bounds, but the parent widget will size itself and position other
//! children using the layout size and position.
//!
//! ```
//! use zero_ui::prelude::*;
//! # let _scope = APP.defaults();
//!
//! # let _ =
//! Stack! {
//! layout::align = layout::Align::CENTER;
//! direction = StackDirection::left_to_right();
//! children = ui_vec![
//! Wgt! {
//! layout::size = (100, 200);
//! widget::background_color = colors::RED;
//! },
//! Wgt! {
//! layout::scale = 120.pct();
//! layout::size = (100, 200);
//! widget::z_index = widget::ZIndex::FRONT;
//! widget::background_color = colors::GREEN;
//! },
//! Wgt! {
//! layout::size = (100, 200);
//! widget::background_color = colors::BLUE;
//! },
//! ];
//! }
//! # ;
//! ```
//!
//! The example above declares a horizontal stack with 3 rectangles, the green rectangle is rendered
//! slightly over the other rectangles because it is [`scale`](fn@scale) to 120% of the size, scale
//! is a render transform only so the stack widget still positions the other rectangles as if the middle
//! one was not scaled. Also note the [`widget::z_index`](fn@crate::widget::z_index) usage, the stack widget
//! render each children in declaration order by default, this is overridden for the green rectangle so
//! it is rendered last, over the blue rectangle too.
//!
//! # Layout Units
//!
//! Most layout properties receive inputs in [`Length`] or length composite types like [`Size`]. These
//! types are layout in the widget context to compute their actual length, the example below demonstrates
//! every [`LengthUnits`], [`FactorUnits`] and length expressions.
//!
//! ```
//! use zero_ui::prelude::*;
@ -33,8 +121,10 @@
//! width!(100.dip()), // 100 device independent pixels
//! width!(100.px()), // 100 device pixels
//! width!(100.pct()), // 100% of the available width
//! width!(100.pct_l()), // 100% of the available width
//! width!(50.pct()), // 50% of the available width
//! width!(1.fct()), // 1 times the available width
//! width!(1.fct_l()), // 1 times the available width
//! width!(0.5.fct()), // 0.5 times the available width
//! width!(100.pt()), // 100 font points
//! width!(8.em()), // 8 times the font size
@ -51,7 +141,9 @@
//! width!(50.vmin_pct()), // 50% of the viewport min(width, height)
//! width!(0.5.vmax()), // 0.5 times the viewport max(width, height)
//! width!(50.vmax_pct()), // 50% of the viewport max(width, height)
//! width!(100.dip() + 50.pct()), // expression, 100dip + 50%.
//! width!(1.lft()), //1 parcel of the leftover space.
//! width!(Length::Default), // default value
//! ];
//! widget::border = 1, colors::RED.desaturate(50.pct());
//! };
@ -60,6 +152,100 @@
//! # ;
//! ```
//!
//! ## Length & Factor Units
//!
//! Length units are defined by [`LengthUnits`] that provides extension methods for `f32` and `i32` values.
//!
//! The most common unit is the *device independent pixel*, or DIP, this is a value that is multiplied by the system scale
//! factor to compute the an exact pixel length, widgets sized in DIPs have a similar apparent size indented of the
//! screen pixel density. This is the default unit, `f32` and `i32` convert to it so `width = 100;` is the same as `width = 100.dip();`.
//!
//! Length can be relative to the available space provided by the parent widget, `100.pct()` and `1.fct()` declare [`FactorPercent`]
//! and [`Factor`] values that convert to [`Length::Factor`]. The [`FactorUnits`] provide the extension methods and
//! is implemented for `f32` and `i32`. You can also use `100.pct_l()` and `1.fct_l()` to get a [`Length`] value directly in places
//! that don't convert the factor types to length.
//!
//! There are multiple units related to font size, `24.pt()` defines a size in *font points*, one font point is `96/72 * scale_factor`
//! device pixels. Size can be relative to the contextual font size, `2.em()` and `200.em_pct()` declare a length twice the computed
//! contextual font size, `2.rem()` and `2.rem_pct()` declare a length twice the computed root font size (the `Window!` font size).
//!
//! Lengths can also be relative to the *viewport*. The viewport is the window content area size, or the parent `Scroll!` visible area size.
//! Lengths `0.2.vw()` and `20.vw_pct()` are 20% of the viewport width, `0.2.vh()` and `20.vh_pct()` are 20% of the viewport height,
//! `1.vmin()` is the minimum viewport length (`min(w, h)`), `1.vmax()` is the maximum viewport length.
//!
//! ### Length Expressions
//!
//! Different length units can be mixed into a length expression, `1.em() + 5.dip()` will create a [`Length::Expr`] value that on layout
//! will compute the pixel length of both terms and then sum. Length expressions support the four basic arithmetic operations, negation,
//! maximum and minimum and absolute.
//!
//! Some basic length expressions are pre-computed on the spot, `10.dip() + 10.dip()` declares a `Length::Dip(20)` value, but most
//! expression declare an object that dynamically executes the expression after all terms are layout.
//!
//! ### Leftover Length
//!
//! The leftover length is a special value that represents the space leftover after non-leftover sibling widgets are layout. This
//! must be implemented by a parent widget to fully work, the `Grid!` widget implements it, in widgets that don't implement it
//! the unit behaves like a factor.
//!
//! ```
//! use zero_ui::prelude::*;
//! # let _scope = APP.defaults();
//!
//! # let _ =
//! Window! {
//! child = Grid! {
//! columns = ui_vec![
//! grid::Column!(1.lft()),
//! grid::Column!(100.dip()),
//! grid::Column!(2.lft()),
//! ];
//! rows = ui_vec![grid::Row!(100.pct())];
//! cells = ui_vec![
//! Wgt! {
//! grid::cell::column = 0;
//! widget::background_color = colors::RED;
//! },
//! Wgt! {
//! grid::cell::column = 1;
//! widget::background_color = colors::GREEN;
//! },
//! Wgt! {
//! grid::cell::column = 2;
//! widget::background_color = colors::BLUE;
//! },
//! ];
//! }
//! }
//! # ;
//! ```
//!
//! The example above declares a grid with 3 columns, on layout the grid computes the width of the middle column first (`100.dip()`),
//! the leftover available width is divided between the other 2 columns proportional to the leftover value. Note that value range
//! of leftover is normalized across all leftover siblings, in the example above changing the values to `2.lft()` and `4.lft()`
//! will produce the column sizes.
//!
//! ### Default Length
//!
//! The [`Length::Default`] value represents the length that is used when no other length is set. It is a placeholder value
//! that is filled in by the widget or property that is resolving the layout. The `grid::Column!()` has `Default` width, in
//! grids this means *auto-size*, the column is sized to fit all cells. In the standalone [`width`](fn@width) property
//! the default width means the fill width.
//!
//! # Layout Pass
//!
//! !!:
//!
//! ## Service
//!
//! ## Constraints & Align
//!
//! ## Outer & Inner Bounds
//!
//! ## Inline
//!
//! ## Border?
//!
//! # Full API
//!
//! See [`zero_ui_layout`], [`zero_ui_wgt_transform`] and [`zero_ui_wgt_size_offset`] for the full API.