gpui_engine/text_system.rs
1//! The text-shaping, wrapping, and line-layout surface the authoring layer drives.
2//!
3//! [`PlatformTextSystem`] is the lower-level shaping SPI; [`TextSystem`] adds the
4//! engine's font-resolution, metric, wrapping, and per-frame line-layout caching
5//! on top of it. `gpui_engine_default` ships the standard implementation.
6
7use anyhow::Result;
8use gpui_shared_string::SharedString;
9use gpui_types::{Bounds, DevicePixels, Hsla, Pixels, Size};
10use std::any::Any;
11use std::borrow::Cow;
12use std::future::Future;
13use std::ops::Range;
14use std::pin::Pin;
15use std::sync::Arc;
16
17use crate::{
18 Font, FontId, FontRun, LineLayout, LineLayoutIndex, LineWrapperHandle, MissingGlyph,
19 PlatformTextSystem, RenderGlyphParams, TextRenderingMode, WrappedLineLayout,
20};
21
22/// A stream of grapheme clusters that exhausted font fallback.
23pub trait MissingGlyphReports: Send {
24 /// Waits for the next batch of missing glyphs, or `None` once reporting stops.
25 fn recv(&mut self) -> Pin<Box<dyn Future<Output = Option<Vec<MissingGlyph>>> + Send + '_>>;
26}
27
28/// The text shaping, metric, wrapping, and line-layout surface.
29///
30/// Implementations wrap a [`PlatformTextSystem`] with the engine's font-id,
31/// metric, raster-bounds, and line-wrapper pools, and with the per-frame
32/// line-layout cache the authoring layer reuses.
33pub trait TextSystem: Send + Sync + Any {
34 /// The platform text system this engine wraps.
35 fn platform_text_system(&self) -> &Arc<dyn PlatformTextSystem>;
36
37 /// All font names known to the text system, including fallbacks.
38 fn all_font_names(&self) -> Vec<String>;
39
40 /// Add font data to the text system.
41 fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()>;
42
43 /// Takes the receiver for missing-glyph reports.
44 ///
45 /// Only one receiver is available for each text system. Returns `None` when
46 /// the receiver was already taken or another caller is taking it.
47 fn take_missing_glyph_receiver(&self) -> Option<Box<dyn MissingGlyphReports>>;
48
49 /// Starts reporting grapheme clusters that exhaust font fallback.
50 fn enable_missing_glyph_reporting(&self);
51
52 /// Stops reporting missing glyphs and discards any reports collected so far.
53 fn disable_missing_glyph_reporting(&self);
54
55 /// Reports missing glyphs as if the platform text system had observed them.
56 ///
57 /// Defaults to doing nothing, for systems with no report stream. The
58 /// declaration is deliberately not feature-gated: a `#[cfg]` here reflects
59 /// *this* crate's feature selection, and feature unification can enable it
60 /// without the implementing crate's matching feature - which leaves that
61 /// implementation reporting `E0046 missing report_missing_glyphs_in_test`.
62 fn report_missing_glyphs_in_test(&self, _missing_glyphs: Vec<MissingGlyph>) {}
63
64 /// The font for a font id, if it was resolved through this system.
65 fn get_font_for_id(&self, id: FontId) -> Option<Font>;
66
67 /// Resolve a font to its id, falling back to the default stack.
68 fn resolve_font(&self, font: &Font) -> FontId;
69
70 /// Prewarm any system font caches needed to shape text.
71 fn prewarm_fonts(&self, fonts: &[Font]);
72
73 /// The bounding box for a font, at a size.
74 fn bounding_box(&self, font_id: FontId, font_size: Pixels) -> Bounds<Pixels>;
75
76 /// The typographic bounds for a character, in a font and size.
77 fn typographic_bounds(
78 &self,
79 font_id: FontId,
80 font_size: Pixels,
81 character: char,
82 ) -> Result<Bounds<Pixels>>;
83
84 /// The advance width for a character, in a font and size.
85 fn advance(&self, font_id: FontId, font_size: Pixels, ch: char) -> Result<Size<Pixels>>;
86
87 /// The shaped layout width of a character, in a font and size.
88 fn layout_width(&self, font_id: FontId, font_size: Pixels, ch: char) -> Pixels;
89
90 /// The width of an em, in a font and size.
91 fn em_width(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels>;
92
93 /// The advance width of an em, in a font and size.
94 fn em_advance(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels>;
95
96 /// The width of the `0` character, in a font and size.
97 fn ch_width(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels>;
98
99 /// The advance width of the `0` character, in a font and size.
100 fn ch_advance(&self, font_id: FontId, font_size: Pixels) -> Result<Pixels>;
101
102 /// The number of font units per em square.
103 fn units_per_em(&self, font_id: FontId) -> u32;
104
105 /// The cap height of a font, at a size.
106 fn cap_height(&self, font_id: FontId, font_size: Pixels) -> Pixels;
107
108 /// The x-height of a font, at a size.
109 fn x_height(&self, font_id: FontId, font_size: Pixels) -> Pixels;
110
111 /// The ascent of a font, at a size.
112 fn ascent(&self, font_id: FontId, font_size: Pixels) -> Pixels;
113
114 /// The descent of a font, at a size.
115 fn descent(&self, font_id: FontId, font_size: Pixels) -> Pixels;
116
117 /// The recommended baseline offset for a font, size, and line height.
118 fn baseline_offset(&self, font_id: FontId, font_size: Pixels, line_height: Pixels) -> Pixels;
119
120 /// Take a pooled font-run buffer, or an empty one when the pool is dry.
121 fn take_font_runs(&self) -> Vec<FontRun>;
122
123 /// Return a font-run buffer to the pool for reuse.
124 fn recycle_font_runs(&self, font_runs: Vec<FontRun>);
125
126 /// A line wrapper for the given font and size, returned from a pool.
127 fn line_wrapper(self: Arc<Self>, font: Font, font_size: Pixels) -> LineWrapperHandle;
128
129 /// The rasterized size and location of a glyph.
130 fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>>;
131
132 /// Rasterize a glyph, returning its size and coverage bitmap.
133 fn rasterize_glyph(&self, params: &RenderGlyphParams) -> Result<(Size<DevicePixels>, Vec<u8>)>;
134
135 /// The dilation level to use for a glyph painted in the given color.
136 fn glyph_dilation_for_color(&self, color: Hsla) -> u8;
137
138 /// The rendering mode recommended for a font and size.
139 fn recommended_rendering_mode(&self, font_id: FontId, font_size: Pixels) -> TextRenderingMode;
140
141 /// Saves the current cache position so it can be reused or truncated later.
142 fn layout_index(&self) -> LineLayoutIndex;
143
144 /// Re-inserts layouts from the previous frame created before `range`.
145 fn reuse_layouts(&self, range: Range<LineLayoutIndex>);
146
147 /// Drops layouts created after `index`.
148 fn truncate_layouts(&self, index: LineLayoutIndex);
149
150 /// Ages the current frame's layouts into the previous frame.
151 fn finish_frame(&self);
152
153 /// Shapes and wraps `text`, reusing a cached layout when the inputs match.
154 fn layout_wrapped_line(
155 &self,
156 text: &str,
157 font_size: Pixels,
158 runs: &[FontRun],
159 wrap_width: Option<Pixels>,
160 max_lines: Option<usize>,
161 ) -> Arc<WrappedLineLayout>;
162
163 /// Shapes a single line of `text`, reusing a cached layout when the inputs match.
164 fn layout_line(
165 &self,
166 text: &str,
167 font_size: Pixels,
168 runs: &[FontRun],
169 force_width: Option<Pixels>,
170 ) -> Arc<LineLayout>;
171
172 /// Probe the line-layout cache using a caller-provided content hash.
173 fn try_layout_line_by_hash(
174 &self,
175 text_hash: u64,
176 text_len: usize,
177 font_size: Pixels,
178 runs: &[FontRun],
179 force_width: Option<Pixels>,
180 ) -> Option<Arc<LineLayout>>;
181
182 /// Layout a line using a caller-provided content hash as the cache key.
183 fn layout_line_by_hash(
184 &self,
185 text_hash: u64,
186 text_len: usize,
187 font_size: Pixels,
188 runs: &[FontRun],
189 force_width: Option<Pixels>,
190 materialize_text: Box<dyn FnOnce() -> SharedString>,
191 ) -> Arc<LineLayout>;
192}
193
194impl dyn TextSystem {
195 /// A type-erased view of this text system, for downcasting to a concrete
196 /// implementation (e.g. `ParleyTextSystem`) when a caller needs
197 /// implementation-specific layout features that the shared SPI does not
198 /// expose.
199 pub fn as_any(&self) -> &dyn Any {
200 self as &dyn Any
201 }
202}