From a02784fcba5e0c5ebddd1a05ab1205cddc0b77ca Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Tue, 12 Nov 2019 11:53:39 +0100 Subject: [PATCH] IntelliJ Themes: fixes for Button, CheckBox, RadioButton, ComboBox and Spinner --- .../com/formdev/flatlaf/IntelliJTheme.java | 95 ++++++++++++++++--- .../flatlaf/icons/FlatCheckBoxIcon.java | 4 +- .../flatlaf/IntelliJTheme$ThemeLaf.properties | 8 ++ 3 files changed, 95 insertions(+), 12 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java index b7236c4f..87873752 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/IntelliJTheme.java @@ -34,6 +34,17 @@ import com.formdev.flatlaf.json.ParseException; import com.formdev.flatlaf.util.StringUtils; /** + * This class supports loading IntelliJ .theme.json files and using them as a Laf. + * + * .theme.json files are used by Theme plugins for IntelliJ IDEA and other + * JetBrains IDEs that are based on IntelliJ platform. + * + * Here you can find IntelliJ Theme plugins: + * https://plugins.jetbrains.com/search?tags=Theme + * + * The IntelliJ .theme.json file are documented here: + * http://www.jetbrains.org/intellij/sdk/docs/reference_guide/ui_themes/themes_customize.html + * * @author Karl Tauber */ public class IntelliJTheme @@ -48,28 +59,49 @@ public class IntelliJTheme private Map namedColors = Collections.emptyMap(); - public static boolean install( InputStream in ) - throws IOException, ParseException - { + /** + * Loads a IntelliJ .theme.json file from the given input stream, + * creates a Laf instance for it and installs it. + * + * The input stream is automatically closed. + * Using a buffered input stream is not necessary. + */ + public static boolean install( InputStream in ) { try { return FlatLaf.install( createLaf( in ) ); } catch( Exception ex ) { - System.err.println( "Failed to initialize look and feel" ); + System.err.println( "Failed to load IntelliJ theme" ); ex.printStackTrace(); return false; } } + /** + * Loads a IntelliJ .theme.json file from the given input stream and + * creates a Laf instance for it. + * + * The input stream is automatically closed. + * Using a buffered input stream is not necessary. + */ public static FlatLaf createLaf( InputStream in ) throws IOException, ParseException { return createLaf( new IntelliJTheme( in ) ); } + /** + * Creates a Laf instance for the given IntelliJ theme. + */ public static FlatLaf createLaf( IntelliJTheme theme ) { return new ThemeLaf( theme ); } + /** + * Loads a IntelliJ .theme.json file from the given input stream. + * + * The input stream is automatically closed. + * Using a buffered input stream is not necessary. + */ @SuppressWarnings( "unchecked" ) public IntelliJTheme( InputStream in ) throws IOException, ParseException @@ -100,6 +132,11 @@ public class IntelliJTheme apply( e.getKey(), e.getValue(), defaults, defaultsKeysCache ); applyCheckBoxColors( defaults ); + + // Spinner arrow button always has same colors as ComboBox arrow button + defaults.put( "Spinner.buttonBackground", defaults.get( "ComboBox.buttonBackground" ) ); + defaults.put( "Spinner.buttonArrowColor", defaults.get( "ComboBox.buttonArrowColor" ) ); + defaults.put( "Spinner.buttonDisabledArrowColor", defaults.get( "ComboBox.buttonDisabledArrowColor" ) ); } /** @@ -137,6 +174,9 @@ public class IntelliJTheme else if( key.endsWith( ".endBackground" ) || key.endsWith( ".endBorderColor" ) ) return; // ignore + // map keys + key = uiKeyMapping.getOrDefault( key, key ); + String valueStr = value.toString(); // map named colors @@ -203,7 +243,21 @@ public class IntelliJTheme } } - // removed colors that Darcula does not provide + // When Darcula replaces colors in SVGs it uses color values and not the keys + // from com.intellij.ide.ui.UITheme.colorPalette, but there are some keys that + // have same color value: + // - Checkbox.Background.Default.Dark has same color as Checkbox.Background.Selected.Dark + // - Checkbox.Border.Default.Dark has same color as Checkbox.Border.Selected.Dark + // - Checkbox.Focus.Thin.Default.Dark has same color as Checkbox.Focus.Thin.Selected.Dark + // + // So if only e.g. Checkbox.Background.Default.Dark is specified in .theme.json, + // then this color is also used for Checkbox.Background.Selected.Dark. + // Occurs e.g. in "Dark purple" theme. + fixCheckBoxColor( defaults, colorPalette, "Checkbox.Background.Default.Dark", "Checkbox.Background.Selected.Dark" ); + fixCheckBoxColor( defaults, colorPalette, "Checkbox.Border.Default.Dark", "Checkbox.Border.Selected.Dark" ); + fixCheckBoxColor( defaults, colorPalette, "Checkbox.Focus.Thin.Default.Dark", "Checkbox.Focus.Thin.Selected.Dark" ); + + // remove hover and pressed colors if( checkboxModified ) { defaults.remove( "CheckBox.icon.hoverBorderColor" ); defaults.remove( "CheckBox.icon.focusedBackground" ); @@ -214,18 +268,37 @@ public class IntelliJTheme } } + private void fixCheckBoxColor( UIDefaults defaults, Map colorPalette, String key1, String key2 ) { + if( colorPalette.containsKey( key1 ) == colorPalette.containsKey( key2 ) ) + return; + + String newKey1 = checkboxKeyMapping.get( StringUtils.removeTrailing( key1, ".Dark" ) ); + String newKey2 = checkboxKeyMapping.get( StringUtils.removeTrailing( key2, ".Dark" ) ); + if( colorPalette.containsKey( key1 ) ) + defaults.put( newKey2, defaults.get( newKey1 ) ); + else + defaults.put( newKey1, defaults.get( newKey2 ) ); + } + + private static Map uiKeyMapping = new HashMap<>(); private static Map checkboxKeyMapping = new HashMap<>(); static { - checkboxKeyMapping.put( "Checkbox.Background.Default", "CheckBox.icon.background" ); + // ComboBox + uiKeyMapping.put( "ComboBox.ArrowButton.background", "ComboBox.buttonEditableBackground" ); + uiKeyMapping.put( "ComboBox.ArrowButton.disabledIconColor", "ComboBox.buttonDisabledArrowColor" ); + uiKeyMapping.put( "ComboBox.ArrowButton.iconColor", "ComboBox.buttonArrowColor" ); + uiKeyMapping.put( "ComboBox.ArrowButton.nonEditableBackground", "ComboBox.buttonBackground" ); + + checkboxKeyMapping.put( "Checkbox.Background.Default", "CheckBox.icon.background" ); checkboxKeyMapping.put( "Checkbox.Background.Disabled", "CheckBox.icon.disabledBackground" ); - checkboxKeyMapping.put( "Checkbox.Border.Default", "CheckBox.icon.borderColor" ); - checkboxKeyMapping.put( "Checkbox.Border.Disabled", "CheckBox.icon.disabledBorderColor" ); - checkboxKeyMapping.put( "Checkbox.Focus.Thin.Default", "CheckBox.icon.focusedBorderColor" ); - checkboxKeyMapping.put( "Checkbox.Focus.Wide", "Button.default.focusColor" ); + checkboxKeyMapping.put( "Checkbox.Border.Default", "CheckBox.icon.borderColor" ); + checkboxKeyMapping.put( "Checkbox.Border.Disabled", "CheckBox.icon.disabledBorderColor" ); + checkboxKeyMapping.put( "Checkbox.Focus.Thin.Default", "CheckBox.icon.focusedBorderColor" ); + checkboxKeyMapping.put( "Checkbox.Focus.Wide", "CheckBox.icon.focusedColor" ); checkboxKeyMapping.put( "Checkbox.Foreground.Disabled", "CheckBox.icon.disabledCheckmarkColor" ); checkboxKeyMapping.put( "Checkbox.Background.Selected", "CheckBox.icon.selectedBackground" ); - checkboxKeyMapping.put( "Checkbox.Border.Selected", "CheckBox.icon.selectedBorderColor" ); + checkboxKeyMapping.put( "Checkbox.Border.Selected", "CheckBox.icon.selectedBorderColor" ); checkboxKeyMapping.put( "Checkbox.Foreground.Selected", "CheckBox.icon.checkmarkColor" ); checkboxKeyMapping.put( "Checkbox.Focus.Thin.Selected", "CheckBox.icon.selectedFocusedBorderColor" ); } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java index a5dc4236..0d20061c 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatCheckBoxIcon.java @@ -38,6 +38,7 @@ import com.formdev.flatlaf.ui.FlatUIUtils; * * @uiDefault Component.focusWidth int * @uiDefault Component.focusColor Color + * @uiDefault CheckBox.icon.focusedColor Color optional; defaults to Component.focusColor * @uiDefault CheckBox.icon.borderColor Color * @uiDefault CheckBox.icon.disabledBorderColor Color * @uiDefault CheckBox.icon.selectedBorderColor Color @@ -62,7 +63,8 @@ public class FlatCheckBoxIcon extends FlatAbstractIcon { protected final int focusWidth = UIManager.getInt( "Component.focusWidth" ); - protected final Color focusColor = UIManager.getColor( "Component.focusColor" ); + protected final Color focusColor = FlatUIUtils.getUIColor( "CheckBox.icon.focusedColor", + UIManager.getColor( "Component.focusColor" ) ); protected final int arc = FlatUIUtils.getUIInt( "CheckBox.arc", 2 ); protected final Color borderColor = UIManager.getColor( "CheckBox.icon.borderColor" ); diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/IntelliJTheme$ThemeLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/IntelliJTheme$ThemeLaf.properties index 5363acb4..c26c896e 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/IntelliJTheme$ThemeLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/IntelliJTheme$ThemeLaf.properties @@ -13,3 +13,11 @@ # See the License for the specific language governing permissions and # limitations under the License. # + +#---- Button ---- + +Button.hoverBorderColor=null +Button.default.hoverBorderColor=null + +Button.default.hoverBackground=@buttonHoverBackground +Button.default.pressedBackground=@buttonPressedBackground