1

I am trying to use an Undecorated stage in a JavaFX project, stage.initStyle(StageStyle.UNDECORATED);. It is a modular Gradle project. It is also a multi-project build, though because I am working on it in IntelliJ, it might be more appropriate to refer to it as a multi-module build.

I wish to be able to add regular functionality to this Undecorated stage. I have already been able to add the usual minimize, restore, and close buttons, but if a user clicks on the program's icon in the Windows Task Bar, I want it to minimize and restore as well.

enter image description here

I have found code in this older StackOverflow post that might do this, but I have run into an error I can't quite figure out.

brian's (the poster's) Code:

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);
        }
    }

}

The part that causes me the error is the com.sun.glass.ui.Window.getWindows().get(0).getNativeWindow();

The Error:

package com.sun.glass.ui is not visible
(package com.sun.glass.ui is declared in module javafx.graphics, which does not export it to module apple.orange.main)

I don't really understand this error well. Is the sun.glass.ui package declared in javafx.graphics because I tried to use it in my code or does it reside inside the javafx.graphics module but is not able to be accessed? My best guess is that this package belongs to some module I don't know the name of which I need to include as a dependency in Gradle and as a requires in my module-info.java file. This is something I am capable of doing, but my Google searches have turned up very little which explains where this package comes from. The comments under this SO question suggest that such com.sun packages should not be used at all and seem to imply that there is probably some equivalent better used.

Jack J
  • 1,514
  • 3
  • 20
  • 28
  • which java version are you using ? also which java runtime is installed to your dev machine? – AntJavaDev Nov 29 '19 at 03:17
  • I am running Java 11 with JavaFX 11, but I have other versions ready to go if I need them. My environment variables are set to the 11's. – Jack J Nov 29 '19 at 03:30
  • well its worth trying with java 8(oracle VM) but it also might be something similar with the question link you placed. Have you tried switching this to `java.awt.Window.getWindows()` , or you really need the native libs? Also minimize and restore for JavaFX applications works smooth, so i would guess, you have introduced something to the scene which prevents that. – AntJavaDev Nov 29 '19 at 03:37
  • I haven't tried AWT. If AWT is the solution I am willing to try it, but posts [like this one](https://stackoverflow.com/a/11121207/2031150) advise against mixing AWT with JavaFX. The reason for my efforts is that I am working with an Undecorated stage. – Jack J Nov 29 '19 at 03:44
  • yep thats correct , even worse mixing com.sun packages. Thats why its getting more confused, have you tried the javafx one ? `javafx.stage.Window.getWindows()` – AntJavaDev Nov 29 '19 at 03:47
  • I haven't seen javafx.stage.Window.getWindows(). This is my first foray into the area of JNA, so it is a little confusing to see JavaFX code do what [I've read it couldn't do](https://stackoverflow.com/questions/43059987/maximize-process-from-java-from-taskbar). – Jack J Nov 29 '19 at 03:55
  • well the answer there proposes to download the library by yourself, have you done/tried so? – AntJavaDev Nov 29 '19 at 05:12
  • In this context, "module" has nothing to do with Gradle dependencies but rather the module system of Java added in version 9. You're attempting to use a class from a package in the `javafx.graphics` module but said module does not export said package to at least your module. You can override this with the appropriate `--add-exports` VM argument. Keep in mind that relying on private API is fragile as it can change, or even disappear, without notice. – Slaw Nov 29 '19 at 13:39
  • What is the difference between public and private API? Are all com.sun API considered private including Java Native Access? Is anything on Maven Central public? – Jack J Nov 29 '19 at 20:54
  • Are you just experimenting and trying to get the window handle of the native window that is the peer for the JavaFX `Stage`? If yes, then I suggest function [FindWindowA](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-findwindowa) which should exist in class `com.sun.jna.platform.win32.User32`. – Abra Nov 29 '19 at 23:04
  • I don't yet understand the code I posted since I haven't gotten it to work. Is the Windows Taskbar Icon something I need to put an event onto much like a button? If yes, is using JNA the way to get a handle to it. Would FindWindowA just be the equivalent of stage, and if so couldn't I just use JavaFX code to iconify or restore instead of doing anything with FindWindowA? Ideally, I would love to find a way to do something like `icon.setOnMouseClicked{event -> {if(stage.isIconified())stage.setRestore(); else{stage.setIconified(true)}}`. – Jack J Nov 30 '19 at 09:45
  • Within the context of the JDK and JavaFX, every package prefixed with `com.sun`, `sun`, `oracle`, `jdk.internal`, and so on are private API. The JNA library is different in this regard; packages prefixed with `com.sun.jna.internal` are private API. Other libraries typically use the same approach as JNA in that packages with `internal` or `impl` in the name are private API. A surefire way to tell if an API is public is whether or not it's documented in the public documentation (e.g. Javadoc). In a modular world, another way to tell is whether or not the package is exported. – Slaw Nov 30 '19 at 18:11

1 Answers1

1

I encountered this same issue in Windows 10. Thankfully, @Slaw 's suggestion was correct.

You need to go to VM options in your IDE (Run -> Edit Configurations..., in IntelliJ) and add this:

--add-exports
javafx.graphics/com.sun.glass.ui=ALL-UNNAMED

It should work after that.

Zack
  • 330
  • 3
  • 11