kas_core/core/
layout.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Layout, Tile and TileExt traits
7
8use crate::Id;
9use crate::event::ConfigCx;
10use crate::geom::{Coord, Rect};
11use crate::layout::{AlignHints, AxisInfo, SizeRules};
12use crate::theme::{DrawCx, SizeCx};
13use kas_macros::autoimpl;
14
15#[allow(unused)] use super::{Events, Tile, Widget};
16#[allow(unused)] use crate::layout::{self, AlignPair};
17#[allow(unused)] use kas_macros as macros;
18
19/// Positioning and drawing routines for [`Widget`]s
20///
21/// `Layout` is used to implement [`Widget`] sizing and drawing operations
22/// ("layout").
23/// See [`Widget`] documentation and the [`#widget`] macro.
24/// `Layout` may not be implemented independently.
25///
26/// # Implementation
27///
28/// The [`#widget`] macro will, when its `layout` property is specified,
29/// generate an implementation of this trait (if omitted from the surrounding
30/// `#[impl_self]`) or provide default implementations of its methods (if an
31/// explicit impl of `Layout` is found but some methods are missing).
32///
33/// [`#widget`]: macros::widget
34#[autoimpl(for<T: trait + ?Sized> &'_ mut T, Box<T>)]
35pub trait Layout {
36    /// Get the widget's region
37    ///
38    /// Coordinates are relative to the parent's coordinate space.
39    ///
40    /// This method is usually implemented by the `#[widget]` macro.
41    /// See also [`kas::widget_set_rect`].
42    fn rect(&self) -> Rect;
43
44    /// Get size rules for the given axis
45    ///
46    /// # Calling
47    ///
48    /// This method is called during sizing (see
49    /// [widget lifecycle](Widget#widget-lifecycle)).
50    /// Typically, this method is called twice: first for the horizontal axis,
51    /// second for the vertical axis (with resolved width available through
52    /// the `axis` parameter allowing content wrapping).
53    /// For a description of the widget size model, see [`SizeRules`].
54    ///
55    /// ## Call order
56    ///
57    /// Required: `self` is configured ([`ConfigCx::configure`]) before this
58    /// method is called, and that `size_rules` is called for the
59    /// horizontal axis before it is called for the vertical axis.
60    /// Further, [`Self::set_rect`] must be called after this method before
61    /// drawing or event handling.
62    ///
63    /// # Implementation
64    ///
65    /// This method is expected to cache any size requirements calculated from
66    /// children which would be required for space allocations in
67    /// [`Self::set_rect`]. As an example, the horizontal [`SizeRules`] for a
68    /// row layout is the sum of the rules for each column (plus margins);
69    /// these per-column [`SizeRules`] are also needed to calculate column
70    /// widths in [`Self::size_rules`] once the available size is known.
71    ///
72    /// For row/column/grid layouts, a [`crate::layout::RulesSolver`] engine
73    /// may be useful.
74    ///
75    /// ## Default implementation
76    ///
77    /// The `#[widget]` macro may implement this method as a wrapper over
78    /// the corresponding [`MacroDefinedLayout`].
79    fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules;
80
81    /// Set size and position
82    ///
83    /// # Calling
84    ///
85    /// This method is called after [`Self::size_rules`] and may use values
86    /// cached by `size_rules` (in the case `size_rules` is not called first,
87    /// the widget may exhibit incorrect layout but should not panic). This
88    /// method should not write over values cached by `size_rules` since
89    /// `set_rect` may be called multiple times consecutively.
90    /// After `set_rect` is called, the widget must be ready for drawing and event handling.
91    ///
92    /// ## Call order
93    ///
94    /// Required: [`Self::size_rules`] is called for both axes before this
95    /// method is called, and that this method has been called *after* the last
96    /// call to [`Self::size_rules`] *before* any of the following methods:
97    /// [`Layout::try_probe`], [`Layout::draw`], [`Events::handle_event`].
98    ///
99    /// # Implementation
100    ///
101    /// The size of the assigned `rect` is normally at least the minimum size
102    /// requested by [`Self::size_rules`], but this is not guaranteed. In case
103    /// this minimum is not met, it is permissible for the widget to draw
104    /// outside of its assigned `rect` and to not function as normal.
105    ///
106    /// The assigned `rect` may be larger than the widget's size requirements,
107    /// regardless of the [`Stretch`] policy used: containers divide up space
108    /// based on children's [`SizeRules`] but do not attempt to align content
109    /// when excess space is available. Instead, content is responsible for
110    /// aligning itself using the provided `hints` and/or local information.
111    ///
112    /// ## Default implementation
113    ///
114    /// The `#[widget]` macro may implement this method as a wrapper over
115    /// the corresponding [`MacroDefinedLayout`].
116    ///
117    /// [`Stretch`]: crate::layout::Stretch
118    fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints);
119
120    /// Probe a coordinate for a widget's [`Id`]
121    ///
122    /// Returns the [`Id`] of the widget expected to handle clicks and touch
123    /// events at the given `coord`, or `None` if `self` does not occupy this
124    /// `coord`. Typically the result is the lowest descendant in
125    /// the widget tree at the given `coord`, but it is not required to be; e.g.
126    /// a `Button` may use an inner widget as a label but return its own [`Id`]
127    /// to indicate that the button (not the inner label) handles clicks.
128    ///
129    /// # Calling
130    ///
131    /// ## Call order
132    ///
133    /// It is expected that [`Layout::set_rect`] is called before this method,
134    /// but failure to do so should not cause a fatal error.
135    ///
136    /// # Implementation
137    ///
138    /// Widgets should implement [`Tile::probe`] instead, in which case an
139    /// implemention of this method will be provided:
140    /// ```ignore
141    /// self.rect().contains(coord).then(|| self.probe(coord))
142    /// ```
143    /// Derive-mode widgets may implement either method.
144    ///
145    /// ## Default implementation
146    ///
147    /// Non-widgets do not have an [`Id`], and therefore should use the default
148    /// implementation which simply returns `None`.
149    fn try_probe(&self, coord: Coord) -> Option<Id> {
150        let _ = coord;
151        None
152    }
153
154    /// Draw a widget and its children
155    ///
156    /// # Calling
157    ///
158    /// This method is invoked each frame to draw visible widgets. It should
159    /// draw itself and recurse into all visible children.
160    ///
161    /// ## Call order
162    ///
163    /// It is expected that [`Self::set_rect`] is called before this method,
164    /// but failure to do so should not cause a fatal error.
165    ///
166    /// # Implementation
167    ///
168    /// ## Default implementation
169    ///
170    /// The `#[widget]` macro may implement this method as a wrapper over
171    /// the corresponding [`MacroDefinedLayout`].
172    ///
173    /// ## Method modification
174    ///
175    /// The `#[widget]` macro injects a call to [`DrawCx::set_id`] into this
176    /// method where possible, allowing correct detection of disabled and
177    /// highlight states.
178    ///
179    /// This method modification should never cause issues (besides the implied
180    /// limitation that widgets cannot easily detect a parent's state while
181    /// being drawn).
182    fn draw(&self, draw: DrawCx);
183}
184
185/// Macro-defined layout
186///
187/// This trait is a copy of [`Layout`], implemented by the [`#[layout]`] attribute.
188///
189/// This trait may prove useful to modify the layout code generated by
190/// [`#[layout]`] with additional code. For examples, see the `RadioButton` and
191/// `SpinBox` widgets.
192///
193/// [`#[layout]`]: kas::layout
194pub trait MacroDefinedLayout {
195    /// Get the widget's region
196    fn rect(&self) -> Rect;
197
198    /// Get size rules for the given axis
199    fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules;
200
201    /// Set size and position
202    fn set_rect(&mut self, cx: &mut ConfigCx, rect: Rect, hints: AlignHints);
203
204    /// Probe a coordinate for a widget's [`Id`]
205    fn try_probe(&self, coord: Coord) -> Option<Id>;
206
207    /// Draw a widget and its children
208    fn draw(&self, draw: DrawCx);
209}