Skip to content

Commit

Permalink
Merge PR #779: macOS: window title bar close/minimize/zoom buttons sp…
Browse files Browse the repository at this point in the history
…acing
  • Loading branch information
DevCharly committed Jan 25, 2024
2 parents cf44a5c + 3465fa6 commit ca514dd
Show file tree
Hide file tree
Showing 16 changed files with 1,488 additions and 107 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ FlatLaf Change Log

#### New features and improvements

- macOS: Support larger window title bar close/minimize/zoom buttons spacing in
[full window content](https://www.formdev.com/flatlaf/macos/#full_window_content)
mode and introduced "buttons placeholder". (PR #779)
- Native libraries: System property `flatlaf.nativeLibraryPath` now supports
loading native libraries named the same as on Maven central. Improved log
messages for loading fails.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,80 @@ public interface FlatClientProperties
String COMPONENT_TITLE_BAR_CAPTION = "JComponent.titleBarCaption";


//---- Panel --------------------------------------------------------------

/**
* Marks the panel as placeholder for the iconfify/maximize/close buttons
* in fullWindowContent mode.
* <p>
* If fullWindowContent mode is enabled, the preferred size of the panel is equal
* to the size of the iconfify/maximize/close buttons. Otherwise is is {@code 0,0}.
* <p>
* You're responsible to layout that panel at the top-left or top-right corner,
* depending on platform, where the iconfify/maximize/close buttons are located.
* <p>
* Syntax of the value string is: {@code "win|mac [horizontal|vertical] [zeroInFullScreen] [leftToRight|rightToLeft]"}.
* <p>
* The string must start with {@code "win"} (for Windows or Linux) or
* with {@code "mac"} (for macOS) and specifies the platform where the placeholder
* should be used. On macOS, you need the placeholder in the top-left corner,
* but on Windows/Linux you need it in the top-right corner. So if your application supports
* fullWindowContent mode on both platforms, you can add two placeholders to your layout
* and FlatLaf automatically uses only one of them. The other gets size {@code 0,0}.
* <p>
* Optionally, you can append following options to the value string (separated by space characters):
* <ul>
* <li>{@code "horizontal"} - preferred height is zero
* <li>{@code "vertical"} - preferred width is zero
* <li>{@code "zeroInFullScreen"} - in full-screen mode on macOS, preferred size is {@code 0,0}
* <li>{@code "leftToRight"} - in right-to-left component orientation, preferred size is {@code 0,0}
* <li>{@code "rightToLeft"} - in left-to-right component orientation, preferred size is {@code 0,0}
* </ul>
*
* Example for adding placeholder to top-left corner on macOS:
* <pre>{@code
* JPanel placeholder = new JPanel();
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
*
* 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 );
* }</pre>
*
* Or add placeholder as first item to the tool bar:
* <pre>{@code
* JPanel placeholder = new JPanel();
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
*
* JToolBar toolBar = new JToolBar();
* toolBar.add( placeholder );
* // add tool bar items
*
* frame.getContentPane().add( toolBar, BorderLayout.NORTH );
* }</pre>
*
* If a tabbed pane is located at the top, you can add the placeholder
* as leading component to that tabbed pane:
* <pre>{@code
* JPanel placeholder = new JPanel();
* placeholder.putClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER, "mac" );
*
* tabbedPane.putClientProperty( FlatClientProperties.TABBED_PANE_LEADING_COMPONENT, placeholder );
* }</pre>
* <p>
* <strong>Component</strong> {@link javax.swing.JPanel}<br>
* <strong>Value type</strong> {@link java.lang.String}
*
* @since 3.4
*/
String FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER = "FlatLaf.fullWindowContent.buttonsPlaceholder";


//---- Popup --------------------------------------------------------------

/**
Expand Down Expand Up @@ -388,6 +462,20 @@ public interface FlatClientProperties
*/
String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded";

/**
* Contains the current bounds of the iconfify/maximize/close buttons
* (in root pane coordinates) if fullWindowContent mode is enabled.
* Otherwise its value is {@code null}.
* <p>
* <b>Note</b>: Do not set this client property. It is set by FlatLaf.
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.awt.Rectangle}
*
* @since 3.4
*/
String FULL_WINDOW_CONTENT_BUTTONS_BOUNDS = "FlatLaf.fullWindowContent.buttonsBounds";

/**
* Specifies whether the window icon should be shown in the window title bar
* (requires enabled window decorations). Default is UI property {@code TitlePane.showIcon}.
Expand Down Expand Up @@ -1263,6 +1351,44 @@ public interface FlatClientProperties
String TREE_PAINT_SELECTION = "JTree.paintSelection";


//---- macOS --------------------------------------------------------------

/**
* Specifies the spacing around the macOS window close/minimize/zoom buttons.
* Useful if <a href="https://www.formdev.com/flatlaf/macos/#full_window_content">full window content</a>
* is enabled.
* <p>
* (requires macOS 10.14+ for "medium" spacing and macOS 11+ for "large" spacing, requires Java 17+)
* <p>
* <strong>Component</strong> {@link javax.swing.JRootPane}<br>
* <strong>Value type</strong> {@link java.lang.String}<br>
* <strong>Allowed Values</strong>
* {@link #MACOS_WINDOW_BUTTONS_SPACING_MEDIUM} or
* {@link #MACOS_WINDOW_BUTTONS_SPACING_LARGE} (requires macOS 11+)
*
* @since 3.4
*/
String MACOS_WINDOW_BUTTONS_SPACING = "FlatLaf.macOS.windowButtonsSpacing";

/**
* Add medium spacing around the macOS window close/minimize/zoom buttons.
*
* @see #MACOS_WINDOW_BUTTONS_SPACING
* @since 3.4
*/
String MACOS_WINDOW_BUTTONS_SPACING_MEDIUM = "medium";

/**
* Add large spacing around the macOS window close/minimize/zoom buttons.
* <p>
* (requires macOS 11+; "medium" is used on older systems)
*
* @see #MACOS_WINDOW_BUTTONS_SPACING
* @since 3.4
*/
String MACOS_WINDOW_BUTTONS_SPACING_LARGE = "large";


//---- helper methods -----------------------------------------------------

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

package com.formdev.flatlaf.ui;

import java.awt.Rectangle;
import java.awt.Window;
import com.formdev.flatlaf.util.SystemInfo;

/**
* Native methods for macOS.
Expand Down Expand Up @@ -49,8 +51,19 @@ public class FlatNativeMacLibrary
* method of this class. Otherwise, the native library may not be loaded.
*/
public static boolean isLoaded() {
return FlatNativeLibrary.isLoaded();
return SystemInfo.isMacOS && FlatNativeLibrary.isLoaded();
}

public native static boolean setWindowRoundedBorder( Window window, float radius, float borderWidth, int borderColor );

/** @since 3.4 */
public static final int
BUTTONS_SPACING_DEFAULT = 0,
BUTTONS_SPACING_MEDIUM = 1,
BUTTONS_SPACING_LARGE = 2;

/** @since 3.4 */ public native static boolean setWindowButtonsSpacing( Window window, int buttonsSpacing );
/** @since 3.4 */ public native static Rectangle getWindowButtonsBounds( Window window );
/** @since 3.4 */ public native static boolean isWindowFullScreen( Window window );
/** @since 3.4 */ public native static boolean toggleWindowFullScreen( Window window );
}
40 changes: 40 additions & 0 deletions flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPanelUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

package com.formdev.flatlaf.ui;

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.LookAndFeel;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPanelUI;
import com.formdev.flatlaf.FlatClientProperties;
Expand Down Expand Up @@ -69,6 +71,8 @@ public void installUI( JComponent c ) {
super.installUI( c );

c.addPropertyChangeListener( this );
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
FullWindowContentSupport.registerPlaceholder( c );

installStyle( (JPanel) c );
}
Expand All @@ -78,10 +82,20 @@ public void uninstallUI( JComponent c ) {
super.uninstallUI( c );

c.removePropertyChangeListener( this );
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
FullWindowContentSupport.unregisterPlaceholder( c );

oldStyleValues = null;
}

@Override
protected void installDefaults( JPanel p ) {
super.installDefaults( p );

if( p.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
LookAndFeel.installProperty( p, "opaque", false );
}

/** @since 2.0.1 */
@Override
public void propertyChange( PropertyChangeEvent e ) {
Expand All @@ -98,6 +112,17 @@ public void propertyChange( PropertyChangeEvent e ) {
c.revalidate();
c.repaint();
break;

case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER:
JPanel p = (JPanel) e.getSource();
if( e.getOldValue() != null )
FullWindowContentSupport.unregisterPlaceholder( p );
if( e.getNewValue() != null )
FullWindowContentSupport.registerPlaceholder( p );

// make panel non-opaque for placeholders
LookAndFeel.installProperty( p, "opaque", e.getNewValue() == null );
break;
}
}

Expand Down Expand Up @@ -162,4 +187,19 @@ public void update( Graphics g, JComponent c ) {

paint( g, c );
}

@Override
public Dimension getPreferredSize( JComponent c ) {
Object value = c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER );
if( value != null )
return FullWindowContentSupport.getPlaceholderPreferredSize( c, (String) value );

return super.getPreferredSize( c );
}

@Override
public void paint( Graphics g, JComponent c ) {
if( c.getClientProperty( FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_PLACEHOLDER ) != null )
FullWindowContentSupport.debugPaint( g, c );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public class FlatRootPaneUI
private LayoutManager oldLayout;
private PropertyChangeListener ancestorListener;
private ComponentListener componentListener;
private ComponentListener macFullWindowContentListener;

public static ComponentUI createUI( JComponent c ) {
return new FlatRootPaneUI();
Expand Down Expand Up @@ -207,6 +208,9 @@ public void componentShown( ComponentEvent e ) {
};
root.addPropertyChangeListener( "ancestor", ancestorListener );
}

if( SystemInfo.isMacFullWindowContentSupported )
macFullWindowContentListener = FullWindowContentSupport.macInstallListeners( root );
}

@Override
Expand All @@ -223,6 +227,11 @@ protected void uninstallListeners( JRootPane root ) {
root.removePropertyChangeListener( "ancestor", ancestorListener );
ancestorListener = null;
}

if( SystemInfo.isMacFullWindowContentSupported ) {
FullWindowContentSupport.macUninstallListeners( root, macFullWindowContentListener );
macFullWindowContentListener = null;
}
}

/** @since 1.1.2 */
Expand Down Expand Up @@ -359,6 +368,10 @@ public void propertyChange( PropertyChangeEvent e ) {
titlePane.titleBarColorsChanged();
break;

case FlatClientProperties.FULL_WINDOW_CONTENT_BUTTONS_BOUNDS:
FullWindowContentSupport.revalidatePlaceholders( rootPane );
break;

case FlatClientProperties.GLASS_PANE_FULL_HEIGHT:
rootPane.revalidate();
break;
Expand All @@ -367,6 +380,50 @@ public void propertyChange( PropertyChangeEvent e ) {
if( rootPane.isDisplayable() )
throw new IllegalComponentStateException( "The client property 'Window.style' must be set before the window becomes displayable." );
break;

case "ancestor":
// FlatNativeMacLibrary.setWindowButtonsSpacing() and
// FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty()
// require a native window, but setting the client properties
// "apple.awt.fullWindowContent" or FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING
// is usually done before the native window is created
// --> try again when native window is created
if( !SystemInfo.isMacOS || e.getNewValue() == null )
break;

// fall through

case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING:
if( SystemInfo.isMacOS ) {
// set window buttons spacing
if( SystemInfo.isJava_17_orLater && rootPane.isDisplayable() && FlatNativeMacLibrary.isLoaded() ) {
int buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_DEFAULT;
String value = (String) rootPane.getClientProperty( FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING );
if( value != null ) {
switch( value ) {
case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_MEDIUM:
buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_MEDIUM;
break;

case FlatClientProperties.MACOS_WINDOW_BUTTONS_SPACING_LARGE:
buttonsSpacing = FlatNativeMacLibrary.BUTTONS_SPACING_LARGE;
break;
}
}

Window window = SwingUtilities.windowForComponent( rootPane );
FlatNativeMacLibrary.setWindowButtonsSpacing( window, buttonsSpacing );
}

// update buttons bounds client property
FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty( rootPane );
}
break;

case "apple.awt.fullWindowContent":
if( SystemInfo.isMacOS )
FullWindowContentSupport.macUpdateFullWindowContentButtonsBoundsProperty( rootPane );
break;
}
}

Expand Down
Loading

0 comments on commit ca514dd

Please sign in to comment.