diff --git a/CHANGELOG.md b/CHANGELOG.md index fc7efe32..052d0ca3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ FlatLaf Change Log #### New features and improvements +- TextField, FormattedTextField and PasswordField: Support leading and trailing + icons (set client property `JTextField.leadingIcon` or + `JTextField.trailingIcon` to an `Icon`). (issue #368) - InternalFrame: Double-click on icon in internal frame title bar now closes the internal frame. (issue #374) - IntelliJ Themes: Removed deprecated `install()` methods. @@ -55,8 +58,8 @@ FlatLaf Change Log #### New features and improvements -- TextField, FormattedTextField and PasswordField: Support adding extra padding. - (set client property `JTextField.padding` to `Insets`). +- TextField, FormattedTextField and PasswordField: Support adding extra padding + (set client property `JTextField.padding` to an `Insets`). - PasswordField: UI delegate `FlatPasswordFieldUI` now extends `FlatTextFieldUI` (instead of `BasicPasswordFieldUI`) to avoid duplicate code and for easier extensibility. 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 dace152f..780cb166 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java @@ -745,6 +745,26 @@ public interface FlatClientProperties */ String TEXT_FIELD_PADDING = "JTextField.padding"; + /** + * Specifies an icon that will be placed at the leading edge of the text field. + *

+ * Component {@link javax.swing.JTextField} (and subclasses)
+ * Value type {@link javax.swing.Icon} + * + * @since 2 + */ + String TEXT_FIELD_LEADING_ICON = "JTextField.leadingIcon"; + + /** + * Specifies an icon that will be placed at the trailing edge of the text field. + *

+ * Component {@link javax.swing.JTextField} (and subclasses)
+ * Value type {@link javax.swing.Icon} + * + * @since 2 + */ + String TEXT_FIELD_TRAILING_ICON = "JTextField.trailingIcon"; + //---- JToggleButton ------------------------------------------------------ /** @@ -813,8 +833,7 @@ public interface FlatClientProperties * If the client property is not set, or not a {@link Boolean}, defaultValue is returned. */ static Boolean clientPropertyBooleanStrict( JComponent c, String key, Boolean defaultValue ) { - Object value = c.getClientProperty( key ); - return (value instanceof Boolean) ? (Boolean) value : defaultValue; + return clientProperty( c, key, defaultValue, Boolean.class ); } /** @@ -831,7 +850,18 @@ public interface FlatClientProperties * If the client property is not set, or not a color, defaultValue is returned. */ static Color clientPropertyColor( JComponent c, String key, Color defaultValue ) { + return clientProperty( c, key, defaultValue, Color.class ); + } + + /** + * Returns the value of the specified client property if it is an instance of + * the specified type. Otherwise, defaultValue is returned. + * + * @since 2 + */ + @SuppressWarnings( "unchecked" ) + static T clientProperty( JComponent c, String key, T defaultValue, Class type ) { Object value = c.getClientProperty( key ); - return (value instanceof Color) ? (Color) value : defaultValue; + return type.isInstance( value ) ? (T) value : defaultValue; } } diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCaret.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCaret.java index 83a5aac3..7b20ec61 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCaret.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatCaret.java @@ -18,7 +18,6 @@ package com.formdev.flatlaf.ui; import static com.formdev.flatlaf.FlatClientProperties.*; import java.awt.EventQueue; -import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.FocusEvent; import java.awt.event.MouseEvent; @@ -68,11 +67,12 @@ public class FlatCaret protected void adjustVisibility( Rectangle nloc ) { JTextComponent c = getComponent(); if( c != null && c.getUI() instanceof FlatTextFieldUI ) { - Insets padding = ((FlatTextFieldUI)c.getUI()).getPadding(); - if( padding != null ) { - nloc.x -= padding.left; - nloc.y -= padding.top; - } + // need to fix x location because JTextField.scrollRectToVisible() uses insets.left + // (as BasicTextUI.getVisibleEditorRect() does), + // but FlatTextFieldUI.getVisibleEditorRect() may add some padding + Rectangle r = ((FlatTextFieldUI)c.getUI()).getVisibleEditorRect(); + if( r != null ) + nloc.x -= r.x - c.getInsets().left; } super.adjustVisibility( nloc ); } 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 a016d7da..6e34a643 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 @@ -43,6 +43,7 @@ import javax.swing.plaf.ComponentUI; * @uiDefault Component.isIntelliJTheme boolean * @uiDefault FormattedTextField.placeholderForeground Color * @uiDefault FormattedTextField.focusedBackground Color optional + * @uiDefault FormattedTextField.iconTextGap int optional, default is 4 * @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 e1f74421..454e9593 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,8 +17,7 @@ package com.formdev.flatlaf.ui; import java.awt.Graphics; -import java.awt.Insets; -import java.awt.Shape; +import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; @@ -36,6 +35,7 @@ import javax.swing.text.Element; import javax.swing.text.JTextComponent; import javax.swing.text.PasswordView; import javax.swing.text.View; +import com.formdev.flatlaf.util.UIScale; /** * Provides the Flat LaF UI delegate for {@link javax.swing.JPasswordField}. @@ -61,6 +61,7 @@ import javax.swing.text.View; * @uiDefault Component.isIntelliJTheme boolean * @uiDefault PasswordField.placeholderForeground Color * @uiDefault PasswordField.focusedBackground Color optional + * @uiDefault PasswordField.iconTextGap int optional, default is 4 * @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always * @uiDefault TextComponent.selectAllOnMouseClick boolean * @@ -160,29 +161,38 @@ public class FlatPasswordFieldUI return new PasswordView( elem ); } + /** @since 2 */ @Override - protected void paintSafely( Graphics g ) { - // safe and restore clipping area because super.paintSafely() modifies it - // and the caps lock icon would be truncated - Shape oldClip = g.getClip(); - super.paintSafely( g ); - g.setClip( oldClip ); + protected void paintIcons( Graphics g, Rectangle r ) { + super.paintIcons( g, r ); - paintCapsLock( g ); + if( isCapsLockVisible() ) + paintCapsLock( g, r ); } - protected void paintCapsLock( Graphics g ) { - if( !isCapsLockVisible() ) - return; - + /** @since 2 */ + protected void paintCapsLock( Graphics g, Rectangle r ) { JTextComponent c = getComponent(); - int y = (c.getHeight() - capsLockIcon.getIconHeight()) / 2; int x = c.getComponentOrientation().isLeftToRight() - ? c.getWidth() - capsLockIcon.getIconWidth() - y - : y; + ? r.x + r.width - capsLockIcon.getIconWidth() + : r.x; + int y = r.y + Math.round( (r.height - capsLockIcon.getIconHeight()) / 2f ); capsLockIcon.paintIcon( c, g, x, y ); } + /** @since 2 */ + @Override + protected boolean hasTrailingIcon() { + return super.hasTrailingIcon() || isCapsLockVisible(); + } + + /** @since 2 */ + @Override + protected int getTrailingIconWidth() { + return super.getTrailingIconWidth() + + (isCapsLockVisible() ? capsLockIcon.getIconWidth() + UIScale.scale( iconTextGap ) : 0); + } + /** * @since 1.4 */ @@ -194,18 +204,4 @@ public class FlatPasswordFieldUI return FlatUIUtils.isPermanentFocusOwner( c ) && Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK ); } - - /** - * @since 1.4 - */ - @Override - protected Insets getPadding() { - Insets padding = super.getPadding(); - if( !isCapsLockVisible() ) - return padding; - - boolean ltr = getComponent().getComponentOrientation().isLeftToRight(); - int iconWidth = capsLockIcon.getIconWidth(); - return FlatUIUtils.addInsets( padding, new Insets( 0, ltr ? 0 : iconWidth, 0, ltr ? iconWidth : 0 ) ); - } } 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 5a02d059..6cd7f88e 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 @@ -16,6 +16,7 @@ package com.formdev.flatlaf.ui; +import static com.formdev.flatlaf.FlatClientProperties.*; import static com.formdev.flatlaf.util.UIScale.scale; import java.awt.Color; import java.awt.Container; @@ -28,6 +29,7 @@ import java.awt.Rectangle; import java.awt.event.FocusListener; import java.beans.PropertyChangeEvent; import java.util.Objects; +import javax.swing.Icon; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JSpinner; @@ -39,10 +41,8 @@ import javax.swing.plaf.UIResource; import javax.swing.plaf.basic.BasicTextFieldUI; import javax.swing.text.Caret; import javax.swing.text.JTextComponent; -import com.formdev.flatlaf.FlatClientProperties; import com.formdev.flatlaf.util.HiDPIUtils; import com.formdev.flatlaf.util.JavaCompatibility; -import com.formdev.flatlaf.util.UIScale; /** * Provides the Flat LaF UI delegate for {@link javax.swing.JTextField}. @@ -68,6 +68,7 @@ import com.formdev.flatlaf.util.UIScale; * @uiDefault Component.isIntelliJTheme boolean * @uiDefault TextField.placeholderForeground Color * @uiDefault TextField.focusedBackground Color optional + * @uiDefault TextField.iconTextGap int optional, default is 4 * @uiDefault TextComponent.selectAllOnFocusPolicy String never, once (default) or always * @uiDefault TextComponent.selectAllOnMouseClick boolean * @@ -80,6 +81,10 @@ public class FlatTextFieldUI protected boolean isIntelliJTheme; protected Color placeholderForeground; protected Color focusedBackground; + protected int iconTextGap; + + protected Icon leadingIcon; + protected Icon trailingIcon; private Insets defaultMargin; @@ -89,6 +94,22 @@ public class FlatTextFieldUI return new FlatTextFieldUI(); } + @Override + public void installUI( JComponent c ) { + super.installUI( c ); + + leadingIcon = clientProperty( c, TEXT_FIELD_LEADING_ICON, null, Icon.class ); + trailingIcon = clientProperty( c, TEXT_FIELD_TRAILING_ICON, null, Icon.class ); + } + + @Override + public void uninstallUI( JComponent c ) { + super.uninstallUI( c ); + + leadingIcon = null; + trailingIcon = null; + } + @Override protected void installDefaults() { super.installDefaults(); @@ -98,6 +119,7 @@ public class FlatTextFieldUI isIntelliJTheme = UIManager.getBoolean( "Component.isIntelliJTheme" ); placeholderForeground = UIManager.getColor( prefix + ".placeholderForeground" ); focusedBackground = UIManager.getColor( prefix + ".focusedBackground" ); + iconTextGap = FlatUIUtils.getUIInt( prefix + ".iconTextGap", 4 ); defaultMargin = UIManager.getInsets( prefix + ".margin" ); @@ -142,20 +164,28 @@ public class FlatTextFieldUI @Override protected void propertyChange( PropertyChangeEvent e ) { super.propertyChange( e ); - propertyChange( getComponent(), e ); - } - static void propertyChange( JTextComponent c, PropertyChangeEvent e ) { + JTextComponent c = getComponent(); switch( e.getPropertyName() ) { - case FlatClientProperties.PLACEHOLDER_TEXT: - case FlatClientProperties.COMPONENT_ROUND_RECT: - case FlatClientProperties.TEXT_FIELD_PADDING: + case PLACEHOLDER_TEXT: + case COMPONENT_ROUND_RECT: + case TEXT_FIELD_PADDING: c.repaint(); break; - case FlatClientProperties.MINIMUM_WIDTH: + case MINIMUM_WIDTH: c.revalidate(); break; + + case TEXT_FIELD_LEADING_ICON: + leadingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null; + c.repaint(); + break; + + case TEXT_FIELD_TRAILING_ICON: + trailingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null; + c.repaint(); + break; } } @@ -164,6 +194,15 @@ public class FlatTextFieldUI paintBackground( g, getComponent(), isIntelliJTheme, focusedBackground ); paintPlaceholder( g ); + if( hasLeadingIcon() || hasTrailingIcon() ) + paintIcons( g, new Rectangle( getIconsRect() ) ); + +/*debug + Rectangle r = getVisibleEditorRect(); + g.setColor( Color.red ); + g.drawRect( r.x, r.y, r.width - 1, r.height - 1 ); +debug*/ + super.paintSafely( HiDPIUtils.createGraphicsTextYCorrection( (Graphics2D) g ) ); } @@ -230,15 +269,15 @@ public class FlatTextFieldUI JComponent jc = (parent instanceof JComboBox) ? (JComboBox) parent : c; // get placeholder text - Object placeholder = jc.getClientProperty( FlatClientProperties.PLACEHOLDER_TEXT ); - if( !(placeholder instanceof String) ) + String placeholder = clientProperty( jc, PLACEHOLDER_TEXT, null, String.class ); + if( placeholder == null ) return; // compute placeholder location Rectangle r = getVisibleEditorRect(); FontMetrics fm = c.getFontMetrics( c.getFont() ); - String clippedPlaceholder = JavaCompatibility.getClippedString( c, fm, (String) placeholder, r.width ); - int x = r.x + (c.getComponentOrientation().isLeftToRight() ? 0 : r.width - fm.stringWidth( clippedPlaceholder )); + String clippedPlaceholder = JavaCompatibility.getClippedString( c, fm, placeholder, r.width ); + int x = r.x + (isLeftToRight() ? 0 : r.width - fm.stringWidth( clippedPlaceholder )); int y = r.y + fm.getAscent() + ((r.height - fm.getHeight()) / 2); // paint placeholder @@ -246,6 +285,42 @@ public class FlatTextFieldUI FlatUIUtils.drawString( c, g, clippedPlaceholder, x, y ); } + /** + * Paints the leading and trailing icons in the given rectangle. + * The rectangle is updated by this method so that subclasses can use it + * without painting over leading or trailing icons. + * + * @since 2 + */ + protected void paintIcons( Graphics g, Rectangle r ) { + boolean ltr = isLeftToRight(); + Icon leftIcon = ltr ? leadingIcon : trailingIcon; + Icon rightIcon = ltr ? trailingIcon : leadingIcon; + + // paint left icon + if( leftIcon != null ) { + int x = r.x; + int y = r.y + Math.round( (r.height - leftIcon.getIconHeight()) / 2f ); + leftIcon.paintIcon( getComponent(), g, x, y ); + + // update rectangle so that subclasses can use it + int w = leftIcon.getIconWidth() + scale( iconTextGap ); + r.x += w; + r.width -= w; + } + + // paint right icon + if( rightIcon != null ) { + int iconWidth = rightIcon.getIconWidth(); + int x = r.x + r.width - iconWidth; + int y = r.y + Math.round( (r.height - rightIcon.getIconHeight()) / 2f ); + rightIcon.paintIcon( getComponent(), g, x, y ); + + // update rectangle so that subclasses can use it + r.width -= iconWidth + scale( iconTextGap ); + } + } + @Override public Dimension getPreferredSize( JComponent c ) { return applyMinimumWidth( c, super.getPreferredSize( c ), minimumWidth ); @@ -283,27 +358,104 @@ public class FlatTextFieldUI return margin instanceof UIResource && Objects.equals( margin, defaultMargin ); } + /** + * Returns the rectangle used for the root view of the text. + * This method is used to place the text. + */ @Override protected Rectangle getVisibleEditorRect() { + Rectangle r = getIconsRect(); + if( r == null ) + return null; + + // remove space needed for leading and trailing icons + int leading = getLeadingIconWidth(); + int trailing = getTrailingIconWidth(); + if( leading != 0 || trailing != 0 ) { + boolean ltr = isLeftToRight(); + int left = ltr ? leading : trailing; + int right = ltr ? trailing : leading; + r.x += left; + r.width -= left + right; + } + + // remove padding + Insets padding = getPadding(); + if( padding != null ) + r = FlatUIUtils.subtractInsets( r, padding ); + + // make sure that width and height are not negative + r.width = Math.max( r.width, 0 ); + r.height = Math.max( r.height, 0 ); + + return r; + } + + /** + * Returns the rectangle used to paint leading and trailing icons. + * It invokes {@code super.getVisibleEditorRect()} and reduces left and/or + * right margin if the text field has leading or trailing icons. + * + * @since 2 + */ + protected Rectangle getIconsRect() { Rectangle r = super.getVisibleEditorRect(); - if( r != null ) { - // remove padding - Insets padding = getPadding(); - if( padding != null ) { - r = FlatUIUtils.subtractInsets( r, padding ); - r.width = Math.max( r.width, 0 ); - r.height = Math.max( r.height, 0 ); + if( r == null ) + return null; + + // if a leading/trailing icon is shown, then the left/right margin is reduced + // to the top margin, which places the icon nicely centered on left/right side + boolean ltr = isLeftToRight(); + if( ltr ? hasLeadingIcon() : hasTrailingIcon() ) { + // reduce left margin + Insets margin = getComponent().getMargin(); + int newLeftMargin = Math.min( margin.left, margin.top ); + if( newLeftMargin < margin.left ) { + int diff = scale( margin.left - newLeftMargin ); + r.x -= diff; + r.width += diff; } } + if( ltr ? hasTrailingIcon() : hasLeadingIcon() ) { + // reduce right margin + Insets margin = getComponent().getMargin(); + int newRightMargin = Math.min( margin.right, margin.top ); + if( newRightMargin < margin.left ) + r.width += scale( margin.right - newRightMargin ); + } + return r; } + /** @since 2 */ + protected boolean hasLeadingIcon() { + return leadingIcon != null; + } + + /** @since 2 */ + protected boolean hasTrailingIcon() { + return trailingIcon != null; + } + + /** @since 2 */ + protected int getLeadingIconWidth() { + return (leadingIcon != null) ? leadingIcon.getIconWidth() + scale( iconTextGap ) : 0; + } + + /** @since 2 */ + protected int getTrailingIconWidth() { + return (trailingIcon != null) ? trailingIcon.getIconWidth() + scale( iconTextGap ) : 0; + } + + boolean isLeftToRight() { + return getComponent().getComponentOrientation().isLeftToRight(); + } + /** * @since 1.4 */ protected Insets getPadding() { - Object padding = getComponent().getClientProperty( FlatClientProperties.TEXT_FIELD_PADDING ); - return (padding instanceof Insets) ? UIScale.scale( (Insets) padding ) : null; + return scale( clientProperty( getComponent(), TEXT_FIELD_PADDING, null, Insets.class ) ); } /** 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 5688074e..dbf56770 100644 --- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties +++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties @@ -273,6 +273,7 @@ FormattedTextField.border = com.formdev.flatlaf.ui.FlatTextBorder FormattedTextField.margin = @textComponentMargin FormattedTextField.background = @textComponentBackground FormattedTextField.placeholderForeground = @disabledText +FormattedTextField.iconTextGap = 4 #---- HelpButton ---- @@ -413,6 +414,7 @@ PasswordField.border = com.formdev.flatlaf.ui.FlatTextBorder PasswordField.margin = @textComponentMargin PasswordField.background = @textComponentBackground PasswordField.placeholderForeground = @disabledText +PasswordField.iconTextGap = 4 PasswordField.echoChar = \u2022 PasswordField.showCapsLock = true PasswordField.capsLockIcon = com.formdev.flatlaf.icons.FlatCapsLockIcon @@ -680,6 +682,7 @@ TextField.border = com.formdev.flatlaf.ui.FlatTextBorder TextField.margin = @textComponentMargin TextField.background = @textComponentBackground TextField.placeholderForeground = @disabledText +TextField.iconTextGap = 4 #---- TextPane ---- 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 b545ba2e..2d9c57d2 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 @@ -19,6 +19,9 @@ package com.formdev.flatlaf.demo; import java.awt.Component; import javax.swing.*; import javax.swing.text.DefaultEditorKit; +import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.extras.FlatSVGIcon; +import com.formdev.flatlaf.icons.FlatSearchIcon; import net.miginfocom.swing.*; /** @@ -123,6 +126,10 @@ class BasicComponentsPanel JTextField warningHintsTextField = new JTextField(); JComboBox warningHintsComboBox = new JComboBox<>(); JSpinner warningHintsSpinner = new JSpinner(); + JLabel iconsLabel = new JLabel(); + JTextField leadingIconTextField = new JTextField(); + JTextField trailingIconTextField = new JTextField(); + JTextField iconsTextField = new JTextField(); JPopupMenu popupMenu1 = new JPopupMenu(); JMenuItem cutMenuItem = new JMenuItem(); JMenuItem copyMenuItem = new JMenuItem(); @@ -132,7 +139,7 @@ class BasicComponentsPanel setLayout(new MigLayout( "insets dialog,hidemode 3", // columns - "[sizegroup 1]" + + "[]" + "[sizegroup 1]" + "[sizegroup 1]" + "[sizegroup 1]" + @@ -152,6 +159,7 @@ class BasicComponentsPanel "[]" + "[]para" + "[]" + + "[]" + "[]")); //---- labelLabel ---- @@ -646,6 +654,19 @@ class BasicComponentsPanel warningHintsSpinner.putClientProperty("JComponent.outline", "warning"); add(warningHintsSpinner, "cell 3 13,growx"); + //---- iconsLabel ---- + iconsLabel.setText("Leading/trailing icons:"); + add(iconsLabel, "cell 0 14"); + add(leadingIconTextField, "cell 1 14,growx"); + + //---- trailingIconTextField ---- + trailingIconTextField.setText("text"); + add(trailingIconTextField, "cell 2 14,growx"); + + //---- iconsTextField ---- + iconsTextField.setText("text"); + add(iconsTextField, "cell 3 14,growx"); + //======== popupMenu1 ======== { @@ -670,8 +691,20 @@ class BasicComponentsPanel copyMenuItem.addActionListener( new DefaultEditorKit.CopyAction() ); pasteMenuItem.addActionListener( new DefaultEditorKit.PasteAction() ); + // add leading/trailing icons to text fields + leadingIconTextField.putClientProperty( FlatClientProperties.PLACEHOLDER_TEXT, "Search" ); + leadingIconTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_ICON, + new FlatSearchIcon() ); + trailingIconTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_TRAILING_ICON, + new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/DataTables.svg" ) ); + iconsTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_ICON, + new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/user.svg" ) ); + iconsTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_TRAILING_ICON, + new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/bookmarkGroup.svg" ) ); + if( FlatLafDemo.screenshotsMode ) { - Component[] components = { + // hide some components + Component[] hiddenComponents = { button13, button14, button15, button16, comboBox5, comboBox6, textField6, passwordField5, @@ -683,18 +716,26 @@ class BasicComponentsPanel errorHintsLabel, errorHintsTextField, errorHintsComboBox, errorHintsSpinner, warningHintsLabel, warningHintsTextField, warningHintsComboBox, warningHintsSpinner, }; - - for( Component c : components ) + for( Component c : hiddenComponents ) c.setVisible( false ); - // move password fields one row up + // move leading/trailing icon fields and password fields some rows up Component[] formattedTextFields = { formattedTextFieldLabel, formattedTextField1, formattedTextField2, formattedTextField3, formattedTextField4 }; Component[] passwordFields = { passwordFieldLabel, passwordField1, passwordField2, passwordField3, passwordField4 }; + Component[] iconsFields = { iconsLabel, leadingIconTextField, trailingIconTextField, iconsTextField }; MigLayout layout = (MigLayout) getLayout(); + for( int i = 0; i < iconsFields.length; i++ ) { + Object cons = layout.getComponentConstraints( passwordFields[i] ); + layout.setComponentConstraints( iconsFields[i], cons ); + } for( int i = 0; i < passwordFields.length; i++ ) { Object cons = layout.getComponentConstraints( formattedTextFields[i] ); layout.setComponentConstraints( passwordFields[i], cons ); } + + // make "Not editable disabled" combobox smaller + Object cons = layout.getComponentConstraints( comboBox4 ); + layout.setComponentConstraints( comboBox4, cons + ",width 50:50" ); } } diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/BasicComponentsPanel.jfd b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/BasicComponentsPanel.jfd index 75ed432e..53d6e8aa 100644 --- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/BasicComponentsPanel.jfd +++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/BasicComponentsPanel.jfd @@ -1,4 +1,4 @@ -JFDML JFormDesigner: "7.0.2.0.298" Java: "14" encoding: "UTF-8" +JFDML JFormDesigner: "7.0.4.0.360" Java: "16" encoding: "UTF-8" new FormModel { contentType: "form/swing" @@ -8,8 +8,8 @@ new FormModel { } add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "insets dialog,hidemode 3" - "$columnConstraints": "[sizegroup 1][sizegroup 1][sizegroup 1][sizegroup 1][][]" - "$rowConstraints": "[][][][][][][][][][][][]para[][]" + "$columnConstraints": "[][sizegroup 1][sizegroup 1][sizegroup 1][][]" + "$rowConstraints": "[][][][][][][][][][][][]para[][][]" } ) { name: "this" add( new FormComponent( "javax.swing.JLabel" ) { @@ -648,9 +648,32 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 3 13,growx" } ) + add( new FormComponent( "javax.swing.JLabel" ) { + name: "iconsLabel" + "text": "Leading/trailing icons:" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 14" + } ) + add( new FormComponent( "javax.swing.JTextField" ) { + name: "leadingIconTextField" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 1 14,growx" + } ) + add( new FormComponent( "javax.swing.JTextField" ) { + name: "trailingIconTextField" + "text": "text" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 2 14,growx" + } ) + add( new FormComponent( "javax.swing.JTextField" ) { + name: "iconsTextField" + "text": "text" + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 3 14,growx" + } ) }, new FormLayoutConstraints( null ) { "location": new java.awt.Point( 0, 0 ) - "size": new java.awt.Dimension( 920, 440 ) + "size": new java.awt.Dimension( 920, 480 ) } ) add( new FormContainer( "javax.swing.JPopupMenu", new FormLayoutManager( class javax.swing.JPopupMenu ) ) { name: "popupMenu1" diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/DataTables.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/DataTables.svg new file mode 100644 index 00000000..f69b635f --- /dev/null +++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/DataTables.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/bookmarkGroup.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/bookmarkGroup.svg new file mode 100644 index 00000000..c1fe3f6c --- /dev/null +++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/bookmarkGroup.svg @@ -0,0 +1,4 @@ + + + + diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/user.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/user.svg new file mode 100644 index 00000000..a073a132 --- /dev/null +++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/user.svg @@ -0,0 +1,4 @@ + + + + diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatFormattedTextField.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatFormattedTextField.java index 4ebc554d..0201e024 100644 --- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatFormattedTextField.java +++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatFormattedTextField.java @@ -19,6 +19,7 @@ package com.formdev.flatlaf.extras.components; import static com.formdev.flatlaf.FlatClientProperties.*; import java.awt.Color; import java.awt.Insets; +import javax.swing.Icon; import javax.swing.JFormattedTextField; import com.formdev.flatlaf.extras.components.FlatTextField.SelectAllOnFocusPolicy; @@ -46,6 +47,44 @@ public class FlatFormattedTextField } + /** + * Returns the leading icon that will be placed at the leading edge of the text field. + * + * @since 2 + */ + public Icon getLeadingIcon() { + return (Icon) getClientProperty( TEXT_FIELD_LEADING_ICON ); + } + + /** + * Specifies the leading icon that will be placed at the leading edge of the text field. + * + * @since 2 + */ + public void setLeadingIcon( Icon leadingIcon ) { + putClientProperty( TEXT_FIELD_LEADING_ICON, leadingIcon ); + } + + + /** + * Returns the trailing icon that will be placed at the trailing edge of the text field. + * + * @since 2 + */ + public Icon getTrailingIcon() { + return (Icon) getClientProperty( TEXT_FIELD_TRAILING_ICON ); + } + + /** + * Specifies the trailing icon that will be placed at the trailing edge of the text field. + * + * @since 2 + */ + public void setTrailingIcon( Icon trailingIcon ) { + putClientProperty( TEXT_FIELD_TRAILING_ICON, trailingIcon ); + } + + /** * Returns whether all text is selected when the text component gains focus. */ diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPasswordField.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPasswordField.java index 86495da2..4489e259 100644 --- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPasswordField.java +++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatPasswordField.java @@ -19,6 +19,7 @@ package com.formdev.flatlaf.extras.components; import static com.formdev.flatlaf.FlatClientProperties.*; import java.awt.Color; import java.awt.Insets; +import javax.swing.Icon; import javax.swing.JPasswordField; import com.formdev.flatlaf.extras.components.FlatTextField.SelectAllOnFocusPolicy; @@ -46,6 +47,44 @@ public class FlatPasswordField } + /** + * Returns the leading icon that will be placed at the leading edge of the text field. + * + * @since 2 + */ + public Icon getLeadingIcon() { + return (Icon) getClientProperty( TEXT_FIELD_LEADING_ICON ); + } + + /** + * Specifies the leading icon that will be placed at the leading edge of the text field. + * + * @since 2 + */ + public void setLeadingIcon( Icon leadingIcon ) { + putClientProperty( TEXT_FIELD_LEADING_ICON, leadingIcon ); + } + + + /** + * Returns the trailing icon that will be placed at the trailing edge of the text field. + * + * @since 2 + */ + public Icon getTrailingIcon() { + return (Icon) getClientProperty( TEXT_FIELD_TRAILING_ICON ); + } + + /** + * Specifies the trailing icon that will be placed at the trailing edge of the text field. + * + * @since 2 + */ + public void setTrailingIcon( Icon trailingIcon ) { + putClientProperty( TEXT_FIELD_TRAILING_ICON, trailingIcon ); + } + + /** * Returns whether all text is selected when the text component gains focus. */ diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextField.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextField.java index bfa22dd2..99627cf4 100644 --- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextField.java +++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/components/FlatTextField.java @@ -19,6 +19,7 @@ package com.formdev.flatlaf.extras.components; import static com.formdev.flatlaf.FlatClientProperties.*; import java.awt.Color; import java.awt.Insets; +import javax.swing.Icon; import javax.swing.JTextField; /** @@ -45,6 +46,44 @@ public class FlatTextField } + /** + * Returns the leading icon that will be placed at the leading edge of the text field. + * + * @since 2 + */ + public Icon getLeadingIcon() { + return (Icon) getClientProperty( TEXT_FIELD_LEADING_ICON ); + } + + /** + * Specifies the leading icon that will be placed at the leading edge of the text field. + * + * @since 2 + */ + public void setLeadingIcon( Icon leadingIcon ) { + putClientProperty( TEXT_FIELD_LEADING_ICON, leadingIcon ); + } + + + /** + * Returns the trailing icon that will be placed at the trailing edge of the text field. + * + * @since 2 + */ + public Icon getTrailingIcon() { + return (Icon) getClientProperty( TEXT_FIELD_TRAILING_ICON ); + } + + /** + * Specifies the trailing icon that will be placed at the trailing edge of the text field. + * + * @since 2 + */ + public void setTrailingIcon( Icon trailingIcon ) { + putClientProperty( TEXT_FIELD_TRAILING_ICON, trailingIcon ); + } + + // NOTE: enum names must be equal to allowed strings public enum SelectAllOnFocusPolicy { never, once, always }; diff --git a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt index 2f401fdc..2a1fc74e 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0_202.txt @@ -311,6 +311,7 @@ FormattedTextField.caretForeground #bbbbbb HSL 0 0 73 javax.swing.plaf. FormattedTextField.disabledBackground #3c3f41 HSL 204 4 25 javax.swing.plaf.ColorUIResource [UI] FormattedTextField.font [active] $defaultFont [UI] FormattedTextField.foreground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI] +FormattedTextField.iconTextGap 4 FormattedTextField.inactiveBackground #3c3f41 HSL 204 4 25 javax.swing.plaf.ColorUIResource [UI] FormattedTextField.inactiveForeground #888888 HSL 0 0 53 javax.swing.plaf.ColorUIResource [UI] FormattedTextField.margin 2,6,2,6 javax.swing.plaf.InsetsUIResource [UI] @@ -699,6 +700,7 @@ PasswordField.disabledBackground #3c3f41 HSL 204 4 25 javax.swing.plaf.Co PasswordField.echoChar '\u2022' PasswordField.font [active] $defaultFont [UI] PasswordField.foreground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI] +PasswordField.iconTextGap 4 PasswordField.inactiveBackground #3c3f41 HSL 204 4 25 javax.swing.plaf.ColorUIResource [UI] PasswordField.inactiveForeground #888888 HSL 0 0 53 javax.swing.plaf.ColorUIResource [UI] PasswordField.margin 2,6,2,6 javax.swing.plaf.InsetsUIResource [UI] @@ -1155,6 +1157,7 @@ TextField.disabledBackground #3c3f41 HSL 204 4 25 javax.swing.plaf.Colo TextField.font [active] $defaultFont [UI] TextField.foreground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI] TextField.highlight #242424 HSL 0 0 14 javax.swing.plaf.ColorUIResource [UI] +TextField.iconTextGap 4 TextField.inactiveBackground #3c3f41 HSL 204 4 25 javax.swing.plaf.ColorUIResource [UI] TextField.inactiveForeground #888888 HSL 0 0 53 javax.swing.plaf.ColorUIResource [UI] TextField.light #313131 HSL 0 0 19 javax.swing.plaf.ColorUIResource [UI] diff --git a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt index d3942343..02dee1f4 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0_202.txt @@ -315,6 +315,7 @@ FormattedTextField.caretForeground #000000 HSL 0 0 0 javax.swing.plaf. FormattedTextField.disabledBackground #f2f2f2 HSL 0 0 95 javax.swing.plaf.ColorUIResource [UI] FormattedTextField.font [active] $defaultFont [UI] FormattedTextField.foreground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI] +FormattedTextField.iconTextGap 4 FormattedTextField.inactiveBackground #f2f2f2 HSL 0 0 95 javax.swing.plaf.ColorUIResource [UI] FormattedTextField.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI] FormattedTextField.margin 2,6,2,6 javax.swing.plaf.InsetsUIResource [UI] @@ -704,6 +705,7 @@ PasswordField.disabledBackground #f2f2f2 HSL 0 0 95 javax.swing.plaf.Co PasswordField.echoChar '\u2022' PasswordField.font [active] $defaultFont [UI] PasswordField.foreground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI] +PasswordField.iconTextGap 4 PasswordField.inactiveBackground #f2f2f2 HSL 0 0 95 javax.swing.plaf.ColorUIResource [UI] PasswordField.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI] PasswordField.margin 2,6,2,6 javax.swing.plaf.InsetsUIResource [UI] @@ -1160,6 +1162,7 @@ TextField.disabledBackground #f2f2f2 HSL 0 0 95 javax.swing.plaf.Colo TextField.font [active] $defaultFont [UI] TextField.foreground #000000 HSL 0 0 0 javax.swing.plaf.ColorUIResource [UI] TextField.highlight #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] +TextField.iconTextGap 4 TextField.inactiveBackground #f2f2f2 HSL 0 0 95 javax.swing.plaf.ColorUIResource [UI] TextField.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI] TextField.light #e3e3e3 HSL 0 0 89 javax.swing.plaf.ColorUIResource [UI] diff --git a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt index 40d64308..5ce617d7 100644 --- a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt +++ b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0_202.txt @@ -311,6 +311,7 @@ FormattedTextField.disabledBackground #e0e0e0 HSL 0 0 88 javax.swing.pl FormattedTextField.focusedBackground #ffff88 HSL 60 100 77 javax.swing.plaf.ColorUIResource [UI] FormattedTextField.font [active] $defaultFont [UI] FormattedTextField.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] +FormattedTextField.iconTextGap 4 FormattedTextField.inactiveBackground #f0f0f0 HSL 0 0 94 javax.swing.plaf.ColorUIResource [UI] FormattedTextField.inactiveForeground #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI] FormattedTextField.margin 2,6,2,6 javax.swing.plaf.InsetsUIResource [UI] @@ -708,6 +709,7 @@ PasswordField.echoChar '\u2022' PasswordField.focusedBackground #ffff88 HSL 60 100 77 javax.swing.plaf.ColorUIResource [UI] PasswordField.font [active] $defaultFont [UI] PasswordField.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] +PasswordField.iconTextGap 4 PasswordField.inactiveBackground #f0f0f0 HSL 0 0 94 javax.swing.plaf.ColorUIResource [UI] PasswordField.inactiveForeground #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI] PasswordField.margin 2,6,2,6 javax.swing.plaf.InsetsUIResource [UI] @@ -1167,6 +1169,7 @@ TextField.focusedBackground #ffff88 HSL 60 100 77 javax.swing.plaf.Colo TextField.font [active] $defaultFont [UI] TextField.foreground #ff0000 HSL 0 100 50 javax.swing.plaf.ColorUIResource [UI] TextField.highlight #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI] +TextField.iconTextGap 4 TextField.inactiveBackground #f0f0f0 HSL 0 0 94 javax.swing.plaf.ColorUIResource [UI] TextField.inactiveForeground #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI] TextField.light #e3e3e3 HSL 0 0 89 javax.swing.plaf.ColorUIResource [UI] diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTextComponentsTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTextComponentsTest.java index 24ebced1..73e3af9a 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTextComponentsTest.java +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTextComponentsTest.java @@ -16,13 +16,16 @@ package com.formdev.flatlaf.testing; +import java.awt.Color; import java.awt.Component; import java.awt.Dimension; +import java.awt.Graphics; import java.awt.Insets; import javax.swing.*; import javax.swing.border.*; import javax.swing.text.DefaultEditorKit; import com.formdev.flatlaf.FlatClientProperties; +import com.formdev.flatlaf.util.UIScale; import net.miginfocom.swing.*; /** @@ -61,6 +64,23 @@ public class FlatTextComponentsTest } } + private void leadingIcon() { + applyIcon( FlatClientProperties.TEXT_FIELD_LEADING_ICON, leadingIconCheckBox.isSelected() + ? new TestIcon( 8, 16, Color.blue ) : null ); + } + + private void trailingIcon() { + applyIcon( FlatClientProperties.TEXT_FIELD_TRAILING_ICON, trailingIconCheckBox.isSelected() + ? new TestIcon( 24, 12, Color.magenta ) : null ); + } + + private void applyIcon( String key, Icon icon ) { + for( Component c : getComponents() ) { + if( c instanceof JTextField ) + ((JTextField)c).putClientProperty( key, icon ); + } + } + private void initComponents() { // JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents JLabel textFieldLabel = new JLabel(); @@ -80,6 +100,8 @@ public class FlatTextComponentsTest topPaddingField = new JSpinner(); JLabel bottomPaddingLabel = new JLabel(); bottomPaddingField = new JSpinner(); + leadingIconCheckBox = new JCheckBox(); + trailingIconCheckBox = new JCheckBox(); JLabel passwordFieldLabel = new JLabel(); JPasswordField passwordField1 = new JPasswordField(); JPasswordField passwordField3 = new JPasswordField(); @@ -158,12 +180,14 @@ public class FlatTextComponentsTest //---- textField1 ---- textField1.setText("editable"); textField1.setComponentPopupMenu(popupMenu1); + textField1.putClientProperty("JTextField.placeholderText", "place"); textField1.setName("textField1"); add(textField1, "cell 1 0,growx"); //---- textField3 ---- textField3.setText("longer text for testing horizontal scrolling"); textField3.setComponentPopupMenu(popupMenu1); + textField3.putClientProperty("JTextField.placeholderText", "place"); textField3.setName("textField3"); add(textField3, "cell 2 0,growx"); @@ -172,6 +196,7 @@ public class FlatTextComponentsTest textField2.setSelectionStart(1); textField2.setSelectionEnd(4); textField2.setComponentPopupMenu(popupMenu1); + textField2.putClientProperty("JTextField.placeholderText", "place"); textField2.setName("textField2"); add(textField2, "cell 3 0"); @@ -185,12 +210,14 @@ public class FlatTextComponentsTest //---- formattedTextField1 ---- formattedTextField1.setText("editable"); formattedTextField1.setComponentPopupMenu(popupMenu1); + formattedTextField1.putClientProperty("JTextField.placeholderText", "place"); formattedTextField1.setName("formattedTextField1"); add(formattedTextField1, "cell 1 1,growx"); //---- formattedTextField3 ---- formattedTextField3.setText("longer text for testing horizontal scrolling"); formattedTextField3.setComponentPopupMenu(popupMenu1); + formattedTextField3.putClientProperty("JTextField.placeholderText", "place"); formattedTextField3.setName("formattedTextField3"); add(formattedTextField3, "cell 2 1,growx"); @@ -208,6 +235,8 @@ public class FlatTextComponentsTest "[]" + "[]" + "[]" + + "[]" + + "[]" + "[]")); //---- button1 ---- @@ -255,6 +284,18 @@ public class FlatTextComponentsTest bottomPaddingField.setName("bottomPaddingField"); bottomPaddingField.addChangeListener(e -> paddingChanged()); panel1.add(bottomPaddingField, "cell 1 4"); + + //---- leadingIconCheckBox ---- + leadingIconCheckBox.setText("leading icon"); + leadingIconCheckBox.setName("leadingIconCheckBox"); + leadingIconCheckBox.addActionListener(e -> leadingIcon()); + panel1.add(leadingIconCheckBox, "cell 0 5 2 1,alignx left,growx 0"); + + //---- trailingIconCheckBox ---- + trailingIconCheckBox.setText("trailing icon"); + trailingIconCheckBox.setName("trailingIconCheckBox"); + trailingIconCheckBox.addActionListener(e -> trailingIcon()); + panel1.add(trailingIconCheckBox, "cell 0 6 2 1,alignx left,growx 0"); } add(panel1, "cell 4 0 1 6,aligny top,growy 0"); @@ -268,12 +309,14 @@ public class FlatTextComponentsTest //---- passwordField1 ---- passwordField1.setText("editable"); passwordField1.setComponentPopupMenu(popupMenu1); + passwordField1.putClientProperty("JTextField.placeholderText", "place"); passwordField1.setName("passwordField1"); add(passwordField1, "cell 1 2,growx"); //---- passwordField3 ---- passwordField3.setText("longer text for testing horizontal scrolling"); passwordField3.setComponentPopupMenu(popupMenu1); + passwordField3.putClientProperty("JTextField.placeholderText", "place"); passwordField3.setName("passwordField3"); add(passwordField3, "cell 2 2,growx"); @@ -508,5 +551,39 @@ public class FlatTextComponentsTest private JSpinner rightPaddingField; private JSpinner topPaddingField; private JSpinner bottomPaddingField; + private JCheckBox leadingIconCheckBox; + private JCheckBox trailingIconCheckBox; // JFormDesigner - End of variables declaration //GEN-END:variables + + //---- TestIcon ----------------------------------------------------------- + + private static class TestIcon + implements Icon + { + private final int width; + private final int height; + private final Color color; + + TestIcon( int width, int height, Color color ) { + this.width = width; + this.height = height; + this.color = color; + } + + @Override + public void paintIcon( Component c, Graphics g, int x, int y ) { + g.setColor( color ); + g.drawRect( x, y, getIconWidth() - 1, getIconHeight() - 1 ); + } + + @Override + public int getIconWidth() { + return UIScale.scale( width ); + } + + @Override + public int getIconHeight() { + return UIScale.scale( height ); + } + } } diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTextComponentsTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTextComponentsTest.jfd index 8ea38154..c26f61e4 100644 --- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTextComponentsTest.jfd +++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatTextComponentsTest.jfd @@ -25,6 +25,7 @@ new FormModel { name: "textField1" "text": "editable" "componentPopupMenu": &FormReference0 new FormReference( "popupMenu1" ) + "$client.JTextField.placeholderText": "place" auxiliary() { "JavaCodeGenerator.variableLocal": false } @@ -35,6 +36,7 @@ new FormModel { name: "textField3" "text": "longer text for testing horizontal scrolling" "componentPopupMenu": #FormReference0 + "$client.JTextField.placeholderText": "place" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 2 0,growx" } ) @@ -44,6 +46,7 @@ new FormModel { "selectionStart": 1 "selectionEnd": 4 "componentPopupMenu": #FormReference0 + "$client.JTextField.placeholderText": "place" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 3 0" } ) @@ -59,6 +62,7 @@ new FormModel { name: "formattedTextField1" "text": "editable" "componentPopupMenu": #FormReference0 + "$client.JTextField.placeholderText": "place" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 1,growx" } ) @@ -66,13 +70,14 @@ new FormModel { name: "formattedTextField3" "text": "longer text for testing horizontal scrolling" "componentPopupMenu": #FormReference0 + "$client.JTextField.placeholderText": "place" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 2 1,growx" } ) add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) { "$layoutConstraints": "hidemode 3" "$columnConstraints": "[fill][fill]" - "$rowConstraints": "[][][][][]" + "$rowConstraints": "[][][][][][][]" } ) { name: "panel1" "border": new javax.swing.border.TitledBorder( "Control" ) @@ -143,6 +148,26 @@ new FormModel { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 4" } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "leadingIconCheckBox" + "text": "leading icon" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "leadingIcon", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 5 2 1,alignx left,growx 0" + } ) + add( new FormComponent( "javax.swing.JCheckBox" ) { + name: "trailingIconCheckBox" + "text": "trailing icon" + auxiliary() { + "JavaCodeGenerator.variableLocal": false + } + addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "trailingIcon", false ) ) + }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { + "value": "cell 0 6 2 1,alignx left,growx 0" + } ) }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 4 0 1 6,aligny top,growy 0" } ) @@ -158,6 +183,7 @@ new FormModel { name: "passwordField1" "text": "editable" "componentPopupMenu": #FormReference0 + "$client.JTextField.placeholderText": "place" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 1 2,growx" } ) @@ -165,6 +191,7 @@ new FormModel { name: "passwordField3" "text": "longer text for testing horizontal scrolling" "componentPopupMenu": #FormReference0 + "$client.JTextField.placeholderText": "place" }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { "value": "cell 2 2,growx" } ) 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 c50e3328..47e37e9f 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 @@ -241,6 +241,7 @@ FormattedTextField.focusInputMap FormattedTextField.focusedBackground FormattedTextField.font FormattedTextField.foreground +FormattedTextField.iconTextGap FormattedTextField.inactiveBackground FormattedTextField.inactiveForeground FormattedTextField.margin @@ -523,6 +524,7 @@ PasswordField.focusInputMap PasswordField.focusedBackground PasswordField.font PasswordField.foreground +PasswordField.iconTextGap PasswordField.inactiveBackground PasswordField.inactiveForeground PasswordField.margin @@ -884,6 +886,7 @@ TextField.focusedBackground TextField.font TextField.foreground TextField.highlight +TextField.iconTextGap TextField.inactiveBackground TextField.inactiveForeground TextField.light