1use crate::container::{Align, Axis};
2use crate::overflow::Overflow;
3use sequoia_core::{Edges, Length, Size};
4
5#[derive(Clone, Copy, Debug, Default, PartialEq)]
6#[non_exhaustive]
7pub enum Position {
8 #[default]
9 Flow,
10
11 Absolute(Inset),
12}
13
14#[derive(Clone, Copy, Debug, Default, PartialEq)]
15#[non_exhaustive]
16pub struct Inset {
17 pub left: Option<f32>,
18 pub top: Option<f32>,
19 pub right: Option<f32>,
20 pub bottom: Option<f32>,
21}
22
23impl Inset {
24 pub const AUTO: Self = Self {
25 left: None,
26 top: None,
27 right: None,
28 bottom: None,
29 };
30
31 pub fn xy(left: f32, top: f32) -> Self {
32 Self {
33 left: Some(left),
34 top: Some(top),
35 right: None,
36 bottom: None,
37 }
38 }
39
40 pub const fn only(
42 left: Option<f32>,
43 top: Option<f32>,
44 right: Option<f32>,
45 bottom: Option<f32>,
46 ) -> Self {
47 Self {
48 left,
49 top,
50 right,
51 bottom,
52 }
53 }
54}
55
56#[derive(Clone, Copy, Debug, Default, PartialEq)]
57pub struct Indents {
58 pub left: Option<f32>,
59 pub top: Option<f32>,
60 pub right: Option<f32>,
61 pub bottom: Option<f32>,
62}
63
64impl Indents {
65 pub const NONE: Self = Self {
66 left: None,
67 top: None,
68 right: None,
69 bottom: None,
70 };
71
72 pub fn all(v: f32) -> Self {
73 let v = sanitize_indent(v);
74 Self {
75 left: Some(v),
76 top: Some(v),
77 right: Some(v),
78 bottom: Some(v),
79 }
80 }
81
82 pub fn xy(x: f32, y: f32) -> Self {
83 Self {
84 left: Some(sanitize_indent(x)),
85 top: Some(sanitize_indent(y)),
86 right: Some(sanitize_indent(x)),
87 bottom: Some(sanitize_indent(y)),
88 }
89 }
90
91 pub fn axes(horizontal: f32, vertical: f32) -> Self {
92 Self::xy(horizontal, vertical)
93 }
94
95 pub fn horizontal(v: f32) -> Self {
96 let v = sanitize_indent(v);
97 Self {
98 left: Some(v),
99 right: Some(v),
100 top: None,
101 bottom: None,
102 }
103 }
104
105 pub fn vertical(v: f32) -> Self {
106 let v = sanitize_indent(v);
107 Self {
108 left: None,
109 right: None,
110 top: Some(v),
111 bottom: Some(v),
112 }
113 }
114
115 pub const fn only(
116 left: Option<f32>,
117 top: Option<f32>,
118 right: Option<f32>,
119 bottom: Option<f32>,
120 ) -> Self {
121 Self {
122 left,
123 top,
124 right,
125 bottom,
126 }
127 }
128
129 pub fn is_none(self) -> bool {
130 self.left.is_none() && self.top.is_none() && self.right.is_none() && self.bottom.is_none()
131 }
132}
133
134#[derive(Clone, Copy, Debug, PartialEq)]
135pub struct LayoutStyle {
136 pub width: Length,
137 pub height: Length,
138 pub padding: Edges,
139
140 pub margin: Edges,
141 pub min_width: f32,
142 pub max_width: f32,
143 pub min_height: f32,
144 pub max_height: f32,
145
146 pub flex: f32,
147
148 pub flex_shrink: f32,
149
150 pub flex_basis: Option<Length>,
151
152 pub align_self: Option<Align>,
153
154 pub aspect_ratio: Option<f32>,
155
156 pub indent_top: Option<f32>,
157 pub indent_bottom: Option<f32>,
158 pub indent_left: Option<f32>,
159 pub indent_right: Option<f32>,
160
161 pub position: Position,
162 pub overflow: Overflow,
163
164 pub hit_testable: bool,
165}
166
167impl Default for LayoutStyle {
168 fn default() -> Self {
169 Self {
170 width: Length::Fit,
171 height: Length::Fit,
172 padding: Edges::ZERO,
173 margin: Edges::ZERO,
174 min_width: 0.0,
175 max_width: f32::INFINITY,
176 min_height: 0.0,
177 max_height: f32::INFINITY,
178 flex: 0.0,
179 flex_shrink: 1.0,
180 flex_basis: None,
181 align_self: None,
182 aspect_ratio: None,
183 indent_top: None,
184 indent_bottom: None,
185 indent_left: None,
186 indent_right: None,
187 position: Position::Flow,
188 overflow: Overflow::Visible,
189 hit_testable: true,
190 }
191 }
192}
193
194impl LayoutStyle {
195 pub fn width(mut self, width: Length) -> Self {
196 self.width = width;
197 self
198 }
199
200 pub fn height(mut self, height: Length) -> Self {
201 self.height = height;
202 self
203 }
204
205 pub fn fit(mut self) -> Self {
206 self.width = Length::Fit;
207 self.height = Length::Fit;
208 self
209 }
210
211 pub fn hit_testable(mut self, on: bool) -> Self {
212 self.hit_testable = on;
213 self
214 }
215
216 pub fn padding(mut self, padding: impl Into<Edges>) -> Self {
217 self.padding = padding.into();
218 self
219 }
220
221 pub fn pad(self, padding: impl Into<Edges>) -> Self {
222 self.padding(padding)
223 }
224
225 pub fn padding_xy(self, x: f32, y: f32) -> Self {
226 self.padding(Edges::xy(x, y))
227 }
228
229 pub fn padding_x(mut self, x: f32) -> Self {
230 self.padding.left = x;
231 self.padding.right = x;
232 self
233 }
234
235 pub fn padding_y(mut self, y: f32) -> Self {
236 self.padding.top = y;
237 self.padding.bottom = y;
238 self
239 }
240
241 pub fn padding_left(mut self, v: f32) -> Self {
242 self.padding.left = v;
243 self
244 }
245
246 pub fn padding_right(mut self, v: f32) -> Self {
247 self.padding.right = v;
248 self
249 }
250
251 pub fn padding_top(mut self, v: f32) -> Self {
252 self.padding.top = v;
253 self
254 }
255
256 pub fn padding_bottom(mut self, v: f32) -> Self {
257 self.padding.bottom = v;
258 self
259 }
260
261 pub fn min_width(mut self, v: f32) -> Self {
262 self.min_width = sanitize_min(v);
263 self
264 }
265
266 pub fn max_width(mut self, v: f32) -> Self {
267 self.max_width = sanitize_max(v);
268 self
269 }
270
271 pub fn min_height(mut self, v: f32) -> Self {
272 self.min_height = sanitize_min(v);
273 self
274 }
275
276 pub fn max_height(mut self, v: f32) -> Self {
277 self.max_height = sanitize_max(v);
278 self
279 }
280
281 pub fn clamp_width(self, w: f32) -> f32 {
282 clamp_axis(w, self.min_width, self.max_width)
283 }
284
285 pub fn clamp_height(self, h: f32) -> f32 {
286 clamp_axis(h, self.min_height, self.max_height)
287 }
288
289 pub fn clamp_size(self, size: sequoia_core::Size) -> sequoia_core::Size {
290 sequoia_core::Size::new(self.clamp_width(size.width), self.clamp_height(size.height))
291 }
292
293 pub fn margin(mut self, margin: impl Into<Edges>) -> Self {
294 let m = margin.into();
295 self.margin = Edges::only(
296 sanitize_edge(m.left),
297 sanitize_edge(m.top),
298 sanitize_edge(m.right),
299 sanitize_edge(m.bottom),
300 );
301 self
302 }
303
304 pub fn margin_xy(self, x: f32, y: f32) -> Self {
305 self.margin(Edges::xy(x, y))
306 }
307
308 pub fn margin_x(mut self, x: f32) -> Self {
309 let x = sanitize_edge(x);
310 self.margin.left = x;
311 self.margin.right = x;
312 self
313 }
314
315 pub fn margin_y(mut self, y: f32) -> Self {
316 let y = sanitize_edge(y);
317 self.margin.top = y;
318 self.margin.bottom = y;
319 self
320 }
321
322 pub fn margin_left(mut self, v: f32) -> Self {
323 self.margin.left = sanitize_edge(v);
324 self
325 }
326
327 pub fn margin_right(mut self, v: f32) -> Self {
328 self.margin.right = sanitize_edge(v);
329 self
330 }
331
332 pub fn margin_top(mut self, v: f32) -> Self {
333 self.margin.top = sanitize_edge(v);
334 self
335 }
336
337 pub fn margin_bottom(mut self, v: f32) -> Self {
338 self.margin.bottom = sanitize_edge(v);
339 self
340 }
341
342 pub fn flex(mut self, weight: f32) -> Self {
343 self.flex = sanitize_flex(weight);
344 self
345 }
346
347 pub fn flex_grow(mut self, weight: f32) -> Self {
348 self.flex = sanitize_flex(weight);
349 self
350 }
351
352 pub fn flex_shrink(mut self, shrink: f32) -> Self {
353 self.flex_shrink = sanitize_shrink(shrink);
354 self
355 }
356
357 pub fn flex_full(mut self, grow: f32, shrink: f32, basis: Option<Length>) -> Self {
358 self.flex = sanitize_flex(grow);
359 self.flex_shrink = sanitize_shrink(shrink);
360 self.flex_basis = basis;
361 self
362 }
363
364 pub fn flex_basis(mut self, basis: Option<Length>) -> Self {
365 self.flex_basis = basis;
366 self
367 }
368
369 pub fn no_basis(mut self) -> Self {
370 self.flex_basis = None;
371 self
372 }
373
374 pub fn expand(self) -> Self {
375 self.flex(1.0)
376 }
377
378 pub fn flex_weight(self) -> f32 {
379 sanitize_flex(self.flex)
380 }
381
382 pub fn flex_grow_weight(self) -> f32 {
383 sanitize_flex(self.flex)
384 }
385
386 pub fn flex_shrink_weight(self) -> f32 {
387 sanitize_shrink(self.flex_shrink)
388 }
389
390 pub fn align_self(mut self, align: Option<Align>) -> Self {
391 self.align_self = align;
392 self
393 }
394
395 pub fn align_self_value(mut self, align: Align) -> Self {
396 self.align_self = Some(align);
397 self
398 }
399
400 pub fn auto_align(mut self) -> Self {
401 self.align_self = None;
402 self
403 }
404
405 pub fn effective_align(self, container: Align) -> Align {
406 Align::resolve(self.align_self, container)
407 }
408
409 pub fn aspect_ratio(mut self, ratio: Option<f32>) -> Self {
410 self.aspect_ratio = sanitize_ratio(ratio);
411 self
412 }
413
414 pub fn aspect(mut self, ratio: f32) -> Self {
415 self.aspect_ratio = sanitize_ratio(Some(ratio));
416 self
417 }
418
419 pub fn no_aspect(mut self) -> Self {
420 self.aspect_ratio = None;
421 self
422 }
423
424 pub fn position(mut self, position: Position) -> Self {
425 self.position = position;
426 self
427 }
428
429 pub fn absolute(mut self) -> Self {
430 self.position = Position::Absolute(Inset::AUTO);
431 self
432 }
433
434 pub fn absolute_at(mut self, left: f32, top: f32) -> Self {
435 self.position = Position::Absolute(Inset::xy(left, top));
436 self
437 }
438
439 pub fn absolute_inset(mut self, inset: Inset) -> Self {
440 self.position = Position::Absolute(inset);
441 self
442 }
443
444 pub fn is_absolute(self) -> bool {
445 matches!(self.position, Position::Absolute(_))
446 }
447
448 pub fn absolute_inset_of(self) -> Option<Inset> {
449 match self.position {
450 Position::Absolute(inset) => Some(inset),
451 _ => None,
452 }
453 }
454
455 pub fn margin_main(self, axis: Axis) -> f32 {
456 match axis {
457 Axis::Horizontal => self.margin.left + self.margin.right,
458 Axis::Vertical => self.margin.top + self.margin.bottom,
459 }
460 }
461
462 pub fn margin_cross(self, axis: Axis) -> f32 {
463 match axis {
464 Axis::Horizontal => self.margin.top + self.margin.bottom,
465 Axis::Vertical => self.margin.left + self.margin.right,
466 }
467 }
468
469 pub fn apply_aspect(self, mut w: f32, mut h: f32, w_auto: bool, h_auto: bool) -> (f32, f32) {
470 let Some(r) = sanitize_ratio(self.aspect_ratio) else {
471 return (w, h);
472 };
473 if r <= 0.0 {
474 return (w, h);
475 }
476 match (w_auto, h_auto) {
477 (true, false) => {
478 w = (h * r).max(0.0);
479 }
480 (false, true) => {
481 h = if r != 0.0 { (w / r).max(0.0) } else { h };
482 }
483 (true, true) => {
484 if w <= 0.0 && h > 0.0 {
485 w = (h * r).max(0.0);
486 } else if h <= 0.0 && w > 0.0 {
487 h = (w / r).max(0.0);
488 }
489 }
490 (false, false) => {}
491 }
492 (w, h)
493 }
494
495 pub fn outer_size(self, inner: Size) -> Size {
496 Size::new(
497 inner.width + self.margin.horizontal_sum(),
498 inner.height + self.margin.vertical_sum(),
499 )
500 }
501
502 pub fn min_main(self, axis: Axis) -> f32 {
503 sanitize_min(match axis {
504 Axis::Horizontal => self.min_width,
505 Axis::Vertical => self.min_height,
506 })
507 }
508
509 pub fn max_main(self, axis: Axis) -> f32 {
510 sanitize_max(match axis {
511 Axis::Horizontal => self.max_width,
512 Axis::Vertical => self.max_height,
513 })
514 }
515
516 pub fn clamp_main(self, value: f32, axis: Axis) -> f32 {
517 clamp_axis(value, self.min_main(axis), self.max_main(axis))
518 }
519
520 pub fn overflow(mut self, overflow: Overflow) -> Self {
521 self.overflow = overflow;
522 self
523 }
524
525 pub fn indent_top(mut self, v: f32) -> Self {
526 self.indent_top = Some(sanitize_indent(v));
527 self
528 }
529
530 pub fn indent_bottom(mut self, v: f32) -> Self {
531 self.indent_bottom = Some(sanitize_indent(v));
532 self
533 }
534
535 pub fn indent_left(mut self, v: f32) -> Self {
536 self.indent_left = Some(sanitize_indent(v));
537 self
538 }
539
540 pub fn indent_right(mut self, v: f32) -> Self {
541 self.indent_right = Some(sanitize_indent(v));
542 self
543 }
544
545 pub fn clear_indent_top(mut self) -> Self {
546 self.indent_top = None;
547 self
548 }
549
550 pub fn clear_indent_bottom(mut self) -> Self {
551 self.indent_bottom = None;
552 self
553 }
554
555 pub fn clear_indent_left(mut self) -> Self {
556 self.indent_left = None;
557 self
558 }
559
560 pub fn clear_indent_right(mut self) -> Self {
561 self.indent_right = None;
562 self
563 }
564
565 pub fn clear_indents(mut self) -> Self {
566 self.indent_top = None;
567 self.indent_bottom = None;
568 self.indent_left = None;
569 self.indent_right = None;
570 self
571 }
572
573 pub fn indent(mut self, indents: Indents) -> Self {
574 if let Some(v) = indents.top {
575 self.indent_top = Some(sanitize_indent(v));
576 }
577 if let Some(v) = indents.bottom {
578 self.indent_bottom = Some(sanitize_indent(v));
579 }
580 if let Some(v) = indents.left {
581 self.indent_left = Some(sanitize_indent(v));
582 }
583 if let Some(v) = indents.right {
584 self.indent_right = Some(sanitize_indent(v));
585 }
586 self
587 }
588
589 pub fn indent_all(mut self, v: f32) -> Self {
590 let v = sanitize_indent(v);
591 self.indent_top = Some(v);
592 self.indent_bottom = Some(v);
593 self.indent_left = Some(v);
594 self.indent_right = Some(v);
595 self
596 }
597
598 pub fn indent_xy(mut self, x: f32, y: f32) -> Self {
599 let x = sanitize_indent(x);
600 let y = sanitize_indent(y);
601 self.indent_left = Some(x);
602 self.indent_right = Some(x);
603 self.indent_top = Some(y);
604 self.indent_bottom = Some(y);
605 self
606 }
607
608 pub fn indent_axes(self, horizontal: f32, vertical: f32) -> Self {
609 self.indent_xy(horizontal, vertical)
610 }
611
612 pub fn indent_main_start(self, axis: Axis) -> Option<f32> {
613 match axis {
614 Axis::Horizontal => self.indent_left,
615 Axis::Vertical => self.indent_top,
616 }
617 }
618
619 pub fn indent_main_end(self, axis: Axis) -> Option<f32> {
620 match axis {
621 Axis::Horizontal => self.indent_right,
622 Axis::Vertical => self.indent_bottom,
623 }
624 }
625
626 pub fn indent_cross_start(self, axis: Axis) -> Option<f32> {
627 match axis {
628 Axis::Horizontal => self.indent_top,
629 Axis::Vertical => self.indent_left,
630 }
631 }
632
633 pub fn indent_cross_end(self, axis: Axis) -> Option<f32> {
634 match axis {
635 Axis::Horizontal => self.indent_bottom,
636 Axis::Vertical => self.indent_right,
637 }
638 }
639
640 pub fn indents(self) -> Indents {
641 Indents {
642 left: self.indent_left,
643 top: self.indent_top,
644 right: self.indent_right,
645 bottom: self.indent_bottom,
646 }
647 }
648}
649
650pub fn sanitize_flex(v: f32) -> f32 {
651 if v.is_finite() && v > 0.0 { v } else { 0.0 }
652}
653
654pub fn sanitize_shrink(v: f32) -> f32 {
655 if v.is_finite() && v >= 0.0 {
656 v
657 } else if v.is_nan() {
658 1.0
659 } else {
660 0.0
661 }
662}
663
664pub fn sanitize_ratio(v: Option<f32>) -> Option<f32> {
665 match v {
666 Some(r) if r.is_finite() && r > 0.0 => Some(r),
667 _ => None,
668 }
669}
670
671fn sanitize_edge(v: f32) -> f32 {
672 if v.is_finite() { v.max(0.0) } else { 0.0 }
673}
674
675pub fn sanitize_indent(v: f32) -> f32 {
676 if v.is_finite() { v.max(0.0) } else { 0.0 }
677}
678
679fn sanitize_min(v: f32) -> f32 {
680 if !v.is_finite() || v < 0.0 { 0.0 } else { v }
681}
682
683fn sanitize_max(v: f32) -> f32 {
684 if v.is_nan() {
685 f32::INFINITY
686 } else if v.is_infinite() {
687 if v > 0.0 { f32::INFINITY } else { 0.0 }
688 } else {
689 v.max(0.0)
690 }
691}
692
693fn clamp_axis(value: f32, min: f32, max: f32) -> f32 {
694 let (min, max) = if min > max { (max, max) } else { (min, max) };
695 if value < min {
696 min
697 } else if value > max {
698 max
699 } else {
700 value
701 }
702}