Skip to main content

fret_ui_kit/style/
layout.rs

1use fret_core::Px;
2use fret_ui::element::{CrossAlign, PositionStyle};
3
4use super::{
5    InsetEdgeRefinement, InsetRefinement, MarginEdgeRefinement, MarginRefinement, MetricRef,
6    OverflowRefinement, SignedMetricRef, Space,
7};
8
9#[derive(Debug, Clone, Default)]
10pub enum LengthRefinement {
11    #[default]
12    Auto,
13    Px(MetricRef),
14    /// Fraction of the containing block size (percent sizing).
15    ///
16    /// Expressed as a ratio (e.g. `0.5` for 50%).
17    Fraction(f32),
18    Fill,
19}
20
21#[derive(Debug, Clone, Default)]
22pub struct SizeRefinement {
23    pub width: Option<LengthRefinement>,
24    pub height: Option<LengthRefinement>,
25    pub min_width: Option<LengthRefinement>,
26    pub min_height: Option<LengthRefinement>,
27    pub max_width: Option<LengthRefinement>,
28    pub max_height: Option<LengthRefinement>,
29}
30
31impl SizeRefinement {
32    pub fn merge(mut self, other: SizeRefinement) -> Self {
33        if other.width.is_some() {
34            self.width = other.width;
35        }
36        if other.height.is_some() {
37            self.height = other.height;
38        }
39        if other.min_width.is_some() {
40            self.min_width = other.min_width;
41        }
42        if other.min_height.is_some() {
43            self.min_height = other.min_height;
44        }
45        if other.max_width.is_some() {
46            self.max_width = other.max_width;
47        }
48        if other.max_height.is_some() {
49            self.max_height = other.max_height;
50        }
51        self
52    }
53}
54
55#[derive(Debug, Clone, Default)]
56pub struct FlexItemRefinement {
57    pub order: Option<i32>,
58    pub grow: Option<f32>,
59    pub shrink: Option<f32>,
60    pub basis: Option<LengthRefinement>,
61}
62
63impl FlexItemRefinement {
64    pub fn merge(mut self, other: FlexItemRefinement) -> Self {
65        if other.order.is_some() {
66            self.order = other.order;
67        }
68        if other.grow.is_some() {
69            self.grow = other.grow;
70        }
71        if other.shrink.is_some() {
72            self.shrink = other.shrink;
73        }
74        if other.basis.is_some() {
75            self.basis = other.basis;
76        }
77        self
78    }
79}
80
81/// Layout-affecting style patches (margin, positioning, size constraints, flex/grid).
82///
83/// These apply only in the declarative authoring path (or via explicit wrappers). Retained widgets
84/// must not silently accept these fields, as that would create Tailwind-like APIs that appear to
85/// work but are actually no-ops.
86#[derive(Debug, Clone, Default)]
87pub struct LayoutRefinement {
88    pub aspect_ratio: Option<f32>,
89    pub margin: Option<MarginRefinement>,
90    pub position: Option<PositionStyle>,
91    pub inset: Option<InsetRefinement>,
92    pub size: Option<SizeRefinement>,
93    pub flex_item: Option<FlexItemRefinement>,
94    pub align_self: Option<CrossAlign>,
95    pub justify_self: Option<CrossAlign>,
96    pub overflow: Option<OverflowRefinement>,
97}
98
99impl LayoutRefinement {
100    fn ensure_position_for_inset(&mut self) {
101        if self.position.is_none() {
102            self.position = Some(PositionStyle::Relative);
103        }
104    }
105
106    pub fn merge(mut self, other: LayoutRefinement) -> Self {
107        if other.aspect_ratio.is_some() {
108            self.aspect_ratio = other.aspect_ratio;
109        }
110        if let Some(m) = other.margin {
111            self.margin = Some(self.margin.unwrap_or_default().merge(m));
112        }
113        if other.position.is_some() {
114            self.position = other.position;
115        }
116        if let Some(i) = other.inset {
117            self.inset = Some(self.inset.unwrap_or_default().merge(i));
118        }
119        if let Some(s) = other.size {
120            self.size = Some(self.size.unwrap_or_default().merge(s));
121        }
122        if let Some(f) = other.flex_item {
123            self.flex_item = Some(self.flex_item.unwrap_or_default().merge(f));
124        }
125        if other.align_self.is_some() {
126            self.align_self = other.align_self;
127        }
128        if other.justify_self.is_some() {
129            self.justify_self = other.justify_self;
130        }
131        if other.overflow.is_some() {
132            self.overflow = other.overflow;
133        }
134        self
135    }
136
137    pub fn aspect_ratio(mut self, ratio: f32) -> Self {
138        self.aspect_ratio = Some(ratio);
139        self
140    }
141
142    pub fn relative(mut self) -> Self {
143        self.position = Some(PositionStyle::Relative);
144        self
145    }
146
147    pub fn absolute(mut self) -> Self {
148        self.position = Some(PositionStyle::Absolute);
149        self
150    }
151
152    pub fn overflow_hidden(mut self) -> Self {
153        self.overflow = Some(OverflowRefinement::Hidden);
154        self
155    }
156
157    pub fn overflow_visible(mut self) -> Self {
158        self.overflow = Some(OverflowRefinement::Visible);
159        self
160    }
161
162    pub fn overflow_x_hidden(self) -> Self {
163        self.overflow_hidden()
164    }
165
166    pub fn overflow_y_hidden(self) -> Self {
167        self.overflow_hidden()
168    }
169
170    pub fn inset(mut self, space: Space) -> Self {
171        self.ensure_position_for_inset();
172        let m = InsetEdgeRefinement::Px(SignedMetricRef::pos(MetricRef::space(space)));
173        self.inset = Some(InsetRefinement {
174            top: Some(m.clone()),
175            right: Some(m.clone()),
176            bottom: Some(m.clone()),
177            left: Some(m),
178        });
179        self
180    }
181
182    pub fn inset_px(mut self, px: Px) -> Self {
183        self.ensure_position_for_inset();
184        let m = InsetEdgeRefinement::Px(SignedMetricRef::pos(px.into()));
185        self.inset = Some(InsetRefinement {
186            top: Some(m.clone()),
187            right: Some(m.clone()),
188            bottom: Some(m.clone()),
189            left: Some(m),
190        });
191        self
192    }
193
194    /// Shorthand for `inset: 100%` of the containing block (percent sizing).
195    pub fn inset_full(mut self) -> Self {
196        self.ensure_position_for_inset();
197        let m = InsetEdgeRefinement::Fill;
198        self.inset = Some(InsetRefinement {
199            top: Some(m.clone()),
200            right: Some(m.clone()),
201            bottom: Some(m.clone()),
202            left: Some(m),
203        });
204        self
205    }
206
207    /// Shorthand for `inset: <fraction>` of the containing block.
208    ///
209    /// Example: `inset_fraction(0.5)` == 50%.
210    pub fn inset_fraction(mut self, fraction: f32) -> Self {
211        self.ensure_position_for_inset();
212        let m = InsetEdgeRefinement::Fraction(fraction);
213        self.inset = Some(InsetRefinement {
214            top: Some(m.clone()),
215            right: Some(m.clone()),
216            bottom: Some(m.clone()),
217            left: Some(m),
218        });
219        self
220    }
221
222    /// Shorthand for `inset: <percent>%` of the containing block.
223    pub fn inset_percent(self, percent: f32) -> Self {
224        self.inset_fraction(percent / 100.0)
225    }
226
227    pub fn top(mut self, space: Space) -> Self {
228        self.ensure_position_for_inset();
229        let mut inset = self.inset.unwrap_or_default();
230        inset.top = Some(InsetEdgeRefinement::Px(SignedMetricRef::pos(
231            MetricRef::space(space),
232        )));
233        self.inset = Some(inset);
234        self
235    }
236
237    pub fn top_px(mut self, px: Px) -> Self {
238        self.ensure_position_for_inset();
239        let mut inset = self.inset.unwrap_or_default();
240        inset.top = Some(InsetEdgeRefinement::Px(SignedMetricRef::pos(px.into())));
241        self.inset = Some(inset);
242        self
243    }
244
245    /// Shorthand for `top: 100%` of the containing block (percent sizing).
246    pub fn top_full(mut self) -> Self {
247        self.ensure_position_for_inset();
248        let mut inset = self.inset.unwrap_or_default();
249        inset.top = Some(InsetEdgeRefinement::Fill);
250        self.inset = Some(inset);
251        self
252    }
253
254    /// Shorthand for `top: <fraction>` of the containing block.
255    pub fn top_fraction(mut self, fraction: f32) -> Self {
256        self.ensure_position_for_inset();
257        let mut inset = self.inset.unwrap_or_default();
258        inset.top = Some(InsetEdgeRefinement::Fraction(fraction));
259        self.inset = Some(inset);
260        self
261    }
262
263    /// Shorthand for `top: <percent>%` of the containing block.
264    pub fn top_percent(self, percent: f32) -> Self {
265        self.top_fraction(percent / 100.0)
266    }
267
268    pub fn top_neg(mut self, space: Space) -> Self {
269        self.ensure_position_for_inset();
270        let mut inset = self.inset.unwrap_or_default();
271        inset.top = Some(InsetEdgeRefinement::Px(SignedMetricRef::neg(
272            MetricRef::space(space),
273        )));
274        self.inset = Some(inset);
275        self
276    }
277
278    pub fn top_neg_px(mut self, px: impl Into<MetricRef>) -> Self {
279        self.ensure_position_for_inset();
280        let mut inset = self.inset.unwrap_or_default();
281        inset.top = Some(InsetEdgeRefinement::Px(SignedMetricRef::neg(px.into())));
282        self.inset = Some(inset);
283        self
284    }
285
286    pub fn right(mut self, space: Space) -> Self {
287        self.ensure_position_for_inset();
288        let mut inset = self.inset.unwrap_or_default();
289        inset.right = Some(InsetEdgeRefinement::Px(SignedMetricRef::pos(
290            MetricRef::space(space),
291        )));
292        self.inset = Some(inset);
293        self
294    }
295
296    pub fn right_px(mut self, px: Px) -> Self {
297        self.ensure_position_for_inset();
298        let mut inset = self.inset.unwrap_or_default();
299        inset.right = Some(InsetEdgeRefinement::Px(SignedMetricRef::pos(px.into())));
300        self.inset = Some(inset);
301        self
302    }
303
304    /// Shorthand for `right: 100%` of the containing block (percent sizing).
305    pub fn right_full(mut self) -> Self {
306        self.ensure_position_for_inset();
307        let mut inset = self.inset.unwrap_or_default();
308        inset.right = Some(InsetEdgeRefinement::Fill);
309        self.inset = Some(inset);
310        self
311    }
312
313    /// Shorthand for `right: <fraction>` of the containing block.
314    pub fn right_fraction(mut self, fraction: f32) -> Self {
315        self.ensure_position_for_inset();
316        let mut inset = self.inset.unwrap_or_default();
317        inset.right = Some(InsetEdgeRefinement::Fraction(fraction));
318        self.inset = Some(inset);
319        self
320    }
321
322    /// Shorthand for `right: <percent>%` of the containing block.
323    pub fn right_percent(self, percent: f32) -> Self {
324        self.right_fraction(percent / 100.0)
325    }
326
327    pub fn right_neg(mut self, space: Space) -> Self {
328        self.ensure_position_for_inset();
329        let mut inset = self.inset.unwrap_or_default();
330        inset.right = Some(InsetEdgeRefinement::Px(SignedMetricRef::neg(
331            MetricRef::space(space),
332        )));
333        self.inset = Some(inset);
334        self
335    }
336
337    pub fn right_neg_px(mut self, px: impl Into<MetricRef>) -> Self {
338        self.ensure_position_for_inset();
339        let mut inset = self.inset.unwrap_or_default();
340        inset.right = Some(InsetEdgeRefinement::Px(SignedMetricRef::neg(px.into())));
341        self.inset = Some(inset);
342        self
343    }
344
345    pub fn bottom(mut self, space: Space) -> Self {
346        self.ensure_position_for_inset();
347        let mut inset = self.inset.unwrap_or_default();
348        inset.bottom = Some(InsetEdgeRefinement::Px(SignedMetricRef::pos(
349            MetricRef::space(space),
350        )));
351        self.inset = Some(inset);
352        self
353    }
354
355    pub fn bottom_px(mut self, px: Px) -> Self {
356        self.ensure_position_for_inset();
357        let mut inset = self.inset.unwrap_or_default();
358        inset.bottom = Some(InsetEdgeRefinement::Px(SignedMetricRef::pos(px.into())));
359        self.inset = Some(inset);
360        self
361    }
362
363    /// Shorthand for `bottom: 100%` of the containing block (percent sizing).
364    pub fn bottom_full(mut self) -> Self {
365        self.ensure_position_for_inset();
366        let mut inset = self.inset.unwrap_or_default();
367        inset.bottom = Some(InsetEdgeRefinement::Fill);
368        self.inset = Some(inset);
369        self
370    }
371
372    /// Shorthand for `bottom: <fraction>` of the containing block.
373    pub fn bottom_fraction(mut self, fraction: f32) -> Self {
374        self.ensure_position_for_inset();
375        let mut inset = self.inset.unwrap_or_default();
376        inset.bottom = Some(InsetEdgeRefinement::Fraction(fraction));
377        self.inset = Some(inset);
378        self
379    }
380
381    /// Shorthand for `bottom: <percent>%` of the containing block.
382    pub fn bottom_percent(self, percent: f32) -> Self {
383        self.bottom_fraction(percent / 100.0)
384    }
385
386    pub fn bottom_neg(mut self, space: Space) -> Self {
387        self.ensure_position_for_inset();
388        let mut inset = self.inset.unwrap_or_default();
389        inset.bottom = Some(InsetEdgeRefinement::Px(SignedMetricRef::neg(
390            MetricRef::space(space),
391        )));
392        self.inset = Some(inset);
393        self
394    }
395
396    pub fn bottom_neg_px(mut self, px: impl Into<MetricRef>) -> Self {
397        self.ensure_position_for_inset();
398        let mut inset = self.inset.unwrap_or_default();
399        inset.bottom = Some(InsetEdgeRefinement::Px(SignedMetricRef::neg(px.into())));
400        self.inset = Some(inset);
401        self
402    }
403
404    pub fn left(mut self, space: Space) -> Self {
405        self.ensure_position_for_inset();
406        let mut inset = self.inset.unwrap_or_default();
407        inset.left = Some(InsetEdgeRefinement::Px(SignedMetricRef::pos(
408            MetricRef::space(space),
409        )));
410        self.inset = Some(inset);
411        self
412    }
413
414    pub fn left_px(mut self, px: Px) -> Self {
415        self.ensure_position_for_inset();
416        let mut inset = self.inset.unwrap_or_default();
417        inset.left = Some(InsetEdgeRefinement::Px(SignedMetricRef::pos(px.into())));
418        self.inset = Some(inset);
419        self
420    }
421
422    /// Shorthand for `left: 100%` of the containing block (percent sizing).
423    pub fn left_full(mut self) -> Self {
424        self.ensure_position_for_inset();
425        let mut inset = self.inset.unwrap_or_default();
426        inset.left = Some(InsetEdgeRefinement::Fill);
427        self.inset = Some(inset);
428        self
429    }
430
431    /// Shorthand for `left: <fraction>` of the containing block.
432    pub fn left_fraction(mut self, fraction: f32) -> Self {
433        self.ensure_position_for_inset();
434        let mut inset = self.inset.unwrap_or_default();
435        inset.left = Some(InsetEdgeRefinement::Fraction(fraction));
436        self.inset = Some(inset);
437        self
438    }
439
440    /// Shorthand for `left: <percent>%` of the containing block.
441    pub fn left_percent(self, percent: f32) -> Self {
442        self.left_fraction(percent / 100.0)
443    }
444
445    pub fn left_neg(mut self, space: Space) -> Self {
446        self.ensure_position_for_inset();
447        let mut inset = self.inset.unwrap_or_default();
448        inset.left = Some(InsetEdgeRefinement::Px(SignedMetricRef::neg(
449            MetricRef::space(space),
450        )));
451        self.inset = Some(inset);
452        self
453    }
454
455    pub fn left_neg_px(mut self, px: impl Into<MetricRef>) -> Self {
456        self.ensure_position_for_inset();
457        let mut inset = self.inset.unwrap_or_default();
458        inset.left = Some(InsetEdgeRefinement::Px(SignedMetricRef::neg(px.into())));
459        self.inset = Some(inset);
460        self
461    }
462
463    pub fn m(mut self, space: Space) -> Self {
464        let m = MarginEdgeRefinement::Px(SignedMetricRef::pos(MetricRef::space(space)));
465        self.margin = Some(MarginRefinement {
466            top: Some(m.clone()),
467            right: Some(m.clone()),
468            bottom: Some(m.clone()),
469            left: Some(m),
470        });
471        self
472    }
473
474    pub fn m_px(mut self, px: Px) -> Self {
475        let m = MarginEdgeRefinement::Px(SignedMetricRef::pos(px.into()));
476        self.margin = Some(MarginRefinement {
477            top: Some(m.clone()),
478            right: Some(m.clone()),
479            bottom: Some(m.clone()),
480            left: Some(m),
481        });
482        self
483    }
484
485    pub fn m_neg(mut self, space: Space) -> Self {
486        let m = MarginEdgeRefinement::Px(SignedMetricRef::neg(MetricRef::space(space)));
487        self.margin = Some(MarginRefinement {
488            top: Some(m.clone()),
489            right: Some(m.clone()),
490            bottom: Some(m.clone()),
491            left: Some(m),
492        });
493        self
494    }
495
496    pub fn m_auto(mut self) -> Self {
497        let a = MarginEdgeRefinement::Auto;
498        self.margin = Some(MarginRefinement {
499            top: Some(a.clone()),
500            right: Some(a.clone()),
501            bottom: Some(a.clone()),
502            left: Some(a),
503        });
504        self
505    }
506
507    pub fn mx(mut self, space: Space) -> Self {
508        let mut margin = self.margin.unwrap_or_default();
509        let m = MarginEdgeRefinement::Px(SignedMetricRef::pos(MetricRef::space(space)));
510        margin.left = Some(m.clone());
511        margin.right = Some(m);
512        self.margin = Some(margin);
513        self
514    }
515
516    pub fn mx_px(mut self, px: Px) -> Self {
517        let mut margin = self.margin.unwrap_or_default();
518        let m = MarginEdgeRefinement::Px(SignedMetricRef::pos(px.into()));
519        margin.left = Some(m.clone());
520        margin.right = Some(m);
521        self.margin = Some(margin);
522        self
523    }
524
525    pub fn mx_neg(mut self, space: Space) -> Self {
526        let mut margin = self.margin.unwrap_or_default();
527        let m = MarginEdgeRefinement::Px(SignedMetricRef::neg(MetricRef::space(space)));
528        margin.left = Some(m.clone());
529        margin.right = Some(m);
530        self.margin = Some(margin);
531        self
532    }
533
534    pub fn mx_auto(mut self) -> Self {
535        let mut margin = self.margin.unwrap_or_default();
536        margin.left = Some(MarginEdgeRefinement::Auto);
537        margin.right = Some(MarginEdgeRefinement::Auto);
538        self.margin = Some(margin);
539        self
540    }
541
542    pub fn my(mut self, space: Space) -> Self {
543        let mut margin = self.margin.unwrap_or_default();
544        let m = MarginEdgeRefinement::Px(SignedMetricRef::pos(MetricRef::space(space)));
545        margin.top = Some(m.clone());
546        margin.bottom = Some(m);
547        self.margin = Some(margin);
548        self
549    }
550
551    pub fn my_px(mut self, px: Px) -> Self {
552        let mut margin = self.margin.unwrap_or_default();
553        let m = MarginEdgeRefinement::Px(SignedMetricRef::pos(px.into()));
554        margin.top = Some(m.clone());
555        margin.bottom = Some(m);
556        self.margin = Some(margin);
557        self
558    }
559
560    pub fn my_neg(mut self, space: Space) -> Self {
561        let mut margin = self.margin.unwrap_or_default();
562        let m = MarginEdgeRefinement::Px(SignedMetricRef::neg(MetricRef::space(space)));
563        margin.top = Some(m.clone());
564        margin.bottom = Some(m);
565        self.margin = Some(margin);
566        self
567    }
568
569    pub fn my_auto(mut self) -> Self {
570        let mut margin = self.margin.unwrap_or_default();
571        margin.top = Some(MarginEdgeRefinement::Auto);
572        margin.bottom = Some(MarginEdgeRefinement::Auto);
573        self.margin = Some(margin);
574        self
575    }
576
577    pub fn mt(mut self, space: Space) -> Self {
578        let mut margin = self.margin.unwrap_or_default();
579        margin.top = Some(MarginEdgeRefinement::Px(SignedMetricRef::pos(
580            MetricRef::space(space),
581        )));
582        self.margin = Some(margin);
583        self
584    }
585
586    pub fn mt_px(mut self, px: Px) -> Self {
587        let mut margin = self.margin.unwrap_or_default();
588        margin.top = Some(MarginEdgeRefinement::Px(SignedMetricRef::pos(px.into())));
589        self.margin = Some(margin);
590        self
591    }
592
593    pub fn mt_neg(mut self, space: Space) -> Self {
594        let mut margin = self.margin.unwrap_or_default();
595        margin.top = Some(MarginEdgeRefinement::Px(SignedMetricRef::neg(
596            MetricRef::space(space),
597        )));
598        self.margin = Some(margin);
599        self
600    }
601
602    pub fn mt_auto(mut self) -> Self {
603        let mut margin = self.margin.unwrap_or_default();
604        margin.top = Some(MarginEdgeRefinement::Auto);
605        self.margin = Some(margin);
606        self
607    }
608
609    pub fn mr(mut self, space: Space) -> Self {
610        let mut margin = self.margin.unwrap_or_default();
611        margin.right = Some(MarginEdgeRefinement::Px(SignedMetricRef::pos(
612            MetricRef::space(space),
613        )));
614        self.margin = Some(margin);
615        self
616    }
617
618    pub fn mr_px(mut self, px: Px) -> Self {
619        let mut margin = self.margin.unwrap_or_default();
620        margin.right = Some(MarginEdgeRefinement::Px(SignedMetricRef::pos(px.into())));
621        self.margin = Some(margin);
622        self
623    }
624
625    pub fn mr_neg(mut self, space: Space) -> Self {
626        let mut margin = self.margin.unwrap_or_default();
627        margin.right = Some(MarginEdgeRefinement::Px(SignedMetricRef::neg(
628            MetricRef::space(space),
629        )));
630        self.margin = Some(margin);
631        self
632    }
633
634    pub fn mr_auto(mut self) -> Self {
635        let mut margin = self.margin.unwrap_or_default();
636        margin.right = Some(MarginEdgeRefinement::Auto);
637        self.margin = Some(margin);
638        self
639    }
640
641    pub fn mb(mut self, space: Space) -> Self {
642        let mut margin = self.margin.unwrap_or_default();
643        margin.bottom = Some(MarginEdgeRefinement::Px(SignedMetricRef::pos(
644            MetricRef::space(space),
645        )));
646        self.margin = Some(margin);
647        self
648    }
649
650    pub fn mb_px(mut self, px: Px) -> Self {
651        let mut margin = self.margin.unwrap_or_default();
652        margin.bottom = Some(MarginEdgeRefinement::Px(SignedMetricRef::pos(px.into())));
653        self.margin = Some(margin);
654        self
655    }
656
657    pub fn mb_neg(mut self, space: Space) -> Self {
658        let mut margin = self.margin.unwrap_or_default();
659        margin.bottom = Some(MarginEdgeRefinement::Px(SignedMetricRef::neg(
660            MetricRef::space(space),
661        )));
662        self.margin = Some(margin);
663        self
664    }
665
666    pub fn mb_auto(mut self) -> Self {
667        let mut margin = self.margin.unwrap_or_default();
668        margin.bottom = Some(MarginEdgeRefinement::Auto);
669        self.margin = Some(margin);
670        self
671    }
672
673    pub fn ml(mut self, space: Space) -> Self {
674        let mut margin = self.margin.unwrap_or_default();
675        margin.left = Some(MarginEdgeRefinement::Px(SignedMetricRef::pos(
676            MetricRef::space(space),
677        )));
678        self.margin = Some(margin);
679        self
680    }
681
682    pub fn ml_px(mut self, px: Px) -> Self {
683        let mut margin = self.margin.unwrap_or_default();
684        margin.left = Some(MarginEdgeRefinement::Px(SignedMetricRef::pos(px.into())));
685        self.margin = Some(margin);
686        self
687    }
688
689    pub fn ml_neg(mut self, space: Space) -> Self {
690        let mut margin = self.margin.unwrap_or_default();
691        margin.left = Some(MarginEdgeRefinement::Px(SignedMetricRef::neg(
692            MetricRef::space(space),
693        )));
694        self.margin = Some(margin);
695        self
696    }
697
698    pub fn ml_auto(mut self) -> Self {
699        let mut margin = self.margin.unwrap_or_default();
700        margin.left = Some(MarginEdgeRefinement::Auto);
701        self.margin = Some(margin);
702        self
703    }
704
705    fn ensure_size_mut(&mut self) -> &mut SizeRefinement {
706        if self.size.is_none() {
707            self.size = Some(SizeRefinement::default());
708        }
709        self.size.as_mut().expect("size exists")
710    }
711
712    fn ensure_flex_item_mut(&mut self) -> &mut FlexItemRefinement {
713        if self.flex_item.is_none() {
714            self.flex_item = Some(FlexItemRefinement::default());
715        }
716        self.flex_item.as_mut().expect("flex_item exists")
717    }
718
719    /// Shorthand for `min-width: <px>`.
720    pub fn min_w(mut self, width: impl Into<MetricRef>) -> Self {
721        self.ensure_size_mut().min_width = Some(LengthRefinement::Px(width.into()));
722        self
723    }
724
725    pub fn min_w_space(self, width: Space) -> Self {
726        self.min_w(MetricRef::space(width))
727    }
728
729    /// Shorthand for `min-width: 100%` of the containing block.
730    pub fn min_w_full(mut self) -> Self {
731        self.ensure_size_mut().min_width = Some(LengthRefinement::Fill);
732        self
733    }
734
735    /// Shorthand for `min-width: <fraction>` of the containing block.
736    pub fn min_w_fraction(mut self, fraction: f32) -> Self {
737        self.ensure_size_mut().min_width = Some(LengthRefinement::Fraction(fraction));
738        self
739    }
740
741    /// Shorthand for `min-width: <percent>%` of the containing block.
742    pub fn min_w_percent(self, percent: f32) -> Self {
743        self.min_w_fraction(percent / 100.0)
744    }
745
746    /// Shorthand for `min-height: <px>`.
747    pub fn min_h(mut self, height: impl Into<MetricRef>) -> Self {
748        self.ensure_size_mut().min_height = Some(LengthRefinement::Px(height.into()));
749        self
750    }
751
752    pub fn min_h_space(self, height: Space) -> Self {
753        self.min_h(MetricRef::space(height))
754    }
755
756    /// Shorthand for `min-height: 100%` of the containing block.
757    pub fn min_h_full(mut self) -> Self {
758        self.ensure_size_mut().min_height = Some(LengthRefinement::Fill);
759        self
760    }
761
762    /// Shorthand for `min-height: <fraction>` of the containing block.
763    pub fn min_h_fraction(mut self, fraction: f32) -> Self {
764        self.ensure_size_mut().min_height = Some(LengthRefinement::Fraction(fraction));
765        self
766    }
767
768    /// Shorthand for `min-height: <percent>%` of the containing block.
769    pub fn min_h_percent(self, percent: f32) -> Self {
770        self.min_h_fraction(percent / 100.0)
771    }
772
773    /// Shorthand for `min-width: 0px`.
774    ///
775    /// This is especially important for text-heavy children in a horizontal flex row: without it,
776    /// the flex item may refuse to shrink (web-like `min-width: auto` behavior), causing overflow.
777    pub fn min_w_0(self) -> Self {
778        self.min_w(Px(0.0))
779    }
780
781    pub fn w(mut self, width: LengthRefinement) -> Self {
782        self.ensure_size_mut().width = Some(width);
783        self
784    }
785
786    pub fn h(mut self, height: LengthRefinement) -> Self {
787        self.ensure_size_mut().height = Some(height);
788        self
789    }
790
791    pub fn w_px(self, width: impl Into<MetricRef>) -> Self {
792        self.w(LengthRefinement::Px(width.into()))
793    }
794
795    pub fn w_space(self, width: Space) -> Self {
796        self.w_px(MetricRef::space(width))
797    }
798
799    /// Shorthand for `width: auto`.
800    pub fn w_auto(self) -> Self {
801        self.w(LengthRefinement::Auto)
802    }
803
804    pub fn h_px(self, height: impl Into<MetricRef>) -> Self {
805        self.h(LengthRefinement::Px(height.into()))
806    }
807
808    pub fn h_space(self, height: Space) -> Self {
809        self.h_px(MetricRef::space(height))
810    }
811
812    /// Shorthand for `width: 100%` of the containing block (percent sizing).
813    ///
814    /// This is **not** the same as Tailwind `flex-1`. For "take the remaining space as a flex
815    /// item", use [`Self::flex_1`] (and typically [`Self::min_w_0`] for text).
816    pub fn w_full(self) -> Self {
817        self.w(LengthRefinement::Fill)
818    }
819
820    /// Shorthand for `width: <fraction>` of the containing block.
821    ///
822    /// Example: `w_fraction(0.5)` == 50% width.
823    pub fn w_fraction(self, fraction: f32) -> Self {
824        self.w(LengthRefinement::Fraction(fraction))
825    }
826
827    /// Shorthand for `width: <percent>%` of the containing block.
828    pub fn w_percent(self, percent: f32) -> Self {
829        self.w_fraction(percent / 100.0)
830    }
831
832    /// Shorthand for `height: 100%` of the containing block (percent sizing).
833    pub fn h_full(self) -> Self {
834        self.h(LengthRefinement::Fill)
835    }
836
837    /// Shorthand for `height: <fraction>` of the containing block.
838    ///
839    /// Example: `h_fraction(0.5)` == 50% height.
840    pub fn h_fraction(self, fraction: f32) -> Self {
841        self.h(LengthRefinement::Fraction(fraction))
842    }
843
844    /// Shorthand for `height: <percent>%` of the containing block.
845    pub fn h_percent(self, percent: f32) -> Self {
846        self.h_fraction(percent / 100.0)
847    }
848
849    pub fn size_full(self) -> Self {
850        self.w_full().h_full()
851    }
852
853    /// Shorthand for `max-width: <px>`.
854    pub fn max_w(mut self, width: impl Into<MetricRef>) -> Self {
855        self.ensure_size_mut().max_width = Some(LengthRefinement::Px(width.into()));
856        self
857    }
858
859    pub fn max_w_space(self, width: Space) -> Self {
860        self.max_w(MetricRef::space(width))
861    }
862
863    /// Shorthand for `max-width: 100%` of the containing block.
864    pub fn max_w_full(mut self) -> Self {
865        self.ensure_size_mut().max_width = Some(LengthRefinement::Fill);
866        self
867    }
868
869    /// Shorthand for `max-width: <fraction>` of the containing block.
870    pub fn max_w_fraction(mut self, fraction: f32) -> Self {
871        self.ensure_size_mut().max_width = Some(LengthRefinement::Fraction(fraction));
872        self
873    }
874
875    /// Shorthand for `max-width: <percent>%` of the containing block.
876    pub fn max_w_percent(self, percent: f32) -> Self {
877        self.max_w_fraction(percent / 100.0)
878    }
879
880    /// Shorthand for `max-height: <px>`.
881    pub fn max_h(mut self, height: impl Into<MetricRef>) -> Self {
882        self.ensure_size_mut().max_height = Some(LengthRefinement::Px(height.into()));
883        self
884    }
885
886    pub fn max_h_space(self, height: Space) -> Self {
887        self.max_h(MetricRef::space(height))
888    }
889
890    /// Shorthand for `max-height: 100%` of the containing block.
891    pub fn max_h_full(mut self) -> Self {
892        self.ensure_size_mut().max_height = Some(LengthRefinement::Fill);
893        self
894    }
895
896    /// Shorthand for `max-height: <fraction>` of the containing block.
897    pub fn max_h_fraction(mut self, fraction: f32) -> Self {
898        self.ensure_size_mut().max_height = Some(LengthRefinement::Fraction(fraction));
899        self
900    }
901
902    /// Shorthand for `max-height: <percent>%` of the containing block.
903    pub fn max_h_percent(self, percent: f32) -> Self {
904        self.max_h_fraction(percent / 100.0)
905    }
906
907    pub fn basis(mut self, basis: LengthRefinement) -> Self {
908        self.ensure_flex_item_mut().basis = Some(basis);
909        self
910    }
911
912    /// Shorthand for `flex-basis: <fraction>` of the containing block size (main axis).
913    pub fn basis_fraction(self, fraction: f32) -> Self {
914        self.basis(LengthRefinement::Fraction(fraction))
915    }
916
917    /// Shorthand for `flex-basis: <percent>%` of the containing block size (main axis).
918    pub fn basis_percent(self, percent: f32) -> Self {
919        self.basis_fraction(percent / 100.0)
920    }
921
922    pub fn basis_px(self, basis: impl Into<MetricRef>) -> Self {
923        self.basis(LengthRefinement::Px(basis.into()))
924    }
925
926    pub fn basis_0(self) -> Self {
927        self.basis_px(Px(0.0))
928    }
929
930    pub fn flex_grow(mut self, grow: f32) -> Self {
931        self.ensure_flex_item_mut().grow = Some(grow);
932        self
933    }
934
935    pub fn flex_shrink(mut self, shrink: f32) -> Self {
936        self.ensure_flex_item_mut().shrink = Some(shrink);
937        self
938    }
939
940    pub fn flex_shrink_0(self) -> Self {
941        self.flex_shrink(0.0)
942    }
943
944    pub fn order(mut self, order: i32) -> Self {
945        self.ensure_flex_item_mut().order = Some(order);
946        self
947    }
948
949    /// Per-item alignment on the container cross axis.
950    ///
951    /// This feeds both flex and grid `align-self` so first-party surfaces can express `self-*`
952    /// semantics without patching raw layout props after style resolution.
953    pub fn align_self(mut self, align: CrossAlign) -> Self {
954        self.align_self = Some(align);
955        self
956    }
957
958    pub fn self_start(self) -> Self {
959        self.align_self(CrossAlign::Start)
960    }
961
962    pub fn self_center(self) -> Self {
963        self.align_self(CrossAlign::Center)
964    }
965
966    pub fn self_end(self) -> Self {
967        self.align_self(CrossAlign::End)
968    }
969
970    pub fn self_stretch(self) -> Self {
971        self.align_self(CrossAlign::Stretch)
972    }
973
974    /// Inline-axis item alignment for grid children (`justify-self`).
975    pub fn justify_self(mut self, align: CrossAlign) -> Self {
976        self.justify_self = Some(align);
977        self
978    }
979
980    pub fn justify_self_start(self) -> Self {
981        self.justify_self(CrossAlign::Start)
982    }
983
984    pub fn justify_self_center(self) -> Self {
985        self.justify_self(CrossAlign::Center)
986    }
987
988    pub fn justify_self_end(self) -> Self {
989        self.justify_self(CrossAlign::End)
990    }
991
992    pub fn justify_self_stretch(self) -> Self {
993        self.justify_self(CrossAlign::Stretch)
994    }
995
996    /// Tailwind-like `flex-1` shorthand: `grow=1`, `shrink=1`, `basis=0`.
997    ///
998    /// Tip: combine with [`Self::min_w_0`] when the child contains text that should be allowed to
999    /// shrink/wrap instead of overflowing.
1000    pub fn flex_1(mut self) -> Self {
1001        {
1002            let f = self.ensure_flex_item_mut();
1003            f.grow = Some(1.0);
1004            f.shrink = Some(1.0);
1005            f.basis = Some(LengthRefinement::Px(Px(0.0).into()));
1006        }
1007        self
1008    }
1009
1010    /// Tailwind-like `flex-none` shorthand: `grow=0`, `shrink=0`, `basis=auto`.
1011    pub fn flex_none(mut self) -> Self {
1012        {
1013            let f = self.ensure_flex_item_mut();
1014            f.grow = Some(0.0);
1015            f.shrink = Some(0.0);
1016            f.basis = Some(LengthRefinement::Auto);
1017        }
1018        self
1019    }
1020}