From 6c61c41331c43b990ee69f3655bb81491157a2f9 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 08:09:06 +0300 Subject: [PATCH 01/17] Refine popup bias screenshot test around centered target and fallback --- .../components/InteractionDialog.java | 82 ++++++++++--------- .../components/InteractionDialogTest.java | 55 +++++++++++++ .../tests/Cn1ssDeviceRunner.java | 1 + ...eractionDialogPopupBiasScreenshotTest.java | 66 +++++++++++++++ 4 files changed, 167 insertions(+), 37 deletions(-) create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java diff --git a/CodenameOne/src/com/codename1/components/InteractionDialog.java b/CodenameOne/src/com/codename1/components/InteractionDialog.java index d06c410259..00c2f6bdca 100644 --- a/CodenameOne/src/com/codename1/components/InteractionDialog.java +++ b/CodenameOne/src/com/codename1/components/InteractionDialog.java @@ -637,9 +637,10 @@ public void showPopupDialog(Component c) { /// /// - `c`: the context component which is used to position the dialog and can also be pointed at /// - /// - `bias`: @param bias biases the dialog to appear above/below or to the sides. - /// This is ignored if there isn't enough space - public void showPopupDialog(Component c, boolean bias) { + /// - `prioritizeTopOrRightPosition`: if `true`, prefer showing above the target (portrait layout) or to + /// the right of the target (landscape layout) when both positions fit. If `false`, prefer below/left. + /// If there isn't enough room in the preferred position, the dialog falls back automatically. + public void showPopupDialog(Component c, boolean prioritizeTopOrRightPosition) { if (c == null) { throw new IllegalArgumentException("Component cannot be null"); } @@ -653,7 +654,7 @@ public void showPopupDialog(Component c, boolean bias) { componentPos.setX(componentPos.getX() - c.getScrollX()); componentPos.setY(componentPos.getY() - c.getScrollY()); setOwner(c); - showPopupDialog(componentPos, bias); + showPopupDialog(componentPos, prioritizeTopOrRightPosition); } /// A popup dialog is shown with the context of a component and its selection. You should use `#setDisposeWhenPointerOutOfBounds(boolean)` to make it dispose @@ -675,9 +676,10 @@ public void showPopupDialog(Rectangle rect) { /// /// - `rect`: the screen rectangle to which the popup should point /// - /// - `bias`: @param bias biases the dialog to appear above/below or to the sides. - /// This is ignored if there isn't enough space - public void showPopupDialog(Rectangle rect, boolean bias) { + /// - `prioritizeTopOrRightPosition`: if `true`, prefer showing above the target (portrait layout) or to + /// the right of the target (landscape layout) when both positions fit. If `false`, prefer below/left. + /// If there isn't enough room in the preferred position, the dialog falls back automatically. + public void showPopupDialog(Rectangle rect, boolean prioritizeTopOrRightPosition) { if (rect == null) { throw new IllegalArgumentException("rect cannot be null"); } @@ -762,7 +764,7 @@ public void showPopupDialog(Rectangle rect, boolean bias) { int x = 0; int y = 0; - boolean showPortrait = bias; + boolean showPortrait = Display.getInstance().isPortrait(); // if we don't have enough space then disregard device orientation if (showPortrait) { @@ -790,7 +792,22 @@ public void showPopupDialog(Rectangle rect, boolean bias) { } } } - if (rect.getY() + rect.getHeight() < availableHeight / 2) { + int spaceAbove = rect.getY(); + int spaceBelow = availableHeight - (rect.getY() + rect.getHeight()); + boolean canShowAbove = prefHeight <= spaceAbove; + boolean canShowBelow = prefHeight <= spaceBelow; + boolean showAbove; + if (canShowAbove && canShowBelow) { + showAbove = prioritizeTopOrRightPosition; + } else if (canShowAbove) { + showAbove = true; + } else if (canShowBelow) { + showAbove = false; + } else { + showAbove = spaceAbove >= spaceBelow; + } + + if (!showAbove) { // popup downwards y = rect.getY() + rect.getHeight(); int height = Math.min(prefHeight, Math.max(0, availableHeight - y)); @@ -798,30 +815,13 @@ public void showPopupDialog(Rectangle rect, boolean bias) { show(Math.max(0, y), Math.max(0, availableHeight - height - y), Math.max(0, x), Math.max(0, availableWidth - width - x)); padOrientation(contentPaneStyle, TOP, -1); - } else if (rect.getY() > availableHeight / 2) { + } else { // popup upwards int height = Math.min(prefHeight, rect.getY()); y = rect.getY() - height; padOrientation(contentPaneStyle, BOTTOM, 1); show(y, Math.max(0, availableHeight - rect.getY()), x, Math.max(0, availableWidth - width - x)); padOrientation(contentPaneStyle, BOTTOM, -1); - } else if (rect.getY() < availableHeight / 2) { - // popup over aligned with top of rect, but inset a few mm - y = rect.getY() + CN.convertToPixels(3); - - int height = Math.min(prefHeight, availableHeight - y); - padOrientation(contentPaneStyle, BOTTOM, 1); - show(y, Math.max(0, availableHeight - height - y), - Math.max(0, x), Math.max(0, availableWidth - width - x)); - padOrientation(contentPaneStyle, BOTTOM, -1); - } else { - // popup over aligned with bottom of rect but inset a few mm - y = Math.max(0, rect.getY() + rect.getHeight() - CN.convertToPixels(3) - prefHeight); - int height = prefHeight; - padOrientation(contentPaneStyle, TOP, 1); - show(y, Math.max(0, availableHeight - height - y), - Math.max(0, x), Math.max(0, availableWidth - width - x)); - padOrientation(contentPaneStyle, TOP, -1); } } else { int height = Math.min(prefHeight, availableHeight); @@ -839,23 +839,31 @@ public void showPopupDialog(Rectangle rect, boolean bias) { } } - if (prefWidth < availableWidth - rect.getX() - rect.getWidth()) { + int spaceRight = availableWidth - rect.getX() - rect.getWidth(); + int spaceLeft = rect.getX(); + boolean canShowRight = prefWidth <= spaceRight; + boolean canShowLeft = prefWidth <= spaceLeft; + boolean showRight; + if (canShowRight && canShowLeft) { + showRight = prioritizeTopOrRightPosition; + } else if (canShowRight) { + showRight = true; + } else if (canShowLeft) { + showRight = false; + } else { + showRight = spaceRight >= spaceLeft; + } + + if (showRight) { // popup right x = rect.getX() + rect.getWidth(); - - width = Math.min(prefWidth, availableWidth - x); - show(y, availableHeight - height - y, Math.max(0, x), Math.max(0, availableWidth - width - x)); - } else if (prefWidth < rect.getX()) { - x = rect.getX() - prefWidth; - width = prefWidth; - show(y, availableHeight - height - y, Math.max(0, x), Math.max(0, availableWidth - width - x)); } else { // popup left - width = Math.min(prefWidth, availableWidth - (availableWidth - rect.getX())); + width = Math.min(prefWidth, rect.getX()); x = rect.getX() - width; - show(y, availableHeight - height - y, Math.max(0, x), Math.max(0, availableWidth - width - x)); } + show(y, availableHeight - height - y, Math.max(0, x), Math.max(0, availableWidth - width - x)); } } diff --git a/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java b/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java index d72211c9eb..5197672c26 100644 --- a/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java @@ -5,6 +5,7 @@ import com.codename1.ui.Container; import com.codename1.ui.Form; import com.codename1.ui.Label; +import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.geom.Rectangle; import com.codename1.ui.layouts.BorderLayout; @@ -97,6 +98,60 @@ void formModeUsesFormLayeredPane() { dialog.dispose(); } + @Test + void showPopupDialogBiasTruePrefersTopWhenBothFit() { + Form form = new Form(new BorderLayout()); + implementation.setCurrentForm(form); + + InteractionDialog dialog = new InteractionDialog(); + dialog.setLayout(new FlowLayout()); + dialog.add(new Label("Popup")); + dialog.setAnimateShow(false); + + Rectangle rect = new Rectangle(120, 220, 60, 40); + dialog.showPopupDialog(rect, true); + + assertTrue(dialog.getY() + dialog.getHeight() <= rect.getY(), + "Expected popup above target when prioritizeTopOrRightPosition=true"); + dialog.dispose(); + } + + @Test + void showPopupDialogBiasFalsePrefersBottomWhenBothFit() { + Form form = new Form(new BorderLayout()); + implementation.setCurrentForm(form); + + InteractionDialog dialog = new InteractionDialog(); + dialog.setLayout(new FlowLayout()); + dialog.add(new Label("Popup")); + dialog.setAnimateShow(false); + + Rectangle rect = new Rectangle(120, 220, 60, 40); + dialog.showPopupDialog(rect, false); + + assertTrue(dialog.getY() >= rect.getY() + rect.getHeight(), + "Expected popup below target when prioritizeTopOrRightPosition=false"); + dialog.dispose(); + } + + @Test + void showPopupDialogFallsBackWhenPreferredTopDoesNotFit() { + Form form = new Form(new BorderLayout()); + implementation.setCurrentForm(form); + + InteractionDialog dialog = new InteractionDialog(); + dialog.setLayout(new FlowLayout()); + dialog.add(new Label("Popup")); + dialog.setAnimateShow(false); + + Rectangle rect = new Rectangle(120, 2, 60, 40); + dialog.showPopupDialog(rect, true); + + assertTrue(dialog.getY() >= rect.getY() + rect.getHeight(), + "Expected fallback below when preferred top side does not fit"); + dialog.dispose(); + } + private T getPrivateField(Object target, String name, Class type) throws Exception { Field field = target.getClass().getDeclaredField(name); field.setAccessible(true); diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java index 29d31a3a1f..6ad6468272 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java @@ -69,6 +69,7 @@ public final class Cn1ssDeviceRunner extends DeviceRunner { new BrowserComponentScreenshotTest(), new MediaPlaybackScreenshotTest(), new SheetScreenshotTest(), + new InteractionDialogPopupBiasScreenshotTest(), new ImageViewerNavigationScreenshotTest(), new TextAreaAlignmentScreenshotTest(), // Keep this as the last screenshot test; orientation changes can leak into subsequent screenshots. diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java new file mode 100644 index 0000000000..a09b691271 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -0,0 +1,66 @@ +package com.codenameone.examples.hellocodenameone.tests; + +import com.codename1.components.InteractionDialog; +import com.codename1.ui.Component; +import com.codename1.ui.Container; +import com.codename1.ui.Form; +import com.codename1.ui.Label; +import com.codename1.ui.geom.Rectangle; +import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.layouts.BoxLayout; +import com.codename1.ui.layouts.FlowLayout; +import com.codename1.ui.util.UITimer; + +public class InteractionDialogPopupBiasScreenshotTest extends BaseTest { + private InteractionDialog preferredSideDialog; + private InteractionDialog fallbackSideDialog; + + @Override + public boolean runTest() { + Form form = createForm("InteractionDialog Popup Bias", new BorderLayout(), "InteractionDialogPopupBias"); + Container center = new Container(new FlowLayout(Component.CENTER, Component.CENTER)); + Label target = new Label("CENTER TARGET"); + target.getAllStyles().setPaddingMillimeters(2f, 2f, 2f, 2f); + center.add(target); + form.add(BorderLayout.CENTER, center); + form.add(BorderLayout.NORTH, new Label("Top popup: bias works (prefers top when possible)")); + form.add(BorderLayout.SOUTH, new Label("Bottom popup: bias fallback (top requested but unavailable)")); + form.show(); + return true; + } + + @Override + protected void registerReadyCallback(Form parent, Runnable run) { + preferredSideDialog = createDialog("bias=true -> prefers top"); + fallbackSideDialog = createDialog("bias=true -> falls back to bottom"); + int width = parent.getWidth(); + int height = parent.getHeight(); + + // Center target: enough room above and below, so bias=true should place popup above. + Rectangle centerTarget = new Rectangle(width / 2 - 45, height / 2 - 12, 90, 24); + preferredSideDialog.showPopupDialog(centerTarget, true); + + // Near top target: not enough room above, so bias=true should fall back below. + Rectangle topEdgeTarget = new Rectangle(width / 2 - 45, 4, 90, 24); + fallbackSideDialog.showPopupDialog(topEdgeTarget, true); + UITimer.timer(600, false, parent, run); + } + + @Override + public void cleanup() { + if (preferredSideDialog != null && preferredSideDialog.isShowing()) { + preferredSideDialog.dispose(); + } + if (fallbackSideDialog != null && fallbackSideDialog.isShowing()) { + fallbackSideDialog.dispose(); + } + } + + private InteractionDialog createDialog(String text) { + InteractionDialog dialog = new InteractionDialog(); + dialog.setLayout(BoxLayout.y()); + dialog.add(new Label(text)); + dialog.setDisposeWhenPointerOutOfBounds(false); + return dialog; + } +} From 85a2abbdfea3044bec7670ad23e506497cbb0e2b Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 08:59:09 +0300 Subject: [PATCH 02/17] Fix screenshot test style padding API usage --- .../tests/InteractionDialogPopupBiasScreenshotTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java index a09b691271..cd13a341ff 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -20,7 +20,7 @@ public boolean runTest() { Form form = createForm("InteractionDialog Popup Bias", new BorderLayout(), "InteractionDialogPopupBias"); Container center = new Container(new FlowLayout(Component.CENTER, Component.CENTER)); Label target = new Label("CENTER TARGET"); - target.getAllStyles().setPaddingMillimeters(2f, 2f, 2f, 2f); + target.getAllStyles().setPadding(2, 2, 2, 2); center.add(target); form.add(BorderLayout.CENTER, center); form.add(BorderLayout.NORTH, new Label("Top popup: bias works (prefers top when possible)")); From c5f82befe1011b5dc1e4ef93fb39e1d18948aa2b Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 09:22:23 +0300 Subject: [PATCH 03/17] Expand popup bias tests and fix screenshot layout --- .../components/InteractionDialogTest.java | 96 +++++++++++++++++++ ...eractionDialogPopupBiasScreenshotTest.java | 32 +++---- 2 files changed, 111 insertions(+), 17 deletions(-) diff --git a/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java b/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java index 5197672c26..4f1924f87e 100644 --- a/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java @@ -152,6 +152,102 @@ void showPopupDialogFallsBackWhenPreferredTopDoesNotFit() { dialog.dispose(); } + @Test + void showPopupDialogFallsBackWhenPreferredBottomDoesNotFit() { + Form form = new Form(new BorderLayout()); + implementation.setCurrentForm(form); + + InteractionDialog dialog = new InteractionDialog(); + dialog.setLayout(new FlowLayout()); + dialog.add(new Label("Popup")); + dialog.setAnimateShow(false); + + int displayHeight = implementation.getDisplayHeight(); + Rectangle rect = new Rectangle(120, Math.max(0, displayHeight - 8), 60, 6); + dialog.showPopupDialog(rect, false); + + assertTrue(dialog.getY() + dialog.getHeight() <= rect.getY(), + "Expected fallback above when preferred bottom side does not fit"); + dialog.dispose(); + } + + @Test + void showPopupDialogBiasTruePrefersRightInLandscapeWhenBothFit() { + implementation.setPortrait(false); + Form form = new Form(new BorderLayout()); + implementation.setCurrentForm(form); + + InteractionDialog dialog = new InteractionDialog(); + dialog.setLayout(new FlowLayout()); + dialog.add(new Label("Popup")); + dialog.setAnimateShow(false); + + Rectangle rect = new Rectangle(120, 140, 60, 40); + dialog.showPopupDialog(rect, true); + + assertTrue(dialog.getX() >= rect.getX() + rect.getWidth(), + "Expected popup on right side when prioritizeTopOrRightPosition=true in landscape"); + dialog.dispose(); + } + + @Test + void showPopupDialogBiasFalsePrefersLeftInLandscapeWhenBothFit() { + implementation.setPortrait(false); + Form form = new Form(new BorderLayout()); + implementation.setCurrentForm(form); + + InteractionDialog dialog = new InteractionDialog(); + dialog.setLayout(new FlowLayout()); + dialog.add(new Label("Popup")); + dialog.setAnimateShow(false); + + Rectangle rect = new Rectangle(120, 140, 60, 40); + dialog.showPopupDialog(rect, false); + + assertTrue(dialog.getX() + dialog.getWidth() <= rect.getX(), + "Expected popup on left side when prioritizeTopOrRightPosition=false in landscape"); + dialog.dispose(); + } + + @Test + void showPopupDialogFallsBackWhenPreferredRightDoesNotFit() { + implementation.setPortrait(false); + Form form = new Form(new BorderLayout()); + implementation.setCurrentForm(form); + + InteractionDialog dialog = new InteractionDialog(); + dialog.setLayout(new FlowLayout()); + dialog.add(new Label("Popup")); + dialog.setAnimateShow(false); + + int displayWidth = implementation.getDisplayWidth(); + Rectangle rect = new Rectangle(Math.max(0, displayWidth - 8), 140, 6, 40); + dialog.showPopupDialog(rect, true); + + assertTrue(dialog.getX() + dialog.getWidth() <= rect.getX(), + "Expected fallback to left when preferred right side does not fit"); + dialog.dispose(); + } + + @Test + void showPopupDialogFallsBackWhenPreferredLeftDoesNotFit() { + implementation.setPortrait(false); + Form form = new Form(new BorderLayout()); + implementation.setCurrentForm(form); + + InteractionDialog dialog = new InteractionDialog(); + dialog.setLayout(new FlowLayout()); + dialog.add(new Label("Popup")); + dialog.setAnimateShow(false); + + Rectangle rect = new Rectangle(2, 140, 6, 40); + dialog.showPopupDialog(rect, false); + + assertTrue(dialog.getX() >= rect.getX() + rect.getWidth(), + "Expected fallback to right when preferred left side does not fit"); + dialog.dispose(); + } + private T getPrivateField(Object target, String name, Class type) throws Exception { Field field = target.getClass().getDeclaredField(name); field.setAccessible(true); diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java index cd13a341ff..8ee21c3246 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -12,8 +12,8 @@ import com.codename1.ui.util.UITimer; public class InteractionDialogPopupBiasScreenshotTest extends BaseTest { - private InteractionDialog preferredSideDialog; - private InteractionDialog fallbackSideDialog; + private InteractionDialog topPreferredDialog; + private InteractionDialog bottomPreferredDialog; @Override public boolean runTest() { @@ -23,36 +23,34 @@ public boolean runTest() { target.getAllStyles().setPadding(2, 2, 2, 2); center.add(target); form.add(BorderLayout.CENTER, center); - form.add(BorderLayout.NORTH, new Label("Top popup: bias works (prefers top when possible)")); - form.add(BorderLayout.SOUTH, new Label("Bottom popup: bias fallback (top requested but unavailable)")); + form.add(BorderLayout.NORTH, new Label("bias=true : popup should be ABOVE top target")); + form.add(BorderLayout.SOUTH, new Label("bias=false : popup should be BELOW bottom target")); form.show(); return true; } @Override protected void registerReadyCallback(Form parent, Runnable run) { - preferredSideDialog = createDialog("bias=true -> prefers top"); - fallbackSideDialog = createDialog("bias=true -> falls back to bottom"); + topPreferredDialog = createDialog("bias=true -> above target"); + bottomPreferredDialog = createDialog("bias=false -> below target"); int width = parent.getWidth(); int height = parent.getHeight(); - // Center target: enough room above and below, so bias=true should place popup above. - Rectangle centerTarget = new Rectangle(width / 2 - 45, height / 2 - 12, 90, 24); - preferredSideDialog.showPopupDialog(centerTarget, true); - - // Near top target: not enough room above, so bias=true should fall back below. - Rectangle topEdgeTarget = new Rectangle(width / 2 - 45, 4, 90, 24); - fallbackSideDialog.showPopupDialog(topEdgeTarget, true); + // Keep the targets far apart to avoid overlap in screenshot. + Rectangle upperTarget = new Rectangle(width / 2 - 45, height / 3, 90, 24); + Rectangle lowerTarget = new Rectangle(width / 2 - 45, (height * 2 / 3) - 24, 90, 24); + topPreferredDialog.showPopupDialog(upperTarget, true); + bottomPreferredDialog.showPopupDialog(lowerTarget, false); UITimer.timer(600, false, parent, run); } @Override public void cleanup() { - if (preferredSideDialog != null && preferredSideDialog.isShowing()) { - preferredSideDialog.dispose(); + if (topPreferredDialog != null && topPreferredDialog.isShowing()) { + topPreferredDialog.dispose(); } - if (fallbackSideDialog != null && fallbackSideDialog.isShowing()) { - fallbackSideDialog.dispose(); + if (bottomPreferredDialog != null && bottomPreferredDialog.isShowing()) { + bottomPreferredDialog.dispose(); } } From 8814eb42d55c2ee5fadc7336fb10107e43b91c7a Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:39:37 +0300 Subject: [PATCH 04/17] Add third popup scenario to screenshot bias test --- .../InteractionDialogPopupBiasScreenshotTest.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java index 8ee21c3246..86352746c4 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -14,6 +14,7 @@ public class InteractionDialogPopupBiasScreenshotTest extends BaseTest { private InteractionDialog topPreferredDialog; private InteractionDialog bottomPreferredDialog; + private InteractionDialog topFallbackDialog; @Override public boolean runTest() { @@ -23,8 +24,8 @@ public boolean runTest() { target.getAllStyles().setPadding(2, 2, 2, 2); center.add(target); form.add(BorderLayout.CENTER, center); - form.add(BorderLayout.NORTH, new Label("bias=true : popup should be ABOVE top target")); - form.add(BorderLayout.SOUTH, new Label("bias=false : popup should be BELOW bottom target")); + form.add(BorderLayout.NORTH, new Label("3 dialogs: above target, below target, and top-fallback")); + form.add(BorderLayout.SOUTH, new Label("CENTER TARGET helps verify relative arrow direction")); form.show(); return true; } @@ -33,14 +34,17 @@ public boolean runTest() { protected void registerReadyCallback(Form parent, Runnable run) { topPreferredDialog = createDialog("bias=true -> above target"); bottomPreferredDialog = createDialog("bias=false -> below target"); + topFallbackDialog = createDialog("bias=true near top -> fallback below"); int width = parent.getWidth(); int height = parent.getHeight(); // Keep the targets far apart to avoid overlap in screenshot. Rectangle upperTarget = new Rectangle(width / 2 - 45, height / 3, 90, 24); Rectangle lowerTarget = new Rectangle(width / 2 - 45, (height * 2 / 3) - 24, 90, 24); + Rectangle nearTopTarget = new Rectangle(width / 2 - 45, 6, 90, 24); topPreferredDialog.showPopupDialog(upperTarget, true); bottomPreferredDialog.showPopupDialog(lowerTarget, false); + topFallbackDialog.showPopupDialog(nearTopTarget, true); UITimer.timer(600, false, parent, run); } @@ -52,6 +56,9 @@ public void cleanup() { if (bottomPreferredDialog != null && bottomPreferredDialog.isShowing()) { bottomPreferredDialog.dispose(); } + if (topFallbackDialog != null && topFallbackDialog.isShowing()) { + topFallbackDialog.dispose(); + } } private InteractionDialog createDialog(String text) { From d230b149d81c509efc9b21d897c3209b6337f374 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:35:44 +0300 Subject: [PATCH 05/17] Use compact custom UIIDs for popup bias screenshot dialogs --- ...eractionDialogPopupBiasScreenshotTest.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java index 86352746c4..fa43139dfc 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -5,6 +5,7 @@ import com.codename1.ui.Container; import com.codename1.ui.Form; import com.codename1.ui.Label; +import com.codename1.ui.plaf.Style; import com.codename1.ui.geom.Rectangle; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; @@ -18,30 +19,28 @@ public class InteractionDialogPopupBiasScreenshotTest extends BaseTest { @Override public boolean runTest() { - Form form = createForm("InteractionDialog Popup Bias", new BorderLayout(), "InteractionDialogPopupBias"); + Form form = createForm("InteractionDialog Popup", new BorderLayout(), "InteractionDialogPopupBias"); Container center = new Container(new FlowLayout(Component.CENTER, Component.CENTER)); Label target = new Label("CENTER TARGET"); target.getAllStyles().setPadding(2, 2, 2, 2); center.add(target); form.add(BorderLayout.CENTER, center); - form.add(BorderLayout.NORTH, new Label("3 dialogs: above target, below target, and top-fallback")); - form.add(BorderLayout.SOUTH, new Label("CENTER TARGET helps verify relative arrow direction")); form.show(); return true; } @Override protected void registerReadyCallback(Form parent, Runnable run) { - topPreferredDialog = createDialog("bias=true -> above target"); - bottomPreferredDialog = createDialog("bias=false -> below target"); - topFallbackDialog = createDialog("bias=true near top -> fallback below"); + topPreferredDialog = createDialog("T: bias=true"); + bottomPreferredDialog = createDialog("B: bias=false"); + topFallbackDialog = createDialog("F: top fallback"); int width = parent.getWidth(); int height = parent.getHeight(); // Keep the targets far apart to avoid overlap in screenshot. - Rectangle upperTarget = new Rectangle(width / 2 - 45, height / 3, 90, 24); - Rectangle lowerTarget = new Rectangle(width / 2 - 45, (height * 2 / 3) - 24, 90, 24); - Rectangle nearTopTarget = new Rectangle(width / 2 - 45, 6, 90, 24); + Rectangle upperTarget = new Rectangle(width / 2 - 24, Math.max(40, (height / 2) - 130), 48, 20); + Rectangle lowerTarget = new Rectangle(width / 2 - 24, Math.min(height - 60, (height / 2) + 90), 48, 20); + Rectangle nearTopTarget = new Rectangle(width / 2 - 24, 2, 48, 20); topPreferredDialog.showPopupDialog(upperTarget, true); bottomPreferredDialog.showPopupDialog(lowerTarget, false); topFallbackDialog.showPopupDialog(nearTopTarget, true); @@ -63,8 +62,12 @@ public void cleanup() { private InteractionDialog createDialog(String text) { InteractionDialog dialog = new InteractionDialog(); + dialog.setUIID("PopupDialogCN1SSBias"); + dialog.setDialogUIID("PopupDialogCN1SSBiasContent"); dialog.setLayout(BoxLayout.y()); - dialog.add(new Label(text)); + Label label = new Label(text); + label.getAllStyles().setFont(label.getUnselectedStyle().getFont().derive(label.getUnselectedStyle().getFont().getHeight() * 0.75f, Style.FONT_STYLE_PLAIN)); + dialog.add(label); dialog.setDisposeWhenPointerOutOfBounds(false); return dialog; } From ad1ae381000b8b0fa6234e6b5b6579ab723af0cc Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 13:37:39 +0300 Subject: [PATCH 06/17] Fix font style constant in popup bias screenshot test --- .../tests/InteractionDialogPopupBiasScreenshotTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java index fa43139dfc..2f6b16be85 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -3,9 +3,9 @@ import com.codename1.components.InteractionDialog; import com.codename1.ui.Component; import com.codename1.ui.Container; +import com.codename1.ui.Font; import com.codename1.ui.Form; import com.codename1.ui.Label; -import com.codename1.ui.plaf.Style; import com.codename1.ui.geom.Rectangle; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; @@ -66,7 +66,7 @@ private InteractionDialog createDialog(String text) { dialog.setDialogUIID("PopupDialogCN1SSBiasContent"); dialog.setLayout(BoxLayout.y()); Label label = new Label(text); - label.getAllStyles().setFont(label.getUnselectedStyle().getFont().derive(label.getUnselectedStyle().getFont().getHeight() * 0.75f, Style.FONT_STYLE_PLAIN)); + label.getAllStyles().setFont(label.getUnselectedStyle().getFont().derive(label.getUnselectedStyle().getFont().getHeight() * 0.75f, Font.STYLE_PLAIN)); dialog.add(label); dialog.setDisposeWhenPointerOutOfBounds(false); return dialog; From 459e7e3842afce9857c4e4754b9b196c41a9e129 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 14:08:56 +0300 Subject: [PATCH 07/17] Use default popup styles in bias screenshot test for visibility --- ...eractionDialogPopupBiasScreenshotTest.java | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java index 2f6b16be85..639844ce39 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -3,7 +3,6 @@ import com.codename1.components.InteractionDialog; import com.codename1.ui.Component; import com.codename1.ui.Container; -import com.codename1.ui.Font; import com.codename1.ui.Form; import com.codename1.ui.Label; import com.codename1.ui.geom.Rectangle; @@ -21,7 +20,7 @@ public class InteractionDialogPopupBiasScreenshotTest extends BaseTest { public boolean runTest() { Form form = createForm("InteractionDialog Popup", new BorderLayout(), "InteractionDialogPopupBias"); Container center = new Container(new FlowLayout(Component.CENTER, Component.CENTER)); - Label target = new Label("CENTER TARGET"); + Label target = new Label("CENTER TARGET (T:true above / B:false below)"); target.getAllStyles().setPadding(2, 2, 2, 2); center.add(target); form.add(BorderLayout.CENTER, center); @@ -31,16 +30,16 @@ public boolean runTest() { @Override protected void registerReadyCallback(Form parent, Runnable run) { - topPreferredDialog = createDialog("T: bias=true"); - bottomPreferredDialog = createDialog("B: bias=false"); - topFallbackDialog = createDialog("F: top fallback"); + topPreferredDialog = createDialog("T: bias=true (prefer top)"); + bottomPreferredDialog = createDialog("B: bias=false (prefer bottom)"); + topFallbackDialog = createDialog("F: bias=true fallback (shown below)"); int width = parent.getWidth(); int height = parent.getHeight(); // Keep the targets far apart to avoid overlap in screenshot. - Rectangle upperTarget = new Rectangle(width / 2 - 24, Math.max(40, (height / 2) - 130), 48, 20); - Rectangle lowerTarget = new Rectangle(width / 2 - 24, Math.min(height - 60, (height / 2) + 90), 48, 20); - Rectangle nearTopTarget = new Rectangle(width / 2 - 24, 2, 48, 20); + Rectangle upperTarget = new Rectangle(width / 2 - 30, Math.max(56, (height / 2) - 130), 60, 18); + Rectangle lowerTarget = new Rectangle(width / 2 - 30, Math.min(height - 76, (height / 2) + 90), 60, 18); + Rectangle nearTopTarget = new Rectangle(width / 2 - 30, 2, 60, 18); topPreferredDialog.showPopupDialog(upperTarget, true); bottomPreferredDialog.showPopupDialog(lowerTarget, false); topFallbackDialog.showPopupDialog(nearTopTarget, true); @@ -62,12 +61,8 @@ public void cleanup() { private InteractionDialog createDialog(String text) { InteractionDialog dialog = new InteractionDialog(); - dialog.setUIID("PopupDialogCN1SSBias"); - dialog.setDialogUIID("PopupDialogCN1SSBiasContent"); dialog.setLayout(BoxLayout.y()); - Label label = new Label(text); - label.getAllStyles().setFont(label.getUnselectedStyle().getFont().derive(label.getUnselectedStyle().getFont().getHeight() * 0.75f, Font.STYLE_PLAIN)); - dialog.add(label); + dialog.add(new Label(text)); dialog.setDisposeWhenPointerOutOfBounds(false); return dialog; } From 2045e6c60487f78b96f9d08557913d6d7e93cdaa Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 17:59:02 +0300 Subject: [PATCH 08/17] Rework popup bias screenshot to 4 dialogs over 3 targets --- ...eractionDialogPopupBiasScreenshotTest.java | 68 ++++++++++++------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java index 639844ce39..86e43e4018 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -5,57 +5,61 @@ import com.codename1.ui.Container; import com.codename1.ui.Form; import com.codename1.ui.Label; -import com.codename1.ui.geom.Rectangle; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.layouts.BoxLayout; import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.util.UITimer; public class InteractionDialogPopupBiasScreenshotTest extends BaseTest { - private InteractionDialog topPreferredDialog; - private InteractionDialog bottomPreferredDialog; - private InteractionDialog topFallbackDialog; + private InteractionDialog northFallbackDialog; + private InteractionDialog centerTopDialog; + private InteractionDialog centerBottomDialog; + private InteractionDialog southFallbackDialog; + private Label northTarget; + private Label centerTarget; + private Label southTarget; @Override public boolean runTest() { Form form = createForm("InteractionDialog Popup", new BorderLayout(), "InteractionDialogPopupBias"); + northTarget = createTarget("NORTH TARGET"); + centerTarget = createTarget("CENTER TARGET"); + southTarget = createTarget("SOUTH TARGET"); Container center = new Container(new FlowLayout(Component.CENTER, Component.CENTER)); - Label target = new Label("CENTER TARGET (T:true above / B:false below)"); - target.getAllStyles().setPadding(2, 2, 2, 2); - center.add(target); + center.add(centerTarget); + form.add(BorderLayout.NORTH, wrapTarget(northTarget)); form.add(BorderLayout.CENTER, center); + form.add(BorderLayout.SOUTH, wrapTarget(southTarget)); form.show(); return true; } @Override protected void registerReadyCallback(Form parent, Runnable run) { - topPreferredDialog = createDialog("T: bias=true (prefer top)"); - bottomPreferredDialog = createDialog("B: bias=false (prefer bottom)"); - topFallbackDialog = createDialog("F: bias=true fallback (shown below)"); - int width = parent.getWidth(); - int height = parent.getHeight(); - - // Keep the targets far apart to avoid overlap in screenshot. - Rectangle upperTarget = new Rectangle(width / 2 - 30, Math.max(56, (height / 2) - 130), 60, 18); - Rectangle lowerTarget = new Rectangle(width / 2 - 30, Math.min(height - 76, (height / 2) + 90), 60, 18); - Rectangle nearTopTarget = new Rectangle(width / 2 - 30, 2, 60, 18); - topPreferredDialog.showPopupDialog(upperTarget, true); - bottomPreferredDialog.showPopupDialog(lowerTarget, false); - topFallbackDialog.showPopupDialog(nearTopTarget, true); + northFallbackDialog = createDialog("1) North + bias=true -> fallback below"); + centerTopDialog = createDialog("2) Center + bias=true -> above"); + centerBottomDialog = createDialog("3) Center + bias=false -> below"); + southFallbackDialog = createDialog("4) South + bias=false -> fallback above"); + northFallbackDialog.showPopupDialog(northTarget, true); + centerTopDialog.showPopupDialog(centerTarget, true); + centerBottomDialog.showPopupDialog(centerTarget, false); + southFallbackDialog.showPopupDialog(southTarget, false); UITimer.timer(600, false, parent, run); } @Override public void cleanup() { - if (topPreferredDialog != null && topPreferredDialog.isShowing()) { - topPreferredDialog.dispose(); + if (northFallbackDialog != null && northFallbackDialog.isShowing()) { + northFallbackDialog.dispose(); } - if (bottomPreferredDialog != null && bottomPreferredDialog.isShowing()) { - bottomPreferredDialog.dispose(); + if (centerTopDialog != null && centerTopDialog.isShowing()) { + centerTopDialog.dispose(); } - if (topFallbackDialog != null && topFallbackDialog.isShowing()) { - topFallbackDialog.dispose(); + if (centerBottomDialog != null && centerBottomDialog.isShowing()) { + centerBottomDialog.dispose(); + } + if (southFallbackDialog != null && southFallbackDialog.isShowing()) { + southFallbackDialog.dispose(); } } @@ -66,4 +70,16 @@ private InteractionDialog createDialog(String text) { dialog.setDisposeWhenPointerOutOfBounds(false); return dialog; } + + private Label createTarget(String text) { + Label target = new Label(text); + target.getAllStyles().setPadding(2, 2, 2, 2); + return target; + } + + private Container wrapTarget(Component target) { + Container wrapper = new Container(new FlowLayout(Component.CENTER, Component.CENTER)); + wrapper.add(target); + return wrapper; + } } From b5dc2ffb08a5bd1515f9088bb57c54097bfea3ef Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 18:44:43 +0300 Subject: [PATCH 09/17] Make popup bias screenshot dialogs compact to show both arrow directions --- .../InteractionDialogPopupBiasScreenshotTest.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java index 86e43e4018..e5f96933cc 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -36,10 +36,10 @@ public boolean runTest() { @Override protected void registerReadyCallback(Form parent, Runnable run) { - northFallbackDialog = createDialog("1) North + bias=true -> fallback below"); - centerTopDialog = createDialog("2) Center + bias=true -> above"); - centerBottomDialog = createDialog("3) Center + bias=false -> below"); - southFallbackDialog = createDialog("4) South + bias=false -> fallback above"); + northFallbackDialog = createDialog("1) N,true fallback"); + centerTopDialog = createDialog("2) C,true (top)"); + centerBottomDialog = createDialog("3) C,false (bottom)"); + southFallbackDialog = createDialog("4) S,false fallback"); northFallbackDialog.showPopupDialog(northTarget, true); centerTopDialog.showPopupDialog(centerTarget, true); centerBottomDialog.showPopupDialog(centerTarget, false); @@ -66,7 +66,9 @@ public void cleanup() { private InteractionDialog createDialog(String text) { InteractionDialog dialog = new InteractionDialog(); dialog.setLayout(BoxLayout.y()); - dialog.add(new Label(text)); + Label label = new Label(text); + label.getAllStyles().setPadding(1, 1, 1, 1); + dialog.add(label); dialog.setDisposeWhenPointerOutOfBounds(false); return dialog; } From 095191ee9be14f657fffaebe6652c4acec7d96fd Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 20:00:22 +0300 Subject: [PATCH 10/17] Fix popup arrow orientation padding for above/below placement --- .../src/com/codename1/components/InteractionDialog.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CodenameOne/src/com/codename1/components/InteractionDialog.java b/CodenameOne/src/com/codename1/components/InteractionDialog.java index 00c2f6bdca..e1dbffd661 100644 --- a/CodenameOne/src/com/codename1/components/InteractionDialog.java +++ b/CodenameOne/src/com/codename1/components/InteractionDialog.java @@ -811,17 +811,17 @@ public void showPopupDialog(Rectangle rect, boolean prioritizeTopOrRightPosition // popup downwards y = rect.getY() + rect.getHeight(); int height = Math.min(prefHeight, Math.max(0, availableHeight - y)); - padOrientation(contentPaneStyle, TOP, 1); + padOrientation(contentPaneStyle, BOTTOM, 1); show(Math.max(0, y), Math.max(0, availableHeight - height - y), Math.max(0, x), Math.max(0, availableWidth - width - x)); - padOrientation(contentPaneStyle, TOP, -1); + padOrientation(contentPaneStyle, BOTTOM, -1); } else { // popup upwards int height = Math.min(prefHeight, rect.getY()); y = rect.getY() - height; - padOrientation(contentPaneStyle, BOTTOM, 1); + padOrientation(contentPaneStyle, TOP, 1); show(y, Math.max(0, availableHeight - rect.getY()), x, Math.max(0, availableWidth - width - x)); - padOrientation(contentPaneStyle, BOTTOM, -1); + padOrientation(contentPaneStyle, TOP, -1); } } else { int height = Math.min(prefHeight, availableHeight); From d4617b04a998ff4c545bec37cc9a39fca0c52990 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 20:00:26 +0300 Subject: [PATCH 11/17] Tighten screenshot popup labels and refresh layout before showing --- .../tests/InteractionDialogPopupBiasScreenshotTest.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java index e5f96933cc..07727bc6b9 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -36,10 +36,11 @@ public boolean runTest() { @Override protected void registerReadyCallback(Form parent, Runnable run) { - northFallbackDialog = createDialog("1) N,true fallback"); - centerTopDialog = createDialog("2) C,true (top)"); - centerBottomDialog = createDialog("3) C,false (bottom)"); - southFallbackDialog = createDialog("4) S,false fallback"); + northFallbackDialog = createDialog("1) N/T -> down"); + centerTopDialog = createDialog("2) C/T -> up"); + centerBottomDialog = createDialog("3) C/B -> down"); + southFallbackDialog = createDialog("4) S/B -> up"); + parent.revalidate(); northFallbackDialog.showPopupDialog(northTarget, true); centerTopDialog.showPopupDialog(centerTarget, true); centerBottomDialog.showPopupDialog(centerTarget, false); From ea20374bc48a4be81dca338bcfc00225e7bf7b90 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:39:35 +0300 Subject: [PATCH 12/17] Refactor placement booleans and restore border tracking fallback --- .../components/InteractionDialog.java | 40 +++++++------------ ...eractionDialogPopupBiasScreenshotTest.java | 1 + 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/CodenameOne/src/com/codename1/components/InteractionDialog.java b/CodenameOne/src/com/codename1/components/InteractionDialog.java index e1dbffd661..46f7429e5a 100644 --- a/CodenameOne/src/com/codename1/components/InteractionDialog.java +++ b/CodenameOne/src/com/codename1/components/InteractionDialog.java @@ -723,11 +723,13 @@ public void showPopupDialog(Rectangle rect, boolean prioritizeTopOrRightPosition Style contentPaneStyle = getStyle(); // PMD Fix: UnusedLocalVariable removed redundant contentPane reference - if (manager.isThemeConstant(getUIID() + "ArrowBool", false)) { - Image t = manager.getThemeImageConstant(getUIID() + "ArrowTopImage"); - Image b = manager.getThemeImageConstant(getUIID() + "ArrowBottomImage"); - Image l = manager.getThemeImageConstant(getUIID() + "ArrowLeftImage"); - Image r = manager.getThemeImageConstant(getUIID() + "ArrowRightImage"); + boolean arrowEnabled = manager.isThemeConstant(getUIID() + "ArrowBool", false); + Image t = manager.getThemeImageConstant(getUIID() + "ArrowTopImage"); + Image b = manager.getThemeImageConstant(getUIID() + "ArrowBottomImage"); + Image l = manager.getThemeImageConstant(getUIID() + "ArrowLeftImage"); + Image r = manager.getThemeImageConstant(getUIID() + "ArrowRightImage"); + boolean hasArrowImages = t != null || b != null || l != null || r != null; + if (arrowEnabled && hasArrowImages) { Border border = contentPaneStyle.getBorder(); if (border != null) { border.setImageBorderSpecialTile(t, b, l, r, rect); @@ -796,16 +798,10 @@ public void showPopupDialog(Rectangle rect, boolean prioritizeTopOrRightPosition int spaceBelow = availableHeight - (rect.getY() + rect.getHeight()); boolean canShowAbove = prefHeight <= spaceAbove; boolean canShowBelow = prefHeight <= spaceBelow; - boolean showAbove; - if (canShowAbove && canShowBelow) { - showAbove = prioritizeTopOrRightPosition; - } else if (canShowAbove) { - showAbove = true; - } else if (canShowBelow) { - showAbove = false; - } else { - showAbove = spaceAbove >= spaceBelow; - } + // Boolean decision: honor preference when both sides fit, otherwise use the fitting side, + // and if neither fits use the side with more available space. + boolean showAbove = canShowAbove && (canShowBelow ? prioritizeTopOrRightPosition : true) + || !canShowAbove && !canShowBelow && spaceAbove >= spaceBelow; if (!showAbove) { // popup downwards @@ -843,16 +839,10 @@ public void showPopupDialog(Rectangle rect, boolean prioritizeTopOrRightPosition int spaceLeft = rect.getX(); boolean canShowRight = prefWidth <= spaceRight; boolean canShowLeft = prefWidth <= spaceLeft; - boolean showRight; - if (canShowRight && canShowLeft) { - showRight = prioritizeTopOrRightPosition; - } else if (canShowRight) { - showRight = true; - } else if (canShowLeft) { - showRight = false; - } else { - showRight = spaceRight >= spaceLeft; - } + // Boolean decision: honor preference when both sides fit, otherwise use the fitting side, + // and if neither fits use the side with more available space. + boolean showRight = canShowRight && (canShowLeft ? prioritizeTopOrRightPosition : true) + || !canShowRight && !canShowLeft && spaceRight >= spaceLeft; if (showRight) { // popup right diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java index 07727bc6b9..1fa1df25a3 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InteractionDialogPopupBiasScreenshotTest.java @@ -67,6 +67,7 @@ public void cleanup() { private InteractionDialog createDialog(String text) { InteractionDialog dialog = new InteractionDialog(); dialog.setLayout(BoxLayout.y()); + dialog.setAnimateShow(false); Label label = new Label(text); label.getAllStyles().setPadding(1, 1, 1, 1); dialog.add(label); From 73d57b5d1cea1ca60a1fd9da15aef32e6affddce Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Wed, 1 Apr 2026 22:39:39 +0300 Subject: [PATCH 13/17] Use dialog style for popup border/arrow handling --- CodenameOne/src/com/codename1/components/InteractionDialog.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CodenameOne/src/com/codename1/components/InteractionDialog.java b/CodenameOne/src/com/codename1/components/InteractionDialog.java index 46f7429e5a..e5313ce80d 100644 --- a/CodenameOne/src/com/codename1/components/InteractionDialog.java +++ b/CodenameOne/src/com/codename1/components/InteractionDialog.java @@ -721,7 +721,7 @@ public void showPopupDialog(Rectangle rect, boolean prioritizeTopOrRightPosition // allows a text area to recalculate its preferred size if embedded within a dialog revalidate(); - Style contentPaneStyle = getStyle(); // PMD Fix: UnusedLocalVariable removed redundant contentPane reference + Style contentPaneStyle = getDialogStyle(); boolean arrowEnabled = manager.isThemeConstant(getUIID() + "ArrowBool", false); Image t = manager.getThemeImageConstant(getUIID() + "ArrowTopImage"); From fa2ab11f17414e42ee79c09f1581cc50470440d9 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 2 Apr 2026 06:59:19 +0300 Subject: [PATCH 14/17] Restore popup border lookup fallback for arrow rendering --- .../src/com/codename1/components/InteractionDialog.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CodenameOne/src/com/codename1/components/InteractionDialog.java b/CodenameOne/src/com/codename1/components/InteractionDialog.java index e5313ce80d..4423e3d01b 100644 --- a/CodenameOne/src/com/codename1/components/InteractionDialog.java +++ b/CodenameOne/src/com/codename1/components/InteractionDialog.java @@ -721,7 +721,13 @@ public void showPopupDialog(Rectangle rect, boolean prioritizeTopOrRightPosition // allows a text area to recalculate its preferred size if embedded within a dialog revalidate(); - Style contentPaneStyle = getDialogStyle(); + // Popup border can be defined either on the dialog style (PopupDialog) or the content pane style + // (PopupContentPane), depending on theme/platform. Prefer dialog style for arrows, and fall back + // to dialog content style if needed. + Style contentPaneStyle = getStyle(); + if (contentPaneStyle.getBorder() == null) { + contentPaneStyle = getDialogStyle(); + } boolean arrowEnabled = manager.isThemeConstant(getUIID() + "ArrowBool", false); Image t = manager.getThemeImageConstant(getUIID() + "ArrowTopImage"); From e96f4d057931453d77a79be0a42869eb2d4d9d88 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:57:55 +0300 Subject: [PATCH 15/17] Fix arrow tracking rect and add deeper border tracking tests --- .../components/InteractionDialog.java | 2 +- .../components/InteractionDialogTest.java | 46 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/CodenameOne/src/com/codename1/components/InteractionDialog.java b/CodenameOne/src/com/codename1/components/InteractionDialog.java index 4423e3d01b..daaec54367 100644 --- a/CodenameOne/src/com/codename1/components/InteractionDialog.java +++ b/CodenameOne/src/com/codename1/components/InteractionDialog.java @@ -743,7 +743,7 @@ public void showPopupDialog(Rectangle rect, boolean prioritizeTopOrRightPosition } else { Border border = contentPaneStyle.getBorder(); if (border != null) { - border.setTrackComponent(origRect); + border.setTrackComponent(rect); } } calcPreferredSize(); diff --git a/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java b/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java index 4f1924f87e..0a68675613 100644 --- a/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java @@ -8,6 +8,7 @@ import com.codename1.ui.layouts.FlowLayout; import com.codename1.ui.geom.Rectangle; import com.codename1.ui.layouts.BorderLayout; +import com.codename1.ui.plaf.Border; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -248,6 +249,51 @@ void showPopupDialogFallsBackWhenPreferredLeftDoesNotFit() { dialog.dispose(); } + @Test + void showPopupDialogSetsArrowTrackComponentForBothBiasModes() throws Exception { + Form form = new Form(new BorderLayout()); + implementation.setCurrentForm(form); + + InteractionDialog topDialog = new InteractionDialog(); + topDialog.setLayout(new FlowLayout()); + topDialog.add(new Label("Popup")); + topDialog.setAnimateShow(false); + TrackingBorder topBorder = new TrackingBorder(); + topDialog.getAllStyles().setBorder(topBorder); + Rectangle rect = new Rectangle(120, 220, 60, 40); + topDialog.showPopupDialog(rect, true); + assertNotNull(topBorder.lastTrackRect, "Expected border tracking rectangle to be set"); + assertEquals(rect.getX(), topBorder.lastTrackRect.getX()); + assertEquals(rect.getY(), topBorder.lastTrackRect.getY()); + assertTrue(topDialog.getY() + topDialog.getHeight() <= rect.getY(), + "Bias=true should place popup above in this setup"); + topDialog.dispose(); + + InteractionDialog bottomDialog = new InteractionDialog(); + bottomDialog.setLayout(new FlowLayout()); + bottomDialog.add(new Label("Popup")); + bottomDialog.setAnimateShow(false); + TrackingBorder bottomBorder = new TrackingBorder(); + bottomDialog.getAllStyles().setBorder(bottomBorder); + bottomDialog.showPopupDialog(rect, false); + assertNotNull(bottomBorder.lastTrackRect, "Expected border tracking rectangle to be set"); + assertEquals(rect.getX(), bottomBorder.lastTrackRect.getX()); + assertEquals(rect.getY(), bottomBorder.lastTrackRect.getY()); + assertTrue(bottomDialog.getY() >= rect.getY() + rect.getHeight(), + "Bias=false should place popup below in this setup"); + bottomDialog.dispose(); + } + + private static class TrackingBorder extends Border { + private Rectangle lastTrackRect; + + @Override + public void setTrackComponent(Rectangle trackComponent) { + lastTrackRect = trackComponent; + super.setTrackComponent(trackComponent); + } + } + private T getPrivateField(Object target, String name, Class type) throws Exception { Field field = target.getClass().getDeclaredField(name); field.setAccessible(true); From c92cca9e340e70c8ace80fb2390e129e727b16ee Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 2 Apr 2026 07:58:14 +0300 Subject: [PATCH 16/17] Use absolute track rectangle for popup border arrow direction --- .../src/com/codename1/components/InteractionDialog.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CodenameOne/src/com/codename1/components/InteractionDialog.java b/CodenameOne/src/com/codename1/components/InteractionDialog.java index daaec54367..15db239d8c 100644 --- a/CodenameOne/src/com/codename1/components/InteractionDialog.java +++ b/CodenameOne/src/com/codename1/components/InteractionDialog.java @@ -738,12 +738,12 @@ public void showPopupDialog(Rectangle rect, boolean prioritizeTopOrRightPosition if (arrowEnabled && hasArrowImages) { Border border = contentPaneStyle.getBorder(); if (border != null) { - border.setImageBorderSpecialTile(t, b, l, r, rect); + border.setImageBorderSpecialTile(t, b, l, r, origRect); } } else { Border border = contentPaneStyle.getBorder(); if (border != null) { - border.setTrackComponent(rect); + border.setTrackComponent(origRect); } } calcPreferredSize(); From 500a37cf8a1732c0005be36414cc550939441aa6 Mon Sep 17 00:00:00 2001 From: liannacasper <67953602+liannacasper@users.noreply.github.com> Date: Thu, 2 Apr 2026 08:27:47 +0300 Subject: [PATCH 17/17] Stabilize tracking border test by preserving PopupDialog UIID --- .../java/com/codename1/components/InteractionDialogTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java b/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java index 0a68675613..72a0ddaa25 100644 --- a/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java +++ b/maven/core-unittests/src/test/java/com/codename1/components/InteractionDialogTest.java @@ -255,6 +255,7 @@ void showPopupDialogSetsArrowTrackComponentForBothBiasModes() throws Exception { implementation.setCurrentForm(form); InteractionDialog topDialog = new InteractionDialog(); + topDialog.setUIID("PopupDialog"); topDialog.setLayout(new FlowLayout()); topDialog.add(new Label("Popup")); topDialog.setAnimateShow(false); @@ -270,6 +271,7 @@ void showPopupDialogSetsArrowTrackComponentForBothBiasModes() throws Exception { topDialog.dispose(); InteractionDialog bottomDialog = new InteractionDialog(); + bottomDialog.setUIID("PopupDialog"); bottomDialog.setLayout(new FlowLayout()); bottomDialog.add(new Label("Popup")); bottomDialog.setAnimateShow(false);