From 21b50ae28c683b39ada8b1a3d96cf55975bb637d Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:18:01 +0200 Subject: [PATCH 1/3] Improve morph animation regression test --- .../java/com/codename1/ui/ContainerTest.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/ContainerTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/ContainerTest.java index 80e4d8e3f7..50d0b839d1 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/ContainerTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/ContainerTest.java @@ -30,6 +30,23 @@ void testMorphAnimation() { assertEquals(c.getBounds(), a.getBounds()); } + @FormTest + void testAnimateHierarchyWithDefaultOpacityDoesNotCrash() { + Form form = CN.getCurrentForm(); + form.removeAll(); + form.setLayout(BoxLayout.y()); + + Label first = new Label("First"); + Label second = new Label("Second"); + form.addAll(first, second); + form.revalidate(); + + assertDoesNotThrow(() -> { + form.animateHierarchy(100); + form.getAnimationManager().flush(); + }); + } + @Test void testScrollableFlagsRespectBorderLayout() { Container container = new Container(new BorderLayout()); From 69b48f9545530caaee24c1fc269311a5c175903f Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:39:20 +0200 Subject: [PATCH 2/3] Clarify animateHierarchy regression guard --- .../src/test/java/com/codename1/ui/ContainerTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/ContainerTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/ContainerTest.java index 50d0b839d1..e1e077768e 100644 --- a/maven/core-unittests/src/test/java/com/codename1/ui/ContainerTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/ui/ContainerTest.java @@ -41,6 +41,12 @@ void testAnimateHierarchyWithDefaultOpacityDoesNotCrash() { form.addAll(first, second); form.revalidate(); + // Regression guard for commit 4e5cbc2c2092721c8861a34d557fe56fe742e82b where + // MorphAnimation's constructor began initializing opacity to an empty array. + // When animateHierarchy() is invoked without fade (the default, matching + // Form.animateHierarchy and Codename One 7.0.208), MorphAnimation.opacity + // remains zero-length yet non-null. The flush() call below mirrors Display.flushEdt() + // from the stack trace and would previously trigger an ArrayIndexOutOfBoundsException. assertDoesNotThrow(() -> { form.animateHierarchy(100); form.getAnimationManager().flush(); From e9fc36d7a608a6b86e7092bc94720432e22de29d Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Thu, 30 Oct 2025 15:39:30 +0200 Subject: [PATCH 3/3] Guard morph animations from empty opacity arrays --- CodenameOne/src/com/codename1/ui/Container.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CodenameOne/src/com/codename1/ui/Container.java b/CodenameOne/src/com/codename1/ui/Container.java index 082d59e200..98a544c188 100644 --- a/CodenameOne/src/com/codename1/ui/Container.java +++ b/CodenameOne/src/com/codename1/ui/Container.java @@ -4299,8 +4299,6 @@ public MorphAnimation(Container thisContainer, int duration, Motion[][] motions) } this.thisContainer = thisContainer; this.motions = motions; - animatedComponents = new Vector(); - opacity = new Motion[0]; } @Override @@ -4331,7 +4329,7 @@ protected void updateState() { currentCmp.setY(motions[1][iter].getValue()); currentCmp.setWidth(motions[2][iter].getValue()); currentCmp.setHeight(motions[3][iter].getValue()); - if (opacity != null) { + if (opacity != null && iter < opacity.length) { currentCmp.getStyle().setOpacity(opacity[iter].getValue(), false); } } @@ -4351,7 +4349,7 @@ protected void updateState() { currentCmp.setY(motions[1][iter].getValue()); currentCmp.setWidth(motions[2][iter].getValue()); currentCmp.setHeight(motions[3][iter].getValue()); - if (opacity != null) { + if (opacity != null && iter < opacity.length) { currentCmp.getStyle().setOpacity(opacity[iter].getValue(), false); } }