mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-26 03:16:53 -06:00
Hovering a family in the Typography font dropdown previews it on the canvas via text.preview; closing without picking restores the original, clicking applies it as one undo step.
68 lines
2.9 KiB
Rust
68 lines
2.9 KiB
Rust
//! Live text previews: what a text command would do, shown on the canvas without an undo step —
|
|
//! 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.
|
|
|
|
use std::sync::Arc;
|
|
|
|
use serde_json::Value;
|
|
|
|
use crate::{EngineError, Result, Session, bad, find_command, guard, normalize};
|
|
|
|
/// The commands that show or end a preview (they don't end the one showing).
|
|
pub const COMMANDS: [&str; 2] = ["text.preview", "text.endPreview"];
|
|
|
|
/// What can be previewed: text styles, font styles and axis values.
|
|
pub const PREVIEWABLE: [&str; 4] = ["style.apply", "library.applyStyle", "text.setVariations", "text.style"];
|
|
|
|
/// Show `command params` as a live preview (in place of an earlier one).
|
|
pub fn show(s: &mut Session, command: &str, params: &Value) -> Result<()> {
|
|
let cmd = "text.preview";
|
|
if !PREVIEWABLE.contains(&command) {
|
|
return Err(bad(cmd, format!("`{command}` can't be previewed (one of: {})", PREVIEWABLE.join(", "))));
|
|
}
|
|
let spec = find_command(command).ok_or_else(|| EngineError::UnknownCommand(command.to_string()))?;
|
|
end(s);
|
|
let ds = s.ds()?;
|
|
if ds.preview.is_some() {
|
|
return Err(EngineError::Disabled(cmd.into(), crate::PREVIEW_READ_ONLY.into()));
|
|
}
|
|
let (base, selection, text_edit, page, uid) = (ds.doc.clone(), ds.selection.clone(), ds.text_edit.clone(), ds.page, ds.uid);
|
|
let result = guard::run(command, || (spec.run)(s, params))
|
|
// Library copies and instance structure stay as they are, in previews too.
|
|
.and_then(|v| s.check_instance_structure(command, &Some((base.clone(), selection.clone(), page, uid))).map(|()| v));
|
|
let ds = s.ds_mut()?;
|
|
// A preview changes what's shown, never the selection or the text being edited.
|
|
ds.selection = selection;
|
|
ds.text_edit = text_edit;
|
|
match result {
|
|
Ok(_) if !Arc::ptr_eq(&ds.doc, &base) => {
|
|
normalize(ds.doc_mut(), &base);
|
|
ds.text_preview = Some(base);
|
|
ds.revision += 1;
|
|
Ok(())
|
|
}
|
|
Ok(_) => Ok(()),
|
|
Err(e) => {
|
|
ds.doc = base;
|
|
Err(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// End the live preview, if any: the document is back as it was. Returns whether one was showing.
|
|
pub fn end(s: &mut Session) -> bool {
|
|
let Ok(ds) = s.ds_mut() else { return false };
|
|
let Some(base) = ds.text_preview.take() else { return false };
|
|
ds.doc = base;
|
|
ds.revision += 1;
|
|
true
|
|
}
|
|
|
|
/// Whether a live text preview is showing.
|
|
pub fn active(s: &Session) -> bool {
|
|
s.ds().is_ok_and(|d| d.text_preview.is_some())
|
|
}
|