Text and fonts
A line of text, and the fonts it is drawn in. A font is named by the role the text plays, never by typeface, and the host answers with a face it registered, so every width and line height here comes from the font engine that paints the text and nothing is estimated.

Images is the other half of what puts something on the panel: a bitmap you ship and an icon the host draws. Canvas and chrome is how a host measures and draws the text these name. This page is what each piece does.
Topics
Section titled “Topics”Text | Draws a single line of text. |
Font | A font plus the style it is drawn in, as widgets reach for it. |
FontId | A font the host has registered, as a number only the host can interpret. |
FontRole | What a piece of text is for, rather than which typeface it uses. |
FontStyle | Weight and slant, as a host-independent value. |
Draws a single line of text.
pub struct Text
| Builder | Sets | When not called |
|---|---|---|
Text::new | the string | — |
font | the font | Font::ui(), the interface face |
bold · italic | the style, replacing any set before | regular |
The italic lines in the picture are upright. The family its host draws was
never cut in italic, so the host draws the nearest style it has, which is
FontStyle's rule.
It is measured by the host. While the tree is laid out, a Text asks its
font for the width of its string and the height of one line, and keeps both.
That is the size it reports, whatever it is offered.
It is one line, and never shortened. A string wider than the space it is
given reports its whole width and draws past the edge. Shorten it in update,
or put it in a ListRow, whose theme decides what a long
title does.
It draws nothing in a missing face. In
Font::UNAVAILABLE it measures zero and never hands its
string to the host, so no backend paints it in another face over space nothing
reserved.
It holds the string it was built with. Build the string in update and
keep it. A format! inside body runs on every paint and every frame carrying
input, and brings core::fmt with it.
Example — styles
use xpui::{Font, FontStyle, Text, VStack, vstack};
let styles: VStack<()> = vstack![8; Text::new("Regular"), Text::new("Bold").bold(), Text::new("Italic").italic(), Text::new("Bold italic").font(Font::ui().with_style(FontStyle::BoldItalic)),];Example — a figure formatted once
use xpui::{NavigationScreen, Screen, Text, View, vstack};
struct Battery { label: String, // "72%", rebuilt only when the level changes}
impl Screen for Battery { type Message = u8;
fn body(&self) -> impl View<u8> { NavigationScreen::new(vstack![8; Text::new("Battery"), Text::new(self.label.as_str()).bold(), ]) }
fn update(&mut self, percent: u8) { self.label = format!("{percent}%"); }
fn title(&self) -> Option<&'static str> { Some("Power") }}
let mut screen = Battery { label: String::new() };screen.update(72);assert_eq!(screen.label, "72%");Creating text
Section titled “Creating text”Text::new
Section titled “Text::new”Text in the default interface font.
pub fn new(content: impl Into<String>) -> SelfIt asks the installed host for the interface font, so a host must be installed first; the examples on this page install the test host for that reason. A string containing an interior NUL may render as empty: a host with a C boundary cannot pass it.
Font and style
Section titled “Font and style”Text::font
Section titled “Text::font”Draws in font instead of the default.
pub fn font(self, font: Font) -> Selfuse xpui::{Font, Text};
let caption = Text::new("Last synced an hour ago").font(Font::ui_small());let page = Text::new("It is a truth universally acknowledged").font(Font::reader());Text::bold
Section titled “Text::bold”Draws bold.
pub fn bold(self) -> SelfText::italic
Section titled “Text::italic”Draws italic.
pub fn italic(self) -> SelfSee also: Font, FontRole, ListRow
A font plus the style it is drawn in, as widgets reach for it.
pub struct FontFonts are named by what the text is for, never by typeface. Which face a
FontRole resolves to is the host's business, and a role survives
the host's assets being changed. A Font is Copy and compares by face and
style.
| To | Call |
|---|---|
| choose a face | role · ui · ui_small · reader |
| set the style | bold · italic · with_style |
| measure | text_width · line_height · is_available |
Never estimate a width. Measure through the font: an estimate drifts from
what is painted, and content drifts off the panel with it. Text
measures this way for you.
Example — naming fonts by role
use xpui::{Font, FontRole, FontStyle};
xpui::testing::install();let ui = Font::ui(); // interface text: the widget defaultlet small = Font::ui_small(); // captions, secondary labelslet reader = Font::reader(); // the face the user reads inlet bold = Font::ui().bold(); // also .italic()let both = Font::ui().with_style(FontStyle::BoldItalic);
assert_eq!(Font::role(FontRole::Ui), ui);assert_eq!(both.id(), ui.id(), "a style is not another face");assert!(ui.text_width("Battery") > 0);assert!(ui.line_height() > 0);Example — choosing the face that fits
use xpui::{Font, Text, Theme};
xpui::testing::install();let title = "The Collected Poems of Wallace Stevens";let room = Theme::content_area().width();let font = if Font::ui().text_width(title) <= room { Font::ui() } else { Font::ui_small() };let label = Text::new(title).font(font);Choosing a face
Section titled “Choosing a face”Font::role
Section titled “Font::role”The host's font for role, in regular style.
pub fn role(role: FontRole) -> SelfIt asks the installed host when it is called, and keeps the answer.
Font::ui
Section titled “Font::ui”Interface text: the default for every widget.
pub fn ui() -> SelfFont::ui_small
Section titled “Font::ui_small”Smaller interface text, for captions and secondary labels.
pub fn ui_small() -> SelfFont::reader
Section titled “Font::reader”The face the user chose for reading.
pub fn reader() -> SelfFont::UNAVAILABLE
Section titled “Font::UNAVAILABLE”A font this build does not ship, which measures zero.
pub const UNAVAILABLE: Font = FontA build may compile a face out, and the host answers its role with this. A
missing face therefore takes no room in a layout, rather than being given a
size nobody measured, and a Text in it draws nothing: the string
never reaches the host, which might otherwise paint it in a face it does know.
A widget of your own that calls Renderer::draw_text directly makes the same
check with is_available.
use xpui::{Font, Point, Text, View, testing};
testing::install();testing::reset();
let missing = Font::UNAVAILABLE;assert!(!missing.is_available());assert_eq!(missing.text_width("Battery"), 0);assert_eq!(missing.line_height(), 0);
let mut label = Text::new("Battery").font(missing);View::<()>::measure(&mut label, testing::screen());View::<()>::render(&label, Point::ORIGIN);assert!(testing::drawn_text().is_empty());Font::bold
Section titled “Font::bold”The same font in bold, replacing any style set before.
pub fn bold(self) -> SelfFont::italic
Section titled “Font::italic”The same font in italic, replacing any style set before.
pub fn italic(self) -> SelfFont::with_style
Section titled “Font::with_style”The same font in style.
pub fn with_style(self, style: FontStyle) -> SelfThe only way to ask for FontStyle::BoldItalic.
Measuring
Section titled “Measuring”Font::text_width
Section titled “Font::text_width”The width text paints at, or 0 for a face this build lacks.
pub fn text_width(self, text: &str) -> i32In pixels, in this font's style: bold text is usually wider.
Font::line_height
Section titled “Font::line_height”The height one line occupies, or 0 for a face this build lacks.
pub fn line_height(self) -> i32Ascent and descent included, so lines stacked at this pitch do not touch.
Font::is_available
Section titled “Font::is_available”Whether this build ships the face.
pub fn is_available(self) -> boolReading a font
Section titled “Reading a font”What a host is handed when it draws. A screen has no need of these.
Font::id
Section titled “Font::id”The host's id for the face.
pub fn id(self) -> FontIdFont::style
Section titled “Font::style”The weight and slant it is drawn in.
pub fn style(self) -> FontStyleSee also: Text, FontRole, FontStyle, FontId
FontId
Section titled “FontId”A font the host has registered, as a number only the host can interpret.
pub struct FontId(pub i32)The host's side of a Font. A host's TextMetrics::font answers a
role with one, and receives it back with every measurement and every string it
draws; see writing a backend. 0 means "this build
does not ship that font". The number is public, FontId(7), because a host
makes them; a screen only ever receives them.
use xpui::{Font, FontId};
assert!(FontId(7).is_available());assert!(!FontId::UNAVAILABLE.is_available());assert_eq!(Font::UNAVAILABLE.id(), FontId::UNAVAILABLE);Constants and checks
Section titled “Constants and checks”FontId::UNAVAILABLE
Section titled “FontId::UNAVAILABLE”The font a build compiled out, which measures zero.
pub const UNAVAILABLE: FontId = FontId(0)FontId::is_available
Section titled “FontId::is_available”Whether this build ships the font.
pub fn is_available(self) -> boolSee also: Font, Font::UNAVAILABLE
FontRole
Section titled “FontRole”What a piece of text is for, rather than which typeface it uses.
pub enum FontRole
The host decides what each role means. Naming families here would tie the framework to one product's assets, and a role survives those assets being changed.
| Variant | |
|---|---|
FontRole::Ui | Interface text: labels, list rows, values. |
FontRole::UiSmall | Smaller interface text, for captions and secondary labels. |
FontRole::Reader | The face the user chose for reading, including fonts loaded from storage. |
Each has a shorthand on Font: ui, ui_small and
reader.
use xpui::{Font, FontRole, Text, VStack, vstack};
assert_eq!(Font::role(FontRole::UiSmall), Font::ui_small());
let roles: VStack<()> = vstack![8; Text::new("Interface text"), Text::new("A caption beneath it").font(Font::role(FontRole::UiSmall)), Text::new("The face chosen for reading").font(Font::role(FontRole::Reader)),];See also: Font::role, FontStyle
FontStyle
Section titled “FontStyle”Weight and slant, as a host-independent value.
pub enum FontStyle| Variant | |
|---|---|
FontStyle::Regular | Upright, regular weight. |
FontStyle::Bold | Heavier weight. |
FontStyle::Italic | Slanted. |
FontStyle::BoldItalic | Both. |
Regular is the default. The type is #[repr(u8)], and the numbers are what a
host with a C boundary receives: Regular is 0, Bold 1, Italic 2 and
BoldItalic 3. A host without a face in that style draws what it has.
use xpui::{Font, FontStyle};
assert_eq!(FontStyle::default(), FontStyle::Regular);assert_eq!(Font::ui().bold().italic().style(), FontStyle::Italic, "the last one wins");assert_eq!(Font::ui().with_style(FontStyle::BoldItalic).style() as u8, 3);See also: Font::with_style, FontRole