OpenType: a menu choice at a Type tool caret reaches every character

At a caret the panel sends text.setRangeStyle with no range, which
styles all of the text, but the check that skips a choice that changes
nothing read only the insertion style. When the characters at the caret
already had the chosen feature and others did not, the choice did
nothing. The check now covers every character of the text at a caret,
so the choice applies, and a choice that changes no character still
adds no undo step.
This commit is contained in:
dotCooCoo
2026-11-04 22:29:18 +07:00
committed by bflatastic
parent e0ebc13a2e
commit 83f5b7eead
+34 -3
View File
@@ -4,6 +4,7 @@
use egui::Ui;
use serde_json::json;
use vectorcraft_doc::{CharStyle, NodeKind};
use vectorcraft_text::{EXTRA_FEATURES, FontDb, OtFeatures, StylisticSet};
use super::character::{shared, text_editing, text_style};
@@ -135,14 +136,22 @@ impl Change {
}
/// Make `change` to the features of each character the panel acts on, from the character's own
/// features (`featureChanges`), unless it changes none of them.
/// features (`featureChanges`), unless it changes none of them. At a Type tool caret the
/// command styles all the text, so every character of the text is checked.
fn edit(app: &mut VectorcraftApp, change: Change) {
let unchanged = shared(app, |s| {
let same = |s: &CharStyle| {
let before = OtFeatures::from_tags(s.features.iter().map(String::as_str));
let mut after = before;
change.apply(&mut after);
after == before
});
};
let unchanged = match text_editing(app) {
Some((id, a, b)) if b <= a => match app.session.active().and_then(|st| st.doc.node(id)).map(|n| &n.kind) {
Some(NodeKind::Text(t)) => Some(t.runs.iter().all(|r| same(&r.style))),
_ => None,
},
_ => shared(app, same),
};
if unchanged != Some(true) {
run_features(app, "featureChanges", OtFeatures::changes(|f| change.apply(f)));
}
@@ -525,4 +534,26 @@ mod tests {
]
);
}
/// At a caret whose insertion style is already a denominator, Denominator still reaches the
/// characters the command styles at a caret (all of them) in one undo step; chosen again once
/// every character has it, it adds no undo step.
#[test]
fn caret_with_the_same_position_still_applies_it() {
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
app.session.execute("file.new", &json!({"width": 100, "height": 100})).unwrap();
app.session.select_tool("type", Default::default()).unwrap();
let id = app.session.execute("text.create", &json!({"x": 10, "y": 40, "text": "1/23"})).unwrap()["id"].clone();
app.session.execute("text.setRangeStyle", &json!({"id": id, "start": 0, "end": 1, "features": ["dnom"]})).unwrap();
app.session.apply_actions(vec![vectorcraft_tools::Action::Notify("text.editNew".into())]).unwrap();
app.session.set_tool_option("select", &json!({"start": 1, "end": 1}));
assert_eq!(text_editing(&app).map(|(_, a, b)| (a, b)), Some((1, 1)));
assert_eq!(shown_position(&app), Some(4));
let undo = undo_steps(&app);
edit(&mut app, Change::Position(4));
assert_eq!(undo_steps(&app), undo + 1);
assert_eq!(run_features(&mut app, &id, 4), [("1/23".to_string(), json!(["dnom"]))]);
edit(&mut app, Change::Position(4));
assert_eq!(undo_steps(&app), undo + 1);
}
}