mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-9733 Wayland: enable unconstrained popup positioning
Use getRootPane()
.putClientProperty("wlawt.popup_position_unconstrained", Boolean.TRUE)
to enable unconstrained popup positioning.
This commit is contained in:
@@ -88,6 +88,8 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.wl.focus.WLComponentPeer");
|
||||
private static final PlatformLogger popupLog = PlatformLogger.getLogger("sun.awt.wl.popup.WLComponentPeer");
|
||||
|
||||
public static final String POPUP_POSITION_UNCONSTRAINED_CLIENT_PROPERTY = "wlawt.popup_position_unconstrained";
|
||||
|
||||
protected static final int MINIMUM_WIDTH = 1;
|
||||
protected static final int MINIMUM_HEIGHT = 1;
|
||||
|
||||
@@ -354,6 +356,18 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
return popupBounds.getLocation();
|
||||
}
|
||||
|
||||
private boolean isPopupPositionUnconstrained() {
|
||||
if (SunToolkit.isInstanceOf(target, "javax.swing.RootPaneContainer")) {
|
||||
javax.swing.JRootPane rootpane = ((javax.swing.RootPaneContainer) target).getRootPane();
|
||||
Object prop = rootpane.getClientProperty(POPUP_POSITION_UNCONSTRAINED_CLIENT_PROPERTY);
|
||||
if (prop instanceof Boolean booleanProp) {
|
||||
return booleanProp;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void wlSetVisible(boolean v) {
|
||||
// TODO: this whole method should be moved to WLWindowPeer
|
||||
synchronized (getStateLock()) {
|
||||
@@ -373,6 +387,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
: Frame.NORMAL;
|
||||
boolean isMaximized = (state & Frame.MAXIMIZED_BOTH) == Frame.MAXIMIZED_BOTH;
|
||||
boolean isMinimized = (state & Frame.ICONIFIED) == Frame.ICONIFIED;
|
||||
boolean isUnconstrained = isPopupPositionUnconstrained();
|
||||
|
||||
performLocked(() -> {
|
||||
assert wlSurface == null;
|
||||
@@ -384,7 +399,7 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
Window toplevel = getToplevelFor(popupParent);
|
||||
Point nativeLocation = nativeLocationForPopup(popup, popupParent, toplevel);
|
||||
nativeCreatePopup(nativePtr, getNativePtrFor(toplevel), wlSurfacePtr,
|
||||
thisWidth, thisHeight, nativeLocation.x, nativeLocation.y);
|
||||
thisWidth, thisHeight, nativeLocation.x, nativeLocation.y, isUnconstrained);
|
||||
} else {
|
||||
nativeCreateWindow(nativePtr, getParentNativePtr(target), wlSurfacePtr,
|
||||
isModal, isMaximized, isMinimized, title, WLToolkit.getApplicationID());
|
||||
@@ -503,7 +518,8 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
final Component popupParent = AWTAccessor.getWindowAccessor().getPopupParent(popup);
|
||||
final Window toplevel = getToplevelFor(popupParent);
|
||||
Point nativeLocation = nativeLocationForPopup(popup, popupParent, toplevel);
|
||||
nativeRepositionWLPopup(nativePtr, surfaceWidth, surfaceHeight, nativeLocation.x, nativeLocation.y);
|
||||
boolean isUnconstrained = isPopupPositionUnconstrained();
|
||||
nativeRepositionWLPopup(nativePtr, surfaceWidth, surfaceHeight, nativeLocation.x, nativeLocation.y, isUnconstrained);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1154,11 +1170,13 @@ public class WLComponentPeer implements ComponentPeer, WLSurfaceSizeListener {
|
||||
|
||||
protected native void nativeCreatePopup(long ptr, long parentPtr, long wlSurfacePtr,
|
||||
int width, int height,
|
||||
int offsetX, int offsetY);
|
||||
int offsetX, int offsetY,
|
||||
boolean isUnconstrained);
|
||||
|
||||
protected native void nativeRepositionWLPopup(long ptr,
|
||||
int width, int height,
|
||||
int offsetX, int offsetY);
|
||||
int offsetX, int offsetY,
|
||||
boolean isUnconstrained);
|
||||
protected native void nativeHideFrame(long ptr);
|
||||
|
||||
protected native void nativeDisposeFrame(long ptr);
|
||||
|
||||
@@ -375,7 +375,7 @@ Java_sun_awt_wl_WLComponentPeer_nativeCreateWindow
|
||||
|
||||
static struct xdg_positioner *
|
||||
newPositioner
|
||||
(jint width, jint height, jint offsetX, jint offsetY)
|
||||
(jint width, jint height, jint offsetX, jint offsetY, jboolean isUnconstrained)
|
||||
{
|
||||
struct xdg_positioner *xdg_positioner = xdg_wm_base_create_positioner(xdg_wm_base);
|
||||
CHECK_NULL_RETURN(xdg_positioner, NULL);
|
||||
@@ -388,10 +388,15 @@ newPositioner
|
||||
xdg_positioner_set_offset(xdg_positioner, 0, 0);
|
||||
xdg_positioner_set_anchor(xdg_positioner, XDG_POSITIONER_ANCHOR_TOP_LEFT);
|
||||
xdg_positioner_set_gravity(xdg_positioner, XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT);
|
||||
if (isUnconstrained) {
|
||||
xdg_positioner_set_constraint_adjustment(xdg_positioner,
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE);
|
||||
} else {
|
||||
xdg_positioner_set_constraint_adjustment(xdg_positioner,
|
||||
XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y
|
||||
| XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X
|
||||
| XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y);
|
||||
}
|
||||
return xdg_positioner;
|
||||
}
|
||||
|
||||
@@ -399,7 +404,8 @@ JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeCreatePopup
|
||||
(JNIEnv *env, jobject obj, jlong ptr, jlong parentPtr, jlong wlSurfacePtr,
|
||||
jint width, jint height,
|
||||
jint offsetX, jint offsetY)
|
||||
jint offsetX, jint offsetY,
|
||||
jboolean isUnconstrained)
|
||||
{
|
||||
struct WLFrame *frame = (struct WLFrame *) ptr;
|
||||
struct WLFrame *parentFrame = (struct WLFrame*) parentPtr;
|
||||
@@ -411,7 +417,7 @@ Java_sun_awt_wl_WLComponentPeer_nativeCreatePopup
|
||||
frame->toplevel = JNI_FALSE;
|
||||
|
||||
assert(parentFrame);
|
||||
struct xdg_positioner *xdg_positioner = newPositioner(width, height, offsetX, offsetY);
|
||||
struct xdg_positioner *xdg_positioner = newPositioner(width, height, offsetX, offsetY, isUnconstrained);
|
||||
CHECK_NULL(xdg_positioner);
|
||||
JNU_RUNTIME_ASSERT(env, parentFrame->toplevel, "Popup's parent surface must be a toplevel");
|
||||
frame->xdg_popup = xdg_surface_get_popup(frame->xdg_surface, parentFrame->xdg_surface, xdg_positioner);
|
||||
@@ -424,13 +430,14 @@ JNIEXPORT void JNICALL
|
||||
Java_sun_awt_wl_WLComponentPeer_nativeRepositionWLPopup
|
||||
(JNIEnv *env, jobject obj, jlong ptr,
|
||||
jint width, jint height,
|
||||
jint offsetX, jint offsetY)
|
||||
jint offsetX, jint offsetY,
|
||||
jboolean isUnconstrained)
|
||||
{
|
||||
struct WLFrame *frame = jlong_to_ptr(ptr);
|
||||
assert (!frame->toplevel);
|
||||
|
||||
if (wl_proxy_get_version((struct wl_proxy *)xdg_wm_base) >= 3) {
|
||||
struct xdg_positioner *xdg_positioner = newPositioner(width, height, offsetX, offsetY);
|
||||
struct xdg_positioner *xdg_positioner = newPositioner(width, height, offsetX, offsetY, isUnconstrained);
|
||||
CHECK_NULL(xdg_positioner);
|
||||
static int token = 42; // This will be received by xdg_popup_repositioned(); unused for now.
|
||||
xdg_popup_reposition(frame->xdg_popup, xdg_positioner, token++);
|
||||
|
||||
95
test/jdk/jb/javax/swing/wayland/WLPopupUnconstrained.java
Normal file
95
test/jdk/jb/javax/swing/wayland/WLPopupUnconstrained.java
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2025 JetBrains s.r.o.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JWindow;
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @summary Verifies popups can be located regardless of the screen edge proximity
|
||||
* @requires os.family == "linux"
|
||||
* @key headful
|
||||
* @modules java.desktop/sun.awt
|
||||
* @run main/manual WLPopupUnconstrained
|
||||
*/
|
||||
public class WLPopupUnconstrained {
|
||||
static final CompletableFuture<RuntimeException> swingError = new CompletableFuture<>();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SwingUtilities.invokeAndWait(WLPopupUnconstrained::showUI);
|
||||
swingError.get();
|
||||
}
|
||||
|
||||
private static void showUI() {
|
||||
JFrame frame = new JFrame("Unconstrained popup test");
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
|
||||
JLabel label = new JLabel("Right-click here for a popup.");
|
||||
|
||||
JPanel popupContents = new JPanel();
|
||||
popupContents.add(new JLabel("test popup"));
|
||||
JWindow popup = new JWindow(frame);
|
||||
popup.setType(Window.Type.POPUP);
|
||||
sun.awt.AWTAccessor.getWindowAccessor().setPopupParent(popup, label);
|
||||
popup.getRootPane().putClientProperty("wlawt.popup_position_unconstrained", Boolean.TRUE);
|
||||
popup.setSize(300, 250);
|
||||
popup.add(popupContents);
|
||||
label.addMouseListener(new MouseAdapter() {
|
||||
public void mousePressed(MouseEvent e) {
|
||||
if (e.isPopupTrigger()) {
|
||||
popup.setLocation(e.getX(), e.getY());
|
||||
popup.setVisible(true);
|
||||
} else {
|
||||
popup.setVisible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
JPanel content = new JPanel();
|
||||
var layout = new GridLayout(3, 2, 10, 10);
|
||||
content.setLayout(layout);
|
||||
content.add(new JLabel("<html><h1>INSTRUCTIONS</h1>" +
|
||||
"<p>Locate this window close to the bottom-right edge of the screen.</p>" +
|
||||
"<p>Make the popup appear (on the right) such that it is partially outside the screen.</p>" +
|
||||
"<p>Press Pass iff the popup indeed crossed the screen edge.</p>" +
|
||||
"<p>Otherwise press Fail.</p></html>"));
|
||||
content.add(label);
|
||||
JButton passButton = new JButton("Pass");
|
||||
passButton.addActionListener(e -> {swingError.complete(null);});
|
||||
JButton failButton = new JButton("Fail");
|
||||
failButton.addActionListener(e -> {swingError.completeExceptionally(new RuntimeException("The tester has pressed FAILED"));});
|
||||
content.add(failButton);
|
||||
content.add(passButton);
|
||||
frame.setContentPane(content);
|
||||
frame.pack();
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user