Text styles: loading uses a loop the document has under the names found for it

When the document has a loop of styles and patterns under one set of names, its styles
were still matched one by one, so a style could be matched to another copy with the same
attributes and the pattern then compared with the wrong style name. Each definition of
such a loop is now the document's under the name found for it.
This commit is contained in:
dotCooCoo
2026-11-08 12:59:34 +07:00
committed by bflatastic
parent 127251f297
commit ea3fe16896
2 changed files with 31 additions and 2 deletions
+15 -1
View File
@@ -1044,24 +1044,38 @@ impl Clipboard {
// In a loop the document does not have all at once, every definition uses one that is
// added under a free name, so each one is added too.
let mut fresh = false;
// The names in the document of the loop being placed, when it has them all at once.
let mut matched: BTreeMap<(Res, String), String> = BTreeMap::new();
for (def, looped, starts) in steps {
fresh &= looped;
if !looped {
matched.clear();
}
// A loop of uses that the document has all at once is known under those names before
// its definitions are compared.
if let Some(group) = starts {
matched.clear();
match names_of_loop(d, group, &out.relink) {
Some(names) => {
for (def, name) in group.iter().zip(names) {
let (res, own) = def.key();
if own != name {
out.relink.renames.entry(res).or_default().insert(own.to_string(), name);
out.relink.renames.entry(res).or_default().insert(own.to_string(), name.clone());
}
matched.insert((res, own.to_string()), name);
}
fresh = false;
}
None => fresh = true,
}
}
// A definition of such a loop is the document's under the name found for it.
let (res, own) = def.key();
if let Some(name) = matched.get(&(res, own.to_string())) {
let p = if name == own { Placed::Existing } else { Placed::Reused(name.clone()) };
out.placed(res, own, &p);
continue;
}
match def {
Def::Style(r, def) => {
let mut s = def.clone();
+16 -1
View File
@@ -945,7 +945,8 @@ fn a_style_and_the_pattern_that_uses_it_load_together() {
/// A style and the pattern it paints with, whose type is set in that style, are both added when
/// the document has a style of that name and attributes but another pattern of that name: the
/// copy of the style paints with the copy of the pattern, and a second load adds nothing.
/// copy of the style paints with the copy of the pattern, and a second load adds nothing. A loop the
/// document has is used under the names it has together.
#[test]
fn a_style_in_a_loop_is_not_matched_to_a_style_that_paints_with_another_pattern() {
let (mut src, id) = session_with_text("Tile");
@@ -977,6 +978,20 @@ fn a_style_in_a_loop_is_not_matched_to_a_style_that_paints_with_another_pattern(
let r = s.execute("charStyle.load", &load).unwrap();
assert_eq!(r["added"], json!([]), "{r}");
assert_eq!(s.doc().unwrap().doc, d);
// With the loop's style renamed "Accent 3" and an "Accent 2" listed before it that also paints
// with "Dots 2", the loop is still found as "Accent 3" and "Dots 2", the names it has together.
let doc = std::sync::Arc::make_mut(&mut s.doc_mut().unwrap().doc);
let at = doc.char_styles.iter().position(|c| c.name == "Accent 2").unwrap();
doc.char_styles[at].name = "Accent 3".into();
let copy = TextStyleDef { name: "Accent 2".into(), attrs: doc.char_styles[at].attrs.clone() };
doc.char_styles.insert(at, copy);
let tile = doc.patterns.iter_mut().find(|p| p.name == "Dots 2").unwrap();
let NodeKind::Text(text) = &mut std::sync::Arc::make_mut(&mut tile.art[0]).kind else { panic!("pattern art should be type") };
text.runs[0].style.style_name = Some("Accent 3".into());
let d = s.doc().unwrap().doc.clone();
let r = s.execute("charStyle.load", &load).unwrap();
assert_eq!(r, json!({"added": [], "existing": ["Accent 3"], "skipped": []}));
assert_eq!(s.doc().unwrap().doc, d);
}
/// A brush in a loaded style's pattern whose art sets type in a style renamed on the way in