mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-11 06:27:13 -06:00
TextComponents: selectAllOnFocusPolicy related changes:
- No longer select all text if selection (or caret position) was changed by application and `selectAllOnFocusPolicy` is `once` (the default). (issue #983) - FormattedTextField and Spinner: `selectAllOnFocusPolicy = once` behaves now as `always` (was `never` before), which means that all text is selected when component gains focus. This is because of special behavior of `JFormattedTextField` that did not allow implementation of `once`. - Client property `JTextField.selectAllOnFocusPolicy` now also works on (editable) `JComboBox` and on `JSpinner`. - Added client property `JTextField.selectAllOnMouseClick` to override UI property `TextComponent.selectAllOnMouseClick`. (issue #961) - For `selectAllOnMouseClick = true`, clicking with the mouse into the text field, to focus it, now always selects all text, even if `selectAllOnFocusPolicy` is `once`.
This commit is contained in:
@@ -21,6 +21,8 @@ import java.awt.IllegalComponentStateException;
|
||||
import java.awt.Window;
|
||||
import java.util.Objects;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JFormattedTextField;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.SwingConstants;
|
||||
|
||||
/**
|
||||
@@ -1209,12 +1211,15 @@ public interface FlatClientProperties
|
||||
/**
|
||||
* Specifies whether all text is selected when the text component gains focus.
|
||||
* <p>
|
||||
* <strong>Component</strong> {@link javax.swing.JTextField} (and subclasses)<br>
|
||||
* <strong>Components</strong> {@link javax.swing.text.JTextComponent} (and subclasses),
|
||||
* {@link javax.swing.JComboBox} (since 3.6) and {@link javax.swing.JSpinner} (since 3.6)<br>
|
||||
* <strong>Value type</strong> {@link java.lang.String}<br>
|
||||
* <strong>Allowed Values</strong>
|
||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_NEVER},
|
||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ONCE} (default) or
|
||||
* {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}
|
||||
*
|
||||
* @see #SELECT_ALL_ON_MOUSE_CLICK
|
||||
*/
|
||||
String SELECT_ALL_ON_FOCUS_POLICY = "JTextField.selectAllOnFocusPolicy";
|
||||
|
||||
@@ -1229,6 +1234,12 @@ public interface FlatClientProperties
|
||||
* Select all text when the text component gains focus for the first time
|
||||
* and selection was not modified (is at end of text).
|
||||
* This is the default.
|
||||
* <p>
|
||||
* <b>Limitations:</b>
|
||||
* For {@link JFormattedTextField} and {@link JSpinner} this behaves
|
||||
* as {@link #SELECT_ALL_ON_FOCUS_POLICY_ALWAYS}.
|
||||
* This is because of special behavior of {@link JFormattedTextField}
|
||||
* that did not allow implementation of {@code "once"}.
|
||||
*
|
||||
* @see #SELECT_ALL_ON_FOCUS_POLICY
|
||||
*/
|
||||
@@ -1241,6 +1252,19 @@ public interface FlatClientProperties
|
||||
*/
|
||||
String SELECT_ALL_ON_FOCUS_POLICY_ALWAYS = "always";
|
||||
|
||||
/**
|
||||
* Specifies whether all text is selected when when clicking with the mouse
|
||||
* into the text field (and if "select all on focus" policy is enabled).
|
||||
* <p>
|
||||
* <strong>Components</strong> {@link javax.swing.text.JTextComponent} (and subclasses),
|
||||
* {@link javax.swing.JComboBox} and {@link javax.swing.JSpinner}<br>
|
||||
* <strong>Value type</strong> {@link java.lang.Boolean}
|
||||
*
|
||||
* @see #SELECT_ALL_ON_FOCUS_POLICY
|
||||
* @since 3.6
|
||||
*/
|
||||
String SELECT_ALL_ON_MOUSE_CLICK = "JTextField.selectAllOnMouseClick";
|
||||
|
||||
/**
|
||||
* Placeholder text that is only painted if the text field is empty.
|
||||
* <p>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.formdev.flatlaf.ui;
|
||||
|
||||
import static com.formdev.flatlaf.FlatClientProperties.*;
|
||||
import java.awt.Container;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
@@ -24,7 +25,9 @@ import java.awt.event.FocusEvent;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.ActionMap;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFormattedTextField;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.plaf.UIResource;
|
||||
@@ -33,6 +36,7 @@ import javax.swing.text.DefaultCaret;
|
||||
import javax.swing.text.DefaultEditorKit;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.JTextComponent;
|
||||
import javax.swing.text.Position;
|
||||
import javax.swing.text.Utilities;
|
||||
|
||||
/**
|
||||
@@ -48,12 +52,15 @@ public class FlatCaret
|
||||
{
|
||||
private static final String KEY_CARET_INFO = "FlatLaf.internal.caretInfo";
|
||||
|
||||
// selectAllOnFocusPolicy
|
||||
private static final int NEVER = 0, ONCE = 1, ALWAYS = 2;
|
||||
|
||||
private final String selectAllOnFocusPolicy;
|
||||
private final boolean selectAllOnMouseClick;
|
||||
|
||||
private boolean inInstall;
|
||||
private boolean wasFocused;
|
||||
private boolean wasTemporaryLost;
|
||||
private boolean wasFocusTemporaryLost;
|
||||
private boolean isMousePressed;
|
||||
private boolean isWordSelection;
|
||||
private boolean isLineSelection;
|
||||
@@ -94,6 +101,9 @@ public class FlatCaret
|
||||
// restore selection
|
||||
select( (int) ci[1], (int) ci[0] );
|
||||
|
||||
if( ci[4] != 0 )
|
||||
wasFocused = true;
|
||||
|
||||
// if text component is focused, then caret and selection are visible,
|
||||
// but when switching theme, the component does not yet have
|
||||
// a highlighter and the selection is not painted
|
||||
@@ -121,6 +131,7 @@ public class FlatCaret
|
||||
getMark(),
|
||||
getBlinkRate(),
|
||||
System.currentTimeMillis(),
|
||||
wasFocused ? 1 : 0,
|
||||
} );
|
||||
|
||||
super.deinstall( c );
|
||||
@@ -140,11 +151,36 @@ public class FlatCaret
|
||||
super.adjustVisibility( nloc );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDot( int dot ) {
|
||||
super.setDot( dot );
|
||||
|
||||
// mark as focused if invoked from JTextComponent.setCaretPosition()
|
||||
// to disable SELECT_ALL_ON_FOCUS_POLICY_ONCE if application explicitly changes selection
|
||||
if( !wasFocused &&
|
||||
getSelectAllOnFocusPolicy() == ONCE &&
|
||||
StackUtils.wasInvokedFrom( JTextComponent.class.getName(), "setCaretPosition", 6 ) )
|
||||
wasFocused = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveDot( int dot ) {
|
||||
super.moveDot( dot );
|
||||
|
||||
// mark as focused if invoked from JTextComponent.moveCaretPosition()
|
||||
// to disable SELECT_ALL_ON_FOCUS_POLICY_ONCE if application explicitly changes selection
|
||||
if( !wasFocused &&
|
||||
getSelectAllOnFocusPolicy() == ONCE &&
|
||||
StackUtils.wasInvokedFrom( JTextComponent.class.getName(), "moveCaretPosition", 6 ) )
|
||||
wasFocused = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusGained( FocusEvent e ) {
|
||||
if( !inInstall && !wasTemporaryLost && (!isMousePressed || selectAllOnMouseClick) )
|
||||
if( !inInstall && !wasFocusTemporaryLost && (!isMousePressed || isSelectAllOnMouseClick()) )
|
||||
selectAllOnFocusGained();
|
||||
wasTemporaryLost = false;
|
||||
|
||||
wasFocusTemporaryLost = false;
|
||||
wasFocused = true;
|
||||
|
||||
super.focusGained( e );
|
||||
@@ -152,7 +188,7 @@ public class FlatCaret
|
||||
|
||||
@Override
|
||||
public void focusLost( FocusEvent e ) {
|
||||
wasTemporaryLost = e.isTemporary();
|
||||
wasFocusTemporaryLost = e.isTemporary();
|
||||
super.focusLost( e );
|
||||
}
|
||||
|
||||
@@ -232,24 +268,13 @@ public class FlatCaret
|
||||
if( doc == null || !c.isEnabled() || !c.isEditable() || FlatUIUtils.isCellEditor( c ) )
|
||||
return;
|
||||
|
||||
Object selectAllOnFocusPolicy = c.getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
|
||||
if( selectAllOnFocusPolicy == null )
|
||||
selectAllOnFocusPolicy = this.selectAllOnFocusPolicy;
|
||||
|
||||
if( selectAllOnFocusPolicy == null || SELECT_ALL_ON_FOCUS_POLICY_NEVER.equals( selectAllOnFocusPolicy ) )
|
||||
int selectAllOnFocusPolicy = getSelectAllOnFocusPolicy();
|
||||
if( selectAllOnFocusPolicy == NEVER )
|
||||
return;
|
||||
|
||||
if( !SELECT_ALL_ON_FOCUS_POLICY_ALWAYS.equals( selectAllOnFocusPolicy ) ) {
|
||||
// policy is "once" (or null or unknown)
|
||||
|
||||
if( selectAllOnFocusPolicy == ONCE && !isMousePressed ) {
|
||||
// was already focused?
|
||||
if( wasFocused )
|
||||
return;
|
||||
|
||||
// check whether selection was modified before gaining focus
|
||||
int dot = getDot();
|
||||
int mark = getMark();
|
||||
if( dot != mark || dot != doc.getLength() )
|
||||
if( wasFocused && !(c instanceof JFormattedTextField) )
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -265,16 +290,51 @@ public class FlatCaret
|
||||
|
||||
select( 0, c2.getDocument().getLength() );
|
||||
} );
|
||||
} else {
|
||||
} else
|
||||
select( 0, doc.getLength() );
|
||||
}
|
||||
}
|
||||
|
||||
private void select( int mark, int dot ) {
|
||||
if( mark != getMark() )
|
||||
setDot( mark );
|
||||
setDot( mark, Position.Bias.Forward );
|
||||
if( dot != getDot() )
|
||||
moveDot( dot );
|
||||
moveDot( dot, Position.Bias.Forward );
|
||||
}
|
||||
|
||||
private int getSelectAllOnFocusPolicy() {
|
||||
Object value = getClientProperty( SELECT_ALL_ON_FOCUS_POLICY );
|
||||
// Note: using String.valueOf() because selectAllOnFocusPolicy may be null
|
||||
switch( String.valueOf( value instanceof String ? value : selectAllOnFocusPolicy ) ) {
|
||||
default:
|
||||
case SELECT_ALL_ON_FOCUS_POLICY_NEVER: return NEVER;
|
||||
case SELECT_ALL_ON_FOCUS_POLICY_ONCE: return ONCE;
|
||||
case SELECT_ALL_ON_FOCUS_POLICY_ALWAYS: return ALWAYS;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSelectAllOnMouseClick() {
|
||||
Object value = getClientProperty( SELECT_ALL_ON_MOUSE_CLICK );
|
||||
return (value instanceof Boolean) ? (boolean) value : selectAllOnMouseClick;
|
||||
}
|
||||
|
||||
private Object getClientProperty( String key ) {
|
||||
JTextComponent c = getComponent();
|
||||
if( c == null )
|
||||
return null;
|
||||
|
||||
Object value = c.getClientProperty( key );
|
||||
if( value != null )
|
||||
return value;
|
||||
|
||||
Container parent = c.getParent();
|
||||
if( parent instanceof JComboBox )
|
||||
return ((JComboBox<?>)parent).getClientProperty( key );
|
||||
if( parent instanceof JSpinner.DefaultEditor ) {
|
||||
parent = parent.getParent();
|
||||
if( parent instanceof JSpinner )
|
||||
return ((JSpinner)parent).getClientProperty( key );
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @since 1.4 */
|
||||
|
||||
Reference in New Issue
Block a user