JFormDesigner / FlatLaf

FlatLaf - Swing Look and Feel (with Darcula/IntelliJ themes support)
https://www.formdev.com/flatlaf/
Apache License 2.0
3.45k stars 274 forks source link

Support window decorations on linux #482

Closed rkeen-siemens closed 2 years ago

rkeen-siemens commented 2 years ago

The window decorations (including the embedded menu bar and title bar options) would be great to have on linux as well as Windows.

matthiasblaesing commented 2 years ago

As I'm using NetBeans at work on Windows with menu integrated into title bar, I began to wonder how difficult it would be. As a quick experiment I came up with this:

With that I get this window on Ubuntu Linux:

image

Window interactions work as expected, it does not match the configure GTK Theme, but that is to be expected by client side decorations. I don't understand why getSupportsWindowDecorations is so complicated, as the theme obviously can handle window decoration quite fine. Even more window decorations from LAF are only used if switched on by asking JFrame to actually use the support.

The biggest headache will probably be enabling the dynamic switch between OS window decorations and LAF window decorations as the JFrame does not "know" how it was created.

@DevCharly what do you think?

Patch with changes as described (please expand) ```diff diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java index 4c92ac36..4d6d74a1 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java @@ -183,14 +183,15 @@ public abstract class FlatLaf */ @Override public boolean getSupportsWindowDecorations() { - if( SystemInfo.isProjector || SystemInfo.isWebswing || SystemInfo.isWinPE ) - return false; - - if( SystemInfo.isWindows_10_orLater && - FlatNativeWindowBorder.isSupported() ) - return false; - - return SystemInfo.isWindows_10_orLater; +// if( SystemInfo.isProjector || SystemInfo.isWebswing || SystemInfo.isWinPE ) +// return false; +// +// if( SystemInfo.isWindows_10_orLater && +// FlatNativeWindowBorder.isSupported() ) +// return false; +// +// return SystemInfo.isWindows_10_orLater; + return true; } @Override diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java index 678ed225..adfe174e 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java @@ -889,7 +889,7 @@ class DemoFrame copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() ); pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() ); - if( FlatLaf.supportsNativeWindowDecorations() ) { + if( true || FlatLaf.supportsNativeWindowDecorations() ) { windowDecorationsCheckBoxMenuItem.setSelected( FlatLaf.isUseNativeWindowDecorations() ); menuBarEmbeddedCheckBoxMenuItem.setSelected( UIManager.getBoolean( "TitlePane.menuBarEmbedded" ) ); unifiedTitleBarMenuItem.setSelected( UIManager.getBoolean( "TitlePane.unifiedBackground" ) ); diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java index 8073ede9..116d314b 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java @@ -22,6 +22,7 @@ import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.extras.FlatInspector; import com.formdev.flatlaf.extras.FlatUIDefaultsInspector; import com.formdev.flatlaf.util.SystemInfo; +import javax.swing.JFrame; /** * @author Karl Tauber @@ -34,6 +35,7 @@ public class FlatLafDemo static boolean screenshotsMode = Boolean.parseBoolean( System.getProperty( "flatlaf.demo.screenshotsMode" ) ); public static void main( String[] args ) { + JFrame.setDefaultLookAndFeelDecorated(true); // macOS if( SystemInfo.isMacOS ) { // enable screen menu bar ```
rkeen-siemens commented 2 years ago

Thanks, @matthiasblaesing. This looks great and seems to work well with the minor exception (in Gnome 3 on CentOS at least) that dragging to maximize/restore and snap to other windows and the edge of the screen does not seem to work. It would be nice to support the standard system interactions like these (including similar ones with other window managers). Other methods to maximize and snap (double click, max/min/restore buttons, keyboard) do seem to work, so it's still quite usable.

DevCharly commented 2 years ago

Tried this on Linux and it works better than expected. The windows even have shadows 😄 Window resizing had some problems and unified window title bar did not work correctly, but these issues are now fixed.

I've changed FlatLaf.getSupportsWindowDecorations() to allow custom window decorations on Linux.

To enable them in your app use e.g.:

if( SystemInfo.isLinux ) {
    // enable custom window decorations
    JFrame.setDefaultLookAndFeelDecorated( true );
    JDialog.setDefaultLookAndFeelDecorated( true );
}

...with the minor exception (in Gnome 3 on CentOS at least) that dragging to maximize/restore and snap to other windows and the edge of the screen does not seem to work.

That's the downside of enabling custom window decorations.

It would be nice to support the standard system interactions like these (including similar ones with other window managers).

On Windows 10/11, there was the same problem and the only way to solve it was to implement some C++ code that interacts directly with Windows API, which was complex and time consuming.

Similar would be necessary for Linux using X11 API. Currently, I don't have the time to do this.

Also there is OpenJDK Project Wakefield, which will support Wayland display server in the future. So it would be a waste of time to implement something now for X11, and later throw it away and implement same for Wayland...

rkeen-siemens commented 2 years ago

@DevCharly, thanks for the quick update. When I entered this issue I was expecting an explanation of why this would be too difficult to implement, but I'm pleasantly surprised.

rkeen-siemens commented 2 years ago

Siemens would like to see the drag and snap behavior implemented using the X11 API and would be willing to pay for its development (subject to timing and cost). @DevCharly (or other interested parties), do you have an estimate on when this could be released and what would be a reasonable amount to pay for the development and testing? If there's a better forum for this type of request, I'd be happy to repost this request there.

matthiasblaesing commented 2 years ago

If someone want to have a look at this. This is what I tried (feel free to use if it leads to improvements in FlatLaf):

  1. I mapped XUngrabPointer like this:
    interface X11Ext extends X11 {
    X11Ext INSTANCE = Native.load("X11", X11Ext.class);

    int XUngrabPointer(Display display, NativeLong time);
    }
  1. I managed to start a keyboard move like this:
    private static void move_resize(int x, int y) throws HeadlessException {
    System.out.printf("%d x %d%n", x, y);
    Display display = X11Ext.INSTANCE.XOpenDisplay(null);
    Window w = new X11Ext.Window(windowsId);
    X11Ext.XEvent event = new X11Ext.XEvent();
    event.type = X11Ext.ClientMessage;
    event.setType(XClientMessageEvent.class);
    event.xclient.type = X11Ext.ClientMessage;
    event.xclient.serial = new NativeLong(0);
    event.xclient.send_event = 1;
    event.xclient.message_type = X11Ext.INSTANCE.XInternAtom(display, "_NET_WM_MOVERESIZE", false);
    event.xclient.display = display;
    event.xclient.window = w;
    event.xclient.format = 32;
    event.xclient.data.setType(NativeLong[].class);
    event.xclient.data.l[0] = new NativeLong(x);
    event.xclient.data.l[1] = new NativeLong(y);
    event.xclient.data.l[2] = new NativeLong(10);
    event.xclient.data.l[3] = new NativeLong(0);
    event.xclient.data.l[4] = new NativeLong(0);
    System.out.println(X11Ext.INSTANCE.XUngrabPointer(display, new NativeLong(0)));
    System.out.println(X11Ext.INSTANCE.XUngrabKeyboard(display, new NativeLong(0)));
    System.out.println(X11Ext.INSTANCE.XSendEvent(display, X11Ext.INSTANCE.XDefaultRootWindow(display), 0, new NativeLong(SubstructureNotifyMask|X11.SubstructureRedirectMask), event));
    System.out.println(X11Ext.INSTANCE.XFlush(display));
    System.out.println(X11Ext.INSTANCE.XCloseDisplay(display));
    System.out.println("Done");
    }

Theoretically changing the third data element to 8 should make this a mouse move, that did not work for me though.

DevCharly commented 2 years ago

@rkeen-siemens thanks for the offer. I'm willing to work on it. Please contact me via Email: https://www.formdev.com/company/

Is the "drag and snap behavior" the only functionality that you to want? I've compared native windows with FlatLaf decorated windows and there are some more differences:

@matthiasblaesing many thanks for the code. I tried it. Works for me even using 8 (using mouse screen coordinates for x and y). Found out that _NET_WM_MOVERESIZE message can be also used for resizing to get snapping when holding down Shift key. https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html#idm45381394031168

BTW is this API available on all Linux distros (that support X11)?

Any idea how to show that window popup menu via API?

image

Since the above code uses JNA and depending FlatLaf library on JNA is not an option (like to keep it dependency free), I want to translate the code into C/C++ and build a small .so library that is shipped inside of flatlaf.jar (similar to Windows DLLs inside of flatlaf.jar).

matthiasblaesing commented 2 years ago

@DevCharly to my understanding implementors should check the atom list returned by querying the property _NET_SUPPORTED of the root window.

I'm not sure if going though direct X11 calls is the preferred way or if it would be better to use gdk. If I remember correctly, both OpenJFX and Swing/AWT are both build on top of gtk and gtk has support for wayland and X11. The drawback is, that you'd have to check if the function is called identically between the supported gtk versions. A runtime lookup of the function symbol might work correctly, if the function call names differ.

For the popup - sorry, no idea.

As reference this is the GDK function I referenced: https://docs.gtk.org/gdk3/method.Window.begin_move_drag.html And yes, gtk4 changes this: https://docs.gtk.org/gdk4/method.Toplevel.begin_move.html

rkeen-siemens commented 2 years ago

@DevCharly, thanks for the offer. I'm assuming we will want the other behaviors as well. I'll contact you via email in the next couple days with more details.

medmedin2014 commented 2 years ago

I tested it on Manjaro KDE with X11 and it looks okay:

Screenshot_20220318_005332

The problems listed by @DevCharly are all present on KDE Plasma, and the additional one is that window shadow disappeared.

rkeen-siemens commented 2 years ago

@DevCharly, I sent you an email last week with details on my earlier request for updates. Perhaps you have been busy, but I'm wondering if the message was lost in transit. I'll post my request here as well in case you didn't receive it or others are interested in the work.

Summary

Siemens would like to see the drag and snap behavior implemented when window decorations are enabled in Linux.

Already Available

The following are already available and should be maintained:

Scope of Work

The following should be available as they are currently for native windows (i.e. with window decorations disabled and without the need to hold down a modifier key such as Super before performing the action).

Not Required

Assumptions

This assumes support is not OS or desktop environment specific (i.e. it should work in GNOME 3 on CentOS and KDE Plasma on Ubuntu). If there are any known issues in the existing implementation or new scope that are platform specific, please provide details of the limitations.

Timing and Delivery

We'd like to have this available in a publicly released version of FlatLaf no later than 30 June 2022. We are happy to test with pre-release versions or build from a branch if needed.

Ideally, this would be available in the version bundled in NetBeans 14 at the end of May. If that is not possible, we have our own fork of the NetBeans platform so as long as you can provide instructions for us to update it there that would be acceptable as well.

PicoMitchell commented 2 years ago

These custom window decorations on Linux are awesome! Thank you!

But I noticed I'm not getting any window shadows when running on Mint 20.3. Not sure what's different than the other distros where the window shadows work.

DevCharly commented 2 years ago

Finally, a PR is now available to improve FlatLaf window decorations on Linux: #579 😄