diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatUIDefaultsInspector.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatUIDefaultsInspector.java index 9849f805..9d7d9c8d 100644 --- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatUIDefaultsInspector.java +++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatUIDefaultsInspector.java @@ -21,11 +21,14 @@ import java.awt.datatransfer.StringSelection; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Locale; +import java.util.Properties; import java.util.Map.Entry; import java.util.Set; import java.util.function.Predicate; @@ -45,6 +48,7 @@ import com.formdev.flatlaf.ui.FlatEmptyBorder; import com.formdev.flatlaf.ui.FlatLineBorder; import com.formdev.flatlaf.ui.FlatMarginBorder; import com.formdev.flatlaf.ui.FlatUIUtils; +import com.formdev.flatlaf.util.DerivedColor; import com.formdev.flatlaf.util.GrayFilter; import com.formdev.flatlaf.util.HSLColor; import com.formdev.flatlaf.util.ScaledEmptyBorder; @@ -72,6 +76,7 @@ public class FlatUIDefaultsInspector private final PropertyChangeListener lafListener = this::lafChanged; private final PropertyChangeListener lafDefaultsListener = this::lafDefaultsChanged; private boolean refreshPending; + private Properties derivedColorKeys; /** * Installs a key listener into the application that allows enabling and disabling @@ -298,6 +303,7 @@ public class FlatUIDefaultsInspector Set> defaultsSet = defaults.entrySet(); ArrayList items = new ArrayList<>( defaultsSet.size() ); HashSet keys = new HashSet<>( defaultsSet.size() ); + Color[] pBaseColor = new Color[1]; for( Entry e : defaultsSet ) { Object key = e.getKey(); @@ -314,6 +320,13 @@ public class FlatUIDefaultsInspector if( !keys.add( key ) ) continue; + // resolve derived color + if( value instanceof DerivedColor ) { + Color resolvedColor = resolveDerivedColor( defaults, (String) key, (DerivedColor) value, pBaseColor ); + if( resolvedColor != value ) + value = new Color[] { resolvedColor, pBaseColor[0], (Color) value }; + } + // check whether key was overridden using UIManager.put(key,value) Object lafValue = null; if( defaults.containsKey( key ) ) @@ -326,6 +339,51 @@ public class FlatUIDefaultsInspector return items.toArray( new Item[items.size()] ); } + private Color resolveDerivedColor( UIDefaults defaults, String key, Color color, Color[] pBaseColor ) { + if( pBaseColor != null ) + pBaseColor[0] = null; + + if( !(color instanceof DerivedColor) ) + return color; + + if( derivedColorKeys == null ) + derivedColorKeys = loadDerivedColorKeys(); + + Object baseKey = derivedColorKeys.get( key ); + if( baseKey == null ) + return color; + + // this is for keys that may be defined as derived colors, but do not derive them at runtime + if( "null".equals( baseKey ) ) + return color; + + Color baseColor = defaults.getColor( baseKey ); + if( baseColor == null ) + return color; + + if( baseColor instanceof DerivedColor ) + baseColor = resolveDerivedColor( defaults, (String) baseKey, baseColor, null ); + + if( pBaseColor != null ) + pBaseColor[0] = baseColor; + + Color newColor = FlatUIUtils.deriveColor( color, baseColor ); + + // creating a new color instance to drop Color.frgbvalue from newColor + // and avoid rounding issues/differences + return new Color( newColor.getRGB(), true ); + } + + private Properties loadDerivedColorKeys() { + Properties properties = new Properties(); + try( InputStream in = getClass().getResourceAsStream( "/com/formdev/flatlaf/extras/resources/DerivedColorKeys.properties" ) ) { + properties.load( in ); + } catch( IOException ex ) { + ex.printStackTrace(); + } + return properties; + } + private static void updateWindowTitle( JFrame frame ) { String title = frame.getTitle(); String sep = " - "; @@ -382,7 +440,7 @@ public class FlatUIDefaultsInspector return "Boolean"; if( value instanceof Border ) return "Border"; - if( value instanceof Color ) + if( value instanceof Color || value instanceof Color[] ) return "Color"; if( value instanceof Dimension ) return "Dimension"; @@ -590,8 +648,8 @@ public class FlatUIDefaultsInspector } static String valueAsString( Object value ) { - if( value instanceof Color ) { - Color color = (Color) value; + if( value instanceof Color || value instanceof Color[] ) { + Color color = (value instanceof Color[]) ? ((Color[])value)[0] : (Color) value; HSLColor hslColor = new HSLColor( color ); if( color.getAlpha() == 255 ) { return String.format( "%s rgb(%d, %d, %d) hsl(%d, %d, %d)", @@ -628,7 +686,7 @@ public class FlatUIDefaultsInspector if( border instanceof FlatLineBorder ) { FlatLineBorder lineBorder = (FlatLineBorder) border; return valueAsString( lineBorder.getUnscaledBorderInsets() ) - + " " + Item.color2hex( lineBorder.getLineColor() ) + + " " + color2hex( lineBorder.getLineColor() ) + " " + lineBorder.getLineThickness() + " " + border.getClass().getName(); } else if( border instanceof EmptyBorder ) { @@ -898,7 +956,7 @@ public class FlatUIDefaultsInspector init( table, item.key, isSelected, row ); // reset background, foreground and icon - if( !(item.value instanceof Color) ) { + if( !(item.value instanceof Color) && !(item.value instanceof Color[]) ) { setBackground( null ); setForeground( null ); } @@ -910,8 +968,8 @@ public class FlatUIDefaultsInspector super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column ); - if( item.value instanceof Color ) { - Color color = (Color) item.value; + if( item.value instanceof Color || item.value instanceof Color[] ) { + Color color = (item.value instanceof Color[]) ? ((Color[])item.value)[0] : (Color) item.value; boolean isDark = new HSLColor( color ).getLuminance() < 70; setBackground( color ); setForeground( isDark ? Color.white : Color.black ); @@ -933,10 +991,31 @@ public class FlatUIDefaultsInspector @Override protected void paintComponent( Graphics g ) { - if( item.value instanceof Color ) { - // fill background - g.setColor( getBackground() ); - g.fillRect( 0, 0, getWidth(), getHeight() ); + if( item.value instanceof Color || item.value instanceof Color[] ) { + int width = getWidth(); + int height = getHeight(); + Color background = getBackground(); + + // paint color + fillRect( g, background, 0, 0, width, height ); + + if( item.value instanceof Color[] ) { + // paint base color + int width2 = height * 2; + fillRect( g, ((Color[])item.value)[1], width - width2, 0, width2, height ); + + // paint default color + Color defaultColor = ((Color[])item.value)[2]; + if( defaultColor != null && !defaultColor.equals( background ) ) { + int width3 = height / 2; + fillRect( g, defaultColor, width - width3, 0, width3, height ); + } + + // paint "derived color" indicator + int width4 = height / 4; + g.setColor( Color.magenta ); + g.fillRect( width - width4, 0, width4, height ); + } // layout text FontMetrics fm = getFontMetrics( getFont() ); @@ -967,6 +1046,17 @@ public class FlatUIDefaultsInspector paintSeparator( g ); } + + private void fillRect( Graphics g, Color color, int x, int y, int width, int height ) { + // fill white if color is translucent + if( color.getAlpha() != 255 ) { + g.setColor( Color.white ); + g.fillRect( x, y, width, height ); + } + + g.setColor( color ); + g.fillRect( x, y, width, height ); + } } //---- class SafeIcon ----------------------------------------------------- diff --git a/flatlaf-extras/src/main/module-info/module-info.java b/flatlaf-extras/src/main/module-info/module-info.java index 3fef1e03..a62e610b 100644 --- a/flatlaf-extras/src/main/module-info/module-info.java +++ b/flatlaf-extras/src/main/module-info/module-info.java @@ -25,4 +25,6 @@ module com.formdev.flatlaf.extras { exports com.formdev.flatlaf.extras; exports com.formdev.flatlaf.extras.components; + + opens com.formdev.flatlaf.extras.resources; } diff --git a/flatlaf-extras/src/main/resources/com/formdev/flatlaf/extras/resources/DerivedColorKeys.properties b/flatlaf-extras/src/main/resources/com/formdev/flatlaf/extras/resources/DerivedColorKeys.properties new file mode 100644 index 00000000..ca54c452 --- /dev/null +++ b/flatlaf-extras/src/main/resources/com/formdev/flatlaf/extras/resources/DerivedColorKeys.properties @@ -0,0 +1,212 @@ +# +# Copyright 2021 FormDev Software GmbH +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# +# This file lists UI keys of colors, which are computed at runtime. +# +# Colors marked as `derived` in properties files are computed at runtime based on +# a variable base color (usually the current background color of a component). +# This works only for a limited set of UI keys. +# +# The property key is the UI key of a color, which is computed at runtime. +# The property value is the UI key of the base color. +# +# This file is not used at runtime. +# It is only used in tooling (e.g. UI Defaults Inspector or UIDefaultsDump). +# + + +#---- system colors ---- + +scrollbar = null + + +#---- Button ---- + +Button.focusedBackground = Button.background +Button.hoverBackground = Button.background +Button.pressedBackground = Button.background +Button.selectedBackground = Button.background +Button.disabledBackground = Button.background +Button.disabledSelectedBackground = Button.background + +Button.default.focusedBackground = Button.default.background +Button.default.hoverBackground = Button.default.background +Button.default.pressedBackground = Button.default.background + +Button.toolbar.hoverBackground = Button.background +Button.toolbar.pressedBackground = Button.background +Button.toolbar.selectedBackground = Button.background + + +#---- CheckBox ---- + +CheckBox.icon.disabledBackground = CheckBox.icon.background +CheckBox.icon.focusedBackground = CheckBox.icon.background +CheckBox.icon.hoverBackground = CheckBox.icon.background +CheckBox.icon.pressedBackground = CheckBox.icon.background +CheckBox.icon.selectedFocusedBackground = CheckBox.icon.selectedBackground +CheckBox.icon.selectedHoverBackground = CheckBox.icon.selectedBackground +CheckBox.icon.selectedPressedBackground = CheckBox.icon.selectedBackground + +CheckBox.icon[filled].disabledBackground = CheckBox.icon[filled].background +CheckBox.icon[filled].focusedBackground = CheckBox.icon[filled].background +CheckBox.icon[filled].hoverBackground = CheckBox.icon[filled].background +CheckBox.icon[filled].pressedBackground = CheckBox.icon[filled].background +CheckBox.icon[filled].selectedFocusedBackground = CheckBox.icon[filled].selectedBackground +CheckBox.icon[filled].selectedHoverBackground = CheckBox.icon[filled].selectedBackground +CheckBox.icon[filled].selectedPressedBackground = CheckBox.icon[filled].selectedBackground + + +#---- CheckBoxMenuItem ---- + +CheckBoxMenuItem.selectionBackground = CheckBoxMenuItem.background + + +#---- ComboBox ---- + +ComboBox.buttonDisabledArrowColor = ComboBox.buttonArrowColor +ComboBox.buttonHoverArrowColor = ComboBox.buttonArrowColor +ComboBox.buttonPressedArrowColor = ComboBox.buttonArrowColor + + +#---- Component ---- + +Component.custom.borderColor = null + + +#---- HelpButton ---- + +HelpButton.disabledBackground = HelpButton.background +HelpButton.focusedBackground = HelpButton.background +HelpButton.hoverBackground = HelpButton.background +HelpButton.pressedBackground = HelpButton.background + + +#---- InternalFrame ---- + +InternalFrame.buttonHoverBackground = InternalFrame.activeTitleBackground +InternalFrame.buttonPressedBackground = InternalFrame.activeTitleBackground + + +#---- Menu ---- + +Menu.selectionBackground = Menu.background + + +#---- MenuBar ---- + +MenuBar.hoverBackground = Menu.background +MenuBar.underlineSelectionBackground = Menu.background + + +#---- MenuItem ---- + +MenuItem.checkBackground = MenuItem.selectionBackground +MenuItem.underlineSelectionBackground = MenuItem.background +MenuItem.underlineSelectionCheckBackground = MenuItem.selectionBackground +MenuItem.selectionBackground = MenuItem.background + + +#---- RadioButtonMenuItem ---- + +RadioButtonMenuItem.selectionBackground = RadioButtonMenuItem.background + + +#---- RootPane ---- + +RootPane.activeBorderColor = Panel.background +RootPane.inactiveBorderColor = Panel.background + + +#---- ScrollBar ---- + +ScrollBar.track = ScrollBar.background +ScrollBar.thumb = ScrollBar.track +ScrollBar.hoverTrackColor = ScrollBar.track +ScrollBar.hoverThumbColor = ScrollBar.thumb +ScrollBar.pressedTrackColor = ScrollBar.track +ScrollBar.pressedThumbColor = ScrollBar.thumb + +ScrollBar.buttonDisabledArrowColor = ScrollBar.buttonArrowColor +ScrollBar.hoverButtonBackground = ScrollBar.background +ScrollBar.pressedButtonBackground = ScrollBar.background + + +#---- ScrollPane ---- + +ScrollPane.background = null + + +#---- Slider ---- + +Slider.focusedColor = Component.focusColor +Slider.hoverThumbColor = Slider.thumbColor +Slider.pressedThumbColor = Slider.thumbColor +Slider.disabledThumbColor = Slider.thumbColor + + +#---- Spinner ---- + +Spinner.buttonDisabledArrowColor = Spinner.buttonArrowColor +Spinner.buttonHoverArrowColor = Spinner.buttonArrowColor +Spinner.buttonPressedArrowColor = Spinner.buttonArrowColor + + +#---- SplitPaneDivider ---- + +SplitPaneDivider.oneTouchHoverArrowColor = SplitPaneDivider.oneTouchArrowColor +SplitPaneDivider.oneTouchPressedArrowColor = SplitPaneDivider.oneTouchArrowColor + + +#---- TabbedPane ---- + +TabbedPane.selectedBackground = TabbedPane.background +TabbedPane.hoverColor = TabbedPane.background +TabbedPane.focusColor = TabbedPane.background + +TabbedPane.closeBackground = TabbedPane.background +TabbedPane.closeHoverBackground = TabbedPane.background +TabbedPane.closePressedBackground = TabbedPane.background + +TabbedPane.closeForeground = TabbedPane.foreground +TabbedPane.closeHoverForeground = TabbedPane.foreground +TabbedPane.closePressedForeground = TabbedPane.foreground + +TabbedPane.buttonHoverBackground = TabbedPane.background +TabbedPane.buttonPressedBackground = TabbedPane.background + + +#---- TitlePane ---- + +TitlePane.buttonHoverBackground = TitlePane.background +TitlePane.buttonPressedBackground = TitlePane.background + + +#---- ToggleButton ---- + +ToggleButton.focusedBackground = ToggleButton.background +ToggleButton.hoverBackground = ToggleButton.background +ToggleButton.pressedBackground = ToggleButton.background +ToggleButton.selectedBackground = ToggleButton.background +ToggleButton.disabledBackground = ToggleButton.background +ToggleButton.disabledSelectedBackground = ToggleButton.background + +ToggleButton.toolbar.hoverBackground = ToggleButton.background +ToggleButton.toolbar.pressedBackground = ToggleButton.background +ToggleButton.toolbar.selectedBackground = ToggleButton.background + +ToggleButton.tab.hoverBackground = null 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 2f1ed206..6757cb3c 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 @@ -381,6 +381,9 @@ MenuBar.highlight MenuBar.hoverBackground MenuBar.itemMargins MenuBar.shadow +MenuBar.underlineSelectionBackground +MenuBar.underlineSelectionColor +MenuBar.underlineSelectionHeight MenuBar.windowBindings MenuBarUI MenuItem.acceleratorArrowGap @@ -566,6 +569,7 @@ ScrollBar.minimumThumbSize ScrollBar.pressedButtonBackground ScrollBar.pressedThumbColor ScrollBar.pressedThumbWithTrack +ScrollBar.pressedTrackColor ScrollBar.showButtons ScrollBar.squareButtons ScrollBar.thumb @@ -699,6 +703,8 @@ TabbedPane.labelShift TabbedPane.light TabbedPane.scrollButtonsPlacement TabbedPane.scrollButtonsPolicy +TabbedPane.selectedBackground +TabbedPane.selectedForeground TabbedPane.selectedLabelShift TabbedPane.selectedTabPadInsets TabbedPane.selectionFollowsFocus