mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-10-29 04:07:12 -06:00
i18n: the History panel translates the step names that contain a style's name
The History panel and Edit › Undo and Redo did not translate the steps
that apply or redefine a character or paragraph style, or redefine a
graphic style. The engine built their names with format! ("Apply Body",
"Redefine Body", "Redefine Graphic Style “S”"), and the panel looks a
name up as a whole, so no catalog row matched a name with a style's
name in it. A name cannot be matched against template rows instead: a
character style named Brush gives "Apply Brush", which is also the name
of the brush step.
The engine now names these steps with Session::edit_named, and each
such step keeps its template and the style's name
(HistoryEntry::named) through undo and redo. The History panel and
Edit › Undo and Redo show the template's row with the name in it
(panels::history::entry_name). The name stays as the document holds
it, and the Normal styles' names are shown in the interface language.
Every complete catalog has rows for Apply {name} and Redefine {name},
and Redefine Graphic Style “{name}” had one.
every_undo_label_is_translated also requires a row for each template,
and the only step name it allows to be built with format! is the
Liquify tools' "{} Tool".
Docs: docs/target-app-parity.md.
This commit is contained in:
@@ -380,7 +380,9 @@ fn undo(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
let doc = std::mem::replace(&mut st.doc, e.doc);
|
||||
let selection = std::mem::replace(&mut st.selection, Arc::unwrap_or_clone(e.selection));
|
||||
let selection = shared_selection(selection, st.history.redo.last());
|
||||
st.history.redo.push(crate::HistoryEntry::turned(e.label, doc, selection, &st.doc, costs));
|
||||
let mut turned = crate::HistoryEntry::turned(e.label, doc, selection, &st.doc, costs);
|
||||
turned.named = e.named;
|
||||
st.history.redo.push(turned);
|
||||
st.history.trim();
|
||||
st.revision += 1;
|
||||
st.selection.prune(&st.doc);
|
||||
@@ -403,7 +405,9 @@ fn redo(s: &mut Session, _: &Value) -> Result<Value> {
|
||||
let doc = std::mem::replace(&mut st.doc, e.doc);
|
||||
let selection = std::mem::replace(&mut st.selection, Arc::unwrap_or_clone(e.selection));
|
||||
let selection = shared_selection(selection, st.history.undo.last());
|
||||
st.history.undo.push(crate::HistoryEntry::turned(e.label, doc, selection, &st.doc, costs));
|
||||
let mut turned = crate::HistoryEntry::turned(e.label, doc, selection, &st.doc, costs);
|
||||
turned.named = e.named;
|
||||
st.history.undo.push(turned);
|
||||
st.history.trim();
|
||||
st.revision += 1;
|
||||
st.selection.prune(&st.doc);
|
||||
|
||||
@@ -456,7 +456,7 @@ fn style_redefine(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let look = GraphicStyle::of(String::new(), captured(n), n);
|
||||
let name = d.graphic_styles[i].name.clone();
|
||||
let chars = s.prefs.override_char_color;
|
||||
s.edit(&format!("Redefine Graphic Style \u{201c}{name}\u{201d}"), |d, _| {
|
||||
s.edit_named("Redefine Graphic Style “{name}”", &name, false, |d, _| {
|
||||
let id = d.graphic_style_id(i);
|
||||
let old = std::mem::replace(&mut d.graphic_styles[i], GraphicStyle { name: name.clone(), id, ..look });
|
||||
// Objects still linked follow the new definition; edited ones are unlinked (they keep
|
||||
|
||||
@@ -442,7 +442,7 @@ fn new(s: &mut Session, p: &Value, kind: Kind) -> Result<Value> {
|
||||
return Err(bad(&c, format!("a style named `{name}` already exists")));
|
||||
}
|
||||
let n2 = name.clone();
|
||||
s.edit(&format!("New {base}"), |d, _| {
|
||||
s.edit(if kind == Kind::Char { "New Character Style" } else { "New Paragraph Style" }, |d, _| {
|
||||
kind.defs_mut(d).push(TextStyleDef { name: n2, attrs });
|
||||
Ok(())
|
||||
})?;
|
||||
@@ -478,7 +478,7 @@ fn apply(s: &mut Session, p: &Value, kind: Kind) -> Result<Value> {
|
||||
Ok(st)
|
||||
};
|
||||
let count = ids.len();
|
||||
s.edit(&format!("Apply {name}"), |d, _| {
|
||||
s.edit_named("Apply {name}", &name, name == kind.normal(), |d, _| {
|
||||
for id in &ids {
|
||||
let Some(t) = text_mut(d, *id) else { continue };
|
||||
match kind {
|
||||
@@ -528,7 +528,7 @@ fn redefine_to(s: &mut Session, kind: Kind, name: &str, new_attrs: Map<String, V
|
||||
let d = &s.doc()?.doc;
|
||||
let old = kind.attrs(d, name).ok_or_else(|| bad(&kind.cmd(verb), format!("no style `{name}`")))?;
|
||||
let name = name.to_string();
|
||||
s.edit(&format!("Redefine {name}"), |d, _| {
|
||||
s.edit_named("Redefine {name}", &name, name == kind.normal(), |d, _| {
|
||||
match kind.defs_mut(d).iter_mut().find(|s| s.name == name) {
|
||||
Some(def) => def.attrs = new_attrs.clone(),
|
||||
None => kind.defs_mut(d).push(TextStyleDef { name: name.clone(), attrs: new_attrs.clone() }),
|
||||
@@ -664,6 +664,37 @@ mod tests {
|
||||
assert_eq!(list["styles"][1]["uses"], 1);
|
||||
}
|
||||
|
||||
/// Applying and redefining a style record a step named from a template with the style's name
|
||||
/// in it (`HistoryEntry::named`), and undo and redo keep it. The Normal styles' names are
|
||||
/// marked as names VectorCraft gives. A new style's step has a name of its own.
|
||||
#[test]
|
||||
fn style_steps_are_named_from_a_template() {
|
||||
let (mut s, id) = session_with_text("Hello");
|
||||
let last = |s: &Session| {
|
||||
let e = s.doc().unwrap().history.undo.last().unwrap();
|
||||
(e.label.clone(), e.named().map(|n| (n.template, n.name.clone(), n.built_in)))
|
||||
};
|
||||
let named = |template, name: &str, built_in| Some((template, name.to_string(), built_in));
|
||||
s.execute("charStyle.new", &json!({"name": "Body"})).unwrap();
|
||||
assert_eq!(last(&s), ("New Character Style".into(), None));
|
||||
s.execute("charStyle.apply", &json!({"name": "Body", "id": id})).unwrap();
|
||||
assert_eq!(last(&s), ("Apply Body".into(), named("Apply {name}", "Body", false)));
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
let redo = s.doc().unwrap().history.redo.last().unwrap().named().cloned();
|
||||
assert_eq!(redo.map(|n| n.label()).as_deref(), Some("Apply Body"));
|
||||
s.execute("edit.redo", &json!({})).unwrap();
|
||||
assert_eq!(last(&s), ("Apply Body".into(), named("Apply {name}", "Body", false)));
|
||||
s.execute("charStyle.apply", &json!({"name": NORMAL_CHAR, "id": id})).unwrap();
|
||||
assert_eq!(last(&s), (format!("Apply {NORMAL_CHAR}"), named("Apply {name}", NORMAL_CHAR, true)));
|
||||
s.execute("paraStyle.new", &json!({"name": "Lead"})).unwrap();
|
||||
assert_eq!(last(&s), ("New Paragraph Style".into(), None));
|
||||
s.execute("paraStyle.setAttrs", &json!({"name": "Lead", "attrs": {"direction": "rightToLeft"}})).unwrap();
|
||||
assert_eq!(last(&s), ("Redefine Lead".into(), named("Redefine {name}", "Lead", false)));
|
||||
// A step renamed since it was recorded is no longer named from the template.
|
||||
s.doc_mut().unwrap().history.undo.last_mut().unwrap().label = "Typing".into();
|
||||
assert_eq!(last(&s), ("Typing".into(), None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_from_selection_rename_delete_and_undo() {
|
||||
let (mut s, id) = session_with_text("Title");
|
||||
|
||||
Reference in New Issue
Block a user