Skip to main content

TextContext

Struct TextContext 

Source
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

Source

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].

Source

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.

Source

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_lines content lines (wrapping, or max_lines hard \n breaks 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_lines lines came out, but the last visible one is itself wider than max_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.

Source

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).

Source

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.

Source

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.

Source

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

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.