Skip to content

Commit 7937cc7

Browse files
mkartashevjbrbot
authored andcommitted
JBR-10014 Wayland: popup positioning to work with 'screen' coordinates
1 parent 5c0a782 commit 7937cc7

12 files changed

Lines changed: 376 additions & 168 deletions

File tree

src/java.desktop/share/classes/javax/swing/JPopupMenu.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,7 @@ public JMenuItem add(Action a) {
336336
Point adjustPopupLocationToFitScreen(int xPosition, int yPosition) {
337337
Point popupLocation = new Point(xPosition, yPosition);
338338

339-
if(popupPositionFixDisabled
340-
|| GraphicsEnvironment.isHeadless()
341-
|| PopupFactory.isPopupPositionedRelatively()) {
339+
if(popupPositionFixDisabled || GraphicsEnvironment.isHeadless()) {
342340
return popupLocation;
343341
}
344342

@@ -992,7 +990,7 @@ public void show(Component invoker, int x, int y) {
992990
}
993991
}
994992
Point invokerOrigin;
995-
if (invoker != null && !PopupFactory.isPopupPositionedRelatively()) {
993+
if (invoker != null) {
996994
invokerOrigin = invoker.getLocationOnScreen();
997995

998996
// To avoid integer overflow

src/java.desktop/share/classes/javax/swing/ToolTipManager.java

Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -262,57 +262,49 @@ void showTipWindow() {
262262
Point location;
263263
Rectangle sBounds = null;
264264

265-
boolean isTooltipPositionedAbsolutely = !isTooltipPositionedRelatively();
266-
if (isTooltipPositionedAbsolutely) {
267-
Point toFind;
268-
if (preferredLocation != null) {
269-
toFind = new Point(screenLocation.x + preferredLocation.x,
270-
screenLocation.y + preferredLocation.y);
265+
Point toFind;
266+
if (preferredLocation != null) {
267+
toFind = new Point(screenLocation.x + preferredLocation.x,
268+
screenLocation.y + preferredLocation.y);
269+
} else {
270+
if (mouseEvent != null) {
271+
toFind = mouseEvent.getLocationOnScreen();
271272
} else {
272-
if (mouseEvent != null) {
273-
toFind = mouseEvent.getLocationOnScreen();
274-
} else {
275-
toFind = screenLocation;
276-
}
273+
toFind = screenLocation;
277274
}
275+
}
278276

279-
GraphicsConfiguration gc = getDrawingGC(toFind);
277+
GraphicsConfiguration gc = getDrawingGC(toFind);
278+
if (gc == null) {
279+
if (mouseEvent != null) {
280+
toFind = mouseEvent.getLocationOnScreen();
281+
gc = getDrawingGC(toFind);
282+
}
280283
if (gc == null) {
281-
if (mouseEvent != null) {
282-
toFind = mouseEvent.getLocationOnScreen();
283-
gc = getDrawingGC(toFind);
284-
}
285-
if (gc == null) {
286-
gc = insideComponent.getGraphicsConfiguration();
287-
}
284+
gc = insideComponent.getGraphicsConfiguration();
288285
}
286+
}
289287

290-
sBounds = gc.getBounds();
291-
Insets screenInsets = Toolkit.getDefaultToolkit()
292-
.getScreenInsets(gc);
293-
// Take into account screen insets, decrease viewport
294-
sBounds.x += screenInsets.left;
295-
sBounds.y += screenInsets.top;
296-
sBounds.width -= (screenInsets.left + screenInsets.right);
297-
sBounds.height -= (screenInsets.top + screenInsets.bottom);
298-
299-
if (preferredLocation != null) {
300-
location = toFind;
301-
} else {
302-
if (mouseEvent != null) {
303-
location = new Point(screenLocation.x + mouseEvent.getX(),
304-
screenLocation.y + mouseEvent.getY() + 20);
305-
} else {
306-
location = screenLocation;
307-
}
308-
}
288+
sBounds = gc.getBounds();
289+
Insets screenInsets = Toolkit.getDefaultToolkit()
290+
.getScreenInsets(gc);
291+
// Take into account screen insets, decrease viewport
292+
sBounds.x += screenInsets.left;
293+
sBounds.y += screenInsets.top;
294+
sBounds.width -= (screenInsets.left + screenInsets.right);
295+
sBounds.height -= (screenInsets.top + screenInsets.bottom);
296+
297+
if (preferredLocation != null) {
298+
location = toFind;
309299
} else {
310300
if (mouseEvent != null) {
311-
location = new Point(mouseEvent.getX(), mouseEvent.getY() + 20);
301+
location = new Point(screenLocation.x + mouseEvent.getX(),
302+
screenLocation.y + mouseEvent.getY() + 20);
312303
} else {
313304
location = screenLocation;
314305
}
315306
}
307+
316308
boolean leftToRight
317309
= SwingUtilities.isLeftToRight(insideComponent);
318310
// Just to be paranoid
@@ -335,18 +327,16 @@ void showTipWindow() {
335327
popupRect.setBounds(location.x, location.y,
336328
size.width, size.height);
337329

338-
if (isTooltipPositionedAbsolutely) {
339-
// Fit as much of the tooltip on screen as possible
340-
if (location.x < sBounds.x) {
341-
location.x = sBounds.x;
342-
} else if (location.x - sBounds.x + size.width > sBounds.width) {
343-
location.x = sBounds.x + Math.max(0, sBounds.width - size.width);
344-
}
345-
if (location.y < sBounds.y) {
346-
location.y = sBounds.y;
347-
} else if (location.y - sBounds.y + size.height > sBounds.height) {
348-
location.y = sBounds.y + Math.max(0, sBounds.height - size.height);
349-
}
330+
// Fit as much of the tooltip on screen as possible
331+
if (location.x < sBounds.x) {
332+
location.x = sBounds.x;
333+
} else if (location.x - sBounds.x + size.width > sBounds.width) {
334+
location.x = sBounds.x + Math.max(0, sBounds.width - size.width);
335+
}
336+
if (location.y < sBounds.y) {
337+
location.y = sBounds.y;
338+
} else if (location.y - sBounds.y + size.height > sBounds.height) {
339+
location.y = sBounds.y + Math.max(0, sBounds.height - size.height);
350340
}
351341

352342
PopupFactory popupFactory = PopupFactory.getSharedInstance();

src/java.desktop/unix/classes/sun/awt/wl/WLComponentPeer.java

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -345,18 +345,19 @@ static void moveToOverlap(Rectangle what, Rectangle where) {
345345
assert what.intersects(where) : String.format("Failed to move %s to overlap %s", what, where);
346346
}
347347

348-
Point nativeLocationForPopup(Window popup, Component popupParent, Window toplevel) {
348+
Point nativeLocationForPopup(Window popup, Window toplevel) {
349+
Objects.requireNonNull(popup);
350+
Objects.requireNonNull(toplevel);
349351
// NB: all the coordinates are in the "surface" space as consumed by Wayland,
350352
// not in pixels and not in the Java units.
351353

352-
// We need to provide popup's "parent" location relative to the surface this parent is painted upon:
353-
Point parentLocation = javaUnitsToSurfaceUnits(getRelativeLocation(popupParent, toplevel));
354-
355-
// Offset is relative to the top-left corner of the "parent".
356-
Point offsetFromParent = javaUnitsToSurfaceUnits(popup.getLocation());
354+
Point popupOnScreen = popup.getLocation();
355+
Point toplevelOnScreen = toplevel.getLocationOnScreen();
356+
Point popupOnToplevel = new Point(popupOnScreen.x - toplevelOnScreen.x, popupOnScreen.y - toplevelOnScreen.y);
357+
Point offsetFromToplevel = javaUnitsToSurfaceUnits(popupOnToplevel);
357358
var popupBounds = new Rectangle(
358-
parentLocation.x + offsetFromParent.x,
359-
parentLocation.y + offsetFromParent.y,
359+
offsetFromToplevel.x,
360+
offsetFromToplevel.y,
360361
wlSize.getSurfaceWidth(),
361362
wlSize.getSurfaceHeight());
362363
var safeToplevelBounds = toplevel.getSize();
@@ -372,7 +373,7 @@ Point nativeLocationForPopup(Window popup, Component popupParent, Window topleve
372373
// go outside the parent surface's bounds.
373374
moveToOverlap(popupBounds, safePopupBounds);
374375
}
375-
return popupBounds.getLocation();
376+
return popupBounds.getLocation(); // This is offset relative to the top-left corner of the toplevel
376377
}
377378

378379
private boolean isPopupPositionUnconstrained() {
@@ -454,7 +455,7 @@ private void show() {
454455
Window popup = (Window) target;
455456
Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
456457
Window toplevel = getToplevelFor(popupParent);
457-
Point nativeLocation = nativeLocationForPopup(popup, popupParent, toplevel);
458+
Point nativeLocation = nativeLocationForPopup(popup, toplevel);
458459
nativeCreatePopup(nativePtr, getNativePtrFor(toplevel), wlSurfacePtr,
459460
thisWidth, thisHeight, nativeLocation.x, nativeLocation.y, isUnconstrained);
460461
stage = 1;
@@ -518,7 +519,7 @@ protected void notifyNativeWindowToBeHidden(long nativePtr) {
518519
* Returns true if our target should be treated as a popup in Wayland's sense,
519520
* i.e. it has to have a parent to position relative to.
520521
*/
521-
protected boolean targetIsWlPopup() {
522+
protected final boolean targetIsWlPopup() {
522523
return target instanceof Window window && isWlPopup(window);
523524
}
524525

@@ -588,7 +589,7 @@ public void updateSurfaceSize() {
588589
Window popup = (Window) target;
589590
final Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
590591
final Window toplevel = getToplevelFor(popupParent);
591-
Point nativeLocation = nativeLocationForPopup(popup, popupParent, toplevel);
592+
Point nativeLocation = nativeLocationForPopup(popup, toplevel);
592593
boolean isUnconstrained = isPopupPositionUnconstrained();
593594
nativeRepositionWLPopup(nativePtr, surfaceWidth, surfaceHeight, nativeLocation.x, nativeLocation.y, isUnconstrained);
594595
}
@@ -830,18 +831,8 @@ private Point getFakeLocationOnScreen() {
830831
// their parents' fake screen location.
831832
if (targetIsWlPopup()) {
832833
Window popup = (Window) target;
833-
Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
834-
Window toplevel = getToplevelFor(popupParent);
835-
Point popupOffset = popup.getLocation(); // popup's offset from its parent
836-
if (toplevel != null) {
837-
Point parentOffset = getRelativeLocation(popupParent, toplevel);
838-
Point thisLocation = toplevel.getLocationOnScreen();
839-
thisLocation.translate(parentOffset.x, parentOffset.y);
840-
thisLocation.translate(popupOffset.x, popupOffset.y);
841-
return thisLocation;
842-
} else {
843-
return popupOffset;
844-
}
834+
// Popup's Window location is the "location on screen"
835+
return popup.getLocation();
845836
} else {
846837
GraphicsConfiguration graphicsConfig = target.getGraphicsConfiguration();
847838
if (graphicsConfig != null) {
@@ -1784,14 +1775,15 @@ void notifyConfigured(int newSurfaceX, int newSurfaceY, int newSurfaceWidth, int
17841775
int newX = surfaceUnitsToJavaUnits(newSurfaceX);
17851776
int newY = surfaceUnitsToJavaUnits(newSurfaceY);
17861777

1787-
// The popup itself stores its location relative to its parent, but what we've got is
1778+
// The popup location is in it toplevel's screen coordinate system, but what we've got is
17881779
// the location relative to the toplevel. Let's convert:
17891780
Window popup = (Window) target;
17901781
Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
17911782
Window toplevel = getToplevelFor(popupParent);
1792-
Point parentLocation = getRelativeLocation(popupParent, toplevel);
1793-
Point locationRelativeToParent = new Point(newX - parentLocation.x, newY - parentLocation.y);
1794-
resetTargetLocationTo(locationRelativeToParent.x, locationRelativeToParent.y);
1783+
Point toplevelOnScreen = toplevel == null // highly unlikely, if at all possible
1784+
? target.getGraphicsConfiguration().getBounds().getLocation()
1785+
: toplevel.getLocationOnScreen();
1786+
resetTargetLocationTo(newX + toplevelOnScreen.x, newY + toplevelOnScreen.y);
17951787
}
17961788

17971789
// From xdg-shell.xml: "If the width or height arguments are zero,
@@ -1869,7 +1861,22 @@ void checkIfOnNewScreen() {
18691861
if (oldDevice != newDevice) {
18701862
oldDevice.removeWindow(this);
18711863
newDevice.addWindow(this);
1864+
1865+
if (targetIsWlPopup()) {
1866+
Point loc = target.getLocation();
1867+
Point oldScreenLocation = oldDevice.getBounds().getLocation();
1868+
loc.translate(-oldScreenLocation.x, -oldScreenLocation.y);
1869+
Point newScreenLocation = newDevice.getBounds().getLocation();
1870+
loc.translate(newScreenLocation.x, newScreenLocation.y);
1871+
resetTargetLocationTo(loc.x, loc.y);
1872+
} else {
1873+
// A window has been moved to another screen. Since windows are assumed to be located at (0, 0)
1874+
// on their respective screens, update the location to reflect that.
1875+
Point newLocation = newDevice.getBounds().getLocation();
1876+
resetTargetLocationTo(newLocation.x, newLocation.y);
1877+
}
18721878
}
1879+
18731880
performUnlocked(() -> {
18741881
var acc = AWTAccessor.getComponentAccessor();
18751882
acc.setGraphicsConfiguration(target, gc);

test/jdk/jb/javax/swing/wayland/WLPopupAsParent.java

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,8 @@
2121
* questions.
2222
*/
2323

24-
import javax.swing.JFrame;
25-
import javax.swing.JLabel;
26-
import javax.swing.JPanel;
27-
import javax.swing.JWindow;
28-
import javax.swing.SwingUtilities;
29-
import java.awt.Color;
30-
import java.awt.Robot;
31-
import java.awt.Toolkit;
32-
import java.awt.Window;
24+
import javax.swing.*;
25+
import java.awt.*;
3326

3427
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
3528

@@ -39,7 +32,6 @@
3932
* itself is a popup
4033
* @requires os.family == "linux"
4134
* @key headful
42-
* @modules java.desktop/sun.awt
4335
* @run main WLPopupAsParent
4436
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.0 WLPopupAsParent
4537
* @run main/othervm -Dsun.java2d.uiScale.enabled=true -Dsun.java2d.uiScale=1.25 WLPopupAsParent
@@ -48,7 +40,7 @@
4840
*/
4941
public class WLPopupAsParent {
5042
private static JFrame frame;
51-
private static JWindow popup1;
43+
private static Popup popup1;
5244

5345
private static void createAndShowUI() {
5446
frame = new JFrame("WLPopupAsParent Test");
@@ -58,25 +50,23 @@ private static void createAndShowUI() {
5850
}
5951

6052
private static void showPopup1() {
61-
JPanel popupContents = new JPanel();
62-
popupContents.add(new JLabel("test popup"));
63-
popup1 = new JWindow(frame);
64-
popup1.setType(Window.Type.POPUP);
65-
sun.awt.AWTAccessor.getWindowAccessor().setPopupParent(popup1, frame);
66-
popup1.setBounds(15, 33, 100, 50);
67-
popup1.add(popupContents);
68-
popup1.setVisible(true);
53+
Point p = frame.getLocationOnScreen();
54+
popup1 = PopupFactory.getSharedInstance().getPopup(
55+
frame,
56+
new JLabel("test popup"),
57+
p.x + 15,
58+
p.y + 33);
59+
popup1.show();
6960
}
7061

7162
private static void showPopup2() {
72-
JPanel popupContents = new JPanel();
73-
popupContents.add(new JLabel("second-level popup"));
74-
JWindow popup2 = new JWindow(popup1);
75-
popup2.setType(Window.Type.POPUP);
76-
sun.awt.AWTAccessor.getWindowAccessor().setPopupParent(popup2, popup1);
77-
popup2.setBounds(85, 267, 200, 100);
78-
popup2.add(popupContents);
79-
popup2.setVisible(true);
63+
Point p = frame.getLocationOnScreen();
64+
Popup popup2 = PopupFactory.getSharedInstance().getPopup(
65+
frame,
66+
new JLabel("second-level popup"),
67+
p.x + 85,
68+
p.y + 267);
69+
popup2.show();
8070
}
8171

8272
public static void main(String[] args) throws Exception {

0 commit comments

Comments
 (0)