pub struct TextEngine { /* private fields */ }Expand description
Fonts, a bounded glyph cache, and everything that needs both.
One of these per application. It is &mut for measurement as well as drawing,
because measuring is what populates the cache: a label measured during layout
and drawn a moment later rasterises its glyphs once, and a label measured on
every layout pass and never redrawn pays a cache lookup rather than an outline
computation each time.
Implementations§
Source§impl TextEngine
impl TextEngine
Sourcepub fn new() -> TextEngine
pub fn new() -> TextEngine
An engine with the built-in bitmap font registered as FontId(0), and a
64 KB glyph cache.
FontId(0) is always the built-in font, in every configuration, so a
widget that names no font gets something that certainly exists.
Sourcepub fn with_atlas(atlas: GlyphAtlas) -> TextEngine
pub fn with_atlas(atlas: GlyphAtlas) -> TextEngine
As TextEngine::new, with a cache of a chosen size.
Sourcepub fn add_font(&mut self, source: Box<dyn GlyphSource>) -> FontId
pub fn add_font(&mut self, source: Box<dyn GlyphSource>) -> FontId
Registers a font and returns its id.
Sourcepub fn set_default_font(&mut self, font: FontId)
pub fn set_default_font(&mut self, font: FontId)
Draws every style that names no font in this face.
Without this, FontId(0) is the built-in 5x7 bitmap and there is no way
to ask for anything else: every widget in this workspace carries
TextStyle::built_in or TextStyle::default, both of which name
FontId::DEFAULT, so registering a face with
add_font alone registers something nothing
refers to. That was #130.
One indirection, resolved when a style becomes glyphs — so no widget, no
TextStyle and no form file changes, and an application that wants a
real face says two lines instead of threading a style through everything
it builds.
An id that was never registered is ignored, because the alternative is a panel that draws nothing.
let mut engine = TextEngine::new();
// Nothing registered but the built-in, so the default is the built-in.
assert_eq!(engine.default_font(), FontId::DEFAULT);
// An id nobody registered changes nothing.
engine.set_default_font(FontId(9));
assert_eq!(engine.default_font(), FontId::DEFAULT);Sourcepub const fn default_font(&self) -> FontId
pub const fn default_font(&self) -> FontId
Which face FontId::DEFAULT currently stands for.
Sourcepub fn font_count(&self) -> usize
pub fn font_count(&self) -> usize
Number of registered fonts.
Sourcepub fn font_contains(&self, font: FontId, ch: char) -> bool
pub fn font_contains(&self, font: FontId, ch: char) -> bool
Whether a registered font has a glyph of its own for ch.
The question an application asks before choosing what to draw: a
keyboard that would like ⌫ on its Backspace key, a status line that
would like °. Drawing a character the font lacks is not an error — it
comes out as the missing-character box — so this is what turns a silent
row of tofu into a legible fallback the author picked.
false for an unregistered id, and for a font that can only render ch
through shaping — see GlyphSource::glyph_id.
Sourcepub const fn atlas(&self) -> &GlyphAtlas
pub const fn atlas(&self) -> &GlyphAtlas
The glyph cache.
Sourcepub const fn stats(&self) -> AtlasStats
pub const fn stats(&self) -> AtlasStats
Cache statistics.
Sourcepub fn clear_cache(&mut self)
pub fn clear_cache(&mut self)
Empties the glyph cache. Needed after nothing; useful in benches.
Sourcepub fn metrics(&self, style: TextStyle) -> FontMetrics
pub fn metrics(&self, style: TextStyle) -> FontMetrics
Vertical metrics for a style.
Sourcepub fn snap_size(&self, style: TextStyle) -> u16
pub fn snap_size(&self, style: TextStyle) -> u16
The size this style will actually be drawn at.
Sourcepub fn line_height(&self, style: TextStyle) -> i32
pub fn line_height(&self, style: TextStyle) -> i32
Baseline-to-baseline distance for a style.
Sourcepub fn layout_line(
&mut self,
style: TextStyle,
text: &str,
f: impl FnMut(PositionedGlyph),
) -> i32
pub fn layout_line( &mut self, style: TextStyle, text: &str, f: impl FnMut(PositionedGlyph), ) -> i32
Lays out one line, calling f for each glyph that has ink.
Returns the total advance. Positions are relative to the line’s start, with
bounds.y measured from the baseline — so a caller places the line by
translating, and never has to know how the font was measured.
Sourcepub fn measure_line(&mut self, style: TextStyle, text: &str) -> i32
pub fn measure_line(&mut self, style: TextStyle, text: &str) -> i32
Width of one line, ignoring \n.
Sourcepub fn measure(&mut self, style: TextStyle, text: &str) -> Size
pub fn measure(&mut self, style: TextStyle, text: &str) -> Size
Extent of text, honouring \n.
The height is lines * line_height, not the ink’s bounding box: a label
that changes from Ok to Ogg must not change height, or a form would
reflow every time a reading gained a descender.
Sourcepub fn wrap<'a>(
&mut self,
style: TextStyle,
text: &'a str,
max_width: i32,
) -> Vec<&'a str>
pub fn wrap<'a>( &mut self, style: TextStyle, text: &'a str, max_width: i32, ) -> Vec<&'a str>
The lines text becomes when broken to fit max_width.
Greedy: words are added to a line until the next one would not fit. That is what every text editor does, it is one measuring pass, and the alternative — balancing lines by minimising raggedness — is a dynamic-programming problem this toolkit has no reason to solve.
Explicit \n always breaks, so a caller who has already decided where
the lines go keeps that decision.
Slices borrow from text; nothing is copied. Words are separated by ASCII
spaces, which is the boundary the built-in font can render and the one the
languages this toolkit ships keyboard layouts for use.
§A word wider than the line
Goes on a line of its own and overflows, rather than being broken between
characters. Breaking mid-word needs to know where a grapheme ends, and
getting that wrong turns æ into two bytes of nothing — so an honest
overflow the caller can see beats a corruption they cannot. A
max_width of zero or less disables wrapping entirely for the same
reason: there is no width that any word fits in.
Sourcepub fn wrapped_height(
&mut self,
style: TextStyle,
text: &str,
max_width: i32,
) -> i32
pub fn wrapped_height( &mut self, style: TextStyle, text: &str, max_width: i32, ) -> i32
Height of text once wrapped to max_width.
Sourcepub fn draw_line(
&mut self,
canvas: &mut Pen<'_>,
style: TextStyle,
origin: Point,
text: &str,
color: Color,
) -> i32
pub fn draw_line( &mut self, canvas: &mut Pen<'_>, style: TextStyle, origin: Point, text: &str, color: Color, ) -> i32
Draws one line with its baseline at origin.
Returns the total advance, of the whole line and not of the part drawn: a caller measuring a line to know how far it scrolls needs all of it.
Only the glyphs the canvas would keep are rasterised and handed over.
The painter clips the rest away to nothing, so the pixels are the same
either way, but handing them over is not free: a line a megabyte long
is a million glyphs, and a painter that builds geometry per glyph turns
that into hundreds of megabytes for the few hundred that are on screen
— more, on some, than a GPU will take in one buffer. Laying the line
out is still the whole of it, which is where width comes from; it is
cheap beside rasterising, being an advance apiece from the cache.
Sourcepub fn draw(
&mut self,
canvas: &mut Pen<'_>,
style: TextStyle,
origin: Point,
text: &str,
color: Color,
) -> Size
pub fn draw( &mut self, canvas: &mut Pen<'_>, style: TextStyle, origin: Point, text: &str, color: Color, ) -> Size
Draws text with the top-left corner of its first line at origin,
honouring \n. Returns the extent laid out.
Top-left rather than baseline, because a widget positions text in a box and should not have to know where the baseline of a font it did not choose happens to fall.