Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macOS: window title bar close/minimize/zoom buttons spacing #779

Merged
merged 10 commits into from
Jan 25, 2024

Conversation

DevCharly
Copy link
Collaborator

@DevCharly DevCharly commented Dec 14, 2023

If using full window content mode on macOS, this PR enables using larger window title bar, which adds more empty space around the close/minimize/zoom buttons (traffic lights). (issues #769 and #562)

This feature requires Java 17+ and macOS 10.14+.

Default:

image

Medium:

image

Large (requires macOS 11+):

image

Note that the traffic lights have moved, the "main" toolbar has larger left insets and larger height, which horizontally aligns traffic lights and toolbar buttons.

Besides enabling full window content, transparent title bar and hide window title, it is also necessary to set client property FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING (or "FlatLaf.macOS.windowButtonsSpacing") to:

  • FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_LARGE or "large": recommended since native macOS apps like Finder also use this style
  • FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_MEDIUM or "medium"

E.g.:

if( SystemInfo.isMacFullWindowContentSupported ) {
    JRootPane rootPane = frame.getRootPane();
    rootPane.putClientProperty( "apple.awt.fullWindowContent", true );
    rootPane.putClientProperty( "apple.awt.transparentTitleBar", true );
    rootPane.putClientProperty( "apple.awt.windowTitleVisible", false );
    rootPane.putClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING,
                                FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_LARGE );
}

Update 2024-01-22

  • renamed client property MACOS_WINDOW_BUTTON_STYLE to MACOS_WINDOW_BUTTONS_SPACING
  • no longer allow value true for that client property
  • added title bar buttons placeholder
  • added client property FULL_WINDOW_CONTENT_BUTTONS_BOUNDS to root pane that contains title bar buttons bounds
  • undone toolbar extensions from commit ea2447d

Buttons placeholder

To avoid that your components are overlapped by the close/minimize/zoom buttons, you need to add some placeholder component to the layout of your application. For macOS in the top-left corner. A buttons placeholder is a JPanel that has client property FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER set.

Note that FlatLaf controls the preferred size of the placeholder panel. If fullWindowContent mode is enabled, it gets the size of the buttons area. Otherwise, the preferred size is 0, 0, which hides the placeholder.

JPanel placeholder = new JPanel();
placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );

If you have a toolbar at top of your frame, use an additional panel that contains placeholder at WEST and toolbar in CENTER:

JToolBar toolBar = new JToolBar();
// add tool bar items

JPanel toolBarPanel = new JPanel( new BorderLayout() );
toolBarPanel.add( placeholder, BorderLayout.WEST );
toolBarPanel.add( toolBar, BorderLayout.CENTER );

frame.getContentPane().add( toolBarPanel, BorderLayout.NORTH );

See FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER javadoc for details.

For testing, you can make the placeholder "visible" with:

UIManager.put( "FlatLaf.debug.panel.showPlaceholders", true );

image

"main" toolbar

removed in commit 28278a7; use buttons placeholder instead

If there is a JToolBar at the top of the window, at location 0, 0, then FlatLaf automatically increases the left inset (to avoid overlapping with traffic lights) and the preferred toolbar height (for horizontal alignment) of that toolbar.

Otherwise you have to take care yourself to avoid overlapping with traffic lights.

Feedback wanted

I've tested this only on macOS 10.14 (Intel) and on 13.3 (M1).

Would be great to get some feedback whether this works without problems on other macOS versions.

I'm also interested whether macOS Gatekeeper makes problems, because there are two new native libraries (dylib), which are extracted from flatlaf.jar to a temporary directory and loaded from there. The dylibs are signed with "FormDev Software" code signing certificate, but this is not (yet) an Apple certificate. So if you ship a signed application, it may be possible that Gatekeeper pops up. (I don't know yet).

@ctipper
Copy link

ctipper commented Dec 15, 2023

Tested macOS 14.2 JDK 17 no problems though not able to comment on signing question. I am looking for something similar to putClientProperty "apple.awt.windowTransparentTitleBarHeight" (a property now removed taken from JetBrainsRuntime

image

@ctipper
Copy link

ctipper commented Dec 16, 2023

I've tested latest changes, excellent with large size. I wasn't using a JToolBar, when I do, JToggleButtons have same background as the panel and I can find no property related to this.

@DevCharly
Copy link
Collaborator Author

Good news. Found a way to make the space around the traffic lights even larger 😄
They now have the same spacing as used in native macOS apps like Finder.

image

Updated main post. Note that client property has changed.

@ctipper something like JetBrains "apple.awt.windowTransparentTitleBarHeight" is only possible with a lot of native code that hacks the layout of the traffic light buttons. This is what JetBrains does. No plans to do the same in FlatLaf.

@ctipper
Copy link

ctipper commented Dec 16, 2023

I was testing full screen behaviour I noticed a couple of things.

  • there is a disconcerting traffic light jump when exiting full screen
  • the default inset would be nice in full screen

Thinking about it I would like to handle the left inset programmatically, it is nice for setting this up, but it's just as easy to add a horizontal strut and this would work in full screen also

@ctipper
Copy link

ctipper commented Dec 16, 2023

I realised the macOS popup PR was included in the latest build, that's pretty cool too. All my reasons to use JetBrainsRuntime have now been met, and I don't have to use it, the next release of this library looks like it's going to be good.

@remcopoelstra
Copy link

This is looking very good! I just tested on a macbook air M1, MacOS 13.4.1, OpenJDK 19.0.1 (Adoptium) and the traffic lights and rounded popups are working great.

It's already mentioned that the traffic lights can be seen moving when exiting full screen, for me this is totally acceptable, what I did in my own experiments was hide them during the transition, I also tried to postpone the update of the toolbar margins after the transition but I could net get this to work reliable, but when it did work this was the best looking workaround in my opinion.

I did not get any gatekeeper warnings that I did not expect, just the normal warnings about unknown developer.

I noticed that my default JDK on this laptop is still a jetbrains 17.0.2 runtime, while I was testing the full screen behaviour in the FlatLaf demo app I noticed that if I click the theme combobox in the upper right corner after this the main window content is no longer resized when going to full screen, this was also happening without the new MacOS native features so I am pretty sure it is unrelated, but I thought you might like to know.

Next weekend I will also be able to test on an older Intel macbook.

@DevCharly DevCharly linked an issue Dec 21, 2023 that may be closed by this pull request
@ghost
Copy link

ghost commented Dec 23, 2023

M1 macOS 14.2 has no effect
image

@DevCharly
Copy link
Collaborator Author

@duhiqc you need to setup FlatLaf. E.g. using FlatLightLaf.setup()

@ghost
Copy link

ghost commented Dec 23, 2023

@duhiqc您需要设置FlatLaf。例如,使用FlatLightLaf.setup()上传16ec2d54a7d195c988f41414beddf31f.png...
Still won't work

@JFormDesigner
Copy link
Owner

Have you setup FlatLaf before creating the frame?

@ghost
Copy link

ghost commented Dec 23, 2023

Have you setup FlatLaf before creating the frame?

yes

@DevCharly
Copy link
Collaborator Author

Code?

@ghost
Copy link

ghost commented Dec 23, 2023

package org.test;

import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.FlatLightLaf;

import javax.swing.*;

public class Main extends JFrame {
    public Main() {
        FlatLightLaf.setup();
        rootPane.putClientProperty("apple.awt.fullWindowContent", true);
        rootPane.putClientProperty("apple.awt.transparentTitleBar", true);
        rootPane.putClientProperty(FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE_LARGE, true);
        setSize(300, 300);
        this.setVisible(true);
    }

    public static void main(String[] args) {
        new Main();
    }
}

@DevCharly
Copy link
Collaborator Author

You're setting up FlatLaf after creating the frame!

@ghost
Copy link

ghost commented Dec 23, 2023

image

@DevCharly
Copy link
Collaborator Author

You're still setting up FlatLaf after creating the frame!

(hint class Main extends JFrame)

@ghost
Copy link

ghost commented Dec 23, 2023

Wait, just got it wrong, But FlatLightLaf.setup();
No matter where you put it, it won't work

    public static void main(String[] args) {
        FlatLightLaf.setup();

        JFrame frame = new JFrame();
        frame.getRootPane().putClientProperty( "apple.awt.fullWindowContent", true );
        frame.getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true );
        frame.getRootPane().putClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE_LARGE, true );
        frame.setVisible(true);

    }

@ctipper
Copy link

ctipper commented Dec 23, 2023

I am curious I have always installed look and feel before calling setup, as in

UIManager.installLookAndFeel("FlatLightLaf", "com.formdev.flatlaf.FlatLightLaf");
lookAndFeel = "com.formdev.flatlaf.FlatLightLaf";
UIManager.setLookAndFeel(lookAndFeel);`

@DevCharly
Copy link
Collaborator Author

@duhiqc you're setting the wrong client property.

The key of the client property is FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE.
Allowed values are true, FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE_LARGE or FlatClientProperties.MACOS_WINDOW_BUTTON_STYLE_MEDIUM

@ctipper
Copy link

ctipper commented Jan 2, 2024

I've been following JBR and my understanding is that they subsequently removed this property following attempts to fix this issue (my own PR) JBR-4875

@drakeet
Copy link

drakeet commented Jan 3, 2024

I've been following JBR and my understanding is that they subsequently removed this property following attempts to fix this issue (my own PR) JBR-4875

Try this:

JBR.getWindowDecorations().setCustomTitleBar(window, JBR.getWindowDecorations().createCustomTitleBar().also { customTitleBar ->
  customTitleBar.height = topBarHeight // !!!
})

Tip: The way to obtain jbr-api-17.1.7.0-sources.jar: First, build IntelliJ IDEA Community Edition, and then you can find the source code of JBR, which will be in the local maven directory.

@rogerbj
Copy link

rogerbj commented Jan 6, 2024

@DevCharly Looks and works great! Any chance this will be supported for the JIDE CommandBar in addition to JToolBar?

- uses height of macOS window title bar
- adds left insets for close/minimize/zoom buttons (except if full screen, where those buttons are hidden)
…COS_LARGE_WINDOW_TITLE_BAR` (or `"FlatLaf.macOS.largeWindowTitleBar"`)
…ilaible since macOS 11+; standard in macOS Finder, etc) to allow even larger space around close/minimize/zoom buttons
@DevCharly DevCharly force-pushed the macos-large-titlebar branch from d63400c to f68a871 Compare January 12, 2024 22:04
@DevCharly DevCharly marked this pull request as draft January 12, 2024 22:13
- added title bar buttons placeholder
- added client property to root pane that contains title bar buttons bounds
- undone toolbar extensions from commit ea2447d
- renamed client property `MACOS_WINDOW_BUTTON_STYLE` to `MACOS_WINDOW_BUTTONS_SPACING`
- no longer allow value `true` for that client property
- enable using `MACOS_WINDOW_BUTTONS_SPACING` without `apple.awt.fullWindowContent`
- remove client property `FULL_WINDOW_CONTENT_BUTTONS_BOUNDS` when `apple.awt.fullWindowContent` is set to false or null
- added placeholder options `zeroInFullScreen`, `leftToRight` and `rightToLeft`
- hide close/min/max buttons during the transition from full-screen to non-full-screen to avoid that they "jump" when the nsToolbar is made visible
- fixed: full-screen listeners where added multiple times
- updated macOS native libraries
- added `FlatMacOSTest`
@DevCharly DevCharly changed the title macOS: large window title bar macOS: window title bar close/minimize/zoom buttons spacing Jan 21, 2024
@DevCharly
Copy link
Collaborator Author

Thanks for the feedback 👍

There are some (breaking) changes in recent commits. See initial post for updates.

@DevCharly
Copy link
Collaborator Author

Any chance this will be supported for the JIDE CommandBar in addition to JToolBar?

@rogerbj I've removed that "feature" again from toolbar and replaced it with "buttons placeholder". See initial post. This should also work for JIDE CommandBar.

@DevCharly
Copy link
Collaborator Author

there is a disconcerting traffic light jump when exiting full screen

@ctipper fixed

the default inset would be nice in full screen

@ctipper you can do that now with following placeholder:

JPanel placeholder = new JPanel();
placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac zeroInFullScreen" );

@DevCharly
Copy link
Collaborator Author

It's already mentioned that the traffic lights can be seen moving when exiting full screen, for me this is totally acceptable, what I did in my own experiments was hide them during the transition, ...

@remcopoelstra thanks for that idea. That works fine. 👍

@DevCharly
Copy link
Collaborator Author

JBR actually has this API, hopefully it won't conflict with this PR

@drakeet as long as you do not enable both, it should not conflict 😉

@DevCharly DevCharly marked this pull request as ready for review January 22, 2024 00:44
@DevCharly DevCharly added this to the 3.4 milestone Jan 22, 2024
@ctipper
Copy link

ctipper commented Jan 22, 2024

Your latest commits are good, my application works quite well. I don't have any suggestions for the code itself but I don't know whether you should keep the instructions for a placeholder panel, much simpler to use a JPanel on its own and use `add(Box.createHorizontalStrut(90))'. Personally I don't really like what happens whilst cycling to full screen content when the horizontal spacing shrinks to zero, much better in my opinion to have a fixed offset which is easily added by using a horizontal strut.

@DevCharly
Copy link
Collaborator Author

@ctipper

but I don't know whether you should keep the instructions for a placeholder panel, much simpler to use a JPanel on its own and use `add(Box.createHorizontalStrut(90))'.

If you don't like it, don't use it 😉

Personally I don't really like what happens whilst cycling to full screen content when the horizontal spacing shrinks to zero, much better in my opinion to have a fixed offset which is easily added by using a horizontal strut.

But this is how native macOS applications work.
Try Safari, Calendar, Xcode, Firefox, etc.
All of them move the toolbar items to the left edge in full-screen when the traffic lights are hidden...

@ctipper
Copy link

ctipper commented Jan 22, 2024

I take your point but I have my own opinion. But I am going to stick with a fixed offset there's a lot of animation going on.

@DevCharly DevCharly merged commit ca514dd into main Jan 25, 2024
9 checks passed
@DevCharly DevCharly deleted the macos-large-titlebar branch January 25, 2024 16:45
DevCharly added a commit that referenced this pull request Jan 28, 2024
- uninstall when switching from FlatLaf to another Laf
- install when switching from another Laf to FlatLaf

(for PR #779)
@rogerbj
Copy link

rogerbj commented Jan 29, 2024

@DevCharly While trying out the new traffic-light spacing in our app I run into a problem. At first display of the JFrame, the spacing looks perfect but at some point during initialization, the spacing is lost. The height of the placeholder panel is also decreased to its default. I've tried a lot to find out what Swing/AWT calls that restores the spacing and location of the traffic-light icons but fail. As far as I can see in the FlatLaf code, the icons are positioned natively which makes me even more frustrated what in Swing/AWT that effects it. For clarity we're using the JIDE application framework which do not interact with any native code.

2024-01-29_20-29-22 (1)

What I've tried in a test app is to reproduce the exact same scenario. There are buttons in the JToolBar to call various Swing methods (listed below) that potentially could have an effect on the traffic-light icons, but no matter which of these are called, the spacing stays intact.

  • JFrame.pack()
  • JPanel.setVisible(true/false) (that toggles the visibility state of the placeholder panel, still keeping the traffic-light in correct position
  • Frame.setState()
  • Set of these to their opposite boolean values: apple.awt.fullWindowContent, apple.awt.transparentTitleBar, apple.awt.windowTitleVisible
  • Cycling through all options for JRootPane.setWindowDecorationStyle()

None of the above changes the position or size of the traffic-light icons pane.

Would be awesome if you have any ideas or pointers what calls in Swing/AWT that potentially may result that the size of the traffic-light pane is reset.

@DevCharly
Copy link
Collaborator Author

@rogerbj that's strange.

What Java version do you use?

Are you using the latest FlatLaf code from branch main?
I've made some changes after merging this PR.

Have you checked whether FlatLaf itself resets the traffic light spacing?
Maybe there is something wrong in FlatLaf...
To do so set two breakpoints at following lines and check the parameter buttonsSpacing:

FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), buttonsSpacing );

FlatNativeMacLibrary.setWindowButtonsSpacing( getParentWindow( rootPane ), FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT );

@rogerbj
Copy link

rogerbj commented Feb 1, 2024

@DevCharly Problem solved! I updated my fork and now it works perfect. Previously I used one of the snapshot releases. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Move traffic lights on MacOS
6 participants