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//! # fn example() {
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//! # fn example() {
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//! # fn example() {
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//! # fn example() {
191//!
192//! # let _ =
193//! Window! {
194//! child = Grid! {
195//! columns = ui_vec![grid::Column!(1.lft()), grid::Column!(100.dip()), grid::Column!(2.lft()),];
196//! rows = ui_vec![grid::Row!(100.pct())];
197//! cells = ui_vec![
198//! Wgt! {
199//! grid::cell::column = 0;
200//! widget::background_color = colors::RED;
201//! },
202//! Wgt! {
203//! grid::cell::column = 1;
204//! widget::background_color = colors::GREEN;
205//! },
206//! Wgt! {
207//! grid::cell::column = 2;
208//! widget::background_color = colors::BLUE;
209//! },
210//! ];
211//! };
212//! }
213//! # ; }
214//! ```
215//!
216//! The example above declares a grid with 3 columns, on layout the grid computes the width of the middle column first (`100.dip()`),
217//! the leftover available width is divided between the other 2 columns proportional to the leftover value. Note that value range
218//! of leftover is normalized across all leftover siblings, in the example above changing the values to `2.lft()` and `4.lft()`
219//! will produce the column sizes.
220//!
221//! ### Default Length
222//!
223//! The [`Length::Default`] value represents the length that is used when no other length is set. It is a placeholder value
224//! that is filled in by the widget or property that is resolving the layout. The `grid::Column!()` has `Default` width, in
225//! grids this means *auto-size*, the column is sized to fit all cells. In the standalone [`width`](fn@width) property
226//! the default width means the fill width.
227//!
228//! # Measure & Layout
229//!
230//! Nodes that implement custom layout must handle [`UiNode::measure`] and [`UiNode::layout`].
231//! Measure and layout provide a desired size and final size respectively, given the same context both methods return the
232//! same size, the different is that the measure call must not actually affect the widget, it exists to allow a parent widget
233//! to query what the layout result would be for a given context.
234//!
235//! Consider a `Stack!` that is aligned `CENTER` and has children aligned `FILL`, to fulfill these constraints
236//! the stack does the layout in two passes, first it measures each child to find the width, then it layouts
237//! each child constrained to this width. If this same stack is given an exact size it will skip the measure
238//! pass and just do the layout directly.
239//!
240//! The coordination between layout properties on a widget and between widgets is centered on the [`LAYOUT`], [`WidgetMeasure`],
241//! [`WidgetLayout`] and the return [`PxSize`]. Parent nodes set context metrics and constraints using the [`LAYOUT`] service,
242//! child nodes returns the size and optionally set more return metadata in the [`WidgetMeasure`] and [`WidgetLayout`] args.
243//! The parent node then sets the child position using [`WidgetLayout`] or by manually transforming the child during render.
244//!
245//! Other contextual services and variables may complement the layout computation, the [`WIDGET_SIZE`] is used to implement
246//! [`Length::Leftover`] layouts, the [`widget::BORDER`] is used to implement the alignment between borders and the background.
247//! Widgets can use context vars to define layout preferences that only apply to their special layout, the `Text!` and `Image!`
248//! widgets are examples of this.
249//!
250//! UI components are very modular, during layout is when they are the closest coupled, implementers must careful consider
251//! the full [`LAYOUT`], [`WidgetMeasure`] [`WidgetLayout`] APIs, understand what properties placed in the [`NestGroup::LAYOUT`] can do
252//! and what the widget outer and inner bounds are. Implementers also must consider if their layout will support inlining or
253//! 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
254//! module, followed by the `Image!`, `Stack!`, `Grid!` and `Wrap!` implementations.
255//!
256//! ## Outer & Inner Bounds
257//!
258//! Each laidout widget has two computed rectangles, the inner bounds define the rendered area, the outer bounds define
259//! the extra space taken by the widget layout, properties like [`align`](fn@align) and [`margin`](fn@margin) are still
260//! a part of the widget, the blank space they add *around* the widget is inside the widget outer bounds.
261//!
262//! ```
263//! use zng::prelude::*;
264//! # fn example() {
265//!
266//! # let _ =
267//! Window! {
268//! padding = 20;
269//! child = Wgt! {
270//! layout::size = 80;
271//! layout::align = layout::Align::CENTER;
272//! window::inspector::show_bounds = true;
273//! };
274//! }
275//! # ; }
276//! ```
277//!
278//! The example above uses the [`window::inspector::show_bounds`] property to inspect the bounds of a widget, it shows the
279//! outer bounds of the widget extend to almost cover the entire window, that happens because the window default `child_align` is
280//! `FILL` and it only reserved `20` of padding space, leaving the rest of the space for the child widget to handle. The widget
281//! 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
282//! and the inner bounds taking the exact size.
283//!
284//! ## Inline
285//!
286//! Layout has two modes, block and inline, in block layout the shape of the laidout widgets is not changed, they are always
287//! rectangular, inline layout expands layout to alter the shape of laidout widgets to potentially split into multiple rectangles that
288//! define the first line, the middle block of lines and the last line.
289//!
290//! The example below declares a `Wrap!` with three `Text!` children, both the wrap and text widgets support inline layout so the end-result
291//! is that the green text will be reshaped as two rectangles, one after the red text and one before the blue text.
292//!
293//! ```
294//! use zng::prelude::*;
295//! # fn example() {
296//!
297//! # let _ =
298//! Wrap! {
299//! children = ui_vec![
300//! Text! {
301//! widget::background_color = colors::RED.with_alpha(40.pct());
302//! txt = "RED";
303//! },
304//! Text! {
305//! widget::background_color = colors::GREEN.with_alpha(40.pct());
306//! txt = "GREEN\nGREEN";
307//! },
308//! Text! {
309//! widget::background_color = colors::BLUE.with_alpha(40.pct());
310//! txt = "BLUE";
311//! },
312//! ];
313//! }
314//! # ; }
315//! ```
316//!
317//! Inline layout is modeled to support complex text layout interactions, like bidirectional text reordering, inlined widgets don't need
318//! to be text however, the `Wrap!` widget itself can be nested.
319//!
320//! If a widget does not support inline it calls [`WidgetMeasure::disable_inline`], in an inline context these widgets
321//! are *inline-blocks*. If a panel widget does not support inline and it needs to measure children it calls [`WidgetMeasure::measure_block`].
322//!
323//! If a widget or property supports inline it can detect it is in an inline context by [`WidgetMeasure::inline`] where the preferred
324//! 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
325//! during layout and [`LAYOUT`] will have inline constraints. During layout the [`WidgetLayout::inline`] value can
326//! be set to the final inline info.
327//!
328//! 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
329//! of the widget visual properties must support this however, the [`WIDGET.bounds().inline()`] is available during render with cached
330//! negative space clips that can quickly be used. If a visual property is not aware of inline it can potentially render over the
331//! previous sibling, inline should be disabled for the widget if the property cannot support inline.
332//!
333//! [`WIDGET.bounds().inline()`]: crate::widget::info::WidgetBoundsInfo::inline
334//! [`UiNode::measure`]: crate::widget::node::UiNode::measure
335//! [`UiNode::Layout`]: crate::widget::node::UiNode::layout
336//! [`UiNode::render`]: crate::widget::node::UiNode::render
337//! [`widget::BORDER`]: crate::widget::BORDER
338//! [`NestGroup::LAYOUT`]: crate::widget::builder::NestGroup::LAYOUT
339//! [`window::inspector::show_bounds`]: fn@crate::window::inspector::show_bounds
340//!
341//! # Full API
342//!
343//! See [`zng_layout`], [`zng_wgt_transform`] and [`zng_wgt_size_offset`] for the full API.
344
345pub use zng_layout::unit::{
346 Align, AngleDegree, AngleGradian, AngleRadian, AngleTurn, AngleUnits, BoolVector2D, ByteLength, ByteUnits, CornerRadius2D, Dip, DipBox,
347 DipCornerRadius, DipPoint, DipRect, DipSideOffsets, DipSize, DipToPx, DipVector, DistanceKey, Factor, Factor2d, FactorPercent,
348 FactorSideOffsets, FactorUnits, GridSpacing, Layout1d, Layout2d, LayoutAxis, Length, LengthExpr, LengthUnits, Line,
349 LineFromTuplesBuilder, Orientation2D, Point, Px, PxBox, PxConstraints, PxConstraints2d, PxCornerRadius, PxDensity, PxDensity2d,
350 PxDensityUnits, PxGridSpacing, PxLine, PxPoint, PxRect, PxSideOffsets, PxSize, PxToDip, PxTransform, PxVector, Rect,
351 RectFromTuplesBuilder, SideOffsets, SideOffsets2D, Size, TimeUnits, Transform, Vector,
352};
353
354pub use zng_var::animation::{slerp_enabled, slerp_sampler};
355
356pub use zng_layout::context::{
357 DIRECTION_VAR, InlineConstraints, InlineConstraintsLayout, InlineConstraintsMeasure, InlineSegment, InlineSegmentPos, LAYOUT,
358 LayoutDirection, LayoutMask, LayoutMetrics, LayoutMetricsSnapshot, LayoutPassId, TextSegmentKind,
359};
360
361pub use zng_app::widget::info::{WidgetLayout, WidgetMeasure};
362
363pub use zng_wgt_transform::{
364 backface_visibility, perspective, perspective_origin, rotate, rotate_x, rotate_y, rotate_z, scale, scale_x, scale_xy, scale_y, skew,
365 skew_x, skew_y, transform, transform_origin, transform_style, translate, translate_x, translate_y, translate_z,
366};
367
368pub use zng_wgt_size_offset::{
369 WIDGET_SIZE, WidgetLength, actual_bounds, actual_height, actual_height_px, actual_size, actual_size_px, actual_transform, actual_width,
370 actual_width_px, baseline, force_height, force_size, force_width, height, max_height, max_size, max_width, min_height, min_size,
371 min_width, offset, size, sticky_height, sticky_size, sticky_width, width, x, y,
372};
373
374pub use zng_wgt::{InlineMode, align, inline, is_ltr, is_rtl, margin};
375
376pub use zng_wgt_container::{child_align, padding};
377
378pub use zng_app::render::TransformStyle;