zng_layout/
context.rs

1//! Layout context.
2
3use 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
15/// Current layout context.
16///
17/// Only available in measure and layout methods.
18pub struct LAYOUT;
19impl LAYOUT {
20    /// Gets the current window layout pass.
21    ///
22    /// Widgets can be layout more then once per window layout pass, you can use this ID to identify such cases.
23    pub fn pass_id(&self) -> LayoutPassId {
24        LAYOUT_PASS_CTX.get_clone()
25    }
26
27    /// Calls `f` in a new layout pass.
28    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    /// Calls `f` in a new layout context.
34    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    /// Calls `f` without a layout context.
40    pub fn with_no_context<R>(&self, f: impl FnOnce() -> R) -> R {
41        LAYOUT_CTX.with_default(f)
42    }
43
44    /// Gets the context metrics.
45    pub fn metrics(&self) -> LayoutMetrics {
46        LAYOUT_CTX.get().metrics.clone()
47    }
48
49    /// Capture all layout metrics used in `f`.
50    ///
51    /// Note that the captured mask is not propagated to the current context, you can use [`register_metrics_use`] to propagate
52    /// the returned mask.
53    ///
54    /// [`register_metrics_use`]: Self::register_metrics_use
55    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    /// Register that the node layout depends on these contextual values.
64    ///
65    /// Note that the value methods already register by the [`LayoutMetrics`] getter methods.
66    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    /// Current size constraints.
73    pub fn constraints(&self) -> PxConstraints2d {
74        LAYOUT_CTX.get().metrics.constraints()
75    }
76
77    /// Current perspective constraints.
78    pub fn z_constraints(&self) -> PxConstraints {
79        LAYOUT_CTX.get().metrics.z_constraints()
80    }
81
82    /// Current length constraints for the given axis.
83    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    /// Calls `f` with the `constraints` in context.
92    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    /// Calls `f` with the `constraints` for perspective in context.
97    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    /// Calls `f` with the `constraints` in context.
102    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    /// Runs a function `f` in a context that has its max size subtracted by `removed` and its final size added by `removed`.
119    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    /// Runs a function `f` in a layout context that has its max size added by `added` and its final size subtracted by `added`.
124    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    /// Current inline constraints.
129    pub fn inline_constraints(&self) -> Option<InlineConstraints> {
130        LAYOUT_CTX.get().metrics.inline_constraints()
131    }
132
133    /// Calls `f` with no inline constraints.
134    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    /// Root font size.
144    pub fn root_font_size(&self) -> Px {
145        LAYOUT_CTX.get().metrics.root_font_size()
146    }
147
148    /// Current font size.
149    pub fn font_size(&self) -> Px {
150        LAYOUT_CTX.get().metrics.font_size()
151    }
152
153    /// Calls `f` with `font_size` in the context.
154    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    /// Current viewport size.
159    pub fn viewport(&self) -> PxSize {
160        LAYOUT_CTX.get().metrics.viewport()
161    }
162
163    /// Current smallest dimension of the viewport.
164    pub fn viewport_min(&self) -> Px {
165        LAYOUT_CTX.get().metrics.viewport_min()
166    }
167
168    /// Current largest dimension of the viewport.
169    pub fn viewport_max(&self) -> Px {
170        LAYOUT_CTX.get().metrics.viewport_max()
171    }
172
173    /// Current viewport length for the given axis.
174    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    /// Calls `f` with `viewport` in the context.
184    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    /// Current scale factor.
189    pub fn scale_factor(&self) -> Factor {
190        LAYOUT_CTX.get().metrics.scale_factor()
191    }
192
193    /// Calls `f` with `scale_factor` in the context.
194    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    /// Current screen PPI.
199    pub fn screen_ppi(&self) -> Ppi {
200        LAYOUT_CTX.get().metrics.screen_ppi()
201    }
202
203    /// Calls `f` with `screen_ppi` in the context.
204    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    /// Current layout direction.
209    pub fn direction(&self) -> LayoutDirection {
210        LAYOUT_CTX.get().metrics.direction()
211    }
212
213    /// Calls `f` with `direction` in the context.
214    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    /// Context leftover length for the widget, given the [`Length::Leftover`] value it communicated to the parent.
219    ///
220    /// [`Length::Leftover`]: crate::unit::Length::Leftover
221    pub fn leftover(&self) -> euclid::Size2D<Option<Px>, ()> {
222        LAYOUT_CTX.get().metrics.leftover()
223    }
224
225    /// Context leftover length for the given axis.
226    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    /// Calls `f` with [`leftover`] set to `with` and `height`.
237    ///
238    /// [`leftover`]: Self::leftover
239    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/// Identifies the layout pass of a window.
260///
261/// This value is different for each window layout, but the same for children of panels that do more then one layout pass.
262#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
263pub struct LayoutPassId(u32);
264impl LayoutPassId {
265    /// New default.
266    pub const fn new() -> Self {
267        LayoutPassId(0)
268    }
269
270    /// Gets the next layout pass ID.
271    pub const fn next(self) -> LayoutPassId {
272        LayoutPassId(self.0.wrapping_add(1))
273    }
274}
275
276/// Constraints for inline measure.
277///
278/// See [`InlineConstraints`] for more details.
279#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
280pub struct InlineConstraintsMeasure {
281    /// Available space on the first row.
282    pub first_max: Px,
283    /// Current height of the row in the parent. If the widget wraps and defines the first
284    /// row in *this* parent's row, the `mid_clear` value will be the extra space needed to clear
285    /// this minimum or zero if the first row is taller. The widget must use this value to estimate the `mid_clear`
286    /// value and include it in the overall measured height of the widget.
287    pub mid_clear_min: Px,
288}
289
290/// Position of an inline segment set by the inlining parent.
291///
292/// See [`InlineConstraintsLayout::first_segs`] for more details.
293///
294/// [`InlineConstraintsLayout::first_segs`]: crate::context::InlineConstraintsLayout::first_segs
295#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
296pub struct InlineSegmentPos {
297    /// Seg offset to the right from the row origin, in pixels.
298    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/// Constraints for inline layout.
313///
314/// See [`InlineConstraints`] for more details.
315#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, serde::Serialize, serde::Deserialize)]
316pub struct InlineConstraintsLayout {
317    /// First row rect, defined by the parent.
318    pub first: PxRect,
319    /// Extra space in-between the first row and the mid-rows that must be offset to clear the other segments in the row.
320    pub mid_clear: Px,
321    /// Last row rect, defined by the parent.
322    pub last: PxRect,
323
324    /// Position of inline segments of the first row.
325    pub first_segs: Arc<Vec<InlineSegmentPos>>,
326    /// Position of inline segments of the last row.
327    pub last_segs: Arc<Vec<InlineSegmentPos>>,
328}
329
330/// Constraints for inline measure or layout.
331#[derive(Clone, Debug, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
332pub enum InlineConstraints {
333    /// Constraints for the measure pass.
334    Measure(InlineConstraintsMeasure),
335    /// Constraints the layout pass.
336    Layout(InlineConstraintsLayout),
337}
338impl InlineConstraints {
339    /// Get the `Measure` data or default.
340    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    /// Get the `Layout` data or default.
351    pub fn layout(self) -> InlineConstraintsLayout {
352        match self {
353            InlineConstraints::Layout(m) => m,
354            InlineConstraints::Measure(_) => Default::default(),
355        }
356    }
357}
358
359/// Layout metrics snapshot.
360///
361/// A snapshot can be taken using the [`LayoutMetrics::snapshot`].
362#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
363pub struct LayoutMetricsSnapshot {
364    /// The [`constraints`].
365    ///
366    /// [`constraints`]: LayoutMetrics::constraints
367    pub constraints: PxConstraints2d,
368
369    /// The [`inline_constraints`].
370    ///
371    /// [`inline_constraints`]: LayoutMetrics::inline_constraints
372    pub inline_constraints: Option<InlineConstraints>,
373
374    /// The [`z_constraints`].
375    ///
376    /// [`z_constraints`]: LayoutMetrics::z_constraints
377    pub z_constraints: PxConstraints,
378
379    /// The [`font_size`].
380    ///
381    /// [`font_size`]: LayoutMetrics::font_size
382    pub font_size: Px,
383    /// The [`root_font_size`].
384    ///
385    /// [`root_font_size`]: LayoutMetrics::root_font_size
386    pub root_font_size: Px,
387    /// The [`scale_factor`].
388    ///
389    /// [`scale_factor`]: LayoutMetrics::scale_factor
390    pub scale_factor: Factor,
391    /// The [`viewport`].
392    ///
393    /// [`viewport`]: LayoutMetrics::viewport
394    pub viewport: PxSize,
395    /// The [`screen_ppi`].
396    ///
397    /// [`screen_ppi`]: LayoutMetrics::screen_ppi
398    pub screen_ppi: Ppi,
399
400    /// The [`direction`].
401    ///
402    /// [`direction`]: LayoutMetrics::direction
403    pub direction: LayoutDirection,
404
405    /// The [`leftover`].
406    ///
407    /// [`leftover`]: LayoutMetrics::leftover
408    pub leftover: euclid::Size2D<Option<Px>, ()>,
409}
410impl LayoutMetricsSnapshot {
411    /// Gets if all of the fields in `mask` are equal between `self` and `other`.
412    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/// Layout metrics in a [`LAYOUT`] context.
451#[derive(Debug, Clone)]
452pub struct LayoutMetrics {
453    s: LayoutMetricsSnapshot,
454}
455impl LayoutMetrics {
456    /// New root [`LayoutMetrics`].
457    ///
458    /// The `font_size` sets both font sizes, the initial PPI is `96.0`, you can use the builder style method and
459    /// [`with_screen_ppi`] to set a different value.
460    ///
461    /// [`with_screen_ppi`]: LayoutMetrics::with_screen_ppi
462    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    /// Current size constraints.
480    pub fn constraints(&self) -> PxConstraints2d {
481        LAYOUT.register_metrics_use(LayoutMask::CONSTRAINTS);
482        self.s.constraints
483    }
484
485    /// Current perspective constraints.
486    pub fn z_constraints(&self) -> PxConstraints {
487        LAYOUT.register_metrics_use(LayoutMask::CONSTRAINTS);
488        self.s.z_constraints
489    }
490
491    /// Current inline constraints.
492    ///
493    /// Only present if the parent widget supports inline.
494    pub fn inline_constraints(&self) -> Option<InlineConstraints> {
495        LAYOUT.register_metrics_use(LayoutMask::CONSTRAINTS);
496        self.s.inline_constraints.clone()
497    }
498
499    /// Gets the inline or text flow direction.
500    pub fn direction(&self) -> LayoutDirection {
501        LAYOUT.register_metrics_use(LayoutMask::DIRECTION);
502        self.s.direction
503    }
504
505    /// Current computed font size.
506    pub fn font_size(&self) -> Px {
507        LAYOUT.register_metrics_use(LayoutMask::FONT_SIZE);
508        self.s.font_size
509    }
510
511    /// Computed font size at the root widget.
512    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    /// Pixel scale factor.
518    pub fn scale_factor(&self) -> Factor {
519        LAYOUT.register_metrics_use(LayoutMask::SCALE_FACTOR);
520        self.s.scale_factor
521    }
522
523    /// Computed size of the nearest viewport ancestor.
524    ///
525    /// This is usually the window content area size, but can be the scroll viewport size or any other
526    /// value depending on the implementation of the context widgets.
527    pub fn viewport(&self) -> PxSize {
528        LAYOUT.register_metrics_use(LayoutMask::VIEWPORT);
529        self.s.viewport
530    }
531
532    /// Smallest dimension of the [`viewport`].
533    ///
534    /// [`viewport`]: Self::viewport
535    pub fn viewport_min(&self) -> Px {
536        self.s.viewport.width.min(self.s.viewport.height)
537    }
538
539    /// Largest dimension of the [`viewport`].
540    ///
541    /// [`viewport`]: Self::viewport
542    pub fn viewport_max(&self) -> Px {
543        self.s.viewport.width.max(self.s.viewport.height)
544    }
545
546    /// The current screen "pixels-per-inch" resolution.
547    ///
548    /// This value is dependent in the actual physical size of the screen.
549    ///
550    /// Default is `96.0`.
551    pub fn screen_ppi(&self) -> Ppi {
552        self.s.screen_ppi
553    }
554
555    /// Computed leftover length for the widget, given the [`Length::Leftover`] value it communicated to the parent.
556    ///
557    /// [`Length::Leftover`]: crate::unit::Length::Leftover
558    pub fn leftover(&self) -> euclid::Size2D<Option<Px>, ()> {
559        LAYOUT.register_metrics_use(LayoutMask::LEFTOVER);
560        self.s.leftover
561    }
562
563    /// Sets the [`constraints`] to `constraints`.
564    ///
565    /// [`constraints`]: Self::constraints
566    pub fn with_constraints(mut self, constraints: PxConstraints2d) -> Self {
567        self.s.constraints = constraints;
568        self
569    }
570
571    /// Sets the [`z_constraints`] to `constraints`.
572    ///
573    /// [`z_constraints`]: Self::z_constraints
574    pub fn with_z_constraints(mut self, constraints: PxConstraints) -> Self {
575        self.s.z_constraints = constraints;
576        self
577    }
578
579    /// Set the [`inline_constraints`].
580    ///
581    /// [`inline_constraints`]: Self::inline_constraints
582    pub fn with_inline_constraints(mut self, inline_constraints: Option<InlineConstraints>) -> Self {
583        self.s.inline_constraints = inline_constraints;
584        self
585    }
586
587    /// Sets the [`font_size`].
588    ///
589    /// [`font_size`]: Self::font_size
590    pub fn with_font_size(mut self, font_size: Px) -> Self {
591        self.s.font_size = font_size;
592        self
593    }
594
595    /// Sets the [`viewport`].
596    ///
597    /// [`viewport`]: Self::viewport
598    pub fn with_viewport(mut self, viewport: PxSize) -> Self {
599        self.s.viewport = viewport;
600        self
601    }
602
603    /// Sets the [`scale_factor`].
604    ///
605    /// [`scale_factor`]: Self::scale_factor
606    pub fn with_scale_factor(mut self, scale_factor: Factor) -> Self {
607        self.s.scale_factor = scale_factor;
608        self
609    }
610
611    /// Sets the [`screen_ppi`].
612    ///
613    /// [`screen_ppi`]: Self::screen_ppi
614    pub fn with_screen_ppi(mut self, screen_ppi: Ppi) -> Self {
615        self.s.screen_ppi = screen_ppi;
616        self
617    }
618
619    /// Sets the [`direction`].
620    ///
621    /// [`direction`]: Self::direction
622    pub fn with_direction(mut self, direction: LayoutDirection) -> Self {
623        self.s.direction = direction;
624        self
625    }
626
627    /// Sets the [`leftover`].
628    ///
629    /// [`leftover`]: Self::leftover
630    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    /// Clones all current metrics into a [snapshot].
636    ///
637    /// [snapshot]: LayoutMetricsSnapshot
638    pub fn snapshot(&self) -> LayoutMetricsSnapshot {
639        self.s.clone()
640    }
641}
642
643context_var! {
644    /// Wrap direction of text in a widget context.
645    pub static DIRECTION_VAR: LayoutDirection = LayoutDirection::LTR;
646}
647
648/// Defines the layout flow direction.
649///
650/// This affects inline layout, some [`Align`] options and the base text shaping direction.
651///
652/// The contextual value can be read during layout in [`LayoutMetrics::direction`], and it can be set using [`LayoutMetrics::with_direction`].
653/// Properties that define a more specific *direction* value also set this value, for example, a *TextDirection* property will also set the
654/// layout direction.
655///
656/// Note that this does not affect the layout origin, all points are offsets from the top-left corner independent of this value.
657///
658/// [`Align`]: crate::unit::Align
659#[derive(Clone, Copy, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
660pub enum LayoutDirection {
661    /// left-to-right.
662    LTR,
663    /// Right-to-left.
664    RTL,
665}
666impl LayoutDirection {
667    /// Matches `LTR`.
668    pub fn is_ltr(self) -> bool {
669        matches!(self, Self::LTR)
670    }
671
672    /// Matches `RTL`.
673    pub fn is_rtl(self) -> bool {
674        matches!(self, Self::RTL)
675    }
676}
677impl Default for LayoutDirection {
678    /// Default is `LTR`.
679    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/// Represents a segment in an inlined widget first or last row.
696///
697/// This info is used by inlining parent to sort the joiner row in a way that preserves bidirectional text flow.
698#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
699pub struct InlineSegment {
700    /// Width of the segment, in pixels.
701    pub width: f32,
702    /// Info for bidirectional reorder.
703    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/// The type of an inline/text segment.
719#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
720pub enum TextSegmentKind {
721    /// Any strong left-to-right character.
722    LeftToRight,
723    /// Any strong right-to-left (non-Arabic-type) character.
724    RightToLeft,
725    /// Any strong right-to-left (Arabic-type) character.
726    ArabicLetter,
727
728    /// Any ASCII digit or Eastern Arabic-Indic digit.
729    EuropeanNumber,
730    /// Plus and minus signs.
731    EuropeanSeparator,
732    /// A terminator in a numeric format context, includes currency signs.
733    EuropeanTerminator,
734    /// Any Arabic-Indic digit.
735    ArabicNumber,
736    /// Commas, colons, and slashes.
737    CommonSeparator,
738    /// Any non-spacing mark.
739    NonSpacingMark,
740    /// Most format characters, control codes, or non-characters.
741    BoundaryNeutral,
742
743    /// Emoji chars, components and zero-width-joiner between emoji.
744    Emoji,
745
746    /// Various newline characters.
747    LineBreak,
748    /// A sequence of `'\t', '\v'` or `'\u{1F}'`.
749    Tab,
750    /// Spaces.
751    Space,
752    /// Most other symbols and punctuation marks.
753    OtherNeutral,
754    /// Open or close bidi bracket.
755    ///
756    /// Can be any chars in <https://unicode.org/Public/UNIDATA/BidiBrackets.txt>.
757    Bracket(char),
758
759    /// Bidi control character.
760    ///
761    /// Chars can be:
762    ///
763    /// * `\u{202A}`: The LR embedding control.
764    /// * `\u{202D}`: The LR override control.
765    /// * `\u{202B}`: The RL embedding control.
766    /// * `\u{202E}`: The RL override control.
767    /// * `\u{202C}`: Terminates an embedding or override control.
768    ///
769    /// * `\u{2066}`: The LR isolate control.
770    /// * `\u{2067}`: The RL isolate control.
771    /// * `\u{2068}`: The first strong isolate control.
772    /// * `\u{2069}`: Terminates an isolate control.
773    BidiCtrl(char),
774}
775impl TextSegmentKind {
776    /// Returns `true` if the segment can be considered part of a word for the purpose of inserting letter spacing.
777    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    /// Returns `true` if the segment can be considered part of space between words for the purpose of inserting word spacing.
798    pub fn is_space(self) -> bool {
799        matches!(self, Self::Space | Self::Tab)
800    }
801
802    /// Returns `true` if the segment terminates the current line.
803    ///
804    /// Line break segments are the last segment of their line and explicitly start a new line.
805    pub fn is_line_break(self) -> bool {
806        matches!(self, Self::LineBreak)
807    }
808
809    /// If multiple segments of this same kind can be represented by a single segment in the Unicode bidi algorithm.
810    pub fn can_merge(self) -> bool {
811        use TextSegmentKind::*;
812        !matches!(self, Bracket(_) | BidiCtrl(_))
813    }
814
815    /// Get more info about the bracket char if `self` is `Bracket(_)` with a valid char.
816    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    /// Gets the layout direction this segment will always be in, independent of the base direction.
825    ///
826    /// Returns `None` if the segment direction depends on the line context.
827    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    /// Mask of values that can affect the layout operation of a value.
929    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, bytemuck::NoUninit)]
930    #[repr(transparent)]
931    pub struct LayoutMask: u32 {
932        /// The `default_value`.
933        const DEFAULT_VALUE = 1 << 31;
934        /// The [`LayoutMetrics::constraints`], [`LayoutMetrics::z_constraints`] and [`LayoutMetrics::inline_constraints`].
935        const CONSTRAINTS = 1 << 30;
936
937        /// The [`LayoutMetrics::font_size`].
938        const FONT_SIZE = 1;
939        /// The [`LayoutMetrics::root_font_size`].
940        const ROOT_FONT_SIZE = 1 << 1;
941        /// The [`LayoutMetrics::scale_factor`].
942        const SCALE_FACTOR = 1 << 2;
943        /// The [`LayoutMetrics::viewport`].
944        const VIEWPORT = 1 << 3;
945        /// The [`LayoutMetrics::screen_ppi`].
946        const SCREEN_PPI = 1 << 4;
947        /// The [`LayoutMetrics::direction`].
948        const DIRECTION = 1 << 5;
949        /// The [`LayoutMetrics::leftover`].
950        const LEFTOVER = 1 << 6;
951    }
952}
953impl Default for LayoutMask {
954    /// Empty.
955    fn default() -> Self {
956        LayoutMask::empty()
957    }
958}