Expand description
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 properties, layout properties and 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
property influences the widget size and the align
property
influences the widget position, the margin
property potentially influences both size and position.
use zng::prelude::*;
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
property
and derived properties like 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::*;
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
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
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::*;
macro_rules! width {
($width:expr) => {
Text! {
layout::force_width = $width;
txt = stringify!($width);
widget::background_color = colors::BLUE.desaturate(50.pct());
}
};
}
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::*;
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
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
and 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::*;
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::*;
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.
§Full API
See zng_layout
, zng_wgt_transform
and zng_wgt_size_offset
for the full API.
Structs§
x
andy
alignment.- Angle in degrees.
- Angle in gradians.
- Angle in radians.
- Angle in turns (complete rotations).
- A 2d vector of booleans, useful for component-wise logic operations.
- A length in bytes.
- Ellipses that define the radius of the four corners of a 2D box.
- Device independent pixel.
- Comparable key that represents the absolute distance between two pixel points.
- Normalized multiplication factor.
- Scale factor applied to x and y dimensions.
- Multiplication factor in percentage (0%-100%).
- Scale factor applied to margins.
- Spacing in-between grid cells in
Length
units. - Constraints for inline layout.
- Constraints for inline measure.
- Represents a segment in an inlined widget first or last row.
- Position of an inline segment set by the inlining parent.
- Current layout context.
- Mask of values that can affect the layout operation of a value.
- Layout metrics in a
LAYOUT
context. - Layout metrics snapshot.
- Identifies the layout pass of a window.
- 2D line in
Length
units. - 2D point in
Length
units. - Pixels-per-inch resolution.
- Pixels-per-meter resolution.
- Device pixel.
- Pixel length constraints.
- Pixel size constraints.
- Computed
GridSpacing
. - Computed
Line
. - 2D rect in
Length
units. - 2D size offsets in
Length
units. - A group of 2D side offsets, which correspond to top/right/bottom/left for borders, padding, and margins in CSS, optionally tagged with a unit.
- 2D size in
Length
units. - A transform builder type.
- 2D vector in
Length
units. - Exact size property info.
- Represents the in-progress layout pass for a widget tree.
- Represents the in-progress measure pass for a widget tree.
Enums§
- Constraints for inline measure or layout.
- Inline mode explicitly selected for a widget.
- Represents a layout dimension.
- Defines the layout flow direction.
- 1D length units.
- Represents an unresolved
Length
expression. - Orientation of two 2D items.
- A transform in device pixels.
- The type of an inline/text segment.
- Defines if a widget is part of the same 3D space as the parent.
- Represents the width or height property value set on a widget.
Statics§
- Wrap direction of text in a widget context.
Traits§
- Extension methods for initializing angle units.
- Extension methods for initializing
ByteLength
values. - Extension methods for initializing factor units.
- Represents a one-dimensional length value that can be converted to a pixel length in a
LAYOUT
context. - Represents a two-dimensional value that can be converted to a pixel value in a
LAYOUT
context. - Extension methods for initializing
Length
units. - Build a
Line
using the syntax(x1, y1).to(x2, y2)
. - Build a
Rect
using the syntax(width, height).at(x, y)
. - Extension methods for initializing resolution units.
- Extension methods for initializing
Duration
values.
Functions§
P
Getter property, gets the latest rendered widget inner bounds in the window space.P
Getter property, gets the latest rendered widget inner height.P
Getter property, gets the latest rendered widget inner height, in device pixels.P
Getter property, gets the latest rendered widget inner size.P
Getter property, gets the latest rendered widget inner size, in device pixels.P
Getter property, gets the latest rendered widget inner transform.P
Getter property, gets the latest rendered widget inner width.P
Getter property, gets the latest rendered widget inner width, in device pixels.P
Aligns the widget within the available space.P
Sets if the widget is still visible when it is turned back towards the viewport due to rotations in X or Y axis in the widget or in parent widgets.P
Set or overwrite the baseline of the widget.P
Aligns the widget content within the available space.P
Exact height of the widget ignoring the contextual max.P
Exact size of the widget ignoring the contextual max.P
Exact width of the widget ignoring the contextual max.P
Exact height of the widget.P
Enforce an inline mode on the widget.P
If the layout direction is left-to-right.P
If the layout direction is right-to-left.P
Margin space around the widget.P
Maximum height of the widget.P
Maximum size of the widget.P
Maximum width of the widget.P
Minimum height of the widget.P
Minimum size of the widget.P
Minimum width of the widget.P
Widget layout offset.P
Margin space around the content of a widget.P
Distance from the Z plane (0) the viewer is, affects 3D transform on the widget’s children.P
Vanishing point used by 3D transforms in the widget’s children.P
Rotate transform.P
Rotate transform.P
Rotate transform.P
Same asrotate
.P
Scale transform.P
Scale X transform.P
Scale X and Y transform.P
Scale Y transform.P
Exact size of the widget.P
Skew transform.P
Skew X transform.P
Skew Y transform.- Spherical linear interpolation sampler.
P
Retain the widget’s previous height if the new layout height is smaller. The widget is layout using its previous height as the minimum height constrain.P
Retain the widget’s previous size if the new layout size is smaller. The widget is layout using its previous size as the minimum size constrain.P
Retain the widget’s previous width if the new layout width is smaller. The widget is layout using its previous width as the minimum width constrain.P
Custom transform.P
Point relative to the widget inner bounds around which thetransform
is applied.P
Defines how the widget and children are positioned in 3D space.P
Translate transform.P
Translate X transform.P
Translate Y transform.P
Translate Z transform.P
Exact width of the widget.P
Offset on the x axis.P
Offset on the y axis.
Type Aliases§
- A rectangle box in device independent pixels.
- Corner-radius in device independent pixels.
- A point in device independent pixels.
- A rectangle in device independent pixels.
- Side-offsets in device independent pixels.
- A size in device pixels.
- A vector in device independent pixels.
- A rectangle box in device pixels.
- Corner-radius in device pixels.
- A point in device pixels.
- A rectangle in device pixels.
- Side-offsets in device pixels.
- A size in device pixels.
- A vector in device pixels.