Merge pull request #12334 from rmsff/fix/vertical-type-history-name-12058

fix(engine): new vertical text is the history state "New Vertical Type Layer" (#12058)
This commit is contained in:
David Mann authored and GitHub committed 2026-11-11 00:53:57 +01:00
commit 18ec9b1dc8
2 files changed
+79 -2

No files matched your search

+8 -2
View File
@@ -953,7 +953,8 @@ pub(crate) fn new_text_layer(s: &Session, p: &Value, cmd: &str) -> Result<TextLa
Ok(t)
}
/// The name of the history state a new type layer makes: what `type.create` records, and what the
/// The name of the history state a new horizontal type layer makes: what `type.create` records
/// (vertical text is "New Vertical Type Layer", #12058), and what the
/// Type tool's commit keeps in the modes [`commits_as_selection`] names (#12041).
pub const NEW_TYPE_LAYER_LABEL: &str = "New Type Layer";
@@ -1020,7 +1021,9 @@ pub fn specs() -> Vec<CommandSpec> {
let mut t = new_text_layer(s, p, "type.create")?;
let given = p.get("name").and_then(Value::as_str).map(str::to_string);
let name = given.clone().unwrap_or_else(|| layer_name(&t.text));
let id = s.edit(NEW_TYPE_LAYER_LABEL, |doc, active| {
// The reference application names new vertical text after its tool (#12058).
let label = if t.orientation == Orientation::Vertical { "New Vertical Type Layer" } else { NEW_TYPE_LAYER_LABEL };
let id = s.edit(label, |doc, active| {
refresh(doc, &mut t);
let id = crate::layer_place::insert_new(doc, *active, Layer::new(name, LayerContent::Text(t)), crate::layer_place::Place::AutoEditable);
*active = Some(id);
@@ -1306,3 +1309,6 @@ fn step_size(s: &mut Session, p: &Value, up: bool, default_by: f32) -> Result<Va
#[cfg(test)]
mod tests;
#[cfg(test)]
#[path = "type_vertical_history_name_tests.rs"]
mod vertical_history_name_tests;
@@ -0,0 +1,71 @@
//! #12058: new vertical text is the history state "New Vertical Type Layer", point and paragraph
//! text alike; horizontal text stays "New Type Layer", and a committed Type Mask session, vertical
//! or not, still leaves one "Type Mask" state.
use serde_json::{Value, json};
use crate::Session;
fn session() -> Session {
let mut s = Session::new();
s.execute("file.new", json!({"width": 300, "height": 300})).unwrap();
s
}
fn entries(s: &Session) -> Vec<String> {
s.active().unwrap().history.entries()
}
/// The history states `type.create` adds with `params`, and the layer it makes.
fn create(params: Value) -> (Vec<String>, u64) {
let mut s = session();
let before = entries(&s).len();
let layer = s.execute("type.create", params).unwrap()["layer"].as_u64().unwrap();
(entries(&s)[before..].to_vec(), layer)
}
#[test]
fn vertical_text_is_a_new_vertical_type_layer() {
for (what, params) in [
("point", json!({"text": "AB", "x": 150, "y": 40, "orientation": "vertical"})),
("paragraph", json!({"text": "AB", "box": [100, 20, 60, 200], "orientation": "vertical"})),
] {
assert_eq!(create(params).0, ["New Vertical Type Layer"], "{what}");
}
}
#[test]
fn horizontal_text_is_still_a_new_type_layer() {
for (what, params) in [
("point", json!({"text": "AB", "x": 40, "y": 120})),
("point, horizontal", json!({"text": "AB", "x": 40, "y": 120, "orientation": "horizontal"})),
("paragraph", json!({"text": "AB", "box": [20, 20, 200, 60]})),
("paragraph, horizontal", json!({"text": "AB", "box": [20, 20, 200, 60], "orientation": "horizontal"})),
] {
assert_eq!(create(params).0, ["New Type Layer"], "{what}");
}
}
/// The Type Mask tools' commands: a hidden working layer from `type.create`, then
/// `type.createSelection` removing it, all coalesced into the session's one step.
#[test]
fn a_type_mask_session_is_still_one_type_mask_state() {
for orientation in ["horizontal", "vertical"] {
let mut s = session();
let before = entries(&s).len();
let key = json!("type-mask-session");
let created = json!({"text": "", "x": 150, "y": 40, "orientation": orientation, "coalesce": key});
let layer = s.execute("type.create", created).unwrap()["layer"].as_u64().unwrap();
s.execute("layer.setProps", json!({"layer": layer, "visible": false, "coalesce": key})).unwrap();
s.execute("type.edit", json!({"layer": layer, "text": "AB", "coalesce": key})).unwrap();
s.execute("type.createSelection", json!({"layer": layer, "removeLayer": true, "coalesce": key})).unwrap();
assert_eq!(entries(&s)[before..], ["Type Mask"], "{orientation}");
}
// Without a layer, `type.createSelection` makes its text itself: the same one state.
for orientation in ["horizontal", "vertical"] {
let mut s = session();
let before = entries(&s).len();
s.execute("type.createSelection", json!({"text": "AB", "x": 150, "y": 40, "size": 48, "orientation": orientation})).unwrap();
assert_eq!(entries(&s)[before..], ["Type Mask"], "{orientation}, no layer");
}
}