zng/
layout.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
//! Layout service, units and other types.
//!
//! 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).
//!
//! 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.
//!
//! ## 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 zng::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 zng::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 zng::prelude::*;
//! # let _scope = APP.defaults();
//!
//! macro_rules! width {
//!     ($width:expr) => {
//!         Text! {
//!             layout::force_width = $width;
//!             txt = stringify!($width);
//!             widget::background_color = colors::BLUE.desaturate(50.pct());
//!         }
//!     };
//! }
//! # let _ =
//! Window! {
//!     child_align = layout::Align::START;
//!     child = Scroll! {
//!         mode = zng::scroll::ScrollMode::VERTICAL;
//!         padding = 10;
//!         child = Stack! {
//!             direction = StackDirection::top_to_bottom();
//!             spacing = 2;
//!             children = ui_vec![
//!                 width!(100), // 100 device independent pixels
//!                 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
//!                 width!(800.em_pct()), // 800% of the font size
//!                 width!(8.rem()), // 8 times the root font size
//!                 width!(800.rem_pct()), // 800% of the root font size
//!                 width!(1.vw()), // 1 times the viewport width
//!                 width!(100.vw_pct()), // 100% of the viewport width
//!                 width!(0.5.vw()), // 0.5 times the viewport width
//!                 width!(1.vh()), // 1 times the viewport height
//!                 width!(100.vh_pct()), // 100% of the viewport height
//!                 width!(0.5.vh()), // 0.5 times the viewport height
//!                 width!(0.5.vmin()), // 0.5 times the viewport min(width, height)
//!                 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());
//!         };
//!     };
//! }
//! # ;
//! ```
//!
//! ## 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 zng::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.
//!
//! # Measure & Layout
//!
//! Nodes that implement custom layout must handle [`UiNode::measure`] and [`UiNode::layout`].
//! Measure and layout provide a desired size and final size respectively, given the same context both methods return the
//! same size, the different is that the measure call must not actually affect the widget, it exists to allow a parent widget
//! to query what the layout result would be for a given context.
//!
//! Consider a `Stack!` that is aligned `CENTER` and has children aligned `FILL`, to fulfill these constraints
//! the stack does the layout in two passes, first it measures each child to find the width, then it layouts
//! each child constrained to this width. If this same stack is given an exact size it will skip the measure
//! pass and just do the layout directly.
//!
//! The coordination between layout properties on a widget and between widgets is centered on the [`LAYOUT`], [`WidgetMeasure`],
//! [`WidgetLayout`] and the return [`PxSize`]. Parent nodes set context metrics and constraints using the [`LAYOUT`] service,
//! child nodes returns the size and optionally set more return metadata in the [`WidgetMeasure`] and [`WidgetLayout`] args.
//! The parent node then sets the child position using [`WidgetLayout`] or by manually transforming the child during render.
//!
//! Other contextual services and variables may complement the layout computation, the [`WIDGET_SIZE`] is used to implement
//! [`Length::Leftover`] layouts, the [`widget::BORDER`] is used to implement the alignment between borders and the background.
//! Widgets can use context vars to define layout preferences that only apply to their special layout, the `Text!` and `Image!`
//! widgets are examples of this.
//!
//! UI components are very modular, during layout is when they are the closest coupled, implementers must careful consider
//! the full [`LAYOUT`], [`WidgetMeasure`] [`WidgetLayout`] APIs, understand what properties placed in the [`NestGroup::LAYOUT`] can do
//! and what the widget outer and inner bounds are. Implementers also must consider if their layout will support inlining or
//! 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
//! module, followed by the `Image!`, `Stack!`, `Grid!` and `Wrap!` implementations.
//!
//! ## Outer & Inner Bounds
//!
//! Each laidout widget has two computed rectangles, the inner bounds define the rendered area, the outer bounds define
//! the extra space taken by the widget layout, properties like [`align`](fn@align) and [`margin`](fn@margin) are still
//! a part of the widget, the blank space they add *around* the widget is inside the widget outer bounds.
//!
//! ```
//! use zng::prelude::*;
//! # let _scope = APP.defaults();
//!
//! # let _ =
//! Window! {
//!     padding = 20;
//!     child = Wgt! {
//!         layout::size = 80;
//!         layout::align = layout::Align::CENTER;
//!         window::inspector::show_bounds = true;
//!     };
//! }
//! # ;
//! ```
//!
//! The example above uses the [`window::inspector::show_bounds`] property to inspect the bounds of a widget, it shows the
//! outer bounds of the widget extend to almost cover the entire window, that happens because the window default `child_align` is
//! `FILL` and it only reserved `20` of padding space, leaving the rest of the space for the child widget to handle. The widget
//! 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
//! and the inner bounds taking the exact size.
//!
//! ## Inline
//!
//! Layout has two modes, blocks and inline, in block layout the shape of the laidout widgets is not changed, they are always
//! rectangular, inline layout expands layout to alter the shape of laidout widgets to potentially split into multiple rectangles that
//! define the first line, the middle block of lines and the last line.
//!
//! The example below declares a `Wrap!` with 3 `Text!` children, both the wrap and text widgets support inline layout so the end-result
//! is that the green text will be reshaped as two rectangles, one after the red text and one before the blue text.
//!
//! ```
//! use zng::prelude::*;
//! # let _scope = APP.defaults();
//!
//! # let _ =
//! Wrap! {
//!     children = ui_vec![
//!         Text! {
//!             widget::background_color = colors::RED.with_alpha(40.pct());
//!             txt = "RED";
//!         },
//!         Text! {
//!             widget::background_color = colors::GREEN.with_alpha(40.pct());
//!             txt = "GREEN\nGREEN";
//!         },
//!         Text! {
//!             widget::background_color = colors::BLUE.with_alpha(40.pct());
//!             txt = "BLUE";
//!         },
//!     ]
//! }
//! # ;
//! ```
//!
//! Inline layout is modeled to support complex text layout interactions, like bidirectional text reordering, inlined widgets don't need
//! to be text however, the `Wrap!` widget itself can be nested.
//!
//! If a widget does not support inline it calls [`WidgetMeasure::disable_inline`], in an inline context these widgets
//! are *inline-blocks*. If a panel widget does not support inline and it needs to measure children it calls [`WidgetMeasure::measure_block`].
//!
//! If a widget or property supports inline it can detect it is in an inline context by [`WidgetMeasure::inline`] where the preferred
//! 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
//! during layout and [`LAYOUT`] will have inline constraints. During layout the [`WidgetLayout::inline`] value can
//! be set to the final inline info.
//!
//! After inline layout the are positioned so that the last line of the previous sibling connects with the first line of the next, all
//! of the widget visual properties must support this however, the [`WIDGET.bounds().inline()`] is available during render with cached
//! negative space clips that can quickly be used. If a visual property is not aware of inline it can potentially render over the
//! previous sibling, inline should be disabled for the widget if the property cannot support inline.
//!
//! [`WIDGET.bounds().inline()`]: crate::widget::info::WidgetBoundsInfo::inline
//! [`UiNode::measure`]: crate::widget::node::UiNode::measure
//! [`UiNode::Layout`]: crate::widget::node::UiNode::layout
//! [`UiNode::render`]: crate::widget::node::UiNode::render
//! [`widget::BORDER`]: crate::widget::BORDER
//! [`NestGroup::LAYOUT`]: crate::widget::builder::NestGroup::LAYOUT
//! [`window::inspector::show_bounds`]: fn@crate::window::inspector::show_bounds
//!
//! # Full API
//!
//! See [`zng_layout`], [`zng_wgt_transform`] and [`zng_wgt_size_offset`] for the full API.

pub use zng_layout::unit::{
    Align, AngleDegree, AngleGradian, AngleRadian, AngleTurn, AngleUnits, BoolVector2D, ByteLength, ByteUnits, CornerRadius2D, Dip, DipBox,
    DipCornerRadius, DipPoint, DipRect, DipSideOffsets, DipSize, DipToPx, DipVector, DistanceKey, Factor, Factor2d, FactorPercent,
    FactorSideOffsets, FactorUnits, GridSpacing, Layout1d, Layout2d, LayoutAxis, Length, LengthExpr, LengthUnits, Line,
    LineFromTuplesBuilder, Orientation2D, Point, Ppi, Ppm, Px, PxBox, PxConstraints, PxConstraints2d, PxCornerRadius, PxGridSpacing,
    PxLine, PxPoint, PxRect, PxSideOffsets, PxSize, PxToDip, PxTransform, PxVector, Rect, RectFromTuplesBuilder, ResolutionUnits,
    SideOffsets, SideOffsets2D, Size, TimeUnits, Transform, Vector,
};

pub use zng_var::types::{slerp_enabled, slerp_sampler};

pub use zng_layout::context::{
    InlineConstraints, InlineConstraintsLayout, InlineConstraintsMeasure, InlineSegment, InlineSegmentPos, LayoutDirection, LayoutMask,
    LayoutMetrics, LayoutMetricsSnapshot, LayoutPassId, TextSegmentKind, DIRECTION_VAR, LAYOUT,
};

pub use zng_app::widget::info::{WidgetLayout, WidgetMeasure};

pub use zng_wgt_transform::{
    backface_visibility, perspective, perspective_origin, rotate, rotate_x, rotate_y, rotate_z, scale, scale_x, scale_xy, scale_y, skew,
    skew_x, skew_y, transform, transform_origin, transform_style, translate, translate_x, translate_y, translate_z,
};

pub use zng_wgt_size_offset::{
    actual_bounds, actual_height, actual_height_px, actual_size, actual_size_px, actual_transform, actual_width, actual_width_px, baseline,
    force_height, force_size, force_width, height, max_height, max_size, max_width, min_height, min_size, min_width, offset, size,
    sticky_height, sticky_size, sticky_width, width, x, y, WidgetLength, WIDGET_SIZE,
};

pub use zng_wgt::{align, inline, is_ltr, is_rtl, margin, InlineMode};

pub use zng_wgt_container::{child_align, padding};

pub use zng_app::render::TransformStyle;