diff --git a/crates/engine/src/cmd/text.rs b/crates/engine/src/cmd/text.rs index 14ea1762..3b861895 100644 --- a/crates/engine/src/cmd/text.rs +++ b/crates/engine/src/cmd/text.rs @@ -332,7 +332,7 @@ pub fn specs() -> Vec { spec!( "text.preview", "Preview text change", - "{command: style.apply|library.applyStyle|text.setVariations|text.style, params} shows what the command would do on the canvas without an undo step (a hovered text style or named instance, a dragged axis slider), in place of an earlier preview; text.endPreview or any other command ends it, so running the command for real commits it as one undo step", + "{command: style.apply|library.applyStyle|text.setVariations|text.style, params} shows what the command would do on the canvas without an undo step (a hovered font family or style, text style or named instance, a dragged axis slider), in place of an earlier preview; text.endPreview or any other command ends it, so running the command for real commits it as one undo step", false, |s, p| { let c = req_string("text.preview", p, "command")?; diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 6e0e8b12..987fe103 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -357,8 +357,8 @@ pub struct DocState { versions_changed: bool, /// A version shown in place of the document (read-only). pub preview: Option, - /// The document under a live text preview (a hovered text style or font instance, a dragged - /// axis slider): `doc` shows the preview until [`textpreview::end`] puts this back. + /// The document under a live text preview (a hovered font family or style, text style or + /// font instance, a dragged axis slider): `doc` shows the preview until [`textpreview::end`] puts this back. pub text_preview: Option>, /// The document as of the last version saved or restored (none yet): autosaves, restores and /// merges only add a checkpoint for changes since. diff --git a/crates/engine/src/text_style_picker_tests.rs b/crates/engine/src/text_style_picker_tests.rs index 37c7dc5c..6ee8cc2c 100644 --- a/crates/engine/src/text_style_picker_tests.rs +++ b/crates/engine/src/text_style_picker_tests.rs @@ -212,6 +212,31 @@ fn previews_while_editing_keep_the_edit() { assert!(links(&s, t).iter().all(Option::is_none)); } +#[test] +fn previews_show_a_hovered_font_family() { + let mut s = new_session(); + let t = text(&mut s, "Preview me"); + s.ds_mut().unwrap().mark_saved(); + let before = s.doc().unwrap().clone(); + let undo = s.ds().unwrap().history.undo.len(); + // Hovering a font family shows it on the canvas: no undo step, not dirty. + s.execute("text.preview", &json!({"command": "text.style", "params": {"ids": [t], "fontFamily": "Arial"}})).unwrap(); + assert_eq!(data(&s, t).style_at(0).font_family, "Arial"); + assert_eq!(s.ds().unwrap().history.undo.len(), undo); + assert!(!s.ds().unwrap().is_dirty()); + // Moving to another family replaces the preview, from the document itself. + s.execute("text.preview", &json!({"command": "text.style", "params": {"ids": [t], "fontFamily": "Georgia"}})).unwrap(); + assert_eq!(data(&s, t).style_at(0).font_family, "Georgia"); + // Closing without picking puts the document back as it was. + assert_eq!(s.execute("text.endPreview", &json!({})).unwrap()["ended"], json!(true)); + assert_eq!(*s.doc().unwrap(), before); + // Clicking commits it as one undo step. + s.execute("text.style", &json!({"ids": [t], "fontFamily": "Georgia"})).unwrap(); + assert_eq!(data(&s, t).style_at(0).font_family, "Georgia"); + assert_eq!(s.ds().unwrap().history.undo.len(), undo + 1); + assert!(s.ds().unwrap().text_preview.is_none()); +} + #[test] fn previews_refuse_what_they_cant_show() { let mut s = new_session(); diff --git a/crates/engine/src/textpreview.rs b/crates/engine/src/textpreview.rs index 507eccc3..da101d5e 100644 --- a/crates/engine/src/textpreview.rs +++ b/crates/engine/src/textpreview.rs @@ -1,6 +1,6 @@ //! Live text previews: what a text command would do, shown on the canvas without an undo step — -//! while the pointer is over a text style in the style browser or a font's named instance, or -//! while a variable font axis slider is dragged. The document under the preview is kept and comes +//! while the pointer is over a font family or style, a text style in the style browser or a +//! font's named instance, or while a variable font axis slider is dragged. The document under the preview is kept and comes //! back when the preview ends ([`end`], `text.endPreview`). Every other command ends it first, so //! committing (running the command for real) applies to the document itself as one undo step, and //! Escape just ends the preview. diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index 20a8a77c..1199afea 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -719,7 +719,7 @@ impl FigApp { } /// Show what `command params` would do on the canvas as a live preview (`text.preview`) while - /// a text style, a named instance or an axis slider is hovered or dragged. Call it every frame + /// a font family, font style, text style, named instance or axis slider is hovered or dragged. Call it every frame /// the preview should stay: it ends on the first frame nothing asks for it. pub fn preview_text(&mut self, ctx: &egui::Context, command: &str, params: Value) { self.ui.text_preview_wanted = true; diff --git a/crates/ui-egui/src/typography.rs b/crates/ui-egui/src/typography.rs index 78552dcb..7740a9e0 100644 --- a/crates/ui-egui/src/typography.rs +++ b/crates/ui-egui/src/typography.rs @@ -186,9 +186,10 @@ fn font_rows(app: &mut FigApp, ui: &mut Ui, ids: &[NodeId], styles: &[TextStyle] remember_family(ui.ctx(), &f); apply_style(app, "fontFamily", json!(f)); ui.close(); - } else if let Some(f) = pick.highlighted { - // The canvas shows the family the arrow keys are on. - app.preview_text(ui.ctx(), "text.style", text_style_params(ids, range, json!({"fontFamily": f}))); + } else if let Some(h) = pick.hovered.or(pick.highlighted) { + // The canvas shows the hovered family (or the one the arrow keys are on); + // closing without picking ends the preview. + app.preview_text(ui.ctx(), "text.style", text_style_params(ids, range, json!({"fontFamily": h}))); } }); }); @@ -1161,9 +1162,12 @@ pub(crate) struct FontPick { pub picked: Option, /// The family Up and Down are on (Enter picks it). pub highlighted: Option, + /// The family the pointer is over (the caller previews it on the canvas). + pub hovered: Option, } /// The searchable family list: click a family, or step through it with Up and Down and press Enter. +/// Hovering a family previews it on the canvas (closing without picking restores the original). pub(crate) fn font_picker(ui: &mut Ui, current: Option<&str>, just_opened: bool) -> FontPick { let t = Tokens::get(ui.ctx()); let q_id = Id::new("font_query"); @@ -1248,10 +1252,11 @@ pub(crate) fn font_picker(ui: &mut Ui, current: Option<&str>, just_opened: bool) PickRow::Family(f) => Some(f.clone()), PickRow::Header(_) => None, }); - return FontPick { picked: hi.and_then(family).or(first), highlighted: None }; + return FontPick { picked: hi.and_then(family).or(first), highlighted: None, hovered: None }; } let mut budget = 4; let mut picked = None; + let mut hovered_family = None; let mut area = egui::ScrollArea::vertical().max_height(LIST_HEIGHT).auto_shrink([false, true]); if let Some(y) = offset { area = area.vertical_scroll_offset(y); @@ -1297,12 +1302,15 @@ pub(crate) fn font_picker(ui: &mut Ui, current: Option<&str>, just_opened: bool) if resp.clicked() { picked = Some(f.clone()); } + if resp.hovered() { + hovered_family = Some(f.clone()); + } } } } }); ui.data_mut(|d| d.insert_temp(hi_id.with("view"), (shown.state.offset.y, shown.inner_rect.height()))); - FontPick { highlighted: hi.and_then(family), picked } + FontPick { highlighted: hi.and_then(family), picked, hovered: hovered_family } } #[cfg(test)]