diff --git a/crates/engine/src/cmd/panelcmds.rs b/crates/engine/src/cmd/panelcmds.rs index 7934dbe69..68449f5eb 100644 --- a/crates/engine/src/cmd/panelcmds.rs +++ b/crates/engine/src/cmd/panelcmds.rs @@ -318,6 +318,22 @@ fn spacing_param(p: &Value, key: &str, c: &str) -> Result; 3 fn set_format(s: &mut Session, p: &Value) -> Result { const C: &str = "text.setFormat"; let ids = super::typecmd::text_targets(s, p, C)?; + check_switches( + p, + &[ + "underline", + "strikethrough", + "allCaps", + "proportionalMetrics", + "tateChuYoko", + "standardVerticalRoman", + "noBreak", + "hyphenate", + "hyphenateCapitalized", + "romanHangingPunctuation", + ], + C, + )?; let num = |k: &str| p.get(k).and_then(Value::as_f64); let flag = |k: &str| p.get(k).and_then(Value::as_bool); let kerning = match p.get("kerning") { diff --git a/crates/engine/src/tests/textedit.rs b/crates/engine/src/tests/textedit.rs index 025cf6d30..178f8f528 100644 --- a/crates/engine/src/tests/textedit.rs +++ b/crates/engine/src/tests/textedit.rs @@ -325,6 +325,16 @@ fn type_commands_refuse_a_number_or_boolean_given_as_text() { ("text.setRangeStyle", json!({"id": id.0, "allCaps": 1}), "allCaps"), ("text.setRangeStyle", json!({"id": id.0, "noBreak": "true"}), "noBreak"), ("text.setRangeStyle", json!({"id": id.0, "size": 30, "tateChuYoko": 1}), "tateChuYoko"), + ("text.setFormat", json!({"ids": [id.0], "underline": "true"}), "underline"), + ("text.setFormat", json!({"ids": [id.0], "strikethrough": 1}), "strikethrough"), + ("text.setFormat", json!({"ids": [id.0], "allCaps": "false"}), "allCaps"), + ("text.setFormat", json!({"ids": [id.0], "proportionalMetrics": 1}), "proportionalMetrics"), + ("text.setFormat", json!({"ids": [id.0], "tateChuYoko": "true"}), "tateChuYoko"), + ("text.setFormat", json!({"ids": [id.0], "standardVerticalRoman": "true"}), "standardVerticalRoman"), + ("text.setFormat", json!({"ids": [id.0], "rotation": 10, "noBreak": 1}), "noBreak"), + ("text.setFormat", json!({"ids": [id.0], "hyphenate": "false"}), "hyphenate"), + ("text.setFormat", json!({"ids": [id.0], "hyphenateCapitalized": 0}), "hyphenateCapitalized"), + ("text.setFormat", json!({"ids": [id.0], "romanHangingPunctuation": "true"}), "romanHangingPunctuation"), ("text.getRange", json!({"id": id.0, "effective": "true"}), "effective"), ("type.opticalMarginAlignment", json!({"on": "true"}), "on"), ("type.systemLayout", json!({"on": "true"}), "on"), @@ -338,12 +348,13 @@ fn type_commands_refuse_a_number_or_boolean_given_as_text() { assert!(wrong.is_empty(), "not refused: {wrong:#?}"); let style = json!({"id": id.0, "size": 30, "underline": true, "tracking": null, "start": null, "noBreak": true}); s.execute("text.setRangeStyle", &style).unwrap(); + s.execute("text.setFormat", &json!({"ids": [id.0], "strikethrough": true, "hyphenate": null})).unwrap(); s.execute("type.size.increase", &json!({"by": null})).unwrap(); s.execute("type.opticalMarginAlignment", &json!({"on": true})).unwrap(); s.execute("type.ruby", &json!({"text": "a", "size": null, "overhang": false})).unwrap(); let t = obj(&s, id); let st = &t.runs[0].style; - assert!(st.underline && st.no_break && t.optical_margin && st.size > 30.0, "{st:?}"); + assert!(st.underline && st.strikethrough && st.no_break && t.optical_margin && st.size > 30.0, "{st:?}"); assert!(!t.ruby.is_empty() && t.ruby.iter().all(|r| !r.overhang && r.size_percent == 50.0), "{:?}", t.ruby); } diff --git a/crates/ui-egui/src/panels/character.rs b/crates/ui-egui/src/panels/character.rs index f00c33d70..db01f2e38 100644 --- a/crates/ui-egui/src/panels/character.rs +++ b/crates/ui-egui/src/panels/character.rs @@ -419,11 +419,12 @@ pub(crate) fn highlight(app: &mut VectorcraftApp, field: Field) -> Result Result { - let (s, _) = text_style(app).ok_or("select some type first")?; - format(app, json!({"underline": !s.underline})); - Ok(json!(!s.underline)) + text_style(app).ok_or("select some type first")?; + let on = shared(app, |s| s.underline) != Some(true); + format(app, json!({"underline": on})); + Ok(json!(on)) } /// `text.setRangeStyle` with `p` on the range the Type tool has selected (the whole text when @@ -946,15 +947,19 @@ fn attributes(app: &mut VectorcraftApp, ui: &mut Ui) { } ui.add_space(4.0); ui.horizontal(|ui| { + // On only when every character the panel acts on has it; a mixed selection turns it on. + let all_caps = shared(app, |s| s.all_caps) == Some(true); let small = shared(app, |s| s.small_caps.is_some()) == Some(true); let position = shared(app, |s| s.position).map(|p| p.id()).unwrap_or(""); + let underline = shared(app, |s| s.underline) == Some(true); + let strike = shared(app, |s| s.strikethrough) == Some(true); for (kind, tip, on, change) in [ - (Glyph::AllCaps, tl!("All Caps"), s.all_caps, json!({"allCaps": !s.all_caps})), + (Glyph::AllCaps, tl!("All Caps"), all_caps, json!({"allCaps": !all_caps})), (Glyph::SmallCaps, tl!("Small Caps"), small, json!({"smallCaps": !small})), (Glyph::Super, tl!("Superscript"), position == "superscript", position_toggle(position, "superscript")), (Glyph::Sub, tl!("Subscript"), position == "subscript", position_toggle(position, "subscript")), - (Glyph::Underline, tl!("Underline"), s.underline, json!({"underline": !s.underline})), - (Glyph::Strike, tl!("Strikethrough"), s.strikethrough, json!({"strikethrough": !s.strikethrough})), + (Glyph::Underline, tl!("Underline"), underline, json!({"underline": !underline})), + (Glyph::Strike, tl!("Strikethrough"), strike, json!({"strikethrough": !strike})), ] { if style_toggle(ui, kind, tip, on) { format(app, change); @@ -1093,8 +1098,7 @@ fn digits_items(app: &mut VectorcraftApp, ui: &mut Ui, digits: Option