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}