草庐IT

JavaFX 最小化未修饰阶段

coder 2023-08-28 原文

我有一个未修饰的 JavaFX 舞台,以及我自己的最小化、最大化和关闭按钮。但不幸的是,与装饰行为相比,在 Windows 7 中单击任务栏图标不会自动最小化舞台。

有没有一种方法可以通过单击任务栏图标来使用纯 Java 代码最小化未修饰的阶段?如果不是,我如何使用 JNA 来做到这一点?

编辑: 好的,我一直在尝试用 JNA 解决这个问题,但是几乎没有完成任何 C/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's 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's 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);
        }
    }
}

最佳答案

您可以设置合适的窗口样式。它在 XP 中工作,但在 windows 7 32 位中应该没问题。 我认为(但无法测试)如果你使用 64 位然后更改为 Ptr Windows 功能,即。 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's the same pointer
        ta.appendText("HWND java:" + lpVoid + " HWND user32:"+hwnd+" text:"+text+"\n");
        //change the window style if it's 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 不用于未修饰的样式标志之一。这就是最小化不可用的原因。也许如果您尝试将舞台设置为未修饰并添加(使用 |,而不是 &~)最小化框标志,您将得到相同的结果。有一些工具可以从任何窗口中查找所有样式标志。

下面是最简单的代码量,仅使用舞台的 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);
    }

它打印出之前和之后的样式标志,以便您可以查看设置了哪些样式。

关于JavaFX 最小化未修饰阶段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26972683/

有关JavaFX 最小化未修饰阶段的更多相关文章

  1. ruby - 获取数组中的值并最小化某个类属性的最优雅的方法是什么? - 2

    假设我有以下类(class):classPersondefinitialize(name,age)@name=name@age=ageenddefget_agereturn@ageendend我有一组Person对象。是否有一种简洁的、类似于Ruby的方法来获取最小(或最大)年龄的人?如何根据它对它们进行排序? 最佳答案 这样做会:people_array.min_by(&:get_age)people_array.max_by(&:get_age)people_array.sort_by(&:get_age)

  2. elasticsearch源码关于TransportSearchAction【阶段三】 - 2

    1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>

  3. ruby - 返回空白页的最小 Capybara/Poltergeist 测试 - 2

    看来我正在回顾SO帖子中采取的步骤:Capybara,PoltergeistandPhantomjsandgivinganemptyresponseinbody.(如果你愿意,可以将其标记为重复,但我包含了一个最小的独立测试用例和版本号。)问题我做错了什么吗?我可以运行另一个可能有助于隔离问题的最小测试吗?文件:pgtest.rbrequire'rubygems'require'capybara'require'capybara/dsl'require'capybara/poltergeist'modulePGTestincludeCapybara::DSLextendselfdeft

  4. ruby - 寻找产品和商店的最佳组合以最小化成本的算法 - 2

    你好,Stackoverflow的人们,我经营一个网站,为用户寻找最便宜的书籍购买地点。这对于单本书来说很容易,但对于多本书来说,有时在一家商店购买一本书而在另一家商店购买另一本书会更便宜。目前我找到了销售用户列表中所有书籍的最便宜的商店,但我想要一个更智能的系统。这里有更多信息:一本书的价格对于一家商店来说是不变的。运费可能会有所不同,具体取决于书籍的数量或书籍的总值(value)。每个商店对象都可以获取一组书籍并返回运费。通常,并非每家书店都出售每一本书。不确定在这里链接到我的站点是否很酷,但它列在我的用户配置文件中。我希望能够找到最便宜的商店和书籍组合。我担心这需要一种蛮力方法-

  5. ruby hash 添加键/值 if 修饰符 - 2

    我正在使用包含用于在我的MailChimpAPI中创建新订阅者的键/值对的ruby​​hash。user_information={'fname'=>'helloworld','mmerge1'=>'ProductX'ifuser.product_name.present?}显然,我收到了syntaxerror,unexpectedmodifier_if的语法错误...我基本上只想根据条件为真添加mmerge1。 最佳答案 你不能在散列初始化block中那样使用if。您必须在初始化哈希后有条件地添加新的键/值:user_inform

  6. ruby - 正确使用 Ruby 语句修饰符 - 2

    我刚开始使用Ruby,当RubyMine建议我更改此代码时,我发现了语句修饰符:if!VALID_DIRECTIONS.include?(direction)raiseArgumentError,"Invaliddirection"end为此:raiseArgumentError,"Invaliddirection"if!VALID_DIRECTIONS.include?(direction)我喜欢它让代码更简洁的方式。但是,我可以看到它乍一看可能会产生误导并造成可读性问题,因为它将效果放在条件之前。话又说回来,也许这只是因为我太习惯了C风格的语言。有没有人因为使用语句修饰符而遇到麻烦

  7. ruby - 在 Xcode 运行脚本构建阶段使用 rvm 强制指定 Ruby - 2

    在Xcode之外,我使用特定版本的Ruby,使用RVM管理多个Ruby安装。Apple的命令行开发工具将Ruby安装在/usr/bin/ruby并且版本为1.8.7。我通过RVM使用1.9.3。有没有办法强制Xcode在运行其运行脚本构建阶段时使用我的1.9.3安装?我已经尝试将Shell路径设置为我的特定Ruby的完整路径,但这似乎没有什么不同,我的意思是我在1.9.3中安装的特定Gems不可用/在Xcode中运行时对脚本可见。如果我在命令行上通过xcodebuild运行我的项目,运行脚本阶段会使用我的特定Ruby,因为它是从我的shell环境中运行的(即使项目文件中的Shell路径

  8. 用于进行线性或非线性最小二乘近似的 Ruby 库? - 2

    是否有Ruby库允许我对一组数据进行线性或非线性最小二乘法逼近。我想做的是:给定一系列[x,y]数据点针对该数据生成线性或非线性最小二乘法近似值库不必弄清楚它是否需要进行线性或非线性近似。库的调用者应该知道他们需要什么类型的回归我不想尝试移植某些C/C++/Java库来获得此功能,因此我希望有一些现有的Ruby库可供我使用。 最佳答案 尝试使用“statsample”gem。您可以使用下面提供的示例执行对数、指数、幂或任何其他转换。我希望这有帮助。require'statsample'#IndependentVariablex_da

  9. ruby-on-rails - 如何在 Rails 3 的表格列中找到最小值 - 2

    嗨,假设我有一个table(ads),其中有一个column(views)观看次数21463如何找到该列中的最小值?有什么简单的方法可以做到这一点?这就是我的@ads=Ad.all@show_this_ad=@ads.min(:views)这给了我一个“参数数量错误(1代表0)错误”@ads=Ad.all@show_this_ad=@ads.minimum(:views)这给了我一个“未定义的方法错误” 最佳答案 Ad.minimum(:views)应该可以您仍然可以添加更多限制,例如:Ad.where(:user_id=>1234

  10. ruby - 有什么办法可以延迟资源的属性解析到 "execute"阶段? - 2

    我有两个LWRP。第一个涉及创建磁盘卷、对其进行格式化并将其安装到虚拟机上,我们将此资源称为cloud_volume。第二个资源(它的作用并不重要)需要新格式化卷的UUID,这是必需的属性,我们将此资源称为foobar。资源cloud_volume和foobar被用在类似下面的配方中。volumes.eachdo|mount_point,volume|cloud_volume"#{mount_point}"dosizevolume['size']labelvolume['label']action[:create,:initialize]endfoobar"#{mount_point}

随机推荐