From c22747b104e062b1ca75dd8120165f427ed12e2c Mon Sep 17 00:00:00 2001 From: Nikita Rakitin Date: Tue, 13 Oct 2026 23:22:19 +0300 Subject: [PATCH] Morph: the same text moves and resizes instead of cross-fading A title typed as "TEST" on a title slide and on a content slide pairs the two titles, but they differ in placeholder, size and alignment, so Morph with the default Objects option cross-faded them: the old title rose and faded out while the new one faded in. Paired shapes whose text is the same now use the word morph (shared words travel and resize from their old place to their new one) whatever the Morph option, as PowerPoint does. Different texts still cross-fade with Objects. Co-authored-by: Cursor --- crates/anim/src/morph.rs | 6 +++++- crates/anim/src/tests.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/crates/anim/src/morph.rs b/crates/anim/src/morph.rs index b5808c4f..a05f2d6c 100644 --- a/crates/anim/src/morph.rs +++ b/crates/anim/src/morph.rs @@ -237,7 +237,11 @@ pub fn morph_frame(pres: &Presentation, a: &Slide, b: &Slide, t: f64, hidden_b: } else { let new = push(Shape { xfrm: x, ..sb.clone() }, false, s); let old = push(Shape { xfrm: x, ..sa.clone() }, true, 1.0 - s); - if by_text + // The same text in shapes that differ otherwise (a title slide's centred title and a + // content slide's title: other placeholder, size, alignment) moves and resizes word + // by word even with the Objects option, as PowerPoint does, instead of cross-fading. + let same_text = text_of(sa).trim() == text_of(sb).trim(); + if (by_text || same_text) && has_text(sa) && has_text(sb) && let (Some(from), Some(to)) = (box_of(ca.as_ref(), sa), box_of(cb.as_ref(), sb)) diff --git a/crates/anim/src/tests.rs b/crates/anim/src/tests.rs index 1fd82d78..558178d6 100644 --- a/crates/anim/src/tests.rs +++ b/crates/anim/src/tests.rs @@ -789,3 +789,30 @@ fn show_follows_links() { assert_eq!(s.follow(&p, &Action::LastViewed, None), LinkJump::None); assert_eq!(s.slide, 3); } + +/// #316: the same text in two shapes that differ otherwise (a title slide's big centred title, +/// a content slide's title) morphs word by word with the default Objects option, instead of +/// the old box cross-fading into the new one. +#[test] +fn same_text_morphs_even_by_objects() { + let p = deckcraft_model::defaults::new_presentation(None); + let titled = |id, x, size: f64, text: &str| { + let mut body = TextBody::from_text(text); + for para in &mut body.paragraphs { + for r in &mut para.runs { + r.props.size = Some(size); + } + } + Shape { xfrm: Some(Xfrm::new(x, 100.0, 600.0, 120.0)), text: Some(body), ..shape(id, "Title 1") } + }; + let mut a = Slide::default(); + let mut b = Slide::default(); + a.shapes = vec![titled(1, 100.0, 60.0, "TEST")]; + b.shapes = vec![titled(2, 40.0, 44.0, "TEST")]; + let f = morph_frame(&p, &a, &b, 0.5, &|_| false); + assert_eq!(f.text.len(), 1, "the shared text morphs"); + assert!(!f.text[0].chars); + // Different texts still cross-fade with the Objects option. + b.shapes = vec![titled(2, 40.0, 44.0, "Other")]; + assert!(morph_frame(&p, &a, &b, 0.5, &|_| false).text.is_empty()); +}