Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions CodenameOne/src/com/codename1/components/InteractionDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -839,23 +839,48 @@ private void showPopupDialogImpl(Rectangle rect, boolean bias) {
}
}
}
if (rect.getY() + rect.getHeight() < availableHeight / 2) {
// Pick the side of the rect (above vs. below) the popup goes
// on. The original logic chose purely by which half of the
// screen the rect sat in, which placed the popup ON TOP of
// the rect whenever it straddled the midline -- the symptom
// in #5028 (popup covers target) and #5029 (CSSBorder.Arrow
// can't pick a consistent direction so the tip renders on
// the wrong edge). Prefer whichever side fits the popup's
// preferred height, falling back to the larger side. The
// historical "over the rect" branches are kept as a last
// resort for the degenerate case where neither side has any
// room at all.
int spaceAbove = Math.max(0, rect.getY());
int spaceBelow = Math.max(0, availableHeight - rect.getY() - rect.getHeight());
boolean placeBelow;
if (spaceBelow >= prefHeight) {
placeBelow = true;
} else if (spaceAbove >= prefHeight) {
placeBelow = false;
} else if (spaceBelow >= spaceAbove) {
placeBelow = spaceBelow > 0;
} else {
placeBelow = false;
}
if (placeBelow && spaceBelow > 0) {
// popup downwards
y = rect.getY() + rect.getHeight();
int height = Math.min(prefHeight, Math.max(0, availableHeight - y));
int height = Math.min(prefHeight, spaceBelow);
padOrientation(contentPaneStyle, TOP, 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);
} else if (rect.getY() > availableHeight / 2) {
} else if (!placeBelow && spaceAbove > 0) {
// popup upwards
int height = Math.min(prefHeight, rect.getY());
int height = Math.min(prefHeight, spaceAbove);
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
// popup over aligned with top of rect, but inset a few
// mm. Fallback for the truly degenerate case where the
// rect fills the viewport top-to-bottom.
y = rect.getY() + CN.convertToPixels(3);

int height = Math.min(prefHeight, availableHeight - y);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,83 @@ void formModeUsesFormLayeredPane() {
dialog.dispose();
}

@Test
void showPopupDialogStraddlingMidlineDoesNotOverlapTarget() {
// Regression for #5028: when the anchor rect straddles the
// vertical midline, the legacy placement logic fell through to a
// "popup over aligned with top of rect" branch that drew the
// popup ON TOP of the target (covering the Close button in the
// reporter's screenshot). The fix prefers above / below based on
// available space; the popup must end up entirely outside the
// target rect.
implementation.setDisplaySize(1080, 1920);
implementation.setPortrait(true);
Form form = new Form(new BorderLayout());
implementation.setCurrentForm(form);
InteractionDialog dialog = new InteractionDialog();
dialog.setAnimateShow(false);
dialog.addComponent(new Label("Popup body content"));

// 60px target straddling the midline at y=960.
int targetHeight = 60;
int targetY = 1920 / 2 - targetHeight / 2;
Rectangle anchor = new Rectangle(490, targetY, 100, targetHeight);
dialog.showPopupDialog(anchor);

int dlgTop = dialog.getAbsoluteY();
int dlgBottom = dlgTop + dialog.getHeight();
int targetBottom = targetY + targetHeight;
assertTrue(dialog.getHeight() > 0, "popup must have non-zero height");
boolean overlaps = dlgTop < targetBottom && dlgBottom > targetY;
assertFalse(overlaps,
"#5028: popup [" + dlgTop + ".." + dlgBottom
+ ") overlaps anchor [" + targetY + ".." + targetBottom
+ ") -- expected the popup to land entirely above or below the rect");

dialog.dispose();
}

@Test
void showPopupDialogArrowDirectionConsistentWithPlacement() {
// Regression for #5029: with the popup ending up overlapping the
// target (the #5028 bug), CSSBorder.Arrow could not pick a
// consistent direction (cabsY straddles trackY..trackY+h) so the
// arrow tip rendered on the wrong edge. The arrow logic needs the
// popup to be either fully above or fully below the target; this
// test mirrors the reporter's geometry (target halfway down a
// tall column) and pins that invariant.
implementation.setDisplaySize(1080, 1920);
implementation.setPortrait(true);
Form form = new Form(new BorderLayout());
implementation.setCurrentForm(form);
InteractionDialog dialog = new InteractionDialog();
dialog.setAnimateShow(false);
dialog.addComponent(new Label("Popup body content"));

// Target lives at y = available/2 - 1 (rect.getY() < availableHeight/2,
// rect.bottom > availableHeight/2). This is the exact case that
// used to hit the buggy "popup over aligned with top of rect"
// branch before the fix.
Rectangle anchor = new Rectangle(490, 1920 / 2 - 1, 100, 80);
dialog.showPopupDialog(anchor);

int dlgTop = dialog.getAbsoluteY();
int dlgBottom = dlgTop + dialog.getHeight();
int targetTop = anchor.getY();
int targetBottom = targetTop + anchor.getHeight();

boolean popupBelowTarget = dlgTop >= targetBottom;
boolean popupAboveTarget = dlgBottom <= targetTop;
assertTrue(popupBelowTarget || popupAboveTarget,
"#5029: popup at [" + dlgTop + ".." + dlgBottom
+ ") is neither fully above nor fully below target ["
+ targetTop + ".." + targetBottom
+ ") -- CSSBorder.Arrow has no consistent direction"
+ " to point at the target");

dialog.dispose();
}

@Test
void showPopupDialogLandscapeFullWidthRectGetsVisibleSize() {
// Regression for #4991: in landscape, when the anchor rect spans the
Expand Down
Loading