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() -> TextContext
pub fn new() -> TextContext
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.
Trait Implementations§
Source§impl Default for TextContext
impl Default for TextContext
Source§fn default() -> TextContext
fn default() -> TextContext
Auto Trait Implementations§
impl !Freeze for TextContext
impl !RefUnwindSafe for TextContext
impl !UnwindSafe for TextContext
impl Send for TextContext
impl Sync for TextContext
impl Unpin for TextContext
impl UnsafeUnpin for TextContext
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.