diff --git a/crates/ui-egui/src/find_font.rs b/crates/ui-egui/src/find_font.rs index 743b57ac1..9d90d58e2 100644 --- a/crates/ui-egui/src/find_font.rs +++ b/crates/ui-egui/src/find_font.rs @@ -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. diff --git a/crates/ui-egui/src/font_menu.rs b/crates/ui-egui/src/font_menu.rs index b4a581c22..4c6be54e6 100644 --- a/crates/ui-egui/src/font_menu.rs +++ b/crates/ui-egui/src/font_menu.rs @@ -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) -> Option<(String, Option)> { + 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) { diff --git a/crates/ui-egui/src/panels/glyphs.rs b/crates/ui-egui/src/panels/glyphs.rs index 790d5f2ea..da2230187 100644 --- a/crates/ui-egui/src/panels/glyphs.rs +++ b/crates/ui-egui/src/panels/glyphs.rs @@ -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); diff --git a/crates/ui-egui/src/tests_font_menu.rs b/crates/ui-egui/src/tests_font_menu.rs index 0dfc39421..a0d4a0744 100644 --- a/crates/ui-egui/src/tests_font_menu.rs +++ b/crates/ui-egui/src/tests_font_menu.rs @@ -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()); +}