kas_core/layout/mod.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 utilities
7//!
8//! For documentation of layout resolution, see the [`Layout`] trait.
9//!
10//! Size units are physical (real) pixels. This applies to most of KAS.
11//!
12//! ## Data types
13//!
14//! [`SizeRules`] is the "heart" of widget layout, used to specify a widget's
15//! size requirements. It provides various methods to compute derived rules
16//! and [`SizeRules::solve_seq`], the "muscle" of the layout engine.
17//!
18//! [`AxisInfo`], [`Margins`] and [`Stretch`] are auxilliary data types.
19//!
20//! ## Solvers
21//!
22//! The [`RulesSolver`] and [`RulesSetter`] traits define interfaces for
23//! layout engines:
24//!
25//! - [`SingleSolver`] and [`SingleSetter`] are trivial implementations for
26//! single-child parents
27//! - [`RowSolver`] and [`RowSetter`] set out a row or column of children.
28//! These are parametrised over `S: RowStorage` allowing both efficient
29//! operation on a small fixed number of children with [`FixedRowStorage`]
30//! and operation on a over a `Vec` with [`DynRowStorage`].
31//! - [`GridSolver`] and [`GridSetter`] set out children assigned to grid
32//! cells with optional cell-spans. This is the most powerful and flexible
33//! layout engine.
34//!
35//! [`RowPositionSolver`] may be used with widgets set out by [`RowSetter`]
36//! to quickly locate children from a `coord` or `rect`.
37
38mod align;
39mod grid_solver;
40mod row_solver;
41mod single_solver;
42mod size_rules;
43mod size_types;
44mod sizer;
45mod storage;
46mod visitor;
47
48use crate::dir::{Direction, Directional, Directions};
49
50#[allow(unused)] use crate::Layout;
51
52pub use align::{Align, AlignHints, AlignPair};
53pub use grid_solver::{DefaultWithLen, GridCellInfo, GridDimensions, GridSetter, GridSolver};
54pub use row_solver::{RowPositionSolver, RowSetter, RowSolver};
55pub use single_solver::{SingleSetter, SingleSolver};
56pub use size_rules::SizeRules;
57pub use size_types::*;
58pub use sizer::{solve_size_rules, RulesSetter, RulesSolver, SolveCache};
59pub use storage::*;
60pub use visitor::{FrameStorage, PackStorage};
61
62#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
63#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
64pub use visitor::{Visitable, VisitableList, Visitor};
65
66/// Information on which axis is being resized
67///
68/// Also conveys the size of the other axis, if fixed.
69#[derive(Copy, Clone, Debug)]
70pub struct AxisInfo {
71 vertical: bool,
72 has_fixed: bool,
73 other_axis: i32,
74}
75
76impl AxisInfo {
77 /// Construct with direction and an optional value for the other axis
78 ///
79 /// This method is *usually* not required by user code.
80 #[inline]
81 pub fn new(vertical: bool, fixed: Option<i32>) -> Self {
82 AxisInfo {
83 vertical,
84 has_fixed: fixed.is_some(),
85 other_axis: fixed.unwrap_or(0),
86 }
87 }
88
89 /// True if the current axis is vertical
90 #[inline]
91 pub fn is_vertical(self) -> bool {
92 self.vertical
93 }
94
95 /// True if the current axis is horizontal
96 #[inline]
97 pub fn is_horizontal(self) -> bool {
98 !self.vertical
99 }
100
101 /// Size of other axis, if fixed
102 #[inline]
103 pub fn other(&self) -> Option<i32> {
104 if self.has_fixed {
105 Some(self.other_axis)
106 } else {
107 None
108 }
109 }
110
111 /// Subtract `x` from size of other axis (if applicable)
112 #[inline]
113 pub fn sub_other(&mut self, x: i32) {
114 self.other_axis -= x;
115 }
116}
117
118impl Directional for AxisInfo {
119 type Flipped = Self;
120 type Reversed = Self;
121
122 fn flipped(mut self) -> Self::Flipped {
123 self.vertical = !self.vertical;
124 self.has_fixed = false;
125 self
126 }
127
128 #[inline]
129 fn reversed(self) -> Self::Reversed {
130 self
131 }
132
133 #[inline]
134 fn as_direction(self) -> Direction {
135 match self.vertical {
136 false => Direction::Right,
137 true => Direction::Down,
138 }
139 }
140}
141
142impl From<AxisInfo> for Directions {
143 fn from(axis: AxisInfo) -> Directions {
144 match axis.vertical {
145 false => Directions::LEFT | Directions::RIGHT,
146 true => Directions::UP | Directions::DOWN,
147 }
148 }
149}
150
151/// Macro-generated implementation of layout over a [`Visitor`]
152///
153/// This method is implemented by the [`#widget`] macro when a [`layout`]
154/// specification is provided.
155/// Direct implementations of this trait are not supported.
156///
157/// This trait may be used in user-code where a `layout` specification is used
158/// *and* custom behaviour is provided for one or more layout methods, for example:
159/// ```
160/// # extern crate kas_core as kas;
161/// use kas::prelude::*;
162///
163/// impl_scope! {
164/// #[widget {
165/// Data = ();
166/// layout = "Example";
167/// }]
168/// struct Example {
169/// core: widget_core!(),
170/// }
171/// impl Layout for Self {
172/// fn size_rules(&mut self, sizer: SizeCx, axis: AxisInfo) -> SizeRules {
173/// let mut rules = self.layout_visitor().size_rules(sizer, axis);
174/// rules.set_stretch(Stretch::High);
175/// rules
176/// }
177/// }
178/// }
179/// ```
180///
181/// [`#widget`]: crate::widget
182/// [`layout`]: crate::widget#layout-1
183pub trait LayoutVisitor {
184 /// Layout defined by a [`Visitor`]
185 fn layout_visitor(&mut self) -> Visitor<impl Visitable>;
186}
187
188#[cfg(test)]
189#[test]
190fn size() {
191 assert_eq!(std::mem::size_of::<AxisInfo>(), 8);
192}