Skip to main content

ferroui_pretext/
pretext_layout.rs

1use std::cell::OnceCell;
2use std::ops::Deref;
3
4#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
5pub enum WhiteSpaceMode {
6    #[default]
7    Normal,
8    PreWrap,
9}
10
11#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
12pub enum WordBreakMode {
13    #[default]
14    Normal,
15    KeepAll,
16}
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
19pub enum SegmentBreakKind {
20    Text,
21    Space,
22    PreservedSpace,
23    Tab,
24    Glue,
25    ZeroWidthBreak,
26    SoftHyphen,
27    HardBreak,
28}
29
30#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
31pub struct PrepareOptions {
32    pub white_space: WhiteSpaceMode,
33    pub word_break: WordBreakMode,
34}
35
36/// A position in prepared text, independent of UTF-8 and UTF-16 encoding.
37#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
38pub struct LayoutCursor {
39    pub segment_index: usize,
40    pub grapheme_index: usize,
41}
42
43impl LayoutCursor {
44    pub const fn new(segment_index: usize, grapheme_index: usize) -> Self {
45        Self {
46            segment_index,
47            grapheme_index,
48        }
49    }
50}
51
52#[derive(Clone, Copy, Debug, Default, PartialEq)]
53pub struct LayoutResult {
54    pub line_count: usize,
55    pub height: f64,
56}
57
58#[derive(Clone, Copy, Debug, Default, PartialEq)]
59pub struct LineStats {
60    pub line_count: usize,
61    pub max_line_width: f64,
62}
63
64#[derive(Clone, Copy, Debug, Default, PartialEq)]
65pub struct PrepareProfile {
66    pub analysis_ms: f64,
67    pub measure_ms: f64,
68    pub total_ms: f64,
69    pub analysis_segments: usize,
70    pub prepared_segments: usize,
71    pub breakable_segments: usize,
72}
73
74#[derive(Clone, Copy, Debug, PartialEq, Eq)]
75pub struct PreparedLineChunk {
76    pub start_segment_index: usize,
77    pub end_segment_index: usize,
78    pub consumed_end_segment_index: usize,
79}
80
81#[derive(Clone, Debug, PartialEq)]
82pub struct LayoutLine {
83    pub text: String,
84    pub width: f64,
85    pub start: LayoutCursor,
86    pub end: LayoutCursor,
87}
88
89#[derive(Clone, Copy, Debug, PartialEq)]
90pub struct LayoutLineRange {
91    pub width: f64,
92    pub start: LayoutCursor,
93    pub end: LayoutCursor,
94}
95
96#[derive(Clone, Debug, PartialEq)]
97pub struct LayoutLinesResult {
98    pub line_count: usize,
99    pub height: f64,
100    pub lines: Vec<LayoutLine>,
101}
102
103/// Immutable metrics required by allocation-free line walking.
104#[derive(Clone, Debug)]
105pub struct PreparedText {
106    pub(crate) font: String,
107    pub(crate) white_space: WhiteSpaceMode,
108    pub(crate) widths: Vec<f64>,
109    pub(crate) line_end_fit_advances: Vec<f64>,
110    pub(crate) line_end_paint_advances: Vec<f64>,
111    pub(crate) kinds: Vec<SegmentBreakKind>,
112    pub(crate) breakable_widths: Vec<Option<Vec<f64>>>,
113    pub(crate) breakable_prefix_widths: Vec<Option<Vec<f64>>>,
114    pub(crate) discretionary_hyphen_width: f64,
115    pub(crate) tab_stop_advance: f64,
116    pub(crate) chunks: Vec<PreparedLineChunk>,
117    pub(crate) simple_line_walk_fast_path: bool,
118}
119
120impl PreparedText {
121    pub fn font(&self) -> &str {
122        &self.font
123    }
124    pub fn white_space(&self) -> WhiteSpaceMode {
125        self.white_space
126    }
127    pub fn discretionary_hyphen_width(&self) -> f64 {
128        self.discretionary_hyphen_width
129    }
130    pub fn tab_stop_advance(&self) -> f64 {
131        self.tab_stop_advance
132    }
133    pub fn widths(&self) -> &[f64] {
134        &self.widths
135    }
136    pub fn line_end_fit_advances(&self) -> &[f64] {
137        &self.line_end_fit_advances
138    }
139    pub fn line_end_paint_advances(&self) -> &[f64] {
140        &self.line_end_paint_advances
141    }
142    pub fn kinds(&self) -> &[SegmentBreakKind] {
143        &self.kinds
144    }
145    pub fn breakable_widths(&self) -> &[Option<Vec<f64>>] {
146        &self.breakable_widths
147    }
148    pub fn breakable_prefix_widths(&self) -> &[Option<Vec<f64>>] {
149        &self.breakable_prefix_widths
150    }
151    pub fn chunks(&self) -> &[PreparedLineChunk] {
152        &self.chunks
153    }
154    pub fn simple_line_walk_fast_path(&self) -> bool {
155        self.simple_line_walk_fast_path
156    }
157}
158
159/// Prepared metrics with source segments for lazy text and glyph materialization.
160#[derive(Debug)]
161pub struct PreparedTextWithSegments {
162    pub(crate) base: PreparedText,
163    pub(crate) segments: Vec<String>,
164    pub(crate) segment_levels: Option<Vec<i8>>,
165    pub(crate) grapheme_offsets: Vec<OnceCell<Vec<usize>>>,
166}
167
168impl PreparedTextWithSegments {
169    pub fn segments(&self) -> &[String] {
170        &self.segments
171    }
172    pub fn segment_levels(&self) -> Option<&[i8]> {
173        self.segment_levels.as_deref()
174    }
175}
176
177impl Deref for PreparedTextWithSegments {
178    type Target = PreparedText;
179
180    fn deref(&self) -> &Self::Target {
181        &self.base
182    }
183}