diff --git a/CHANGELOG.md b/CHANGELOG.md index f5742448..bfc1a57c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,16 @@ FlatLaf Change Log ## 1.3-SNAPSHOT +#### New features and improvements + +- TextComponents, ComboBox and Spinner: Support different background color when + component is focused (use UI values `TextField.focusedBackground`, + `PasswordField.focusedBackground`, `FormattedTextField.focusedBackground`, + `TextArea.focusedBackground`, `TextPane.focusedBackground`, + `EditorPane.focusedBackground`, `ComboBox.focusedBackground`, + `ComboBox.buttonFocusedBackground`, `ComboBox.popupFocusedBackground` and + `Spinner.focusedBackground`). (issue #335) + #### Fixed bugs - Fixed white lines at bottom and right side of window (in dark themes on HiDPI diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java index df8802ff..27c8d424 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatBorder.java @@ -22,17 +22,12 @@ import java.awt.Component; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Insets; -import java.awt.KeyboardFocusManager; import java.awt.Paint; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JScrollPane; import javax.swing.JSpinner; -import javax.swing.JTable; -import javax.swing.JTextField; -import javax.swing.JTree; import javax.swing.JViewport; -import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.basic.BasicBorders; import com.formdev.flatlaf.FlatClientProperties; @@ -164,37 +159,13 @@ public class FlatBorder } protected boolean isFocused( Component c ) { - if( c instanceof JScrollPane ) { - JViewport viewport = ((JScrollPane)c).getViewport(); - Component view = (viewport != null) ? viewport.getView() : null; - if( view != null ) { - if( FlatUIUtils.isPermanentFocusOwner( view ) ) - return true; - - if( (view instanceof JTable && ((JTable)view).isEditing()) || - (view instanceof JTree && ((JTree)view).isEditing()) ) - { - Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); - if( focusOwner != null ) - return SwingUtilities.isDescendingFrom( focusOwner, view ); - } - } - return false; - } else if( c instanceof JComboBox && ((JComboBox)c).isEditable() ) { - Component editorComponent = ((JComboBox)c).getEditor().getEditorComponent(); - return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false; - } else if( c instanceof JSpinner ) { - if( FlatUIUtils.isPermanentFocusOwner( c ) ) - return true; - - JComponent editor = ((JSpinner)c).getEditor(); - if( editor instanceof JSpinner.DefaultEditor ) { - JTextField textField = ((JSpinner.DefaultEditor)editor).getTextField(); - if( textField != null ) - return FlatUIUtils.isPermanentFocusOwner( textField ); - } - return false; - } else + if( c instanceof JScrollPane ) + return FlatScrollPaneUI.isPermanentFocusOwner( (JScrollPane) c ); + else if( c instanceof JComboBox ) + return FlatComboBoxUI.isPermanentFocusOwner( (JComboBox) c ); + else if( c instanceof JSpinner ) + return FlatSpinnerUI.isPermanentFocusOwner( (JSpinner) c ); + else return FlatUIUtils.isPermanentFocusOwner( c ); } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java index f1b7b948..1f585521 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatComboBoxUI.java @@ -61,6 +61,7 @@ import javax.swing.UIManager; import javax.swing.border.AbstractBorder; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicComboBoxUI; import javax.swing.plaf.basic.BasicComboPopup; import javax.swing.plaf.basic.ComboPopup; @@ -92,14 +93,17 @@ import com.formdev.flatlaf.util.UIScale; * @uiDefault Component.borderColor Color * @uiDefault Component.disabledBorderColor Color * @uiDefault ComboBox.editableBackground Color optional; defaults to ComboBox.background + * @uiDefault ComboBox.focusedBackground Color optional * @uiDefault ComboBox.disabledBackground Color * @uiDefault ComboBox.disabledForeground Color * @uiDefault ComboBox.buttonBackground Color * @uiDefault ComboBox.buttonEditableBackground Color + * @uiDefault ComboBox.buttonFocusedBackground Color optional; defaults to ComboBox.focusedBackground * @uiDefault ComboBox.buttonArrowColor Color * @uiDefault ComboBox.buttonDisabledArrowColor Color * @uiDefault ComboBox.buttonHoverArrowColor Color * @uiDefault ComboBox.buttonPressedArrowColor Color + * @uiDefault ComboBox.popupFocusedBackground Color optional * * @author Karl Tauber */ @@ -115,16 +119,20 @@ public class FlatComboBoxUI protected Color disabledBorderColor; protected Color editableBackground; + protected Color focusedBackground; protected Color disabledBackground; protected Color disabledForeground; protected Color buttonBackground; protected Color buttonEditableBackground; + protected Color buttonFocusedBackground; protected Color buttonArrowColor; protected Color buttonDisabledArrowColor; protected Color buttonHoverArrowColor; protected Color buttonPressedArrowColor; + protected Color popupFocusedBackground; + private MouseListener hoverListener; protected boolean hover; protected boolean pressed; @@ -195,16 +203,20 @@ public class FlatComboBoxUI disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" ); editableBackground = UIManager.getColor( "ComboBox.editableBackground" ); + focusedBackground = UIManager.getColor( "ComboBox.focusedBackground" ); disabledBackground = UIManager.getColor( "ComboBox.disabledBackground" ); disabledForeground = UIManager.getColor( "ComboBox.disabledForeground" ); buttonBackground = UIManager.getColor( "ComboBox.buttonBackground" ); + buttonFocusedBackground = UIManager.getColor( "ComboBox.buttonFocusedBackground" ); buttonEditableBackground = UIManager.getColor( "ComboBox.buttonEditableBackground" ); buttonArrowColor = UIManager.getColor( "ComboBox.buttonArrowColor" ); buttonDisabledArrowColor = UIManager.getColor( "ComboBox.buttonDisabledArrowColor" ); buttonHoverArrowColor = UIManager.getColor( "ComboBox.buttonHoverArrowColor" ); buttonPressedArrowColor = UIManager.getColor( "ComboBox.buttonPressedArrowColor" ); + popupFocusedBackground = UIManager.getColor( "ComboBox.popupFocusedBackground" ); + // set maximumRowCount int maximumRowCount = UIManager.getInt( "ComboBox.maximumRowCount" ); if( maximumRowCount > 0 && maximumRowCount != 8 && comboBox.getMaximumRowCount() == 8 ) @@ -224,16 +236,20 @@ public class FlatComboBoxUI disabledBorderColor = null; editableBackground = null; + focusedBackground = null; disabledBackground = null; disabledForeground = null; buttonBackground = null; buttonEditableBackground = null; + buttonFocusedBackground = null; buttonArrowColor = null; buttonDisabledArrowColor = null; buttonHoverArrowColor = null; buttonPressedArrowColor = null; + popupFocusedBackground = null; + MigLayoutVisualPadding.uninstall( comboBox ); } @@ -423,7 +439,11 @@ public class FlatComboBoxUI // paint arrow button background if( enabled && !isCellRenderer ) { - g2.setColor( paintButton ? buttonEditableBackground : buttonBackground ); + g2.setColor( paintButton + ? buttonEditableBackground + : (buttonFocusedBackground != null || focusedBackground != null) && isPermanentFocusOwner( comboBox ) + ? (buttonFocusedBackground != null ? buttonFocusedBackground : focusedBackground) + : buttonBackground ); Shape oldClip = g2.getClip(); if( isLeftToRight ) g2.clipRect( arrowX, 0, width - arrowX, height ); @@ -483,9 +503,20 @@ public class FlatComboBoxUI } protected Color getBackground( boolean enabled ) { - return enabled - ? (editableBackground != null && comboBox.isEditable() ? editableBackground : comboBox.getBackground()) - : (isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground); + if( enabled ) { + Color background = comboBox.getBackground(); + + // always use explicitly set color + if( !(background instanceof UIResource) ) + return background; + + // focused + if( focusedBackground != null && isPermanentFocusOwner( comboBox ) ) + return focusedBackground; + + return (editableBackground != null && comboBox.isEditable()) ? editableBackground : background; + } else + return isIntelliJTheme ? FlatUIUtils.getParentBackground( comboBox ) : disabledBackground; } protected Color getForeground( boolean enabled ) { @@ -576,6 +607,14 @@ public class FlatComboBoxUI return parentParent != null && !comboBox.getBackground().equals( parentParent.getBackground() ); } + public static boolean isPermanentFocusOwner( JComboBox comboBox ) { + if( comboBox.isEditable() ) { + Component editorComponent = comboBox.getEditor().getEditorComponent(); + return (editorComponent != null) ? FlatUIUtils.isPermanentFocusOwner( editorComponent ) : false; + } else + return FlatUIUtils.isPermanentFocusOwner( comboBox ); + } + //---- class FlatComboBoxButton ------------------------------------------- protected class FlatComboBoxButton @@ -688,6 +727,8 @@ public class FlatComboBoxUI super.configureList(); list.setCellRenderer( new PopupListCellRenderer() ); + if( popupFocusedBackground != null ) + list.setBackground( popupFocusedBackground ); } @Override 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 45484034..0179e60d 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 @@ import com.formdev.flatlaf.util.HiDPIUtils; * * @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 class FlatEditorPaneUI 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 @@ public class FlatEditorPaneUI 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 @@ public class FlatEditorPaneUI @Override protected void paintBackground( Graphics g ) { - JTextComponent c = getComponent(); + paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground ); + } - // for compatibility with IntelliJ themes - if( isIntelliJTheme && (!c.isEnabled() || !c.isEditable()) && (c.getBackground() instanceof UIResource) ) { - FlatUIUtils.paintParentBackground( g, c ); - return; - } - - 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 8b5f47e1..11d91245 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 @@ import javax.swing.plaf.ComponentUI; * @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 99c3ac8f..d338aa53 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 @@ -60,6 +60,7 @@ import com.formdev.flatlaf.util.HiDPIUtils; * @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 @@ -73,6 +74,7 @@ public class FlatPasswordFieldUI protected int minimumWidth; protected boolean isIntelliJTheme; protected Color placeholderForeground; + protected Color focusedBackground; protected boolean showCapsLock; protected Icon capsLockIcon; @@ -91,6 +93,7 @@ public class FlatPasswordFieldUI minimumWidth = UIManager.getInt( "Component.minimumWidth" ); isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" ); placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" ); + focusedBackground = UIManager.getColor( prefix + ".focusedBackground" ); showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" ); capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" ); @@ -104,6 +107,7 @@ public class FlatPasswordFieldUI super.uninstallDefaults(); placeholderForeground = null; + focusedBackground = null; capsLockIcon = null; MigLayoutVisualPadding.uninstall( getComponent() ); @@ -113,7 +117,10 @@ public class FlatPasswordFieldUI 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 ) { @@ -157,7 +164,7 @@ public class FlatPasswordFieldUI @Override protected void paintSafely( Graphics g ) { - FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme ); + FlatTextFieldUI.paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground ); FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground ); paintCapsLock( g ); diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java index fe2575cb..bd8ba14d 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatScrollPaneUI.java @@ -19,6 +19,7 @@ package com.formdev.flatlaf.ui; import java.awt.Component; import java.awt.Graphics; import java.awt.Insets; +import java.awt.KeyboardFocusManager; import java.awt.Rectangle; import java.awt.event.ContainerEvent; import java.awt.event.ContainerListener; @@ -34,11 +35,13 @@ import javax.swing.JComponent; import javax.swing.JScrollBar; import javax.swing.JScrollPane; import javax.swing.JTable; +import javax.swing.JTree; import javax.swing.JViewport; import javax.swing.LookAndFeel; import javax.swing.ScrollPaneConstants; import javax.swing.Scrollable; import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicScrollPaneUI; @@ -329,6 +332,28 @@ public class FlatScrollPaneUI paint( g, c ); } + public static boolean isPermanentFocusOwner( JScrollPane scrollPane ) { + JViewport viewport = scrollPane.getViewport(); + Component view = (viewport != null) ? viewport.getView() : null; + if( view == null ) + return false; + + // check whether view is focus owner + if( FlatUIUtils.isPermanentFocusOwner( view ) ) + return true; + + // check whether editor component in JTable or JTree is focus owner + if( (view instanceof JTable && ((JTable)view).isEditing()) || + (view instanceof JTree && ((JTree)view).isEditing()) ) + { + Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); + if( focusOwner != null ) + return SwingUtilities.isDescendingFrom( focusOwner, view ); + } + + return false; + } + //---- class Handler ------------------------------------------------------ /** @@ -350,11 +375,13 @@ public class FlatScrollPaneUI @Override public void focusGained( FocusEvent e ) { + // necessary to update focus border scrollpane.repaint(); } @Override public void focusLost( FocusEvent e ) { + // necessary to update focus border scrollpane.repaint(); } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java index 17c826d2..a341d0a9 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatSpinnerUI.java @@ -39,6 +39,7 @@ import javax.swing.LookAndFeel; import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicSpinnerUI; import com.formdev.flatlaf.FlatClientProperties; @@ -65,6 +66,7 @@ import com.formdev.flatlaf.FlatClientProperties; * @uiDefault Component.disabledBorderColor Color * @uiDefault Spinner.disabledBackground Color * @uiDefault Spinner.disabledForeground Color + * @uiDefault Spinner.focusedBackground Color optional * @uiDefault Spinner.buttonBackground Color * @uiDefault Spinner.buttonArrowColor Color * @uiDefault Spinner.buttonDisabledArrowColor Color @@ -87,6 +89,7 @@ public class FlatSpinnerUI protected Color disabledBorderColor; protected Color disabledBackground; protected Color disabledForeground; + protected Color focusedBackground; protected Color buttonBackground; protected Color buttonArrowColor; protected Color buttonDisabledArrowColor; @@ -112,6 +115,7 @@ public class FlatSpinnerUI disabledBorderColor = UIManager.getColor( "Component.disabledBorderColor" ); disabledBackground = UIManager.getColor( "Spinner.disabledBackground" ); disabledForeground = UIManager.getColor( "Spinner.disabledForeground" ); + focusedBackground = UIManager.getColor( "Spinner.focusedBackground" ); buttonBackground = UIManager.getColor( "Spinner.buttonBackground" ); buttonArrowColor = UIManager.getColor( "Spinner.buttonArrowColor" ); buttonDisabledArrowColor = UIManager.getColor( "Spinner.buttonDisabledArrowColor" ); @@ -133,6 +137,7 @@ public class FlatSpinnerUI disabledBorderColor = null; disabledBackground = null; disabledForeground = null; + focusedBackground = null; buttonBackground = null; buttonArrowColor = null; buttonDisabledArrowColor = null; @@ -221,10 +226,31 @@ public class FlatSpinnerUI : null; } + public static boolean isPermanentFocusOwner( JSpinner spinner ) { + if( FlatUIUtils.isPermanentFocusOwner( spinner ) ) + return true; + + JTextField textField = getEditorTextField( spinner.getEditor() ); + return (textField != null) + ? FlatUIUtils.isPermanentFocusOwner( textField ) + : false; + } + protected Color getBackground( boolean enabled ) { - return enabled - ? spinner.getBackground() - : (isIntelliJTheme ? FlatUIUtils.getParentBackground( spinner ) : disabledBackground); + if( enabled ) { + Color background = spinner.getBackground(); + + // always use explicitly set color + if( !(background instanceof UIResource) ) + return background; + + // focused + if( focusedBackground != null && isPermanentFocusOwner( spinner ) ) + return focusedBackground; + + return background; + } else + return isIntelliJTheme ? FlatUIUtils.getParentBackground( spinner ) : disabledBackground; } protected Color getForeground( boolean enabled ) { @@ -405,6 +431,7 @@ public class FlatSpinnerUI @Override public void focusGained( FocusEvent e ) { + // necessary to update focus border spinner.repaint(); // if spinner gained focus, transfer it to the editor text field @@ -417,6 +444,7 @@ public class FlatSpinnerUI @Override public void focusLost( FocusEvent e ) { + // necessary to update focus border spinner.repaint(); } 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 9254749f..21824be5 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.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.JTextArea; @@ -52,6 +53,7 @@ import com.formdev.flatlaf.util.HiDPIUtils; * @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 */ @@ -63,6 +65,9 @@ public class FlatTextAreaUI protected Color background; protected Color disabledBackground; protected Color inactiveBackground; + protected Color focusedBackground; + + private FocusListener focusListener; public static ComponentUI createUI( JComponent c ) { return new FlatTextAreaUI(); @@ -84,6 +89,7 @@ public class FlatTextAreaUI background = UIManager.getColor( "TextArea.background" ); disabledBackground = UIManager.getColor( "TextArea.disabledBackground" ); inactiveBackground = UIManager.getColor( "TextArea.inactiveBackground" ); + focusedBackground = UIManager.getColor( "TextArea.focusedBackground" ); } @Override @@ -93,6 +99,24 @@ public class FlatTextAreaUI background = null; disabledBackground = null; inactiveBackground = null; + 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 @@ -156,14 +180,6 @@ public class FlatTextAreaUI @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/FlatTextFieldUI.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatTextFieldUI.java index 572a0298..d80ac851 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 @@ -64,6 +64,7 @@ import com.formdev.flatlaf.util.JavaCompatibility; * @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 * @@ -75,6 +76,7 @@ public class FlatTextFieldUI protected int minimumWidth; protected boolean isIntelliJTheme; protected Color placeholderForeground; + protected Color focusedBackground; private FocusListener focusListener; @@ -90,6 +92,7 @@ public class FlatTextFieldUI minimumWidth = UIManager.getInt( "Component.minimumWidth" ); isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" ); placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" ); + focusedBackground = UIManager.getColor( prefix + ".focusedBackground" ); LookAndFeel.installProperty( getComponent(), "opaque", false ); @@ -101,6 +104,7 @@ public class FlatTextFieldUI super.uninstallDefaults(); placeholderForeground = null; + focusedBackground = null; MigLayoutVisualPadding.uninstall( getComponent() ); } @@ -109,7 +113,8 @@ public class FlatTextFieldUI 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 ); } @@ -148,7 +153,7 @@ public class FlatTextFieldUI @Override protected void paintSafely( Graphics g ) { - paintBackground( g, getComponent(), isIntelliJTheme ); + paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground ); paintPlaceholder( g, getComponent(), placeholderForeground ); super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) ); @@ -159,7 +164,7 @@ public class FlatTextFieldUI // background is painted elsewhere } - static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme ) { + static void paintBackground( Graphics g, JTextComponent c, boolean isIntelliJTheme, Color focusedBackground ) { // do not paint background if: // - not opaque and // - border is not a flat border and @@ -180,18 +185,31 @@ public class FlatTextFieldUI try { FlatUIUtils.setRenderingHints( g2 ); - Color background = 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 0def747f..0da0c538 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 @@ import com.formdev.flatlaf.util.HiDPIUtils; * * @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 class FlatTextPaneUI 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 @@ public class FlatTextPaneUI 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 @@ public class FlatTextPaneUI @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 c868af02..8acbdc1e 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 @@ debug*/ 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 7c988d5b..a3a92549 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 @@ public class FlatDatePickerUI 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 c6b78bfd..cc4d76a7 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 @@ -130,6 +130,10 @@ ComboBox.background = #fff ComboBox.buttonBackground = #f0f0f0 ComboBox.buttonEditableBackground = #ccc +ComboBox.focusedBackground = #ff8 +ComboBox.buttonFocusedBackground = #ff0 +ComboBox.popupFocusedBackground = #ffc + #---- Component ---- @@ -152,6 +156,16 @@ Desktop.background = #afe DesktopIcon.background = darken($Desktop.background,20%) +#---- EditorPane ---- + +EditorPane.focusedBackground = #ff8 + + +#---- FormattedTextField ---- + +FormattedTextField.focusedBackground = #ff8 + + #---- HelpButton ---- HelpButton.focusedBackground = #0ff @@ -223,6 +237,11 @@ OptionPane.icon.warningColor = #fc0 OptionPane.icon.foreground = #fff +#---- PasswordField ---- + +PasswordField.focusedBackground = #ff8 + + #---- Popup ---- Popup.dropShadowColor = #0f0 @@ -280,6 +299,11 @@ Slider.disabledTrackColor = #ff8 Slider.disabledThumbColor = #880 +#---- Spinner ---- + +Spinner.focusedBackground = #ff8 + + #---- SplitPane ---- SplitPaneDivider.draggingColor = #800 @@ -332,6 +356,21 @@ TableHeader.separatorColor = #0f0 TableHeader.bottomSeparatorColor = #0f0 +#---- TextArea ---- + +TextArea.focusedBackground = #ff8 + + +#---- TextField ---- + +TextField.focusedBackground = #ff8 + + +#---- TextPane ---- + +TextPane.focusedBackground = #ff8 + + #---- TitledBorder ---- TitledBorder.titleColor = #f0f