diff --git a/CHANGELOG.md b/CHANGELOG.md index 473519b5..4fc00a6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ FlatLaf Change Log ## Unreleased +- TextComponent: Support placeholder text that is displayed if text field is + empty (set client property "JTextField.placeholderText" to a string). - TextComponent: Scale caret width on HiDPI screens when running on Java 8. - ProgressBar: If progress text is visible: - use smaller font diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java index 32007cb0..10aebe50 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java @@ -80,6 +80,14 @@ public interface FlatClientProperties */ String TABBED_PANE_HAS_FULL_BORDER = "JTabbedPane.hasFullBorder"; + /** + * Placeholder text that is only painted if the text field is empty. + *
+ * Component {@link javax.swing.JTextField} or {@link javax.swing.JComboBox}
+ * Value type {@link java.lang.String}
+ */
+ String PLACEHOLDER_TEXT = "JTextField.placeholderText";
+
/**
* Checks whether a client property of a component has the given value.
*/
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 476742fe..07605de0 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
@@ -52,6 +52,7 @@ import javax.swing.plaf.basic.BasicComboBoxUI;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.text.JTextComponent;
+import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.UIScale;
/**
@@ -239,7 +240,8 @@ public class FlatComboBoxUI
} else if( editor != null && source == comboBox && propertyName == "componentOrientation" ) {
ComponentOrientation o = (ComponentOrientation) e.getNewValue();
editor.applyComponentOrientation( o );
- }
+ } else if( editor != null && FlatClientProperties.PLACEHOLDER_TEXT.equals( propertyName ) )
+ editor.repaint();
}
};
}
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 71732a6f..3bbcbbc6 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
@@ -39,8 +39,10 @@ import javax.swing.plaf.ComponentUI;
*
*
*
- * @uiDefault Component.focusWidth int
- * @uiDefault Component.minimumWidth int
+ * @uiDefault Component.focusWidth int
+ * @uiDefault Component.minimumWidth int
+ * @uiDefault Component.isIntelliJTheme boolean
+ * @uiDefault FormattedTextField.placeholderForeground Color
*
* @author Karl Tauber
*/
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 996899c6..0894bc46 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
@@ -17,14 +17,17 @@
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.event.FocusListener;
+import java.beans.PropertyChangeEvent;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicPasswordFieldUI;
+import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.util.SystemInfo;
/**
@@ -51,6 +54,7 @@ import com.formdev.flatlaf.util.SystemInfo;
* @uiDefault Component.focusWidth int
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
+ * @uiDefault PasswordField.placeholderForeground Color
*
* @author Karl Tauber
*/
@@ -60,6 +64,7 @@ public class FlatPasswordFieldUI
protected int focusWidth;
protected int minimumWidth;
protected boolean isIntelliJTheme;
+ protected Color placeholderForeground;
private FocusListener focusListener;
@@ -75,9 +80,11 @@ public class FlatPasswordFieldUI
if( SystemInfo.IS_MAC )
LookAndFeel.installProperty( getComponent(), "echoChar", '\u2022' );
+ String prefix = getPropertyPrefix();
focusWidth = UIManager.getInt( "Component.focusWidth" );
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
+ placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
LookAndFeel.installProperty( getComponent(), "opaque", focusWidth == 0 );
@@ -88,6 +95,8 @@ public class FlatPasswordFieldUI
protected void uninstallDefaults() {
super.uninstallDefaults();
+ placeholderForeground = null;
+
MigLayoutVisualPadding.uninstall( getComponent() );
}
@@ -107,9 +116,18 @@ public class FlatPasswordFieldUI
focusListener = null;
}
+ @Override
+ protected void propertyChange( PropertyChangeEvent e ) {
+ super.propertyChange( e );
+
+ if( FlatClientProperties.PLACEHOLDER_TEXT.equals( e.getPropertyName() ) )
+ getComponent().repaint();
+ }
+
@Override
protected void paintSafely( Graphics g ) {
FlatTextFieldUI.paintBackground( g, getComponent(), focusWidth, isIntelliJTheme );
+ FlatTextFieldUI.paintPlaceholder( g, getComponent(), placeholderForeground );
super.paintSafely( g );
}
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 b21f441c..d74dabf1 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
@@ -20,9 +20,12 @@ import static com.formdev.flatlaf.util.UIScale.scale;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
+import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
+import java.awt.Insets;
import java.awt.event.FocusListener;
+import java.beans.PropertyChangeEvent;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JSpinner;
@@ -33,6 +36,7 @@ import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTextFieldUI;
import javax.swing.text.JTextComponent;
+import com.formdev.flatlaf.FlatClientProperties;
/**
* Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}.
@@ -57,6 +61,7 @@ import javax.swing.text.JTextComponent;
* @uiDefault Component.focusWidth int
* @uiDefault Component.minimumWidth int
* @uiDefault Component.isIntelliJTheme boolean
+ * @uiDefault TextField.placeholderForeground Color
*
* @author Karl Tauber
*/
@@ -66,6 +71,7 @@ public class FlatTextFieldUI
protected int focusWidth;
protected int minimumWidth;
protected boolean isIntelliJTheme;
+ protected Color placeholderForeground;
private FocusListener focusListener;
@@ -77,9 +83,11 @@ public class FlatTextFieldUI
protected void installDefaults() {
super.installDefaults();
+ String prefix = getPropertyPrefix();
focusWidth = UIManager.getInt( "Component.focusWidth" );
minimumWidth = UIManager.getInt( "Component.minimumWidth" );
isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" );
+ placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" );
LookAndFeel.installProperty( getComponent(), "opaque", focusWidth == 0 );
@@ -90,6 +98,8 @@ public class FlatTextFieldUI
protected void uninstallDefaults() {
super.uninstallDefaults();
+ placeholderForeground = null;
+
MigLayoutVisualPadding.uninstall( getComponent() );
}
@@ -109,9 +119,18 @@ public class FlatTextFieldUI
focusListener = null;
}
+ @Override
+ protected void propertyChange( PropertyChangeEvent e ) {
+ super.propertyChange( e );
+
+ if( FlatClientProperties.PLACEHOLDER_TEXT.equals( e.getPropertyName() ) )
+ getComponent().repaint();
+ }
+
@Override
protected void paintSafely( Graphics g ) {
paintBackground( g, getComponent(), focusWidth, isIntelliJTheme );
+ paintPlaceholder( g, getComponent(), placeholderForeground );
super.paintSafely( g );
}
@@ -152,6 +171,31 @@ public class FlatTextFieldUI
}
}
+ static void paintPlaceholder( Graphics g, JTextComponent c, Color placeholderForeground ) {
+ // check whether text component is empty
+ if( c.getDocument().getLength() > 0 )
+ return;
+
+ // check for JComboBox
+ Container parent = c.getParent();
+ JComponent jc = (parent instanceof JComboBox) ? (JComboBox>) parent : c;
+
+ // get placeholder text
+ Object placeholder = jc.getClientProperty( FlatClientProperties.PLACEHOLDER_TEXT );
+ if( !(placeholder instanceof String) )
+ return;
+
+ // compute placeholder location
+ Insets insets = c.getInsets();
+ FontMetrics fm = c.getFontMetrics( c.getFont() );
+ int x = insets.left;
+ int y = insets.top + fm.getAscent() + ((c.getHeight() - insets.top - insets.bottom - fm.getHeight()) / 2);
+
+ // paint placeholder
+ g.setColor( placeholderForeground );
+ FlatUIUtils.drawString( c, g, (String) placeholder, x, y );
+ }
+
@Override
public Dimension getPreferredSize( JComponent c ) {
return applyMinimumWidth( super.getPreferredSize( c ), c );
diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
index aed69497..99a5b323 100644
--- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
+++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
@@ -147,8 +147,8 @@ Component.hideMnemonics=true
#---- EditorPane ----
EditorPane.border=com.formdev.flatlaf.ui.FlatMarginBorder
-EditorPane.background=@textComponentBackground
EditorPane.margin=@textComponentMargin
+EditorPane.background=@textComponentBackground
#---- FileChooser ----
@@ -172,8 +172,9 @@ FileView.floppyDriveIcon=com.formdev.flatlaf.icons.FlatFileViewFloppyDriveIcon
#---- FormattedTextField ----
FormattedTextField.border=com.formdev.flatlaf.ui.FlatBorder
-FormattedTextField.background=@textComponentBackground
FormattedTextField.margin=@textComponentMargin
+FormattedTextField.background=@textComponentBackground
+FormattedTextField.placeholderForeground=@disabledText
#---- HelpButton ----
@@ -247,8 +248,9 @@ OptionPane.warningIcon=com.formdev.flatlaf.icons.FlatOptionPaneWarningIcon
#---- PasswordField ----
PasswordField.border=com.formdev.flatlaf.ui.FlatBorder
-PasswordField.background=@textComponentBackground
PasswordField.margin=@textComponentMargin
+PasswordField.background=@textComponentBackground
+PasswordField.placeholderForeground=@disabledText
#---- PopupMenu ----
@@ -381,22 +383,23 @@ TableHeader.cellBorder=2,3,2,3
#---- TextArea ----
TextArea.border=com.formdev.flatlaf.ui.FlatMarginBorder
-TextArea.background=@textComponentBackground
TextArea.margin=@textComponentMargin
+TextArea.background=@textComponentBackground
#---- TextField ----
TextField.border=com.formdev.flatlaf.ui.FlatBorder
-TextField.background=@textComponentBackground
TextField.margin=@textComponentMargin
+TextField.background=@textComponentBackground
+TextField.placeholderForeground=@disabledText
#---- TextPane ----
TextPane.border=com.formdev.flatlaf.ui.FlatMarginBorder
-TextPane.background=@textComponentBackground
TextPane.margin=@textComponentMargin
+TextPane.background=@textComponentBackground
#---- TitledBorder ----
diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/BasicComponentsPanel.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/BasicComponentsPanel.java
index 6730520d..7ed32f2b 100644
--- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/BasicComponentsPanel.java
+++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/BasicComponentsPanel.java
@@ -62,21 +62,25 @@ class BasicComponentsPanel
JLabel spinnerLabel = new JLabel();
JSpinner spinner1 = new JSpinner();
JSpinner spinner2 = new JSpinner();
+ JComboBox