JavaFX Минимизация недекорированной сцены
У меня есть незакодированная сцена JavaFX, а также мои собственные кнопки минимизации, максимизации и закрытия. Но, к сожалению, щелчок на значке панели задач в Windows 7 не сводит к минимуму этап - по сравнению с оформленным поведением.
Есть ли способ минимизировать незадекларированный этап с помощью чистого кода Java, щелкнув значок панели задач? Если нет, то как я могу это сделать, например, с помощью JNA?
EDIT:
Хорошо, я пытался решить эту проблему с помощью JNA, но, не выполнив ни одного C/С++/JNA, я немного затрудняюсь настроить это. Я был бы признателен, если бы кто-то помог мне собрать кусочки вместе.
Здесь мой код:
public final class Utils {
static {
if (PlatformUtil.isWin7OrLater()) {
Native.register("shell32");
Native.register("user32");
}
}
// Apparently, this is the event I am after
public static final int WM_ACTIVATEAPP = 0x1C;
public static void registerMinimizeHandler(Stage stage) {
// Hacky way to get a pointer to JavaFX Window
Pointer pointer = getWindowPointer(stage);
WinDef.HWND hwnd = new WinDef.HWND(pointer);
// Here my minimize/activate handler
WinUser.WindowProc windowProc = new MinimizeHandler(stage);
Pointer magicPointer = ... set this to point to windowProc?
// This.. apparently, re-sets the WndProc? But how do I get the "magicPointer" that is "attached" to the windowProc?
User32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, magicPointer);
}
}
private static class MinimizeHandler implements WinUser.WindowProc {
private Stage stage;
private MinimizeHandler(Stage stage) {
this.stage = stage;
}
@Override
public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
if (uMsg == WM_ACTIVATEAPP) {
System.out.println("ACTIVATE");
}
return User32.INSTANCE.DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
private static Pointer getWindowPointer(Stage stage) {
try {
TKStage tkStage = stage.impl_getPeer();
Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow" );
getPlatformWindow.setAccessible(true);
Object platformWindow = getPlatformWindow.invoke(tkStage);
Method getNativeHandle = platformWindow.getClass().getMethod( "getNativeHandle" );
getNativeHandle.setAccessible(true);
Object nativeHandle = getNativeHandle.invoke(platformWindow);
return new Pointer((Long) nativeHandle);
} catch (Throwable e) {
System.err.println("Error getting Window Pointer");
return null;
}
}
ИЗМЕНИТЬ 2: В конце концов я получил дальнейшее развитие с этим, но как только я снова установил WNDPROC, мое незадекларированное окно не отвечало на какие-либо события. Я предлагаю щедрость 100 репутации для автономного примера с рабочим решением. Windows (7+) работает нормально, я даже не знаю, как это ведет себя на других платформах.
ИЗМЕНИТЬ 3:
Ну, я вроде как сдался с этим... Я правильно настроил все, и получил события, но у меня возникли проблемы с выяснением правильного события, которое нужно слушать..
Поскольку у нас был интерес к вопросу, если кто-то хочет попытаться продолжить это, вот мой последний код (он, надеюсь, должен "работать" из коробки):
public final class Utils {
static interface ExtUser32 extends StdCallLibrary, User32 {
ExtUser32 INSTANCE = (ExtUser32) Native.loadLibrary(
"user32",
ExtUser32.class,
W32APIOptions.DEFAULT_OPTIONS);
WinDef.LRESULT CallWindowProcW(
Pointer lpWndProc,
Pointer hWnd,
int msg,
WinDef.WPARAM wParam,
WinDef.LPARAM lParam);
int SetWindowLong(HWND hWnd, int nIndex, com.sun.jna.Callback wndProc) throws LastErrorException;
}
// Some possible event types
public static final int WM_ACTIVATE = 0x0006;
public static final int WM_ACTIVATEAPP = 0x1C;
public static final int WM_NCACTIVATE = 0x0086;
public static void registerMinimizeHandler(Stage stage) {
Pointer pointer = getWindowPointer(stage);
WinDef.HWND hwnd = new WinDef.HWND(pointer);
long old = ExtUser32.INSTANCE.GetWindowLong(hwnd, User32.GWL_WNDPROC);
MinimizeHandler handler = new MinimizeHandler(stage, old);
ExtUser32.INSTANCE.SetWindowLong(hwnd, User32.GWL_WNDPROC, handler);
}
private static Pointer getWindowPointer(Stage stage) {
try {
TKStage tkStage = stage.impl_getPeer();
Method getPlatformWindow = tkStage.getClass().getDeclaredMethod("getPlatformWindow" );
getPlatformWindow.setAccessible(true);
Object platformWindow = getPlatformWindow.invoke(tkStage);
Method getNativeHandle = platformWindow.getClass().getMethod( "getNativeHandle" );
getNativeHandle.setAccessible(true);
Object nativeHandle = getNativeHandle.invoke(platformWindow);
return new Pointer((Long) nativeHandle);
} catch (Throwable e) {
System.err.println("Error getting Window Pointer");
return null;
}
}
private static class MinimizeHandler implements WinUser.WindowProc, StdCallLibrary.StdCallCallback {
private Pointer mPrevWndProc32;
private Stage stage;
private MinimizeHandler(Stage stage, long oldPtr) {
this.stage = stage;
mPrevWndProc32 = new Pointer(oldPtr);
// Set up an event pump to deliver messages for JavaFX to handle
Thread thread = new Thread() {
@Override
public void run() {
int result;
WinUser.MSG msg = new WinUser.MSG();
while ((result = User32.INSTANCE.GetMessage(msg, null, 0, 0)) != 0) {
if (result == -1) {
System.err.println("error in get message");
break;
}
else {
System.out.println("got message: " + result);
User32.INSTANCE.TranslateMessage(msg);
User32.INSTANCE.DispatchMessage(msg);
}
}
}
};
thread.start();
}
@Override
public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
if (uMsg == WM_ACTIVATEAPP) {
// Window deactivated (wParam == 0)... Here where I got stuck and gave up,
// this is probably not the best event to listen to.. the app
// does indeed get iconified now by pressing the task-bar button, but it
// instantly restores afterwards..
if (wParam.intValue() == 0) {
stage.setIconified(true);
}
return new WinDef.LRESULT(0);
}
// Let JavaFX handle other events
return ExtUser32.INSTANCE.CallWindowProcW(
mPrevWndProc32,
hWnd.getPointer(),
uMsg,
wParam,
lParam);
}
}
}
Ответы
Ответ 1
Вы можете просто установить соответствующий стиль окна. Он работает в XP, но должен быть в порядке в Windows 7 32 бит.
Я думаю (но не могу проверить), если вы используете 64 бит, а затем меняете на функции окна Ptr, т.е. GetWindowLongPtr.
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinUser;
import static com.sun.jna.platform.win32.WinUser.GWL_STYLE;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class JNATest extends Application {
public static void main(String[] args) { launch(args); }
@Override
public void start(Stage stage) {
TextArea ta = new TextArea("output\n");
VBox root = new VBox(5,ta);
Scene scene = new Scene(root,800,200);
stage.setTitle("Find this window");
stage.setScene(scene);
stage.show();
//gets this window (stage)
long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow();
Pointer lpVoid = new Pointer(lhwnd);
//gets the foreground (focused) window
final User32 user32 = User32.INSTANCE;
char[] windowText = new char[512];
HWND hwnd = user32.GetForegroundWindow();
//see what the title is
user32.GetWindowText(hwnd, windowText, 512);
//user32.GetWindowText(new HWND(lpVoid), windowText, 512);//to use the hwnd from stage
String text=(Native.toString(windowText));
//see if it the same pointer
ta.appendText("HWND java:" + lpVoid + " HWND user32:"+hwnd+" text:"+text+"\n");
//change the window style if it the right title
if (text.equals(stage.getTitle())){
//the style to change
int WS_DLGFRAME = 0x00400000;//s/b long I think
//not the same constant here??
ta.appendText("windows api:"+WS_DLGFRAME+" JNA: "+WinUser.SM_CXDLGFRAME);
int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE);
int newStyle = oldStyle & ~0x00400000; //bitwise not WS_DLGFRAME means remove the style
newStyle = newStyle & ~0x00040000;//WS_THICKFRAME
user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
}
}
}
Мое предположение: вы заменяете последние 3 строки на
long oldStyleLong = user32.GetWindowLongPtr(hwnd, GWL_STYLE).longValue();
long newStyleLong = oldStyleLong & ~ 0x00400000l;
user32.SetWindowLongPtr(hwnd, GWL_STYLE, new BaseTSD.LONG_PTR(newStyleLong));
для 64 бит. Я думаю, что у меня нет этих функций в моей User32.dll, поэтому я не могу его протестировать. Там много постороннего кода, в основном для тестирования или обучения. Удалите неиспользуемые строки, когда вы выясните, что вы хотите сделать.
пс. Не добавляйте newStyle = newStyle & ~0x00020000;//WS_MINIMIZEBOX
. Этот один из флагов стиля JavaFX не используется для unecorated. Поэтому минимизация недоступна. Может быть, если вы попытаетесь настроить этап без декодирования и добавить (используя |, не & ~) флаг флажка минимизации, вы получите тот же результат. Существуют инструменты для поиска всех флажков стиля из любого окна.
Здесь простейший код, который просто изменяет незадекларированный этап с использованием этапа HWND.
public void start(Stage stage) {
Scene scene = new Scene(new Pane(new Label("Hello World")));
stage.initStyle(StageStyle.UNDECORATED);
stage.setTitle("Find this window");
stage.setScene(scene);
stage.show();
long lhwnd = com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow();
Pointer lpVoid = new Pointer(lhwnd);
HWND hwnd = new HWND(lpVoid);
final User32 user32 = User32.INSTANCE;
int oldStyle = user32.GetWindowLong(hwnd, GWL_STYLE);
System.out.println(Integer.toBinaryString(oldStyle));
int newStyle = oldStyle | 0x00020000;//WS_MINIMIZEBOX
System.out.println(Integer.toBinaryString(newStyle));
user32.SetWindowLong(hwnd, GWL_STYLE, newStyle);
}
Он печатает флаги стиля до и после, поэтому вы можете посмотреть, какие стили установлены.
Ответ 2
Не получил ваш вопрос правильно... но здесь решение
@FXML private void minimize()
{
Stage stage = (Stage) minimize.getScene().getWindow();
stage.setIconified(true);
}