1use std::{fmt, sync::Arc};
4
5use bitflags::bitflags;
6use unicode_bidi::BidiDataSource as _;
7use zng_app_context::context_local;
8use zng_unit::{Factor, Px, PxRect, PxSize, about_eq, about_eq_hash, euclid};
9use zng_var::context_var;
10
11use atomic::{Atomic, Ordering::Relaxed};
12
13use crate::unit::{LayoutAxis, Ppi, PxConstraints, PxConstraints2d};
14
15pub struct LAYOUT;
19impl LAYOUT {
20 pub fn pass_id(&self) -> LayoutPassId {
24 LAYOUT_PASS_CTX.get_clone()
25 }
26
27 pub fn with_root_context<R>(&self, pass_id: LayoutPassId, metrics: LayoutMetrics, f: impl FnOnce() -> R) -> R {
29 let mut pass = Some(Arc::new(pass_id));
30 LAYOUT_PASS_CTX.with_context(&mut pass, || self.with_context(metrics, f))
31 }
32
33 pub fn with_context<R>(&self, metrics: LayoutMetrics, f: impl FnOnce() -> R) -> R {
35 let mut ctx = Some(Arc::new(LayoutCtx { metrics }));
36 LAYOUT_CTX.with_context(&mut ctx, f)
37 }
38
39 pub fn with_no_context<R>(&self, f: impl FnOnce() -> R) -> R {
41 LAYOUT_CTX.with_default(f)
42 }
43
44 pub fn metrics(&self) -> LayoutMetrics {
46 LAYOUT_CTX.get().metrics.clone()
47 }
48
49 pub fn capture_metrics_use<R>(&self, f: impl FnOnce() -> R) -> (LayoutMask, R) {
56 METRICS_USED_CTX.with_context(&mut Some(Arc::new(Atomic::new(LayoutMask::empty()))), || {
57 let r = f();
58 let uses = METRICS_USED_CTX.get().load(Relaxed);
59 (uses, r)
60 })
61 }
62
63 pub fn register_metrics_use(&self, uses: LayoutMask) {
67 let ctx = METRICS_USED_CTX.get();
68 let m = ctx.load(Relaxed);
69 ctx.store(m | uses, Relaxed);
70 }
71
72 pub fn constraints(&self) -> PxConstraints2d {
74 LAYOUT_CTX.get().metrics.constraints()
75 }
76
77 pub fn z_constraints(&self) -> PxConstraints {
79 LAYOUT_CTX.get().metrics.z_constraints()
80 }
81
82 pub fn constraints_for(&self, axis: LayoutAxis) -> PxConstraints {
84 match axis {
85 LayoutAxis::X => self.constraints().x,
86 LayoutAxis::Y => self.constraints().y,
87 LayoutAxis::Z => self.z_constraints(),
88 }
89 }
90
91 pub fn with_constraints<R>(&self, constraints: PxConstraints2d, f: impl FnOnce() -> R) -> R {
93 self.with_context(self.metrics().with_constraints(constraints), f)
94 }
95
96 pub fn with_z_constraints<R>(&self, constraints: PxConstraints, f: impl FnOnce() -> R) -> R {
98 self.with_context(self.metrics().with_z_constraints(constraints), f)
99 }
100
101 pub fn with_constraints_for<R>(&self, axis: LayoutAxis, constraints: PxConstraints, f: impl FnOnce() -> R) -> R {
103 match axis {
104 LayoutAxis::X => {
105 let mut c = self.constraints();
106 c.x = constraints;
107 self.with_constraints(c, f)
108 }
109 LayoutAxis::Y => {
110 let mut c = self.constraints();
111 c.y = constraints;
112 self.with_constraints(c, f)
113 }
114 LayoutAxis::Z => self.with_z_constraints(constraints, f),
115 }
116 }
117
118 pub fn with_sub_size(&self, removed: PxSize, f: impl FnOnce() -> PxSize) -> PxSize {
120 self.with_constraints(self.constraints().with_less_size(removed), f) + removed
121 }
122
123 pub fn with_add_size(&self, added: PxSize, f: impl FnOnce() -> PxSize) -> PxSize {
125 self.with_constraints(self.constraints().with_more_size(added), f) - added
126 }
127
128 pub fn inline_constraints(&self) -> Option<InlineConstraints> {
130 LAYOUT_CTX.get().metrics.inline_constraints()
131 }
132
133 pub fn with_no_inline<R>(&self, f: impl FnOnce() -> R) -> R {
135 let metrics = self.metrics();
136 if metrics.inline_constraints().is_none() {
137 f()
138 } else {
139 self.with_context(metrics.with_inline_constraints(None), f)
140 }
141 }
142
143 pub fn root_font_size(&self) -> Px {
145 LAYOUT_CTX.get().metrics.root_font_size()
146 }
147
148 pub fn font_size(&self) -> Px {
150 LAYOUT_CTX.get().metrics.font_size()
151 }
152
153 pub fn with_font_size<R>(&self, font_size: Px, f: impl FnOnce() -> R) -> R {
155 self.with_context(self.metrics().with_font_size(font_size), f)
156 }
157
158 pub fn viewport(&self) -> PxSize {
160 LAYOUT_CTX.get().metrics.viewport()
161 }
162
163 pub fn viewport_min(&self) -> Px {
165 LAYOUT_CTX.get().metrics.viewport_min()
166 }
167
168 pub fn viewport_max(&self) -> Px {
170 LAYOUT_CTX.get().metrics.viewport_max()
171 }
172
173 pub fn viewport_for(&self, axis: LayoutAxis) -> Px {
175 let vp = self.viewport();
176 match axis {
177 LayoutAxis::X => vp.width,
178 LayoutAxis::Y => vp.height,
179 LayoutAxis::Z => Px::MAX,
180 }
181 }
182
183 pub fn with_viewport<R>(&self, viewport: PxSize, f: impl FnOnce() -> R) -> R {
185 self.with_context(self.metrics().with_viewport(viewport), f)
186 }
187
188 pub fn scale_factor(&self) -> Factor {
190 LAYOUT_CTX.get().metrics.scale_factor()
191 }
192
193 pub fn with_scale_factor<R>(&self, scale_factor: Factor, f: impl FnOnce() -> R) -> R {
195 self.with_context(self.metrics().with_scale_factor(scale_factor), f)
196 }
197
198 pub fn screen_ppi(&self) -> Ppi {
200 LAYOUT_CTX.get().metrics.screen_ppi()
201 }
202
203 pub fn with_screen_ppi<R>(&self, screen_ppi: Ppi, f: impl FnOnce() -> R) -> R {
205 self.with_context(self.metrics().with_screen_ppi(screen_ppi), f)
206 }
207
208 pub fn direction(&self) -> LayoutDirection {
210 LAYOUT_CTX.get().metrics.direction()
211 }
212
213 pub fn with_direction<R>(&self, direction: LayoutDirection, f: impl FnOnce() -> R) -> R {
215 self.with_context(self.metrics().with_direction(direction), f)
216 }
217
218 pub fn leftover(&self) -> euclid::Size2D<Option<Px>, ()> {
222 LAYOUT_CTX.get().metrics.leftover()
223 }
224
225 pub fn leftover_for(&self, axis: LayoutAxis) -> Option<Px> {
227 let l = self.leftover();
228
229 match axis {
230 LayoutAxis::X => l.width,
231 LayoutAxis::Y => l.height,
232 LayoutAxis::Z => None,
233 }
234 }
235
236 pub fn with_leftover<R>(&self, width: Option<Px>, height: Option<Px>, f: impl FnOnce() -> R) -> R {
240 self.with_context(self.metrics().with_leftover(width, height), f)
241 }
242}
243
244context_local! {
245 static LAYOUT_CTX: LayoutCtx = LayoutCtx::no_context();
246 static LAYOUT_PASS_CTX: LayoutPassId = LayoutPassId::new();
247 static METRICS_USED_CTX: Atomic<LayoutMask> = Atomic::new(LayoutMask::empty());
248}
249
250struct LayoutCtx {
251 metrics: LayoutMetrics,
252}
253impl LayoutCtx {
254 fn no_context() -> Self {
255 panic!("no layout context")
256 }
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
263pub struct LayoutPassId(u32);
264impl LayoutPassId {
265 pub const fn new() -> Self {
267 LayoutPassId(0)
268 }
269
270 pub const fn next(self) -> LayoutPassId {
272 LayoutPassId(self.0.wrapping_add(1))
273 }
274}
275
276#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
280pub struct InlineConstraintsMeasure {
281 pub first_max: Px,
283 pub mid_clear_min: Px,
288}
289
290#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
296pub struct InlineSegmentPos {
297 pub x: f32,
299}
300impl PartialEq for InlineSegmentPos {
301 fn eq(&self, other: &Self) -> bool {
302 about_eq(self.x, other.x, 0.001)
303 }
304}
305impl Eq for InlineSegmentPos {}
306impl std::hash::Hash for InlineSegmentPos {
307 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
308 about_eq_hash(self.x, 0.001, state);
309 }
310}
311
312#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
316pub struct InlineConstraintsLayout {
317 pub first: PxRect,
319 pub mid_clear: Px,
321 pub last: PxRect,
323
324 pub first_segs: Arc<Vec<InlineSegmentPos>>,
326 pub last_segs: Arc<Vec<InlineSegmentPos>>,
328}
329
330#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
332pub enum InlineConstraints {
333 Measure(InlineConstraintsMeasure),
335 Layout(InlineConstraintsLayout),
337}
338impl InlineConstraints {
339 pub fn measure(self) -> InlineConstraintsMeasure {
341 match self {
342 InlineConstraints::Measure(m) => m,
343 InlineConstraints::Layout(l) => InlineConstraintsMeasure {
344 first_max: l.first.width(),
345 mid_clear_min: l.mid_clear,
346 },
347 }
348 }
349
350 pub fn layout(self) -> InlineConstraintsLayout {
352 match self {
353 InlineConstraints::Layout(m) => m,
354 InlineConstraints::Measure(_) => Default::default(),
355 }
356 }
357}
358
359#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
363pub struct LayoutMetricsSnapshot {
364 pub constraints: PxConstraints2d,
368
369 pub inline_constraints: Option<InlineConstraints>,
373
374 pub z_constraints: PxConstraints,
378
379 pub font_size: Px,
383 pub root_font_size: Px,
387 pub scale_factor: Factor,
391 pub viewport: PxSize,
395 pub screen_ppi: Ppi,
399
400 pub direction: LayoutDirection,
404
405 pub leftover: euclid::Size2D<Option<Px>, ()>,
409}
410impl LayoutMetricsSnapshot {
411 pub fn masked_eq(&self, other: &Self, mask: LayoutMask) -> bool {
413 (!mask.contains(LayoutMask::CONSTRAINTS)
414 || (self.constraints == other.constraints
415 && self.z_constraints == other.z_constraints
416 && self.inline_constraints == other.inline_constraints))
417 && (!mask.contains(LayoutMask::FONT_SIZE) || self.font_size == other.font_size)
418 && (!mask.contains(LayoutMask::ROOT_FONT_SIZE) || self.root_font_size == other.root_font_size)
419 && (!mask.contains(LayoutMask::SCALE_FACTOR) || self.scale_factor == other.scale_factor)
420 && (!mask.contains(LayoutMask::VIEWPORT) || self.viewport == other.viewport)
421 && (!mask.contains(LayoutMask::SCREEN_PPI) || self.screen_ppi == other.screen_ppi)
422 && (!mask.contains(LayoutMask::DIRECTION) || self.direction == other.direction)
423 && (!mask.contains(LayoutMask::LEFTOVER) || self.leftover == other.leftover)
424 }
425}
426impl PartialEq for LayoutMetricsSnapshot {
427 fn eq(&self, other: &Self) -> bool {
428 self.constraints == other.constraints
429 && self.z_constraints == other.z_constraints
430 && self.inline_constraints == other.inline_constraints
431 && self.font_size == other.font_size
432 && self.root_font_size == other.root_font_size
433 && self.scale_factor == other.scale_factor
434 && self.viewport == other.viewport
435 && self.screen_ppi == other.screen_ppi
436 }
437}
438impl std::hash::Hash for LayoutMetricsSnapshot {
439 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
440 self.constraints.hash(state);
441 self.inline_constraints.hash(state);
442 self.font_size.hash(state);
443 self.root_font_size.hash(state);
444 self.scale_factor.hash(state);
445 self.viewport.hash(state);
446 self.screen_ppi.hash(state);
447 }
448}
449
450#[derive(Debug, Clone)]
452pub struct LayoutMetrics {
453 s: LayoutMetricsSnapshot,
454}
455impl LayoutMetrics {
456 pub fn new(scale_factor: Factor, viewport: PxSize, font_size: Px) -> Self {
463 LayoutMetrics {
464 s: LayoutMetricsSnapshot {
465 constraints: PxConstraints2d::new_fill_size(viewport),
466 z_constraints: PxConstraints::new_unbounded().with_min(Px(1)),
467 inline_constraints: None,
468 font_size,
469 root_font_size: font_size,
470 scale_factor,
471 viewport,
472 screen_ppi: Ppi::default(),
473 direction: LayoutDirection::default(),
474 leftover: euclid::size2(None, None),
475 },
476 }
477 }
478
479 pub fn constraints(&self) -> PxConstraints2d {
481 LAYOUT.register_metrics_use(LayoutMask::CONSTRAINTS);
482 self.s.constraints
483 }
484
485 pub fn z_constraints(&self) -> PxConstraints {
487 LAYOUT.register_metrics_use(LayoutMask::CONSTRAINTS);
488 self.s.z_constraints
489 }
490
491 pub fn inline_constraints(&self) -> Option<InlineConstraints> {
495 LAYOUT.register_metrics_use(LayoutMask::CONSTRAINTS);
496 self.s.inline_constraints.clone()
497 }
498
499 pub fn direction(&self) -> LayoutDirection {
501 LAYOUT.register_metrics_use(LayoutMask::DIRECTION);
502 self.s.direction
503 }
504
505 pub fn font_size(&self) -> Px {
507 LAYOUT.register_metrics_use(LayoutMask::FONT_SIZE);
508 self.s.font_size
509 }
510
511 pub fn root_font_size(&self) -> Px {
513 LAYOUT.register_metrics_use(LayoutMask::ROOT_FONT_SIZE);
514 self.s.root_font_size
515 }
516
517 pub fn scale_factor(&self) -> Factor {
519 LAYOUT.register_metrics_use(LayoutMask::SCALE_FACTOR);
520 self.s.scale_factor
521 }
522
523 pub fn viewport(&self) -> PxSize {
528 LAYOUT.register_metrics_use(LayoutMask::VIEWPORT);
529 self.s.viewport
530 }
531
532 pub fn viewport_min(&self) -> Px {
536 self.s.viewport.width.min(self.s.viewport.height)
537 }
538
539 pub fn viewport_max(&self) -> Px {
543 self.s.viewport.width.max(self.s.viewport.height)
544 }
545
546 pub fn screen_ppi(&self) -> Ppi {
552 self.s.screen_ppi
553 }
554
555 pub fn leftover(&self) -> euclid::Size2D<Option<Px>, ()> {
559 LAYOUT.register_metrics_use(LayoutMask::LEFTOVER);
560 self.s.leftover
561 }
562
563 pub fn with_constraints(mut self, constraints: PxConstraints2d) -> Self {
567 self.s.constraints = constraints;
568 self
569 }
570
571 pub fn with_z_constraints(mut self, constraints: PxConstraints) -> Self {
575 self.s.z_constraints = constraints;
576 self
577 }
578
579 pub fn with_inline_constraints(mut self, inline_constraints: Option<InlineConstraints>) -> Self {
583 self.s.inline_constraints = inline_constraints;
584 self
585 }
586
587 pub fn with_font_size(mut self, font_size: Px) -> Self {
591 self.s.font_size = font_size;
592 self
593 }
594
595 pub fn with_viewport(mut self, viewport: PxSize) -> Self {
599 self.s.viewport = viewport;
600 self
601 }
602
603 pub fn with_scale_factor(mut self, scale_factor: Factor) -> Self {
607 self.s.scale_factor = scale_factor;
608 self
609 }
610
611 pub fn with_screen_ppi(mut self, screen_ppi: Ppi) -> Self {
615 self.s.screen_ppi = screen_ppi;
616 self
617 }
618
619 pub fn with_direction(mut self, direction: LayoutDirection) -> Self {
623 self.s.direction = direction;
624 self
625 }
626
627 pub fn with_leftover(mut self, width: Option<Px>, height: Option<Px>) -> Self {
631 self.s.leftover = euclid::size2(width, height);
632 self
633 }
634
635 pub fn snapshot(&self) -> LayoutMetricsSnapshot {
639 self.s.clone()
640 }
641}
642
643context_var! {
644 pub static DIRECTION_VAR: LayoutDirection = LayoutDirection::LTR;
646}
647
648#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
660pub enum LayoutDirection {
661 LTR,
663 RTL,
665}
666impl LayoutDirection {
667 pub fn is_ltr(self) -> bool {
669 matches!(self, Self::LTR)
670 }
671
672 pub fn is_rtl(self) -> bool {
674 matches!(self, Self::RTL)
675 }
676}
677impl Default for LayoutDirection {
678 fn default() -> Self {
680 Self::LTR
681 }
682}
683impl fmt::Debug for LayoutDirection {
684 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
685 if f.alternate() {
686 write!(f, "LayoutDirection::")?;
687 }
688 match self {
689 Self::LTR => write!(f, "LTR"),
690 Self::RTL => write!(f, "RTL"),
691 }
692 }
693}
694
695#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
699pub struct InlineSegment {
700 pub width: f32,
702 pub kind: TextSegmentKind,
704}
705impl PartialEq for InlineSegment {
706 fn eq(&self, other: &Self) -> bool {
707 about_eq(self.width, other.width, 0.001) && self.kind == other.kind
708 }
709}
710impl Eq for InlineSegment {}
711impl std::hash::Hash for InlineSegment {
712 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
713 about_eq_hash(self.width, 0.001, state);
714 self.kind.hash(state);
715 }
716}
717
718#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
720pub enum TextSegmentKind {
721 LeftToRight,
723 RightToLeft,
725 ArabicLetter,
727
728 EuropeanNumber,
730 EuropeanSeparator,
732 EuropeanTerminator,
734 ArabicNumber,
736 CommonSeparator,
738 NonSpacingMark,
740 BoundaryNeutral,
742
743 Emoji,
745
746 LineBreak,
748 Tab,
750 Space,
752 OtherNeutral,
754 Bracket(char),
758
759 BidiCtrl(char),
774}
775impl TextSegmentKind {
776 pub fn is_word(self) -> bool {
778 use TextSegmentKind::*;
779 matches!(
780 self,
781 LeftToRight
782 | RightToLeft
783 | ArabicLetter
784 | EuropeanNumber
785 | EuropeanSeparator
786 | EuropeanTerminator
787 | ArabicNumber
788 | CommonSeparator
789 | NonSpacingMark
790 | BoundaryNeutral
791 | OtherNeutral
792 | Bracket(_)
793 | Emoji
794 )
795 }
796
797 pub fn is_space(self) -> bool {
799 matches!(self, Self::Space | Self::Tab)
800 }
801
802 pub fn is_line_break(self) -> bool {
806 matches!(self, Self::LineBreak)
807 }
808
809 pub fn can_merge(self) -> bool {
811 use TextSegmentKind::*;
812 !matches!(self, Bracket(_) | BidiCtrl(_))
813 }
814
815 pub fn bracket_info(self) -> Option<unicode_bidi::data_source::BidiMatchedOpeningBracket> {
817 if let TextSegmentKind::Bracket(c) = self {
818 unicode_bidi::HardcodedBidiData.bidi_matched_opening_bracket(c)
819 } else {
820 None
821 }
822 }
823
824 pub fn strong_direction(self) -> Option<LayoutDirection> {
828 use TextSegmentKind::*;
829
830 match self {
831 LeftToRight => Some(LayoutDirection::LTR),
832 RightToLeft | ArabicLetter => Some(LayoutDirection::RTL),
833 BidiCtrl(_) => {
834 use unicode_bidi::BidiClass::*;
835 match unicode_bidi::BidiClass::from(self) {
836 LRE | LRO | LRI => Some(LayoutDirection::LTR),
837 RLE | RLO | RLI => Some(LayoutDirection::RTL),
838 _ => None,
839 }
840 }
841 _ => None,
842 }
843 }
844}
845impl From<char> for TextSegmentKind {
846 fn from(c: char) -> Self {
847 use unicode_bidi::*;
848
849 unicode_bidi::HardcodedBidiData.bidi_class(c).into()
850 }
851}
852
853impl From<unicode_bidi::BidiClass> for TextSegmentKind {
854 fn from(value: unicode_bidi::BidiClass) -> Self {
855 use TextSegmentKind::*;
856 use unicode_bidi::BidiClass::*;
857
858 match value {
859 WS => Space,
860 L => LeftToRight,
861 R => RightToLeft,
862 AL => ArabicLetter,
863 AN => ArabicNumber,
864 CS => CommonSeparator,
865 B => LineBreak,
866 EN => EuropeanNumber,
867 ES => EuropeanSeparator,
868 ET => EuropeanTerminator,
869 S => Tab,
870 ON => OtherNeutral,
871 BN => BoundaryNeutral,
872 NSM => NonSpacingMark,
873 RLE => BidiCtrl('\u{202B}'),
874 LRI => BidiCtrl('\u{2066}'),
875 RLI => BidiCtrl('\u{2067}'),
876 LRO => BidiCtrl('\u{202D}'),
877 FSI => BidiCtrl('\u{2068}'),
878 PDF => BidiCtrl('\u{202C}'),
879 LRE => BidiCtrl('\u{202A}'),
880 PDI => BidiCtrl('\u{2069}'),
881 RLO => BidiCtrl('\u{202E}'),
882 }
883 }
884}
885impl From<TextSegmentKind> for unicode_bidi::BidiClass {
886 fn from(value: TextSegmentKind) -> Self {
887 use TextSegmentKind::*;
888 use unicode_bidi::BidiClass::*;
889
890 match value {
891 Space => WS,
892 LeftToRight => L,
893 RightToLeft => R,
894 ArabicLetter => AL,
895 ArabicNumber => AN,
896 CommonSeparator => CS,
897 LineBreak => B,
898 EuropeanNumber => EN,
899 EuropeanSeparator => ES,
900 EuropeanTerminator => ET,
901 Tab => S,
902 OtherNeutral | Emoji | Bracket(_) => ON,
903 BoundaryNeutral => BN,
904 NonSpacingMark => NSM,
905 BidiCtrl(c) => match c {
906 '\u{202A}' => LRE,
907 '\u{202D}' => LRO,
908 '\u{202B}' => RLE,
909 '\u{202E}' => RLO,
910 '\u{202C}' => PDF,
911 '\u{2066}' => LRI,
912 '\u{2067}' => RLI,
913 '\u{2068}' => FSI,
914 '\u{2069}' => PDI,
915 _c => {
916 #[cfg(debug_assertions)]
917 {
918 tracing::error!("invalid bidi ctrl char '{_c}'");
919 }
920 ON
921 }
922 },
923 }
924 }
925}
926
927bitflags! {
928 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, bytemuck::NoUninit)]
930 #[repr(transparent)]
931 pub struct LayoutMask: u32 {
932 const DEFAULT_VALUE = 1 << 31;
934 const CONSTRAINTS = 1 << 30;
936
937 const FONT_SIZE = 1;
939 const ROOT_FONT_SIZE = 1 << 1;
941 const SCALE_FACTOR = 1 << 2;
943 const VIEWPORT = 1 << 3;
945 const SCREEN_PPI = 1 << 4;
947 const DIRECTION = 1 << 5;
949 const LEFTOVER = 1 << 6;
951 }
952}
953impl Default for LayoutMask {
954 fn default() -> Self {
956 LayoutMask::empty()
957 }
958}