wrflib/
layout.rs

1use crate::*;
2
3/// Indicates when to wrap the current line to a new line. See also [`Direction`].
4#[derive(Copy, Clone, Debug, PartialEq)]
5pub enum LineWrap {
6    /// Never wrap to a new line.
7    None,
8
9    /// Wrap to a new line when the available width is exhausted.
10    Overflow,
11}
12impl LineWrap {
13    /// TODO(JP): Replace these with LineWrap::default() when
14    /// <https://github.com/rust-lang/rust/issues/67792> gets done
15    pub const DEFAULT: LineWrap = LineWrap::None;
16}
17impl Default for LineWrap {
18    fn default() -> Self {
19        LineWrap::DEFAULT
20    }
21}
22
23/// Configure how a [`CxLayoutBox`] is going to walk, typically bounded by the
24/// dimensions of a parent [`CxLayoutBox`].
25#[derive(Copy, Clone, Debug)]
26pub(crate) struct Layout {
27    /// See [`LayoutSize`].
28    pub layout_size: LayoutSize,
29    /// See [`Padding`].
30    pub padding: Padding,
31    /// See [`Direction`].
32    pub direction: Direction,
33    /// See [`LineWrap`].
34    pub line_wrap: LineWrap,
35    /// Absolutely position by overriding the [`CxLayoutBox::origin`] with (0,0) instead of using the parent's
36    /// current position.
37    pub absolute: bool,
38    /// Override the maximum size of the [`Window`]/[`Pass`]. Should typically
39    /// not be used; instead set [`CxLayoutBox::width`] and [`CxLayoutBox::height`]
40    /// through [`Layout::layout_size`].
41    pub abs_size: Option<Vec2>,
42}
43
44impl Layout {
45    /// TODO(JP): Replace these with Layout::default() when
46    /// <https://github.com/rust-lang/rust/issues/67792> gets done
47    pub const DEFAULT: Layout = Layout {
48        layout_size: LayoutSize::DEFAULT,
49        padding: Padding::DEFAULT,
50        direction: Direction::DEFAULT,
51        line_wrap: LineWrap::DEFAULT,
52        absolute: false,
53        abs_size: None,
54    };
55}
56
57impl Default for Layout {
58    fn default() -> Self {
59        Layout::DEFAULT
60    }
61}
62
63/// Determines how a [`CxLayoutBox`] should walk. Can be applied to a new [`CxLayoutBox`]
64/// through [`Layout::layout_size`], or directly to move an existing [`CxLayoutBox`] by
65/// using [`Cx::add_box`].
66#[derive(Copy, Clone, Debug)]
67pub struct LayoutSize {
68    pub width: Width,
69    pub height: Height,
70}
71
72impl LayoutSize {
73    /// TODO(JP): Replace these with Align::default() when
74    /// <https://github.com/rust-lang/rust/issues/67792> gets done
75    pub const DEFAULT: LayoutSize = LayoutSize { width: Width::DEFAULT, height: Height::DEFAULT };
76    pub const FILL: LayoutSize = LayoutSize { width: Width::Fill, height: Height::Fill };
77
78    pub const fn new(w: Width, h: Height) -> Self {
79        Self { width: w, height: h }
80    }
81}
82impl Default for LayoutSize {
83    fn default() -> Self {
84        LayoutSize::DEFAULT
85    }
86}
87
88/// Inner padding dimensions that should be applied on top of the width/height
89/// from the parent [`CxLayoutBox`].
90
91/// TODO(JP): these values can be negative, which can be quite confusing, but we
92/// seem to actually honor that in the layout boxes code. Might be good to look into that
93/// and see if we should forbid that or not (we seem to never actually do that yet).
94#[derive(Clone, Copy, Debug)]
95pub struct Padding {
96    pub l: f32,
97    pub t: f32,
98    pub r: f32,
99    pub b: f32,
100}
101impl Padding {
102    pub const ZERO: Padding = Padding { l: 0.0, t: 0.0, r: 0.0, b: 0.0 };
103
104    /// TODO(JP): Replace these with Padding::default() when
105    /// <https://github.com/rust-lang/rust/issues/67792> gets done
106    pub const DEFAULT: Padding = Padding::ZERO;
107
108    pub const fn all(v: f32) -> Padding {
109        Padding { l: v, t: v, r: v, b: v }
110    }
111
112    pub const fn left(v: f32) -> Padding {
113        Padding { l: v, ..Padding::ZERO }
114    }
115
116    pub const fn top(v: f32) -> Padding {
117        Padding { t: v, ..Padding::ZERO }
118    }
119
120    pub const fn right(v: f32) -> Padding {
121        Padding { r: v, ..Padding::ZERO }
122    }
123
124    pub const fn bottom(v: f32) -> Padding {
125        Padding { b: v, ..Padding::ZERO }
126    }
127}
128impl Default for Padding {
129    fn default() -> Self {
130        Padding::DEFAULT
131    }
132}
133
134/// The direction in which the [`CxLayoutBox`] should walk. It will typically walk
135/// in a straight line in this direction. E.g. when walking to [`Direction::Right`],
136/// it will only walk horizontally, not vertically, until it hits the [`CxLayoutBox::width`],
137/// at which point it will wrap around using [`LineWrap`], based on the maximum
138/// height of widgets that have been drawn so far, which is registered in
139/// [`CxLayoutBox::biggest`].
140///
141/// TODO(JP): This line wrapping behavior makes sense for [`Direction::Right`],
142/// but not so much for [`Direction::Down`].. Maybe we should split [`CxLayoutBox`]
143/// into different kinds of behavior?
144#[derive(Copy, Clone, Debug, PartialEq)]
145pub enum Direction {
146    Right,
147    Down,
148}
149impl Direction {
150    /// TODO(JP): Replace these with Direction::default() when
151    /// <https://github.com/rust-lang/rust/issues/67792> gets done
152    pub const DEFAULT: Direction = Direction::Right;
153}
154impl Default for Direction {
155    fn default() -> Self {
156        Direction::DEFAULT
157    }
158}
159
160/// Different ways in which a [`LayoutSize`] can get a width.
161///
162/// TODO(JP): Something like `FillUpTo(f32)` or `FillMax(f32)` might be useful here, to mimic
163/// CSS'es `max-width`. For now you can manually use `Cx::get_width_left` with
164/// `Width::Fix` as a workaround.
165///
166/// TODO(JP): See [`Height::DEFAULT`] for a related TODO.
167#[derive(Copy, Clone, Debug, PartialEq)]
168pub enum Width {
169    /// Fill up as much of the available space as possible.
170    Fill,
171    /// Use a fixed width.
172    Fix(f32),
173    /// Will defer computation of [`CxLayoutBox::width`] by setting it to [`f32::NAN`],
174    /// and only properly computing it later on.
175    ///
176    /// TODO(JP): This can also be passed into [`Cx::add_box`] but there it
177    /// makes no sense!
178    Compute,
179    // Fill up as much of the available space as possible up to provided width
180    FillUntil(f32),
181}
182impl Width {
183    /// TODO(JP): Replace these with Width::default() when
184    /// <https://github.com/rust-lang/rust/issues/67792> gets done
185    pub const DEFAULT: Width = Width::Fill;
186}
187impl Default for Width {
188    fn default() -> Self {
189        Width::Fill
190    }
191}
192
193/// Different ways in which a [`LayoutSize`] can get a height.
194///
195/// See [`Width`] for more documentation, since it's analogous.
196#[derive(Copy, Clone, Debug, PartialEq)]
197pub enum Height {
198    // See [`Width::Fill`].
199    Fill,
200    // See [`Width::Fix`].
201    Fix(f32),
202    // See [`Width::Compute`].
203    Compute,
204    // See [`Width::FillUntil`],
205    FillUntil(f32),
206}
207impl Height {
208    /// TODO(JP): [`Height::Fill`] might be a bad default, because if you use
209    /// [`Direction::Down`] it will push out everything out it below.
210    /// HTML/CSS uses something more like [`Height::Compute`] by default for height,
211    /// and only [`Height::Fill`] for width (for block-layout elements).
212    ///
213    /// TODO(JP): Replace these with Height::default() when
214    /// <https://github.com/rust-lang/rust/issues/67792> gets done
215    pub const DEFAULT: Height = Height::Fill;
216}
217impl Default for Height {
218    fn default() -> Self {
219        Height::Fill
220    }
221}
222
223/// Defines how elements on [`Cx::layout_box_align_list`] should be moved horizontally
224pub(crate) struct AlignX(pub f32);
225
226impl AlignX {
227    // Note: LEFT is the default so not needed as explicit option
228    pub(crate) const CENTER: AlignX = AlignX(0.5);
229    #[allow(dead_code)]
230    pub(crate) const RIGHT: AlignX = AlignX(1.0);
231}
232
233/// Defines how elements on [`Cx::layout_box_align_list`] should be moved vertically
234pub(crate) struct AlignY(pub f32);
235
236impl AlignY {
237    // Note: TOP is the default so not needed as explicit option
238    pub(crate) const CENTER: AlignY = AlignY(0.5);
239    #[allow(dead_code)]
240    pub(crate) const BOTTOM: AlignY = AlignY(1.0);
241}