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, block 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 three 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 children 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§
- Align
xandyalignment.- Angle
Degree - Angle in degrees.
- Angle
Gradian - Angle in gradians.
- Angle
Radian - Angle in radians.
- Angle
Turn - Angle in turns (complete rotations).
- Bool
Vector2D - A 2d vector of booleans, useful for component-wise logic operations.
- Byte
Length - A length in bytes.
- Corner
Radius2D - Ellipses that define the radius of the four corners of a 2D box.
- Dip
- Device independent pixel.
- Distance
Key - Comparable key that represents the absolute distance between two pixel points.
- Factor
- Normalized multiplication factor.
- Factor2d
- Scale factor applied to x and y dimensions.
- Factor
Percent - Multiplication factor in percentage (0%-100%).
- Factor
Side Offsets - Scale factor applied to margins.
- Grid
Spacing - Spacing in-between grid cells in
Lengthunits. - Inline
Constraints Layout - Constraints for inline layout.
- Inline
Constraints Measure - Constraints for inline measure.
- Inline
Segment - Represents a segment in an inlined widget first or last row.
- Inline
Segment Pos - Position of an inline segment set by the inlining parent.
- LAYOUT
- Current layout context.
- Layout
Mask - Mask of values that can affect the layout operation of a value.
- Layout
Metrics - Layout metrics in a
LAYOUTcontext. - Layout
Metrics Snapshot - Layout metrics snapshot.
- Layout
Pass Id - Identifies the layout pass of a window.
- Line
- 2D line in
Lengthunits. - Point
- 2D point in
Lengthunits. - Px
- Device pixel.
- PxConstraints
- Pixel length constraints.
- PxConstraints2d
- Pixel size constraints.
- PxDensity
- Measurement of pixels in a screen or points in print.
- PxGrid
Spacing - Computed
GridSpacing. - PxLine
- Computed
Line. - Rect
- 2D rect in
Lengthunits. - Side
Offsets - 2D size offsets in
Lengthunits. - Side
Offsets2D - 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.
- Size
- 2D size in
Lengthunits. - Transform
- A transform builder type.
- Vector
- 2D vector in
Lengthunits. - WIDGET_
SIZE - Exact size property info.
- Widget
Layout - Represents the in-progress layout pass for a widget tree.
- Widget
Measure - Represents the in-progress measure pass for a widget tree.
Enums§
- Inline
Constraints - Constraints for inline measure or layout.
- Inline
Mode - Inline mode explicitly selected for a widget.
- Layout
Axis - Represents a layout dimension.
- Layout
Direction - Defines the layout flow direction.
- Length
- 1D length units.
- Length
Expr - Represents an unresolved
Lengthexpression. - Orientation2D
- Orientation of two 2D items.
- PxTransform
- A transform in device pixels.
- Text
Segment Kind - The type of an inline/text segment.
- Transform
Style - Defines if a widget is part of the same 3D space as the parent.
- Widget
Length - Represents the width or height property value set on a widget.
Statics§
- DIRECTION_
VAR - Wrap direction of text in a widget context.
Traits§
- Angle
Units - Extension methods for initializing angle units.
- Byte
Units - Extension methods for initializing
ByteLengthvalues. - DipToPx
- Conversion from
DiptoPxunits. - Factor
Units - Extension methods for initializing factor units.
- Layout1d
- Represents a one-dimensional length value that can be converted to a pixel length in a
LAYOUTcontext. - Layout2d
- Represents a two-dimensional value that can be converted to a pixel value in a
LAYOUTcontext. - Length
Units - Extension methods for initializing
Lengthunits. - Line
From Tuples Builder - Build a
Lineusing the syntax(x1, y1).to(x2, y2). - PxDensity
Units - Extension methods for initializing pixel density units.
- PxToDip
- Conversion from
PxtoDipunits. - Rect
From Tuples Builder - Build a
Rectusing the syntax(width, height).at(x, y). - Time
Units - Extension methods for initializing
Durationvalues.
Functions§
- actual_
bounds PGetter property, gets the latest rendered widget inner bounds in the window space.- actual_
height PGetter property, gets the latest layout widget inner height.- actual_
height_ px PGetter property, gets the latest layout widget inner height, in device pixels.- actual_
size PGetter property, gets the latest layout widget inner size.- actual_
size_ px PGetter property, gets the latest layout widget inner size, in device pixels.- actual_
transform PGetter property, gets the latest rendered widget inner transform.- actual_
width PGetter property, gets the latest layout widget inner width.- actual_
width_ px PGetter property, gets the latest layout widget inner width, in device pixels.- align
PAligns the widget within the available space.- backface_
visibility PSets 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.- baseline
PSet or overwrite the baseline of the widget.- child_
align PAligns the widget content within the available space.- force_
height PExact height of the widget ignoring the contextual max.- force_
size PExact size of the widget ignoring the contextual max.- force_
width PExact width of the widget ignoring the contextual max.- height
PExact height of the widget.- inline
PEnforce an inline mode on the widget.- is_ltr
PIf the layout direction is left-to-right.- is_rtl
PIf the layout direction is right-to-left.- margin
PMargin space around the widget.- max_
height PMaximum height of the widget.- max_
size PMaximum size of the widget.- max_
width PMaximum width of the widget.- min_
height PMinimum height of the widget.- min_
size PMinimum size of the widget.- min_
width PMinimum width of the widget.- offset
PWidget layout offset.- padding
PMargin space around the content of a widget.- perspective
PDistance from the Z plane (0) the viewer is, affects 3D transform on the widget’s children.- perspective_
origin PVanishing point used by 3D transforms in the widget’s children.- rotate
PRotate transform.- rotate_
x PRotate transform.- rotate_
y PRotate transform.- rotate_
z PSame asrotate.- scale
PScale transform.- scale_x
PScale X transform.- scale_
xy PScale X and Y transform.- scale_y
PScale Y transform.- size
PExact size of the widget.- skew
PSkew transform.- skew_x
PSkew X transform.- skew_y
PSkew Y transform.- slerp_
enabled - Calls
fwithis_slerp_enabledset toenabled. - slerp_
sampler - Spherical linear interpolation sampler.
- sticky_
height PRetain 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.- sticky_
size PRetain 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.- sticky_
width PRetain 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.- transform
PCustom transform.- transform_
origin PPoint relative to the widget inner bounds around which thetransformis applied.- transform_
style PDefines how the widget and children are positioned in 3D space.- translate
PTranslate transform.- translate_
x PTranslate X transform.- translate_
y PTranslate Y transform.- translate_
z PTranslate Z transform.- width
PExact width of the widget.- x
POffset on the x axis.- y
POffset on the y axis.
Type Aliases§
- DipBox
- A rectangle box in device independent pixels.
- DipCorner
Radius - Corner-radius in device independent pixels.
- DipPoint
- A point in device independent pixels.
- DipRect
- A rectangle in device independent pixels.
- DipSide
Offsets - Side-offsets in device independent pixels.
- DipSize
- A size in device pixels.
- DipVector
- A vector in device independent pixels.
- PxBox
- A rectangle box in device pixels.
- PxCorner
Radius - Corner-radius in device pixels.
- PxDensity2d
- Pixel density value that can differ between dimensions.
- PxPoint
- A point in device pixels.
- PxRect
- A rectangle in device pixels.
- PxSide
Offsets - Side-offsets in device pixels.
- PxSize
- A size in device pixels.
- PxVector
- A vector in device pixels.