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}