我有一个透明的舞台,当用户在该舞台内单击时,我需要将鼠标事件传递到 JavaFX 舞台后面的窗口(例如,如果我的舞台后面有一个文本编辑器,则应将鼠标事件传递到那个编辑器窗口)。换句话说,我需要舞台对鼠标事件是透明的。
这是我的代码:
public class Test12 extends Application {
@Override
public void start(Stage primaryStage) {
var vBox = new VBox();
vBox.setStyle("-fx-border-width: 1; -fx-border-color: black");
vBox.setMouseTransparent(true);
var scene = new Scene(vBox, 400, 300);
scene.setFill(Color.TRANSPARENT);
scene.getStylesheets().add(this.getClass().getResource("test.css").toExternalForm());
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setAlwaysOnTop(true);
}
public static void main(String[] args) {
launch(args);
}
}
CSS
.root { -fx-background-color: transparent; }
如果可能的话,谁能告诉我该怎么做。
更新
这是一个 C 程序,使窗口的右半部分对鼠标事件透明(使用
gcc -o program program.c -lX11 -lXext
):
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdio.h>
#include <X11/extensions/shape.h>
/* ----------------------------------------------------------------------------------------------- */
#define _NET_WM_STATE_REMOVE 0 // remove/unset property
#define _NET_WM_STATE_ADD 1 // add/set property
#define _NET_WM_STATE_TOGGLE 2 // toggle property
Bool makeAlwaysOnTop(Display* display, Window mywin) {
int screen = DefaultScreen(display);
Window root = RootWindow(display, screen);
Atom wmStateAbove = XInternAtom( display, "_NET_WM_STATE_ABOVE", 1 );
if( wmStateAbove != None ) {
printf( "_NET_WM_STATE_ABOVE has atom of %ld\n", (long)wmStateAbove );
} else {
printf( "ERROR: cannot find atom for _NET_WM_STATE_ABOVE !\n" );
return False;
}
Atom wmNetWmState = XInternAtom( display, "_NET_WM_STATE", 1 );
if( wmNetWmState != None ) {
printf( "_NET_WM_STATE has atom of %ld\n", (long)wmNetWmState );
} else {
printf( "ERROR: cannot find atom for _NET_WM_STATE !\n" );
return False;
}
if( wmStateAbove != None )
{
XClientMessageEvent xclient;
memset( &xclient, 0, sizeof (xclient) );
xclient.type = ClientMessage;
xclient.window = mywin;
xclient.message_type = wmNetWmState;
xclient.format = 32;
xclient.data.l[0] = _NET_WM_STATE_ADD;
xclient.data.l[1] = wmStateAbove;
xclient.data.l[2] = 0;
xclient.data.l[3] = 0;
xclient.data.l[4] = 0;
XSendEvent( display,
root,
False,
SubstructureRedirectMask | SubstructureNotifyMask,
(XEvent *)&xclient );
XFlush(display);
return True;
}
return False;
}
/* ----------------------------------------------------------------------------------------------- */
void makeHalfTransparent(Display* dpy, Window win) {
Pixmap shape = XCreatePixmap(dpy, win, 400, 300, 1);
GC gc = XCreateGC(dpy, shape, 0, NULL);
XSetFillStyle(dpy, gc, FillSolid);
XSetForeground(dpy, gc, 0);
XFillRectangle(dpy, shape, gc, 0, 0, 400, 300);
XSetForeground(dpy, gc, 1);
XFillRectangle(dpy, shape, gc, 0, 0, 200, 300);
XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, shape, ShapeSet);
XFreeGC(dpy, gc);
}
/* ----------------------------------------------------------------------------------------------- */
int main() {
Display *dpy;
Window win;
XEvent event;
int screen;
dpy = XOpenDisplay(NULL);
if (dpy == NULL) {
fprintf(stderr, "Cannot open display\n");
return 1;
}
screen = DefaultScreen(dpy);
win = XCreateSimpleWindow(dpy, RootWindow(dpy, screen), 10, 10, 400, 300, 1, BlackPixel(dpy, screen), WhitePixel(dpy, screen));
XSelectInput(dpy, win, ExposureMask | KeyPressMask);
XMapWindow(dpy, win);
makeAlwaysOnTop(dpy, win);
makeHalfTransparent(dpy, win);
while (1) {
XNextEvent(dpy, &event);
if (event.type == KeyPress) {
break;
}
}
XDestroyWindow(dpy, win);
XCloseDisplay(dpy);
return 0;
}
这是我的 JavaFX 应用程序,使用 JNA 必须使窗口的右半部分对鼠标事件透明(换句话说,我将
makeHalfTransparent
函数从 C 移植到 Java):
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.unix.X11;
import com.sun.jna.ptr.PointerByReference;
import java.lang.reflect.Method;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.stage.StageStyle;
public class JavaFxTest10 extends Application {
public static interface CustomX11 extends Library {
CustomX11 INSTANCE = (CustomX11) Native.load("X11", CustomX11.class);
X11.Display XOpenDisplay(String display_name);
int XFreeGC(X11.Display display, X11.GC gc);
void XSetForeground(X11.Display display, X11.GC gc, int foreground);
X11.Pixmap XCreatePixmap(X11.Display display, X11.Window w, int width, int height, int depth);
void XFillRectangle(X11.Display display, X11.Pixmap drawable, X11.GC gc, int x, int y, int width, int height);
X11.GC XCreateGC(X11.Display display, X11.Pixmap drawable, int valuemask, X11.XGCValues values);
void XFreePixmap(X11.Display display, X11.Pixmap pixmap);
int XFetchName(X11.Display display, Window w, PointerByReference windowName);
X11.Window XDefaultRootWindow(X11.Display display);
X11.Window XCreateSimpleWindow(X11.Display display, X11.Window rootWindow, int a, int b, int c, int d, int e, int h, int g);
X11.Atom XInternAtom(X11.Display display, String atom_name, boolean only_if_exists);
void XChangeProperty(X11.Display display, X11.Window window, X11.Atom property, X11.Atom type, int format, int mode, Pointer data, int nelements);
void XSetFillStyle(X11.Display display, X11.GC gc, int fillStyle);
void XFillRectangle(X11.Display display, X11.Drawable drawable, X11.GC gc, int x, int y, int width, int height);
}
public interface Xext extends Library {
Xext INSTANCE = Native.load("Xext", Xext.class);
void XShapeCombineMask(X11.Display display, X11.Window w, int shapeOp, int x, int y, X11.Pixmap shape, int shapeKind);
}
@Override
public void start(Stage primaryStage) {
var button = new Button("Push Me");
button.setOnAction(e -> {
var p = getWIndowPoiter(primaryStage);
var window = getWindow(p);
printWindowTitle(window);//checking pointer
makeHalfTransparent(window, (int) primaryStage.getWidth(), (int) primaryStage.getHeight());
});
var hBox = new HBox(button);
hBox.setStyle("-fx-background-color: green");
var content = new VBox();
content.setMouseTransparent(true);
VBox.setVgrow(content, Priority.ALWAYS);
var mainBox = new VBox(hBox, content);
mainBox.setStyle("-fx-border-width: 1; -fx-border-color: black");
var scene = new Scene(mainBox, 400, 300);
scene.setFill(Color.TRANSPARENT);
scene.getStylesheets().add(this.getClass().getResource("test.css").toExternalForm());
primaryStage.initStyle(StageStyle.TRANSPARENT);
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setAlwaysOnTop(true);
primaryStage.setTitle("JafaFX Window 777");
}
private Pointer getWIndowPoiter(Stage stage) {
try {
final Method getPeer = Window.class.getDeclaredMethod("getPeer", null);
getPeer.setAccessible(true);
final Object tkStage = getPeer.invoke(stage);
final Method getRawHandle = tkStage.getClass().getMethod("getRawHandle");
getRawHandle.setAccessible(true);
var l = (Long) getRawHandle.invoke(tkStage);
final Pointer pointer = new Pointer(l);
System.out.println(pointer);
return pointer;
} catch (Exception ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
return null;
}
}
private X11.Window getWindow( Pointer windowPointer) {
X11.Window window = new X11.Window(Pointer.nativeValue(windowPointer));
return window;
}
private void printWindowTitle(X11.Window window) {
final X11 x11 = X11.INSTANCE;
X11.Display display = x11.XOpenDisplay(null);
PointerByReference windowNameReference = new PointerByReference();
int status = X11.INSTANCE.XFetchName(display, window, windowNameReference);
if (status == 1) {
Pointer windowNamePointer = windowNameReference.getValue();
String windowTitle = windowNamePointer.getString(0);
System.out.println("Window Title: " + windowTitle);
} else {
System.out.println("Failed to fetch window name.");
}
}
public static final int FillSolid = 0;
public static final int ShapeBounding = 0;
public static final int ShapeSet = 0;
private void makeHalfTransparent(X11.Window win, int width, int height) {
X11.Display display = CustomX11.INSTANCE.XOpenDisplay(null);
//Pixmap shape = XCreatePixmap(dpy, win, 400, 300, 1);
X11.Pixmap shape = CustomX11.INSTANCE.XCreatePixmap(display, win, width, height, 1);
//GC gc = XCreateGC(dpy, shape, 0, NULL);
X11.GC gc2 = CustomX11.INSTANCE.XCreateGC(display, shape, 0, null);
//XSetFillStyle(dpy, gc, FillSolid);
CustomX11.INSTANCE.XSetFillStyle(display, gc2, FillSolid);
//XSetForeground(dpy, gc, 0);
CustomX11.INSTANCE.XSetForeground(display, gc2, 0);
//XFillRectangle(dpy, shape, gc, 0, 0, 400, 300);
CustomX11.INSTANCE.XFillRectangle(display, shape, gc2, 0, 0, width, height);
//XSetForeground(dpy, gc, 1);
CustomX11.INSTANCE.XSetForeground(display, gc2, 1);
//XFillRectangle(dpy, shape, gc, 0, 0, 200, 300);
CustomX11.INSTANCE.XFillRectangle(display, shape, gc2, 0, 0, width / 2, height);
//XShapeCombineMask(dpy, win, ShapeBounding, 0, 0, shape, ShapeSet);
Xext.INSTANCE.XShapeCombineMask(display, win, ShapeBounding, 0, 0, shape, ShapeSet);
//XFreeGC(dpy, gc);
CustomX11.INSTANCE.XFreeGC(display, gc2);
}
public static void main(String[] args) {
launch(args);
}
}
但它在 JavaFX 中不起作用。谁能帮助我使 Linux X11 的 JavaFX 窗口对鼠标事件透明?
不确定这种解决方法是否适合您的用例,但您可以通过暂时最小化窗口、使用 java fx Robot 单击鼠标并最大化窗口来实现此效果。这可以通过事件处理程序来完成,如下所示:
Robot robot = new Robot();
scene.setOnMouseClicked(event -> {
primaryStage.setIconified(true);
robot.mousePress(MouseButton.PRIMARY);
robot.mouseRelease(MouseButton.PRIMARY);
primaryStage.setIconified(false);
});