1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License in the LICENSE-APACHE file or at:
//     https://www.apache.org/licenses/LICENSE-2.0

//! Layout utilities
//!
//! For documentation of layout resolution, see the [`Layout`] trait.
//!
//! Size units are physical (real) pixels. This applies to most of KAS.
//!
//! ## Data types
//!
//! [`SizeRules`] is the "heart" of widget layout, used to specify a widget's
//! size requirements. It provides various methods to compute derived rules
//! and [`SizeRules::solve_seq`], the "muscle" of the layout engine.
//!
//! [`AxisInfo`], [`Margins`] and [`Stretch`] are auxilliary data types.
//!
//! ## Solvers
//!
//! The [`RulesSolver`] and [`RulesSetter`] traits define interfaces for
//! layout engines:
//!
//! -   [`SingleSolver`] and [`SingleSetter`] are trivial implementations for
//!     single-child parents
//! -   [`RowSolver`] and [`RowSetter`] set out a row or column of children.
//!     These are parametrised over `S: RowStorage` allowing both efficient
//!     operation on a small fixed number of children with [`FixedRowStorage`]
//!     and operation on a over a `Vec` with [`DynRowStorage`].
//! -   [`GridSolver`] and [`GridSetter`] set out children assigned to grid
//!     cells with optional cell-spans. This is the most powerful and flexible
//!     layout engine.
//!
//! [`RowPositionSolver`] may be used with widgets set out by [`RowSetter`]
//! to quickly locate children from a `coord` or `rect`.

mod align;
mod grid_solver;
mod row_solver;
mod single_solver;
mod size_rules;
mod size_types;
mod sizer;
mod storage;
mod visitor;

use crate::dir::{Direction, Directional, Directions};
use crate::event::ConfigMgr;
use crate::geom::{Coord, Rect};
use crate::theme::{DrawMgr, SizeMgr};
use crate::WidgetId;

#[allow(unused)] use crate::Layout;

pub use align::{Align, AlignHints, AlignPair};
pub use grid_solver::{DefaultWithLen, GridChildInfo, GridDimensions, GridSetter, GridSolver};
pub use row_solver::{RowPositionSolver, RowSetter, RowSolver};
pub use single_solver::{SingleSetter, SingleSolver};
pub use size_rules::SizeRules;
pub use size_types::*;
pub use sizer::{solve_size_rules, RulesSetter, RulesSolver, SolveCache};
pub use storage::*;
pub use visitor::{FrameStorage, PackStorage, Visitor};

/// Information on which axis is being resized
///
/// Also conveys the size of the other axis, if fixed.
#[derive(Copy, Clone, Debug)]
pub struct AxisInfo {
    vertical: bool,
    has_fixed: bool,
    other_axis: i32,
    align: Option<Align>,
}

impl AxisInfo {
    /// Construct with direction and an optional value for the other axis
    ///
    /// This method is *usually* not required by user code.
    #[inline]
    pub fn new(vertical: bool, fixed: Option<i32>, align: Option<Align>) -> Self {
        AxisInfo {
            vertical,
            has_fixed: fixed.is_some(),
            other_axis: fixed.unwrap_or(0),
            align,
        }
    }

    /// Construct a copy using the given alignment hints
    #[inline]
    pub fn with_align_hints(mut self, hints: AlignHints) -> Self {
        self.align = hints.extract(self).or(self.align);
        self
    }

    /// True if the current axis is vertical
    #[inline]
    pub fn is_vertical(self) -> bool {
        self.vertical
    }

    /// True if the current axis is horizontal
    #[inline]
    pub fn is_horizontal(self) -> bool {
        !self.vertical
    }

    /// Get align parameter
    #[inline]
    pub fn align(self) -> Option<Align> {
        self.align
    }

    /// Set align parameter
    #[inline]
    pub fn set_align(&mut self, align: Option<Align>) {
        self.align = align;
    }

    /// Set default alignment
    ///
    /// If the optional alignment parameter is `None`, replace with `align`.
    #[inline]
    pub fn set_default_align(&mut self, align: Align) {
        if self.align.is_none() {
            self.align = Some(align);
        }
    }

    /// Set default alignment
    ///
    /// If the optional alignment parameter is `None`, replace with either
    /// `horiz` or `vert` depending on this axis' orientation.
    #[inline]
    pub fn set_default_align_hv(&mut self, horiz: Align, vert: Align) {
        if self.align.is_none() {
            if self.is_horizontal() {
                self.align = Some(horiz);
            } else {
                self.align = Some(vert);
            }
        }
    }

    /// Get align parameter, defaulting to [`Align::Default`]
    #[inline]
    pub fn align_or_default(self) -> Align {
        self.align.unwrap_or(Align::Default)
    }

    /// Get align parameter, defaulting to [`Align::Center`]
    #[inline]
    pub fn align_or_center(self) -> Align {
        self.align.unwrap_or(Align::Center)
    }

    /// Get align parameter, defaulting to [`Align::Stretch`]
    #[inline]
    pub fn align_or_stretch(self) -> Align {
        self.align.unwrap_or(Align::Stretch)
    }

    /// Size of other axis, if fixed
    #[inline]
    pub fn other(&self) -> Option<i32> {
        if self.has_fixed {
            Some(self.other_axis)
        } else {
            None
        }
    }

    /// Size of other axis, if fixed and `vertical` matches this axis.
    #[inline]
    pub fn size_other_if_fixed(&self, vertical: bool) -> Option<i32> {
        if vertical == self.vertical && self.has_fixed {
            Some(self.other_axis)
        } else {
            None
        }
    }

    /// Subtract `x` from size of other axis (if applicable)
    #[inline]
    pub fn sub_other(&mut self, x: i32) {
        self.other_axis -= x;
    }
}

impl Directional for AxisInfo {
    type Flipped = Self;
    type Reversed = Self;

    fn flipped(mut self) -> Self::Flipped {
        self.vertical = !self.vertical;
        self.has_fixed = false;
        self
    }

    #[inline]
    fn reversed(self) -> Self::Reversed {
        self
    }

    #[inline]
    fn as_direction(self) -> Direction {
        match self.vertical {
            false => Direction::Right,
            true => Direction::Down,
        }
    }
}

impl From<AxisInfo> for Directions {
    fn from(axis: AxisInfo) -> Directions {
        match axis.vertical {
            false => Directions::LEFT | Directions::RIGHT,
            true => Directions::UP | Directions::DOWN,
        }
    }
}

/// Implementation generated by use of `layout = ..` property of `#[widget]`
///
/// This trait need not be implemented by the user, however it may be useful to
/// adjust the result of an automatic implementation, for example:
/// ```
/// # extern crate kas_core as kas;
/// use kas::prelude::*;
///
/// impl_scope! {
///     #[derive(Debug)]
///     #[widget {
///         layout = "Example";
///     }]
///     struct Example {
///         core: widget_core!(),
///     }
///     impl Layout for Self {
///         fn size_rules(&mut self, size_mgr: SizeMgr, axis: AxisInfo) -> SizeRules {
///             let mut rules = kas::layout::AutoLayout::size_rules(self, size_mgr, axis);
///             rules.set_stretch(Stretch::High);
///             rules
///         }
///     }
/// }
/// ```
///
/// It is not recommended to import this trait since method names conflict with [`Layout`].
pub trait AutoLayout {
    /// Get size rules for the given axis
    ///
    /// This functions identically to [`Layout::size_rules`].
    fn size_rules(&mut self, size_mgr: SizeMgr, axis: AxisInfo) -> SizeRules;

    /// Set size and position
    ///
    /// This functions identically to [`Layout::set_rect`].
    fn set_rect(&mut self, mgr: &mut ConfigMgr, rect: Rect);

    /// Translate a coordinate to a [`WidgetId`]
    ///
    /// This functions identically to [`Layout::find_id`].
    fn find_id(&mut self, coord: Coord) -> Option<WidgetId>;

    /// Draw a widget and its children
    ///
    /// This functions identically to [`Layout::draw`].
    fn draw(&mut self, draw: DrawMgr);
}

#[cfg(test)]
#[test]
fn size() {
    assert_eq!(std::mem::size_of::<AxisInfo>(), 8);
}