* craft-ui: combo boxes whose menu grows back with its contents egui's ComboBox lays its menu out in the height it had on the previous pass, and its scroll area takes no more than that. A filtered list that shrinks (a search with no matches) never grows back when the search is cleared (emilk/egui#5225, #5138). craft_ui::combo::menu offers its contents the full height on every pass; craft_ui::combo::combo draws the combo box button around it. The Font Manager's Language, Script and Sample menus use it. * craft-ui: combo and menu take the close behaviour A menu with a search field that closes itself on a pick (DesignCraft's font menu) closes on a click outside only. * craft-ui: ComboBox, a drop-in for egui's combo box, with header menus craft_ui::ComboBox has egui 0.37's ComboBox builder and button (same atoms, style classes and icon, pixel-identical in a test) with a menu that grows back. show_ui_with_header and show_rows_with_header keep a search field in place and scroll only the list; combo::Menu is the same menu under a field an app draws itself. clippy.toml disallows egui::ComboBox. The Font Manager's Language menu, the keymap dialog's combo boxes and the print dialog's printer and duplex choices use it. * craft-ui: combo menus keep to the room on their side, and footers A menu is never taller than the room on the side of its field it opens on, so an unbounded height fits on screen and scrolls. The side is settled once the menu is measured and kept while it is open, so a menu that shrinks or grows while typing doesn't jump to the other side of its field. Footer forms keep a row under the scrolling list. Menu::new takes the field's Ui for its default height and frame, and Menu::open shows the menu under a text field that keeps the focus while typing. * craft-ui: combo menus open by the app keep their side, and take picks Menu::open takes &mut bool: the menu clears it when it closes (close behavior, Escape, ui.close(), the field losing the focus to anything but the menu), so a click on a row under a focused text field is picked. The side settles only once the menu was shown and measured, also for menus the app opens (Popup::open_id). A footer that changes height remeasures the menu before it is shown. The popup frame's margins come from the popup style; only above/below alignments are tried. * craft-ui: a resting pointer over a combo menu doesn't keep it open The menu counts the pointer as having moved into it only on a press or click. A footer height change also requests a repaint, in case the discard is denied. * craft-ui: a footer menu opens without a second sizing pass A footer height change no longer discards the pass or forces another sizing pass: the menu measures again on the next pass and settles its side then. A forced sizing pass ran a search field's focus request in a pass where the field can't take the focus, so a footer menu opened with the search unfocused. * craft-ui: tests use Ui::scope_id * craft-ui: hide the measuring pass of a footer menu, cap remeasures The pass that measures a menu before its side is settled is laid out but drawn fully transparent, so no frame shows a side or height the menu then leaves. A footer that changes height on every pass remeasures twice per open, then settles. * craft-ui: tests satisfy clippy
craft-font-manager
Craft Font Manager as a library: every font the suite's apps can use, and the font collections to add to them: Google Fonts, Noto, and index URLs the user adds. ArtCraft Launcher shows it as its Fonts tab; the standalone app (aiongg/craft-font-manager) shows it as its window.
| Part | Feature | What it does |
|---|---|---|
filter |
always | Search over the index: family names ignoring case and accents, category, language (English name, autonym or id) and script |
index |
always | The pinned built-in indexes (Google Fonts and Noto) compiled into craft-font-store (bundled), each family's source (source_label: its name and whether it is curated), and the categories |
installed |
always | The installed fonts craft-font-catalog sees, grouped by family and source |
store |
always | Installer, what the interface needs from the font store: the download consent, install, remove, downloads for a preview, the indexes (indexes, fetch_indexes) and the sources the user added (user_sources, add_user_source, remove_user_source). SuiteStore is craft-font-store's store, FakeStore a stand-in for tests, NoStore one for when the store folder is unknown |
FontsPanel |
egui |
The whole interface as one egui panel |
The panel
The layout follows Adobe Fonts' and Google Fonts' browse pages.
- Across the top: Browse and Installed, with their counts, and one search field.
- Filters at the reading start, each under its label: category, language (searchable by its English name or its own, "Japanese · 日本語"), script, source, and installed or not; each option with its count.
- Family cards, one per row: the name, style count, category and source on one line with the card's state at its end (Install and Preview, Installed, a progress bar while downloading, Couldn't install with Retry, "Not curated" for a source the user added), and a large sample in the face below. The list is virtual: only the cards on screen are laid out, and their faces load as they come into view. The arrow keys step through the cards and Enter opens one.
- Sample toolbar: a preset (a sentence in the language filtered by, or in the family's own script when it has no Latin; the alphabet; numerals), the sample text, and the size.
- Details sheet over the list: every style in its own face with Install or Remove for each, Install all and Remove all, the source, designer, licence, scripts and languages, and what a preview downloads.
- Installed: system and font store fonts as the same cards, filtered by source. Only font store families can be removed; system fonts are removed with the system's own tools.
- Sources (
sources_ui, for a host's settings page): the built-in sources, the index URLs the user added, marked not curated and flagged when the file at their address changed since it was added, adding one, and Update sources.
FontsPanel fills the Ui it is given and has no window, menu or interface fonts of its own; the
host's Look gives it the host's colours and font families. The faces it draws samples in are egui
font families of its own, added a few per frame and taken out again past 64, without touching the
host's fonts. Right-to-left interfaces mirror through craft_ui::direction; sample text keeps its
own direction.
let catalog = Arc::clone(Catalog::global());
catalog.scan_in_background();
let installer: Arc<dyn Installer> = match SuiteStore::for_user(Arc::clone(&catalog)) {
Some(store) => Arc::new(store),
None => Arc::new(NoStore),
};
let index = installer.indexes().map_err(|e| e.to_string());
let options = Options { catalog, installer, index, runner: Runner::Thread };
let mut panel = FontsPanel::new(options, Settings::default());
// each frame
panel.ui(ui);
| Call | Use |
|---|---|
set_look(look) |
The host's palettes (dark and light) and font families, and whether labels are set in capitals |
settings(), settings_mut() |
What the host saves: the sample text, preset and size, and whether the downloads note was closed (serde) |
downloads_allowed(), set_downloads_allowed(on) |
The download consent, for a host's settings page. It is the font store's own setting, shared by every Craft app |
show_downloads_switch(false) |
For a host with the switch in its own settings |
sources_ui(ui) |
The font sources, for a host's settings page |
set_index(index) |
The indexes, after the host fetched them |
set_language(tag) |
The interface language, the host's own; English when its catalog isn't embedded |
install_interface_fonts(ctx, craft_faces) |
For a host without interface fonts of its own (the standalone app): craft-ui's font stack for the panel's language, craft-fonts' faces the app embeds with craft-fonts-build, and the interface direction |
Network use
Nothing is downloaded until the user allows it. Downloads are off by default; the first Install or Preview asks first. Files come from each source's repositories at the commit or tag its index records, through jsDelivr (raw GitHub as the fallback), and each is checked against the index's size and sha256 before use. Previews stay in memory and are never installed. A source the user adds is an index at an https URL; its families are never curated.
Strings
Every interface string goes through craft-i18n with the English text as the key. The panel embeds
its own Japanese and Arabic catalogs (src/panel/catalogs/) until its rows are part of
craft-i18n; style and weight names, licence names and product names stay in English.
Testing
cargo test -p craft-font-manager --all-features
CRAFT_FONT_MANAGER_SNAPSHOTS=<dir> cargo test -p craft-font-manager --features egui --test flows screenshots -- --test-threads=1
tests/flows.rs drives the panel with egui_kittest against a small index, a font folder made per
test and FakeStore. They install egui's Hack and Ubuntu Light faces into temporary folders. With
CRAFT_FONT_MANAGER_GOOGLE_FONTS=<google/fonts checkout>, the fake store also "downloads" the
index's files from the checkout, so screenshots show real previews.