diff --git a/CHANGELOG.md b/CHANGELOG.md index da6f25ae..435808d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,10 @@ FlatLaf Change Log (issue #416) - TextField, FormattedTextField and PasswordField: Support leading and trailing icons (set client property `JTextField.leadingIcon` or - `JTextField.trailingIcon` to an `Icon`). (PR #378; issue #368) + `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) - 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 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 4af43c96..dd5e1242 100644 --- a/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java +++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/FlatClientProperties.java @@ -763,9 +763,9 @@ public interface FlatClientProperties /** * Specifies a component that will be placed at the leading edge of the tabs area. *
- * For top and bottom tab placement, the layed out component size will be
+ * For top and bottom tab placement, the laid out component size will be
* the preferred component width and the tab area height.
- * For left and right tab placement, the layed out component size will be
+ * For left and right tab placement, the laid out component size will be
* the tab area width and the preferred component height.
*
* Component {@link javax.swing.JTabbedPane}
@@ -776,9 +776,9 @@ public interface FlatClientProperties
/**
* Specifies a component that will be placed at the trailing edge of the tabs area.
*
- * For top and bottom tab placement, the layed out component size will be
+ * For top and bottom tab placement, the laid out component size will be
* the available horizontal space (minimum is preferred component width) and the tab area height.
- * For left and right tab placement, the layed out component size will be
+ * For left and right tab placement, the laid out component size will be
* the tab area width and the available vertical space (minimum is preferred component height).
*
* Component {@link javax.swing.JTabbedPane}
@@ -863,6 +863,46 @@ public interface FlatClientProperties
*/
String TEXT_FIELD_TRAILING_ICON = "JTextField.trailingIcon";
+ /**
+ * Specifies a component that will be placed at the leading edge of the text field.
+ *
+ * The component will be positioned inside and aligned to the visible text field border. + * There is no gap between the visible border and the component. + * The laid out component size will be the preferred component width + * and the inner text field height. + *
+ * The component should be not opaque because the text field border is painted + * slightly inside the usually visible border in some cases. + * E.g. when focused (in some themes) or when an outline color is specified + * (see {@link #OUTLINE}. + *
+ * Component {@link javax.swing.JTextField} (and subclasses)
+ * Value type {@link javax.swing.JComponent}
+ *
+ * @since 2
+ */
+ String TEXT_FIELD_LEADING_COMPONENT = "JTextField.leadingComponent";
+
+ /**
+ * Specifies a component that will be placed at the trailing edge of the text field.
+ *
+ * The component will be positioned inside and aligned to the visible text field border. + * There is no gap between the visible border and the component. + * The laid out component size will be the preferred component width + * and the inner text field height. + *
+ * The component should be not opaque because the text field border is painted + * slightly inside the usually visible border in some cases. + * E.g. when focused (in some themes) or when an outline color is specified + * (see {@link #OUTLINE}. + *
+ * Component {@link javax.swing.JTextField} (and subclasses)
+ * Value type {@link javax.swing.JComponent}
+ *
+ * @since 2
+ */
+ String TEXT_FIELD_TRAILING_COMPONENT = "JTextField.trailingComponent";
+
//---- JToggleButton ------------------------------------------------------
/**
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java
index 8e641c95..165c407f 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/UIDefaultsLoader.java
@@ -364,6 +364,12 @@ class UIDefaultsLoader
if( resultValueType == null )
resultValueType = tempResultValueType;
+ // do not parse styles here
+ if( key.startsWith( "[style]" ) ) {
+ resultValueType[0] = ValueType.STRING;
+ return value;
+ }
+
value = value.trim();
// null
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatClearIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatClearIcon.java
index edce46f9..30f691c4 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatClearIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatClearIcon.java
@@ -47,8 +47,16 @@ public class FlatClearIcon
@Styleable protected Color clearIconHoverColor = UIManager.getColor( "SearchField.clearIconHoverColor" );
@Styleable protected Color clearIconPressedColor = UIManager.getColor( "SearchField.clearIconPressedColor" );
+ private final boolean ignoreButtonState;
+
public FlatClearIcon() {
+ this( false );
+ }
+
+ /** @since 2 */
+ public FlatClearIcon( boolean ignoreButtonState ) {
super( 16, 16, null );
+ this.ignoreButtonState = ignoreButtonState;
}
/** @since 2 */
@@ -63,7 +71,7 @@ public class FlatClearIcon
@Override
protected void paintIcon( Component c, Graphics2D g ) {
- if( c instanceof AbstractButton ) {
+ if( !ignoreButtonState && c instanceof AbstractButton ) {
ButtonModel model = ((AbstractButton)c).getModel();
if( model.isPressed() || model.isRollover() ) {
/*
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchIcon.java
index 89fd7af0..e9f72ffa 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchIcon.java
@@ -45,8 +45,16 @@ public class FlatSearchIcon
@Styleable protected Color searchIconHoverColor = UIManager.getColor( "SearchField.searchIconHoverColor" );
@Styleable protected Color searchIconPressedColor = UIManager.getColor( "SearchField.searchIconPressedColor" );
+ private final boolean ignoreButtonState;
+
public FlatSearchIcon() {
+ this( false );
+ }
+
+ /** @since 2 */
+ public FlatSearchIcon( boolean ignoreButtonState ) {
super( 16, 16, null );
+ this.ignoreButtonState = ignoreButtonState;
}
/** @since 2 */
@@ -70,8 +78,10 @@ public class FlatSearchIcon
*/
- g.setColor( FlatButtonUI.buttonStateColor( c, searchIconColor, searchIconColor,
- null, searchIconHoverColor, searchIconPressedColor ) );
+ g.setColor( ignoreButtonState
+ ? searchIconColor
+ : FlatButtonUI.buttonStateColor( c, searchIconColor, searchIconColor,
+ null, searchIconHoverColor, searchIconPressedColor ) );
// paint magnifier
Area area = new Area( new Ellipse2D.Float( 2, 2, 10, 10 ) );
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchWithHistoryIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchWithHistoryIcon.java
index 262bb8fc..d8a4e00b 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchWithHistoryIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/icons/FlatSearchWithHistoryIcon.java
@@ -30,6 +30,12 @@ public class FlatSearchWithHistoryIcon
extends FlatSearchIcon
{
public FlatSearchWithHistoryIcon() {
+ this( false );
+ }
+
+ /** @since 2 */
+ public FlatSearchWithHistoryIcon( boolean ignoreButtonState ) {
+ super( ignoreButtonState );
}
@Override
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 6814e7f8..b6946080 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
@@ -19,12 +19,16 @@ 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.Component;
import java.awt.Container;
+import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.LayoutManager2;
import java.awt.Rectangle;
import java.awt.event.FocusListener;
import java.beans.PropertyChangeEvent;
@@ -32,10 +36,13 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.Icon;
+import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JSpinner;
import javax.swing.JTextField;
+import javax.swing.JToggleButton;
+import javax.swing.JToolBar;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
@@ -94,6 +101,8 @@ public class FlatTextFieldUI
/** @since 2 */ @Styleable protected Icon leadingIcon;
/** @since 2 */ @Styleable protected Icon trailingIcon;
+ /** @since 2 */ protected JComponent leadingComponent;
+ /** @since 2 */ protected JComponent trailingComponent;
private Color oldDisabledBackground;
private Color oldInactiveBackground;
@@ -115,11 +124,17 @@ public class FlatTextFieldUI
leadingIcon = clientProperty( c, TEXT_FIELD_LEADING_ICON, null, Icon.class );
trailingIcon = clientProperty( c, TEXT_FIELD_TRAILING_ICON, null, Icon.class );
+ installLeadingComponent();
+ installTrailingComponent();
+
installStyle();
}
@Override
public void uninstallUI( JComponent c ) {
+ uninstallLeadingComponent();
+ uninstallTrailingComponent();
+
super.uninstallUI( c );
leadingIcon = null;
@@ -225,6 +240,20 @@ public class FlatTextFieldUI
trailingIcon = (e.getNewValue() instanceof Icon) ? (Icon) e.getNewValue() : null;
c.repaint();
break;
+
+ case TEXT_FIELD_LEADING_COMPONENT:
+ uninstallLeadingComponent();
+ installLeadingComponent();
+ c.revalidate();
+ c.repaint();
+ break;
+
+ case TEXT_FIELD_TRAILING_COMPONENT:
+ uninstallTrailingComponent();
+ installTrailingComponent();
+ c.revalidate();
+ c.repaint();
+ break;
}
}
@@ -444,6 +473,12 @@ debug*/
// add width of leading and trailing icons
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;
+
return size;
}
@@ -510,7 +545,8 @@ debug*/
/**
* 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.
+ * right margin if the text field has leading or trailing icons or components.
+ * Also the preferred widths of leading and trailing components are removed.
*
* @since 2
*/
@@ -519,10 +555,24 @@ debug*/
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() ) {
+
+ // 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;
+ }
+ 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
+ if( leftVisible || (ltr ? hasLeadingIcon() : hasTrailingIcon()) ) {
// reduce left margin
Insets margin = getComponent().getMargin();
int newLeftMargin = Math.min( margin.left, margin.top );
@@ -532,7 +582,7 @@ debug*/
r.width += diff;
}
}
- if( ltr ? hasTrailingIcon() : hasLeadingIcon() ) {
+ if( rightVisible || (ltr ? hasTrailingIcon() : hasLeadingIcon()) ) {
// reduce right margin
Insets margin = getComponent().getMargin();
int newRightMargin = Math.min( margin.right, margin.top );
@@ -540,6 +590,10 @@ debug*/
r.width += scale( margin.right - newRightMargin );
}
+ // 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;
}
@@ -578,4 +632,152 @@ debug*/
if( caret instanceof FlatCaret )
((FlatCaret)caret).scrollCaretToVisible();
}
+
+ /** @since 2 */
+ protected void installLeadingComponent() {
+ JTextComponent c = getComponent();
+ leadingComponent = clientProperty( c, TEXT_FIELD_LEADING_COMPONENT, null, JComponent.class );
+ if( leadingComponent != null ) {
+ prepareLeadingOrTrailingComponent( leadingComponent );
+ installLayout();
+ c.add( leadingComponent );
+ }
+ }
+
+ /** @since 2 */
+ protected void installTrailingComponent() {
+ JTextComponent c = getComponent();
+ trailingComponent = clientProperty( c, TEXT_FIELD_TRAILING_COMPONENT, null, JComponent.class );
+ if( trailingComponent != null ) {
+ prepareLeadingOrTrailingComponent( trailingComponent );
+ installLayout();
+ c.add( trailingComponent );
+ }
+ }
+
+ /** @since 2 */
+ protected void uninstallLeadingComponent() {
+ if( leadingComponent != null ) {
+ getComponent().remove( leadingComponent );
+ leadingComponent = null;
+ }
+ }
+
+ /** @since 2 */
+ protected void uninstallTrailingComponent() {
+ if( trailingComponent != null ) {
+ getComponent().remove( trailingComponent );
+ trailingComponent = null;
+ }
+ }
+
+ /** @since 2 */
+ protected void prepareLeadingOrTrailingComponent( JComponent c ) {
+ c.putClientProperty( STYLE_CLASS, "inTextField" );
+ c.setCursor( Cursor.getDefaultCursor() );
+
+ if( c instanceof JButton || c instanceof JToggleButton )
+ c.putClientProperty( BUTTON_TYPE, BUTTON_TYPE_TOOLBAR_BUTTON );
+ else if( c instanceof JToolBar ) {
+ for( Component child : c.getComponents() ) {
+ if( child instanceof JComponent )
+ ((JComponent)child).putClientProperty( STYLE_CLASS, "inTextField" );
+ }
+ }
+ }
+
+ private void installLayout() {
+ JTextComponent c = getComponent();
+ LayoutManager oldLayout = c.getLayout();
+ if( !(oldLayout instanceof FlatTextFieldLayout) )
+ c.setLayout( new FlatTextFieldLayout( oldLayout ) );
+ }
+
+ //---- class FlatTextFieldLayout ------------------------------------------
+
+ private class FlatTextFieldLayout
+ implements LayoutManager2, UIResource
+ {
+ private final LayoutManager delegate;
+
+ FlatTextFieldLayout( LayoutManager delegate ) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void addLayoutComponent( String name, Component comp ) {
+ if( delegate != null )
+ delegate.addLayoutComponent( name, comp );
+ }
+
+ @Override
+ public void removeLayoutComponent( Component comp ) {
+ if( delegate != null )
+ delegate.removeLayoutComponent( comp );
+ }
+
+ @Override
+ public Dimension preferredLayoutSize( Container parent ) {
+ return (delegate != null) ? delegate.preferredLayoutSize( parent ) : null;
+ }
+
+ @Override
+ public Dimension minimumLayoutSize( Container parent ) {
+ return (delegate != null) ? delegate.minimumLayoutSize( parent ) : null;
+ }
+
+ @Override
+ public void layoutContainer( Container parent ) {
+ 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;
+
+ // layout left component
+ if( leftComponent != null && leftComponent.isVisible() ) {
+ int w = leftComponent.getPreferredSize().width;
+ leftComponent.setBounds( ow, ow, w, h );
+ }
+
+ // layout right component
+ if( rightComponent != null && rightComponent.isVisible() ) {
+ int w = rightComponent.getPreferredSize().width;
+ rightComponent.setBounds( parent.getWidth() - ow - w, ow, w, h );
+ }
+ }
+
+ @Override
+ public void addLayoutComponent( Component comp, Object constraints ) {
+ if( delegate instanceof LayoutManager2 )
+ ((LayoutManager2)delegate).addLayoutComponent( comp, constraints );
+ }
+
+ @Override
+ public Dimension maximumLayoutSize( Container target ) {
+ return (delegate instanceof LayoutManager2) ? ((LayoutManager2)delegate).maximumLayoutSize( target ) : null;
+ }
+
+ @Override
+ public float getLayoutAlignmentX( Container target ) {
+ return (delegate instanceof LayoutManager2) ? ((LayoutManager2)delegate).getLayoutAlignmentX( target ) : 0.5f;
+ }
+
+ @Override
+ public float getLayoutAlignmentY( Container target ) {
+ return (delegate instanceof LayoutManager2) ? ((LayoutManager2)delegate).getLayoutAlignmentY( target ) : 0.5f;
+ }
+
+ @Override
+ public void invalidateLayout( Container target ) {
+ if( delegate instanceof LayoutManager2 )
+ ((LayoutManager2)delegate).invalidateLayout( target );
+ }
+ }
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java
index 2c5cd1b9..fda0c1c8 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/ui/FlatUIUtils.java
@@ -278,6 +278,32 @@ public class FlatUIUtils
: 0;
}
+ /**
+ * Returns the scaled line thickness used to compute the border insets.
+ *
+ * @since 2
+ */
+ public static float getBorderLineWidth( JComponent c ) {
+ FlatBorder border = getOutsideFlatBorder( c );
+ return (border != null)
+ ? UIScale.scale( (float) border.getLineWidth( c ) )
+ : 0;
+ }
+
+ /**
+ * Returns the scaled thickness of the border.
+ * This includes the outer focus border and the actual component border.
+ *
+ * @since 2
+ */
+ public static int getBorderFocusAndLineWidth( JComponent c ) {
+ FlatBorder border = getOutsideFlatBorder( c );
+ return (border != null)
+ ? Math.round( UIScale.scale( (float) border.getFocusWidth( c ) )
+ + UIScale.scale( (float) border.getLineWidth( c ) ) )
+ : 0;
+ }
+
/**
* Returns the scaled arc diameter of the border for the given component.
*/
diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties
index 82c080a7..9307635e 100644
--- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties
+++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatDarkLaf.properties
@@ -355,3 +355,19 @@ ToolTip.background = shade(@background,50%)
#---- Tree ----
Tree.hash = lighten($Tree.background,5%)
+
+
+
+#---- Styles ------------------------------------------------------------------
+
+#---- inTextField ----
+# for leading/trailing components in text fields
+
+[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)
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 33340e78..53879b71 100644
--- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
+++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLaf.properties
@@ -227,6 +227,7 @@ Button.defaultButtonFollowsFocus = false
Button.borderWidth = 1
Button.default.borderWidth = 1
+# for buttons in toolbars
Button.toolbar.margin = 3,3,3,3
Button.toolbar.spacingInsets = 1,2,1,2
@@ -889,3 +890,20 @@ Tree.icon.collapsedColor = @icon
Tree.icon.leafColor = @icon
Tree.icon.closedColor = @icon
Tree.icon.openColor = @icon
+
+
+
+#---- Styles ------------------------------------------------------------------
+
+#---- inTextField ----
+# for leading/trailing components in text fields
+
+[style]ToggleButton.inTextField = $[style]Button.inTextField
+
+[style]ToolBar.inTextField = \
+ floatable: false; \
+ opaque: false; \
+ borderMargins: 0,0,0,0
+
+[style]ToolBarSeparator.inTextField = \
+ separatorWidth: 3
diff --git a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties
index a0ab8a28..9be66470 100644
--- a/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties
+++ b/flatlaf-core/src/main/resources/com/formdev/flatlaf/FlatLightLaf.properties
@@ -362,3 +362,19 @@ ToolTip.background = lighten(@background,3%)
#---- Tree ----
Tree.hash = darken($Tree.background,10%)
+
+
+
+#---- Styles ------------------------------------------------------------------
+
+#---- inTextField ----
+# for leading/trailing components in text fields
+
+[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)
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 02455ce9..dc9e05e3 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
@@ -22,6 +22,7 @@ import javax.swing.text.DefaultEditorKit;
import com.formdev.flatlaf.FlatClientProperties;
import com.formdev.flatlaf.extras.FlatSVGIcon;
import com.formdev.flatlaf.icons.FlatSearchIcon;
+import com.formdev.flatlaf.icons.FlatSearchWithHistoryIcon;
import net.miginfocom.layout.AC;
import net.miginfocom.layout.BoundSize;
import net.miginfocom.layout.ConstraintParser;
@@ -36,6 +37,42 @@ class BasicComponentsPanel
{
BasicComponentsPanel() {
initComponents();
+
+ // search history button
+ JButton searchHistoryButton = new JButton( new FlatSearchWithHistoryIcon( true ) );
+ searchHistoryButton.setToolTipText( "Search History" );
+ searchHistoryButton.addActionListener( e -> {
+ JPopupMenu popupMenu = new JPopupMenu();
+ popupMenu.add( "(empty)" );
+ popupMenu.show( searchHistoryButton, 0, searchHistoryButton.getHeight() );
+ } );
+ compsTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT, searchHistoryButton );
+
+ // match case button
+ JToggleButton matchCaseButton = new JToggleButton( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/matchCase.svg" ) );
+ matchCaseButton.setRolloverIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/matchCaseHovered.svg" ) );
+ matchCaseButton.setSelectedIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/matchCaseSelected.svg" ) );
+ matchCaseButton.setToolTipText( "Match Case" );
+
+ // whole words button
+ JToggleButton wordsButton = new JToggleButton( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/words.svg" ) );
+ wordsButton.setRolloverIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/wordsHovered.svg" ) );
+ wordsButton.setSelectedIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/wordsSelected.svg" ) );
+ wordsButton.setToolTipText( "Whole Words" );
+
+ // regex button
+ JToggleButton regexButton = new JToggleButton( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/regex.svg" ) );
+ regexButton.setRolloverIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/regexHovered.svg" ) );
+ regexButton.setSelectedIcon( new FlatSVGIcon( "com/formdev/flatlaf/demo/icons/regexSelected.svg" ) );
+ regexButton.setToolTipText( "Regular Expression" );
+
+ // search toolbar
+ JToolBar searchToolbar = new JToolBar();
+ searchToolbar.add( matchCaseButton );
+ searchToolbar.add( wordsButton );
+ searchToolbar.addSeparator();
+ searchToolbar.add( regexButton );
+ compsTextField.putClientProperty( FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT, searchToolbar );
}
private void initComponents() {
@@ -134,6 +171,8 @@ class BasicComponentsPanel
JTextField leadingIconTextField = new JTextField();
JTextField trailingIconTextField = new JTextField();
JTextField iconsTextField = new JTextField();
+ JLabel compsLabel = new JLabel();
+ compsTextField = new JTextField();
JLabel fontsLabel = new JLabel();
JLabel h00Label = new JLabel();
JLabel h0Label = new JLabel();
@@ -181,6 +220,7 @@ class BasicComponentsPanel
"[]" +
"[]" +
"[]" +
+ "[]" +
"[]0" +
"[]"));
@@ -689,84 +729,89 @@ class BasicComponentsPanel
iconsTextField.setText("text");
add(iconsTextField, "cell 3 14,growx");
+ //---- compsLabel ----
+ compsLabel.setText("Leading/trailing comp.:");
+ add(compsLabel, "cell 0 15");
+ add(compsTextField, "cell 1 15 2 1,growx");
+
//---- fontsLabel ----
fontsLabel.setText("Typography / Fonts:");
- add(fontsLabel, "cell 0 15");
+ add(fontsLabel, "cell 0 16");
//---- h00Label ----
h00Label.setText("H00");
h00Label.putClientProperty("FlatLaf.styleClass", "h00");
- add(h00Label, "cell 1 15 5 1");
+ add(h00Label, "cell 1 16 5 1");
//---- h0Label ----
h0Label.setText("H0");
h0Label.putClientProperty("FlatLaf.styleClass", "h0");
- add(h0Label, "cell 1 15 5 1");
+ add(h0Label, "cell 1 16 5 1");
//---- h1Label ----
h1Label.setText("H1");
h1Label.putClientProperty("FlatLaf.styleClass", "h1");
- add(h1Label, "cell 1 15 5 1");
+ add(h1Label, "cell 1 16 5 1");
//---- h2Label ----
h2Label.setText("H2");
h2Label.putClientProperty("FlatLaf.styleClass", "h2");
- add(h2Label, "cell 1 15 5 1");
+ add(h2Label, "cell 1 16 5 1");
//---- h3Label ----
h3Label.setText("H3");
h3Label.putClientProperty("FlatLaf.styleClass", "h3");
- add(h3Label, "cell 1 15 5 1");
+ add(h3Label, "cell 1 16 5 1");
//---- h4Label ----
h4Label.setText("H4");
h4Label.putClientProperty("FlatLaf.styleClass", "h4");
- add(h4Label, "cell 1 15 5 1");
+ add(h4Label, "cell 1 16 5 1");
//---- lightLabel ----
lightLabel.setText("light");
lightLabel.putClientProperty("FlatLaf.style", "font: 200% $light.font");
- add(lightLabel, "cell 1 15 5 1,gapx 30");
+ add(lightLabel, "cell 1 16 5 1,gapx 30");
//---- semiboldLabel ----
semiboldLabel.setText("semibold");
semiboldLabel.putClientProperty("FlatLaf.style", "font: 200% $semibold.font");
- add(semiboldLabel, "cell 1 15 5 1");
+ add(semiboldLabel, "cell 1 16 5 1");
//---- fontZoomLabel ----
fontZoomLabel.setText("(200%)");
fontZoomLabel.putClientProperty("FlatLaf.styleClass", "small");
fontZoomLabel.setEnabled(false);
- add(fontZoomLabel, "cell 1 15 5 1");
+ add(fontZoomLabel, "cell 1 16 5 1");
//---- largeLabel ----
largeLabel.setText("large");
largeLabel.putClientProperty("FlatLaf.styleClass", "large");
- add(largeLabel, "cell 1 16 5 1");
+ add(largeLabel, "cell 1 17 5 1");
//---- defaultLabel ----
defaultLabel.setText("default");
- add(defaultLabel, "cell 1 16 5 1");
+ add(defaultLabel, "cell 1 17 5 1");
//---- mediumLabel ----
mediumLabel.setText("medium");
mediumLabel.putClientProperty("FlatLaf.styleClass", "medium");
- add(mediumLabel, "cell 1 16 5 1");
+ add(mediumLabel, "cell 1 17 5 1");
//---- smallLabel ----
smallLabel.setText("small");
smallLabel.putClientProperty("FlatLaf.styleClass", "small");
- add(smallLabel, "cell 1 16 5 1");
+ add(smallLabel, "cell 1 17 5 1");
//---- miniLabel ----
miniLabel.setText("mini");
miniLabel.putClientProperty("FlatLaf.styleClass", "mini");
- add(miniLabel, "cell 1 16 5 1");
+ add(miniLabel, "cell 1 17 5 1");
//---- monospacedLabel ----
monospacedLabel.setText("monospaced");
monospacedLabel.putClientProperty("FlatLaf.styleClass", "monospaced");
- add(monospacedLabel, "cell 1 16 5 1,gapx 30");
+ add(monospacedLabel, "cell 1 17 5 1,gapx 30");
//======== popupMenu1 ========
{
@@ -841,7 +886,7 @@ class BasicComponentsPanel
rows[11].setGapAfter( zeroGap );
rows[12].setGapBefore( zeroGap );
rows[13].setGapBefore( zeroGap );
- rows[15].setGapBefore( zeroGap );
+ rows[16].setGapBefore( zeroGap );
layout.setRowConstraints( ac );
// move two text field into same row as spinners
@@ -859,5 +904,6 @@ class BasicComponentsPanel
}
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables
+ private JTextField compsTextField;
// JFormDesigner - End of variables declaration //GEN-END:variables
}
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 f1a7e69a..c5eb1554 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.4.0.360" Java: "16" encoding: "UTF-8"
+JFDML JFormDesigner: "7.0.5.0.404" Java: "17" encoding: "UTF-8"
new FormModel {
contentType: "form/swing"
@@ -9,7 +9,7 @@ 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][][]"
- "$rowConstraints": "[][][][][][][][][][][][]para[][][][]0[]"
+ "$rowConstraints": "[][][][][][][][][][][][]para[][][][][]0[]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
@@ -671,67 +671,81 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 3 14,growx"
} )
+ add( new FormComponent( "javax.swing.JLabel" ) {
+ name: "compsLabel"
+ "text": "Leading/trailing comp.:"
+ }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
+ "value": "cell 0 15"
+ } )
+ add( new FormComponent( "javax.swing.JTextField" ) {
+ name: "compsTextField"
+ auxiliary() {
+ "JavaCodeGenerator.variableLocal": false
+ }
+ }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
+ "value": "cell 1 15 2 1,growx"
+ } )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fontsLabel"
"text": "Typography / Fonts:"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 0 15"
+ "value": "cell 0 16"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h00Label"
"text": "H00"
"$client.FlatLaf.styleClass": "h00"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 15 5 1"
+ "value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h0Label"
"text": "H0"
"$client.FlatLaf.styleClass": "h0"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 15 5 1"
+ "value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h1Label"
"text": "H1"
"$client.FlatLaf.styleClass": "h1"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 15 5 1"
+ "value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h2Label"
"text": "H2"
"$client.FlatLaf.styleClass": "h2"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 15 5 1"
+ "value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h3Label"
"text": "H3"
"$client.FlatLaf.styleClass": "h3"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 15 5 1"
+ "value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "h4Label"
"text": "H4"
"$client.FlatLaf.styleClass": "h4"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 15 5 1"
+ "value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "lightLabel"
"text": "light"
"$client.FlatLaf.style": "font: 200% $light.font"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 15 5 1,gapx 30"
+ "value": "cell 1 16 5 1,gapx 30"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "semiboldLabel"
"text": "semibold"
"$client.FlatLaf.style": "font: 200% $semibold.font"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 15 5 1"
+ "value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "fontZoomLabel"
@@ -739,48 +753,48 @@ new FormModel {
"$client.FlatLaf.styleClass": "small"
"enabled": false
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 15 5 1"
+ "value": "cell 1 16 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "largeLabel"
"text": "large"
"$client.FlatLaf.styleClass": "large"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 16 5 1"
+ "value": "cell 1 17 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "defaultLabel"
"text": "default"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 16 5 1"
+ "value": "cell 1 17 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "mediumLabel"
"text": "medium"
"$client.FlatLaf.styleClass": "medium"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 16 5 1"
+ "value": "cell 1 17 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "smallLabel"
"text": "small"
"$client.FlatLaf.styleClass": "small"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 16 5 1"
+ "value": "cell 1 17 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "miniLabel"
"text": "mini"
"$client.FlatLaf.styleClass": "mini"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 16 5 1"
+ "value": "cell 1 17 5 1"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "monospacedLabel"
"text": "monospaced"
"$client.FlatLaf.styleClass": "monospaced"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
- "value": "cell 1 16 5 1,gapx 30"
+ "value": "cell 1 17 5 1,gapx 30"
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
diff --git a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java
index 9a78afe1..7bca3551 100644
--- a/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java
+++ b/flatlaf-demo/src/main/java/com/formdev/flatlaf/demo/FlatLafDemo.java
@@ -72,7 +72,7 @@ public class FlatLafDemo
DemoFrame frame = new DemoFrame();
if( FlatLafDemo.screenshotsMode )
- frame.setPreferredSize( new Dimension( 1660, 840 ) );
+ frame.setPreferredSize( new Dimension( 1660, 880 ) );
// show frame
frame.pack();
diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/matchCase.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/matchCase.svg
new file mode 100644
index 00000000..64b341ed
--- /dev/null
+++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/matchCase.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/matchCaseHovered.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/matchCaseHovered.svg
new file mode 100644
index 00000000..e07af386
--- /dev/null
+++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/matchCaseHovered.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/matchCaseSelected.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/matchCaseSelected.svg
new file mode 100644
index 00000000..6be2c27e
--- /dev/null
+++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/matchCaseSelected.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/regex.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/regex.svg
new file mode 100644
index 00000000..f073c590
--- /dev/null
+++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/regex.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/regexHovered.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/regexHovered.svg
new file mode 100644
index 00000000..545853d7
--- /dev/null
+++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/regexHovered.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/regexSelected.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/regexSelected.svg
new file mode 100644
index 00000000..29a9c56d
--- /dev/null
+++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/regexSelected.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/words.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/words.svg
new file mode 100644
index 00000000..e427f911
--- /dev/null
+++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/words.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/wordsHovered.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/wordsHovered.svg
new file mode 100644
index 00000000..d99d09d6
--- /dev/null
+++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/wordsHovered.svg
@@ -0,0 +1,6 @@
+
+
diff --git a/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/wordsSelected.svg b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/wordsSelected.svg
new file mode 100644
index 00000000..21533311
--- /dev/null
+++ b/flatlaf-demo/src/main/resources/com/formdev/flatlaf/demo/icons/wordsSelected.svg
@@ -0,0 +1,6 @@
+
+
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 b8f1a270..326930f7 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
@@ -20,6 +20,7 @@ import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.Color;
import java.awt.Insets;
import javax.swing.Icon;
+import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import com.formdev.flatlaf.extras.components.FlatTextField.SelectAllOnFocusPolicy;
@@ -85,6 +86,64 @@ public class FlatFormattedTextField
}
+ /**
+ * Returns a component that will be placed at the leading edge of the text field.
+ *
+ * @since 2
+ */
+ public JComponent getLeadingComponent() {
+ return (JComponent) getClientProperty( TEXT_FIELD_LEADING_COMPONENT );
+ }
+
+ /**
+ * Specifies a component that will be placed at the leading edge of the text field.
+ *
+ * The component will be positioned inside and aligned to the visible text field border. + * There is no gap between the visible border and the component. + * The laid out component size will be the preferred component width + * and the inner text field height. + *
+ * The component should be not opaque because the text field border is painted + * slightly inside the usually visible border in some cases. + * E.g. when focused (in some themes) or when an outline color is specified + * (see {@link #setOutline(Object)}. + * + * @since 2 + */ + public void setLeadingComponent( JComponent leadingComponent ) { + putClientProperty( TEXT_FIELD_LEADING_COMPONENT, leadingComponent ); + } + + + /** + * Returns a component that will be placed at the trailing edge of the text field. + * + * @since 2 + */ + public JComponent getTrailingComponent() { + return (JComponent) getClientProperty( TEXT_FIELD_TRAILING_COMPONENT ); + } + + /** + * Specifies a component that will be placed at the trailing edge of the text field. + *
+ * The component will be positioned inside and aligned to the visible text field border. + * There is no gap between the visible border and the component. + * The laid out component size will be the preferred component width + * and the inner text field height. + *
+ * The component should be not opaque because the text field border is painted + * slightly inside the usually visible border in some cases. + * E.g. when focused (in some themes) or when an outline color is specified + * (see {@link #setOutline(Object)}. + * + * @since 2 + */ + public void setTrailingComponent( JComponent trailingComponent ) { + putClientProperty( TEXT_FIELD_TRAILING_COMPONENT, trailingComponent ); + } + + /** * 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 1078d368..cd7f2374 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 @@ -20,6 +20,7 @@ import static com.formdev.flatlaf.FlatClientProperties.*; import java.awt.Color; import java.awt.Insets; import javax.swing.Icon; +import javax.swing.JComponent; import javax.swing.JPasswordField; import com.formdev.flatlaf.extras.components.FlatTextField.SelectAllOnFocusPolicy; @@ -85,6 +86,64 @@ public class FlatPasswordField } + /** + * Returns a component that will be placed at the leading edge of the text field. + * + * @since 2 + */ + public JComponent getLeadingComponent() { + return (JComponent) getClientProperty( TEXT_FIELD_LEADING_COMPONENT ); + } + + /** + * Specifies a component that will be placed at the leading edge of the text field. + *
+ * The component will be positioned inside and aligned to the visible text field border. + * There is no gap between the visible border and the component. + * The laid out component size will be the preferred component width + * and the inner text field height. + *
+ * The component should be not opaque because the text field border is painted + * slightly inside the usually visible border in some cases. + * E.g. when focused (in some themes) or when an outline color is specified + * (see {@link #setOutline(Object)}. + * + * @since 2 + */ + public void setLeadingComponent( JComponent leadingComponent ) { + putClientProperty( TEXT_FIELD_LEADING_COMPONENT, leadingComponent ); + } + + + /** + * Returns a component that will be placed at the trailing edge of the text field. + * + * @since 2 + */ + public JComponent getTrailingComponent() { + return (JComponent) getClientProperty( TEXT_FIELD_TRAILING_COMPONENT ); + } + + /** + * Specifies a component that will be placed at the trailing edge of the text field. + *
+ * The component will be positioned inside and aligned to the visible text field border. + * There is no gap between the visible border and the component. + * The laid out component size will be the preferred component width + * and the inner text field height. + *
+ * The component should be not opaque because the text field border is painted + * slightly inside the usually visible border in some cases. + * E.g. when focused (in some themes) or when an outline color is specified + * (see {@link #setOutline(Object)}. + * + * @since 2 + */ + public void setTrailingComponent( JComponent trailingComponent ) { + putClientProperty( TEXT_FIELD_TRAILING_COMPONENT, trailingComponent ); + } + + /** * 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 31d8850d..c7747bf5 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 @@ -20,6 +20,7 @@ import static com.formdev.flatlaf.FlatClientProperties.*; import java.awt.Color; import java.awt.Insets; import javax.swing.Icon; +import javax.swing.JComponent; import javax.swing.JTextField; /** @@ -84,6 +85,64 @@ public class FlatTextField } + /** + * Returns a component that will be placed at the leading edge of the text field. + * + * @since 2 + */ + public JComponent getLeadingComponent() { + return (JComponent) getClientProperty( TEXT_FIELD_LEADING_COMPONENT ); + } + + /** + * Specifies a component that will be placed at the leading edge of the text field. + *
+ * The component will be positioned inside and aligned to the visible text field border. + * There is no gap between the visible border and the component. + * The laid out component size will be the preferred component width + * and the inner text field height. + *
+ * The component should be not opaque because the text field border is painted + * slightly inside the usually visible border in some cases. + * E.g. when focused (in some themes) or when an outline color is specified + * (see {@link #setOutline(Object)}. + * + * @since 2 + */ + public void setLeadingComponent( JComponent leadingComponent ) { + putClientProperty( TEXT_FIELD_LEADING_COMPONENT, leadingComponent ); + } + + + /** + * Returns a component that will be placed at the trailing edge of the text field. + * + * @since 2 + */ + public JComponent getTrailingComponent() { + return (JComponent) getClientProperty( TEXT_FIELD_TRAILING_COMPONENT ); + } + + /** + * Specifies a component that will be placed at the trailing edge of the text field. + *
+ * The component will be positioned inside and aligned to the visible text field border. + * There is no gap between the visible border and the component. + * The laid out component size will be the preferred component width + * and the inner text field height. + *
+ * The component should be not opaque because the text field border is painted
+ * slightly inside the usually visible border in some cases.
+ * E.g. when focused (in some themes) or when an outline color is specified
+ * (see {@link #setOutline(Object)}.
+ *
+ * @since 2
+ */
+ public void setTrailingComponent( JComponent trailingComponent ) {
+ putClientProperty( TEXT_FIELD_TRAILING_COMPONENT, trailingComponent );
+ }
+
+
// 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.txt b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt
index 5d305543..7cf0ed6a 100644
--- a/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt
+++ b/flatlaf-testing/dumps/uidefaults/FlatDarkLaf_1.8.0.txt
@@ -1419,6 +1419,26 @@ ViewportUI com.formdev.flatlaf.ui.FlatViewportUI
[style].small font: $small.font
+#---- [style]Button ----
+
+[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)
+
+
+#---- [style]ToggleButton ----
+
+[style]ToggleButton.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)
+
+
+#---- [style]ToolBar ----
+
+[style]ToolBar.inTextField floatable: false; opaque: false; borderMargins: 0,0,0,0
+
+
+#---- [style]ToolBarSeparator ----
+
+[style]ToolBarSeparator.inTextField separatorWidth: 3
+
+
#---- ----
activeCaption #434e60 HSL 217 18 32 javax.swing.plaf.ColorUIResource [UI]
diff --git a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt
index d9efdd96..c0ae8ece 100644
--- a/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt
+++ b/flatlaf-testing/dumps/uidefaults/FlatLightLaf_1.8.0.txt
@@ -1424,6 +1424,26 @@ ViewportUI com.formdev.flatlaf.ui.FlatViewportUI
[style].small font: $small.font
+#---- [style]Button ----
+
+[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)
+
+
+#---- [style]ToggleButton ----
+
+[style]ToggleButton.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)
+
+
+#---- [style]ToolBar ----
+
+[style]ToolBar.inTextField floatable: false; opaque: false; borderMargins: 0,0,0,0
+
+
+#---- [style]ToolBarSeparator ----
+
+[style]ToolBarSeparator.inTextField separatorWidth: 3
+
+
#---- ----
activeCaption #99b4d1 HSL 211 38 71 javax.swing.plaf.ColorUIResource [UI]
diff --git a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt
index 43836b5f..be298499 100644
--- a/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt
+++ b/flatlaf-testing/dumps/uidefaults/FlatTestLaf_1.8.0.txt
@@ -1437,6 +1437,26 @@ ViewportUI com.formdev.flatlaf.ui.FlatViewportUI
[style].small font: $small.font
+#---- [style]Button ----
+
+[style]Button.inTextField focusable: false; toolbar.margin: 1,1,1,1; toolbar.spacingInsets: 1,1,1,1
+
+
+#---- [style]ToggleButton ----
+
+[style]ToggleButton.inTextField focusable: false; toolbar.margin: 1,1,1,1; toolbar.spacingInsets: 1,1,1,1
+
+
+#---- [style]ToolBar ----
+
+[style]ToolBar.inTextField floatable: false; opaque: false; borderMargins: 0,0,0,0
+
+
+#---- [style]ToolBarSeparator ----
+
+[style]ToolBarSeparator.inTextField separatorWidth: 3
+
+
#---- ----
activeCaption #99b4d1 HSL 211 38 71 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 e72cb16b..a0c66148 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
@@ -21,6 +21,7 @@ import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
+import java.util.function.Supplier;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.text.DefaultEditorKit;
@@ -58,26 +59,79 @@ public class FlatTextComponentsTest
if( padding.equals( new Insets( 0, 0, 0, 0 ) ) )
padding = null;
- for( Component c : getComponents() ) {
- if( c instanceof JTextField )
- ((JTextField)c).putClientProperty( FlatClientProperties.TEXT_FIELD_PADDING, padding );
- }
+ putTextFieldClientProperty( FlatClientProperties.TEXT_FIELD_PADDING, padding );
}
private void leadingIcon() {
- applyIcon( FlatClientProperties.TEXT_FIELD_LEADING_ICON, leadingIconCheckBox.isSelected()
+ putTextFieldClientProperty( 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()
+ putTextFieldClientProperty( FlatClientProperties.TEXT_FIELD_TRAILING_ICON, trailingIconCheckBox.isSelected()
? new TestIcon( 24, 12, Color.magenta ) : null );
}
- private void applyIcon( String key, Icon icon ) {
+ private void leadingComponent() {
+ putTextFieldClientProperty( FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT, () -> {
+ if( !leadingComponentCheckBox.isSelected() )
+ return null;
+
+ JLabel l = new JLabel( "lead" );
+ l.setOpaque( true );
+ l.setBackground( Color.green );
+ l.setVisible( leadingComponentVisibleCheckBox.isSelected() );
+ return l;
+ } );
+ }
+
+ private void trailingComponent() {
+ putTextFieldClientProperty( FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT, () -> {
+ if( !trailingComponentCheckBox.isSelected() )
+ return null;
+
+ JLabel l = new JLabel( "tr" );
+ l.setOpaque( true );
+ l.setBackground( Color.magenta );
+ l.setVisible( trailingComponentVisibleCheckBox.isSelected() );
+ return l;
+ } );
+ }
+
+ private void leadingComponentVisible() {
+ setLeadingTrailingComponentVisible( FlatClientProperties.TEXT_FIELD_LEADING_COMPONENT,
+ leadingComponentVisibleCheckBox.isSelected() );
+ }
+
+ private void trailingComponentVisible() {
+ setLeadingTrailingComponentVisible( FlatClientProperties.TEXT_FIELD_TRAILING_COMPONENT,
+ trailingComponentVisibleCheckBox.isSelected() );
+ }
+
+ private void setLeadingTrailingComponentVisible( String key, boolean visible ) {
+ for( Component c : getComponents() ) {
+ if( c instanceof JTextField ) {
+ Object value = ((JTextField)c).getClientProperty( key );
+ if( value instanceof JComponent ) {
+ ((JComponent)value).setVisible( visible );
+ c.revalidate();
+ c.repaint();
+ }
+ }
+ }
+ }
+
+ private void putTextFieldClientProperty( String key, Object value ) {
for( Component c : getComponents() ) {
if( c instanceof JTextField )
- ((JTextField)c).putClientProperty( key, icon );
+ ((JTextField)c).putClientProperty( key, value );
+ }
+ }
+
+ private void putTextFieldClientProperty( String key, Supplier