pub struct TextContext { /* private fields */ }Expand description
Owns parley’s font matching and layout scratch state.
This is deliberately not Clone/Sync: it is expensive per-instance state
meant to be constructed once and borrowed mutably for each layout pass. The
generic brush parameter is fixed to peniko::Brush so glyph runs carry the
same paint vocabulary as frust_scene.
Implementations§
Source§impl TextContext
impl TextContext
Sourcepub fn new() -> Self
pub fn new() -> Self
Builds a context with system fonts available, seeded with every app font registered so far.
parley::FontContext::new populates the fontique source collection from
the platform (Core Text on macOS); no manual font registration is
required for the default crate::FontFamily::SystemUi to resolve.
The seeding step (Self::sync_app_fonts) is what makes a private
context (a TextInput’s) shape with the same app fonts the shell-owned
context does, however late it is constructed — see [APP_FONTS]. It
also applies every pending register_generic_fallback entry, so a
generic-family fallback a shell registered before this call is already
live in the returned context — see [GENERIC_FALLBACKS].
Sourcepub fn layout(
&mut self,
text: &str,
style: &TextStyle,
max_width: Option<f32>,
) -> TextLayout
pub fn layout( &mut self, text: &str, style: &TextStyle, max_width: Option<f32>, ) -> TextLayout
Lays out text with style, wrapping to max_width when supplied.
max_width is in the same logical-pixel units as style.size (the
layout scale is fixed at 1.0 here — physical-pixel scaling is applied
downstream via the scene transform). Passing None produces a single
unwrapped line per hard break in text. An empty text yields a layout
with no glyph runs and a near-zero size.
style.align positions every line within the layout’s width (a
no-op distinction from crate::TextAlign::Start until max_width
is bounded, since an unbounded line’s width already equals its
content). Applies here and on the width-change re-break path in
[crate::shape_cache::ShapeCache::get] — see that fn’s docs.
Sourcepub fn layout_bounded(
&mut self,
text: &str,
style: &TextStyle,
max_width: Option<f32>,
max_lines: Option<usize>,
overflow: TextOverflow,
) -> TextLayout
pub fn layout_bounded( &mut self, text: &str, style: &TextStyle, max_width: Option<f32>, max_lines: Option<usize>, overflow: TextOverflow, ) -> TextLayout
Lays out text exactly like Self::layout, then caps it to
max_lines (when Some), applying overflow to whatever is cut.
max_lines = None delegates straight to Self::layout — the
zero-cost, behavior-unchanged path every existing caller keeps taking.
parley 0.11 has no native max_lines/ellipsis support, so a bounded
call does two cached shaping passes on the truncating path: once to
measure the full text, once more (in Self::layout, so still
shape-cache-backed) to shape the truncated result — post-shaping
measure-and-truncate, not a parley feature. The ellipsis walk in
between adds only bounded, cache-invisible measurements
(Self::measure_uncached).
§Algorithm
A layout overflows max_lines in one of two ways parley itself
exposes no direct query for, so both are checked explicitly against
the full (untruncated) layout:
- extra lines: line-breaking produced more than
max_linescontent lines (wrapping, ormax_lineshard\nbreaks in the source) — see [visible_line_count] for why the raw line count is not that number when the text ends in\n. - an unbreakable overrun: exactly
max_lineslines came out, but the last visible one is itself wider thanmax_width— a run with no break opportunity (one long unspaced word) that parley lets overflow rather than force-break.
Neither condition holds → the text already fits (this is also why an
exact-fit line, width == max_width, never gets truncated: the
comparison is a strict >); both overflow modes then return the full
layout as-is, unless full itself still carries the raw phantom
line parley opens after a terminal '\n' — in which case it’s
reshaped with the trailing newline(s) stripped first, so a fitting
layout’s reported line count and height always match what paints
(see [visible_line_count]).
On overflow, TextOverflow::Clip only ever drops whole trailing
lines — the source text is cut at the end of line max_lines - 1’s
span and reshaped; an unbreakable-overrun-only case (no extra lines
to drop) is left untouched, since Clip never character-trims.
TextOverflow::Ellipsis does the same line drop, then further
truncates the last visible line’s own text via
Self::truncate_last_line and appends [ELLIPSIS], before
reshaping the whole (earlier lines + truncated last line) string —
which is also why earlier lines reliably survive verbatim: parley’s
line-breaker is greedy/left-to-right, so shortening what follows a
line never changes how that line itself broke.
Sourcepub fn shape_cache_stats(&self) -> ShapeCacheStats
pub fn shape_cache_stats(&self) -> ShapeCacheStats
The shape cache’s instrumentation counters (shapes performed, line-break-only relayouts, full hits, evictions).
The observable hook the shape-cache tests assert against, and a
perf signal otherwise. Plain scalar data — no parley/vello/wgpu
type leaks through (scene-layer purity).
Sourcepub fn register_fonts(
&mut self,
data: Vec<u8>,
) -> Result<Vec<RegisteredFamily>, FontError>
pub fn register_fonts( &mut self, data: Vec<u8>, ) -> Result<Vec<RegisteredFamily>, FontError>
Registers font faces from raw bytes (TTF/OTF, or a TTC/OTC
collection) so they resolve by family name via
crate::FontFamily::named/crate::FontFamily::stack.
Wraps fontique’s parley::fontique::Collection::register_fonts. A
registered family shadows a same-named system family (fontique
0.11 semantics: the registered map is checked before the system map),
so bundling a family already present on the platform (e.g. “Roboto”)
deterministically wins over the platform’s own copy.
Always clears the shape cache on success — a same-named registered
family changes shaping without changing the cache key, so any layout
shaped before this call could otherwise be served stale. Returns
FontError::NoFacesFound (no panic) for invalid/empty data, an
empty byte slice, or bytes with no faces fontique can parse.
Caller-visible relayout contract: registering fonts after a shell
has already laid out text does not retroactively re-shape anything
still cached elsewhere (e.g. a widget’s own retained layout) — a shell
calling this must force ChangeFlags::LAYOUT | PAINT the same way a
theme swap does (see docs/ARCHITECTURE.md’s Theme delivery), so the
next layout pass re-shapes against the newly registered faces. This
crate only owns the shape-cache half of that contract.
An accepted payload is also recorded process-wide ([APP_FONTS]), so
every TextContext constructed later — and every existing one that
calls Self::sync_app_fonts — resolves the same family. That is what
carries an app font from a shell’s drain into a widget-owned private
context.
Sourcepub fn sync_app_fonts(&mut self) -> bool
pub fn sync_app_fonts(&mut self) -> bool
Registers every app font this context is missing (see [APP_FONTS])
and applies every register_generic_fallback entry it is missing
(see [GENERIC_FALLBACKS]), returning whether either actually
changed this context’s resolution.
Cheap when there is nothing to do — a lock and a length compare per record, no allocation and no font parsing — so a widget owning a private context can call it once per layout pass to pick up a font (or fallback) registered after the context was built (the shells’ per-frame late drain).
A true return carries the same caller-visible relayout contract as
Self::register_fonts: this context’s shape cache is cleared, but a
layout retained outside it (a crate::TextEditor’s parley layout)
must be re-shaped by its owner.
Sourcepub fn clear_shape_cache(&mut self)
pub fn clear_shape_cache(&mut self)
Drops every cached shaped layout, forcing the next Self::layout
call for any (text, style) pair to re-shape from scratch.
Exposed (not just an internal helper) for the shell late-drain path
(a shell registering fonts after startup, once widgets may already
hold cached layouts elsewhere) — Self::register_fonts already
calls this internally on success, so a caller registering fonts
doesn’t need to call it separately.