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, about_eq_ord, euclid};
9use zng_var::context_var;
10
11use atomic::{Atomic, Ordering::Relaxed};
12
13use crate::unit::{LayoutAxis, PxConstraints, PxConstraints2d, PxDensity};
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 #[inline(always)]
56 pub fn capture_metrics_use<R>(&self, f: impl FnOnce() -> R) -> (LayoutMask, R) {
57 METRICS_USED_CTX.with_context(&mut Some(Arc::new(Atomic::new(LayoutMask::empty()))), || {
58 let r = f();
59 let uses = METRICS_USED_CTX.get().load(Relaxed);
60 (uses, r)
61 })
62 }
63
64 pub fn register_metrics_use(&self, uses: LayoutMask) {
68 let ctx = METRICS_USED_CTX.get();
69 let m = ctx.load(Relaxed);
70 ctx.store(m | uses, Relaxed);
71 }
72
73 pub fn constraints(&self) -> PxConstraints2d {
75 LAYOUT_CTX.get().metrics.constraints()
76 }
77
78 pub fn z_constraints(&self) -> PxConstraints {
80 LAYOUT_CTX.get().metrics.z_constraints()
81 }
82
83 pub fn constraints_for(&self, axis: LayoutAxis) -> PxConstraints {
85 match axis {
86 LayoutAxis::X => self.constraints().x,
87 LayoutAxis::Y => self.constraints().y,
88 LayoutAxis::Z => self.z_constraints(),
89 }
90 }
91
92 pub fn with_constraints<R>(&self, constraints: PxConstraints2d, f: impl FnOnce() -> R) -> R {
94 self.with_context(self.metrics().with_constraints(constraints), f)
95 }
96
97 pub fn with_z_constraints<R>(&self, constraints: PxConstraints, f: impl FnOnce() -> R) -> R {
99 self.with_context(self.metrics().with_z_constraints(constraints), f)
100 }
101
102 pub fn with_constraints_for<R>(&self, axis: LayoutAxis, constraints: PxConstraints, f: impl FnOnce() -> R) -> R {
104 match axis {
105 LayoutAxis::X => {
106 let mut c = self.constraints();
107 c.x = constraints;
108 self.with_constraints(c, f)
109 }
110 LayoutAxis::Y => {
111 let mut c = self.constraints();
112 c.y = constraints;
113 self.with_constraints(c, f)
114 }
115 LayoutAxis::Z => self.with_z_constraints(constraints, f),
116 }
117 }
118
119 pub fn with_sub_size(&self, removed: PxSize, f: impl FnOnce() -> PxSize) -> PxSize {
121 self.with_constraints(self.constraints().with_less_size(removed), f) + removed
122 }
123
124 pub fn with_add_size(&self, added: PxSize, f: impl FnOnce() -> PxSize) -> PxSize {
126 self.with_constraints(self.constraints().with_more_size(added), f) - added
127 }
128
129 pub fn inline_constraints(&self) -> Option<InlineConstraints> {
131 LAYOUT_CTX.get().metrics.inline_constraints()
132 }
133
134 pub fn with_no_inline<R>(&self, f: impl FnOnce() -> R) -> R {
136 let metrics = self.metrics();
137 if metrics.inline_constraints().is_none() {
138 f()
139 } else {
140 self.with_context(metrics.with_inline_constraints(None), f)
141 }
142 }
143
144 pub fn root_font_size(&self) -> Px {
146 LAYOUT_CTX.get().metrics.root_font_size()
147 }
148
149 pub fn font_size(&self) -> Px {
151 LAYOUT_CTX.get().metrics.font_size()
152 }
153
154 pub fn with_font_size<R>(&self, font_size: Px, f: impl FnOnce() -> R) -> R {
156 self.with_context(self.metrics().with_font_size(font_size), f)
157 }
158
159 pub fn viewport(&self) -> PxSize {
161 LAYOUT_CTX.get().metrics.viewport()
162 }
163
164 pub fn viewport_min(&self) -> Px {
166 LAYOUT_CTX.get().metrics.viewport_min()
167 }
168
169 pub fn viewport_max(&self) -> Px {
171 LAYOUT_CTX.get().metrics.viewport_max()
172 }
173
174 pub fn viewport_for(&self, axis: LayoutAxis) -> Px {
176 let vp = self.viewport();
177 match axis {
178 LayoutAxis::X => vp.width,
179 LayoutAxis::Y => vp.height,
180 LayoutAxis::Z => Px::MAX,
181 }
182 }
183
184 pub fn with_viewport<R>(&self, viewport: PxSize, f: impl FnOnce() -> R) -> R {
186 self.with_context(self.metrics().with_viewport(viewport), f)
187 }
188
189 pub fn scale_factor(&self) -> Factor {
191 LAYOUT_CTX.get().metrics.scale_factor()
192 }
193
194 pub fn with_scale_factor<R>(&self, scale_factor: Factor, f: impl FnOnce() -> R) -> R {
196 self.with_context(self.metrics().with_scale_factor(scale_factor), f)
197 }
198
199 pub fn screen_density(&self) -> PxDensity {
201 LAYOUT_CTX.get().metrics.screen_density()
202 }
203
204 pub fn with_screen_density<R>(&self, screen_density: PxDensity, f: impl FnOnce() -> R) -> R {
206 self.with_context(self.metrics().with_screen_density(screen_density), f)
207 }
208
209 pub fn direction(&self) -> LayoutDirection {
211 LAYOUT_CTX.get().metrics.direction()
212 }
213
214 pub fn with_direction<R>(&self, direction: LayoutDirection, f: impl FnOnce() -> R) -> R {
216 self.with_context(self.metrics().with_direction(direction), f)
217 }
218
219 pub fn leftover(&self) -> euclid::Size2D<Option<Px>, ()> {
223 LAYOUT_CTX.get().metrics.leftover()
224 }
225
226 pub fn leftover_for(&self, axis: LayoutAxis) -> Option<Px> {
228 let l = self.leftover();
229
230 match axis {
231 LayoutAxis::X => l.width,
232 LayoutAxis::Y => l.height,
233 LayoutAxis::Z => None,
234 }
235 }
236
237 pub fn with_leftover<R>(&self, width: Option<Px>, height: Option<Px>, f: impl FnOnce() -> R) -> R {
241 self.with_context(self.metrics().with_leftover(width, height), f)
242 }
243}
244
245context_local! {
246 static LAYOUT_CTX: LayoutCtx = LayoutCtx::no_context();
247 static LAYOUT_PASS_CTX: LayoutPassId = LayoutPassId::new();
248 static METRICS_USED_CTX: Atomic<LayoutMask> = Atomic::new(LayoutMask::empty());
249}
250
251struct LayoutCtx {
252 metrics: LayoutMetrics,
253}
254impl LayoutCtx {
255 fn no_context() -> Self {
256 panic!("no layout context")
257 }
258}
259
260#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
264pub struct LayoutPassId(u32);
265impl LayoutPassId {
266 pub const fn new() -> Self {
268 LayoutPassId(0)
269 }
270
271 pub const fn next(self) -> LayoutPassId {
273 LayoutPassId(self.0.wrapping_add(1))
274 }
275}
276
277#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
281#[non_exhaustive]
282pub struct InlineConstraintsMeasure {
283 pub first_max: Px,
285 pub mid_clear_min: Px,
290}
291impl InlineConstraintsMeasure {
292 pub fn new(first_max: Px, mid_clear_min: Px) -> Self {
294 Self { first_max, mid_clear_min }
295 }
296}
297
298#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
304#[non_exhaustive]
305pub struct InlineSegmentPos {
306 pub x: f32,
308}
309impl InlineSegmentPos {
310 pub fn new(x: f32) -> Self {
312 Self { x }
313 }
314}
315impl PartialEq for InlineSegmentPos {
316 fn eq(&self, other: &Self) -> bool {
317 about_eq(self.x, other.x, 0.001)
318 }
319}
320impl Eq for InlineSegmentPos {}
321impl std::hash::Hash for InlineSegmentPos {
322 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
323 about_eq_hash(self.x, 0.001, state);
324 }
325}
326impl PartialOrd for InlineSegmentPos {
327 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
328 Some(self.cmp(other))
329 }
330}
331impl Ord for InlineSegmentPos {
332 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
333 about_eq_ord(self.x, other.x, 0.001)
334 }
335}
336
337#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
341#[non_exhaustive]
342pub struct InlineConstraintsLayout {
343 pub first: PxRect,
345 pub mid_clear: Px,
347 pub last: PxRect,
349
350 pub first_segs: Arc<Vec<InlineSegmentPos>>,
352 pub last_segs: Arc<Vec<InlineSegmentPos>>,
354}
355
356impl InlineConstraintsLayout {
357 pub fn new(
359 first: PxRect,
360 mid_clear: Px,
361 last: PxRect,
362 first_segs: Arc<Vec<InlineSegmentPos>>,
363 last_segs: Arc<Vec<InlineSegmentPos>>,
364 ) -> Self {
365 Self {
366 first,
367 mid_clear,
368 last,
369 first_segs,
370 last_segs,
371 }
372 }
373}
374
375#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
377pub enum InlineConstraints {
378 Measure(InlineConstraintsMeasure),
380 Layout(InlineConstraintsLayout),
382}
383impl InlineConstraints {
384 pub fn measure(self) -> InlineConstraintsMeasure {
386 match self {
387 InlineConstraints::Measure(m) => m,
388 InlineConstraints::Layout(l) => InlineConstraintsMeasure {
389 first_max: l.first.width(),
390 mid_clear_min: l.mid_clear,
391 },
392 }
393 }
394
395 pub fn layout(self) -> InlineConstraintsLayout {
397 match self {
398 InlineConstraints::Layout(m) => m,
399 InlineConstraints::Measure(_) => Default::default(),
400 }
401 }
402}
403
404#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
408#[non_exhaustive]
409pub struct LayoutMetricsSnapshot {
410 pub constraints: PxConstraints2d,
414
415 pub inline_constraints: Option<InlineConstraints>,
419
420 pub z_constraints: PxConstraints,
424
425 pub font_size: Px,
429 pub root_font_size: Px,
433 pub scale_factor: Factor,
437 pub viewport: PxSize,
441 pub screen_density: PxDensity,
445
446 pub direction: LayoutDirection,
450
451 pub leftover: euclid::Size2D<Option<Px>, ()>,
455}
456impl LayoutMetricsSnapshot {
457 pub fn masked_eq(&self, other: &Self, mask: LayoutMask) -> bool {
459 (!mask.contains(LayoutMask::CONSTRAINTS) || self.constraints == other.constraints)
460 && (!mask.contains(LayoutMask::Z_CONSTRAINTS) || self.z_constraints == other.z_constraints)
461 && (!mask.contains(LayoutMask::INLINE_CONSTRAINTS) || self.inline_constraints == other.inline_constraints)
462 && (!mask.contains(LayoutMask::FONT_SIZE) || self.font_size == other.font_size)
463 && (!mask.contains(LayoutMask::ROOT_FONT_SIZE) || self.root_font_size == other.root_font_size)
464 && (!mask.contains(LayoutMask::SCALE_FACTOR) || self.scale_factor == other.scale_factor)
465 && (!mask.contains(LayoutMask::VIEWPORT) || self.viewport == other.viewport)
466 && (!mask.contains(LayoutMask::SCREEN_DENSITY) || self.screen_density == other.screen_density)
467 && (!mask.contains(LayoutMask::DIRECTION) || self.direction == other.direction)
468 && (!mask.contains(LayoutMask::LEFTOVER) || self.leftover == other.leftover)
469 }
470}
471impl PartialEq for LayoutMetricsSnapshot {
472 fn eq(&self, other: &Self) -> bool {
473 self.constraints == other.constraints
474 && self.z_constraints == other.z_constraints
475 && self.inline_constraints == other.inline_constraints
476 && self.font_size == other.font_size
477 && self.root_font_size == other.root_font_size
478 && self.scale_factor == other.scale_factor
479 && self.viewport == other.viewport
480 && self.screen_density == other.screen_density
481 && self.direction == other.direction
482 && self.leftover == other.leftover
483 }
484}
485impl std::hash::Hash for LayoutMetricsSnapshot {
486 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
487 self.constraints.hash(state);
488 self.inline_constraints.hash(state);
489 self.font_size.hash(state);
490 self.root_font_size.hash(state);
491 self.scale_factor.hash(state);
492 self.viewport.hash(state);
493 self.screen_density.hash(state);
494 self.direction.hash(state);
495 self.leftover.hash(state);
496 }
497}
498
499#[derive(Debug, Clone)]
501pub struct LayoutMetrics {
502 s: LayoutMetricsSnapshot,
503}
504impl LayoutMetrics {
505 pub fn new(scale_factor: Factor, viewport: PxSize, font_size: Px) -> Self {
512 LayoutMetrics {
513 s: LayoutMetricsSnapshot {
514 constraints: PxConstraints2d::new_fill_size(viewport),
515 z_constraints: PxConstraints::new_unbounded().with_min(Px(1)),
516 inline_constraints: None,
517 font_size,
518 root_font_size: font_size,
519 scale_factor,
520 viewport,
521 screen_density: PxDensity::default(),
522 direction: LayoutDirection::default(),
523 leftover: euclid::size2(None, None),
524 },
525 }
526 }
527
528 pub fn constraints(&self) -> PxConstraints2d {
530 LAYOUT.register_metrics_use(LayoutMask::CONSTRAINTS);
531 self.s.constraints
532 }
533
534 pub fn z_constraints(&self) -> PxConstraints {
536 LAYOUT.register_metrics_use(LayoutMask::Z_CONSTRAINTS);
537 self.s.z_constraints
538 }
539
540 pub fn inline_constraints(&self) -> Option<InlineConstraints> {
544 LAYOUT.register_metrics_use(LayoutMask::INLINE_CONSTRAINTS);
545 self.s.inline_constraints.clone()
546 }
547
548 pub fn direction(&self) -> LayoutDirection {
550 LAYOUT.register_metrics_use(LayoutMask::DIRECTION);
551 self.s.direction
552 }
553
554 pub fn font_size(&self) -> Px {
556 LAYOUT.register_metrics_use(LayoutMask::FONT_SIZE);
557 self.s.font_size
558 }
559
560 pub fn root_font_size(&self) -> Px {
562 LAYOUT.register_metrics_use(LayoutMask::ROOT_FONT_SIZE);
563 self.s.root_font_size
564 }
565
566 pub fn scale_factor(&self) -> Factor {
568 LAYOUT.register_metrics_use(LayoutMask::SCALE_FACTOR);
569 self.s.scale_factor
570 }
571
572 pub fn viewport(&self) -> PxSize {
577 LAYOUT.register_metrics_use(LayoutMask::VIEWPORT);
578 self.s.viewport
579 }
580
581 pub fn viewport_min(&self) -> Px {
585 self.s.viewport.width.min(self.s.viewport.height)
586 }
587
588 pub fn viewport_max(&self) -> Px {
592 self.s.viewport.width.max(self.s.viewport.height)
593 }
594
595 pub fn screen_density(&self) -> PxDensity {
599 LAYOUT.register_metrics_use(LayoutMask::SCREEN_DENSITY);
600 self.s.screen_density
601 }
602
603 pub fn leftover(&self) -> euclid::Size2D<Option<Px>, ()> {
607 LAYOUT.register_metrics_use(LayoutMask::LEFTOVER);
608 self.s.leftover
609 }
610
611 pub fn with_constraints(mut self, constraints: PxConstraints2d) -> Self {
615 self.s.constraints = constraints;
616 self
617 }
618
619 pub fn with_z_constraints(mut self, constraints: PxConstraints) -> Self {
623 self.s.z_constraints = constraints;
624 self
625 }
626
627 pub fn with_inline_constraints(mut self, inline_constraints: Option<InlineConstraints>) -> Self {
631 self.s.inline_constraints = inline_constraints;
632 self
633 }
634
635 pub fn with_font_size(mut self, font_size: Px) -> Self {
639 self.s.font_size = font_size;
640 self
641 }
642
643 pub fn with_viewport(mut self, viewport: PxSize) -> Self {
647 self.s.viewport = viewport;
648 self
649 }
650
651 pub fn with_scale_factor(mut self, scale_factor: Factor) -> Self {
655 self.s.scale_factor = scale_factor;
656 self
657 }
658
659 pub fn with_screen_density(mut self, screen_density: PxDensity) -> Self {
663 self.s.screen_density = screen_density;
664 self
665 }
666
667 pub fn with_direction(mut self, direction: LayoutDirection) -> Self {
671 self.s.direction = direction;
672 self
673 }
674
675 pub fn with_leftover(mut self, width: Option<Px>, height: Option<Px>) -> Self {
679 self.s.leftover = euclid::size2(width, height);
680 self
681 }
682
683 pub fn snapshot(&self) -> LayoutMetricsSnapshot {
687 self.s.clone()
688 }
689}
690
691context_var! {
692 pub static DIRECTION_VAR: LayoutDirection = LayoutDirection::LTR;
694}
695
696#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
708pub enum LayoutDirection {
709 LTR,
711 RTL,
713}
714impl LayoutDirection {
715 pub fn is_ltr(self) -> bool {
717 matches!(self, Self::LTR)
718 }
719
720 pub fn is_rtl(self) -> bool {
722 matches!(self, Self::RTL)
723 }
724}
725impl Default for LayoutDirection {
726 fn default() -> Self {
728 Self::LTR
729 }
730}
731impl fmt::Debug for LayoutDirection {
732 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
733 if f.alternate() {
734 write!(f, "LayoutDirection::")?;
735 }
736 match self {
737 Self::LTR => write!(f, "LTR"),
738 Self::RTL => write!(f, "RTL"),
739 }
740 }
741}
742
743#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
747#[non_exhaustive]
748pub struct InlineSegment {
749 pub width: f32,
751 pub kind: TextSegmentKind,
753}
754
755impl InlineSegment {
756 pub fn new(width: f32, kind: TextSegmentKind) -> Self {
758 Self { width, kind }
759 }
760}
761impl PartialEq for InlineSegment {
762 fn eq(&self, other: &Self) -> bool {
763 about_eq(self.width, other.width, 0.001) && self.kind == other.kind
764 }
765}
766impl Eq for InlineSegment {}
767impl std::hash::Hash for InlineSegment {
768 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
769 about_eq_hash(self.width, 0.001, state);
770 self.kind.hash(state);
771 }
772}
773
774#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
776pub enum TextSegmentKind {
777 LeftToRight,
779 RightToLeft,
781 ArabicLetter,
783
784 EuropeanNumber,
786 EuropeanSeparator,
788 EuropeanTerminator,
790 ArabicNumber,
792 CommonSeparator,
794 NonSpacingMark,
799 BoundaryNeutral,
801
802 Emoji,
804
805 LineBreak,
807 Tab,
809 Space,
811 OtherNeutral,
813 Bracket(char),
817
818 BidiCtrl(char),
833}
834impl TextSegmentKind {
835 pub fn is_word(self) -> bool {
837 use TextSegmentKind::*;
838 matches!(
839 self,
840 LeftToRight
841 | RightToLeft
842 | ArabicLetter
843 | EuropeanNumber
844 | EuropeanSeparator
845 | EuropeanTerminator
846 | ArabicNumber
847 | CommonSeparator
848 | NonSpacingMark
849 | BoundaryNeutral
850 | OtherNeutral
851 | Bracket(_)
852 | Emoji
853 )
854 }
855
856 pub fn is_space(self) -> bool {
858 matches!(self, Self::Space | Self::Tab)
859 }
860
861 pub fn is_line_break(self) -> bool {
865 matches!(self, Self::LineBreak)
866 }
867
868 pub fn can_merge_bidi(self) -> bool {
870 use TextSegmentKind::*;
871 !matches!(self, Bracket(_) | BidiCtrl(_))
872 }
873
874 pub fn bracket_info(self) -> Option<unicode_bidi::data_source::BidiMatchedOpeningBracket> {
876 if let TextSegmentKind::Bracket(c) = self {
877 unicode_bidi::HardcodedBidiData.bidi_matched_opening_bracket(c)
878 } else {
879 None
880 }
881 }
882
883 pub fn strong_direction(self) -> Option<LayoutDirection> {
887 use TextSegmentKind::*;
888
889 match self {
890 LeftToRight => Some(LayoutDirection::LTR),
891 RightToLeft | ArabicLetter => Some(LayoutDirection::RTL),
892 BidiCtrl(_) => {
893 use unicode_bidi::BidiClass::*;
894 match unicode_bidi::BidiClass::from(self) {
895 LRE | LRO | LRI => Some(LayoutDirection::LTR),
896 RLE | RLO | RLI => Some(LayoutDirection::RTL),
897 _ => None,
898 }
899 }
900 _ => None,
901 }
902 }
903}
904impl From<char> for TextSegmentKind {
905 fn from(c: char) -> Self {
906 use unicode_bidi::*;
907
908 unicode_bidi::HardcodedBidiData.bidi_class(c).into()
909 }
910}
911
912impl From<unicode_bidi::BidiClass> for TextSegmentKind {
913 fn from(value: unicode_bidi::BidiClass) -> Self {
914 use TextSegmentKind::*;
915 use unicode_bidi::BidiClass::*;
916
917 match value {
918 WS => Space,
919 L => LeftToRight,
920 R => RightToLeft,
921 AL => ArabicLetter,
922 AN => ArabicNumber,
923 CS => CommonSeparator,
924 B => LineBreak,
925 EN => EuropeanNumber,
926 ES => EuropeanSeparator,
927 ET => EuropeanTerminator,
928 S => Tab,
929 ON => OtherNeutral,
930 BN => BoundaryNeutral,
931 NSM => NonSpacingMark,
932 RLE => BidiCtrl('\u{202B}'),
933 LRI => BidiCtrl('\u{2066}'),
934 RLI => BidiCtrl('\u{2067}'),
935 LRO => BidiCtrl('\u{202D}'),
936 FSI => BidiCtrl('\u{2068}'),
937 PDF => BidiCtrl('\u{202C}'),
938 LRE => BidiCtrl('\u{202A}'),
939 PDI => BidiCtrl('\u{2069}'),
940 RLO => BidiCtrl('\u{202E}'),
941 }
942 }
943}
944impl From<TextSegmentKind> for unicode_bidi::BidiClass {
945 fn from(value: TextSegmentKind) -> Self {
946 use TextSegmentKind::*;
947 use unicode_bidi::BidiClass::*;
948
949 match value {
950 Space => WS,
951 LeftToRight => L,
952 RightToLeft => R,
953 ArabicLetter => AL,
954 ArabicNumber => AN,
955 CommonSeparator => CS,
956 LineBreak => B,
957 EuropeanNumber => EN,
958 EuropeanSeparator => ES,
959 EuropeanTerminator => ET,
960 Tab => S,
961 OtherNeutral | Emoji | Bracket(_) => ON,
962 BoundaryNeutral => BN,
963 NonSpacingMark => NSM,
964 BidiCtrl(c) => match c {
965 '\u{202A}' => LRE,
966 '\u{202D}' => LRO,
967 '\u{202B}' => RLE,
968 '\u{202E}' => RLO,
969 '\u{202C}' => PDF,
970 '\u{2066}' => LRI,
971 '\u{2067}' => RLI,
972 '\u{2068}' => FSI,
973 '\u{2069}' => PDI,
974 _c => {
975 #[cfg(debug_assertions)]
976 {
977 tracing::error!("invalid bidi ctrl char '{_c}'");
978 }
979 ON
980 }
981 },
982 }
983 }
984}
985
986bitflags! {
987 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, bytemuck::NoUninit)]
989 #[repr(transparent)]
990 pub struct LayoutMask: u32 {
991 const DEFAULT_VALUE = 1 << 31;
993 const CONSTRAINTS = 1 << 30;
995 const Z_CONSTRAINTS = 1 << 29;
997 const INLINE_CONSTRAINTS = 1 << 28;
999
1000 const FONT_SIZE = 1;
1002 const ROOT_FONT_SIZE = 1 << 1;
1004 const SCALE_FACTOR = 1 << 2;
1006 const VIEWPORT = 1 << 3;
1008 const SCREEN_DENSITY = 1 << 4;
1010 const DIRECTION = 1 << 5;
1012 const LEFTOVER = 1 << 6;
1014 }
1015}
1016impl Default for LayoutMask {
1017 fn default() -> Self {
1019 LayoutMask::empty()
1020 }
1021}