From f456185f7dab500409e4a46d9623ae9c245b8b9f Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Mon, 5 Apr 2021 14:19:41 +0200 Subject: [PATCH] Native window decorations: support changing title bar background and foreground colors per window (via client property) --- CHANGELOG.md | 8 +++- .../formdev/flatlaf/FlatClientProperties.java | 20 ++++++++++ .../formdev/flatlaf/ui/FlatRootPaneUI.java | 6 +++ .../com/formdev/flatlaf/ui/FlatTitlePane.java | 20 ++++++++-- .../testing/FlatWindowDecorationsTest.java | 40 ++++++++++++++----- .../testing/FlatWindowDecorationsTest.jfd | 34 ++++++++++------ 6 files changed, 99 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bf82237..d222ca45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ FlatLaf Change Log supports window decorations (`FlatLaf.supportsNativeWindowDecorations()`) and to toggle window decorations of all windows (`FlatLaf.setUseNativeWindowDecorations(boolean)`). +- Native window decorations: Support changing title bar background and + foreground colors per window. (set client properties + `JRootPane.titleBarBackground` and `JRootPane.titleBarForeground` on root pane + to a `java.awt.Color`). #### Fixed bugs @@ -30,8 +34,8 @@ FlatLaf Change Log #### New features and improvements - Native window decorations: Support disabling native window decorations per - window. (set client property `JRootPane.useWindowDecorations` to `false` on - root pane). + window. (set client property `JRootPane.useWindowDecorations` on root pane to + `false`). - Support running on WinPE. (issue #279) #### Fixed bugs diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java index 3475d114..76d88675 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java @@ -269,6 +269,26 @@ public interface FlatClientProperties */ String MENU_BAR_EMBEDDED = "JRootPane.menuBarEmbedded"; + /** + * Background color of window title bar (requires enabled window decorations). + *

+ * Component {@link javax.swing.JRootPane}
+ * Value type {@link java.awt.Color} + * + * @since 1.1.2 + */ + String TITLE_BAR_BACKGROUND = "JRootPane.titleBarBackground"; + + /** + * Foreground color of window title bar (requires enabled window decorations). + *

+ * Component {@link javax.swing.JRootPane}
+ * Value type {@link java.awt.Color} + * + * @since 1.1.2 + */ + String TITLE_BAR_FOREGROUND = "JRootPane.titleBarForeground"; + //---- JScrollBar / JScrollPane ------------------------------------------- /** 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 a484b6cd..286467c6 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 @@ -256,6 +256,12 @@ public class FlatRootPaneUI rootPane.repaint(); } break; + + case FlatClientProperties.TITLE_BAR_BACKGROUND: + case FlatClientProperties.TITLE_BAR_FOREGROUND: + if( titlePane != null ) + titlePane.titleBarColorsChanged(); + break; } } 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 2b423a06..15565d06 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,10 +265,17 @@ public class FlatTitlePane } protected void activeChanged( boolean active ) { - boolean hasEmbeddedMenuBar = hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() ); - Color background = FlatUIUtils.nonUIResource( active ? activeBackground : inactiveBackground ); - Color foreground = FlatUIUtils.nonUIResource( active ? activeForeground : inactiveForeground ); - Color titleForeground = (hasEmbeddedMenuBar && active) ? FlatUIUtils.nonUIResource( embeddedForeground ) : foreground; + Color background = FlatClientProperties.clientPropertyColor( rootPane, FlatClientProperties.TITLE_BAR_BACKGROUND, null ); + Color foreground = FlatClientProperties.clientPropertyColor( rootPane, FlatClientProperties.TITLE_BAR_FOREGROUND, null ); + Color titleForeground = foreground; + if( background == null ) + background = FlatUIUtils.nonUIResource( active ? activeBackground : inactiveBackground ); + if( foreground == null ) { + foreground = FlatUIUtils.nonUIResource( active ? activeForeground : inactiveForeground ); + titleForeground = (active && hasVisibleEmbeddedMenuBar( rootPane.getJMenuBar() )) + ? FlatUIUtils.nonUIResource( embeddedForeground ) + : foreground; + } setBackground( background ); titleLabel.setForeground( titleForeground ); @@ -480,6 +487,11 @@ public class FlatTitlePane return null; } + protected void titleBarColorsChanged() { + activeChanged( window == null || window.isActive() ); + repaint(); + } + protected void menuBarChanged() { menuBarPlaceholder.invalidate(); 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 856058ae..54f6704c 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 @@ -170,6 +170,16 @@ public class FlatWindowDecorationsTest } } + private void colorizeTitleBar() { + JRootPane rootPane = getWindowRootPane(); + if( rootPane == null ) + return; + + boolean colorize = colorizeTitleBarCheckBox.isSelected(); + rootPane.putClientProperty( FlatClientProperties.TITLE_BAR_BACKGROUND, colorize ? Color.green : null ); + rootPane.putClientProperty( FlatClientProperties.TITLE_BAR_FOREGROUND, colorize ? Color.blue : null ); + } + private void colorizeMenuBar() { boolean colorize = colorizeMenuBarCheckBox.isSelected(); Color menuBarBackground = colorize ? new Color( 0xffccff ) : UIManager.getColor( "MenuBar.background" ); @@ -372,6 +382,7 @@ public class FlatWindowDecorationsTest colorizeMenuBarCheckBox = new JCheckBox(); unifiedBackgroundCheckBox = new JCheckBox(); colorizeMenusCheckBox = new JCheckBox(); + colorizeTitleBarCheckBox = new JCheckBox(); resizableCheckBox = new JCheckBox(); maximizedBoundsCheckBox = new JCheckBox(); undecoratedCheckBox = new JCheckBox(); @@ -432,6 +443,7 @@ public class FlatWindowDecorationsTest "para[]0" + "[]0" + "[]0" + + "[]0" + "[]unrel" + "[]0" + "[]unrel" + @@ -488,7 +500,7 @@ public class FlatWindowDecorationsTest changeTitleButton.addActionListener(e -> changeTitle()); panel3.add(changeTitleButton, "cell 0 4"); } - add(panel3, "cell 2 0 1 7,aligny top,growy 0"); + add(panel3, "cell 2 0 1 8,aligny top,growy 0"); //---- menuBarEmbeddedCheckBox ---- menuBarEmbeddedCheckBox.setText("embedded menu bar"); @@ -522,34 +534,39 @@ public class FlatWindowDecorationsTest colorizeMenusCheckBox.addActionListener(e -> colorizeMenus()); add(colorizeMenusCheckBox, "cell 1 3"); + //---- colorizeTitleBarCheckBox ---- + colorizeTitleBarCheckBox.setText("colorize title bar"); + colorizeTitleBarCheckBox.addActionListener(e -> colorizeTitleBar()); + add(colorizeTitleBarCheckBox, "cell 0 4"); + //---- resizableCheckBox ---- resizableCheckBox.setText("resizable"); resizableCheckBox.setSelected(true); resizableCheckBox.addActionListener(e -> resizableChanged()); - add(resizableCheckBox, "cell 0 4"); + add(resizableCheckBox, "cell 0 5"); //---- maximizedBoundsCheckBox ---- maximizedBoundsCheckBox.setText("maximized bounds (50,100, 1000,700)"); maximizedBoundsCheckBox.addActionListener(e -> maximizedBoundsChanged()); - add(maximizedBoundsCheckBox, "cell 1 4"); + add(maximizedBoundsCheckBox, "cell 1 5"); //---- undecoratedCheckBox ---- undecoratedCheckBox.setText("undecorated"); undecoratedCheckBox.addActionListener(e -> undecoratedChanged()); - add(undecoratedCheckBox, "cell 0 5"); + add(undecoratedCheckBox, "cell 0 6"); //---- fullScreenCheckBox ---- fullScreenCheckBox.setText("full screen"); fullScreenCheckBox.addActionListener(e -> fullScreenChanged()); - add(fullScreenCheckBox, "cell 1 5"); + add(fullScreenCheckBox, "cell 1 6"); //---- label1 ---- label1.setText("Style:"); - add(label1, "cell 0 6"); + add(label1, "cell 0 7"); //---- label2 ---- label2.setText("Icon:"); - add(label2, "cell 1 6"); + add(label2, "cell 1 7"); //======== panel1 ======== { @@ -614,7 +631,7 @@ public class FlatWindowDecorationsTest styleFileChooserRadioButton.addActionListener(e -> decorationStyleChanged()); panel1.add(styleFileChooserRadioButton, "cell 0 8"); } - add(panel1, "cell 0 7"); + add(panel1, "cell 0 8"); //======== panel2 ======== { @@ -643,18 +660,18 @@ public class FlatWindowDecorationsTest iconTestRandomRadioButton.addActionListener(e -> iconChanged()); panel2.add(iconTestRandomRadioButton, "cell 0 2"); } - add(panel2, "cell 1 7"); + add(panel2, "cell 1 8"); //---- openDialogButton ---- openDialogButton.setText("Open Dialog"); openDialogButton.addActionListener(e -> openDialog()); - add(openDialogButton, "cell 0 8 2 1"); + add(openDialogButton, "cell 0 9 2 1"); //---- openFrameButton ---- openFrameButton.setText("Open Frame"); openFrameButton.setMnemonic('A'); openFrameButton.addActionListener(e -> openFrame()); - add(openFrameButton, "cell 0 8 2 1"); + add(openFrameButton, "cell 0 9 2 1"); //======== menuBar ======== { @@ -858,6 +875,7 @@ public class FlatWindowDecorationsTest private JCheckBox colorizeMenuBarCheckBox; private JCheckBox unifiedBackgroundCheckBox; private JCheckBox colorizeMenusCheckBox; + private JCheckBox colorizeTitleBarCheckBox; private JCheckBox resizableCheckBox; private JCheckBox maximizedBoundsCheckBox; private JCheckBox undecoratedCheckBox; diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd index 02c0167c..113f6d11 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd @@ -9,7 +9,7 @@ new FormModel { add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "ltr,insets dialog,hidemode 3" "$columnConstraints": "[left]para[left][fill]" - "$rowConstraints": "para[]0[]0[]0[]unrel[]0[]unrel[][top][]" + "$rowConstraints": "para[]0[]0[]0[]0[]unrel[]0[]unrel[][top][]" } ) { name: "this" add( new FormComponent( "javax.swing.JCheckBox" ) { @@ -90,7 +90,7 @@ new FormModel { "value": "cell 0 4" } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 2 0 1 7,aligny top,growy 0" + "value": "cell 2 0 1 8,aligny top,growy 0" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "menuBarEmbeddedCheckBox" @@ -154,6 +154,16 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 3" } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "colorizeTitleBarCheckBox" + "text": "colorize title bar" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "colorizeTitleBar", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 4" + } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "resizableCheckBox" "text": "resizable" @@ -163,7 +173,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "resizableChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 4" + "value": "cell 0 5" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "maximizedBoundsCheckBox" @@ -173,7 +183,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "maximizedBoundsChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 4" + "value": "cell 1 5" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "undecoratedCheckBox" @@ -183,7 +193,7 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "undecoratedChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 5" + "value": "cell 0 6" } ) add( new FormComponent( "javax.swing.JCheckBox" ) { name: "fullScreenCheckBox" @@ -193,19 +203,19 @@ new FormModel { } addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "fullScreenChanged", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 5" + "value": "cell 1 6" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "label1" "text": "Style:" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 6" + "value": "cell 0 7" } ) add( new FormComponent( "javax.swing.JLabel" ) { name: "label2" "text": "Icon:" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 6" + "value": "cell 1 7" } ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$columnConstraints": "[fill]" @@ -314,7 +324,7 @@ new FormModel { "value": "cell 0 8" } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 7" + "value": "cell 0 8" } ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$columnConstraints": "[fill]" @@ -357,14 +367,14 @@ new FormModel { "value": "cell 0 2" } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 1 7" + "value": "cell 1 8" } ) add( new FormComponent( "javax.swing.JButton" ) { name: "openDialogButton" "text": "Open Dialog" addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "openDialog", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 8 2 1" + "value": "cell 0 9 2 1" } ) add( new FormComponent( "javax.swing.JButton" ) { name: "openFrameButton" @@ -372,7 +382,7 @@ new FormModel { "mnemonic": 65 addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "openFrame", false ) ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { - "value": "cell 0 8 2 1" + "value": "cell 0 9 2 1" } ) }, new FormLayoutConstraints( null ) { "location": new java.awt.Point( 0, 0 )