zng/
layout.rs

1//! Layout service, units and other types.
2//!
3//! A widget final size and position is influenced by the widget and all ancestor widgets, the properties
4//! and nodes that influence the size and position can be grouped into [widget intrinsics](#widget-intrinsics),
5//! [widget properties](#widget-properties), [layout properties](#layout-properties) and [transform properties](#transform-properties).
6//!
7//! Internally this is split into two passes [`UiNode::layout`] and [`UiNode::render`], transform properties are only applied
8//! during render and only influence the size and position of the widget and descendants, the other properties are true layout
9//! and influence the size and position of the parent widget and siblings too.
10//!
11//! ## Widget Intrinsics
12//!
13//! Each widget defines a size preference, the default widget has no minimum nor maximum size, it fills available space and collapses
14//! to zero when aligned, most widgets override this and have a minimum size preference.
15//! The `Text!` prefers a size that fits the entire text without introducing wrap line breaks,
16//! the `Stack!` widget prefers a size that fits all its children positioned in a given direction.
17//!
18//! ### Widget Properties
19//!
20//! Widget size can be influenced by properties widget specific properties, the `Text!` widget is affected by the font properties
21//! for example, as different fonts have different sizes. The `Stack!` widget is affected by the `direction` property that changes
22//! position of children widgets and so changes the size that best fits the children.
23//!
24//! ## Layout Properties
25//!
26//! Widget size and position can be more directly configured using the standalone layout properties defined in this module,
27//! as an example the [`min_size`](fn@min_size) property influences the widget size and the [`align`](fn@align) property
28//! influences the widget position, the [`margin`](fn@margin) property potentially influences both size and position.
29//!
30//! ```
31//! use zng::prelude::*;
32//! # let _scope = APP.defaults();
33//!
34//! # let _ =
35//! Window! {
36//!     child = Wgt! {
37//!         layout::min_size = 40;
38//!         layout::align = layout::Align::CENTER;
39//!         widget::background_color = colors::AZURE;
40//!     };
41//! }
42//! # ;
43//! ```
44//!
45//! ## Transform Properties
46//!
47//! Widget size and position can be affected during render only, the standalone [`transform`](fn@transform) property
48//! and derived properties like [`scale`](fn@scale) change the final size and position of the widget by transforming
49//! the final layout size and position, this affects only the widget and descendants, widget interactions like clicks
50//! will *see* the widget at its final transformed bounds, but the parent widget will size itself and position other
51//! children using the layout size and position.
52//!
53//! ```
54//! use zng::prelude::*;
55//! # let _scope = APP.defaults();
56//!
57//! # let _ =
58//! Stack! {
59//!     layout::align = layout::Align::CENTER;
60//!     direction = StackDirection::left_to_right();
61//!     children = ui_vec![
62//!         Wgt! {
63//!             layout::size = (100, 200);
64//!             widget::background_color = colors::RED;
65//!         },
66//!         Wgt! {
67//!             layout::scale = 120.pct();
68//!             layout::size = (100, 200);
69//!             widget::z_index = widget::ZIndex::FRONT;
70//!             widget::background_color = colors::GREEN;
71//!         },
72//!         Wgt! {
73//!             layout::size = (100, 200);
74//!             widget::background_color = colors::BLUE;
75//!         },
76//!     ];
77//! }
78//! # ;
79//! ```
80//!
81//! The example above declares a horizontal stack with 3 rectangles, the green rectangle is rendered
82//! slightly over the other rectangles because it is [`scale`](fn@scale) to 120% of the size, scale
83//! is a render transform only so the stack widget still positions the other rectangles as if the middle
84//! one was not scaled. Also note the [`widget::z_index`](fn@crate::widget::z_index) usage, the stack widget
85//! render each children in declaration order by default, this is overridden for the green rectangle so
86//! it is rendered last, over the blue rectangle too.
87//!
88//! # Layout Units
89//!
90//! Most layout properties receive inputs in [`Length`] or length composite types like [`Size`]. These
91//! types are layout in the widget context to compute their actual length, the example below demonstrates
92//! every [`LengthUnits`], [`FactorUnits`] and length expressions.
93//!
94//! ```
95//! use zng::prelude::*;
96//! # let _scope = APP.defaults();
97//!
98//! macro_rules! width {
99//!     ($width:expr) => {
100//!         Text! {
101//!             layout::force_width = $width;
102//!             txt = stringify!($width);
103//!             widget::background_color = colors::BLUE.desaturate(50.pct());
104//!         }
105//!     };
106//! }
107//! # let _ =
108//! Window! {
109//!     child_align = layout::Align::START;
110//!     child = Scroll! {
111//!         mode = zng::scroll::ScrollMode::VERTICAL;
112//!         padding = 10;
113//!         child = Stack! {
114//!             direction = StackDirection::top_to_bottom();
115//!             spacing = 2;
116//!             children = ui_vec![
117//!                 width!(100), // 100 device independent pixels
118//!                 width!(100.dip()), // 100 device independent pixels
119//!                 width!(100.px()), // 100 device pixels
120//!                 width!(100.pct()), // 100% of the available width
121//!                 width!(100.pct_l()), // 100% of the available width
122//!                 width!(50.pct()), // 50% of the available width
123//!                 width!(1.fct()), // 1 times the available width
124//!                 width!(1.fct_l()), // 1 times the available width
125//!                 width!(0.5.fct()), // 0.5 times the available width
126//!                 width!(100.pt()), // 100 font points
127//!                 width!(8.em()), // 8 times the font size
128//!                 width!(800.em_pct()), // 800% of the font size
129//!                 width!(8.rem()), // 8 times the root font size
130//!                 width!(800.rem_pct()), // 800% of the root font size
131//!                 width!(1.vw()), // 1 times the viewport width
132//!                 width!(100.vw_pct()), // 100% of the viewport width
133//!                 width!(0.5.vw()), // 0.5 times the viewport width
134//!                 width!(1.vh()), // 1 times the viewport height
135//!                 width!(100.vh_pct()), // 100% of the viewport height
136//!                 width!(0.5.vh()), // 0.5 times the viewport height
137//!                 width!(0.5.vmin()), // 0.5 times the viewport min(width, height)
138//!                 width!(50.vmin_pct()), // 50% of the viewport min(width, height)
139//!                 width!(0.5.vmax()), // 0.5 times the viewport max(width, height)
140//!                 width!(50.vmax_pct()), // 50% of the viewport max(width, height)
141//!                 width!(100.dip() + 50.pct()), // expression, 100dip + 50%.
142//!                 width!(1.lft()), //1 parcel of the leftover space.
143//!                 width!(Length::Default), // default value
144//!             ];
145//!             widget::border = 1, colors::RED.desaturate(50.pct());
146//!         };
147//!     };
148//! }
149//! # ;
150//! ```
151//!
152//! ## Length & Factor Units
153//!
154//! Length units are defined by [`LengthUnits`] that provides extension methods for `f32` and `i32` values.
155//!
156//! The most common unit is the *device independent pixel*, or DIP, this is a value that is multiplied by the system scale
157//! factor to compute the an exact pixel length, widgets sized in DIPs have a similar apparent size indented of the
158//! screen pixel density. This is the default unit, `f32` and `i32` convert to it so `width = 100;` is the same as `width = 100.dip();`.
159//!
160//! Length can be relative to the available space provided by the parent widget, `100.pct()` and `1.fct()` declare [`FactorPercent`]
161//! and [`Factor`] values that convert to [`Length::Factor`]. The [`FactorUnits`] provide the extension methods and
162//! 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
163//! that don't convert the factor types to length.
164//!
165//! There are multiple units related to font size, `24.pt()` defines a size in *font points*, one font point is `96/72 * scale_factor`
166//! device pixels. Size can be relative to the contextual font size, `2.em()` and `200.em_pct()` declare a length twice the computed
167//! contextual font size, `2.rem()` and `2.rem_pct()` declare a length twice the computed root font size (the `Window!` font size).
168//!
169//! Lengths can also be relative to the *viewport*. The viewport is the window content area size, or the parent `Scroll!` visible area size.
170//! 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,
171//! `1.vmin()` is the minimum viewport length (`min(w, h)`), `1.vmax()` is the maximum viewport length.
172//!
173//! ### Length Expressions
174//!
175//! Different length units can be mixed into a length expression, `1.em() + 5.dip()` will create a [`Length::Expr`] value that on layout
176//! will compute the pixel length of both terms and then sum. Length expressions support the four basic arithmetic operations, negation,
177//! maximum and minimum and absolute.
178//!
179//! Some basic length expressions are pre-computed on the spot, `10.dip() + 10.dip()` declares a `Length::Dip(20)` value, but most
180//! expression declare an object that dynamically executes the expression after all terms are layout.
181//!
182//! ### Leftover Length
183//!
184//! The leftover length is a special value that represents the space leftover after non-leftover sibling widgets are layout. This
185//! must be implemented by a parent widget to fully work, the `Grid!` widget implements it, in widgets that don't implement it
186//! the unit behaves like a factor.
187//!
188//! ```
189//! use zng::prelude::*;
190//! # let _scope = APP.defaults();
191//!
192//! # let _ =
193//! Window! {
194//!     child = Grid! {
195//!         columns = ui_vec![
196//!             grid::Column!(1.lft()),
197//!             grid::Column!(100.dip()),
198//!             grid::Column!(2.lft()),
199//!         ];
200//!         rows = ui_vec![grid::Row!(100.pct())];
201//!         cells = ui_vec![
202//!             Wgt! {
203//!                 grid::cell::column = 0;
204//!                 widget::background_color = colors::RED;
205//!             },
206//!             Wgt! {
207//!                 grid::cell::column = 1;
208//!                 widget::background_color = colors::GREEN;
209//!             },
210//!             Wgt! {
211//!                 grid::cell::column = 2;
212//!                 widget::background_color = colors::BLUE;
213//!             },
214//!         ];
215//!     }
216//! }
217//! # ;
218//! ```
219//!
220//! The example above declares a grid with 3 columns, on layout the grid computes the width of the middle column first (`100.dip()`),
221//! the leftover available width is divided between the other 2 columns proportional to the leftover value. Note that value range
222//! of leftover is normalized across all leftover siblings, in the example above changing the values to `2.lft()` and `4.lft()`
223//! will produce the column sizes.
224//!
225//! ### Default Length
226//!
227//! The [`Length::Default`] value represents the length that is used when no other length is set. It is a placeholder value
228//! that is filled in by the widget or property that is resolving the layout. The `grid::Column!()` has `Default` width, in
229//! grids this means *auto-size*, the column is sized to fit all cells. In the standalone [`width`](fn@width) property
230//! the default width means the fill width.
231//!
232//! # Measure & Layout
233//!
234//! Nodes that implement custom layout must handle [`UiNode::measure`] and [`UiNode::layout`].
235//! Measure and layout provide a desired size and final size respectively, given the same context both methods return the
236//! same size, the different is that the measure call must not actually affect the widget, it exists to allow a parent widget
237//! to query what the layout result would be for a given context.
238//!
239//! Consider a `Stack!` that is aligned `CENTER` and has children aligned `FILL`, to fulfill these constraints
240//! the stack does the layout in two passes, first it measures each child to find the width, then it layouts
241//! each child constrained to this width. If this same stack is given an exact size it will skip the measure
242//! pass and just do the layout directly.
243//!
244//! The coordination between layout properties on a widget and between widgets is centered on the [`LAYOUT`], [`WidgetMeasure`],
245//! [`WidgetLayout`] and the return [`PxSize`]. Parent nodes set context metrics and constraints using the [`LAYOUT`] service,
246//! child nodes returns the size and optionally set more return metadata in the [`WidgetMeasure`] and [`WidgetLayout`] args.
247//! The parent node then sets the child position using [`WidgetLayout`] or by manually transforming the child during render.
248//!
249//! Other contextual services and variables may complement the layout computation, the [`WIDGET_SIZE`] is used to implement
250//! [`Length::Leftover`] layouts, the [`widget::BORDER`] is used to implement the alignment between borders and the background.
251//! Widgets can use context vars to define layout preferences that only apply to their special layout, the `Text!` and `Image!`
252//! widgets are examples of this.
253//!
254//! UI components are very modular, during layout is when they are the closest coupled, implementers must careful consider
255//! the full [`LAYOUT`], [`WidgetMeasure`] [`WidgetLayout`] APIs, understand what properties placed in the [`NestGroup::LAYOUT`] can do
256//! and what the widget outer and inner bounds are. Implementers also must consider if their layout will support inlining or
257//! if it will only be a block. After reading the APIs a good way to learn is by studying the source code of properties in this
258//! module, followed by the `Image!`, `Stack!`, `Grid!` and `Wrap!` implementations.
259//!
260//! ## Outer & Inner Bounds
261//!
262//! Each laidout widget has two computed rectangles, the inner bounds define the rendered area, the outer bounds define
263//! the extra space taken by the widget layout, properties like [`align`](fn@align) and [`margin`](fn@margin) are still
264//! a part of the widget, the blank space they add *around* the widget is inside the widget outer bounds.
265//!
266//! ```
267//! use zng::prelude::*;
268//! # let _scope = APP.defaults();
269//!
270//! # let _ =
271//! Window! {
272//!     padding = 20;
273//!     child = Wgt! {
274//!         layout::size = 80;
275//!         layout::align = layout::Align::CENTER;
276//!         window::inspector::show_bounds = true;
277//!     };
278//! }
279//! # ;
280//! ```
281//!
282//! The example above uses the [`window::inspector::show_bounds`] property to inspect the bounds of a widget, it shows the
283//! outer bounds of the widget extend to almost cover the entire window, that happens because the window default `child_align` is
284//! `FILL` and it only reserved `20` of padding space, leaving the rest of the space for the child widget to handle. The widget
285//! wants to have an exact size of `80` centered on the available space, so it ends up with the outer bounds taking the available space
286//! and the inner bounds taking the exact size.
287//!
288//! ## Inline
289//!
290//! Layout has two modes, block and inline, in block layout the shape of the laidout widgets is not changed, they are always
291//! rectangular, inline layout expands layout to alter the shape of laidout widgets to potentially split into multiple rectangles that
292//! define the first line, the middle block of lines and the last line.
293//!
294//! The example below declares a `Wrap!` with three `Text!` children, both the wrap and text widgets support inline layout so the end-result
295//! is that the green text will be reshaped as two rectangles, one after the red text and one before the blue text.
296//!
297//! ```
298//! use zng::prelude::*;
299//! # let _scope = APP.defaults();
300//!
301//! # let _ =
302//! Wrap! {
303//!     children = ui_vec![
304//!         Text! {
305//!             widget::background_color = colors::RED.with_alpha(40.pct());
306//!             txt = "RED";
307//!         },
308//!         Text! {
309//!             widget::background_color = colors::GREEN.with_alpha(40.pct());
310//!             txt = "GREEN\nGREEN";
311//!         },
312//!         Text! {
313//!             widget::background_color = colors::BLUE.with_alpha(40.pct());
314//!             txt = "BLUE";
315//!         },
316//!     ]
317//! }
318//! # ;
319//! ```
320//!
321//! Inline layout is modeled to support complex text layout interactions, like bidirectional text reordering, inlined widgets don't need
322//! to be text however, the `Wrap!` widget itself can be nested.
323//!
324//! If a widget does not support inline it calls [`WidgetMeasure::disable_inline`], in an inline context these widgets
325//! are *inline-blocks*. If a panel widget does not support inline and it needs to measure children it calls [`WidgetMeasure::measure_block`].
326//!
327//! If a widget or property supports inline it can detect it is in an inline context by [`WidgetMeasure::inline`] where the preferred
328//! segments of the widget can be set for the parent inline panel to analyze, if inline is set during measure it will also be inline
329//! during layout and [`LAYOUT`] will have inline constraints. During layout the [`WidgetLayout::inline`] value can
330//! be set to the final inline info.
331//!
332//! After inline layout the children are positioned so that the last line of the previous sibling connects with the first line of the next, all
333//! of the widget visual properties must support this however, the [`WIDGET.bounds().inline()`] is available during render with cached
334//! negative space clips that can quickly be used. If a visual property is not aware of inline it can potentially render over the
335//! previous sibling, inline should be disabled for the widget if the property cannot support inline.
336//!
337//! [`WIDGET.bounds().inline()`]: crate::widget::info::WidgetBoundsInfo::inline
338//! [`UiNode::measure`]: crate::widget::node::UiNode::measure
339//! [`UiNode::Layout`]: crate::widget::node::UiNode::layout
340//! [`UiNode::render`]: crate::widget::node::UiNode::render
341//! [`widget::BORDER`]: crate::widget::BORDER
342//! [`NestGroup::LAYOUT`]: crate::widget::builder::NestGroup::LAYOUT
343//! [`window::inspector::show_bounds`]: fn@crate::window::inspector::show_bounds
344//!
345//! # Full API
346//!
347//! See [`zng_layout`], [`zng_wgt_transform`] and [`zng_wgt_size_offset`] for the full API.
348
349pub use zng_layout::unit::{
350    Align, AngleDegree, AngleGradian, AngleRadian, AngleTurn, AngleUnits, BoolVector2D, ByteLength, ByteUnits, CornerRadius2D, Dip, DipBox,
351    DipCornerRadius, DipPoint, DipRect, DipSideOffsets, DipSize, DipToPx, DipVector, DistanceKey, Factor, Factor2d, FactorPercent,
352    FactorSideOffsets, FactorUnits, GridSpacing, Layout1d, Layout2d, LayoutAxis, Length, LengthExpr, LengthUnits, Line,
353    LineFromTuplesBuilder, Orientation2D, Point, Ppi, Ppm, Px, PxBox, PxConstraints, PxConstraints2d, PxCornerRadius, PxGridSpacing,
354    PxLine, PxPoint, PxRect, PxSideOffsets, PxSize, PxToDip, PxTransform, PxVector, Rect, RectFromTuplesBuilder, ResolutionUnits,
355    SideOffsets, SideOffsets2D, Size, TimeUnits, Transform, Vector,
356};
357
358pub use zng_var::types::{slerp_enabled, slerp_sampler};
359
360pub use zng_layout::context::{
361    DIRECTION_VAR, InlineConstraints, InlineConstraintsLayout, InlineConstraintsMeasure, InlineSegment, InlineSegmentPos, LAYOUT,
362    LayoutDirection, LayoutMask, LayoutMetrics, LayoutMetricsSnapshot, LayoutPassId, TextSegmentKind,
363};
364
365pub use zng_app::widget::info::{WidgetLayout, WidgetMeasure};
366
367pub use zng_wgt_transform::{
368    backface_visibility, perspective, perspective_origin, rotate, rotate_x, rotate_y, rotate_z, scale, scale_x, scale_xy, scale_y, skew,
369    skew_x, skew_y, transform, transform_origin, transform_style, translate, translate_x, translate_y, translate_z,
370};
371
372pub use zng_wgt_size_offset::{
373    WIDGET_SIZE, WidgetLength, actual_bounds, actual_height, actual_height_px, actual_size, actual_size_px, actual_transform, actual_width,
374    actual_width_px, baseline, force_height, force_size, force_width, height, max_height, max_size, max_width, min_height, min_size,
375    min_width, offset, size, sticky_height, sticky_size, sticky_width, width, x, y,
376};
377
378pub use zng_wgt::{InlineMode, align, inline, is_ltr, is_rtl, margin};
379
380pub use zng_wgt_container::{child_align, padding};
381
382pub use zng_app::render::TransformStyle;