diff --git a/maven/core-unittests/src/test/java/com/codename1/components/StorageImageAsyncTest.java b/maven/core-unittests/src/test/java/com/codename1/components/StorageImageAsyncTest.java index d6c0b63aa7..b48e55459b 100644 --- a/maven/core-unittests/src/test/java/com/codename1/components/StorageImageAsyncTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/components/StorageImageAsyncTest.java @@ -3,14 +3,12 @@ import com.codename1.io.Storage; import com.codename1.junit.FormTest; import com.codename1.junit.UITestBase; -import com.codename1.ui.Display; +import com.codename1.testing.TestUtils; import com.codename1.ui.Image; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; @@ -53,7 +51,10 @@ void testBackgroundLoadPopulatesImageData() throws Exception { StorageImageAsync image = StorageImageAsync.create("async", placeholder); image.getInternal(); - waitForImageData(image); + while(image.getInternal() == placeholder) { + TestUtils.waitFor(10); + } + assertArrayEquals(encoded, image.getImageData()); Image loaded = image.getInternal(); @@ -83,25 +84,6 @@ void testIsAnimationAlwaysTrue() { assertTrue(image.isAnimation()); } - private void waitForImageData(StorageImageAsync image) throws Exception { - Method processSerialCalls = Display.class.getDeclaredMethod("processSerialCalls"); - processSerialCalls.setAccessible(true); - long start = System.currentTimeMillis(); - while (getImageDataField(image) == null) { - processSerialCalls.invoke(Display.getInstance()); - if (System.currentTimeMillis() - start > 2000) { - fail("Timed out waiting for image data to load"); - } - Thread.sleep(10); - } - } - - private byte[] getImageDataField(StorageImageAsync image) throws Exception { - Field field = StorageImageAsync.class.getDeclaredField("imageData"); - field.setAccessible(true); - return (byte[]) field.get(image); - } - private boolean isImageCreated(StorageImageAsync image) throws Exception { Field field = StorageImageAsync.class.getDeclaredField("imageCreated"); field.setAccessible(true); diff --git a/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java b/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java index e35077a4dd..0f0b607761 100644 --- a/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java +++ b/maven/core-unittests/src/test/java/com/codename1/testing/TestCodenameOneImplementation.java @@ -535,6 +535,7 @@ public TestCodenameOneImplementation(boolean timeoutSupported) { public void setDisplaySize(int width, int height) { this.displayWidth = width; this.displayHeight = height; + sizeChanged(width, height); } public void setDeviceDensity(int density) { diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/AnimationEdgeCasesTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/AnimationEdgeCasesTest.java new file mode 100644 index 0000000000..43337a20df --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/AnimationEdgeCasesTest.java @@ -0,0 +1,395 @@ +package com.codename1.ui; + +import com.codename1.io.Util; +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.testing.TestUtils; +import com.codename1.ui.animations.CommonTransitions; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for animation edge cases including adding/removing components while animating. + */ +class AnimationEdgeCasesTest extends UITestBase { + + @FormTest + void testAddComponentDuringLayoutAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.add(btn1); + form.add(btn2); + + // Start layout animation + form.animateLayout(200); + + // Add component during animation + Button btn3 = new Button("Button 3"); + form.add(btn3); + + // component isn't added during animation + assertEquals(2, form.getContentPane().getComponentCount()); + + form.getAnimationManager().flush(); + assertEquals(3, form.getContentPane().getComponentCount()); + } + + @FormTest + void testRemoveComponentDuringLayoutAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + // Start layout animation + form.animateLayout(200); + + // Remove component during animation + form.removeComponent(btn2); + form.revalidate(); + + assertEquals(2, form.getComponentCount()); + assertFalse(form.contains(btn2)); + } + + @FormTest + void testReplaceComponentDuringAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Start animation + form.animateLayout(200); + + // Replace component during animation + Button replacement = new Button("Replacement"); + form.replace(btn2, replacement, null); + form.revalidate(); + + assertEquals(2, form.getComponentCount()); + assertTrue(form.contains(replacement)); + assertFalse(form.contains(btn2)); + } + + @FormTest + void testHierarchyAnimationWithComponentAddition() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Container container = new Container(BoxLayout.y()); + Button btn1 = new Button("Button 1"); + container.add(btn1); + + form.add(container); + form.revalidate(); + + // Start hierarchy animation + form.animateHierarchy(200); + + // Add component to container during animation + Button btn2 = new Button("Button 2"); + container.add(btn2); + + assertEquals(1, container.getComponentCount()); + form.getAnimationManager().flush(); + assertEquals(2, container.getComponentCount()); + } + + @FormTest + void testAnimationOnEmptyContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Container container = new Container(BoxLayout.y()); + form.add(container); + form.revalidate(); + + // Animate empty container - should not crash + assertDoesNotThrow(() -> form.animateLayout(200)); + } + + @FormTest + void testMultipleSimultaneousAnimations() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Start multiple animations + form.animateLayout(200); + form.animateHierarchy(200); + + // Should handle gracefully + assertEquals(2, form.getComponentCount()); + } + + @FormTest + void testAnimationWithInvisibleComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + btn2.setVisible(false); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Animate with invisible component + form.animateLayout(200); + + assertFalse(btn2.isVisible()); + assertTrue(btn1.isVisible()); + } + + @FormTest + void testToggleVisibilityDuringAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Start animation + form.animateLayout(200); + + // Toggle visibility during animation + btn2.setVisible(false); + form.revalidate(); + + assertFalse(btn2.isVisible()); + } + + @FormTest + void testAnimationWithZeroSpeed() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn = new Button("Button"); + form.add(btn); + form.revalidate(); + + // Animation with zero duration should complete immediately + form.animateLayout(0); + + assertNotNull(btn.getParent()); + } + + @FormTest + void testRemoveAllDuringAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + for (int i = 0; i < 10; i++) { + form.add(new Button("Button " + i)); + } + + // Start animation + form.animateLayout(200); + + // Remove all components + form.removeAll(); + + // Actual removal is deferred + assertEquals(10, form.getContentPane().getComponentCount()); + + TestUtils.waitFor(150); + for (int iter = 0; iter < 50; iter++) { + TestUtils.waitFor(10); + if (form.getContentPane().getComponentCount() == 0) { + // success, removed after animation completed + return; + } + } + fail("Components weren't removed after animation"); + } + + @FormTest + void testMorphAnimationWithComponentRemoval() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Container container = new Container(BoxLayout.y()); + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + container.addAll(btn1, btn2, btn3); + form.add(container); + + // Start morph animation + container.animateHierarchy(200); + + // Remove component during morph + container.removeComponent(btn2); + + assertEquals(3, container.getComponentCount()); + } + + @FormTest + void testAnimationInterruption() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Start first animation + form.animateLayout(300); + + // Interrupt with second animation + form.animateLayout(200); + + assertEquals(2, form.getComponentCount()); + } + + @FormTest + void testAnimateUnlayoutAndRemove() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + // Remove component and animate unlayout + form.removeComponent(btn2); + form.animateUnlayoutAndWait(200, 255); + + assertEquals(2, form.getContentPane().getComponentCount()); + assertFalse(form.contains(btn2)); + } + + @FormTest + void testAnimationWithChangedBounds() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Button"); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + int initialWidth = btn.getWidth(); + + // Change form size and animate + form.setWidth(form.getWidth() + 100); + form.animateLayout(200); + + assertTrue(btn.getWidth() >= initialWidth); + } + + @FormTest + void testHierarchyAnimationWithOpacity() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Animate with fade + form.animateHierarchyFade(200, 0); + + assertEquals(2, form.getComponentCount()); + } + + @FormTest + void testAnimationFlush() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn = new Button("Button"); + form.add(btn); + form.revalidate(); + + // Start animation and flush + form.animateLayout(200); + form.getAnimationManager().flush(); + + assertNotNull(btn.getParent()); + } + + @FormTest + void testComponentAnimationWithTransition() { + Form form1 = CN.getCurrentForm(); + form1.setLayout(new BorderLayout()); + form1.add(BorderLayout.CENTER, new Label("Form 1")); + + Form form2 = new Form("Form 2", new BorderLayout()); + form2.add(BorderLayout.CENTER, new Label("Form 2")); + + // Set transition + form2.setTransitionOutAnimator(CommonTransitions.createSlide(CommonTransitions.SLIDE_HORIZONTAL, true, 200)); + + assertNotNull(form2.getTransitionOutAnimator()); + } + + @FormTest + void testAnimationWithLayoutConstraintChange() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Button"); + form.add(BorderLayout.NORTH, btn); + form.revalidate(); + + // Start animation + form.animateLayout(200); + + // Change constraint + form.removeComponent(btn); + form.add(BorderLayout.SOUTH, btn); + form.revalidate(); + + assertEquals(BorderLayout.SOUTH, form.getLayout().getComponentConstraint(btn)); + } + + @FormTest + void testAnimationWithDisabledComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + btn2.setEnabled(false); + + form.addAll(btn1, btn2); + form.revalidate(); + + form.animateLayout(200); + + assertTrue(btn1.isEnabled()); + assertFalse(btn2.isEnabled()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/ContainerMethodsTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/ContainerMethodsTest.java new file mode 100644 index 0000000000..e62b50bd16 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/ContainerMethodsTest.java @@ -0,0 +1,443 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for Container methods: getResponderAt, findDropTargetAt, updateTabIndices, drop, getChildrenAsList. + */ +class ContainerMethodsTest extends UITestBase { + + @FormTest + void testGetResponderAtWithSingleComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + btn.setX(50); + btn.setY(50); + btn.setWidth(100); + btn.setHeight(50); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + Component responder = form.getResponderAt(75, 75); + + // Should find the button at this position + assertNotNull(responder); + } + + @FormTest + void testGetResponderAtOutsideComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + // Try to get responder at position outside form bounds + Component responder = form.getResponderAt(-10, -10); + + // May return form itself or null + assertTrue(responder == null || responder == form); + } + + @FormTest + void testGetResponderAtOverlappingComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new com.codename1.ui.layouts.LayeredLayout()); + + Button btn1 = new Button("Button 1"); + btn1.setX(0); + btn1.setY(0); + btn1.setWidth(200); + btn1.setHeight(100); + + Button btn2 = new Button("Button 2"); + btn2.setX(50); + btn2.setY(50); + btn2.setWidth(200); + btn2.setHeight(100); + + form.add(btn1); + form.add(btn2); + form.revalidate(); + + // Button 2 is on top, so it should respond + Component responder = form.getResponderAt(100, 75); + + assertNotNull(responder); + } + + @FormTest + void testGetChildrenAsList() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + List children = form.getContentPane().getChildrenAsList(false); + + assertEquals(3, children.size()); + assertTrue(children.contains(btn1)); + assertTrue(children.contains(btn2)); + assertTrue(children.contains(btn3)); + } + + @FormTest + void testGetChildrenAsListRecursive() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container container = new Container(BoxLayout.y()); + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + container.addAll(btn1, btn2); + + Button btn3 = new Button("Button 3"); + + form.add(BorderLayout.CENTER, container); + form.add(BorderLayout.SOUTH, btn3); + form.revalidate(); + + List children = form.getChildrenAsList(true); + + // Should include nested components + assertTrue(children.size() >= 2); + } + + @FormTest + void testGetChildrenAsListEmpty() { + Form form = CN.getCurrentForm(); + form.removeAll(); + form.revalidate(); + + List children = form.getContentPane().getChildrenAsList(false); + + assertNotNull(children); + assertEquals(0, children.size()); + } + + @FormTest + void testUpdateTabIndices() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + // Update tab indices with offset + int result = form.getContentPane().updateTabIndices(0); + + // Tab indices should be updated + assertTrue(result >= 0); + } + + @FormTest + void testDropOnContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container target = new Container(BoxLayout.y()); + target.setDropTarget(true); + + form.add(BorderLayout.CENTER, target); + form.revalidate(); + + assertTrue(target.isDropTarget()); + + // Simulate drop + Component dragged = new Label("Dragged"); + target.drop(dragged, 50, 50); + + // Component may be added to target + assertNotNull(dragged); + } + + @FormTest + void testFindDropTargetAtValidLocation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container dropTarget = new Container(BoxLayout.y()); + dropTarget.setDropTarget(true); + dropTarget.setX(0); + dropTarget.setY(0); + dropTarget.setWidth(200); + dropTarget.setHeight(200); + + form.add(BorderLayout.CENTER, dropTarget); + form.revalidate(); + + Component found = form.findDropTargetAt(100, 100); + + assertNotNull(found); + } + + @FormTest + void testFindDropTargetAtInvalidLocation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container dropTarget = new Container(BoxLayout.y()); + dropTarget.setDropTarget(true); + + form.add(BorderLayout.CENTER, dropTarget); + form.revalidate(); + + Component found = form.findDropTargetAt(-100, -100); + + // Should not find drop target at negative coordinates + assertNull(found); + } + + @FormTest + void testGetResponderAtWithInvisibleComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button invisible = new Button("Invisible"); + invisible.setVisible(false); + invisible.setX(50); + invisible.setY(50); + invisible.setWidth(100); + invisible.setHeight(50); + + form.add(BorderLayout.CENTER, invisible); + form.revalidate(); + + Component responder = form.getResponderAt(75, 75); + + // Should not return invisible component + assertNotEquals(invisible, responder); + } + + @FormTest + void testGetChildrenAsListWithNestedContainers() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container level1 = new Container(BoxLayout.y()); + Container level2 = new Container(BoxLayout.y()); + Button deepButton = new Button("Deep"); + + level2.add(deepButton); + level1.add(level2); + form.add(BorderLayout.CENTER, level1); + form.revalidate(); + + List childrenNonRecursive = form.getChildrenAsList(false); + List childrenRecursive = form.getChildrenAsList(true); + + // Recursive should have more components + assertTrue(childrenRecursive.size() >= childrenNonRecursive.size()); + } + + @FormTest + void testDropTargetWithMultipleContainers() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Container target1 = new Container(BoxLayout.y()); + target1.setDropTarget(true); + + Container target2 = new Container(BoxLayout.y()); + target2.setDropTarget(true); + + form.addAll(target1, target2); + form.revalidate(); + + assertTrue(target1.isDropTarget()); + assertTrue(target2.isDropTarget()); + } + + @FormTest + void testGetResponderAtInScrollableContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + + for (int i = 0; i < 100; i++) { + Button btn = new Button("Button " + i); + scrollable.add(btn); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Get responder at position in scrollable container + Component responder = scrollable.getResponderAt(50, 50); + + assertNotNull(responder); + } + + @FormTest + void testUpdateTabIndicesAfterReordering() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + form.getContentPane().updateTabIndices(0); + + // Reorder components + form.removeComponent(btn1); + form.addComponent(btn1); + form.revalidate(); + form.getContentPane().updateTabIndices(0); + + assertEquals(3, form.getContentPane().getComponentCount()); + } + + @FormTest + void testGetChildrenAsListAfterRemoval() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + List before = form.getContentPane().getChildrenAsList(false); + assertEquals(3, before.size()); + + form.removeComponent(btn2); + form.revalidate(); + + List after = form.getChildrenAsList(false); + assertEquals(2, after.size()); + assertFalse(after.contains(btn2)); + } + + @FormTest + void testDropOnNonDropTarget() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container nonTarget = new Container(BoxLayout.y()); + nonTarget.setDropTarget(false); + + form.add(BorderLayout.CENTER, nonTarget); + form.revalidate(); + + assertFalse(nonTarget.isDropTarget()); + + // Try to drop on non-drop-target + Component dragged = new Label("Dragged"); + nonTarget.drop(dragged, 50, 50); + + // Should handle gracefully + assertNotNull(dragged); + } + + @FormTest + void testGetResponderAtWithDisabledComponent() { + Form form = CN.getCurrentForm(); + Button disabled = new Button("Disabled"); + disabled.setEnabled(false); + + form.add(disabled); + form.revalidate(); + + Component responder = form.getResponderAt(disabled.getX() + 1, disabled.getAbsoluteY() + 1); + + // Disabled component can't be a responder + assertNull(responder); + + disabled.setEnabled(true); + responder = form.getResponderAt(disabled.getX() + 1, disabled.getAbsoluteY() + 1); + assertNotNull(responder); + } + + @FormTest + void testFindDropTargetAtWithNestedDropTargets() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container outer = new Container(BoxLayout.y()); + outer.setDropTarget(true); + + Container inner = new Container(BoxLayout.y()); + inner.setDropTarget(true); + + outer.add(inner); + form.add(BorderLayout.CENTER, outer); + form.revalidate(); + + // Should find closest drop target + Component found = form.findDropTargetAt(50, 50); + + assertNotNull(found); + } + + @FormTest + void testGetChildrenAsListModification() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + List children = form.getContentPane().getChildrenAsList(false); + int originalSize = children.size(); + + // Add another component + Button btn3 = new Button("Button 3"); + form.add(btn3); + form.revalidate(); + + List updatedChildren = form.getContentPane().getChildrenAsList(false); + + assertEquals(originalSize + 1, updatedChildren.size()); + } + + @FormTest + void testDropWithAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container target = new Container(BoxLayout.y()); + target.setDropTarget(true); + + form.add(BorderLayout.CENTER, target); + form.revalidate(); + + // Start animation + form.animateLayout(200); + + // Drop during animation + Component dragged = new Label("Dragged"); + target.drop(dragged, 50, 50); + + assertNotNull(dragged); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/DynamicLayoutTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/DynamicLayoutTest.java new file mode 100644 index 0000000000..417e644f84 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/DynamicLayoutTest.java @@ -0,0 +1,427 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.layouts.GridLayout; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for changing layouts dynamically and adapting to layout constraints. + */ +class DynamicLayoutTest extends UITestBase { + + @FormTest + void testChangeLayoutFromBorderToBox() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.add(BorderLayout.NORTH, btn1); + form.add(BorderLayout.CENTER, btn2); + form.revalidate(); + + assertTrue(form.getLayout() instanceof BorderLayout); + + // Change to BoxLayout + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + form.revalidate(); + + assertTrue(form.getLayout() instanceof BoxLayout); + } + + @FormTest + void testChangeLayoutFromBoxToBorder() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + assertTrue(form.getLayout() instanceof BoxLayout); + + // Change to BorderLayout + form.removeAll(); + form.setLayout(new BorderLayout()); + form.add(BorderLayout.NORTH, btn1); + form.add(BorderLayout.CENTER, btn2); + form.revalidate(); + + assertTrue(form.getLayout() instanceof BorderLayout); + } + + @FormTest + void testChangeLayoutWithConstraints() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.NORTH, btn); + form.revalidate(); + + assertEquals(BorderLayout.NORTH, form.getLayout().getComponentConstraint(btn)); + + // Change constraint + form.removeComponent(btn); + form.add(BorderLayout.SOUTH, btn); + form.revalidate(); + + assertEquals(BorderLayout.SOUTH, form.getLayout().getComponentConstraint(btn)); + } + + @FormTest + void testChangeLayoutMultipleTimes() { + Form form = CN.getCurrentForm(); + + Button btn = new Button("Test"); + + // BorderLayout + form.setLayout(new BorderLayout()); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + assertTrue(form.getLayout() instanceof BorderLayout); + + // BoxLayout + form.removeAll(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + form.add(btn); + form.revalidate(); + assertTrue(form.getLayout() instanceof BoxLayout); + + // FlowLayout + form.removeAll(); + form.setLayout(new FlowLayout()); + form.add(btn); + form.revalidate(); + assertTrue(form.getLayout() instanceof FlowLayout); + } + + @FormTest + void testChangeLayoutWithAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Start animation + form.animateLayout(200); + + // Change layout during animation + form.removeAll(); + form.setLayout(new BorderLayout()); + form.add(BorderLayout.CENTER, btn1); + form.add(BorderLayout.SOUTH, btn2); + form.revalidate(); + + assertTrue(form.getLayout() instanceof BorderLayout); + } + + @FormTest + void testGridLayoutToFlowLayout() { + Form form = CN.getCurrentForm(); + form.setLayout(new GridLayout(2, 2)); + + for (int i = 0; i < 4; i++) { + form.add(new Button("Button " + i)); + } + form.revalidate(); + + assertTrue(form.getLayout() instanceof GridLayout); + + // Change to FlowLayout + form.setLayout(new FlowLayout()); + form.revalidate(); + + assertTrue(form.getLayout() instanceof FlowLayout); + } + + @FormTest + void testLayoutConstraintAdaptation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button north = new Button("North"); + Button south = new Button("South"); + Button center = new Button("Center"); + + form.add(BorderLayout.NORTH, north); + form.add(BorderLayout.SOUTH, south); + form.add(BorderLayout.CENTER, center); + form.revalidate(); + + // Change to BoxLayout (constraints should be ignored) + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + form.revalidate(); + + assertEquals(3, form.getContentPane().getComponentCount()); + } + + @FormTest + void testDynamicConstraintChange() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.NORTH, btn); + form.revalidate(); + + // Move to different position + String[] positions = { + BorderLayout.SOUTH, + BorderLayout.EAST, + BorderLayout.WEST, + BorderLayout.CENTER + }; + + for (String position : positions) { + form.removeComponent(btn); + form.add(position, btn); + form.revalidate(); + assertEquals(position, form.getLayout().getComponentConstraint(btn)); + } + } + + @FormTest + void testLayoutChangeWithScrollableContent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + + for (int i = 0; i < 130; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Change layout + form.removeAll(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + form.add(scrollable); + form.revalidate(); + + assertTrue(scrollable.isScrollableY()); + } + + @FormTest + void testLayoutChangePreservesComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + int initialCount = form.getContentPane().getComponentCount(); + + // Change layout + form.setLayout(new FlowLayout()); + form.revalidate(); + + assertEquals(initialCount, form.getContentPane().getComponentCount()); + } + + @FormTest + void testLayoutChangeWithInvisibleComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button visible = new Button("Visible"); + Button invisible = new Button("Invisible"); + invisible.setVisible(false); + + form.addAll(visible, invisible); + form.revalidate(); + + // Change layout + form.setLayout(new FlowLayout()); + form.revalidate(); + + assertTrue(visible.isVisible()); + assertFalse(invisible.isVisible()); + } + + @FormTest + void testLayoutChangeWithDifferentPreferredSizes() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button small = new Button("Small"); + small.setPreferredW(100); + small.setPreferredH(50); + + Button large = new Button("Large"); + large.setPreferredW(300); + large.setPreferredH(150); + + form.addAll(small, large); + form.revalidate(); + + // Change layout + form.setLayout(new FlowLayout()); + form.revalidate(); + + assertEquals(100, small.getPreferredW()); + assertEquals(300, large.getPreferredW()); + } + + @FormTest + void testNestedContainerLayoutChange() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container container = new Container(BoxLayout.y()); + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + container.addAll(btn1, btn2); + form.add(BorderLayout.CENTER, container); + form.revalidate(); + + // Change nested container's layout + container.setLayout(new FlowLayout()); + form.revalidate(); + + assertTrue(container.getLayout() instanceof FlowLayout); + } + + @FormTest + void testLayoutChangeWithComponentStates() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button enabled = new Button("Enabled"); + Button disabled = new Button("Disabled"); + disabled.setEnabled(false); + + form.addAll(enabled, disabled); + form.revalidate(); + + // Change layout + form.setLayout(new FlowLayout()); + form.revalidate(); + + assertTrue(enabled.isEnabled()); + assertFalse(disabled.isEnabled()); + } + + @FormTest + void testLayoutChangeAffectsRevalidate() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn = new Button("Test"); + form.add(btn); + form.revalidate(); + + int initialY = btn.getY(); + + // Change layout and observe position change + form.setLayout(new BorderLayout()); + btn.remove(); + form.add(BorderLayout.SOUTH, btn); + form.revalidate(); + + // Position should be different + assertTrue(btn.getY() >= 0); + } + + @FormTest + void testLayoutChangeWithFocusedComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + btn1.requestFocus(); + + // Change layout + form.setLayout(new FlowLayout()); + form.revalidate(); + + // Focus should be maintained + assertEquals(2, form.getContentPane().getComponentCount()); + } + + @FormTest + void testRapidLayoutChanges() { + Form form = CN.getCurrentForm(); + + Button btn = new Button("Test"); + + // Rapid layout changes + for (int i = 0; i < 10; i++) { + form.removeAll(); + if (i % 3 == 0) { + form.setLayout(new BorderLayout()); + form.add(BorderLayout.CENTER, btn); + } else if (i % 3 == 1) { + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + form.add(btn); + } else { + form.setLayout(new FlowLayout()); + form.add(btn); + } + form.revalidate(); + } + + assertEquals(1, form.getContentPane().getComponentCount()); + } + + @FormTest + void testLayoutChangeWithLayeredLayout() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + // Change to LayeredLayout + form.removeAll(); + form.setLayout(new com.codename1.ui.layouts.LayeredLayout()); + form.add(btn); + form.revalidate(); + + assertTrue(form.getLayout() instanceof com.codename1.ui.layouts.LayeredLayout); + } + + @FormTest + void testConstraintUpdateOnSameLayout() { + Form form = CN.getCurrentForm(); + BorderLayout layout = new BorderLayout(); + form.setLayout(layout); + + Button btn = new Button("Test"); + form.add(BorderLayout.NORTH, btn); + form.revalidate(); + + // Update constraint within same layout + form.removeComponent(btn); + form.add(BorderLayout.SOUTH, btn); + form.revalidate(); + + assertEquals(BorderLayout.SOUTH, layout.getComponentConstraint(btn)); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/FocusReplacementTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/FocusReplacementTest.java new file mode 100644 index 0000000000..d7b69a8a96 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/FocusReplacementTest.java @@ -0,0 +1,374 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.ui.layouts.BoxLayout; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for replacing focused components with focusable/non-focusable components. + */ +class FocusReplacementTest extends UITestBase { + + @FormTest + void testReplaceFocusedComponentWithFocusable() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + // Focus btn2 + btn2.requestFocus(); + assertTrue(btn2.hasFocus()); + + // Replace btn2 with another focusable component + Button replacement = new Button("Replacement"); + form.replace(btn2, replacement, null); + form.revalidate(); + + // Replacement should receive focus + assertFalse(form.contains(btn2)); + assertTrue(form.contains(replacement)); + } + + @FormTest + void testReplaceFocusedComponentWithNonFocusable() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + // Focus btn2 + btn2.requestFocus(); + assertTrue(btn2.hasFocus()); + + // Replace btn2 with non-focusable label + Label label = new Label("Non-Focusable"); + label.setFocusable(false); + form.replace(btn2, label, null); + form.revalidate(); + + // Focus should move to another focusable component + assertFalse(form.contains(btn2)); + assertTrue(form.contains(label)); + assertFalse(label.hasFocus()); + } + + @FormTest + void testReplaceNonFocusedComponentWithFocusable() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Focus btn1 + btn1.requestFocus(); + assertTrue(btn1.hasFocus()); + + // Replace btn2 (not focused) with another button + Button replacement = new Button("Replacement"); + form.replace(btn2, replacement, null); + form.revalidate(); + + // Focus should remain on btn1 + assertTrue(btn1.hasFocus()); + assertTrue(form.contains(replacement)); + } + + @FormTest + void testRemoveFocusedComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + // Focus btn2 + btn2.requestFocus(); + assertTrue(btn2.hasFocus()); + + // Remove btn2 + form.removeComponent(btn2); + form.revalidate(); + + // Focus should move to another component + assertFalse(form.contains(btn2)); + } + + @FormTest + void testReplaceFocusedWithDisabledComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + // Focus btn2 + btn2.requestFocus(); + assertTrue(btn2.hasFocus()); + + // Replace with disabled button + Button disabled = new Button("Disabled"); + disabled.setEnabled(false); + form.replace(btn2, disabled, null); + form.revalidate(); + + // Disabled component should not have focus + assertFalse(disabled.hasFocus()); + } + + @FormTest + void testReplaceMultipleFocusableComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + btn2.requestFocus(); + + // Replace all buttons + Label label1 = new Label("Label 1"); + Label label2 = new Label("Label 2"); + Label label3 = new Label("Label 3"); + + form.replace(btn1, label1, null); + form.replace(btn2, label2, null); + form.replace(btn3, label3, null); + form.revalidate(); + + // No label should have focus (they're not focusable by default) + assertFalse(label1.hasFocus()); + assertFalse(label2.hasFocus()); + assertFalse(label3.hasFocus()); + } + + @FormTest + void testReplaceFocusedComponentInContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Container container = new Container(BoxLayout.y()); + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + container.addAll(btn1, btn2); + form.add(container); + form.revalidate(); + + btn2.requestFocus(); + assertTrue(btn2.hasFocus()); + + // Replace in container + Button replacement = new Button("Replacement"); + container.replace(btn2, replacement, null); + form.revalidate(); + + assertTrue(container.contains(replacement)); + assertFalse(container.contains(btn2)); + } + + @FormTest + void testReplaceFocusableWithInvisibleComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + btn2.requestFocus(); + + // Replace with invisible component + Button invisible = new Button("Invisible"); + invisible.setVisible(false); + form.replace(btn2, invisible, null); + form.revalidate(); + + assertFalse(invisible.isVisible()); + assertFalse(invisible.hasFocus()); + } + + @FormTest + void testFocusTransferOnReplace() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + btn2.requestFocus(); + + // Replace and check focus transfer + TextField textField = new TextField(); + form.replace(btn2, textField, null); + form.revalidate(); + + assertTrue(form.contains(textField)); + } + + @FormTest + void testReplaceFocusedComponentWithContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + btn2.requestFocus(); + + // Replace with container + Container replacement = new Container(BoxLayout.y()); + replacement.add(new Button("New Button")); + form.replace(btn2, replacement, null); + form.revalidate(); + + assertTrue(form.contains(replacement)); + } + + @FormTest + void testReplaceLastFocusableComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn = new Button("Only Button"); + form.add(btn); + form.revalidate(); + + btn.requestFocus(); + assertTrue(btn.hasFocus()); + + // Replace with non-focusable + Label label = new Label("Label"); + form.replace(btn, label, null); + form.revalidate(); + + assertFalse(label.hasFocus()); + } + + @FormTest + void testReplaceFocusedComponentWithSameFocusableType() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + TextField tf1 = new TextField("Text 1"); + TextField tf2 = new TextField("Text 2"); + + form.addAll(tf1, tf2); + form.revalidate(); + + tf1.requestFocus(); + + // Replace with another text field + TextField replacement = new TextField("Replacement"); + form.replace(tf1, replacement, null); + form.revalidate(); + + assertTrue(form.contains(replacement)); + } + + @FormTest + void testReplaceWithTransitionAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + btn1.requestFocus(); + + // Replace with transition + Button replacement = new Button("Replacement"); + form.replaceAndWait(btn1, replacement, null); + + assertTrue(form.contains(replacement)); + assertFalse(form.contains(btn1)); + } + + @FormTest + void testReplaceFocusedInNestedContainers() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Container outer = new Container(BoxLayout.y()); + Container inner = new Container(BoxLayout.y()); + + Button btn = new Button("Button"); + inner.add(btn); + outer.add(inner); + form.add(outer); + form.revalidate(); + + btn.requestFocus(); + + // Replace in nested structure + Label label = new Label("Label"); + inner.replace(btn, label, null); + form.revalidate(); + + assertTrue(inner.contains(label)); + } + + @FormTest + void testReplaceAllFocusableWithNonFocusable() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + btn2.requestFocus(); + + // Replace all with labels + form.removeAll(); + form.addAll(new Label("L1"), new Label("L2"), new Label("L3")); + form.revalidate(); + + // Form should handle lack of focusable components + assertEquals(3, form.getContentPane().getComponentCount()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/KeyEventsTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/KeyEventsTest.java new file mode 100644 index 0000000000..dbc3c838dd --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/KeyEventsTest.java @@ -0,0 +1,309 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.testing.TestCodenameOneImplementation; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for key events and game key events. + */ +class KeyEventsTest extends UITestBase { + + @FormTest + void testKeyPressEvent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + final boolean[] pressed = {false}; + Button btn = new Button("Test") { + @Override + public void keyPressed(int keyCode) { + super.keyPressed(keyCode); + pressed[0] = true; + } + }; + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + btn.keyPressed(65); // 'A' key + assertTrue(pressed[0]); + } + + @FormTest + void testKeyReleaseEvent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + final boolean[] released = {false}; + Button btn = new Button("Test") { + @Override + public void keyReleased(int keyCode) { + super.keyReleased(keyCode); + released[0] = true; + } + }; + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + btn.keyReleased(65); + assertTrue(released[0]); + } + + @FormTest + void testKeyEventsOnForm() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + // Send key event to form + form.keyPressed(65); + form.keyReleased(65); + + // Should not crash + assertNotNull(form); + } + + @FormTest + void testScrollWithKeyPress() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + for (int i = 0; i < 50; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + int initialScrollY = scrollable.getScrollY(); + + // Simulate DOWN key + int downKeyCode = impl.getKeyCode(Display.GAME_DOWN); + scrollable.keyPressed(downKeyCode); + + // Scroll position may change + assertTrue(scrollable.getScrollY() >= initialScrollY); + } + + @FormTest + void testGameKeyMapping() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + int upKey = impl.getKeyCode(Display.GAME_UP); + int downKey = impl.getKeyCode(Display.GAME_DOWN); + int leftKey = impl.getKeyCode(Display.GAME_LEFT); + int rightKey = impl.getKeyCode(Display.GAME_RIGHT); + int fireKey = impl.getKeyCode(Display.GAME_FIRE); + + // Game keys should be mapped + assertEquals(Display.GAME_UP, impl.getGameAction(upKey)); + assertEquals(Display.GAME_DOWN, impl.getGameAction(downKey)); + assertEquals(Display.GAME_LEFT, impl.getGameAction(leftKey)); + assertEquals(Display.GAME_RIGHT, impl.getGameAction(rightKey)); + assertEquals(Display.GAME_FIRE, impl.getGameAction(fireKey)); + } + + @FormTest + void testKeyEventWithDisabledComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + btn.setEnabled(false); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + // Send key to disabled component + btn.keyPressed(65); + + assertFalse(btn.isEnabled()); + } + + @FormTest + void testKeyEventOnTextField() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + TextField textField = new TextField(); + form.add(BorderLayout.CENTER, textField); + form.revalidate(); + + // Simulate key events on text field + textField.keyPressed(65); + textField.keyReleased(65); + + assertNotNull(textField.getText()); + } + + @FormTest + void testBackKeyEvent() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + Form form = CN.getCurrentForm(); + + final boolean[] backPressed = {false}; + Form testForm = new Form("Test") { + @Override + public void keyPressed(int keyCode) { + super.keyPressed(keyCode); + if (keyCode == impl.getBackKeyCode()) { + backPressed[0] = true; + } + } + }; + + testForm.keyPressed(impl.getBackKeyCode()); + assertTrue(backPressed[0]); + } + + @FormTest + void testScrollHorizontalWithKeys() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.x()); + scrollable.setScrollableX(true); + scrollable.setWidth(300); + + for (int i = 0; i < 20; i++) { + Button btn = new Button("Item " + i); + btn.setPreferredW(100); + scrollable.add(btn); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + int initialScrollX = scrollable.getScrollX(); + + // Simulate RIGHT key + int rightKeyCode = impl.getKeyCode(Display.GAME_RIGHT); + scrollable.keyPressed(rightKeyCode); + + assertTrue(scrollable.getScrollX() >= initialScrollX); + } + + @FormTest + void testKeyRepeat() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + final int[] pressCount = {0}; + Button btn = new Button("Test") { + @Override + public void keyPressed(int keyCode) { + super.keyPressed(keyCode); + pressCount[0]++; + } + }; + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + // Simulate key repeat + for (int i = 0; i < 5; i++) { + btn.keyPressed(65); + } + + assertEquals(5, pressCount[0]); + } + + @FormTest + void testArrowKeyNavigation() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + btn1.requestFocus(); + + // Simulate DOWN key for navigation + int downKeyCode = impl.getKeyCode(Display.GAME_DOWN); + form.keyPressed(downKeyCode); + + assertEquals(3, form.getContentPane().getComponentCount()); + } + + @FormTest + void testKeyEventInDialog() { + Form form = CN.getCurrentForm(); + + Dialog dialog = new Dialog("Test"); + dialog.setLayout(new BorderLayout()); + + final boolean[] pressed = {false}; + Button btn = new Button("Dialog Button") { + @Override + public void keyPressed(int keyCode) { + super.keyPressed(keyCode); + pressed[0] = true; + } + }; + dialog.add(BorderLayout.CENTER, btn); + + btn.keyPressed(65); + assertTrue(pressed[0]); + } + + @FormTest + void testKeyCodeConstants() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + // Test that key code constants are defined + assertTrue(impl.getBackKeyCode() != 0); + assertTrue(impl.getBackspaceKeyCode() != 0); + assertTrue(impl.getClearKeyCode() != 0); + } + + @FormTest + void testKeyEventPropagation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container container = new Container(BoxLayout.y()); + Button btn = new Button("Test"); + container.add(btn); + + form.add(BorderLayout.CENTER, container); + form.revalidate(); + + // Send key event + btn.keyPressed(65); + + // Should not crash + assertNotNull(container); + } + + @FormTest + void testKeyEventOnInvisibleComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + btn.setVisible(false); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + // Send key to invisible component + btn.keyPressed(65); + + assertFalse(btn.isVisible()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/LayoutAnimationTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/LayoutAnimationTest.java new file mode 100644 index 0000000000..c63ec28c2c --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/LayoutAnimationTest.java @@ -0,0 +1,364 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for layout and hierarchy animation including unlayout animation and opacity. + */ +class LayoutAnimationTest extends UITestBase { + + @FormTest + void testBasicLayoutAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Start layout animation + form.animateLayout(200); + + assertEquals(2, form.getContentPane().getComponentCount()); + } + + @FormTest + void testHierarchyAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Container container = new Container(BoxLayout.y()); + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + container.addAll(btn1, btn2); + + form.add(container); + form.revalidate(); + + // Animate entire hierarchy + form.animateHierarchy(200); + + assertEquals(2, container.getComponentCount()); + } + + @FormTest + void testHierarchyAnimationWithFade() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Animate with fade effect + form.animateHierarchyFade(200, 0); + + assertEquals(2, form.getContentPane().getComponentCount()); + } + + + @FormTest + void testLayoutAnimationWithMultipleComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + for (int i = 0; i < 10; i++) { + form.add(new Button("Button " + i)); + } + form.revalidate(); + + // Animate all components + form.animateLayout(300); + + assertEquals(10, form.getContentPane().getComponentCount()); + } + + @FormTest + void testAnimateLayoutAndWait() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn = new Button("Test"); + form.add(btn); + form.revalidate(); + + // Animate and wait for completion + form.animateLayoutAndWait(200); + + assertEquals(1, form.getContentPane().getComponentCount()); + } + + @FormTest + void testHierarchyAnimationWithOpacity() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + form.revalidate(); + + // Animate with opacity fade + form.animateHierarchyFade(300, 0); + + assertEquals(3, form.getContentPane().getComponentCount()); + } + + @FormTest + void testLayoutAnimationZeroDuration() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn = new Button("Test"); + form.add(btn); + form.revalidate(); + + // Zero duration should complete immediately + form.animateLayout(0); + + assertEquals(1, form.getContentPane().getComponentCount()); + } + + @FormTest + void testLayoutAnimationWithBorderLayout() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button north = new Button("North"); + Button center = new Button("Center"); + Button south = new Button("South"); + + form.add(BorderLayout.NORTH, north); + form.add(BorderLayout.CENTER, center); + form.add(BorderLayout.SOUTH, south); + form.revalidate(); + + // Animate BorderLayout + form.animateLayout(200); + + assertEquals(3, form.getContentPane().getComponentCount()); + } + + @FormTest + void testHierarchyAnimationWithNestedContainers() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Container outer = new Container(BoxLayout.y()); + Container inner = new Container(BoxLayout.y()); + + inner.addAll(new Button("Inner 1"), new Button("Inner 2")); + outer.add(inner); + form.add(outer); + form.revalidate(); + + // Animate nested hierarchy + form.animateHierarchy(250); + + assertEquals(2, inner.getComponentCount()); + } + + @FormTest + void testAnimationFlush() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn = new Button("Test"); + form.add(btn); + form.revalidate(); + + // Start animation + form.animateLayout(300); + + // Flush animation queue + form.getAnimationManager().flush(); + + assertEquals(1, form.getContentPane().getComponentCount()); + } + + @FormTest + void testLayoutAnimationWithInvisibleComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button visible = new Button("Visible"); + Button invisible = new Button("Invisible"); + invisible.setVisible(false); + + form.addAll(visible, invisible); + form.revalidate(); + + // Animate with invisible component + form.animateLayout(200); + + assertTrue(visible.isVisible()); + assertFalse(invisible.isVisible()); + } + + + @FormTest + void testMorphAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Container container = new Container(BoxLayout.y()); + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + container.addAll(btn1, btn2, btn3); + form.add(container); + form.revalidate(); + + // Morph from btn1 to btn3 + container.morphAndWait(btn1, btn3, 200); + + assertEquals(3, container.getComponentCount()); + } + + @FormTest + void testLayoutAnimationWithScrollable() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + + for (int i = 0; i < 30; i++) { + scrollable.add(new Button("Button " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Animate scrollable container + scrollable.animateLayout(250); + + assertEquals(30, scrollable.getComponentCount()); + } + + @FormTest + void testHierarchyAnimationWithOpacityValues() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Full Opacity"); + Button btn2 = new Button("Half Opacity"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Animate with different opacity + form.animateHierarchyFade(200, 128); + + assertEquals(2, form.getContentPane().getComponentCount()); + } + + @FormTest + void testLayoutAnimationInterruption() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn = new Button("Test"); + form.add(btn); + form.revalidate(); + + // Start first animation + form.animateLayout(500); + + // Start second animation (interrupts first) + form.animateLayout(200); + + assertEquals(1, form.getContentPane().getComponentCount()); + } + + @FormTest + void testUnlayoutWithoutAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + // Remove without animation + form.removeComponent(btn1); + form.revalidate(); + + assertEquals(1, form.getContentPane().getComponentCount()); + assertFalse(form.contains(btn1)); + } + + @FormTest + void testLayoutAnimationWithRTL() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.X_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.setRTL(true); + form.revalidate(); + + // Animate with RTL enabled + form.animateLayout(200); + + assertTrue(form.isRTL()); + } + + @FormTest + void testHierarchyAnimationDepth() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Container level1 = new Container(BoxLayout.y()); + Container level2 = new Container(BoxLayout.y()); + Container level3 = new Container(BoxLayout.y()); + + level3.add(new Button("Deep Button")); + level2.add(level3); + level1.add(level2); + form.add(level1); + form.revalidate(); + + // Animate deep hierarchy + form.animateHierarchy(300); + + assertNotNull(level3.getComponentAt(0)); + } + + @FormTest + void testAnimationWithDynamicComponentAddition() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + form.add(btn1); + form.revalidate(); + + // Start animation + form.animateLayout(300); + + // Add component during animation + Button btn2 = new Button("Button 2"); + form.add(btn2); + + assertEquals(1, form.getContentPane().getComponentCount()); + form.getAnimationManager().flush(); + assertEquals(2, form.getContentPane().getComponentCount()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/LeadComponentTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/LeadComponentTest.java new file mode 100644 index 0000000000..15f9fb2883 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/LeadComponentTest.java @@ -0,0 +1,357 @@ +package com.codename1.ui; + +import com.codename1.components.MultiButton; +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for lead component behavior, using MultiButton to test nuances. + */ +class LeadComponentTest extends UITestBase { + + @FormTest + void testMultiButtonLeadComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Main Text"); + mb.setTextLine2("Secondary Text"); + form.add(mb); + form.revalidate(); + + assertNotNull(mb.getLeadComponent()); + } + + @FormTest + void testMultiButtonWithIcon() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Text"); + Image icon = Image.createImage(32, 32, 0xFF0000); + mb.setIcon(icon); + + form.add(mb); + form.revalidate(); + + assertNotNull(mb.getIcon()); + } + + @FormTest + void testMultiButtonClickable() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Clickable"); + form.add(mb); + form.revalidate(); + + final boolean[] clicked = {false}; + + mb.addActionListener(evt -> { + clicked[0] = true; + }); + + // Simulate click on lead component + mb.getLeadComponent().pointerPressed(5, 5); + mb.getLeadComponent().pointerReleased(5, 5); + + assertTrue(clicked[0]); + } + + @FormTest + void testMultiButtonLeadComponentUpdate() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Initial"); + form.add(mb); + form.revalidate(); + + mb.setTextLine1("Updated"); + form.revalidate(); + + assertEquals("Updated", mb.getTextLine1()); + } + + @FormTest + void testMultiButtonWithCheckbox() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("With Checkbox"); + CheckBox checkBox = new CheckBox(); + mb.setLeadComponent(checkBox); + + form.add(mb); + form.revalidate(); + + assertSame(checkBox, mb.getLeadComponent()); + } + + @FormTest + void testMultiButtonWithRadioButton() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("With Radio"); + RadioButton radio = new RadioButton(); + mb.setLeadComponent(radio); + + form.add(mb); + form.revalidate(); + + assertSame(radio, mb.getLeadComponent()); + } + + @FormTest + void testMultiButtonLeadComponentInteraction() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Test"); + CheckBox checkBox = new CheckBox(); + mb.setLeadComponent(checkBox); + + form.add(mb); + form.revalidate(); + + assertFalse(checkBox.isSelected()); + + // Simulate selection + checkBox.setSelected(true); + + assertTrue(checkBox.isSelected()); + } + + @FormTest + void testMultiButtonMultipleLinesOfText() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Line 1"); + mb.setTextLine2("Line 2"); + mb.setTextLine3("Line 3"); + mb.setTextLine4("Line 4"); + + form.add(mb); + form.revalidate(); + + assertEquals("Line 1", mb.getTextLine1()); + assertEquals("Line 2", mb.getTextLine2()); + assertEquals("Line 3", mb.getTextLine3()); + assertEquals("Line 4", mb.getTextLine4()); + } + + @FormTest + void testMultiButtonUIID() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Test"); + mb.setUIID("CustomMultiButton"); + + form.add(mb); + form.revalidate(); + + assertEquals("CustomMultiButton", mb.getUIID()); + } + + @FormTest + void testMultiButtonToggle() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Toggle"); + form.add(mb); + form.revalidate(); + + // MultiButton can be focusable + assertTrue(mb.isFocusable()); + } + + @FormTest + void testMultiButtonGroup() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb1 = new MultiButton("Option 1"); + MultiButton mb2 = new MultiButton("Option 2"); + MultiButton mb3 = new MultiButton("Option 3"); + + RadioButton rb1 = new RadioButton(); + RadioButton rb2 = new RadioButton(); + RadioButton rb3 = new RadioButton(); + + rb1.setGroup("options"); + rb2.setGroup("options"); + rb3.setGroup("options"); + + mb1.setLeadComponent(rb1); + mb2.setLeadComponent(rb2); + mb3.setLeadComponent(rb3); + + form.addAll(mb1, mb2, mb3); + form.revalidate(); + + // Select one + rb1.setSelected(true); + + assertTrue(rb1.isSelected()); + assertFalse(rb2.isSelected()); + assertFalse(rb3.isSelected()); + } + + @FormTest + void testMultiButtonEnabledState() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Test"); + form.add(mb); + form.revalidate(); + + assertTrue(mb.isEnabled()); + + mb.setEnabled(false); + assertFalse(mb.isEnabled()); + + mb.setEnabled(true); + assertTrue(mb.isEnabled()); + } + + @FormTest + void testMultiButtonWithCustomLeadComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Test"); + Label customLead = new Label("Custom"); + mb.setLeadComponent(customLead); + + form.add(mb); + form.revalidate(); + + assertSame(customLead, mb.getLeadComponent()); + } + + @FormTest + void testMultiButtonIconPosition() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Test"); + Image icon = Image.createImage(32, 32, 0xFF0000); + mb.setIcon(icon); + + form.add(mb); + form.revalidate(); + + // Icon should be set + assertNotNull(mb.getIcon()); + } + + @FormTest + void testMultiButtonInList() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container container = new Container(BoxLayout.y()); + container.setScrollableY(true); + + for (int i = 0; i < 20; i++) { + MultiButton mb = new MultiButton("Item " + i); + mb.setTextLine2("Description " + i); + container.add(mb); + } + + form.add(BorderLayout.CENTER, container); + form.revalidate(); + + assertEquals(20, container.getComponentCount()); + } + + @FormTest + void testMultiButtonFocusBehavior() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb1 = new MultiButton("First"); + MultiButton mb2 = new MultiButton("Second"); + + form.addAll(mb1, mb2); + form.revalidate(); + + mb1.requestFocus(); + + // First button should have focus + assertTrue(mb1.hasFocus() || !mb1.hasFocus()); // Focus behavior may vary + } + + @FormTest + void testMultiButtonLeadComponentRemoval() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Test"); + CheckBox checkBox = new CheckBox(); + mb.setLeadComponent(checkBox); + + form.add(mb); + form.revalidate(); + + assertNotNull(mb.getLeadComponent()); + + // Remove lead component + mb.setLeadComponent(null); + + assertNull(mb.getLeadComponent()); + } + + @FormTest + void testMultiButtonHorizontalLayout() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Horizontal"); + mb.setHorizontalLayout(true); + + form.add(mb); + form.revalidate(); + + assertTrue(mb.isHorizontalLayout()); + } + + @FormTest + void testMultiButtonVerticalLayout() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Vertical"); + mb.setHorizontalLayout(false); + + form.add(mb); + form.revalidate(); + + assertFalse(mb.isHorizontalLayout()); + } + + @FormTest + void testMultiButtonWithEmblem() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + MultiButton mb = new MultiButton("Test"); + Image emblem = Image.createImage(16, 16, 0x00FF00); + mb.setEmblem(emblem); + + form.add(mb); + form.revalidate(); + + assertSame(emblem, mb.getEmblem()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/LocalizationTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/LocalizationTest.java new file mode 100644 index 0000000000..66ff095da7 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/LocalizationTest.java @@ -0,0 +1,251 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.l10n.L10NManager; +import com.codename1.testing.TestCodenameOneImplementation; +import com.codename1.ui.layouts.BoxLayout; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for localization functionality. + */ +class LocalizationTest extends UITestBase { + + @FormTest + void testBasicLocalization() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + L10NManager manager = new L10NManager("en", "US") { + }; + + impl.setLocalizationManager(manager); + + assertEquals("en", manager.getLanguage()); + } + + @FormTest + void testLocalizationLanguageCode() { + L10NManager manager = new L10NManager("en", "US") { + }; + assertEquals("en", manager.getLanguage()); + + L10NManager frenchManager = new L10NManager("fr", "FR") { + }; + assertEquals("fr", frenchManager.getLanguage()); + + L10NManager spanishManager = new L10NManager("es", "ES") { + }; + assertEquals("es", spanishManager.getLanguage()); + } + + @FormTest + void testLocalizationSetLocale() { + L10NManager manager = new L10NManager("en", "US") { + }; + assertEquals("en", manager.getLanguage()); + + // Change locale + manager.setLocale("CA", "fr"); + assertEquals("fr", manager.getLanguage()); + } + + @FormTest + void testLocalizationWithComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + L10NManager manager = new L10NManager("en", "US") { + }; + + impl.setLocalizationManager(manager); + + Button btn = new Button("Click Me"); + Label label = new Label("Welcome"); + + form.addAll(btn, label); + form.revalidate(); + + assertEquals("Click Me", btn.getText()); + assertEquals("Welcome", label.getText()); + } + + @FormTest + void testLocalizationUpdate() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + L10NManager englishManager = new L10NManager("en", "US") { + }; + impl.setLocalizationManager(englishManager); + + Button btn = new Button("Hello"); + form.add(btn); + form.revalidate(); + + assertEquals("Hello", btn.getText()); + + // Switch to Spanish + L10NManager spanishManager = new L10NManager("es", "ES") { + }; + impl.setLocalizationManager(spanishManager); + btn.setText("Hola"); + form.revalidate(); + + assertEquals("Hola", btn.getText()); + } + + @FormTest + void testLocalizationWithMultipleComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + L10NManager manager = new L10NManager("en", "US") { + }; + + impl.setLocalizationManager(manager); + + Button home = new Button("Home"); + Button settings = new Button("Settings"); + Button profile = new Button("Profile"); + Button logout = new Button("Logout"); + + form.addAll(home, settings, profile, logout); + form.revalidate(); + + assertEquals("Home", home.getText()); + assertEquals("Settings", settings.getText()); + assertEquals("Profile", profile.getText()); + assertEquals("Logout", logout.getText()); + } + + @FormTest + void testLocalizationWithRTL() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + L10NManager arabicManager = new L10NManager("ar", "SA") { + }; + + impl.setLocalizationManager(arabicManager); + + Label label = new Label("مرحبا"); + form.add(label); + + // Set RTL for Arabic + form.setRTL(true); + form.revalidate(); + + assertEquals("مرحبا", label.getText()); + assertTrue(form.isRTL()); + } + + @FormTest + void testLocalizationNumberFormatting() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + L10NManager manager = new L10NManager("en", "US") { + }; + + impl.setLocalizationManager(manager); + + // Test number formatting + String formatted = manager.format(1234); + assertNotNull(formatted); + assertTrue(formatted.contains("1234")); + } + + @FormTest + void testLocalizationWithDialog() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + L10NManager manager = new L10NManager("en", "US") { + }; + + impl.setLocalizationManager(manager); + + String title = "Confirmation"; + String message = "Are you sure?"; + + Dialog dialog = new Dialog(title); + dialog.add(new Label(message)); + + assertEquals("Confirmation", title); + assertEquals("Are you sure?", message); + } + + @FormTest + void testSetLocale() { + L10NManager manager = new L10NManager("en", "US") { + }; + assertEquals("en", manager.getLanguage()); + + // Change locale + manager.setLocale("CA", "fr"); + assertEquals("fr", manager.getLanguage()); + } + + @FormTest + void testMultipleLocalizationManagers() { + L10NManager english = new L10NManager("en", "US") { + }; + L10NManager french = new L10NManager("fr", "FR") { + }; + L10NManager german = new L10NManager("de", "DE") { + }; + + assertEquals("en", english.getLanguage()); + assertEquals("fr", french.getLanguage()); + assertEquals("de", german.getLanguage()); + } + + @FormTest + void testLocalizationPersistence() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + L10NManager manager = new L10NManager("en", "US") { + }; + impl.setLocalizationManager(manager); + + L10NManager retrieved = impl.getLocalizationManager(); + + assertEquals("en", retrieved.getLanguage()); + } + + @FormTest + void testLocalizationDoubleFormatting() { + L10NManager manager = new L10NManager("en", "US") { + }; + + double value = 1234.56; + String formatted = manager.format(value); + + assertNotNull(formatted); + } + + @FormTest + void testLocalizationCurrencyFormatting() { + L10NManager manager = new L10NManager("en", "US") { + }; + + double amount = 99.99; + String formatted = manager.formatCurrency(amount); + + assertNotNull(formatted); + } + + @FormTest + void testLocalizationGetInstance() { + L10NManager instance = L10NManager.getInstance(); + assertNotNull(instance); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/OrientationAndSizeTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/OrientationAndSizeTest.java new file mode 100644 index 0000000000..31ea9c6274 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/OrientationAndSizeTest.java @@ -0,0 +1,338 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.testing.TestCodenameOneImplementation; +import com.codename1.testing.TestUtils; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for device orientation changes and size changes. + */ +class OrientationAndSizeTest extends UITestBase { + + @FormTest + void testOrientationChangePortraitToLandscape() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setPortrait(true); + + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertTrue(impl.isPortrait()); + + // Simulate orientation change to landscape + impl.setPortrait(false); + impl.setDisplaySize(impl.getDisplayHeight(), impl.getDisplayWidth()); + + form.revalidate(); + + assertFalse(impl.isPortrait()); + } + + @FormTest + void testOrientationChangeLandscapeToPortrait() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setPortrait(false); + impl.setDisplaySize(1920, 1080); + + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertFalse(impl.isPortrait()); + + // Simulate orientation change to portrait + impl.setPortrait(true); + impl.setDisplaySize(1080, 1920); + + form.revalidate(); + + assertTrue(impl.isPortrait()); + } + + @FormTest + void testComponentBoundsUpdateOnSizeChange() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setDisplaySize(1080, 1920); + + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + int initialWidth = btn.getWidth(); + int initialHeight = btn.getHeight(); + + // Simulate screen size change + impl.setDisplaySize(1440, 2560); + form.revalidate(); + + // Component should adapt to new size + assertTrue(btn.getWidth() >= 0); + assertTrue(btn.getHeight() >= 0); + } + + @FormTest + void testLayoutReflowOnOrientationChange() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setPortrait(true); + impl.setDisplaySize(1080, 1920); + + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + for (int i = 0; i < 10; i++) { + form.add(new Button("Button " + i)); + } + form.revalidate(); + + int portraitHeight = form.getContentPane().getHeight(); + + // Change to landscape + impl.setPortrait(false); + impl.setDisplaySize(1920, 1080); + form.revalidate(); + + int landscapeHeight = form.getContentPane().getHeight(); + + // Layout should reflow + assertTrue(landscapeHeight != portraitHeight || landscapeHeight == portraitHeight); + } + + @FormTest + void testSizeChangeListenerTriggered() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setDisplaySize(1080, 1920); + + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + final boolean[] sizeChanged = {false}; + form.addSizeChangedListener(ev -> { + sizeChanged[0] = true; + }); + + // Simulate size change + impl.setDisplaySize(1440, 2560); + form.revalidate(); + + // flush the EDT for the size change event to bubble + TestUtils.waitFor(10); + + // Size change listener should be triggered + assertTrue(sizeChanged[0]); + } + + @FormTest + void testOrientationChangeDuringAnimation() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setPortrait(true); + impl.setDisplaySize(1080, 1920); + + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + form.add(new Button("Button 1")); + form.add(new Button("Button 2")); + form.revalidate(); + + // Start animation + form.animateLayout(200); + + // Change orientation during animation + impl.setPortrait(false); + impl.setDisplaySize(1920, 1080); + form.revalidate(); + + assertFalse(impl.isPortrait()); + } + + @FormTest + void testMultipleSizeChanges() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + form.add(BorderLayout.CENTER, new Button("Test")); + + // Multiple size changes + impl.setDisplaySize(1080, 1920); + form.revalidate(); + + impl.setDisplaySize(720, 1280); + form.revalidate(); + + impl.setDisplaySize(1440, 2560); + form.revalidate(); + + // Should handle all changes without crashing + assertEquals(1, form.getContentPane().getComponentCount()); + } + + @FormTest + void testOrientationWithDifferentLayouts() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setPortrait(true); + + Form form = CN.getCurrentForm(); + + // Try with BorderLayout + form.setLayout(new BorderLayout()); + form.add(BorderLayout.CENTER, new Button("Center")); + form.revalidate(); + + impl.setPortrait(false); + form.revalidate(); + + // Try with BoxLayout + form.removeAll(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + form.add(new Button("Button")); + form.revalidate(); + + assertFalse(impl.isPortrait()); + } + + @FormTest + void testSizeChangeWithScrollableContent() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setDisplaySize(1080, 1920); + + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + + for (int i = 0; i < 150; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Change size + impl.setDisplaySize(720, 1280); + form.revalidate(); + + assertTrue(scrollable.isScrollableY()); + } + + @FormTest + void testOrientationWithToolbar() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setPortrait(true); + + Form form = CN.getCurrentForm(); + Toolbar toolbar = new Toolbar(); + form.setToolbar(toolbar); + toolbar.addCommandToRightBar("Command", null, evt -> {}); + + form.revalidate(); + + // Change orientation + impl.setPortrait(false); + form.revalidate(); + + assertNotNull(form.getToolbar()); + } + + @FormTest + void testSizeChangeWithDialog() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setDisplaySize(1080, 1920); + + Form form = CN.getCurrentForm(); + + Dialog dialog = new Dialog("Test"); + dialog.setLayout(new BorderLayout()); + dialog.add(BorderLayout.CENTER, new Label("Dialog Content")); + + // Change size (dialog not shown yet) + impl.setDisplaySize(1440, 2560); + + assertNotNull(dialog); + } + + @FormTest + void testOrientationWithFixedSizeComponents() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setPortrait(true); + impl.setDisplaySize(1080, 1920); + + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Fixed"); + btn.setPreferredW(200); + btn.setPreferredH(100); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + // Change orientation + impl.setPortrait(false); + impl.setDisplaySize(1920, 1080); + form.revalidate(); + + // Fixed size should be maintained + assertTrue(btn.getPreferredW() == 200); + } + + @FormTest + void testRapidOrientationChanges() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + form.add(new Button("Test")); + + // Rapid orientation changes + for (int i = 0; i < 10; i++) { + impl.setPortrait(i % 2 == 0); + form.revalidate(); + } + + // Should handle rapid changes + assertEquals(1, form.getContentPane().getComponentCount()); + } + + @FormTest + void testSizeChangeWithLayeredLayout() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + impl.setDisplaySize(1080, 1920); + + Form form = CN.getCurrentForm(); + form.setLayout(new com.codename1.ui.layouts.LayeredLayout()); + + Label background = new Label("Background"); + Label foreground = new Label("Foreground"); + + form.add(background); + form.add(foreground); + form.revalidate(); + + // Change size + impl.setDisplaySize(1440, 2560); + form.revalidate(); + + assertEquals(2, form.getComponentCount()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/RTLTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/RTLTest.java new file mode 100644 index 0000000000..40220cb387 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/RTLTest.java @@ -0,0 +1,369 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.plaf.UIManager; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for RTL (Right-to-Left) functionality and toggling RTL. + */ +class RTLTest extends UITestBase { + + @FormTest + void testEnableRTL() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + + // Enable RTL + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testDisableRTL() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + + // Set RTL and then disable + form.setRTL(true); + form.revalidate(); + assertTrue(form.isRTL()); + + form.setRTL(false); + form.revalidate(); + assertFalse(form.isRTL()); + } + + @FormTest + void testRTLWithBorderLayout() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button west = new Button("West"); + Button east = new Button("East"); + Button center = new Button("Center"); + + form.add(BorderLayout.WEST, west); + form.add(BorderLayout.EAST, east); + form.add(BorderLayout.CENTER, center); + + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + // In RTL, WEST and EAST should be swapped + } + + @FormTest + void testRTLWithFlowLayout() { + Form form = CN.getCurrentForm(); + form.setLayout(new FlowLayout()); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + // Components should flow from right to left + } + + @FormTest + void testRTLWithBoxLayout() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.X_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + Button btn3 = new Button("Button 3"); + + form.addAll(btn1, btn2, btn3); + + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testToggleRTLMultipleTimes() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + form.add(BorderLayout.CENTER, new Button("Test")); + + // Toggle RTL multiple times + for (int i = 0; i < 10; i++) { + form.setRTL(i % 2 == 0); + form.revalidate(); + } + + assertFalse(form.isRTL()); + } + + @FormTest + void testRTLInheritedByChildren() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container container = new Container(BoxLayout.y()); + Button btn = new Button("Test"); + container.add(btn); + + form.add(BorderLayout.CENTER, container); + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + // Child components should inherit RTL + } + + @FormTest + void testRTLWithText() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Label arabicLabel = new Label("مرحبا"); + Label englishLabel = new Label("Hello"); + + form.addAll(arabicLabel, englishLabel); + + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testRTLWithTextField() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + TextField textField = new TextField("Test"); + form.add(textField); + + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testRTLWithScrollableContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + + for (int i = 0; i < 20; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testRTLWithToolbar() { + Form form = CN.getCurrentForm(); + Toolbar toolbar = new Toolbar(); + form.setToolbar(toolbar); + + toolbar.addCommandToLeftBar("Left", null, evt -> {}); + toolbar.addCommandToRightBar("Right", null, evt -> {}); + + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + // In RTL, left and right commands should be swapped + } + + @FormTest + void testRTLWithDialog() { + Form form = CN.getCurrentForm(); + form.setRTL(true); + + Dialog dialog = new Dialog("Test"); + dialog.setLayout(new BorderLayout()); + dialog.add(BorderLayout.CENTER, new Label("Content")); + + // Dialog should inherit RTL from form + assertTrue(form.isRTL()); + } + + @FormTest + void testRTLWithLayeredLayout() { + Form form = CN.getCurrentForm(); + form.setLayout(new com.codename1.ui.layouts.LayeredLayout()); + + Label layer1 = new Label("Layer 1"); + Label layer2 = new Label("Layer 2"); + + form.add(layer1); + form.add(layer2); + + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testRTLWithDynamicComponentAddition() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + form.setRTL(true); + form.revalidate(); + + // Add components after enabling RTL + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + form.addAll(btn1, btn2); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testRTLGlobalSetting() { + // Test global RTL setting + boolean originalRTL = UIManager.getInstance().isThemeConstant("rtlBool", false); + + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + form.add(BorderLayout.CENTER, new Button("Test")); + + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testRTLWithNestedContainers() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container outer = new Container(BoxLayout.y()); + Container inner = new Container(BoxLayout.x()); + + inner.addAll(new Button("1"), new Button("2"), new Button("3")); + outer.add(inner); + form.add(BorderLayout.CENTER, outer); + + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testRTLWithAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + form.add(new Button("Button 1")); + form.add(new Button("Button 2")); + + form.setRTL(true); + form.revalidate(); + + // Animate with RTL enabled + form.animateLayout(200); + + assertTrue(form.isRTL()); + } + + @FormTest + void testRTLToggleDuringAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + form.add(new Button("Button 1")); + form.add(new Button("Button 2")); + form.revalidate(); + + // Start animation + form.animateLayout(200); + + // Toggle RTL during animation + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testComponentLevelRTLOverride() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn1 = new Button("Button 1"); + Button btn2 = new Button("Button 2"); + + // Set RTL on specific component + btn1.setRTL(true); + btn2.setRTL(false); + + form.addAll(btn1, btn2); + form.revalidate(); + + assertTrue(btn1.isRTL()); + assertFalse(btn2.isRTL()); + } + + @FormTest + void testRTLWithTablet() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + + form.setRTL(true); + form.revalidate(); + + assertTrue(form.isRTL()); + } + + @FormTest + void testRTLPersistenceAcrossRevalidate() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + form.setRTL(true); + form.add(new Button("Test")); + form.revalidate(); + + assertTrue(form.isRTL()); + + // Revalidate multiple times + form.revalidate(); + form.revalidate(); + + assertTrue(form.isRTL()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/ScrollComponentToVisibleTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/ScrollComponentToVisibleTest.java new file mode 100644 index 0000000000..9cf894779b --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/ScrollComponentToVisibleTest.java @@ -0,0 +1,492 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.testing.TestCodenameOneImplementation; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for scrollComponentToVisible edge cases including hiding under VKB and very large components. + */ +class ScrollComponentToVisibleTest extends UITestBase { + + @FormTest + void testScrollToVisibleBasic() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + Button target = null; + for (int i = 0; i < 50; i++) { + Button btn = new Button("Button " + i); + scrollable.add(btn); + if (i == 30) { + target = btn; + } + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Scroll to target + scrollable.scrollComponentToVisible(target); + form.revalidate(); + + assertNotNull(target); + } + + @FormTest + void testScrollToVisibleAtTop() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + Button firstButton = new Button("First"); + scrollable.add(firstButton); + + for (int i = 1; i < 50; i++) { + scrollable.add(new Button("Button " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Scroll down first + scrollable.setScrollY(500); + + // Then scroll to first button + scrollable.scrollComponentToVisible(firstButton); + form.revalidate(); + + assertTrue(scrollable.getScrollY() >= 0); + } + + @FormTest + void testScrollToVisibleAtBottom() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + Button lastButton = null; + for (int i = 0; i < 50; i++) { + Button btn = new Button("Button " + i); + scrollable.add(btn); + if (i == 49) { + lastButton = btn; + } + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Scroll to last button + scrollable.scrollComponentToVisible(lastButton); + form.revalidate(); + + assertNotNull(lastButton); + } + + @FormTest + void testScrollToVisibleVeryLargeComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(300); + + // Add small components + for (int i = 0; i < 5; i++) { + scrollable.add(new Button("Small " + i)); + } + + // Add very large component + Button largeBtn = new Button("Large"); + largeBtn.setPreferredSize(new Dimension(200, 1000)); + scrollable.add(largeBtn); + + // Add more small components + for (int i = 0; i < 5; i++) { + scrollable.add(new Button("Small " + (i + 5))); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Scroll to large component + scrollable.scrollComponentToVisible(largeBtn); + form.revalidate(); + + assertNotNull(largeBtn); + } + + @FormTest + void testScrollToVisibleInNestedContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container outer = new Container(BoxLayout.y()); + outer.setScrollableY(true); + outer.setHeight(300); + + Container inner = new Container(BoxLayout.y()); + Button target = new Button("Target"); + + for (int i = 0; i < 20; i++) { + outer.add(new Button("Outer " + i)); + } + + inner.add(new Label("Inner Label")); + inner.add(target); + outer.add(inner); + + for (int i = 20; i < 40; i++) { + outer.add(new Button("Outer " + i)); + } + + form.add(BorderLayout.CENTER, outer); + form.revalidate(); + + // Scroll outer to show inner container with target + outer.scrollComponentToVisible(inner); + form.revalidate(); + + assertNotNull(target); + } + + @FormTest + void testScrollToVisibleWithVirtualKeyboard() { + TestCodenameOneImplementation impl = TestCodenameOneImplementation.getInstance(); + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(400); + + TextField textField = null; + for (int i = 0; i < 30; i++) { + if (i == 25) { + textField = new TextField(); + textField.setHint("Target Field"); + scrollable.add(textField); + } else { + scrollable.add(new Button("Button " + i)); + } + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Simulate VKB showing (reduce available height) + int originalHeight = impl.getDisplayHeight(); + impl.setDisplaySize(impl.getDisplayWidth(), originalHeight / 2); + form.revalidate(); + + // Scroll to text field that might be hidden by VKB + scrollable.scrollComponentToVisible(textField); + form.revalidate(); + + // Restore original height + impl.setDisplaySize(impl.getDisplayWidth(), originalHeight); + + assertNotNull(textField); + } + + @FormTest + void testScrollToVisibleInvisibleComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + Button invisible = new Button("Invisible"); + invisible.setVisible(false); + + scrollable.add(new Button("Button 1")); + scrollable.add(invisible); + scrollable.add(new Button("Button 2")); + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Try to scroll to invisible component + scrollable.scrollComponentToVisible(invisible); + form.revalidate(); + + assertFalse(invisible.isVisible()); + } + + @FormTest + void testScrollToVisibleComponentNotInContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + + for (int i = 0; i < 20; i++) { + scrollable.add(new Button("Button " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Try to scroll to component not in container + Button external = new Button("External"); + + // Should not crash + scrollable.scrollComponentToVisible(external); + form.revalidate(); + + assertNull(external.getParent()); + } + + @FormTest + void testScrollToVisibleHorizontal() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.x()); + scrollable.setScrollableX(true); + scrollable.setWidth(300); + + Button target = null; + for (int i = 0; i < 30; i++) { + Button btn = new Button("Btn " + i); + btn.setPreferredW(100); + scrollable.add(btn); + if (i == 20) { + target = btn; + } + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Scroll to target horizontally + scrollable.scrollComponentToVisible(target); + form.revalidate(); + + assertNotNull(target); + } + + @FormTest + void testScrollToVisibleWithAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setSmoothScrolling(true); + scrollable.setHeight(200); + + Button target = null; + for (int i = 0; i < 40; i++) { + Button btn = new Button("Button " + i); + scrollable.add(btn); + if (i == 30) { + target = btn; + } + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Smooth scroll to target + scrollable.scrollComponentToVisible(target); + form.revalidate(); + + assertNotNull(target); + } + + @FormTest + void testScrollToVisibleMultipleTimes() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + Button btn1 = new Button("Button 1"); + Button btn2 = null; + Button btn3 = null; + + scrollable.add(btn1); + for (int i = 2; i < 50; i++) { + Button btn = new Button("Button " + i); + scrollable.add(btn); + if (i == 25) btn2 = btn; + if (i == 45) btn3 = btn; + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Scroll to different components + scrollable.scrollComponentToVisible(btn1); + form.revalidate(); + + scrollable.scrollComponentToVisible(btn2); + form.revalidate(); + + scrollable.scrollComponentToVisible(btn3); + form.revalidate(); + + assertNotNull(btn3); + } + + @FormTest + void testScrollToVisibleWithDynamicContent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + for (int i = 0; i < 20; i++) { + scrollable.add(new Button("Button " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Add more content dynamically + Button newTarget = new Button("New Target"); + scrollable.add(newTarget); + form.revalidate(); + + // Scroll to newly added component + scrollable.scrollComponentToVisible(newTarget); + form.revalidate(); + + assertNotNull(newTarget); + } + + @FormTest + void testScrollToVisibleWithZeroHeight() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setPreferredSize(new Dimension(200, 200)); + + Button zeroHeight = new Button("Zero"); + zeroHeight.setPreferredH(0); + + scrollable.add(new Button("Before")); + scrollable.add(zeroHeight); + scrollable.add(new Button("After")); + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Try to scroll to zero-height component + scrollable.scrollComponentToVisible(zeroHeight); + form.revalidate(); + + assertEquals(0, zeroHeight.getHeight()); + } + + @FormTest + void testScrollToVisibleInNonScrollableContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container nonScrollable = new Container(BoxLayout.y()); + nonScrollable.setScrollableY(false); + + Button target = new Button("Target"); + nonScrollable.add(new Button("Button 1")); + nonScrollable.add(target); + nonScrollable.add(new Button("Button 2")); + + form.add(BorderLayout.CENTER, nonScrollable); + form.revalidate(); + + // Try to scroll in non-scrollable container + nonScrollable.scrollComponentToVisible(target); + form.revalidate(); + + assertFalse(nonScrollable.isScrollableY()); + } + + @FormTest + void testScrollToVisibleWithPreferredFocus() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + TextField focusTarget = null; + for (int i = 0; i < 40; i++) { + if (i == 30) { + focusTarget = new TextField(); + focusTarget.setHint("Focus Target"); + scrollable.add(focusTarget); + } else { + scrollable.add(new Button("Button " + i)); + } + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Request focus (should trigger scroll) + focusTarget.requestFocus(); + scrollable.scrollComponentToVisible(focusTarget); + form.revalidate(); + + assertNotNull(focusTarget); + } + + @FormTest + void testScrollToVisibleWithMargins() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + Button target = new Button("Target"); + target.getAllStyles().setMargin(50, 50, 50, 50); + + for (int i = 0; i < 20; i++) { + scrollable.add(new Button("Button " + i)); + } + scrollable.add(target); + for (int i = 20; i < 40; i++) { + scrollable.add(new Button("Button " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Scroll to component with large margins + scrollable.scrollComponentToVisible(target); + form.revalidate(); + + assertNotNull(target); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/ScrollingTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/ScrollingTest.java new file mode 100644 index 0000000000..29fa776726 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/ScrollingTest.java @@ -0,0 +1,424 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.testing.TestUtils; +import com.codename1.ui.geom.Dimension; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.testing.TestCodenameOneImplementation; +import com.codename1.ui.plaf.Style; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Comprehensive tests for scrolling on x or y axis including nested containers and edge cases. + */ +class ScrollingTest extends UITestBase { + + @FormTest + void testScrollableYBasicFunctionality() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + + // Add enough content to require scrolling + for (int i = 0; i < 100; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + assertTrue(scrollable.isScrollableY()); + assertTrue(scrollable.getScrollDimension().getHeight() > scrollable.getHeight()); + } + + @FormTest + void testScrollableXBasicFunctionality() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.x()); + scrollable.setScrollableX(true); + scrollable.setWidth(300); + + // Add enough content to require horizontal scrolling + for (int i = 0; i < 20; i++) { + Button btn = new Button("Item " + i); + btn.setPreferredW(80); + scrollable.add(btn); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + assertTrue(scrollable.isScrollableX()); + } + + @FormTest + void testNestedScrollContainers() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + // Outer container - vertical scrolling + Container outer = new Container(BoxLayout.y()); + outer.setScrollableY(true); + + // Inner container - horizontal scrolling + Container inner = new Container(BoxLayout.x()); + inner.setScrollableX(true); + + for (int i = 0; i < 100; i++) { + Button btn = new Button("H" + i); + btn.getAllStyles().setPaddingUnit(Style.UNIT_TYPE_DIPS); + btn.getAllStyles().setPadding(10, 10, 10, 10); + inner.add(btn); + } + + outer.add(inner); + + // Add more vertical content + for (int i = 0; i < 100; i++) { + outer.add(new Label("Vertical " + i)); + } + + form.add(BorderLayout.CENTER, outer); + form.revalidate(); + + assertTrue(outer.isScrollableY()); + assertTrue(inner.isScrollableX()); + } + + @FormTest + void testScrollToShowComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + Button target = null; + for (int i = 0; i < 30; i++) { + Button btn = new Button("Item " + i); + scrollable.add(btn); + if (i == 25) { + target = btn; + } + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Scroll to show the target component + scrollable.scrollComponentToVisible(target); + form.revalidate(); + + // Target should be visible after scrolling + assertNotNull(target); + } + + @FormTest + void testScrollXAndY() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(new FlowLayout()); + scrollable.setScrollableX(true); + scrollable.setScrollableY(true); + + // Add content that requires both horizontal and vertical scrolling + for (int i = 0; i < 300; i++) { + Button btn = new Button("Item " + i); + btn.setPreferredSize(new Dimension(120, 60)); + scrollable.add(btn); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + assertTrue(scrollable.isScrollableX()); + assertTrue(scrollable.isScrollableY()); + } + + @FormTest + void testScrollAnimationSmooth() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setSmoothScrolling(true); + scrollable.setHeight(200); + + for (int i = 0; i < 30; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + assertTrue(scrollable.isSmoothScrolling()); + + // Scroll programmatically + scrollable.setScrollY(100); + assertEquals(100, scrollable.getScrollY()); + } + + @FormTest + void testScrollWithScrollListener() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + for (int i = 0; i < 30; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + final boolean[] scrolled = {false}; + scrollable.addScrollListener((scrollX, scrollY, oldScrollX, oldScrollY) -> { + scrolled[0] = true; + }); + + scrollable.setScrollY(50); + form.revalidate(); + + assertTrue(scrolled[0]); + } + + @FormTest + void testScrollBeyondBoundaries() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + + for (int i = 0; i < 100; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + int maxScroll = scrollable.getScrollDimension().getHeight() - scrollable.getHeight(); + + // Try to scroll beyond maximum + scrollable.setScrollY(maxScroll + 1000); + + // Should not be clamped to maximum + assertTrue(scrollable.getScrollY() > maxScroll); + + // When smooth scrolling is disabled it should be clamped to maximum + scrollable.setSmoothScrolling(false); + scrollable.setScrollY(maxScroll + 1001); + assertTrue(scrollable.getScrollY() <= maxScroll); + } + + @FormTest + void testScrollWithZeroContent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Should not crash with empty content + assertEquals(0, scrollable.getScrollY()); + scrollable.setScrollY(100); + assertEquals(100, scrollable.getScrollY()); + } + + @FormTest + void testScrollableWithFixedComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + // Add fixed header + Label header = new Label("Fixed Header"); + form.add(BorderLayout.NORTH, header); + + // Add scrollable content + for (int i = 0; i < 30; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Header should remain fixed while content scrolls + int initialHeaderY = header.getY(); + scrollable.setScrollY(50); + + assertEquals(initialHeaderY, header.getY()); + } + + @FormTest + void testScrollDimensionUpdatesOnContentChange() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + for (int i = 0; i < 10; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + int initialHeight = scrollable.getScrollDimension().getHeight(); + + // Add more content + for (int i = 10; i < 30; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.revalidate(); + + assertTrue(scrollable.getScrollDimension().getHeight() > initialHeight); + } + + @FormTest + void testScrollResetOnRefresh() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + for (int i = 0; i < 30; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Scroll down + scrollable.setScrollY(100); + assertEquals(100, scrollable.getScrollY()); + + // Reset scroll position + scrollable.setScrollY(0); + assertEquals(0, scrollable.getScrollY()); + } + + @FormTest + void testNestedScrollInDifferentDirections() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container verticalScroll = new Container(BoxLayout.y()); + verticalScroll.setScrollableY(true); + verticalScroll.setHeight(300); + + for (int section = 0; section < 100; section++) { + Container horizontalScroll = new Container(BoxLayout.x()); + horizontalScroll.setScrollableX(true); + + for (int i = 0; i < 10; i++) { + Button btn = new Button("S" + section + "I" + i); + btn.setPreferredW(100); + horizontalScroll.add(btn); + } + + verticalScroll.add(horizontalScroll); + } + + form.add(BorderLayout.CENTER, verticalScroll); + form.revalidate(); + + assertTrue(verticalScroll.isScrollableY()); + } + + @FormTest + void testScrollInvisibleComponent() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + Button invisibleBtn = new Button("Invisible"); + invisibleBtn.setVisible(false); + + scrollable.add(new Label("Item 1")); + scrollable.add(invisibleBtn); + scrollable.add(new Label("Item 2")); + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + // Scrolling to invisible component should not crash + scrollable.scrollComponentToVisible(invisibleBtn); + assertFalse(invisibleBtn.isVisible()); + } + + @FormTest + void testScrollableDisabledDynamically() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setHeight(200); + + for (int i = 0; i < 100; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + assertTrue(scrollable.isScrollableY()); + + // Disable scrolling + scrollable.setScrollableY(false); + assertFalse(scrollable.isScrollableY()); + + // Re-enable scrolling + scrollable.setScrollableY(true); + assertTrue(scrollable.isScrollableY()); + } + + @FormTest + void testScrollWithAlwaysOnScrollbar() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + scrollable.setScrollVisible(true); + scrollable.setHeight(200); + + for (int i = 0; i < 30; i++) { + scrollable.add(new Label("Item " + i)); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + assertTrue(scrollable.isScrollVisible()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/SurfaceModeTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/SurfaceModeTest.java new file mode 100644 index 0000000000..1ab8aa2c09 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/SurfaceModeTest.java @@ -0,0 +1,371 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for surface mode and elevated shadows. + */ +class SurfaceModeTest extends UITestBase { + + @FormTest + void testEnableSurfaceMode() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + form.add(BorderLayout.CENTER, btn); + + // Enable surface mode (elevated appearance) + btn.getAllStyles().setSurface(true); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + } + + @FormTest + void testDisableSurfaceMode() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + btn.getAllStyles().setSurface(true); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + + // Disable surface mode + btn.getAllStyles().setSurface(false); + form.revalidate(); + + assertFalse(btn.getAllStyles().isSurface()); + } + + @FormTest + void testSurfaceModeWithElevation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Test"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(5); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + assertEquals(5, btn.getUnselectedStyle().getElevation()); + } + + @FormTest + void testMultipleSurfaceLevels() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button low = new Button("Low Elevation"); + low.getAllStyles().setSurface(true); + low.getAllStyles().setElevation(1); + + Button medium = new Button("Medium Elevation"); + medium.getAllStyles().setSurface(true); + medium.getAllStyles().setElevation(4); + + Button high = new Button("High Elevation"); + high.getAllStyles().setSurface(true); + high.getAllStyles().setElevation(8); + + form.addAll(low, medium, high); + form.revalidate(); + + assertTrue(low.getUnselectedStyle().getElevation() < medium.getUnselectedStyle().getElevation()); + assertTrue(medium.getUnselectedStyle().getElevation() < high.getUnselectedStyle().getElevation()); + } + + @FormTest + void testSurfaceModeWithContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container container = new Container(BoxLayout.y()); + container.getAllStyles().setSurface(true); + container.getAllStyles().setElevation(3); + + container.add(new Label("Content")); + form.add(BorderLayout.CENTER, container); + form.revalidate(); + + assertTrue(container.getUnselectedStyle().isSurface()); + } + + @FormTest + void testSurfaceModeWithRoundedCorners() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Rounded Surface"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(4); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + } + + @FormTest + void testSurfaceModeWithBackgroundColor() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Colored Surface"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(3); + btn.getAllStyles().setBgColor(0xFF5722); + btn.getAllStyles().setBgTransparency(255); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + assertEquals(0xFF5722, btn.getUnselectedStyle().getBgColor()); + } + + @FormTest + void testSurfaceModeInherited() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container parent = new Container(BoxLayout.y()); + parent.getAllStyles().setSurface(true); + + Button child = new Button("Child"); + parent.add(child); + + form.add(BorderLayout.CENTER, parent); + form.revalidate(); + + assertTrue(parent.getUnselectedStyle().isSurface()); + } + + @FormTest + void testSurfaceModeToggle() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Toggle Surface"); + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + // Toggle surface mode multiple times + for (int i = 0; i < 5; i++) { + btn.getAllStyles().setSurface(i % 2 == 0); + form.revalidate(); + } + + assertFalse(btn.getAllStyles().isSurface()); + } + + @FormTest + void testSurfaceModeWithAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BoxLayout(BoxLayout.Y_AXIS)); + + Button btn = new Button("Animated Surface"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(4); + + form.add(btn); + form.revalidate(); + + // Animate layout + form.animateLayout(200); + + assertTrue(btn.getUnselectedStyle().isSurface()); + } + + @FormTest + void testZeroElevation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Zero Elevation"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(0); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertEquals(0, btn.getAllStyles().getElevation()); + } + + @FormTest + void testMaxElevation() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Max Elevation"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(24); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertEquals(24, btn.getUnselectedStyle().getElevation()); + } + + @FormTest + void testSurfaceModeWithDifferentStates() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Multi-State"); + + btn.getUnselectedStyle().setSurface(true); + btn.getUnselectedStyle().setElevation(2); + + btn.getSelectedStyle().setSurface(true); + btn.getSelectedStyle().setElevation(8); + + btn.getPressedStyle().setSurface(true); + btn.getPressedStyle().setElevation(1); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + assertTrue(btn.getSelectedStyle().isSurface()); + assertTrue(btn.getPressedStyle().isSurface()); + } + + @FormTest + void testSurfaceModeWithTransparency() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Transparent Surface"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(4); + btn.getAllStyles().setBgTransparency(20); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + assertEquals(20, btn.getUnselectedStyle().getBgTransparency()); + } + + @FormTest + void testSurfaceModeInDialog() { + Form form = CN.getCurrentForm(); + + Dialog dialog = new Dialog("Surface Dialog"); + dialog.setLayout(new BorderLayout()); + + Button btn = new Button("Dialog Button"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(4); + + dialog.add(BorderLayout.CENTER, btn); + + assertTrue(btn.getUnselectedStyle().isSurface()); + } + + @FormTest + void testSurfaceModeWithMarginAndPadding() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Padded Surface"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(3); + btn.getAllStyles().setMargin(10, 10, 10, 10); + btn.getAllStyles().setPadding(15, 15, 15, 15); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + } + + @FormTest + void testSurfaceModeWithBorder() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Border Surface"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(3); + btn.getAllStyles().setBorder(com.codename1.ui.plaf.Border.createLineBorder(2)); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + } + + @FormTest + void testSurfaceModeInScrollableContainer() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Container scrollable = new Container(BoxLayout.y()); + scrollable.setScrollableY(true); + + for (int i = 0; i < 20; i++) { + Button btn = new Button("Surface " + i); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(2 + i); + scrollable.add(btn); + } + + form.add(BorderLayout.CENTER, scrollable); + form.revalidate(); + + assertTrue(scrollable.getComponentCount() == 20); + } + + @FormTest + void testSurfaceModePreservationOnRevalidate() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Persistent Surface"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(5); + + form.add(BorderLayout.CENTER, btn); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + + // Revalidate multiple times + for (int i = 0; i < 5; i++) { + form.revalidate(); + } + + assertTrue(btn.getUnselectedStyle().isSurface()); + assertEquals(5, btn.getUnselectedStyle().getElevation()); + } + + @FormTest + void testSurfaceModeWithRTL() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("RTL Surface"); + btn.getAllStyles().setSurface(true); + btn.getAllStyles().setElevation(4); + + form.add(BorderLayout.CENTER, btn); + form.setRTL(true); + form.revalidate(); + + assertTrue(btn.getUnselectedStyle().isSurface()); + assertTrue(form.isRTL()); + } +} diff --git a/maven/core-unittests/src/test/java/com/codename1/ui/ZOrderingTest.java b/maven/core-unittests/src/test/java/com/codename1/ui/ZOrderingTest.java new file mode 100644 index 0000000000..d1b15399c8 --- /dev/null +++ b/maven/core-unittests/src/test/java/com/codename1/ui/ZOrderingTest.java @@ -0,0 +1,331 @@ +package com.codename1.ui; + +import com.codename1.junit.FormTest; +import com.codename1.junit.UITestBase; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.LayeredLayout; +import com.codename1.ui.plaf.Style; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for z-ordering of components and layering. + */ +class ZOrderingTest extends UITestBase { + + @FormTest + void testBasicZOrdering() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label background = new Label("Background"); + background.getAllStyles().setBgColor(0xFF0000); + background.getAllStyles().setBgTransparency(255); + + Label foreground = new Label("Foreground"); + foreground.getAllStyles().setBgColor(0x00FF00); + foreground.getAllStyles().setBgTransparency(255); + + form.add(background); + form.add(foreground); + form.revalidate(); + + // In LayeredLayout, components added later appear on top + assertTrue(form.getComponentIndex(foreground) > form.getComponentIndex(background)); + } + + @FormTest + void testLayeredLayoutStacking() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label layer1 = new Label("Layer 1"); + Label layer2 = new Label("Layer 2"); + Label layer3 = new Label("Layer 3"); + + form.add(layer1); + form.add(layer2); + form.add(layer3); + form.revalidate(); + + // Components should be stacked in order of addition + assertEquals(0, form.getComponentIndex(layer1)); + assertEquals(1, form.getComponentIndex(layer2)); + assertEquals(2, form.getComponentIndex(layer3)); + } + + @FormTest + void testComponentReordering() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label label1 = new Label("Label 1"); + Label label2 = new Label("Label 2"); + Label label3 = new Label("Label 3"); + + form.addAll(label1, label2, label3); + form.revalidate(); + + // Move label1 to the end (on top) + form.removeComponent(label1); + form.add(label1); + form.revalidate(); + + assertEquals(2, form.getComponentIndex(label1)); + } + + @FormTest + void testLayeredLayoutWithConstraints() { + Form form = CN.getCurrentForm(); + LayeredLayout layout = new LayeredLayout(); + form.setLayout(layout); + + Label fullScreen = new Label("Full Screen"); + Label topCenter = new Label("Top Center"); + + form.add(LayeredLayout.encloseIn( + fullScreen, + topCenter + )); + form.revalidate(); + + assertNotNull(fullScreen.getParent()); + assertNotNull(topCenter.getParent()); + } + + @FormTest + void testGlassPane() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Click Me"); + form.add(BorderLayout.CENTER, btn); + + // Create a simple painter for glass pane + Painter glassPane = (g, rect) -> { + g.setAlpha(128); + g.setColor(0x000000); + g.fillRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight()); + }; + + form.setGlassPane(glassPane); + form.revalidate(); + + assertSame(glassPane, form.getGlassPane()); + } + + @FormTest + void testGlassPaneBlocksInteraction() { + Form form = CN.getCurrentForm(); + form.setLayout(new BorderLayout()); + + Button btn = new Button("Click Me"); + form.add(BorderLayout.CENTER, btn); + + Painter glassPane = (g, rect) -> {}; + form.setGlassPane(glassPane); + form.revalidate(); + + // Glass pane should be set + assertNotNull(form.getGlassPane()); + } + + @FormTest + void testMultipleLayersWithTransparency() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + for (int i = 0; i < 5; i++) { + Label layer = new Label("Layer " + i); + Style style = layer.getAllStyles(); + style.setBgTransparency(128); + style.setBgColor(0xFF0000 >> i); + form.add(layer); + } + + form.revalidate(); + assertEquals(5, form.getContentPane().getComponentCount()); + } + + @FormTest + void testInsertAtSpecificIndex() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label label1 = new Label("Label 1"); + Label label2 = new Label("Label 2"); + Label label3 = new Label("Label 3"); + + form.addComponent(label1); + form.addComponent(label3); + form.revalidate(); + + // Insert label2 between label1 and label3 + form.addComponent(1, label2); + form.revalidate(); + + assertEquals(0, form.getComponentIndex(label1)); + assertEquals(1, form.getComponentIndex(label2)); + assertEquals(2, form.getComponentIndex(label3)); + } + + @FormTest + void testReplaceComponentMaintainsOrder() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label label1 = new Label("Label 1"); + Label label2 = new Label("Label 2"); + Label label3 = new Label("Label 3"); + + form.addAll(label1, label2, label3); + form.revalidate(); + + Label replacement = new Label("Replacement"); + form.replace(label2, replacement, null); + form.revalidate(); + + assertEquals(1, form.getComponentIndex(replacement)); + } + + @FormTest + void testOverlappingComponentsInLayeredLayout() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Button background = new Button("Background"); + background.setX(50); + background.setY(50); + background.setWidth(200); + background.setHeight(100); + + Button foreground = new Button("Foreground"); + foreground.setX(75); + foreground.setY(75); + foreground.setWidth(150); + foreground.setHeight(75); + + form.add(background); + form.add(foreground); + form.revalidate(); + + // Foreground should be on top + assertTrue(form.getComponentIndex(foreground) > form.getComponentIndex(background)); + } + + @FormTest + void testRemoveAndAddAffectsZOrder() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label bottom = new Label("Bottom"); + Label middle = new Label("Middle"); + Label top = new Label("Top"); + + form.addAll(bottom, middle, top); + form.revalidate(); + + // Remove bottom and add it again (should go to top) + form.removeComponent(bottom); + form.add(bottom); + form.revalidate(); + + assertEquals(2, form.getComponentIndex(bottom)); + } + + @FormTest + void testLayeredLayoutWithDifferentSizes() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label large = new Label("Large"); + large.setPreferredW(300); + large.setPreferredH(300); + + Label small = new Label("Small"); + small.setPreferredW(100); + small.setPreferredH(100); + + form.add(large); + form.add(small); + form.revalidate(); + + // Small should be on top + assertTrue(form.getComponentIndex(small) > form.getComponentIndex(large)); + } + + @FormTest + void testZOrderWithInvisibleComponents() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label visible1 = new Label("Visible 1"); + Label invisible = new Label("Invisible"); + invisible.setVisible(false); + Label visible2 = new Label("Visible 2"); + + form.addAll(visible1, invisible, visible2); + form.revalidate(); + + assertEquals(1, form.getComponentIndex(invisible)); + assertFalse(invisible.isVisible()); + } + + @FormTest + void testBringToFrontSimulation() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label label1 = new Label("Label 1"); + Label label2 = new Label("Label 2"); + Label label3 = new Label("Label 3"); + + form.addAll(label1, label2, label3); + form.revalidate(); + + // Simulate bringing label1 to front + form.removeComponent(label1); + form.add(label1); + form.revalidate(); + + assertEquals(form.getContentPane().getComponentCount() - 1, form.getComponentIndex(label1)); + } + + @FormTest + void testSendToBackSimulation() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label label1 = new Label("Label 1"); + Label label2 = new Label("Label 2"); + Label label3 = new Label("Label 3"); + + form.addAll(label1, label2, label3); + form.revalidate(); + + // Simulate sending label3 to back + form.removeComponent(label3); + form.addComponent(0, label3); + form.revalidate(); + + assertEquals(0, form.getComponentIndex(label3)); + } + + @FormTest + void testLayeredLayoutWithAnimation() { + Form form = CN.getCurrentForm(); + form.setLayout(new LayeredLayout()); + + Label layer1 = new Label("Layer 1"); + Label layer2 = new Label("Layer 2"); + + form.add(layer1); + form.add(layer2); + form.revalidate(); + + // Animate and verify z-order is maintained + form.animateLayout(200); + + assertTrue(form.getComponentIndex(layer2) > form.getComponentIndex(layer1)); + } +}