Merge fix/666

This commit is contained in:
bflatastic
2026-10-17 15:20:30 +07:00
6 changed files with 44 additions and 11 deletions
+1 -1
View File
@@ -332,7 +332,7 @@ pub fn specs() -> Vec<CommandSpec> {
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")?;
+2 -2
View File
@@ -357,8 +357,8 @@ pub struct DocState {
versions_changed: bool,
/// A version shown in place of the document (read-only).
pub preview: Option<VersionPreview>,
/// 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<Arc<Document>>,
/// The document as of the last version saved or restored (none yet): autosaves, restores and
/// merges only add a checkpoint for changes since.
@@ -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();
+2 -2
View File
@@ -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.
+1 -1
View File
@@ -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;
+13 -5
View File
@@ -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<String>,
/// The family Up and Down are on (Enter picks it).
pub highlighted: Option<String>,
/// The family the pointer is over (the caller previews it on the canvas).
pub hovered: Option<String>,
}
/// 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)]