Trait kas_core::Layout

source ·
pub trait Layout {
    fn size_rules(&mut self, size_mgr: SizeMgr<'_>, axis: AxisInfo) -> SizeRules;
    fn set_rect(&mut self, mgr: &mut ConfigMgr<'_>, rect: Rect);
    fn find_id(&mut self, coord: Coord) -> Option<WidgetId>;
    fn draw(&mut self, draw: DrawMgr<'_>);
}
Expand description

Positioning and drawing routines for Widgets

This trait is related to Widget, but may be used independently.

Implementing Layout

There are three cases:

  • For a non-widget, all methods must be implemented directly.
  • For a Widget without using the layout macro property, the Self::set_rect and Self::find_id methods gain default implementations (generated via macro).
  • For a Widget where the #[widget{ layout = .. }] property is set (see macros::widget documentation), all methods have a default implementation. Custom implementations may use AutoLayout to access these default implementations.

Solving layout

Layout is resolved as follows:

  1. Widget::configure is called (widgets only), and may be used to load assets
  2. Self::size_rules is called at least once for each axis
  3. Self::set_rect is called to position elements. This may use data cached by size_rules.
  4. Self::find_id may be used to find the widget under the mouse and Self::draw to draw elements.

Usually, Layout::size_rules methods are called recursively. To instead solve layout for a single widget/layout object, it may be useful to use layout::solve_size_rules or layout::SolveCache.

Required Methods§

Get size rules for the given axis

Typically, this method is called twice: first for the horizontal axis, second for the vertical axis (with resolved width available through the axis parameter allowing content wrapping). For a description of the widget size model, see SizeRules.

This method is expected to cache any size requirements calculated from children which would be required for space allocations in Self::set_rect. As an example, the horizontal SizeRules for a row layout is the sum of the rules for each column (plus margins); these per-column SizeRules are also needed to calculate column widths in Self::size_rules once the available size is known.

For row/column/grid layouts, a crate::layout::RulesSolver engine may be useful.

Default implementation:

Set size and position

This method is called after Self::size_rules and may use values cached by size_rules (in the case size_rules is not called first, the widget may exhibit incorrect layout but should not panic). This method should not write over values cached by size_rules since set_rect may be called multiple times consecutively. After set_rect is called, the widget must be ready for drawing and event handling.

The size of the assigned rect is normally at least the minimum size requested by Self::size_rules, but this is not guaranteed. In case this minimum is not met, it is permissible for the widget to draw outside of its assigned rect and to not function as normal.

The assigned rect may be larger than the widget’s size requirements, regardless of the Stretch policy used. If the widget should never stretch, it must align itself. Example: the CheckBox widget uses an AlignPair (set from size_rules’s AxisInfo) and uses ConfigMgr::align_feature. Another example: Label uses a Text object which handles alignment internally.

Default implementation:

  • Independent usage: no default
  • For a widget without layout property, set rect field of widget_core!()
  • For a widget with the layout property, call AutoLayout::set_rect

Default: set rect of widget_core!() field. If layout = .. property is used, also calls <Self as AutoLayout>::set_rect.

Translate a coordinate to a WidgetId

This method is used to determine which widget reacts to the mouse cursor or a touch event. The result affects mouse-hover highlighting, event handling by the target, and potentially also event handling by other widgets (e.g. a Label widget will not handle touch events, but if it is contained by a ScrollRegion, that widget may capture these via Widget::handle_unused to implement touch scrolling).

The result is usually the widget which draws at the given coord, but does not have to be. For example, a Button widget will return its own id for coordinates drawn by internal content, while the CheckButton widget uses an internal component for event handling and thus reports this component’s id even over its own area.

It is expected that Layout::set_rect is called before this method, but failure to do so should not cause a fatal error.

The default implementation suffices for widgets without children as well as widgets using the layout property of #[widget]. Custom implementations may be required if:

  • A custom Layout implementation is used
  • Event stealing or donation is desired (but note that layout = button: ..; does this already)

The implementation is slightly different for widgets and non-widget components:

  • Widgets should test self.rect().contains(coord), returning None if this test is false; otherwise, they should always return some WidgetId, either a childs or their own.
  • Widgets may use a translated coordinate space: recursion uses child.find_id(coord + self.translation()).

Default implementation:

  • Non-widgets: no default implementation.
  • For a widget without the layout property, self.rect().contains(coord).then(|| self.id()).
  • For a widget with the layout property, the following snippet:
    if !self.rect().contains(coord) {
        return None;
    }
    let coord = coord + self.translation();
    (#layout).find_id(coord).or_else(|| Some(self.id()))

Draw a widget and its children

This method is invoked each frame to draw visible widgets. It should draw itself and recurse into all visible children.

It is expected that Self::set_rect is called before this method, but failure to do so should not cause a fatal error.

The draw parameter is pre-parameterized with this widget’s WidgetId, allowing drawn components to react to input state. This implies that when calling draw on children, the child’s id must be supplied via DrawMgr::re_id or DrawMgr::recurse.

Default implementation:

  • No default implementation, except,
  • For a widget with the layout property, call AutoLayout::draw

Implementations on Foreign Types§

Implementors§