From d333d0c9e43337127c85e0f0c0f805e68fdc5117 Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Tue, 31 Aug 2021 14:06:41 +0200 Subject: [PATCH] Accent color: - Demo: added accent color switching to toolbar - added `FlatLaf.setGlobalExtraDefaults()` for easy setting accent color at runtime (issue #233) --- .../java/com/formdev/flatlaf/FlatLaf.java | 41 +++++- .../formdev/flatlaf/util/ColorFunctions.java | 31 ++++ .../flatlaf/FlatIntelliJLaf.properties | 2 +- .../com/formdev/flatlaf/demo/DemoFrame.java | 134 ++++++++++++++++-- .../com/formdev/flatlaf/demo/DemoFrame.jfd | 7 +- .../flatlaf/demo/FlatDarkLaf.properties | 12 ++ .../flatlaf/demo/FlatLightLaf.properties | 12 ++ 7 files changed, 220 insertions(+), 19 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java index 0b29bcdb..10119c2d 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatLaf.java @@ -86,6 +86,7 @@ public abstract class FlatLaf private static final String DESKTOPFONTHINTS = "awt.font.desktophints"; private static List customDefaultsSources; + private static Map globalExtraDefaults; private Map extraDefaults; private String desktopPropertyName; @@ -465,11 +466,14 @@ public abstract class FlatLaf } protected Properties getAdditionalDefaults() { - if( extraDefaults == null ) + if( globalExtraDefaults == null && extraDefaults == null ) return null; Properties properties = new Properties(); - properties.putAll( extraDefaults ); + if( globalExtraDefaults != null ) + properties.putAll( globalExtraDefaults ); + if( extraDefaults != null ) + properties.putAll( extraDefaults ); return properties; } @@ -785,6 +789,38 @@ public abstract class FlatLaf customDefaultsSources.remove( folder ); } + /** + * Gets global extra UI defaults; or {@code null}. + * + * @since 1.6 + */ + public static Map getGlobalExtraDefaults() { + return globalExtraDefaults; + } + + /** + * Sets global extra UI defaults, which are only used when setting up the application look and feel. + * E.g. using {@link UIManager#setLookAndFeel(LookAndFeel)} or {@link #setup(LookAndFeel)}. + *

+ * The global extra defaults are useful for smaller additional defaults that may change. + * E.g. accent color. Otherwise FlatLaf properties files should be used. + * See {@link #registerCustomDefaultsSource(String)}. + *

+ * The keys and values are strings in same format as in FlatLaf properties files. + *

+ * Sample that setups "FlatLaf Light" theme with red accent color: + *

{@code
+	 * FlatLaf.setGlobalExtraDefaults( Collections.singletonMap( "@accentColor", "#f00" ) );
+	 * FlatLightLaf.setup();
+	 * }
+ * + * @see #setExtraDefaults(Map) + * @since 1.6 + */ + public static void setGlobalExtraDefaults( Map globalExtraDefaults ) { + FlatLaf.globalExtraDefaults = globalExtraDefaults; + } + /** * Gets extra UI defaults; or {@code null}. * @@ -811,6 +847,7 @@ public abstract class FlatLaf * FlatLaf.setup( laf ); * } * + * @see #setGlobalExtraDefaults(Map) * @since 1.6 */ public void setExtraDefaults( Map extraDefaults ) { diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ColorFunctions.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ColorFunctions.java index 14fe2ce0..eca7fe3a 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ColorFunctions.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ColorFunctions.java @@ -49,6 +49,9 @@ public class ColorFunctions /** * Returns a color that is a mixture of two colors. + *

+ * This can be used to animate a color change from {@code color1} to {@code color2} + * by invoking this method multiple times with growing {@code weight} (from 0 to 1). * * @param color1 first color * @param color2 second color @@ -79,6 +82,34 @@ public class ColorFunctions Math.round( a2 + ((a1 - a2) * weight) ) ); } + /** + * Mix color with white, which makes the color brighter. + * This is the same as {@link #mix}{@code (Color.white, color, weight)}. + * + * @param color second color + * @param weight the weight (in range 0-1) to mix the two colors. + * Larger weight uses more of first color, smaller weight more of second color. + * @return mixture of colors + * @since 1.6 + */ + public static Color tint( Color color, float weight ) { + return mix( Color.white, color, weight ); + } + + /** + * Mix color with black, which makes the color darker. + * This is the same as {@link #mix}{@code (Color.black, color, weight)}. + * + * @param color second color + * @param weight the weight (in range 0-1) to mix the two colors. + * Larger weight uses more of first color, smaller weight more of second color. + * @return mixture of colors + * @since 1.6 + */ + public static Color shade( Color color, float weight ) { + return mix( Color.black, color, weight ); + } + /** * Calculates the luma (perceptual brightness) of the given color. *

diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatIntelliJLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatIntelliJLaf.properties index 918023b3..bde21c6c 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatIntelliJLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatIntelliJLaf.properties @@ -41,7 +41,7 @@ Button.focusedBackground = null Button.default.background = @accentButtonDefaultBackground -Button.default.foreground = #fff +Button.default.foreground = contrast($Button.default.background,lighten(@foreground,50%),#fff,50%) Button.default.focusedBackground = null Button.default.borderColor = shade($Button.default.background,15%) Button.default.hoverBorderColor = tint($Button.default.background,50%) diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java index 942d2472..22391701 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.java @@ -24,11 +24,16 @@ import java.net.URISyntaxException; import java.time.Year; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.prefs.Preferences; import javax.swing.*; import javax.swing.text.DefaultEditorKit; import javax.swing.text.StyleContext; +import com.formdev.flatlaf.FlatDarculaLaf; +import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatIntelliJLaf; import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.FlatLightLaf; import com.formdev.flatlaf.demo.HintManager.Hint; import com.formdev.flatlaf.demo.extras.*; import com.formdev.flatlaf.demo.intellijthemes.*; @@ -37,9 +42,12 @@ import com.formdev.flatlaf.extras.FlatSVGIcon; import com.formdev.flatlaf.extras.FlatUIDefaultsInspector; import com.formdev.flatlaf.extras.components.FlatButton; import com.formdev.flatlaf.extras.components.FlatButton.ButtonType; +import com.formdev.flatlaf.icons.FlatAbstractIcon; import com.formdev.flatlaf.extras.FlatSVGUtils; import com.formdev.flatlaf.ui.FlatUIUtils; import com.formdev.flatlaf.ui.JBRCustomDecorations; +import com.formdev.flatlaf.util.ColorFunctions; +import com.formdev.flatlaf.util.LoggingFacade; import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.UIScale; import net.miginfocom.layout.ConstraintParser; @@ -65,6 +73,7 @@ class DemoFrame initComponents(); updateFontMenuItems(); + initAccentColors(); controlBar.initialize( this, tabbedPane ); setIconImages( FlatSVGUtils.createWindowIconImages( "/com/formdev/flatlaf/demo/FlatLaf.svg" ) ); @@ -323,6 +332,74 @@ class DemoFrame item.setEnabled( enabled ); } + // the real colors are defined in + // flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/FlatLightLaf.properties and + // flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/FlatDarkLaf.properties + private static String[] accentColorKeys = { + "Demo.accent.blue", "Demo.accent.purple", "Demo.accent.red", + "Demo.accent.orange", "Demo.accent.yellow", "Demo.accent.green", + }; + private final JToggleButton[] accentColorButtons = new JToggleButton[accentColorKeys.length]; + private JLabel accentColorLabel; + + private void initAccentColors() { + accentColorLabel = new JLabel( "Accent color:" ); + + toolBar.add( Box.createHorizontalGlue() ); + toolBar.add( accentColorLabel ); + + ButtonGroup group = new ButtonGroup(); + for( int i = 0; i < accentColorButtons.length; i++ ) { + accentColorButtons[i] = new JToggleButton( new AccentColorIcon( accentColorKeys[i] ) ); + accentColorButtons[i].addActionListener( this::accentColorChanged ); + toolBar.add( accentColorButtons[i] ); + group.add( accentColorButtons[i] ); + } + + accentColorButtons[0].setSelected( true ); + + UIManager.addPropertyChangeListener( e -> { + if( "lookAndFeel".equals( e.getPropertyName() ) ) + updateAccentColorButtons(); + } ); + updateAccentColorButtons(); + } + + private void accentColorChanged( ActionEvent e ) { + String accentColor = accentColorKeys[0]; + for( int i = 0; i < accentColorButtons.length; i++ ) { + if( accentColorButtons[i].isSelected() ) { + accentColor = accentColorKeys[i]; + break; + } + } + + FlatLaf.setGlobalExtraDefaults( (accentColor != accentColorKeys[0]) + ? Collections.singletonMap( "@accentColor", "$" + accentColor ) + : null ); + + Class lafClass = UIManager.getLookAndFeel().getClass(); + try { + FlatLaf.setup( lafClass.newInstance() ); + FlatLaf.updateUI(); + } catch( InstantiationException | IllegalAccessException ex ) { + LoggingFacade.INSTANCE.logSevere( null, ex ); + } + } + + private void updateAccentColorButtons() { + Class lafClass = UIManager.getLookAndFeel().getClass(); + boolean isAccentColorSupported = + lafClass == FlatLightLaf.class || + lafClass == FlatDarkLaf.class || + lafClass == FlatIntelliJLaf.class || + lafClass == FlatDarculaLaf.class; + + accentColorLabel.setEnabled( isAccentColorSupported ); + for( int i = 0; i < accentColorButtons.length; i++ ) + accentColorButtons[i].setEnabled( isAccentColorSupported ); + } + private void initComponents() { // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents JMenuBar menuBar1 = new JMenuBar(); @@ -369,7 +446,7 @@ class DemoFrame JMenuItem showUIDefaultsInspectorMenuItem = new JMenuItem(); JMenu helpMenu = new JMenu(); JMenuItem aboutMenuItem = new JMenuItem(); - JToolBar toolBar1 = new JToolBar(); + toolBar = new JToolBar(); JButton backButton = new JButton(); JButton forwardButton = new JButton(); JButton cutButton = new JButton(); @@ -671,43 +748,43 @@ class DemoFrame } setJMenuBar(menuBar1); - //======== toolBar1 ======== + //======== toolBar ======== { - toolBar1.setMargin(new Insets(3, 3, 3, 3)); + toolBar.setMargin(new Insets(3, 3, 3, 3)); //---- backButton ---- backButton.setToolTipText("Back"); - toolBar1.add(backButton); + toolBar.add(backButton); //---- forwardButton ---- forwardButton.setToolTipText("Forward"); - toolBar1.add(forwardButton); - toolBar1.addSeparator(); + toolBar.add(forwardButton); + toolBar.addSeparator(); //---- cutButton ---- cutButton.setToolTipText("Cut"); - toolBar1.add(cutButton); + toolBar.add(cutButton); //---- copyButton ---- copyButton.setToolTipText("Copy"); - toolBar1.add(copyButton); + toolBar.add(copyButton); //---- pasteButton ---- pasteButton.setToolTipText("Paste"); - toolBar1.add(pasteButton); - toolBar1.addSeparator(); + toolBar.add(pasteButton); + toolBar.addSeparator(); //---- refreshButton ---- refreshButton.setToolTipText("Refresh"); - toolBar1.add(refreshButton); - toolBar1.addSeparator(); + toolBar.add(refreshButton); + toolBar.addSeparator(); //---- showToggleButton ---- showToggleButton.setSelected(true); showToggleButton.setToolTipText("Show Details"); - toolBar1.add(showToggleButton); + toolBar.add(showToggleButton); } - contentPane.add(toolBar1, BorderLayout.NORTH); + contentPane.add(toolBar, BorderLayout.NORTH); //======== contentPanel ======== { @@ -813,8 +890,37 @@ class DemoFrame private JCheckBoxMenuItem underlineMenuSelectionMenuItem; private JCheckBoxMenuItem alwaysShowMnemonicsMenuItem; private JCheckBoxMenuItem animatedLafChangeMenuItem; + private JToolBar toolBar; private JTabbedPane tabbedPane; private ControlBar controlBar; IJThemesPanel themesPanel; // JFormDesigner - End of variables declaration //GEN-END:variables + + //---- class AccentColorIcon ---------------------------------------------- + + private static class AccentColorIcon + extends FlatAbstractIcon + { + private final String colorKey; + + AccentColorIcon( String colorKey ) { + super( 16, 16, null ); + this.colorKey = colorKey; + } + + @Override + protected void paintIcon( Component c, Graphics2D g ) { + Color color = UIManager.getColor( colorKey ); + if( color == null ) + color = Color.lightGray; + else if( !c.isEnabled() ) { + color = FlatLaf.isLafDark() + ? ColorFunctions.shade( color, 0.5f ) + : ColorFunctions.tint( color, 0.6f ); + } + + g.setColor( color ); + g.fillRoundRect( 1, 1, width - 2, height - 2, 5, 5 ); + } + } } diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.jfd b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.jfd index 4087ab97..b74c088c 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.jfd +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/DemoFrame.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "7.0.3.1.342" Java: "16" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.4.0.360" Java: "11.0.11" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -13,8 +13,11 @@ new FormModel { "$locationPolicy": 2 "$sizePolicy": 2 add( new FormContainer( "javax.swing.JToolBar", new FormLayoutManager( class javax.swing.JToolBar ) ) { - name: "toolBar1" + name: "toolBar" "margin": new java.awt.Insets( 3, 3, 3, 3 ) + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } add( new FormComponent( "javax.swing.JButton" ) { name: "backButton" "toolTipText": "Back" diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/FlatDarkLaf.properties b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/FlatDarkLaf.properties index 803f03b8..e87ea1e1 100644 --- a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/FlatDarkLaf.properties +++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/FlatDarkLaf.properties @@ -14,4 +14,16 @@ # limitations under the License. # +#---- Demo.accent ---- + +Demo.accent.blue = #4B6EAF +Demo.accent.purple = #BF5AF2 +Demo.accent.red = #e81123 +Demo.accent.orange = #FF9F0A +Demo.accent.yellow = #FFCC00 +Demo.accent.green = #32D74B + + +#---- HintPanel ---- + HintPanel.backgroundColor = darken(#ffffe1,80%) diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/FlatLightLaf.properties b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/FlatLightLaf.properties index 1b848c29..da420470 100644 --- a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/FlatLightLaf.properties +++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/FlatLightLaf.properties @@ -14,4 +14,16 @@ # limitations under the License. # +#---- Demo.accent ---- + +Demo.accent.blue = #2675BF +Demo.accent.purple = #BF5AF2 +Demo.accent.red = #FF3B30 +Demo.accent.orange = #FF9500 +Demo.accent.yellow = #FFCC00 +Demo.accent.green = #28CD41 + + +#---- HintPanel ---- + HintPanel.backgroundColor = #ffffe1