From a51294d57082538a33e3b45e8c8182486660aecf Mon Sep 17 00:00:00 2001 From: Karl Tauber Date: Sat, 12 Jun 2021 20:46:59 +0200 Subject: [PATCH] TextComponents: - use focusedBackground only if editable (and enabled) - prefer explicit set background color over focusedBackground - added FlatTextFieldUI.getBackground() used by all text components - support EditorPane.focusedBackground - support TextPane.focusedBackground (issue #335) --- .../formdev/flatlaf/ui/FlatEditorPaneUI.java | 40 ++++++++++++++----- .../flatlaf/ui/FlatFormattedTextFieldUI.java | 1 + .../flatlaf/ui/FlatPasswordFieldUI.java | 7 +++- .../formdev/flatlaf/ui/FlatTextAreaUI.java | 38 ++++++++++-------- .../formdev/flatlaf/ui/FlatTextFieldUI.java | 30 ++++++++++---- .../formdev/flatlaf/ui/FlatTextPaneUI.java | 38 +++++++++++++----- .../com/formdev/flatlaf/ui/FlatUIUtils.java | 10 +++-- .../flatlaf/swingx/ui/FlatDatePickerUI.java | 2 +- .../flatlaf/testing/FlatTestLaf.properties | 30 ++++++++++++++ 9 files changed, 146 insertions(+), 50 deletions(-) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEditorPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEditorPaneUI.java index 454840345..0179e60d5 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEditorPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatEditorPaneUI.java @@ -17,15 +17,16 @@ package com.formdev.flatlaf.ui; import static com.formdev.flatlaf.util.UIScale.scale; +import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.event.FocusListener; import java.beans.PropertyChangeEvent; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicEditorPaneUI; import javax.swing.text.JTextComponent; import com.formdev.flatlaf.FlatClientProperties; @@ -53,6 +54,7 @@ * * @uiDefault Component.minimumWidth int * @uiDefault Component.isIntelliJTheme boolean + * @uiDefault EditorPane.focusedBackground Color optional * * @author Karl Tauber */ @@ -61,8 +63,10 @@ public class FlatEditorPaneUI { protected int minimumWidth; protected boolean isIntelliJTheme; + protected Color focusedBackground; private Object oldHonorDisplayProperties; + private FocusListener focusListener; public static ComponentUI createUI( JComponent c ) { return new FlatEditorPaneUI(); @@ -72,8 +76,10 @@ public static ComponentUI createUI( JComponent c ) { protected void installDefaults() { super.installDefaults(); + String prefix = getPropertyPrefix(); minimumWidth = UIManager.getInt( "Component.minimumWidth" ); isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" ); + focusedBackground = UIManager.getColor( prefix + ".focusedBackground" ); // use component font and foreground for HTML text oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES ); @@ -84,9 +90,28 @@ protected void installDefaults() { protected void uninstallDefaults() { super.uninstallDefaults(); + focusedBackground = null; + getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties ); } + @Override + protected void installListeners() { + super.installListeners(); + + // necessary to update focus background + focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), c -> focusedBackground != null ); + getComponent().addFocusListener( focusListener ); + } + + @Override + protected void uninstallListeners() { + super.uninstallListeners(); + + getComponent().removeFocusListener( focusListener ); + focusListener = null; + } + @Override protected void propertyChange( PropertyChangeEvent e ) { super.propertyChange( e ); @@ -128,14 +153,11 @@ protected void paintSafely( Graphics g ) { @Override protected void paintBackground( Graphics g ) { - JTextComponent c = getComponent(); - - // for compatibility with IntelliJ themes - if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) { - FlatUIUtils.paintParentBackground( g, c ); - return; - } + paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground ); + } - super.paintBackground( g ); + static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) { + g.setColor( FlatTextFieldUI.getBackground( c, isIntelliJTheme, focusedBackground ) ); + g.fillRect( 0, 0, c.getWidth(), c.getHeight() ); } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatFormattedTextFieldUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatFormattedTextFieldUI.java index 8b5f47e17..11d912455 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatFormattedTextFieldUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatFormattedTextFieldUI.java @@ -44,6 +44,7 @@ * @uiDefault Component.minimumWidth int * @uiDefault Component.isIntelliJTheme boolean * @uiDefault FormattedTextField.placeholderForeground Color + * @uiDefault FormattedTextField.focusedBackground Color optional * @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always * @uiDefault TextComponent.selectAllOnMouseClick boolean * diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java index 5d279e67f..d338aa531 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatPasswordFieldUI.java @@ -50,7 +50,6 @@ * @uiDefault PasswordField.disabledBackground Color used if not enabled * @uiDefault PasswordField.inactiveBackground Color used if not editable * @uiDefault PasswordField.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground) - * @uiDefault PasswordField.focusedBackground Color optional * @uiDefault PasswordField.border Border * @uiDefault PasswordField.margin Insets * @uiDefault PasswordField.echoChar character @@ -61,6 +60,7 @@ * @uiDefault Component.minimumWidth int * @uiDefault Component.isIntelliJTheme boolean * @uiDefault PasswordField.placeholderForeground Color + * @uiDefault PasswordField.focusedBackground Color optional * @uiDefault PasswordField.showCapsLock boolean * @uiDefault PasswordField.capsLockIcon Icon * @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always @@ -117,7 +117,10 @@ protected void uninstallDefaults() { protected void installListeners() { super.installListeners(); - focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() ); + // necessary to update focus border and background + focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), null ); + + // update caps lock indicator capsLockListener = new KeyAdapter() { @Override public void keyPressed( KeyEvent e ) { diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextAreaUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextAreaUI.java index fcb440c3a..21824be52 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextAreaUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextAreaUI.java @@ -20,6 +20,7 @@ import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.event.FocusListener; import java.beans.PropertyChangeEvent; import javax.swing.JComponent; import javax.swing.JTextArea; @@ -42,7 +43,6 @@ * @uiDefault TextArea.selectionBackground Color * @uiDefault TextArea.selectionForeground Color * @uiDefault TextArea.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground) - * @uiDefault TextArea.focusedBackground Color optional * @uiDefault TextArea.border Border * @uiDefault TextArea.margin Insets * @uiDefault TextArea.caretBlinkRate int default is 500 milliseconds @@ -53,6 +53,7 @@ * @uiDefault Component.isIntelliJTheme boolean * @uiDefault TextArea.disabledBackground Color used if not enabled * @uiDefault TextArea.inactiveBackground Color used if not editable + * @uiDefault TextArea.focusedBackground Color optional * * @author Karl Tauber */ @@ -66,6 +67,8 @@ public class FlatTextAreaUI protected Color inactiveBackground; protected Color focusedBackground; + private FocusListener focusListener; + public static ComponentUI createUI( JComponent c ) { return new FlatTextAreaUI(); } @@ -99,6 +102,23 @@ protected void uninstallDefaults() { focusedBackground = null; } + @Override + protected void installListeners() { + super.installListeners(); + + // necessary to update focus background + focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), c -> focusedBackground != null ); + getComponent().addFocusListener( focusListener ); + } + + @Override + protected void uninstallListeners() { + super.uninstallListeners(); + + getComponent().removeFocusListener( focusListener ); + focusListener = null; + } + @Override protected void propertyChange( PropertyChangeEvent e ) { super.propertyChange( e ); @@ -160,20 +180,6 @@ protected void paintSafely( Graphics g ) { @Override protected void paintBackground( Graphics g ) { - JTextComponent c = getComponent(); - - // for compatibility with IntelliJ themes - if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) { - FlatUIUtils.paintParentBackground( g, c ); - return; - } - - if( focusedBackground != null && FlatUIUtils.isPermanentFocusOwner( c ) ) { - g.setColor( focusedBackground ); - g.fillRect( 0, 0, c.getWidth(), c.getHeight() ); - return; - } - - super.paintBackground( g ); + FlatEditorPaneUI.paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground ); } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java index 90e78f057..d80ac851e 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java @@ -55,7 +55,6 @@ * @uiDefault TextField.disabledBackground Color used if not enabled * @uiDefault TextField.inactiveBackground Color used if not editable * @uiDefault TextField.inactiveForeground Color used if not enabled (yes, this is confusing; this should be named disabledForeground) - * @uiDefault TextField.focusedBackground Color optional * @uiDefault TextField.border Border * @uiDefault TextField.margin Insets * @uiDefault TextField.caretBlinkRate int default is 500 milliseconds @@ -65,6 +64,7 @@ * @uiDefault Component.minimumWidth int * @uiDefault Component.isIntelliJTheme boolean * @uiDefault TextField.placeholderForeground Color + * @uiDefault TextField.focusedBackground Color optional * @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always * @uiDefault TextComponent.selectAllOnMouseClick boolean * @@ -113,7 +113,8 @@ protected void uninstallDefaults() { protected void installListeners() { super.installListeners(); - focusListener = new FlatUIUtils.RepaintFocusListener( getComponent() ); + // necessary to update focus border and background + focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), null ); getComponent().addFocusListener( focusListener ); } @@ -184,18 +185,31 @@ static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJThe try { FlatUIUtils.setRenderingHints( g2 ); - Color background = focusedBackground != null && FlatUIUtils.isPermanentFocusOwner( c ) ? focusedBackground : c.getBackground(); - g2.setColor( !(background instanceof UIResource) - ? background - : (isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) - ? FlatUIUtils.getParentBackground( c ) - : background) ); + g2.setColor( getBackground( c, isIntelliJTheme, focusedBackground ) ); FlatUIUtils.paintComponentBackground( g2, 0, 0, c.getWidth(), c.getHeight(), focusWidth, arc ); } finally { g2.dispose(); } } + static Color getBackground( JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) { + Color background = c.getBackground(); + + // always use explicitly set color + if( !(background instanceof UIResource) ) + return background; + + // for compatibility with IntelliJ themes + if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) ) + return FlatUIUtils.getParentBackground( c ); + + // focused and editable + if( focusedBackground != null && c.isEditable() && FlatUIUtils.isPermanentFocusOwner( c ) ) + return focusedBackground; + + return background; + } + static void paintPlaceholder( Graphics g, JTextComponent c, Color placeholderForeground ) { // check whether text component is empty if( c.getDocument().getLength() > 0 ) diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextPaneUI.java index 0def747fa..0da0c5388 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextPaneUI.java @@ -16,17 +16,17 @@ package com.formdev.flatlaf.ui; +import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; +import java.awt.event.FocusListener; import java.beans.PropertyChangeEvent; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicTextPaneUI; -import javax.swing.text.JTextComponent; import com.formdev.flatlaf.util.HiDPIUtils; /** @@ -51,6 +51,7 @@ * * @uiDefault Component.minimumWidth int * @uiDefault Component.isIntelliJTheme boolean + * @uiDefault TextPane.focusedBackground Color optional * * @author Karl Tauber */ @@ -59,8 +60,10 @@ public class FlatTextPaneUI { protected int minimumWidth; protected boolean isIntelliJTheme; + protected Color focusedBackground; private Object oldHonorDisplayProperties; + private FocusListener focusListener; public static ComponentUI createUI( JComponent c ) { return new FlatTextPaneUI(); @@ -70,8 +73,10 @@ public static ComponentUI createUI( JComponent c ) { protected void installDefaults() { super.installDefaults(); + String prefix = getPropertyPrefix(); minimumWidth = UIManager.getInt( "Component.minimumWidth" ); isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" ); + focusedBackground = UIManager.getColor( prefix + ".focusedBackground" ); // use component font and foreground for HTML text oldHonorDisplayProperties = getComponent().getClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES ); @@ -82,9 +87,28 @@ protected void installDefaults() { protected void uninstallDefaults() { super.uninstallDefaults(); + focusedBackground = null; + getComponent().putClientProperty( JEditorPane.HONOR_DISPLAY_PROPERTIES, oldHonorDisplayProperties ); } + @Override + protected void installListeners() { + super.installListeners(); + + // necessary to update focus background + focusListener = new FlatUIUtils.RepaintFocusListener( getComponent(), c -> focusedBackground != null ); + getComponent().addFocusListener( focusListener ); + } + + @Override + protected void uninstallListeners() { + super.uninstallListeners(); + + getComponent().removeFocusListener( focusListener ); + focusListener = null; + } + @Override protected void propertyChange( PropertyChangeEvent e ) { super.propertyChange( e ); @@ -108,14 +132,6 @@ protected void paintSafely( Graphics g ) { @Override protected void paintBackground( Graphics g ) { - JTextComponent c = getComponent(); - - // for compatibility with IntelliJ themes - if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) { - FlatUIUtils.paintParentBackground( g, c ); - return; - } - - super.paintBackground( g ); + FlatEditorPaneUI.paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground ); } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java index c868af02d..8acbdc1e8 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java @@ -837,19 +837,23 @@ public static class RepaintFocusListener implements FocusListener { private final Component repaintComponent; + private final Predicate repaintCondition; - public RepaintFocusListener( Component repaintComponent ) { + public RepaintFocusListener( Component repaintComponent, Predicate repaintCondition ) { this.repaintComponent = repaintComponent; + this.repaintCondition = repaintCondition; } @Override public void focusGained( FocusEvent e ) { - repaintComponent.repaint(); + if( repaintCondition == null || repaintCondition.test( repaintComponent ) ) + repaintComponent.repaint(); } @Override public void focusLost( FocusEvent e ) { - repaintComponent.repaint(); + if( repaintCondition == null || repaintCondition.test( repaintComponent ) ) + repaintComponent.repaint(); } } } diff --git a/flatlaf-swingx/src/main/java/com/formdev/flatlaf/swingx/ui/FlatDatePickerUI.java b/flatlaf-swingx/src/main/java/com/formdev/flatlaf/swingx/ui/FlatDatePickerUI.java index 7c988d5b0..a3a92549e 100644 --- a/flatlaf-swingx/src/main/java/com/formdev/flatlaf/swingx/ui/FlatDatePickerUI.java +++ b/flatlaf-swingx/src/main/java/com/formdev/flatlaf/swingx/ui/FlatDatePickerUI.java @@ -156,7 +156,7 @@ protected JFormattedTextField createEditor() { editor.setName( "dateField" ); editor.setBorder( BorderFactory.createEmptyBorder() ); editor.setOpaque( false ); - editor.addFocusListener( new FlatUIUtils.RepaintFocusListener( datePicker ) ); + editor.addFocusListener( new FlatUIUtils.RepaintFocusListener( datePicker, null ) ); return editor; } diff --git a/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/FlatTestLaf.properties b/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/FlatTestLaf.properties index c6b78bfd2..6af9434ae 100644 --- a/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/FlatTestLaf.properties +++ b/flatlaf-testing/src/main/resources/com/formdev/flatlaf/testing/FlatTestLaf.properties @@ -152,6 +152,16 @@ Desktop.background = #afe DesktopIcon.background = darken($Desktop.background,20%) +#---- EditorPane ---- + +EditorPane.focusedBackground = #ff8 + + +#---- FormattedTextField ---- + +FormattedTextField.focusedBackground = #ff8 + + #---- HelpButton ---- HelpButton.focusedBackground = #0ff @@ -223,6 +233,11 @@ OptionPane.icon.warningColor = #fc0 OptionPane.icon.foreground = #fff +#---- PasswordField ---- + +PasswordField.focusedBackground = #ff8 + + #---- Popup ---- Popup.dropShadowColor = #0f0 @@ -332,6 +347,21 @@ TableHeader.separatorColor = #0f0 TableHeader.bottomSeparatorColor = #0f0 +#---- TextArea ---- + +TextArea.focusedBackground = #ff8 + + +#---- TextField ---- + +TextField.focusedBackground = #ff8 + + +#---- TextPane ---- + +TextPane.focusedBackground = #ff8 + + #---- TitledBorder ---- TitledBorder.titleColor = #f0f