Merge pull request #5893 from dotCooCoo/fix/empty-thread-frame-style

Text: empty later frames preserve an edited paragraph style
This commit is contained in:
BFlat
2026-11-06 15:21:27 +08:00
committed by GitHub
3 changed files with 43 additions and 1 deletions
+19
View File
@@ -20,6 +20,25 @@ fn text(s: &Session, id: u64) -> String {
}
}
#[test]
fn empty_later_frames_keep_the_first_frames_edited_paragraph_style() {
let mut s = Session::new();
s.execute("file.new", &json!({"width": 600, "height": 300})).unwrap();
let a =
s.execute("text.create", &json!({"x": 10, "y": 10, "text": "First", "area": {"width": 150, "height": 60}})).unwrap()["id"].as_u64().unwrap();
let b = s.execute("shape.rectangle", &json!({"x": 200, "y": 10, "width": 150, "height": 60})).unwrap()["id"].as_u64().unwrap();
let c = s.execute("shape.rectangle", &json!({"x": 400, "y": 10, "width": 150, "height": 60})).unwrap()["id"].as_u64().unwrap();
s.execute("select.set", &json!({"ids": [a, b, c]})).unwrap();
s.execute("text.thread.create", &json!({})).unwrap();
assert_eq!([text(&s, a), text(&s, b), text(&s, c)], ["First", "", ""]);
s.execute("text.setFormat", &json!({"ids": [a], "hyphenate": true})).unwrap();
let hyphenates = |id| match &s.doc().unwrap().doc.node(NodeId(id)).unwrap().kind {
NodeKind::Text(t) => t.para_at(0).hyphenate,
_ => panic!("not text"),
};
assert_eq!([hyphenates(a), hyphenates(b), hyphenates(c)], [true, true, true]);
}
#[test]
fn thread_flows_reflows_on_edit_and_survives_delete() {
let (mut s, a, b) = setup();
+5 -1
View File
@@ -34,7 +34,11 @@ pub fn story_paras(frames: &[&TextObject], later_wins: &[bool]) -> Vec<ParaStyle
}
out.extend(styles);
let text = f.plain_text();
prev_empty_tail = text.is_empty() || text.ends_with('\n') || out.is_empty();
// An empty frame has no paragraph boundary of its own. Keep the preceding frame's
// boundary state so another empty frame cannot replace a paragraph's edited style.
if !text.is_empty() {
prev_empty_tail = text.ends_with('\n') || out.is_empty();
}
}
out
}
+19
View File
@@ -144,6 +144,25 @@ fn paragraph_styles_flow_with_their_paragraphs() {
assert_eq!(story_paras(&[&fa, &fb], &[false, false])[0].justify, Left);
}
#[test]
fn empty_frames_do_not_replace_an_earlier_paragraph_style() {
let mut first = frame(0.0);
first.runs = vec![TextRun { text: "First".into(), style: CharStyle::default(), inline: None }];
first.set_all_paras(ParaStyle { justify: Justify::Right, ..Default::default() });
let middle = frame(200.0);
let last = frame(400.0);
assert_eq!(story_paras(&[&first, &middle, &last], &[true, false, false])[0].justify, Justify::Right);
// An empty frame between two parts of the same paragraph changes no style either.
let mut last_with_text = last.clone();
last_with_text.runs = vec![TextRun { text: "Tail".into(), style: CharStyle::default(), inline: None }];
assert_eq!(story_paras(&[&first, &middle, &last_with_text], &[true, false, false])[0].justify, Justify::Right);
let mut restyled_middle = middle.clone();
restyled_middle.set_all_paras(ParaStyle { justify: Justify::Center, ..Default::default() });
assert_eq!(story_paras(&[&first, &restyled_middle, &last], &[false, true, false])[0].justify, Justify::Center);
}
/// Numbered list paragraphs count on from one frame to the next: the second frame's first
/// whole paragraph takes the next number. A paragraph split between the frames has its number
/// in the first frame only, and the paragraph after it takes the next number.