Merge PR #442: TextField clear button and PasswordField reveal password button

This commit is contained in:
Karl Tauber
2021-12-14 11:35:44 +01:00
23 changed files with 579 additions and 44 deletions

View File

@@ -28,12 +28,18 @@ FlatLaf Change Log
setting UI default `OptionPane.showIcon` to `true`. (issue #416)
- No longer show the Java "duke/cup" icon if no window icon image is set.
(issue #416)
- TextField, FormattedTextField and PasswordField: Support leading and trailing
icons (set client property `JTextField.leadingIcon` or
`JTextField.trailingIcon` to a `javax.swing.Icon`). (PR #378; issue #368)
- TextField, FormattedTextField and PasswordField: Support leading and trailing
components (set client property `JTextField.leadingComponent` or
`JTextField.trailingComponent` to a `java.awt.Component`). (PR #386)
- TextField, FormattedTextField and PasswordField:
- Support leading and trailing icons (set client property
`JTextField.leadingIcon` or `JTextField.trailingIcon` to a
`javax.swing.Icon`). (PR #378; issue #368)
- Support leading and trailing components (set client property
`JTextField.leadingComponent` or `JTextField.trailingComponent` to a
`java.awt.Component`). (PR #386)
- Support "clear" (or "cancel") button to empty text field. Only shown if text
field is not empty, editable and enabled. (set client property
`JTextField.showClearButton` to `true`). (PR #442)
- PasswordField: Support reveal (or "eye") button to show password. (see UI
value `PasswordField.showRevealButton`) (PR #442; issue #173)
- TextComponents: Double/triple-click-and-drag now extends selection by whole
words/lines.
- Theming improvements: Reworks core themes to make it easier to create new

View File

@@ -903,6 +903,47 @@ public interface FlatClientProperties
*/
String TEXT_FIELD_TRAILING_COMPONENT = "JTextField.trailingComponent";
/**
* Specifies whether a "clear" (or "cancel") button is shown on the trailing side
* if the text field is not empty, editable and enabled. Default is {@code false}.
* <p>
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
* <strong>Value type</strong> {@link java.lang.Boolean}
*
* @since 2
*/
String TEXT_FIELD_SHOW_CLEAR_BUTTON = "JTextField.showClearButton";
/**
* Specifies the callback that is invoked when a "clear" (or "cancel") button is clicked.
* If a callback is specified than it is responsible for clearing the text field.
* Without callback, the text field clears itself.
* <p>
* Either use a {@link java.lang.Runnable}:
* <pre>{@code
* myTextField.putClientProperty( "JTextField.clearCallback",
* (Runnable) () -> {
* // clear field here or cancel search
* } );
* }</pre>
* Or use a {@link java.util.function.Consumer}&lt;javax.swing.text.JTextComponent&gt;
* that receives the text field as parameter:
* <pre>{@code
* myTextField.putClientProperty( "JTextField.clearCallback",
* (Consumer<JTextComponent>) textField -> {
* // clear field here or cancel search
* } );
* }</pre>
* <p>
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
* <strong>Value type</strong> {@link java.lang.Runnable}
* or {@link java.util.function.Consumer}&lt;javax.swing.text.JTextComponent&gt;
*
* @see #TEXT_FIELD_SHOW_CLEAR_BUTTON
* @since 2
*/
String TEXT_FIELD_CLEAR_CALLBACK = "JTextField.clearCallback";
//---- JToggleButton ------------------------------------------------------
/**

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2021 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.icons;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import javax.swing.UIManager;
/**
* "eye" icon for {@link javax.swing.JPasswordField}.
*
* @uiDefault PasswordField.revealIconColor Color
*
* @author Karl Tauber
* @since 2
*/
public class FlatRevealIcon
extends FlatAbstractIcon
{
public FlatRevealIcon() {
super( 16, 16, UIManager.getColor( "PasswordField.revealIconColor" ) );
}
@Override
protected void paintIcon( Component c, Graphics2D g ) {
Path2D path = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path.append( new Ellipse2D.Float( 5.15f, 6.15f, 5.7f, 5.7f ), false );
path.append( new Ellipse2D.Float( 6, 7, 4, 4 ), false );
g.fill( path );
Path2D path2 = new Path2D.Float( Path2D.WIND_EVEN_ODD );
path2.append( new Ellipse2D.Float( 2.15f, 4.15f, 11.7f, 11.7f ), false );
path2.append( new Ellipse2D.Float( 3, 5, 10, 10 ), false );
Area area = new Area( path2 );
area.subtract( new Area( new Rectangle2D.Float( 0, 9.5f, 16, 16 ) ) );
g.fill( area );
}
}

View File

@@ -28,6 +28,7 @@ import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JToggleButton;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
@@ -73,18 +74,25 @@ import com.formdev.flatlaf.util.UIScale;
*
* @uiDefault PasswordField.echoChar character
* @uiDefault PasswordField.showCapsLock boolean
* @uiDefault PasswordField.showRevealButton boolean
* @uiDefault PasswordField.capsLockIcon Icon
* @uiDefault PasswordField.revealIcon Icon
*
* @author Karl Tauber
*/
public class FlatPasswordFieldUI
extends FlatTextFieldUI
{
private Character echoChar;
@Styleable protected boolean showCapsLock;
/** @since 2 */ @Styleable protected boolean showRevealButton;
protected Icon capsLockIcon;
/** @since 2 */ protected Icon revealIcon;
private KeyListener capsLockListener;
private boolean capsLockIconShared = true;
private JToggleButton revealButton;
public static ComponentUI createUI( JComponent c ) {
return new FlatPasswordFieldUI();
@@ -95,17 +103,33 @@ public class FlatPasswordFieldUI
return "PasswordField";
}
@Override
public void installUI( JComponent c ) {
super.installUI( c );
installRevealButton();
}
@Override
public void uninstallUI( JComponent c ) {
uninstallRevealButton();
super.uninstallUI( c );
}
@Override
protected void installDefaults() {
super.installDefaults();
String prefix = getPropertyPrefix();
Character echoChar = (Character) UIManager.get( prefix + ".echoChar" );
echoChar = (Character) UIManager.get( prefix + ".echoChar" );
if( echoChar != null )
LookAndFeel.installProperty( getComponent(), "echoChar", echoChar );
showCapsLock = UIManager.getBoolean( "PasswordField.showCapsLock" );
showRevealButton = UIManager.getBoolean( "PasswordField.showRevealButton" );
capsLockIcon = UIManager.getIcon( "PasswordField.capsLockIcon" );
revealIcon = UIManager.getIcon( "PasswordField.revealIcon" );
capsLockIconShared = true;
}
@@ -114,6 +138,7 @@ public class FlatPasswordFieldUI
super.uninstallDefaults();
capsLockIcon = null;
revealIcon = null;
}
@Override
@@ -168,6 +193,18 @@ public class FlatPasswordFieldUI
return "PasswordField";
}
@Override
protected void applyStyle( Object style ) {
boolean oldShowRevealButton = showRevealButton;
super.applyStyle( style );
if( showRevealButton != oldShowRevealButton ) {
uninstallRevealButton();
installRevealButton();
}
}
/** @since 2 */
@Override
protected Object applyStyleProperty( String key, Object value ) {
@@ -236,4 +273,39 @@ public class FlatPasswordFieldUI
return FlatUIUtils.isPermanentFocusOwner( c ) &&
Toolkit.getDefaultToolkit().getLockingKeyState( KeyEvent.VK_CAPS_LOCK );
}
/** @since 2 */
protected void installRevealButton() {
JTextComponent c = getComponent();
if( showRevealButton ) {
revealButton = createRevealButton();
installLayout();
c.add( revealButton );
}
}
/** @since 2 */
protected JToggleButton createRevealButton() {
JToggleButton button = new JToggleButton( revealIcon );
prepareLeadingOrTrailingComponent( button );
button.addActionListener( e -> {
LookAndFeel.installProperty( getComponent(), "echoChar", button.isSelected()
? '\0'
: (echoChar != null ? echoChar : '*'));
} );
return button;
}
/** @since 2 */
protected void uninstallRevealButton() {
if( revealButton != null ) {
getComponent().remove( revealButton );
revealButton = null;
}
}
@Override
protected JComponent[] getTrailingComponents() {
return new JComponent[] { trailingComponent, revealButton, clearButton };
}
}

View File

@@ -35,6 +35,7 @@ import java.beans.PropertyChangeEvent;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComboBox;
@@ -45,10 +46,13 @@ import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
import javax.swing.plaf.basic.BasicTextFieldUI;
import javax.swing.text.Caret;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import com.formdev.flatlaf.ui.FlatStylingSupport.Styleable;
import com.formdev.flatlaf.ui.FlatStylingSupport.StyleableUI;
@@ -103,6 +107,10 @@ public class FlatTextFieldUI
/** @since 2 */ @Styleable protected Icon trailingIcon;
/** @since 2 */ protected JComponent leadingComponent;
/** @since 2 */ protected JComponent trailingComponent;
/** @since 2 */ protected JComponent clearButton;
// only used via styling (not in UI defaults, but has likewise client properties)
/** @since 2 */ @Styleable protected boolean showClearButton;
private Color oldDisabledBackground;
private Color oldInactiveBackground;
@@ -110,6 +118,7 @@ public class FlatTextFieldUI
private Insets defaultMargin;
private FocusListener focusListener;
private DocumentListener documentListener;
private Map<String, Object> oldStyleValues;
private AtomicBoolean borderShared;
@@ -126,6 +135,7 @@ public class FlatTextFieldUI
installLeadingComponent();
installTrailingComponent();
installClearButton();
installStyle();
}
@@ -134,6 +144,7 @@ public class FlatTextFieldUI
public void uninstallUI( JComponent c ) {
uninstallLeadingComponent();
uninstallTrailingComponent();
uninstallClearButton();
super.uninstallUI( c );
@@ -196,6 +207,11 @@ public class FlatTextFieldUI
getComponent().removeFocusListener( focusListener );
focusListener = null;
if( documentListener != null ) {
getComponent().getDocument().removeDocumentListener( documentListener );
documentListener = null;
}
}
@Override
@@ -254,9 +270,47 @@ public class FlatTextFieldUI
c.revalidate();
c.repaint();
break;
case TEXT_FIELD_SHOW_CLEAR_BUTTON:
uninstallClearButton();
installClearButton();
c.revalidate();
c.repaint();
break;
case "enabled":
case "editable":
updateClearButton();
break;
case "document":
if( documentListener != null ) {
if( e.getOldValue() instanceof Document )
((Document)e.getOldValue()).removeDocumentListener( documentListener );
if( e.getNewValue() instanceof Document )
((Document)e.getNewValue()).addDocumentListener( documentListener );
updateClearButton();
}
break;
}
}
/** @since 2 */
protected void installDocumentListener() {
if( documentListener != null )
return;
documentListener = new FlatDocumentListener();
getComponent().getDocument().addDocumentListener( documentListener );
}
/** @since 2 */
protected void documentChanged( DocumentEvent e ) {
if( clearButton != null )
updateClearButton();
}
/** @since 2 */
protected void installStyle() {
try {
@@ -275,10 +329,15 @@ public class FlatTextFieldUI
protected void applyStyle( Object style ) {
oldDisabledBackground = disabledBackground;
oldInactiveBackground = inactiveBackground;
boolean oldShowClearButton = showClearButton;
oldStyleValues = FlatStylingSupport.parseAndApply( oldStyleValues, style, this::applyStyleProperty );
updateBackground();
if( showClearButton != oldShowClearButton ) {
uninstallClearButton();
installClearButton();
}
}
/** @since 2 */
@@ -474,10 +533,14 @@ debug*/
size.width += getLeadingIconWidth() + getTrailingIconWidth();
// add width of leading and trailing components
if( leadingComponent != null && leadingComponent.isVisible() )
size.width += leadingComponent.getPreferredSize().width;
if( trailingComponent != null && trailingComponent.isVisible() )
size.width += trailingComponent.getPreferredSize().width;
for( JComponent comp : getLeadingComponents() ) {
if( comp != null && comp.isVisible() )
size.width += comp.getPreferredSize().width;
}
for( JComponent comp : getTrailingComponents() ) {
if( comp != null && comp.isVisible() )
size.width += comp.getPreferredSize().width;
}
return size;
}
@@ -558,17 +621,24 @@ debug*/
boolean ltr = isLeftToRight();
// remove width of leading/trailing components
JComponent leftComponent = ltr ? leadingComponent : trailingComponent;
JComponent rightComponent = ltr ? trailingComponent : leadingComponent;
boolean leftVisible = leftComponent != null && leftComponent.isVisible();
boolean rightVisible = rightComponent != null && rightComponent.isVisible();
if( leftVisible ) {
int w = leftComponent.getPreferredSize().width;
r.x += w;
r.width -= w;
JComponent[] leftComponents = ltr ? getLeadingComponents() : getTrailingComponents();
JComponent[] rightComponents = ltr ? getTrailingComponents() : getLeadingComponents();
boolean leftVisible = false;
boolean rightVisible = false;
for( JComponent leftComponent : leftComponents ) {
if( leftComponent != null && leftComponent.isVisible() ) {
int w = leftComponent.getPreferredSize().width;
r.x += w;
r.width -= w;
leftVisible = true;
}
}
for( JComponent rightComponent : rightComponents ) {
if( rightComponent != null && rightComponent.isVisible() ) {
r.width -= rightComponent.getPreferredSize().width;
rightVisible = true;
}
}
if( rightVisible )
r.width -= rightComponent.getPreferredSize().width;
// if a leading/trailing icons (or components) are shown, then the left/right margins are reduced
// to the top margin, which places the icon nicely centered on left/right side
@@ -671,6 +741,87 @@ debug*/
}
}
/** @since 2 */
protected void installClearButton() {
JTextComponent c = getComponent();
if( clientPropertyBoolean( c, TEXT_FIELD_SHOW_CLEAR_BUTTON, showClearButton ) ) {
clearButton = createClearButton();
updateClearButton();
installDocumentListener();
installLayout();
c.add( clearButton );
}
}
/** @since 2 */
protected void uninstallClearButton() {
if( clearButton != null ) {
getComponent().remove( clearButton );
clearButton = null;
}
}
/** @since 2 */
protected JComponent createClearButton() {
JButton button = new JButton();
button.putClientProperty( STYLE_CLASS, "clearButton" );
button.putClientProperty( BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON );
button.setCursor( Cursor.getDefaultCursor() );
button.addActionListener( e -> clearButtonClicked() );
return button;
}
/** @since 2 */
@SuppressWarnings( "unchecked" )
protected void clearButtonClicked() {
JTextComponent c = getComponent();
Object callback = c.getClientProperty( TEXT_FIELD_CLEAR_CALLBACK );
if( callback instanceof Runnable )
((Runnable)callback).run();
else if( callback instanceof Consumer )
((Consumer<JTextComponent>)callback).accept( c );
else
c.setText( "" );
}
/** @since 2 */
protected void updateClearButton() {
if( clearButton == null )
return;
JTextComponent c = getComponent();
boolean visible = c.isEnabled() && c.isEditable() && c.getDocument().getLength() > 0;
if( visible != clearButton.isVisible() ) {
clearButton.setVisible( visible );
c.revalidate();
c.repaint();
}
}
/**
* Returns components placed at the leading side of the text field.
* The returned array may contain {@code null}.
* The default implementation returns {@link #leadingComponent}.
*
* @since 2
*/
protected JComponent[] getLeadingComponents() {
return new JComponent[] { leadingComponent };
}
/**
* Returns components placed at the trailing side of the text field.
* The returned array may contain {@code null}.
* The default implementation returns {@link #trailingComponent} and {@link #clearButton}.
* <p>
* <strong>Note</strong>: The components in the array must be in reverse (visual) order.
*
* @since 2
*/
protected JComponent[] getTrailingComponents() {
return new JComponent[] { trailingComponent, clearButton };
}
/** @since 2 */
protected void prepareLeadingOrTrailingComponent( JComponent c ) {
c.putClientProperty( STYLE_CLASS, "inTextField" );
@@ -686,7 +837,8 @@ debug*/
}
}
private void installLayout() {
/** @since 2 */
protected void installLayout() {
JTextComponent c = getComponent();
LayoutManager oldLayout = c.getLayout();
if( !(oldLayout instanceof FlatTextFieldLayout) )
@@ -731,25 +883,30 @@ debug*/
if( delegate != null )
delegate.layoutContainer( parent );
if( leadingComponent == null && trailingComponent == null )
return;
int ow = FlatUIUtils.getBorderFocusAndLineWidth( getComponent() );
int h = parent.getHeight() - ow - ow;
boolean ltr = isLeftToRight();
JComponent leftComponent = ltr ? leadingComponent : trailingComponent;
JComponent rightComponent = ltr ? trailingComponent : leadingComponent;
JComponent[] leftComponents = ltr ? getLeadingComponents() : getTrailingComponents();
JComponent[] rightComponents = ltr ? getTrailingComponents() : getLeadingComponents();
// layout left component
if( leftComponent != null && leftComponent.isVisible() ) {
int w = leftComponent.getPreferredSize().width;
leftComponent.setBounds( ow, ow, w, h );
// layout left components
int x = ow;
for( JComponent leftComponent : leftComponents ) {
if( leftComponent != null && leftComponent.isVisible() ) {
int cw = leftComponent.getPreferredSize().width;
leftComponent.setBounds( x, ow, cw, h );
x += cw;
}
}
// layout right component
if( rightComponent != null && rightComponent.isVisible() ) {
int w = rightComponent.getPreferredSize().width;
rightComponent.setBounds( parent.getWidth() - ow - w, ow, w, h );
// layout right components
x = parent.getWidth() - ow;
for( JComponent rightComponent : rightComponents ) {
if( rightComponent != null && rightComponent.isVisible() ) {
int cw = rightComponent.getPreferredSize().width;
x -= cw;
rightComponent.setBounds( x, ow, cw, h );
}
}
}
@@ -780,4 +937,24 @@ debug*/
((LayoutManager2)delegate).invalidateLayout( target );
}
}
//---- class FlatDocumentListener -----------------------------------------
private class FlatDocumentListener
implements DocumentListener
{
@Override
public void insertUpdate( DocumentEvent e ) {
documentChanged( e );
}
@Override
public void removeUpdate( DocumentEvent e ) {
documentChanged( e );
}
@Override
public void changedUpdate( DocumentEvent e ) {
documentChanged( e );
}
}
}

View File

@@ -484,7 +484,10 @@ PasswordField.placeholderForeground = @disabledForeground
PasswordField.iconTextGap = 4
PasswordField.echoChar = \u2022
PasswordField.showCapsLock = true
PasswordField.showRevealButton = false
PasswordField.capsLockIcon = com.formdev.flatlaf.icons.FlatCapsLockIcon
PasswordField.revealIcon = com.formdev.flatlaf.icons.FlatRevealIcon
PasswordField.revealIconColor = lazy(Actions.Grey)
#---- Popup ----
@@ -907,3 +910,16 @@ Tree.icon.openColor = @icon
[style]ToolBarSeparator.inTextField = \
separatorWidth: 3
#---- clearButton ----
# for clear/cancel button in text fields
[style]Button.clearButton = \
icon: com.formdev.flatlaf.icons.FlatClearIcon; \
focusable: false; \
toolbar.margin: 1,1,1,1; \
toolbar.spacingInsets: 1,1,1,1; \
background: $TextField.background; \
toolbar.hoverBackground: $TextField.background; \
toolbar.pressedBackground: $TextField.background

View File

@@ -398,7 +398,8 @@ public class TestFlatStyleableInfo
Map<String, Class<?>> expected = new LinkedHashMap<>();
expectedMap( expected,
"showCapsLock", boolean.class
"showCapsLock", boolean.class,
"showRevealButton", boolean.class
);
// FlatPasswordFieldUI extends FlatTextFieldUI
@@ -823,7 +824,8 @@ public class TestFlatStyleableInfo
"focusedBackground", Color.class,
"iconTextGap", int.class,
"leadingIcon", Icon.class,
"trailingIcon", Icon.class
"trailingIcon", Icon.class,
"showClearButton", boolean.class
);
// border

View File

@@ -551,6 +551,7 @@ public class TestFlatStyling
textField( ui );
ui.applyStyle( "showCapsLock: true" );
ui.applyStyle( "showRevealButton: true" );
// capsLockIcon
ui.applyStyle( "capsLockIconColor: #fff" );
@@ -1011,6 +1012,8 @@ public class TestFlatStyling
ui.applyStyle( "leadingIcon: com.formdev.flatlaf.icons.FlatSearchIcon" );
ui.applyStyle( "trailingIcon: com.formdev.flatlaf.icons.FlatClearIcon" );
ui.applyStyle( "showClearButton: true" );
// border
flatTextBorder( style -> ui.applyStyle( style ) );

View File

@@ -38,6 +38,11 @@ class BasicComponentsPanel
BasicComponentsPanel() {
initComponents();
// show reveal button for password field
// to enable this for all password fields use:
// UIManager.put( "PasswordField.showRevealButton", true );
passwordField1.putClientProperty( FlatClientProperties.STYLE, "showRevealButton: true" );
// search history button
JButton searchHistoryButton = new JButton( new FlatSearchWithHistoryIcon( true ) );
searchHistoryButton.setToolTipText( "Search History" );
@@ -73,6 +78,10 @@ class BasicComponentsPanel
searchToolbar.addSeparator();
searchToolbar.add( regexButton );
compsTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT, searchToolbar );
// show clear button (if text field is not empty)
compsTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true );
clearTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON, true );
}
private void initComponents() {
@@ -124,7 +133,7 @@ class BasicComponentsPanel
JFormattedTextField formattedTextField4 = new JFormattedTextField();
JFormattedTextField formattedTextField5 = new JFormattedTextField();
JLabel passwordFieldLabel = new JLabel();
JPasswordField passwordField1 = new JPasswordField();
passwordField1 = new JPasswordField();
JPasswordField passwordField2 = new JPasswordField();
JPasswordField passwordField3 = new JPasswordField();
JPasswordField passwordField4 = new JPasswordField();
@@ -173,6 +182,7 @@ class BasicComponentsPanel
JTextField iconsTextField = new JTextField();
JLabel compsLabel = new JLabel();
compsTextField = new JTextField();
clearTextField = new JTextField();
JLabel fontsLabel = new JLabel();
JLabel h00Label = new JLabel();
JLabel h0Label = new JLabel();
@@ -734,6 +744,10 @@ class BasicComponentsPanel
add(compsLabel, "cell 0 15");
add(compsTextField, "cell 1 15 2 1,growx");
//---- clearTextField ----
clearTextField.setText("clear me");
add(clearTextField, "cell 3 15,growx");
//---- fontsLabel ----
fontsLabel.setText("Typography / Fonts:");
add(fontsLabel, "cell 0 16");
@@ -904,6 +918,8 @@ class BasicComponentsPanel
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
private JPasswordField passwordField1;
private JTextField compsTextField;
private JTextField clearTextField;
// JFormDesigner - End of variables declaration //GEN-END:variables
}

View File

@@ -375,6 +375,9 @@ new FormModel {
add( new FormComponent( "javax.swing.JPasswordField" ) {
name: "passwordField1"
"text": "Editable"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 8,growx"
} )
@@ -685,6 +688,15 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 15 2 1,growx"
} )
add( new FormComponent( "javax.swing.JTextField" ) {
name: "clearTextField"
"text": "clear me"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 15,growx"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fontsLabel"
"text": "Typography / Fonts:"

View File

@@ -43,6 +43,7 @@ import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumnModel;
import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.extras.components.FlatTextField;
import com.formdev.flatlaf.icons.FlatAbstractIcon;
import com.formdev.flatlaf.ui.FlatBorder;
import com.formdev.flatlaf.ui.FlatEmptyBorder;
@@ -557,7 +558,7 @@ public class FlatUIDefaultsInspector
panel = new JPanel();
filterPanel = new JPanel();
flterLabel = new JLabel();
filterField = new JTextField();
filterField = new FlatTextField();
valueTypeLabel = new JLabel();
valueTypeField = new JComboBox<>();
scrollPane = new JScrollPane();
@@ -588,7 +589,8 @@ public class FlatUIDefaultsInspector
new Insets(0, 0, 0, 10), 0, 0));
//---- filterField ----
filterField.putClientProperty("JTextField.placeholderText", "enter one or more filter strings, separated by space characters");
filterField.setPlaceholderText("enter one or more filter strings, separated by space characters");
filterField.setShowClearButton(true);
filterPanel.add(filterField, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 10), 0, 0));
@@ -667,7 +669,7 @@ public class FlatUIDefaultsInspector
private JPanel panel;
private JPanel filterPanel;
private JLabel flterLabel;
private JTextField filterField;
private FlatTextField filterField;
private JLabel valueTypeLabel;
private JComboBox<String> valueTypeField;
private JScrollPane scrollPane;

View File

@@ -20,9 +20,10 @@ new FormModel {
"labelFor": new FormReference( "filterField" )
"displayedMnemonic": 70
}, new FormLayoutConstraints( class com.jformdesigner.runtime.GridBagConstraintsEx ) )
add( new FormComponent( "javax.swing.JTextField" ) {
add( new FormComponent( "com.formdev.flatlaf.extras.components.FlatTextField" ) {
name: "filterField"
"$client.JTextField.placeholderText": "enter one or more filter strings, separated by space characters"
"placeholderText": "enter one or more filter strings, separated by space characters"
"showClearButton": true
}, new FormLayoutConstraints( class com.jformdesigner.runtime.GridBagConstraintsEx ) {
"gridx": 1
} )

View File

@@ -144,6 +144,26 @@ public class FlatFormattedTextField
}
/**
* Returns whether a "clear" (or "cancel") button is shown.
*
* @since 2
*/
public boolean isShowClearButton() {
return getClientPropertyBoolean( TEXT_FIELD_SHOW_CLEAR_BUTTON, false );
}
/**
* Specifies whether a "clear" (or "cancel") button is shown on the trailing side
* if the text field is not empty, editable and enabled.
*
* @since 2
*/
public void setShowClearButton( boolean showClearButton ) {
putClientPropertyBoolean( TEXT_FIELD_SHOW_CLEAR_BUTTON, showClearButton, false );
}
/**
* Returns whether all text is selected when the text component gains focus.
*/

View File

@@ -144,6 +144,26 @@ public class FlatPasswordField
}
/**
* Returns whether a "clear" (or "cancel") button is shown.
*
* @since 2
*/
public boolean isShowClearButton() {
return getClientPropertyBoolean( TEXT_FIELD_SHOW_CLEAR_BUTTON, false );
}
/**
* Specifies whether a "clear" (or "cancel") button is shown on the trailing side
* if the text field is not empty, editable and enabled.
*
* @since 2
*/
public void setShowClearButton( boolean showClearButton ) {
putClientPropertyBoolean( TEXT_FIELD_SHOW_CLEAR_BUTTON, showClearButton, false );
}
/**
* Returns whether all text is selected when the text component gains focus.
*/

View File

@@ -143,6 +143,26 @@ public class FlatTextField
}
/**
* Returns whether a "clear" (or "cancel") button is shown.
*
* @since 2
*/
public boolean isShowClearButton() {
return getClientPropertyBoolean( TEXT_FIELD_SHOW_CLEAR_BUTTON, false );
}
/**
* Specifies whether a "clear" (or "cancel") button is shown on the trailing side
* if the text field is not empty, editable and enabled.
*
* @since 2
*/
public void setShowClearButton( boolean showClearButton ) {
putClientPropertyBoolean( TEXT_FIELD_SHOW_CLEAR_BUTTON, showClearButton, false );
}
// NOTE: enum names must be equal to allowed strings
public enum SelectAllOnFocusPolicy { never, once, always };

View File

@@ -711,9 +711,12 @@ PasswordField.inactiveBackground #3c3f41 HSL 204 4 25 javax.swing.plaf.Co
PasswordField.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
PasswordField.margin 2,6,2,6 javax.swing.plaf.InsetsUIResource [UI]
PasswordField.placeholderForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
PasswordField.revealIcon [lazy] 16,16 com.formdev.flatlaf.icons.FlatRevealIcon [UI]
PasswordField.revealIconColor [lazy] #afb1b3 HSL 210 3 69 javax.swing.plaf.ColorUIResource [UI]
PasswordField.selectionBackground #4b6eaf HSL 219 40 49 javax.swing.plaf.ColorUIResource [UI]
PasswordField.selectionForeground #bbbbbb HSL 0 0 73 javax.swing.plaf.ColorUIResource [UI]
PasswordField.showCapsLock true
PasswordField.showRevealButton false
PasswordFieldUI com.formdev.flatlaf.ui.FlatPasswordFieldUI
@@ -1421,6 +1424,7 @@ ViewportUI com.formdev.flatlaf.ui.FlatViewportUI
#---- [style]Button ----
[style]Button.clearButton icon: com.formdev.flatlaf.icons.FlatClearIcon; focusable: false; toolbar.margin: 1,1,1,1; toolbar.spacingInsets: 1,1,1,1; background: $TextField.background; toolbar.hoverBackground: $TextField.background; toolbar.pressedBackground: $TextField.background
[style]Button.inTextField focusable: false; toolbar.margin: 1,1,1,1; toolbar.spacingInsets: 1,1,1,1; background: $TextField.background; toolbar.hoverBackground: lighten($TextField.background,4%,derived); toolbar.pressedBackground: lighten($TextField.background,6%,derived); toolbar.selectedBackground: lighten($TextField.background,12%,derived)

View File

@@ -716,9 +716,12 @@ PasswordField.inactiveBackground #f2f2f2 HSL 0 0 95 javax.swing.plaf.Co
PasswordField.inactiveForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
PasswordField.margin 2,6,2,6 javax.swing.plaf.InsetsUIResource [UI]
PasswordField.placeholderForeground #8c8c8c HSL 0 0 55 javax.swing.plaf.ColorUIResource [UI]
PasswordField.revealIcon [lazy] 16,16 com.formdev.flatlaf.icons.FlatRevealIcon [UI]
PasswordField.revealIconColor [lazy] #6e6e6e HSL 0 0 43 javax.swing.plaf.ColorUIResource [UI]
PasswordField.selectionBackground #2675bf HSL 209 67 45 javax.swing.plaf.ColorUIResource [UI]
PasswordField.selectionForeground #ffffff HSL 0 0 100 javax.swing.plaf.ColorUIResource [UI]
PasswordField.showCapsLock true
PasswordField.showRevealButton false
PasswordFieldUI com.formdev.flatlaf.ui.FlatPasswordFieldUI
@@ -1426,6 +1429,7 @@ ViewportUI com.formdev.flatlaf.ui.FlatViewportUI
#---- [style]Button ----
[style]Button.clearButton icon: com.formdev.flatlaf.icons.FlatClearIcon; focusable: false; toolbar.margin: 1,1,1,1; toolbar.spacingInsets: 1,1,1,1; background: $TextField.background; toolbar.hoverBackground: $TextField.background; toolbar.pressedBackground: $TextField.background
[style]Button.inTextField focusable: false; toolbar.margin: 1,1,1,1; toolbar.spacingInsets: 1,1,1,1; background: $TextField.background; toolbar.hoverBackground: darken($TextField.background,4%,derived); toolbar.pressedBackground: darken($TextField.background,8%,derived); toolbar.selectedBackground: darken($TextField.background,12%,derived)

View File

@@ -726,9 +726,12 @@ PasswordField.inactiveBackground #f0f0f0 HSL 0 0 94 javax.swing.plaf.Co
PasswordField.inactiveForeground #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
PasswordField.margin 2,6,2,6 javax.swing.plaf.InsetsUIResource [UI]
PasswordField.placeholderForeground #000088 HSL 240 100 27 javax.swing.plaf.ColorUIResource [UI]
PasswordField.revealIcon [lazy] 16,16 com.formdev.flatlaf.icons.FlatRevealIcon [UI]
PasswordField.revealIconColor [lazy] #6e6e6e HSL 0 0 43 javax.swing.plaf.ColorUIResource [UI]
PasswordField.selectionBackground #00aa00 HSL 120 100 33 javax.swing.plaf.ColorUIResource [UI]
PasswordField.selectionForeground #ffff00 HSL 60 100 50 javax.swing.plaf.ColorUIResource [UI]
PasswordField.showCapsLock true
PasswordField.showRevealButton false
PasswordFieldUI com.formdev.flatlaf.ui.FlatPasswordFieldUI
@@ -1439,6 +1442,7 @@ ViewportUI com.formdev.flatlaf.ui.FlatViewportUI
#---- [style]Button ----
[style]Button.clearButton icon: com.formdev.flatlaf.icons.FlatClearIcon; focusable: false; toolbar.margin: 1,1,1,1; toolbar.spacingInsets: 1,1,1,1; background: $TextField.background; toolbar.hoverBackground: $TextField.background; toolbar.pressedBackground: $TextField.background
[style]Button.inTextField focusable: false; toolbar.margin: 1,1,1,1; toolbar.spacingInsets: 1,1,1,1

View File

@@ -121,6 +121,19 @@ public class FlatTextComponentsTest
}
}
private void showClearButton() {
putTextFieldClientProperty( FlatClientProperties.TEXT_FIELD_SHOW_CLEAR_BUTTON,
showClearButtonCheckBox.isSelected() );
}
private void showRevealButton() {
for( Component c : getComponents() ) {
if( c instanceof JPasswordField )
((JPasswordField)c).putClientProperty(FlatClientProperties.STYLE,
showRevealButtonCheckBox.isSelected() ? "showRevealButton: true" : null );
}
}
private void putTextFieldClientProperty( String key, Object value ) {
for( Component c : getComponents() ) {
if( c instanceof JTextField )
@@ -168,6 +181,8 @@ public class FlatTextComponentsTest
trailingComponentCheckBox = new JCheckBox();
leadingComponentVisibleCheckBox = new JCheckBox();
trailingComponentVisibleCheckBox = new JCheckBox();
showClearButtonCheckBox = new JCheckBox();
showRevealButtonCheckBox = new JCheckBox();
JLabel passwordFieldLabel = new JLabel();
JPasswordField passwordField1 = new JPasswordField();
JPasswordField passwordField3 = new JPasswordField();
@@ -319,6 +334,8 @@ public class FlatTextComponentsTest
"[]0" +
"[]" +
"[]0" +
"[]" +
"[]" +
"[]"));
//---- button1 ----
@@ -404,6 +421,18 @@ public class FlatTextComponentsTest
trailingComponentVisibleCheckBox.setName("trailingComponentVisibleCheckBox");
trailingComponentVisibleCheckBox.addActionListener(e -> trailingComponentVisible());
panel1.add(trailingComponentVisibleCheckBox, "cell 0 10 2 1,alignx left,growx 0");
//---- showClearButtonCheckBox ----
showClearButtonCheckBox.setText("clear button");
showClearButtonCheckBox.setName("showClearButtonCheckBox");
showClearButtonCheckBox.addActionListener(e -> showClearButton());
panel1.add(showClearButtonCheckBox, "cell 0 11 2 1,alignx left,growx 0");
//---- showRevealButtonCheckBox ----
showRevealButtonCheckBox.setText("password reveal button");
showRevealButtonCheckBox.setName("showRevealButtonCheckBox");
showRevealButtonCheckBox.addActionListener(e -> showRevealButton());
panel1.add(showRevealButtonCheckBox, "cell 0 12 2 1,alignx left,growx 0");
}
add(panel1, "cell 4 0 1 10,aligny top,growy 0");
@@ -719,6 +748,8 @@ public class FlatTextComponentsTest
private JCheckBox trailingComponentCheckBox;
private JCheckBox leadingComponentVisibleCheckBox;
private JCheckBox trailingComponentVisibleCheckBox;
private JCheckBox showClearButtonCheckBox;
private JCheckBox showRevealButtonCheckBox;
private JTextField textField;
private JCheckBox dragEnabledCheckBox;
private JTextArea textArea;

View File

@@ -77,7 +77,7 @@ new FormModel {
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "hidemode 3"
"$columnConstraints": "[fill][fill]"
"$rowConstraints": "[][][][][][]0[][]0[][]0[]"
"$rowConstraints": "[][][][][][]0[][]0[][]0[][][]"
} ) {
name: "panel1"
"border": new javax.swing.border.TitledBorder( "Control" )
@@ -210,6 +210,26 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 10 2 1,alignx left,growx 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showClearButtonCheckBox"
"text": "clear button"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showClearButton", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 11 2 1,alignx left,growx 0"
} )
add( new FormComponent( "javax.swing.JCheckBox" ) {
name: "showRevealButtonCheckBox"
"text": "password reveal button"
auxiliary() {
"JavaCodeGenerator.variableLocal": false
}
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "showRevealButton", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 12 2 1,alignx left,growx 0"
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 4 0 1 10,aligny top,growy 0"
} )

View File

@@ -304,6 +304,7 @@ class FlatFindReplaceBar
//---- findField ----
findField.setColumns(16);
findField.setSelectAllOnFocusPolicy(FlatTextField.SelectAllOnFocusPolicy.always);
findField.setShowClearButton(true);
findField.addActionListener(e -> find());
add(findField, "cell 1 0");
@@ -365,6 +366,7 @@ class FlatFindReplaceBar
//---- replaceField ----
replaceField.setColumns(16);
replaceField.setSelectAllOnFocusPolicy(FlatTextField.SelectAllOnFocusPolicy.always);
replaceField.setShowClearButton(true);
add(replaceField, "cell 1 1");
//======== toolBar1 ========

View File

@@ -22,6 +22,7 @@ new FormModel {
name: "findField"
"columns": 16
"selectAllOnFocusPolicy": enum com.formdev.flatlaf.extras.components.FlatTextField$SelectAllOnFocusPolicy always
"showClearButton": true
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "find", false ) )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 0"
@@ -91,6 +92,7 @@ new FormModel {
name: "replaceField"
"columns": 16
"selectAllOnFocusPolicy": enum com.formdev.flatlaf.extras.components.FlatTextField$SelectAllOnFocusPolicy always
"showClearButton": true
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
} )

View File

@@ -557,9 +557,12 @@ PasswordField.inactiveBackground
PasswordField.inactiveForeground
PasswordField.margin
PasswordField.placeholderForeground
PasswordField.revealIcon
PasswordField.revealIconColor
PasswordField.selectionBackground
PasswordField.selectionForeground
PasswordField.showCapsLock
PasswordField.showRevealButton
PasswordFieldUI
Popup.dropShadowColor
Popup.dropShadowInsets
@@ -1115,6 +1118,7 @@ ViewportUI
[style].monospaced
[style].semibold
[style].small
[style]Button.clearButton
[style]Button.inTextField
[style]ToggleButton.inTextField
[style]ToolBar.inTextField