text_typeset/types.rs
1/// Opaque handle to a registered font face.
2///
3/// Obtained from [`crate::Typesetter::register_font`] or [`crate::Typesetter::register_font_as`].
4/// Pass to [`crate::Typesetter::set_default_font`] to make it the default.
5#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
6pub struct FontFaceId(pub u32);
7
8// ── Render output ───────────────────────────────────────────────
9
10/// Everything needed to draw one frame.
11///
12/// Produced by [`crate::Typesetter::render`]. Contains glyph quads (textured rectangles
13/// from the atlas), inline image placeholders, and decoration rectangles
14/// (selections, cursor, underlines, table borders, etc.).
15///
16/// The adapter draws the frame in three passes:
17/// 1. Upload `atlas_pixels` as a GPU texture (only when `atlas_dirty` is true).
18/// 2. Draw each [`GlyphQuad`] as a textured rectangle from the atlas.
19/// 3. Draw each [`DecorationRect`] as a colored rectangle.
20pub struct RenderFrame {
21 /// True if the atlas texture changed since the last frame (needs re-upload).
22 pub atlas_dirty: bool,
23 /// Atlas texture width in pixels.
24 pub atlas_width: u32,
25 /// Atlas texture height in pixels.
26 pub atlas_height: u32,
27 /// RGBA pixel data, row-major. Length = `atlas_width * atlas_height * 4`.
28 pub atlas_pixels: Vec<u8>,
29 /// One textured rectangle per visible glyph.
30 pub glyphs: Vec<GlyphQuad>,
31 /// Inline image placeholders. The adapter loads the actual image data
32 /// (e.g., via `TextDocument::resource(name)`) and draws it at the given
33 /// screen position.
34 pub images: Vec<ImageQuad>,
35 /// Decoration rectangles: selections, cursor, underlines, strikeouts,
36 /// overlines, backgrounds, table borders, and cell backgrounds.
37 pub decorations: Vec<DecorationRect>,
38 /// Per-block glyph data for incremental updates. Keyed by block_id.
39 pub(crate) block_glyphs: Vec<(usize, Vec<GlyphQuad>)>,
40 /// Per-block decoration data (underlines, etc. — NOT cursor/selection).
41 pub(crate) block_decorations: Vec<(usize, Vec<DecorationRect>)>,
42 /// Per-block image data for incremental updates.
43 pub(crate) block_images: Vec<(usize, Vec<ImageQuad>)>,
44 /// Per-block height snapshot for detecting height changes in incremental render.
45 pub(crate) block_heights: std::collections::HashMap<usize, f32>,
46}
47
48/// A positioned glyph to draw as a textured quad from the atlas.
49///
50/// The adapter draws the rectangle at `screen` position, sampling from
51/// the `atlas` rectangle in the atlas texture, tinted with `color`.
52#[derive(Clone)]
53pub struct GlyphQuad {
54 /// Screen position and size: `[x, y, width, height]` in pixels.
55 pub screen: [f32; 4],
56 /// Atlas source rectangle: `[x, y, width, height]` in atlas pixel coordinates.
57 pub atlas: [f32; 4],
58 /// Glyph color: `[r, g, b, a]`, 0.0-1.0.
59 /// For normal text glyphs, this is the text color (default black).
60 /// For color emoji, this is `[1, 1, 1, 1]` (color is baked into the atlas).
61 pub color: [f32; 4],
62}
63
64/// An inline image placeholder.
65///
66/// text-typeset computes the position and size but does NOT load or rasterize
67/// the image. The adapter retrieves the image data (e.g., from
68/// `TextDocument::resource(name)`) and draws it as a separate texture.
69#[derive(Clone)]
70pub struct ImageQuad {
71 /// Screen position and size: `[x, y, width, height]` in pixels.
72 pub screen: [f32; 4],
73 /// Image resource name (matches `FragmentContent::Image::name` from text-document).
74 pub name: String,
75}
76
77/// A colored rectangle for decorations (underlines, selections, borders, etc.).
78#[derive(Clone)]
79pub struct DecorationRect {
80 /// Screen position and size: `[x, y, width, height]` in pixels.
81 pub rect: [f32; 4],
82 /// Color: `[r, g, b, a]`, 0.0-1.0.
83 pub color: [f32; 4],
84 /// What kind of decoration this rectangle represents.
85 pub kind: DecorationKind,
86}
87
88/// The type of a [`DecorationRect`].
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum DecorationKind {
91 /// Selection highlight (translucent background behind selected text).
92 Selection,
93 /// Cursor caret (thin vertical line at the insertion point).
94 Cursor,
95 /// Underline (below baseline, from font metrics).
96 Underline,
97 /// Strikethrough (at x-height, from font metrics).
98 Strikeout,
99 /// Overline (at ascent line).
100 Overline,
101 /// Generic background (e.g., frame borders).
102 Background,
103 /// Block-level background color.
104 BlockBackground,
105 /// Table border line.
106 TableBorder,
107 /// Table cell background color.
108 TableCellBackground,
109 /// Text-level background highlight (behind individual text runs).
110 /// Adapters should draw these before glyph quads so text appears on top.
111 TextBackground,
112 /// Cell-level selection highlight (entire cell background when cells are
113 /// selected as a rectangular region, as opposed to text within cells).
114 CellSelection,
115}
116
117/// Underline style for text decorations.
118#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
119pub enum UnderlineStyle {
120 /// No underline.
121 #[default]
122 None,
123 /// Solid single underline.
124 Single,
125 /// Dashed underline.
126 Dash,
127 /// Dotted underline.
128 Dot,
129 /// Alternating dash-dot pattern.
130 DashDot,
131 /// Alternating dash-dot-dot pattern.
132 DashDotDot,
133 /// Wavy underline.
134 Wave,
135 /// Spell-check underline (wavy, typically red).
136 SpellCheck,
137}
138
139/// Vertical alignment for characters (superscript, subscript, etc.).
140#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
141pub enum VerticalAlignment {
142 /// Normal baseline alignment.
143 #[default]
144 Normal,
145 /// Superscript: smaller size, shifted up.
146 SuperScript,
147 /// Subscript: smaller size, shifted down.
148 SubScript,
149}
150
151// ── Hit testing ─────────────────────────────────────────────────
152
153/// Result of [`crate::Typesetter::hit_test`] - maps a screen-space point to a
154/// document position.
155pub struct HitTestResult {
156 /// Absolute character position in the document.
157 pub position: usize,
158 /// Which block (paragraph) was hit, identified by stable block ID.
159 pub block_id: usize,
160 /// Character offset within the block (0 = start of block).
161 pub offset_in_block: usize,
162 /// What region of the layout was hit.
163 pub region: HitRegion,
164 /// Tooltip text if the hit position has a tooltip. None otherwise.
165 pub tooltip: Option<String>,
166 /// When non-None, the hit position is inside a table cell.
167 /// Identifies the table by its stable table ID.
168 /// None for hits on top-level blocks, frame blocks, or outside any table.
169 pub table_id: Option<usize>,
170}
171
172/// What region of the layout a hit test landed in.
173#[derive(Debug)]
174pub enum HitRegion {
175 /// Inside a text run (normal text content).
176 Text,
177 /// In the block's left margin area (before any text content).
178 LeftMargin,
179 /// In the block's indent area.
180 Indent,
181 /// On a table border line.
182 TableBorder,
183 /// Below all content in the document.
184 BelowContent,
185 /// Past the end of a line (to the right of the last character).
186 PastLineEnd,
187 /// On an inline image.
188 Image { name: String },
189 /// On a hyperlink.
190 Link { href: String },
191}
192
193// ── Cursor display ──────────────────────────────────────────────
194
195/// Cursor display state for rendering.
196///
197/// The adapter reads cursor position from text-document's `TextCursor`
198/// and creates this struct to feed to [`crate::Typesetter::set_cursor`].
199/// text-typeset uses it to generate caret and selection decorations
200/// in the next [`crate::Typesetter::render`] call.
201pub struct CursorDisplay {
202 /// Cursor position (character offset in the document).
203 pub position: usize,
204 /// Selection anchor. Equals `position` when there is no selection.
205 /// When different from `position`, the range `[min(anchor, position), max(anchor, position))`
206 /// is highlighted as a selection.
207 pub anchor: usize,
208 /// Whether the caret is visible (false during the blink-off phase).
209 /// The adapter manages the blink timer; text-typeset just respects this flag.
210 pub visible: bool,
211 /// When non-empty, render cell-level selection highlights instead of
212 /// text-level selection. Each tuple is `(table_id, row, col)` identifying
213 /// a selected cell. The adapter fills this from `TextCursor::selected_cells()`.
214 pub selected_cells: Vec<(usize, usize, usize)>,
215}
216
217// ── Scrolling ───────────────────────────────────────────────────
218
219/// Visual position and size of a laid-out block.
220///
221/// Returned by [`crate::Typesetter::block_visual_info`].
222pub struct BlockVisualInfo {
223 /// Block ID (matches `BlockSnapshot::block_id`).
224 pub block_id: usize,
225 /// Y position of the block's top edge relative to the document start, in pixels.
226 pub y: f32,
227 /// Total height of the block including margins, in pixels.
228 pub height: f32,
229}
230
231// ── Single-line API ────────────────────────────────────────────
232
233/// Text formatting parameters for the single-line layout API.
234///
235/// Controls font selection, size, and text color. All fields are optional
236/// and fall back to the typesetter's defaults (default font, default size,
237/// default text color).
238#[derive(Clone, Debug, Default)]
239pub struct TextFormat {
240 /// Font family name (e.g., "Noto Sans", "monospace").
241 /// None means use the default font.
242 pub font_family: Option<String>,
243 /// Font weight (100-900). Overrides `font_bold`.
244 pub font_weight: Option<u32>,
245 /// Shorthand for weight 700. Ignored if `font_weight` is set.
246 pub font_bold: Option<bool>,
247 /// Italic style.
248 pub font_italic: Option<bool>,
249 /// Font size in pixels. None means use the default size.
250 pub font_size: Option<f32>,
251 /// Text color (RGBA, 0.0-1.0). None means use the typesetter's text color.
252 pub color: Option<[f32; 4]>,
253}
254
255/// Result of [`crate::Typesetter::layout_single_line`].
256///
257/// Contains the measured dimensions and GPU-ready glyph quads for a
258/// single line of text. No flow layout, line breaking, or bidi analysis
259/// is performed.
260pub struct SingleLineResult {
261 /// Total advance width of the shaped text, in pixels.
262 pub width: f32,
263 /// Line height (ascent + descent + leading), in pixels.
264 pub height: f32,
265 /// Distance from the top of the line to the baseline, in pixels.
266 pub baseline: f32,
267 /// GPU-ready glyph quads, positioned at y=0 (no scroll offset).
268 pub glyphs: Vec<GlyphQuad>,
269}
270
271/// Result of [`crate::Typesetter::layout_paragraph`].
272///
273/// Contains the measured dimensions and GPU-ready glyph quads for a
274/// multi-line paragraph wrapped at a fixed width. Glyphs are positioned
275/// in paragraph-local coordinates: `x = 0` is the left edge of the
276/// paragraph, `y = 0` is the top of the first line's line box. The
277/// adapter should offset all glyph quads by the paragraph's screen
278/// position before drawing.
279pub struct ParagraphResult {
280 /// Width of the widest laid-out line, in pixels. May be less than the
281 /// `max_width` passed to `layout_paragraph` if the content is narrower.
282 pub width: f32,
283 /// Total stacked paragraph height in pixels — sum of line heights for
284 /// all emitted lines.
285 pub height: f32,
286 /// Distance from `y = 0` to the baseline of the first line, in pixels.
287 pub baseline_first: f32,
288 /// Number of lines actually emitted (respects `max_lines` when set).
289 pub line_count: usize,
290 /// GPU-ready glyph quads in paragraph-local coordinates.
291 pub glyphs: Vec<GlyphQuad>,
292}
293
294impl RenderFrame {
295 pub(crate) fn new() -> Self {
296 Self {
297 atlas_dirty: false,
298 atlas_width: 0,
299 atlas_height: 0,
300 atlas_pixels: Vec::new(),
301 glyphs: Vec::new(),
302 images: Vec::new(),
303 decorations: Vec::new(),
304 block_glyphs: Vec::new(),
305 block_decorations: Vec::new(),
306 block_images: Vec::new(),
307 block_heights: std::collections::HashMap::new(),
308 }
309 }
310}