mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-10 01:50:22 -06:00
Character Styles: Style Options sets Advanced Character Formats
Character Style Options and New Character Style get an Advanced Character Formats section with Horizontal Scale, Vertical Scale, Baseline Shift, Character Rotation and Language. Empty fields stay unset, an explicit 100%, 0 degrees or English: USA stays distinct from unset, and Reset Panel on the section clears only these five. Scales take 1% to 10000% and baseline shift -1296 pt to 1296 pt, as text.setRangeStyle does; rotation is stored from -180 to 180 degrees. Language sets the language the characters are shaped in; hyphenation and spelling do not change. A field still as it opened keeps its stored value without the range check, so a style captured from a scaled type object can be renamed. The same applies to Basic's Size and Leading minimum. charStyle.edit keeps a language tag that is not in the list when the style already has it, so a style read from a file with such a tag can be edited.
This commit is contained in:
@@ -475,7 +475,9 @@ fn name_param<'a>(p: &'a Value, kind: Kind, verb: &str) -> Result<&'a str> {
|
||||
str_param(p, "name").ok_or_else(|| bad(&kind.cmd(verb), "missing `name`"))
|
||||
}
|
||||
|
||||
fn attrs_param(p: &Value, kind: Kind, verb: &str) -> Result<Option<Map<String, Value>>> {
|
||||
/// The `attrs` of `p`, validated. `kept` holds the style's current attributes (`charStyle.edit`):
|
||||
/// a language tag that is not in the list, which a file can give a style, stays when `attrs` keeps it.
|
||||
fn attrs_param(p: &Value, kind: Kind, verb: &str, kept: Option<&Map<String, Value>>) -> Result<Option<Map<String, Value>>> {
|
||||
match p.get("attrs") {
|
||||
None | Some(Value::Null) => Ok(None),
|
||||
Some(Value::Object(m)) => {
|
||||
@@ -486,7 +488,9 @@ fn attrs_param(p: &Value, kind: Kind, verb: &str) -> Result<Option<Map<String, V
|
||||
Kind::Char => {
|
||||
let mut st = with_attrs(&CharStyle::default(), m, &c)?;
|
||||
// A language of the list, stored as its tag (as text.setFormat takes it).
|
||||
if let Some(l) = &st.language {
|
||||
if let Some(l) = &st.language
|
||||
&& kept.and_then(|k| k.get("language")) != m.get("language")
|
||||
{
|
||||
st.language = vectorcraft_doc::setup::char_language(l).map_err(|e| bad(&c, e))?.map(str::to_string);
|
||||
}
|
||||
char_attrs(&st)
|
||||
@@ -532,7 +536,7 @@ fn list(s: &mut Session, kind: Kind) -> Result<Value> {
|
||||
|
||||
fn new(s: &mut Session, p: &Value, kind: Kind) -> Result<Value> {
|
||||
let c = kind.cmd("new");
|
||||
let attrs = match attrs_param(p, kind, "new")? {
|
||||
let attrs = match attrs_param(p, kind, "new", None)? {
|
||||
Some(a) => a,
|
||||
None => match selection_attrs(s, p, kind)? {
|
||||
Some(a) => a,
|
||||
@@ -660,7 +664,7 @@ fn redefine(s: &mut Session, p: &Value, kind: Kind) -> Result<Value> {
|
||||
|
||||
fn set_attrs(s: &mut Session, p: &Value, kind: Kind) -> Result<Value> {
|
||||
let name = name_param(p, kind, "setAttrs")?.to_string();
|
||||
let attrs = attrs_param(p, kind, "setAttrs")?.ok_or_else(|| bad(&kind.cmd("setAttrs"), "missing `attrs`"))?;
|
||||
let attrs = attrs_param(p, kind, "setAttrs", None)?.ok_or_else(|| bad(&kind.cmd("setAttrs"), "missing `attrs`"))?;
|
||||
redefine_to(s, kind, &name, attrs, "setAttrs")
|
||||
}
|
||||
|
||||
@@ -668,9 +672,9 @@ fn edit_style(s: &mut Session, p: &Value, kind: Kind) -> Result<Value> {
|
||||
let c = kind.cmd("edit");
|
||||
let name = name_param(p, kind, "edit")?.to_string();
|
||||
let to = str_param(p, "to").ok_or_else(|| bad(&c, "missing `to`"))?.trim().to_string();
|
||||
let attrs = attrs_param(p, kind, "edit")?.ok_or_else(|| bad(&c, "missing `attrs`"))?;
|
||||
let d = &s.doc()?.doc;
|
||||
let old = kind.attrs(d, &name).ok_or_else(|| bad(&c, format!("no style `{name}`")))?;
|
||||
let attrs = attrs_param(p, kind, "edit", Some(&old))?.ok_or_else(|| bad(&c, "missing `attrs`"))?;
|
||||
if name == kind.normal() {
|
||||
return Err(bad(&c, format!("`{name}` can't be edited this way")));
|
||||
}
|
||||
|
||||
@@ -205,6 +205,46 @@ fn character_style_options_edit_applies_to_users_and_is_one_saved_undo_step() {
|
||||
assert_eq!(s.doc().unwrap().doc, changed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn character_style_options_edit_sets_the_advanced_character_formats() {
|
||||
let (mut s, id) = session_with_text("First second");
|
||||
s.execute("charStyle.new", &json!({"name":"Body", "attrs":{"h_scale":100.0,"rotation":0.0}})).unwrap();
|
||||
s.execute("charStyle.apply", &json!({"name":"Body", "id":id})).unwrap();
|
||||
s.execute("text.setRangeStyle", &json!({"id":id,"start":6,"end":12,"rotation":30.0})).unwrap();
|
||||
let before = s.doc().unwrap().doc.clone();
|
||||
let attrs = json!({"h_scale":80.0,"v_scale":120.0,"baseline_shift":-2.0,"rotation":15.0,"language":"German"});
|
||||
s.execute("charStyle.edit", &json!({"name":"Body","to":"Body","attrs":attrs})).unwrap();
|
||||
let changed = s.doc().unwrap().doc.clone();
|
||||
let def = changed.char_styles.iter().find(|d| d.name == "Body").unwrap();
|
||||
assert_eq!(def.attrs["language"], json!("de"));
|
||||
assert_eq!(
|
||||
runs(&s, id)
|
||||
.iter()
|
||||
.map(|r| (r.style.h_scale, r.style.v_scale, r.style.baseline_shift, r.style.rotation, r.style.language.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
[(80.0, 120.0, -2.0, 15.0, Some("de".to_string())), (80.0, 120.0, -2.0, 30.0, Some("de".to_string()))]
|
||||
);
|
||||
let saved = vectorcraft_format::load(&vectorcraft_format::save(&changed, false)).unwrap();
|
||||
assert_eq!(saved.char_styles.iter().find(|d| d.name == "Body").unwrap().attrs, def.attrs);
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().doc, before);
|
||||
s.execute("edit.redo", &json!({})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().doc, changed);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn character_style_options_edit_keeps_a_language_tag_from_a_file() {
|
||||
let (mut s, _) = session_with_text("Text");
|
||||
s.execute("charStyle.new", &json!({"name":"Body", "attrs":{}})).unwrap();
|
||||
if let Some(def) = std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc).char_styles.iter_mut().find(|d| d.name == "Body") {
|
||||
def.attrs.insert("language".into(), json!("x-file"));
|
||||
}
|
||||
s.execute("charStyle.edit", &json!({"name":"Body","to":"Body","attrs":{"size":18.0,"language":"x-file"}})).unwrap();
|
||||
assert_eq!(s.doc().unwrap().doc.char_styles.iter().find(|d| d.name == "Body").unwrap().attrs["language"], json!("x-file"));
|
||||
let error = s.execute("charStyle.edit", &json!({"name":"Body","to":"Body","attrs":{"language":"x-other"}})).unwrap_err().to_string();
|
||||
assert!(error.contains("unknown language"), "{error}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn editing_character_style_clears_removed_size_without_losing_local_override() {
|
||||
let (mut s, id) = session_with_text("One two");
|
||||
|
||||
Reference in New Issue
Block a user