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}