Remove serde renames from Display (#153)
1//! A representation of [CSS layout properties](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) in Rust, used for flexbox layout
2
3use crate::geometry::{Rect, Size};
4use crate::number::Number;
5
6/// How [`Nodes`](crate::node::Node) are aligned relative to the cross axis
7///
8/// The default behavior is [`AlignItems::Stretch`].
9///
10/// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-items-property)
11#[derive(Copy, Clone, PartialEq, Eq, Debug)]
12#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
13pub enum AlignItems {
14 /// Items are packed toward the start of the cross axis
15 FlexStart,
16 /// Items are packed toward the end of the cross axis
17 FlexEnd,
18 /// Items are packed along the center of the cross axis
19 Center,
20 /// Items are aligned such as their baselines align
21 Baseline,
22 /// Stretch to fill the container
23 Stretch,
24}
25
26impl Default for AlignItems {
27 fn default() -> Self {
28 Self::Stretch
29 }
30}
31
32/// Overrides the inherited [`AlignItems`] behavior for this node.
33///
34/// The behavior of any child nodes will be controlled by this node's [`AlignItems`] value.
35///
36/// The default behavior is [`AlignSelf::Auto`].
37///
38/// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-items-property)
39#[derive(Copy, Clone, PartialEq, Eq, Debug)]
40#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
41pub enum AlignSelf {
42 /// Inherits the [`AlignItems`] behavior of the parent
43 Auto,
44 /// Items are packed toward the start of the cross axis
45 FlexStart,
46 /// Items are packed toward the end of the cross axis
47 FlexEnd,
48 /// Items are packed along the center of the cross axis
49 Center,
50 /// Items are aligned such as their baselines align
51 Baseline,
52 /// Distribute items evenly, but stretch them to fill the container
53 Stretch,
54}
55
56impl Default for AlignSelf {
57 fn default() -> Self {
58 Self::Auto
59 }
60}
61
62/// Sets the distribution of space between and around content items along the cross-axis
63///
64/// The default value is [`AlignContent::Stretch`].
65///
66/// [Specification](https://www.w3.org/TR/css-flexbox-1/#align-content-property)
67#[derive(Copy, Clone, PartialEq, Eq, Debug)]
68#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
69pub enum AlignContent {
70 /// Items are packed toward the start of the cross axis
71 FlexStart,
72 /// Items are packed toward the end of the cross axis
73 FlexEnd,
74 /// Items are packed along the center of the cross axis
75 Center,
76 /// Distribute items evenly, but stretch them to fill the container
77 Stretch,
78 /// Distribute items evenly, such that the first and last item are aligned with the edges
79 SpaceBetween,
80 /// Distribute items evenly,
81 /// such that the space between items is the same as the space between the first and last item and the edges
82 SpaceAround,
83}
84
85impl Default for AlignContent {
86 fn default() -> Self {
87 Self::Stretch
88 }
89}
90
91/// Sets the layout used for the children of this node
92///
93/// [`Display::Flex`] is the default value.
94#[derive(Copy, Clone, PartialEq, Eq, Debug)]
95#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
96pub enum Display {
97 /// The children will follow the flexbox layout algorithm
98 Flex,
99 /// The children will not be laid out, and will follow absolute positioning
100 None,
101}
102
103impl Default for Display {
104 fn default() -> Self {
105 Self::Flex
106 }
107}
108
109/// The direction of the flexbox layout main axis.
110///
111/// There are always two perpendicular layout axes: main (or primary) and cross (or secondary).
112/// Adding items will cause them to be positioned adjacent to each other along the main axis.
113/// By varying this value throughout your tree, you can create complex axis-aligned layouts.
114///
115/// Items are always aligned relative to the cross axis, and justified relative to the main axis.
116///
117/// The default behavior is [`FlexDirection::Row`].
118///
119/// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-direction-property)
120#[derive(Copy, Clone, PartialEq, Eq, Debug)]
121#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
122pub enum FlexDirection {
123 /// Defines +x as the main axis
124 ///
125 /// Items will be added from left to right in a row.
126 Row,
127 /// Defines +y as the main axis
128 ///
129 /// Items will be added from top to bottom in a column.
130 Column,
131 /// Defines -x as the main axis
132 ///
133 /// Items will be added from right to left in a row.
134 RowReverse,
135 /// Defines -y as the main axis
136 ///
137 /// Items will be added from bottom to top in a column.
138 ColumnReverse,
139}
140
141impl Default for FlexDirection {
142 fn default() -> Self {
143 Self::Row
144 }
145}
146
147impl FlexDirection {
148 #[inline]
149 /// Is the direction [`FlexDirection::Row`] or [`FlexDirection::RowReverse`]?
150 pub(crate) fn is_row(self) -> bool {
151 matches!(self, Self::Row | Self::RowReverse)
152 }
153
154 #[inline]
155 /// Is the direction [`FlexDirection::Column`] or [`FlexDirection::ColumnReverse`]?
156 pub(crate) fn is_column(self) -> bool {
157 matches!(self, Self::Column | Self::ColumnReverse)
158 }
159
160 #[inline]
161 /// Is the direction [`FlexDirection::RowReverse`] or [`FlexDirection::ColumnReverse`]?
162 pub(crate) fn is_reverse(self) -> bool {
163 matches!(self, Self::RowReverse | Self::ColumnReverse)
164 }
165}
166
167/// Sets the distribution of space between and around content items along the main-axis
168///
169/// The default value is [`JustifyContent::FlexStart`].
170///
171/// [Specification](https://www.w3.org/TR/css-flexbox-1/#justify-content-property)
172#[derive(Copy, Clone, PartialEq, Eq, Debug)]
173#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
174pub enum JustifyContent {
175 /// Items are packed toward the start of the main axis
176 FlexStart,
177 /// Items are packed toward the end of the main axis
178 FlexEnd,
179 /// Items are packed along the center of the main axis
180 Center,
181 /// Distribute items evenly, such that the first and last item are aligned with the edges
182 SpaceBetween,
183 /// Distribute items evenly,
184 /// such that the space between items is twice same as the space between the first and last item and the edges
185 SpaceAround,
186 /// Distribute items evenly,
187 /// such that the space between items is the same as the space between the first and last item and the edges
188 SpaceEvenly,
189}
190
191impl Default for JustifyContent {
192 fn default() -> Self {
193 Self::FlexStart
194 }
195}
196
197/// The positioning strategy for this item.
198///
199/// This controls both how the origin is determined for the [`Style::position`] field,
200/// and whether or not the item will be controlled by flexbox's layout algorithm.
201///
202/// WARNING: this enum follows the behavior of [CSS's `position` property](https://developer.mozilla.org/en-US/docs/Web/CSS/position),
203/// which can be unintuitive.
204///
205/// [`PositionType::Relative`] is the default value, in contrast to the default behavior in CSS.
206#[derive(Copy, Clone, PartialEq, Eq, Debug)]
207#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
208pub enum PositionType {
209 /// The offset is computed relative to the final position given by the layout algorithm.
210 /// Offsets do not affect the position of any other items; they are effectively a correction factor applied at the end.
211 Relative,
212 /// The offset is computed relative to this item's closest positioned ancestor, if any.
213 /// Otherwise, it is placed relative to the origin.
214 /// No space is created for the item in the page layout, and its size will not be altered.
215 ///
216 /// WARNING: to opt-out of layouting entirely, you must use [`Display::None`] instead on your [`Style`] object.
217 Absolute,
218}
219
220impl Default for PositionType {
221 fn default() -> Self {
222 Self::Relative
223 }
224}
225
226/// Controls whether flex items are forced onto one line or can wrap onto multiple lines.
227///
228/// Defaults to [`FlexWrap::NoWrap`]
229///
230/// [Specification](https://www.w3.org/TR/css-flexbox-1/#flex-wrap-property)
231#[derive(Copy, Clone, PartialEq, Eq, Debug)]
232#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
233pub enum FlexWrap {
234 /// Items will not wrap and stay on a single line
235 NoWrap,
236 /// Items will wrap according to this item's [`FlexDirection`]
237 Wrap,
238 /// Items will wrap in the oppposite direction to this item's [`FlexDirection`]
239 WrapReverse,
240}
241
242impl Default for FlexWrap {
243 fn default() -> Self {
244 Self::NoWrap
245 }
246}
247
248/// A unit of linear measurement
249///
250/// This is commonly combined with [`Rect`], [`Point`](crate::geometry::Point) and [`Size<T>`].
251/// The default value is [`Dimension::Undefined`].
252#[derive(Copy, Clone, PartialEq, Debug)]
253#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
254pub enum Dimension {
255 /// The dimension is not given
256 Undefined,
257 /// The dimension should be automatically computed
258 Auto,
259 /// The dimension is stored in [points](https://en.wikipedia.org/wiki/Point_(typography))
260 ///
261 /// Each point is about 0.353 mm in size.
262 Points(f32),
263 /// The dimension is stored in percentage relative to the parent item.
264 Percent(f32),
265}
266
267impl Default for Dimension {
268 fn default() -> Self {
269 Self::Undefined
270 }
271}
272
273impl Dimension {
274 /// Converts the given [`Dimension`] into a concrete value of points
275 pub(crate) fn resolve(self, parent_dim: Number) -> Number {
276 match self {
277 Dimension::Points(points) => Number::Defined(points),
278 Dimension::Percent(percent) => parent_dim * percent,
279 _ => Number::Undefined,
280 }
281 }
282
283 /// Is this value defined?
284 pub(crate) fn is_defined(self) -> bool {
285 matches!(self, Dimension::Points(_) | Dimension::Percent(_))
286 }
287}
288
289impl Default for Rect<Dimension> {
290 fn default() -> Self {
291 Self { start: Default::default(), end: Default::default(), top: Default::default(), bottom: Default::default() }
292 }
293}
294
295impl Default for Size<Dimension> {
296 fn default() -> Self {
297 Self { width: Dimension::Auto, height: Dimension::Auto }
298 }
299}
300
301/// The flexbox layout information for a single [`Node`](crate::node::Node).
302///
303/// The most important idea in flexbox is the notion of a "main" and "cross" axis, which are always perpendicular to each other.
304/// The orientation of these axes are controlled via the [`FlexDirection`] field of this struct.
305///
306/// This struct follows the [CSS equivalent](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) directly;
307/// information about the behavior on the web should transfer directly.
308///
309/// Detailed information about the exact behavior of each of these fields
310/// can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS) by searching for the field name.
311/// The distinction between margin, padding and border is explained well in
312/// this [introduction to the box model](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model).
313///
314/// If the behavior does not match the flexbox layout algorithm on the web, please file a bug!
315#[derive(Copy, Clone, PartialEq, Debug)]
316#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
317#[cfg_attr(feature = "serde", serde(default))]
318pub struct FlexboxLayout {
319 /// What layout strategy should be used?
320 pub display: Display,
321 /// What should the `position` value of this struct use as a base offset?
322 pub position_type: PositionType,
323 /// Which direction does the main axis flow in?
324 pub flex_direction: FlexDirection,
325 /// Should elements wrap, or stay in a single line?
326 pub flex_wrap: FlexWrap,
327 /// How should items be aligned relative to the cross axis?
328 pub align_items: AlignItems,
329 /// Should this item violate the cross axis alignment specified by its parent's [`AlignItems`]?
330 pub align_self: AlignSelf,
331 /// How should content contained within this item be aligned relative to the cross axis?
332 pub align_content: AlignContent,
333 /// How should items be aligned relative to the main axis?
334 pub justify_content: JustifyContent,
335 /// How should the position of this element be tweaked relative to the layout defined?
336 pub position: Rect<Dimension>,
337 /// How large should the margin be on each side?
338 pub margin: Rect<Dimension>,
339 /// How large should the padding be on each side?
340 pub padding: Rect<Dimension>,
341 /// How large should the border be on each side?
342 pub border: Rect<Dimension>,
343 /// The relative rate at which this item grows when it is expanding to fill space
344 ///
345 /// 0.0 is the default value, and this value must be positive.
346 pub flex_grow: f32,
347 /// The relative rate at which this item shrinks when it is contracting to fit into space
348 ///
349 /// 1.0 is the default value, and this value must be positive.
350 pub flex_shrink: f32,
351 /// Sets the initial main axis size of the item
352 pub flex_basis: Dimension,
353 /// Sets the initial size of the item
354 // TODO: why does this exist as distinct from flex_basis? How do they interact?
355 pub size: Size<Dimension>,
356 /// Controls the minimum size of the item
357 pub min_size: Size<Dimension>,
358 /// Controls the maximum size of the item
359 pub max_size: Size<Dimension>,
360 /// Sets the preferred aspect ratio for the item
361 ///
362 /// The ratio is calculated as width divided by height.
363 pub aspect_ratio: Number,
364}
365
366impl Default for FlexboxLayout {
367 fn default() -> Self {
368 Self {
369 display: Default::default(),
370 position_type: Default::default(),
371 flex_direction: Default::default(),
372 flex_wrap: Default::default(),
373 align_items: Default::default(),
374 align_self: Default::default(),
375 align_content: Default::default(),
376 justify_content: Default::default(),
377 position: Default::default(),
378 margin: Default::default(),
379 padding: Default::default(),
380 border: Default::default(),
381 flex_grow: 0.0,
382 flex_shrink: 1.0,
383 flex_basis: Dimension::Auto,
384 size: Default::default(),
385 min_size: Default::default(),
386 max_size: Default::default(),
387 aspect_ratio: Default::default(),
388 }
389 }
390}
391
392impl FlexboxLayout {
393 /// If the `direction` is row-oriented, the min width. Otherwise the min height
394 pub(crate) fn min_main_size(&self, direction: FlexDirection) -> Dimension {
395 if direction.is_row() {
396 self.min_size.width
397 } else {
398 self.min_size.height
399 }
400 }
401
402 /// If the `direction` is row-oriented, the max width. Otherwise the max height
403 pub(crate) fn max_main_size(&self, direction: FlexDirection) -> Dimension {
404 if direction.is_row() {
405 self.max_size.width
406 } else {
407 self.max_size.height
408 }
409 }
410
411 /// If the `direction` is row-oriented, the margin start. Otherwise the margin top
412 pub(crate) fn main_margin_start(&self, direction: FlexDirection) -> Dimension {
413 if direction.is_row() {
414 self.margin.start
415 } else {
416 self.margin.top
417 }
418 }
419
420 /// If the `direction` is row-oriented, the margin end. Otherwise the margin bottom
421 pub(crate) fn main_margin_end(&self, direction: FlexDirection) -> Dimension {
422 if direction.is_row() {
423 self.margin.end
424 } else {
425 self.margin.bottom
426 }
427 }
428
429 /// If the `direction` is row-oriented, the height. Otherwise the width
430 pub(crate) fn cross_size(&self, direction: FlexDirection) -> Dimension {
431 if direction.is_row() {
432 self.size.height
433 } else {
434 self.size.width
435 }
436 }
437
438 /// If the `direction` is row-oriented, the min height. Otherwise the min width
439 pub(crate) fn min_cross_size(&self, direction: FlexDirection) -> Dimension {
440 if direction.is_row() {
441 self.min_size.height
442 } else {
443 self.min_size.width
444 }
445 }
446
447 /// If the `direction` is row-oriented, the max height. Otherwise the max width
448 pub(crate) fn max_cross_size(&self, direction: FlexDirection) -> Dimension {
449 if direction.is_row() {
450 self.max_size.height
451 } else {
452 self.max_size.width
453 }
454 }
455
456 /// If the `direction` is row-oriented, the margin top. Otherwise the margin start
457 pub(crate) fn cross_margin_start(&self, direction: FlexDirection) -> Dimension {
458 if direction.is_row() {
459 self.margin.top
460 } else {
461 self.margin.start
462 }
463 }
464
465 /// If the `direction` is row-oriented, the margin bottom. Otherwise the margin end
466 pub(crate) fn cross_margin_end(&self, direction: FlexDirection) -> Dimension {
467 if direction.is_row() {
468 self.margin.bottom
469 } else {
470 self.margin.end
471 }
472 }
473
474 /// Computes the final alignment of this item based on the parent's [`AlignItems`] and this item's [`AlignSelf`]
475 pub(crate) fn align_self(&self, parent: &FlexboxLayout) -> AlignSelf {
476 if self.align_self == AlignSelf::Auto {
477 match parent.align_items {
478 AlignItems::FlexStart => AlignSelf::FlexStart,
479 AlignItems::FlexEnd => AlignSelf::FlexEnd,
480 AlignItems::Center => AlignSelf::Center,
481 AlignItems::Baseline => AlignSelf::Baseline,
482 AlignItems::Stretch => AlignSelf::Stretch,
483 }
484 } else {
485 self.align_self
486 }
487 }
488}