Find Font and Glyphs: pick fonts from the font menu (#358 follow-up)

Both switch from widgets::font_dropdown to font_menu::font_menu, so they
get the same samples, favourites and filters as the Character and
Properties panels. They only pick a font: the new font_menu::picked
returns the choice and toggles stars, and never previews on the document.

Follow-up to #339 as suggested in #364.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01BRE8ZD3p8QgYctENhnJDz3
This commit is contained in:
DWAA1660andClaude Opus 5.5 authored and bflatastic committed 2026-10-08 07:32:11 +07:00
1 parent 18cbe007b8
commit 1d5e9cb239
4 files changed
+76 -21

No files matched your search

+6 -3
View File
@@ -4,10 +4,10 @@
use egui::Ui;
use serde_json::{Value, json};
use crate::VectorcraftApp;
use crate::state::Dialog;
use crate::theme::{self, Tokens};
use crate::widgets;
use crate::{VectorcraftApp, font_menu};
/// Open the dialog.
pub fn open(app: &mut VectorcraftApp) {
@@ -34,6 +34,7 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
let Some(mut d) = app.ui.dialog.clone() else { return };
let t = Tokens::get(ctx);
let list = fonts(app);
let sample = font_menu::sample_text(app);
let mut close = false;
let mut act: Option<&str> = None;
egui::Area::new(egui::Id::new("modal-dim")).order(egui::Order::Middle).fixed_pos(egui::pos2(0.0, 0.0)).show(ctx, |ui| {
@@ -71,9 +72,11 @@ pub fn show(app: &mut VectorcraftApp, ctx: &egui::Context) {
widgets::subheader(ui, tl!("Replace With Font"));
let fam = d.str("family");
ui.horizontal(|ui| {
if let Some(f) = widgets::font_dropdown(ui, "ff-family", &fam, 220.0) {
// Picks the font only: nothing is previewed on the document.
let pick = font_menu::font_menu(ui, "ff-family", &fam, 220.0, sample.as_deref(), font_menu::MenuLook::of(app));
if let Some((f, style)) = font_menu::picked(app, pick) {
d.fields.insert("family".into(), json!(f));
d.fields.insert("style".into(), json!(""));
d.fields.insert("style".into(), json!(style.unwrap_or_default()));
}
let styles = vectorcraft_text::FontDb::global().styles(&d.str("family"));
// "(closest)" is ours; the font's style names are shown as they are.
+35 -16
View File
@@ -1,10 +1,12 @@
//! The font family menu of the Character and Properties panels: a search field over every family
//! available, each row the family's name and a sample of the selected text set in it. The row
//! under the pointer or reached with ↑/↓ previews its font on the selected text (one live
//! interaction, nothing in the history); a click or Enter applies it as one step, Escape or
//! closing the menu puts the text back. A star marks a favourite family, and the ★ filter lists
//! only those. Preferences › Type › Enable in-menu font previews and Font Preview Size turn the
//! samples off or size the rows.
//! The font family menu of the Character, Properties and Glyphs panels and Find Font's Replace
//! With: a search field over every family available, each row the family's name and a sample of
//! the selected text set in it. In the Character and Properties panels the row under the pointer
//! or reached with ↑/↓ previews its font on the selected text (one live interaction, nothing in
//! the history); a click or Enter applies it as one step, Escape or closing the menu puts the text
//! back. The Glyphs panel and Find Font only pick a font ([`picked`]): nothing is previewed on the
//! document. A star marks a favourite family, and the ★ filter lists only those. Preferences ›
//! Type › Enable in-menu font previews and Font Preview Size turn the samples off or size the
//! rows.
use std::collections::HashMap;
use std::sync::Arc;
@@ -637,15 +639,7 @@ pub(crate) fn apply(app: &mut VectorcraftApp, ctx: &egui::Context, pick: FontPic
app.sync_views();
}
}
FontPick::Favorite(f) => {
let favs = &mut app.ui.favorite_fonts;
match favs.iter().position(|x| x.eq_ignore_ascii_case(&f)) {
Some(i) => {
favs.remove(i);
}
None => favs.push(f),
}
}
FontPick::Favorite(f) => toggle_favorite(app, f),
FontPick::Chosen(f, style) => {
if previewing {
app.session.cancel_interaction().ok();
@@ -658,6 +652,31 @@ pub(crate) fn apply(app: &mut VectorcraftApp, ctx: &egui::Context, pick: FontPic
}
}
/// Star `family`, or take its star off (any case).
fn toggle_favorite(app: &mut VectorcraftApp, family: String) {
let favs = &mut app.ui.favorite_fonts;
match favs.iter().position(|x| x.eq_ignore_ascii_case(&family)) {
Some(i) => {
favs.remove(i);
}
None => favs.push(family),
}
}
/// For a font menu that only picks a font and never changes the document (Find Font's Replace
/// With, the Glyphs panel): the family (and style) chosen, if any. A star is toggled here; the
/// highlighted row is not previewed.
pub(crate) fn picked(app: &mut VectorcraftApp, pick: Option<FontPick>) -> Option<(String, Option<String>)> {
match pick? {
FontPick::Chosen(f, style) => Some((f, style)),
FontPick::Favorite(f) => {
toggle_favorite(app, f);
None
}
FontPick::Preview(..) | FontPick::EndPreview => None,
}
}
/// The command that sets font `family` (and `style`, else the closest the family has) on what the
/// menus act on.
fn font_command(app: &VectorcraftApp, family: &str, style: Option<&str>) -> (&'static str, serde_json::Value) {
+6 -2
View File
@@ -198,8 +198,12 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
.color(t.text_dim),
);
ui.horizontal(|ui| {
if let Some(f) = widgets::font_dropdown(ui, "gl-family", &family, (w * 0.6).max(80.0)) {
let st = db.face(&f, "Regular").map_or_else(|| "Regular".into(), |face| face.style.clone());
// Picks the font the panel browses only: nothing is previewed on the document.
let sample = crate::font_menu::sample_text(app);
let look = crate::font_menu::MenuLook::of(app);
let pick = crate::font_menu::font_menu(ui, "gl-family", &family, (w * 0.6).max(80.0), sample.as_deref(), look);
if let Some((f, style)) = crate::font_menu::picked(app, pick) {
let st = style.unwrap_or_else(|| db.face(&f, "Regular").map_or_else(|| "Regular".into(), |face| face.style.clone()));
set_pstate(ui.ctx(), "gl-font", Some((f, st)));
}
let styles = db.styles(&family);
+29
View File
@@ -241,3 +241,32 @@ fn stars_mark_favourites_and_the_preferences_size_the_rows() {
let look = MenuLook::of(&app);
assert!(!look.samples && look.row > 24.0, "Font Preview Size and Enable in-menu font previews");
}
/// Find Font's Replace With and the Glyphs panel only pick a font: their menu's highlight never
/// previews on the document, a choice is just returned, and a star is still toggled.
#[test]
fn menus_that_only_pick_a_font_leave_the_document_alone() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.new", json!({"width": 300, "height": 200})).unwrap();
let id = app.session.execute("text.create", &json!({"x": 20, "y": 50, "text": "Gagaku", "font": "Inter"})).unwrap()["id"].as_u64().unwrap();
app.session.execute("select.set", &json!({"ids": [id]})).unwrap();
let font = |app: &VectorcraftApp| match &app.session.active().unwrap().doc.node(vectorcraft_doc::NodeId(id)).unwrap().kind {
NodeKind::Text(t) => t.first_style().font_family,
_ => String::new(),
};
let steps = |app: &VectorcraftApp| app.session.active().unwrap().history.undo.len();
let before = steps(&app);
use crate::font_menu::picked;
assert_eq!(picked(&mut app, None), None);
assert_eq!(picked(&mut app, Some(FontPick::Preview("Source Serif 4".into(), None))), None);
assert_eq!(picked(&mut app, Some(FontPick::EndPreview)), None);
assert_eq!(
picked(&mut app, Some(FontPick::Chosen("Source Serif 4".into(), Some("Bold".into())))),
Some(("Source Serif 4".to_string(), Some("Bold".to_string())))
);
assert_eq!((font(&app), steps(&app)), ("Inter".to_string(), before), "nothing previewed or applied on the text");
assert_eq!(picked(&mut app, Some(FontPick::Favorite("Inter".into()))), None);
assert_eq!(app.ui.favorite_fonts, ["Inter"], "a star is still toggled");
assert_eq!(picked(&mut app, Some(FontPick::Favorite("inter".into()))), None);
assert!(app.ui.favorite_fonts.is_empty());
}