From 6ee737b314c1afbe6befe98e572882852f780d70 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sat, 9 Jul 2022 10:30:33 +0200 Subject: [PATCH 1/8] Window decorations: small area at top of embedded menu bar to resize window --- CHANGELOG.md | 6 ++++ .../com/formdev/flatlaf/ui/FlatTitlePane.java | 33 +++++++++++-------- .../com/formdev/flatlaf/FlatLaf.properties | 1 + .../dumps/uidefaults/FlatDarkLaf_1.8.0.txt | 1 + .../dumps/uidefaults/FlatLightLaf_1.8.0.txt | 1 + .../dumps/uidefaults/FlatTestLaf_1.8.0.txt | 1 + .../flatlaf/themeeditor/FlatLafUIKeys.txt | 1 + 7 files changed, 31 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0181e076..4fbe39b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ FlatLaf Change Log ## 2.4-SNAPSHOT +#### New features and improvements + +- Native window decorations (Windows 10/11 only): + - There is now a small area at top of the embedded menu bar to resize the + window. + #### Fixed bugs - ComboBox: Fixed vertical alignment of text in popup list with text in combo diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java index 4854274f..742f1012 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java @@ -87,6 +87,7 @@ import com.formdev.flatlaf.util.UIScale; * @uiDefault TitlePane.centerTitle boolean * @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean * @uiDefault TitlePane.menuBarTitleGap int + * @uiDefault TitlePane.menuBarResizeHeight int * @uiDefault TitlePane.closeIcon Icon * @uiDefault TitlePane.iconifyIcon Icon * @uiDefault TitlePane.maximizeIcon Icon @@ -111,6 +112,7 @@ public class FlatTitlePane protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" ); protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true ); protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 20 ); + /** @since 2.4 */ protected final int menuBarResizeHeight = FlatUIUtils.getUIInt( "TitlePane.menuBarResizeHeight", 4 ); protected final JRootPane rootPane; @@ -233,10 +235,7 @@ public class FlatTitlePane @Override public Dimension getPreferredSize() { Dimension size = super.getPreferredSize(); - if( buttonMaximizedHeight > 0 && - window instanceof Frame && - (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 ) - { + if( buttonMaximizedHeight > 0 && isWindowMaximized() ) { // make title pane height smaller when frame is maximized size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) ); } @@ -567,6 +566,11 @@ debug*/ frame.setExtendedState( frame.getExtendedState() | Frame.ICONIFIED ); } + /** @since 2.4 */ + protected boolean isWindowMaximized() { + return window instanceof Frame && (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0; + } + /** * Maximizes the window. */ @@ -741,9 +745,7 @@ debug*/ // if frame is maximized, increase icon bounds to upper-left corner // of window to allow closing window via double-click in upper-left corner - if( window instanceof Frame && - (((Frame)window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0 ) - { + if( isWindowMaximized() ) { iconBounds.height += iconBounds.y; iconBounds.y = 0; @@ -768,6 +770,15 @@ debug*/ if( hasVisibleEmbeddedMenuBar( menuBar ) ) { r = getNativeHitTestSpot( menuBar ); if( r != null ) { + // if frame is not maximized, make menu bar hit test spot smaller at top + // to have a small area above the menu bar to resize the window + if( !isWindowMaximized() ) { + // limit to 8, because Windows does not use a larger height + int resizeHeight = UIScale.scale( Math.min( menuBarResizeHeight, 8 ) ); + r.y += resizeHeight; + r.height -= resizeHeight; + } + Component horizontalGlue = findHorizontalGlue( menuBar ); if( horizontalGlue != null ) { // If menu bar is embedded and contains a horizontal glue component, @@ -854,7 +865,7 @@ debug*/ } else if( borderColor != null && (rootPane.getJMenuBar() == null || !rootPane.getJMenuBar().isVisible()) ) insets.bottom += UIScale.scale( 1 ); - if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized( c ) ) + if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() ) insets = FlatUIUtils.addInsets( insets, WindowTopBorder.getInstance().getBorderInsets() ); return insets; @@ -873,7 +884,7 @@ debug*/ FlatUIUtils.paintFilledRectangle( g, borderColor, x, y + height - lineHeight, width, lineHeight ); } - if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized( c ) ) + if( !SystemInfo.isWindows_11_orLater && hasNativeCustomDecoration() && !isWindowMaximized() ) WindowTopBorder.getInstance().paintBorder( c, g, x, y, width, height ); } @@ -881,10 +892,6 @@ debug*/ JMenuBar menuBar = rootPane.getJMenuBar(); return hasVisibleEmbeddedMenuBar( menuBar ) ? menuBar.getBorder() : null; } - - protected boolean isWindowMaximized( Component c ) { - return window instanceof Frame && (((Frame) window).getExtendedState() & Frame.MAXIMIZED_BOTH) != 0; - } } //---- class FlatTitleLabelUI --------------------------------------------- diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties index 5939219c..621e6473 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties @@ -794,6 +794,7 @@ TitlePane.buttonMaximizedHeight = 22 TitlePane.centerTitle = false TitlePane.centerTitleIfMenuBarEmbedded = true TitlePane.menuBarTitleGap = 20 +TitlePane.menuBarResizeHeight = 4 TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon TitlePane.maximizeIcon = com.formdev.flatlaf.icons.FlatWindowMaximizeIcon diff --git a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt index 18ee11f2..628f7fd9 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt @@ -1233,6 +1233,7 @@ TitlePane.inactiveBackground #303234 HSL 210 4 20 javax.swing.plaf.Colo TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.menuBarEmbedded true +TitlePane.menuBarResizeHeight 4 TitlePane.menuBarTitleGap 20 TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] diff --git a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt index 13731937..17e0d6cd 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt @@ -1238,6 +1238,7 @@ TitlePane.inactiveBackground #ffffff HSL 0 0 100 javax.swing.plaf.Colo TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.menuBarEmbedded true +TitlePane.menuBarResizeHeight 4 TitlePane.menuBarTitleGap 20 TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] diff --git a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt index 8900a298..4c691646 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt @@ -1266,6 +1266,7 @@ TitlePane.inactiveBackground #008800 HSL 120 100 27 javax.swing.plaf.Colo TitlePane.inactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.menuBarEmbedded true +TitlePane.menuBarResizeHeight 4 TitlePane.menuBarTitleGap 20 TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] diff --git a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt index 1ce4fc69..1c6fa061 100644 --- a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt +++ b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt @@ -999,6 +999,7 @@ TitlePane.inactiveBackground TitlePane.inactiveForeground TitlePane.maximizeIcon TitlePane.menuBarEmbedded +TitlePane.menuBarResizeHeight TitlePane.menuBarTitleGap TitlePane.noIconLeftGap TitlePane.restoreIcon From 42764550e6ba8e7fdbd984136f05a006d1765bc1 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sat, 9 Jul 2022 19:54:29 +0200 Subject: [PATCH 2/8] Window decorations: improved window title bar layout for small window widths: - width of iconify/maximize/close buttons is reduced to give more space to embedded menu bar and title - window title now has a minimum width to always allow moving window --- CHANGELOG.md | 6 ++ .../com/formdev/flatlaf/ui/FlatTitlePane.java | 60 +++++++++++++++---- .../com/formdev/flatlaf/FlatLaf.properties | 2 + .../dumps/uidefaults/FlatDarkLaf_1.8.0.txt | 2 + .../dumps/uidefaults/FlatLightLaf_1.8.0.txt | 2 + .../dumps/uidefaults/FlatTestLaf_1.8.0.txt | 2 + .../flatlaf/themeeditor/FlatLafUIKeys.txt | 2 + 7 files changed, 64 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fbe39b2..cd47512c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ FlatLaf Change Log - Native window decorations (Windows 10/11 only): - There is now a small area at top of the embedded menu bar to resize the window. + - Improved window title bar layout for small window widths: + - Width of iconify/maximize/close buttons is reduced (if necessary) to give + more space to embedded menu bar and title. + - Window title now has a minimum width to always allow moving window + (click-and-drag on window title). Instead, embedded menu bar is made + smaller. #### Fixed bugs diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java index 742f1012..ba51272b 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java @@ -83,6 +83,8 @@ import com.formdev.flatlaf.util.UIScale; * @uiDefault TitlePane.iconMargins Insets * @uiDefault TitlePane.titleMargins Insets * @uiDefault TitlePane.menuBarEmbedded boolean + * @uiDefault TitlePane.titleMinimumWidth int + * @uiDefault TitlePane.buttonMinimumWidth int * @uiDefault TitlePane.buttonMaximizedHeight int * @uiDefault TitlePane.centerTitle boolean * @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean @@ -108,6 +110,8 @@ public class FlatTitlePane /** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", true ); /** @since 2 */ protected final int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 ); protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" ); + /** @since 2.4 */ protected final int titleMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.titleMinimumWidth", 30 ); + /** @since 2.4 */ protected final int buttonMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.buttonMinimumWidth", 22 ); protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" ); protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" ); protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true ); @@ -185,18 +189,44 @@ public class FlatTitlePane setLayout( new BorderLayout() { @Override public void layoutContainer( Container target ) { - super.layoutContainer( target ); - - // make left panel (with embedded menu bar) smaller if horizontal space is rare - // to avoid that embedded menu bar overlaps button bar + // compute available bounds Insets insets = target.getInsets(); - int width = target.getWidth() - insets.left - insets.right; - if( leftPanel.getWidth() + buttonPanel.getWidth() > width ) { - int oldWidth = leftPanel.getWidth(); - int newWidth = Math.max( width - buttonPanel.getWidth(), 0 ); - leftPanel.setSize( newWidth, leftPanel.getHeight() ); - if( !getComponentOrientation().isLeftToRight() ) - leftPanel.setLocation( leftPanel.getX() + (oldWidth - newWidth), leftPanel.getY() ); + int x = insets.left; + int y = insets.top; + int w = target.getWidth() - insets.left - insets.right; + int h = target.getHeight() - insets.top - insets.bottom; + + // compute widths + int leftWidth = leftPanel.getPreferredSize().width; + int buttonsWidth = buttonPanel.getPreferredSize().width; + int titleWidth = w - leftWidth - buttonsWidth; + int minTitleWidth = UIScale.scale( titleMinimumWidth ); + + // if title is too small, reduce width of buttons + if( titleWidth < minTitleWidth ) { + buttonsWidth = Math.max( buttonsWidth - (minTitleWidth - titleWidth), buttonPanel.getMinimumSize().width ); + titleWidth = w - leftWidth - buttonsWidth; + } + + // if title is still too small, reduce width of left panel (icon and embedded menu bar) + if( titleWidth < minTitleWidth ) { + int minLeftWidth = iconLabel.isVisible() + ? iconLabel.getWidth() - iconLabel.getInsets().right + : UIScale.scale( noIconLeftGap ); + leftWidth = Math.max( leftWidth - (minTitleWidth - titleWidth), minLeftWidth ); + titleWidth = w - leftWidth - buttonsWidth; + } + + if( target.getComponentOrientation().isLeftToRight() ) { + // left-to-right + leftPanel.setBounds( x, y, leftWidth, h ); + titleLabel.setBounds( x + leftWidth, y, titleWidth, h ); + buttonPanel.setBounds( x + leftWidth + titleWidth, y, buttonsWidth, h ); + } else { + // right-to-left + buttonPanel.setBounds( x, y, buttonsWidth, h ); + titleLabel.setBounds( x + buttonsWidth, y, titleWidth, h ); + leftPanel.setBounds( x + buttonsWidth + titleWidth, y, leftWidth, h ); } // If menu bar is embedded and contains a horizontal glue component, @@ -258,7 +288,13 @@ public class FlatTitlePane } protected JButton createButton( String iconKey, String accessibleName, ActionListener action ) { - JButton button = new JButton( UIManager.getIcon( iconKey ) ); + JButton button = new JButton( UIManager.getIcon( iconKey ) ) { + @Override + public Dimension getMinimumSize() { + // allow the button to shrink if space is rare + return new Dimension( UIScale.scale( buttonMinimumWidth ), super.getMinimumSize().height ); + } + }; button.setFocusable( false ); button.setContentAreaFilled( false ); button.setBorder( BorderFactory.createEmptyBorder() ); diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties index 621e6473..66e1b015 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties @@ -789,7 +789,9 @@ TitlePane.noIconLeftGap = 8 TitlePane.iconSize = 16,16 TitlePane.iconMargins = 3,8,3,8 TitlePane.titleMargins = 3,0,3,0 +TitlePane.titleMinimumWidth = 30 TitlePane.buttonSize = 44,30 +TitlePane.buttonMinimumWidth = 22 TitlePane.buttonMaximizedHeight = 22 TitlePane.centerTitle = false TitlePane.centerTitleIfMenuBarEmbedded = true diff --git a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt index 628f7fd9..e0f90f62 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt @@ -1214,6 +1214,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI TitlePane.background #303234 HSL 210 4 20 javax.swing.plaf.ColorUIResource [UI] TitlePane.buttonHoverBackground #55585c HSL 214 4 35 com.formdev.flatlaf.util.DerivedColor [UI] lighten(15% autoInverse) TitlePane.buttonMaximizedHeight 22 +TitlePane.buttonMinimumWidth 22 TitlePane.buttonPressedBackground #484c4f HSL 206 5 30 com.formdev.flatlaf.util.DerivedColor [UI] lighten(10% autoInverse) TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.centerTitle false @@ -1239,6 +1240,7 @@ TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.showIcon true TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] +TitlePane.titleMinimumWidth 30 TitlePane.unifiedBackground true TitlePane.useWindowDecorations true diff --git a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt index 17e0d6cd..1e32e40b 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt @@ -1219,6 +1219,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI TitlePane.background #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] TitlePane.buttonHoverBackground #e6e6e6 HSL 0 0 90 com.formdev.flatlaf.util.DerivedColor [UI] darken(10% autoInverse) TitlePane.buttonMaximizedHeight 22 +TitlePane.buttonMinimumWidth 22 TitlePane.buttonPressedBackground #ebebeb HSL 0 0 92 com.formdev.flatlaf.util.DerivedColor [UI] darken(8% autoInverse) TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.centerTitle false @@ -1244,6 +1245,7 @@ TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.showIcon true TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] +TitlePane.titleMinimumWidth 30 TitlePane.unifiedBackground true TitlePane.useWindowDecorations true diff --git a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt index 4c691646..11ba3707 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt @@ -1249,6 +1249,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI TitlePane.background #00ff00 HSL 120 100 50 javax.swing.plaf.ColorUIResource [UI] TitlePane.borderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] TitlePane.buttonMaximizedHeight 22 +TitlePane.buttonMinimumWidth 22 TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.centerTitle false TitlePane.centerTitleIfMenuBarEmbedded true @@ -1272,6 +1273,7 @@ TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.showIcon true TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] +TitlePane.titleMinimumWidth 30 TitlePane.unifiedBackground true TitlePane.useWindowDecorations true diff --git a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt index 1c6fa061..7bb5a8f9 100644 --- a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt +++ b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt @@ -980,6 +980,7 @@ TitlePane.background TitlePane.borderColor TitlePane.buttonHoverBackground TitlePane.buttonMaximizedHeight +TitlePane.buttonMinimumWidth TitlePane.buttonPressedBackground TitlePane.buttonSize TitlePane.centerTitle @@ -1005,6 +1006,7 @@ TitlePane.noIconLeftGap TitlePane.restoreIcon TitlePane.showIcon TitlePane.titleMargins +TitlePane.titleMinimumWidth TitlePane.unifiedBackground TitlePane.useWindowDecorations TitledBorder.border From cddbb3d7d46262e9afa787bdeec27bab4f0e04c3 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sun, 10 Jul 2022 13:57:37 +0200 Subject: [PATCH 3/8] Window decorations: make sure that a horizontal glue in embedded menu bar has a minimum width and is always visible --- .../com/formdev/flatlaf/ui/FlatMenuBarUI.java | 133 ++++++++++++++++++ .../formdev/flatlaf/ui/FlatPopupMenuUI.java | 11 +- .../formdev/flatlaf/ui/FlatRootPaneUI.java | 6 + .../testing/FlatWindowDecorationsTest.java | 17 +++ 4 files changed, 163 insertions(+), 4 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java index 273fb080..e167c368 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatMenuBarUI.java @@ -18,8 +18,10 @@ package com.formdev.flatlaf.ui; import java.awt.Color; import java.awt.Component; +import java.awt.Container; import java.awt.Graphics; import java.awt.Insets; +import java.awt.LayoutManager; import java.awt.Window; import java.awt.event.ActionEvent; import java.beans.PropertyChangeListener; @@ -27,6 +29,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.AbstractAction; import javax.swing.ActionMap; +import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JMenuBar; @@ -40,11 +43,13 @@ import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicMenuBarUI; +import javax.swing.plaf.basic.DefaultMenuLayout; import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable; import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI; import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.SystemInfo; +import com.formdev.flatlaf.util.UIScale; /** * Provides the Flat LaF UI delegate for {@link javax.swing.JMenuBar}. @@ -100,6 +105,10 @@ public class FlatMenuBarUI super.installDefaults(); LookAndFeel.installProperty( menuBar, "opaque", false ); + + LayoutManager layout = menuBar.getLayout(); + if( layout == null || layout instanceof UIResource ) + menuBar.setLayout( new FlatMenuBarLayout( menuBar ) ); } @Override @@ -221,6 +230,130 @@ public class FlatMenuBarUI rootPane.getWindowDecorationStyle() != JRootPane.NONE; } + //---- class FlatMenuBarLayout -------------------------------------------- + + /** + * @since 2.4 + */ + protected static class FlatMenuBarLayout + extends DefaultMenuLayout + { + public FlatMenuBarLayout( Container target ) { + super( target, BoxLayout.LINE_AXIS ); + } + + @Override + public void layoutContainer( Container target ) { + super.layoutContainer( target ); + + + // The only purpose of the code below is to make sure that a horizontal glue, + // which can be used to move window and displays the window title in embedded menu bar, + // is always visible within the menu bar bounds and has a minimum width. + // If this is not the case, the horizontal glue is made larger and + // components that are on the left side of the glue are made smaller. + + + // get root pane and check whether this menu bar is the root pane menu bar + JRootPane rootPane = SwingUtilities.getRootPane( target ); + if( rootPane == null || rootPane.getJMenuBar() != target ) + return; + + // get title pane and check whether menu bar is embedded + FlatTitlePane titlePane = FlatRootPaneUI.getTitlePane( rootPane ); + if( titlePane == null || !titlePane.isMenuBarEmbedded() ) + return; + + // check whether there is a horizontal glue (used for window title in embedded menu bar) + // and check minimum width of horizontal glue + Component horizontalGlue = titlePane.findHorizontalGlue( (JMenuBar) target ); + int minTitleWidth = UIScale.scale( titlePane.titleMinimumWidth ); + if( horizontalGlue != null && horizontalGlue.getWidth() < minTitleWidth ) { + // get index of glue component + int glueIndex = -1; + Component[] components = target.getComponents(); + for( int i = components.length - 1; i >= 0; i-- ) { + if( components[i] == horizontalGlue ) { + glueIndex = i; + break; + } + } + if( glueIndex < 0 ) + return; // should never happen + + if( target.getComponentOrientation().isLeftToRight() ) { + // left-to-right + + // make horizontal glue wider (minimum title width) + int offset = minTitleWidth - horizontalGlue.getWidth(); + horizontalGlue.setSize( minTitleWidth, horizontalGlue.getHeight() ); + + // check whether glue is fully visible + int minGlueX = target.getWidth() - target.getInsets().right - minTitleWidth; + if( minGlueX < horizontalGlue.getX() ) { + // move glue to the left to make it fully visible + offset -= (horizontalGlue.getX() - minGlueX); + horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() ); + + // shrink and move components that are on the left side of the glue + for( int i = glueIndex - 1; i >= 0; i-- ) { + Component c = components[i]; + if( c.getX() > minGlueX ) { + // move component and set width to zero + c.setBounds( minGlueX, c.getY(), 0, c.getHeight() ); + } else { + // reduce size of component + c.setSize( minGlueX - c.getX(), c.getHeight() ); + break; + } + } + } + + // move components that are on the right side of the glue + for( int i = glueIndex + 1; i < components.length; i++ ) { + Component c = components[i]; + c.setLocation( c.getX() + offset, c.getY() ); + } + } else { + // right-to-left + + // make horizontal glue wider (minimum title width) + int offset = minTitleWidth - horizontalGlue.getWidth(); + horizontalGlue.setBounds( horizontalGlue.getX() - offset, horizontalGlue.getY(), + minTitleWidth, horizontalGlue.getHeight() ); + + // check whether glue is fully visible + int minGlueX = target.getInsets().left; + if( minGlueX > horizontalGlue.getX() ) { + // move glue to the right to make it fully visible + offset -= (horizontalGlue.getX() - minGlueX); + horizontalGlue.setLocation( minGlueX, horizontalGlue.getY() ); + + // shrink and move components that are on the right side of the glue + int x = horizontalGlue.getX() + horizontalGlue.getWidth(); + for( int i = glueIndex - 1; i >= 0; i-- ) { + Component c = components[i]; + if( c.getX() + c.getWidth() < x ) { + // move component and set width to zero + c.setBounds( x, c.getY(), 0, c.getHeight() ); + } else { + // move component and reduce size + c.setBounds( x, c.getY(), c.getWidth() - (x - c.getX()), c.getHeight() ); + break; + } + } + } + + // move components that are on the left side of the glue + for( int i = glueIndex + 1; i < components.length; i++ ) { + Component c = components[i]; + c.setLocation( c.getX() - offset, c.getY() ); + } + } + } + } + } + //---- class TakeFocus ---------------------------------------------------- /** diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java index 3c0014ab..2ea093a5 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPopupMenuUI.java @@ -130,7 +130,7 @@ public class FlatPopupMenuUI LayoutManager layout = popupMenu.getLayout(); if( layout == null || layout instanceof UIResource ) - popupMenu.setLayout( new FlatMenuLayout( popupMenu, BoxLayout.Y_AXIS ) ); + popupMenu.setLayout( new FlatPopupMenuLayout( popupMenu, BoxLayout.Y_AXIS ) ); } @Override @@ -230,12 +230,15 @@ public class FlatPopupMenuUI return screenBounds.height - screenInsets.top - screenInsets.bottom; } - //---- class FlatMenuLayout ----------------------------------------------- + //---- class FlatPopupMenuLayout ------------------------------------------ - protected static class FlatMenuLayout + /** + * @since 2.4 + */ + protected static class FlatPopupMenuLayout extends DefaultMenuLayout { - public FlatMenuLayout( Container target, int axis ) { + public FlatPopupMenuLayout( Container target, int axis ) { super( target, axis ); } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java index eee1b3c7..54640afd 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatRootPaneUI.java @@ -364,6 +364,12 @@ public class FlatRootPaneUI ((FlatRootPaneUI)ui).titlePane.isMenuBarEmbedded(); } + /** @since 2.4 */ + protected static FlatTitlePane getTitlePane( JRootPane rootPane ) { + RootPaneUI ui = rootPane.getUI(); + return ui instanceof FlatRootPaneUI ? ((FlatRootPaneUI)ui).titlePane : null; + } + //---- class FlatRootLayout ----------------------------------------------- protected class FlatRootLayout diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java index c8f99783..c7e2937f 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java @@ -241,6 +241,23 @@ public class FlatWindowDecorationsTest if( c instanceof Box.Filler ) return; } + +/*debug + menuBar.add( new Box.Filler( new Dimension(), new Dimension(), + new Dimension( Short.MAX_VALUE, Short.MAX_VALUE ) ) + { + @Override + protected void paintComponent( Graphics g ) { + int w = getWidth(); + int h = getHeight(); + g.setColor( Color.blue ); + g.drawRect( 0, 0, w - 1, h - 1 ); + g.drawLine( 0, 0, w, h ); + g.drawLine( 0, h, w, 0 ); + } + } ); +debug*/ + menuBar.add( Box.createGlue() ); menuBar.revalidate(); } From 52feaac92a0b9007dec2a203ded9c880127c1d3c Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sun, 10 Jul 2022 14:03:45 +0200 Subject: [PATCH 4/8] Window decorations: no longer reduce height of window title bar if it has an embedded menu bar and is maximized --- CHANGELOG.md | 2 ++ .../src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd47512c..ae494763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ FlatLaf Change Log - Window title now has a minimum width to always allow moving window (click-and-drag on window title). Instead, embedded menu bar is made smaller. + - No longer reduce height of window title bar if it has an embedded menu bar + and is maximized. #### Fixed bugs diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java index ba51272b..9441e70b 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java @@ -265,7 +265,7 @@ public class FlatTitlePane @Override public Dimension getPreferredSize() { Dimension size = super.getPreferredSize(); - if( buttonMaximizedHeight > 0 && isWindowMaximized() ) { + if( buttonMaximizedHeight > 0 && isWindowMaximized() && !hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ) ) { // make title pane height smaller when frame is maximized size = new Dimension( size.width, Math.min( size.height, UIScale.scale( buttonMaximizedHeight ) ) ); } From ef151c68f4083d2381899300846b02882b356579 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Mon, 11 Jul 2022 17:28:30 +0200 Subject: [PATCH 5/8] Window decorations: - improved title bar usability by using larger gaps and minimum sizes - added minimum gap between embedded menu bar and window title - fixed oscillating title while resizing window width - fixed lost right-to-left component orientation in title bar when switching Laf --- CHANGELOG.md | 2 + .../com/formdev/flatlaf/ui/FlatTitlePane.java | 87 ++++++++++++------- .../com/formdev/flatlaf/FlatLaf.properties | 7 +- .../dumps/uidefaults/FlatDarkLaf_1.8.0.txt | 7 +- .../dumps/uidefaults/FlatLightLaf_1.8.0.txt | 7 +- .../dumps/uidefaults/FlatTestLaf_1.8.0.txt | 7 +- .../flatlaf/themeeditor/FlatLafUIKeys.txt | 1 + 7 files changed, 77 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae494763..0ba535c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,8 @@ FlatLaf Change Log `true` on Windows 10. (issue #540) - Fixed missing top window border in dark themes if window drop shadows are disabled in system settings. (issue #554; Windows 10 only) + - Right-to-left component orientation of title bar was lost when switching + theme. ## 2.3 diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java index 9441e70b..3ad11e84 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java @@ -24,6 +24,7 @@ import java.awt.Container; import java.awt.Dialog; import java.awt.Dimension; import java.awt.EventQueue; +import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Graphics; import java.awt.GraphicsConfiguration; @@ -50,13 +51,13 @@ import javax.accessibility.AccessibleContext; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; +import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JMenuBar; import javax.swing.JPanel; import javax.swing.JRootPane; -import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.AbstractBorder; @@ -110,12 +111,13 @@ public class FlatTitlePane /** @since 2 */ protected final boolean showIcon = FlatUIUtils.getUIBoolean( "TitlePane.showIcon", true ); /** @since 2 */ protected final int noIconLeftGap = FlatUIUtils.getUIInt( "TitlePane.noIconLeftGap", 8 ); protected final Dimension iconSize = UIManager.getDimension( "TitlePane.iconSize" ); - /** @since 2.4 */ protected final int titleMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.titleMinimumWidth", 30 ); - /** @since 2.4 */ protected final int buttonMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.buttonMinimumWidth", 22 ); + /** @since 2.4 */ protected final int titleMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.titleMinimumWidth", 60 ); + /** @since 2.4 */ protected final int buttonMinimumWidth = FlatUIUtils.getUIInt( "TitlePane.buttonMinimumWidth", 30 ); protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" ); protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" ); protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true ); - protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 20 ); + protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 40 ); + /** @since 2.4 */ protected final int menuBarTitleMinimumGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleMinimumGap", 12 ); /** @since 2.4 */ protected final int menuBarResizeHeight = FlatUIUtils.getUIInt( "TitlePane.menuBarResizeHeight", 4 ); protected final JRootPane rootPane; @@ -148,6 +150,8 @@ public class FlatTitlePane // necessary for closing window with double-click on icon iconLabel.addMouseListener( handler ); + + applyComponentOrientation( rootPane.getComponentOrientation() ); } protected FlatTitlePaneBorder createTitlePaneBorder() { @@ -169,7 +173,6 @@ public class FlatTitlePane }; iconLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.iconMargins" ) ) ); titleLabel.setBorder( new FlatEmptyBorder( UIManager.getInsets( "TitlePane.titleMargins" ) ) ); - titleLabel.setHorizontalAlignment( SwingConstants.CENTER ); leftPanel.setLayout( new BoxLayout( leftPanel, BoxLayout.LINE_AXIS ) ); leftPanel.setOpaque( false ); @@ -941,36 +944,62 @@ debug*/ } @Override - protected void paintEnabledText( JLabel l, Graphics g, String s, int textX, int textY ) { + protected String layoutCL( JLabel label, FontMetrics fontMetrics, String text, Icon icon, + Rectangle viewR, Rectangle iconR, Rectangle textR ) + { JMenuBar menuBar = rootPane.getJMenuBar(); - boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( menuBar ) && hasMenus( menuBar ); - int labelWidth = l.getWidth(); - int textWidth = labelWidth - (textX * 2); - int gap = UIScale.scale( menuBarTitleGap ); + boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( menuBar ); + boolean hasEmbeddedLeadingMenus = hasEmbeddedMenuBar && hasLeadingMenus( menuBar ); + boolean leftToRight = getComponentOrientation().isLeftToRight(); - // The passed in textX coordinate is always to horizontally center the text within the label bounds. - // Modify textX so that the text is painted either centered within the window bounds or leading aligned. - boolean center = hasEmbeddedMenuBar ? centerTitleIfMenuBarEmbedded : centerTitle; - if( center ) { - // If window is wide enough, center title within window bounds. - // Otherwise, leave it centered within free space (label bounds). - int centeredTextX = ((l.getParent().getWidth() - textWidth) / 2) - l.getX(); - if( centeredTextX >= gap && centeredTextX + textWidth <= labelWidth - gap ) - textX = centeredTextX; - } else { - // leading aligned - boolean leftToRight = getComponentOrientation().isLeftToRight(); - Insets insets = l.getInsets(); - int leadingInset = hasEmbeddedMenuBar ? gap : (leftToRight ? insets.left : insets.right); - int leadingTextX = leftToRight ? leadingInset : labelWidth - leadingInset - textWidth; - if( leftToRight ? leadingTextX < textX : leadingTextX > textX ) - textX = leadingTextX; + if( hasEmbeddedMenuBar ) { + int minGap = UIScale.scale( menuBarTitleMinimumGap ); + + // apply minimum leading gap (between embedded menu bar and title) + if( hasEmbeddedLeadingMenus ) { + if( leftToRight ) + viewR.x += minGap; + viewR.width -= minGap; + } + + // apply minimum trailing gap (between title and right aligned components of embedded menu bar) + Component horizontalGlue = findHorizontalGlue( menuBar ); + if( horizontalGlue != null && menuBar.getComponent( menuBar.getComponentCount() - 1 ) != horizontalGlue ) { + if( !leftToRight ) + viewR.x += minGap; + viewR.width -= minGap; + } } - super.paintEnabledText( l, g, s, textX, textY ); + String clippedText = super.layoutCL( label, fontMetrics, text, icon, viewR, iconR, textR ); + + if( !clippedText.equals( text ) ) { + // if text is clipped, align to left (or right) + textR.x = leftToRight ? viewR.x : viewR.x + viewR.width - textR.width; + } else { + int leadingGap = hasEmbeddedLeadingMenus ? UIScale.scale( menuBarTitleGap - menuBarTitleMinimumGap ) : 0; + + boolean center = hasEmbeddedLeadingMenus ? centerTitleIfMenuBarEmbedded : centerTitle; + if( center ) { + // If window is wide enough, center title within window bounds. + // Otherwise, center within free space (label bounds). + Container parent = label.getParent(); + int centeredTextX = (parent != null) ? ((parent.getWidth() - textR.width) / 2) - label.getX() : -1; + textR.x = (centeredTextX >= viewR.x + leadingGap && centeredTextX + textR.width <= viewR.x + viewR.width - leadingGap) + ? centeredTextX + : viewR.x + ((viewR.width - textR.width) / 2); + } else { + // leading aligned with leading gap, which is reduced is space is rare + textR.x = leftToRight + ? Math.min( viewR.x + leadingGap, viewR.x + viewR.width - textR.width ) + : Math.max( viewR.x + viewR.width - textR.width - leadingGap, viewR.x ); + } + } + + return clippedText; } - private boolean hasMenus( JMenuBar menuBar ) { + private boolean hasLeadingMenus( JMenuBar menuBar ) { // check whether menu bar is empty if( menuBar.getComponentCount() == 0 || menuBar.getWidth() == 0 ) return false; diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties index 66e1b015..f100d89e 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties @@ -789,13 +789,14 @@ TitlePane.noIconLeftGap = 8 TitlePane.iconSize = 16,16 TitlePane.iconMargins = 3,8,3,8 TitlePane.titleMargins = 3,0,3,0 -TitlePane.titleMinimumWidth = 30 +TitlePane.titleMinimumWidth = 60 TitlePane.buttonSize = 44,30 -TitlePane.buttonMinimumWidth = 22 +TitlePane.buttonMinimumWidth = 30 TitlePane.buttonMaximizedHeight = 22 TitlePane.centerTitle = false TitlePane.centerTitleIfMenuBarEmbedded = true -TitlePane.menuBarTitleGap = 20 +TitlePane.menuBarTitleGap = 40 +TitlePane.menuBarTitleMinimumGap = 12 TitlePane.menuBarResizeHeight = 4 TitlePane.closeIcon = com.formdev.flatlaf.icons.FlatWindowCloseIcon TitlePane.iconifyIcon = com.formdev.flatlaf.icons.FlatWindowIconifyIcon diff --git a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt index e0f90f62..f0d83179 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt @@ -1214,7 +1214,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI TitlePane.background #303234 HSL 210 4 20 javax.swing.plaf.ColorUIResource [UI] TitlePane.buttonHoverBackground #55585c HSL 214 4 35 com.formdev.flatlaf.util.DerivedColor [UI] lighten(15% autoInverse) TitlePane.buttonMaximizedHeight 22 -TitlePane.buttonMinimumWidth 22 +TitlePane.buttonMinimumWidth 30 TitlePane.buttonPressedBackground #484c4f HSL 206 5 30 com.formdev.flatlaf.util.DerivedColor [UI] lighten(10% autoInverse) TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.centerTitle false @@ -1235,12 +1235,13 @@ TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.Colo TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.menuBarEmbedded true TitlePane.menuBarResizeHeight 4 -TitlePane.menuBarTitleGap 20 +TitlePane.menuBarTitleGap 40 +TitlePane.menuBarTitleMinimumGap 12 TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.showIcon true TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] -TitlePane.titleMinimumWidth 30 +TitlePane.titleMinimumWidth 60 TitlePane.unifiedBackground true TitlePane.useWindowDecorations true diff --git a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt index 1e32e40b..6a54fd96 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt @@ -1219,7 +1219,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI TitlePane.background #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] TitlePane.buttonHoverBackground #e6e6e6 HSL 0 0 90 com.formdev.flatlaf.util.DerivedColor [UI] darken(10% autoInverse) TitlePane.buttonMaximizedHeight 22 -TitlePane.buttonMinimumWidth 22 +TitlePane.buttonMinimumWidth 30 TitlePane.buttonPressedBackground #ebebeb HSL 0 0 92 com.formdev.flatlaf.util.DerivedColor [UI] darken(8% autoInverse) TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.centerTitle false @@ -1240,12 +1240,13 @@ TitlePane.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.Colo TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.menuBarEmbedded true TitlePane.menuBarResizeHeight 4 -TitlePane.menuBarTitleGap 20 +TitlePane.menuBarTitleGap 40 +TitlePane.menuBarTitleMinimumGap 12 TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.showIcon true TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] -TitlePane.titleMinimumWidth 30 +TitlePane.titleMinimumWidth 60 TitlePane.unifiedBackground true TitlePane.useWindowDecorations true diff --git a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt index 11ba3707..db0b76c8 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt @@ -1249,7 +1249,7 @@ TextPaneUI com.formdev.flatlaf.ui.FlatTextPaneUI TitlePane.background #00ff00 HSL 120 100 50 javax.swing.plaf.ColorUIResource [UI] TitlePane.borderColor #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] TitlePane.buttonMaximizedHeight 22 -TitlePane.buttonMinimumWidth 22 +TitlePane.buttonMinimumWidth 30 TitlePane.buttonSize 44,30 javax.swing.plaf.DimensionUIResource [UI] TitlePane.centerTitle false TitlePane.centerTitleIfMenuBarEmbedded true @@ -1268,12 +1268,13 @@ TitlePane.inactiveForeground #ffffff HSL 0 0 100 javax.swing.plaf.Colo TitlePane.maximizeIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowMaximizeIcon [UI] TitlePane.menuBarEmbedded true TitlePane.menuBarResizeHeight 4 -TitlePane.menuBarTitleGap 20 +TitlePane.menuBarTitleGap 40 +TitlePane.menuBarTitleMinimumGap 12 TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.showIcon true TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] -TitlePane.titleMinimumWidth 30 +TitlePane.titleMinimumWidth 60 TitlePane.unifiedBackground true TitlePane.useWindowDecorations true diff --git a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt index 7bb5a8f9..13eaa219 100644 --- a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt +++ b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt @@ -1002,6 +1002,7 @@ TitlePane.maximizeIcon TitlePane.menuBarEmbedded TitlePane.menuBarResizeHeight TitlePane.menuBarTitleGap +TitlePane.menuBarTitleMinimumGap TitlePane.noIconLeftGap TitlePane.restoreIcon TitlePane.showIcon From 2c041dce3a1bb83b48cf6c3b7da72175ac37b255 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Mon, 11 Jul 2022 17:47:04 +0200 Subject: [PATCH 6/8] Window decorations: add small resize area at top of embedded menu bar only if frame is resizable --- .../src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java index 3ad11e84..754b03b2 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java @@ -809,9 +809,9 @@ debug*/ if( hasVisibleEmbeddedMenuBar( menuBar ) ) { r = getNativeHitTestSpot( menuBar ); if( r != null ) { - // if frame is not maximized, make menu bar hit test spot smaller at top + // if frame is resizable and not maximized, make menu bar hit test spot smaller at top // to have a small area above the menu bar to resize the window - if( !isWindowMaximized() ) { + if( window instanceof Frame && ((Frame)window).isResizable() && !isWindowMaximized() ) { // limit to 8, because Windows does not use a larger height int resizeHeight = UIScale.scale( Math.min( menuBarResizeHeight, 8 ) ); r.y += resizeHeight; From 10a965d7653f4151aa4bac6c0b4d28f70df5adf5 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Wed, 13 Jul 2022 17:58:25 +0200 Subject: [PATCH 7/8] Window decorations: option to show window icon beside window title, if menu bar is embedded or title is centered --- CHANGELOG.md | 2 + .../com/formdev/flatlaf/ui/FlatTitlePane.java | 55 +++++++++++++++---- .../com/formdev/flatlaf/FlatLaf.properties | 1 + .../dumps/uidefaults/FlatDarkLaf_1.8.0.txt | 1 + .../dumps/uidefaults/FlatLightLaf_1.8.0.txt | 1 + .../dumps/uidefaults/FlatTestLaf_1.8.0.txt | 1 + .../flatlaf/themeeditor/FlatLafUIKeys.txt | 1 + 7 files changed, 52 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ba535c5..83324d5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ FlatLaf Change Log - Window title now has a minimum width to always allow moving window (click-and-drag on window title). Instead, embedded menu bar is made smaller. + - Option to show window icon beside window title, if menu bar is embedded or + title is centered. Set UI value `TitlePane.showIconBesideTitle` to `true`. - No longer reduce height of window title bar if it has an embedded menu bar and is maximized. diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java index 754b03b2..97bc6bdf 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java @@ -89,6 +89,7 @@ import com.formdev.flatlaf.util.UIScale; * @uiDefault TitlePane.buttonMaximizedHeight int * @uiDefault TitlePane.centerTitle boolean * @uiDefault TitlePane.centerTitleIfMenuBarEmbedded boolean + * @uiDefault TitlePane.showIconBesideTitle boolean * @uiDefault TitlePane.menuBarTitleGap int * @uiDefault TitlePane.menuBarResizeHeight int * @uiDefault TitlePane.closeIcon Icon @@ -116,6 +117,7 @@ public class FlatTitlePane protected final int buttonMaximizedHeight = UIManager.getInt( "TitlePane.buttonMaximizedHeight" ); protected final boolean centerTitle = UIManager.getBoolean( "TitlePane.centerTitle" ); protected final boolean centerTitleIfMenuBarEmbedded = FlatUIUtils.getUIBoolean( "TitlePane.centerTitleIfMenuBarEmbedded", true ); + /** @since 2.4 */ protected final boolean showIconBesideTitle = UIManager.getBoolean( "TitlePane.showIconBesideTitle" ); protected final int menuBarTitleGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleGap", 40 ); /** @since 2.4 */ protected final int menuBarTitleMinimumGap = FlatUIUtils.getUIInt( "TitlePane.menuBarTitleMinimumGap", 12 ); /** @since 2.4 */ protected final int menuBarResizeHeight = FlatUIUtils.getUIInt( "TitlePane.menuBarResizeHeight", 4 ); @@ -205,6 +207,14 @@ public class FlatTitlePane int titleWidth = w - leftWidth - buttonsWidth; int minTitleWidth = UIScale.scale( titleMinimumWidth ); + // increase minimum width if icon is show besides the title + Icon icon = titleLabel.getIcon(); + if( icon != null ) { + Insets iconInsets = iconLabel.getInsets(); + int iconTextGap = titleLabel.getComponentOrientation().isLeftToRight() ? iconInsets.right : iconInsets.left; + minTitleWidth += icon.getIconWidth() + iconTextGap; + } + // if title is too small, reduce width of buttons if( titleWidth < minTitleWidth ) { buttonsWidth = Math.max( buttonsWidth - (minTitleWidth - titleWidth), buttonPanel.getMinimumSize().width ); @@ -394,11 +404,12 @@ public class FlatTitlePane boolean hasIcon = (images != null && !images.isEmpty()); // set icon - iconLabel.setIcon( hasIcon ? new FlatTitlePaneIcon( images, iconSize ) : null ); + iconLabel.setIcon( hasIcon && !showIconBesideTitle ? new FlatTitlePaneIcon( images, iconSize ) : null ); + titleLabel.setIcon( hasIcon && showIconBesideTitle ? new FlatTitlePaneIcon( images, iconSize ) : null ); // show/hide icon - iconLabel.setVisible( hasIcon ); - leftPanel.setBorder( hasIcon ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) ); + iconLabel.setVisible( hasIcon && !showIconBesideTitle ); + leftPanel.setBorder( hasIcon && !showIconBesideTitle ? null : FlatUIUtils.nonUIResource( new FlatEmptyBorder( 0, noIconLeftGap, 0, 0 ) ) ); updateNativeTitleBarHeightAndHitTestSpotsLater(); } @@ -971,11 +982,28 @@ debug*/ } } - String clippedText = super.layoutCL( label, fontMetrics, text, icon, viewR, iconR, textR ); + // compute icon width and gap (if icon is show besides the title) + int iconTextGap = 0; + int iconWidthAndGap = 0; + if( icon != null ) { + Insets iconInsets = iconLabel.getInsets(); + iconTextGap = leftToRight ? iconInsets.right : iconInsets.left; + iconWidthAndGap = icon.getIconWidth() + iconTextGap; + } + // layout title and icon (if show besides the title) + String clippedText = SwingUtilities.layoutCompoundLabel( label, fontMetrics, text, icon, + label.getVerticalAlignment(), label.getHorizontalAlignment(), + label.getVerticalTextPosition(), label.getHorizontalTextPosition(), + viewR, iconR, textR, + iconTextGap ); + + // compute text X location if( !clippedText.equals( text ) ) { // if text is clipped, align to left (or right) - textR.x = leftToRight ? viewR.x : viewR.x + viewR.width - textR.width; + textR.x = leftToRight + ? viewR.x + iconWidthAndGap + : viewR.x + viewR.width - iconWidthAndGap - textR.width; } else { int leadingGap = hasEmbeddedLeadingMenus ? UIScale.scale( menuBarTitleGap - menuBarTitleMinimumGap ) : 0; @@ -984,18 +1012,25 @@ debug*/ // If window is wide enough, center title within window bounds. // Otherwise, center within free space (label bounds). Container parent = label.getParent(); - int centeredTextX = (parent != null) ? ((parent.getWidth() - textR.width) / 2) - label.getX() : -1; + int centeredTextX = (parent != null) ? ((parent.getWidth() - textR.width - iconWidthAndGap) / 2) + iconWidthAndGap - label.getX() : -1; textR.x = (centeredTextX >= viewR.x + leadingGap && centeredTextX + textR.width <= viewR.x + viewR.width - leadingGap) ? centeredTextX - : viewR.x + ((viewR.width - textR.width) / 2); + : viewR.x + ((viewR.width - textR.width - iconWidthAndGap) / 2) + iconWidthAndGap; } else { - // leading aligned with leading gap, which is reduced is space is rare + // leading aligned with leading gap, which is reduced if space is rare textR.x = leftToRight - ? Math.min( viewR.x + leadingGap, viewR.x + viewR.width - textR.width ) - : Math.max( viewR.x + viewR.width - textR.width - leadingGap, viewR.x ); + ? Math.min( viewR.x + leadingGap + iconWidthAndGap, viewR.x + viewR.width - textR.width ) + : Math.max( viewR.x + viewR.width - leadingGap - iconWidthAndGap - textR.width, viewR.x ); } } + // compute icon X location (relative to text X location) + if( icon != null ) { + iconR.x = leftToRight + ? textR.x - iconWidthAndGap + : textR.x + textR.width + iconTextGap; + } + return clippedText; } diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties index f100d89e..5d24952f 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties @@ -795,6 +795,7 @@ TitlePane.buttonMinimumWidth = 30 TitlePane.buttonMaximizedHeight = 22 TitlePane.centerTitle = false TitlePane.centerTitleIfMenuBarEmbedded = true +TitlePane.showIconBesideTitle = false TitlePane.menuBarTitleGap = 40 TitlePane.menuBarTitleMinimumGap = 12 TitlePane.menuBarResizeHeight = 4 diff --git a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt index f0d83179..daeb5837 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt @@ -1240,6 +1240,7 @@ TitlePane.menuBarTitleMinimumGap 12 TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.showIcon true +TitlePane.showIconBesideTitle false TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.titleMinimumWidth 60 TitlePane.unifiedBackground true diff --git a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt index 6a54fd96..f6448d31 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt @@ -1245,6 +1245,7 @@ TitlePane.menuBarTitleMinimumGap 12 TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.showIcon true +TitlePane.showIconBesideTitle false TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.titleMinimumWidth 60 TitlePane.unifiedBackground true diff --git a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt index db0b76c8..7cdc3e39 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt @@ -1273,6 +1273,7 @@ TitlePane.menuBarTitleMinimumGap 12 TitlePane.noIconLeftGap 8 TitlePane.restoreIcon [lazy] 44,30 com.formdev.flatlaf.icons.FlatWindowRestoreIcon [UI] TitlePane.showIcon true +TitlePane.showIconBesideTitle false TitlePane.titleMargins 3,0,3,0 javax.swing.plaf.InsetsUIResource [UI] TitlePane.titleMinimumWidth 60 TitlePane.unifiedBackground true diff --git a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt index 13eaa219..9f0d4025 100644 --- a/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt +++ b/flatlaf-theme-editor/src/main/resources/com/formdev/flatlaf/themeeditor/FlatLafUIKeys.txt @@ -1006,6 +1006,7 @@ TitlePane.menuBarTitleMinimumGap TitlePane.noIconLeftGap TitlePane.restoreIcon TitlePane.showIcon +TitlePane.showIconBesideTitle TitlePane.titleMargins TitlePane.titleMinimumWidth TitlePane.unifiedBackground From 4dad337377656bf14a242ba362b4304c2a98a48f Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Wed, 13 Jul 2022 23:11:32 +0200 Subject: [PATCH 8/8] Window decorations: fixed app icon hit test bounds if icon is shown beside title --- .../com/formdev/flatlaf/ui/FlatTitlePane.java | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java index 97bc6bdf..48301ab9 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTitlePane.java @@ -783,7 +783,7 @@ debug*/ List hitTestSpots = new ArrayList<>(); Rectangle appIconBounds = null; - if( iconLabel.isVisible() ) { + if( !showIconBesideTitle && iconLabel.isVisible() ) { // compute real icon size (without insets; 1px larger for easier hitting) Point location = SwingUtilities.convertPoint( iconLabel, 0, 0, window ); Insets iconInsets = iconLabel.getInsets(); @@ -810,6 +810,38 @@ debug*/ hitTestSpots.add( iconBounds ); else appIconBounds = iconBounds; + } else if( showIconBesideTitle && titleLabel.getIcon() != null && titleLabel.getUI() instanceof FlatTitleLabelUI ) { + FlatTitleLabelUI ui = (FlatTitleLabelUI) titleLabel.getUI(); + + // compute real icon bounds + Insets insets = titleLabel.getInsets(); + Rectangle viewR = new Rectangle( insets.left, insets.top, + titleLabel.getWidth() - insets.left - insets.right, + titleLabel.getHeight() - insets.top - insets.bottom ); + Rectangle iconR = new Rectangle(); + Rectangle textR = new Rectangle(); + ui.layoutCL( titleLabel, titleLabel.getFontMetrics( titleLabel.getFont() ), + titleLabel.getText(), titleLabel.getIcon(), + viewR, iconR, textR ); + + // Windows shows the window system menu only in the upper-left corner + if( iconR.x == 0 ) { + // convert icon location to window coordinates + Point location = SwingUtilities.convertPoint( titleLabel, 0, 0, window ); + iconR.x += location.x; + iconR.y += location.y; + + // make icon bounds 1px larger for easier hitting + iconR.x -= 1; + iconR.y -= 1; + iconR.width += 2; + iconR.height += 2; + + if( hasJBRCustomDecoration() ) + hitTestSpots.add( iconR ); + else + appIconBounds = iconR; + } } Rectangle r = getNativeHitTestSpot( buttonPanel );