Skip to main content

kozan_core/layout/
context.rs

1//! Layout context — shared state for a layout pass.
2//!
3//! Chrome equivalent: the combination of `LayoutView` (document-level state)
4//! and `NGLayoutAlgorithmParams` (per-algorithm context).
5//!
6//! `LayoutContext` is created once per layout pass and threaded through
7//! every algorithm call. It carries dependencies that algorithms need
8//! but should NOT hardcode (text measurement, font system, etc.).
9//!
10//! # Why a struct, not individual parameters?
11//!
12//! 1. Adding a new dependency (e.g., image loader) doesn't change every
13//!    function signature in the call chain.
14//! 2. Algorithms can't accidentally create their own measurer —
15//!    they MUST use the one provided.
16//! 3. Mirrors Chrome: algorithms receive context, not individual services.
17
18use super::inline::measurer::TextMeasurer;
19
20/// Shared context for a layout pass.
21///
22/// Created once by the caller (platform/view layer) and passed through
23/// the entire layout tree. Algorithms read from it, never construct
24/// their own dependencies.
25///
26/// Chrome equivalent: combination of `Document::GetLayoutView()` context
27/// and the font/shaping system accessible through `ComputedStyle::GetFont()`.
28pub struct LayoutContext<'a> {
29    /// The text measurer for this layout pass.
30    ///
31    /// Provides text width measurement and font metrics.
32    /// Chrome: accessed via `Font` → `CachingWordShaper` → `HarfBuzzShaper`.
33    ///
34    /// The platform layer sets this to the appropriate implementation:
35    /// - `DefaultTextMeasurer` for estimation (no font system yet)
36    /// - Parley-based measurer when integrated
37    /// - Custom measurer for testing
38    pub text_measurer: &'a dyn TextMeasurer,
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use crate::layout::inline::measurer::FontMetrics;
45    use crate::layout::inline::{FontSystem, TextMetrics};
46
47    #[test]
48    fn context_carries_measurer() {
49        let measurer = FontSystem::new();
50        let ctx = LayoutContext {
51            text_measurer: &measurer,
52        };
53
54        // Algorithms use ctx.text_measurer, not create their own.
55        let metrics = ctx.text_measurer.measure("Hello", 16.0);
56        assert!(metrics.width > 0.0);
57    }
58
59    #[test]
60    fn custom_measurer_via_context() {
61        struct TestMeasurer;
62        impl TextMeasurer for TestMeasurer {
63            fn measure(&self, _text: &str, _font_size: f32) -> TextMetrics {
64                TextMetrics { width: 42.0 }
65            }
66            fn font_metrics(&self, _font_size: f32) -> FontMetrics {
67                FontMetrics {
68                    ascent: 12.0,
69                    descent: 4.0,
70                    line_gap: 0.0,
71                }
72            }
73        }
74
75        let measurer = TestMeasurer;
76        let ctx = LayoutContext {
77            text_measurer: &measurer,
78        };
79        assert_eq!(ctx.text_measurer.measure("x", 16.0).width, 42.0);
80    }
81}