mirror of
https://github.com/JFormDesigner/FlatLaf.git
synced 2026-02-12 06:57:13 -06:00
Extras: FlatTriStateCheckBox reworked
This commit is contained in:
@@ -28,11 +28,31 @@ import com.formdev.flatlaf.FlatLaf;
|
||||
/**
|
||||
* A tri-state check box.
|
||||
* <p>
|
||||
* The initial state is {@link State#INDETERMINATE}.
|
||||
* <p>
|
||||
* By default the third state is allowed and clicking on the checkbox cycles thru all
|
||||
* three states. If you want that the user can cycle only thru two states, disallow
|
||||
* intermediate state using {@link #setAllowIndeterminate(boolean)}. Then you can still
|
||||
* set the indeterminate state via API if necessary, but the user can not.
|
||||
* <p>
|
||||
* The default state cycle order is {@link State#UNSELECTED} to {@link State#INDETERMINATE}
|
||||
* to {@link State#SELECTED}.
|
||||
* This is the same order as used by macOS, win32, IntelliJ IDEA and on the web as recommended by W3C in
|
||||
* <a href="https://www.w3.org/TR/wai-aria-practices-1.1/examples/checkbox/checkbox-2/checkbox-2.html">Tri-State Checkbox Example</a>).
|
||||
* <p>
|
||||
* If {@link #isAltStateCycleOrder()} returns {@code true},
|
||||
* the state cycle order is {@link State#UNSELECTED} to {@link State#SELECTED}
|
||||
* to {@link State#INDETERMINATE}. This order is used by Windows 10 UWP apps.
|
||||
* <p>
|
||||
* If you prefer the alternative state cycle order for all tri-state check boxes, enable it using:
|
||||
* <pre>
|
||||
* UIManager.put( "FlatTriStateCheckBox.altStateCycleOrder", true );
|
||||
* </pre>
|
||||
* <p>
|
||||
* To display the third state, this component requires an LaF that supports painting
|
||||
* the indeterminate state if client property {@code "JButton.selectedState"} has the
|
||||
* value {@code "indeterminate"}.
|
||||
* <p>
|
||||
* FlatLaf and Mac Aqua LaF support the third state.
|
||||
* FlatLaf and macOS Aqua LaF support the third state.
|
||||
* For other LaFs a magenta rectangle is painted around the component for the third state.
|
||||
*
|
||||
* @author Karl Tauber
|
||||
@@ -40,10 +60,11 @@ import com.formdev.flatlaf.FlatLaf;
|
||||
public class FlatTriStateCheckBox
|
||||
extends JCheckBox
|
||||
{
|
||||
public enum State { INDETERMINATE, SELECTED, UNSELECTED }
|
||||
public enum State { UNSELECTED, INDETERMINATE, SELECTED }
|
||||
|
||||
private State state;
|
||||
private boolean thirdStateEnabled = true;
|
||||
private boolean allowIndeterminate = true;
|
||||
private boolean altStateCycleOrder = UIManager.getBoolean( "FlatTriStateCheckBox.altStateCycleOrder" );
|
||||
|
||||
public FlatTriStateCheckBox() {
|
||||
this( null );
|
||||
@@ -64,11 +85,7 @@ public class FlatTriStateCheckBox
|
||||
|
||||
@Override
|
||||
public void setSelected( boolean b ) {
|
||||
switch( state ) {
|
||||
case INDETERMINATE: setState( State.SELECTED ); break;
|
||||
case SELECTED: setState( State.UNSELECTED ); break;
|
||||
case UNSELECTED: setState( thirdStateEnabled ? State.INDETERMINATE : State.SELECTED ); break;
|
||||
}
|
||||
setState( nextState( state ) );
|
||||
|
||||
fireStateChanged();
|
||||
fireItemStateChanged( new ItemEvent( this, ItemEvent.ITEM_STATE_CHANGED, this,
|
||||
@@ -79,10 +96,19 @@ public class FlatTriStateCheckBox
|
||||
setState( initialState );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the state as {@link State} enum.
|
||||
* <p>
|
||||
* Alternatively you can use {@link #getChecked()} to get all three states as {@link Boolean}
|
||||
* or {@link #isIndeterminate()} to check only for indeterminate state.
|
||||
*/
|
||||
public State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the state as {@link State} enum.
|
||||
*/
|
||||
public void setState( State state ) {
|
||||
if( this.state == state )
|
||||
return;
|
||||
@@ -90,34 +116,58 @@ public class FlatTriStateCheckBox
|
||||
State oldState = this.state;
|
||||
this.state = state;
|
||||
|
||||
putClientProperty( SELECTED_STATE, state == State.INDETERMINATE ? SELECTED_STATE_INDETERMINATE : null );
|
||||
putClientProperty( SELECTED_STATE, (state == State.INDETERMINATE) ? SELECTED_STATE_INDETERMINATE : null );
|
||||
|
||||
firePropertyChange( "state", oldState, state );
|
||||
repaint();
|
||||
}
|
||||
|
||||
public Boolean getValue() {
|
||||
switch( state ) {
|
||||
default:
|
||||
case INDETERMINATE: return null;
|
||||
case SELECTED: return true;
|
||||
case UNSELECTED: return false;
|
||||
/**
|
||||
* Returns the next state that follows the given state, depending on
|
||||
* {@link #isAllowIndeterminate()} and {@link #isAltStateCycleOrder()}.
|
||||
*/
|
||||
protected State nextState( State state ) {
|
||||
if( !altStateCycleOrder ) {
|
||||
// default cycle order: UNSELECTED --> INDETERMINATE --> SELECTED
|
||||
switch( state ) {
|
||||
default:
|
||||
case UNSELECTED: return allowIndeterminate ? State.INDETERMINATE : State.SELECTED;
|
||||
case INDETERMINATE: return State.SELECTED;
|
||||
case SELECTED: return State.UNSELECTED;
|
||||
}
|
||||
} else {
|
||||
// alternative cycle order: INDETERMINATE --> UNSELECTED --> SELECTED
|
||||
switch( state ) {
|
||||
default:
|
||||
case UNSELECTED: return State.SELECTED;
|
||||
case INDETERMINATE: return State.UNSELECTED;
|
||||
case SELECTED: return allowIndeterminate ? State.INDETERMINATE : State.UNSELECTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setValue( Boolean value ) {
|
||||
setState( value == null ? State.INDETERMINATE : (value ? State.SELECTED : State.UNSELECTED) );
|
||||
/**
|
||||
* Returns the state as {@link Boolean}.
|
||||
* Returns {@code null} if the state is {@link State#INDETERMINATE}.
|
||||
* <p>
|
||||
* Alternatively you can use {@link #getState()} to get state as {@link State} enum
|
||||
* or {@link #isIndeterminate()} to check only for indeterminate state.
|
||||
*/
|
||||
public Boolean getChecked() {
|
||||
switch( state ) {
|
||||
default:
|
||||
case UNSELECTED: return false;
|
||||
case INDETERMINATE: return null;
|
||||
case SELECTED: return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isThirdStateEnabled() {
|
||||
return thirdStateEnabled;
|
||||
}
|
||||
|
||||
public void setThirdStateEnabled( boolean thirdStateEnabled ) {
|
||||
this.thirdStateEnabled = thirdStateEnabled;
|
||||
|
||||
if( state == State.INDETERMINATE )
|
||||
setState( State.UNSELECTED );
|
||||
/**
|
||||
* Sets the state as {@link Boolean}.
|
||||
* Passing {@code null} sets state to {@link State#INDETERMINATE}.
|
||||
*/
|
||||
public void setChecked( Boolean value ) {
|
||||
setState( (value == null) ? State.INDETERMINATE : (value ? State.SELECTED : State.UNSELECTED) );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,17 +175,80 @@ public class FlatTriStateCheckBox
|
||||
setState( b ? State.SELECTED : State.UNSELECTED );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether state is indeterminate.
|
||||
*/
|
||||
public boolean isIndeterminate() {
|
||||
return state == State.INDETERMINATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets indeterminate state.
|
||||
*/
|
||||
public void setIndeterminate( boolean indeterminate ) {
|
||||
if( indeterminate )
|
||||
setState( State.INDETERMINATE );
|
||||
else if( state == State.INDETERMINATE )
|
||||
setState( State.UNSELECTED );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether indeterminate state is allowed.
|
||||
* <p>
|
||||
* This affects only the user when clicking on the checkbox.
|
||||
* Setting state to indeterminate via API is always allowed.
|
||||
*/
|
||||
public boolean isAllowIndeterminate() {
|
||||
return allowIndeterminate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether indeterminate state is allowed.
|
||||
* <p>
|
||||
* This affects only the user when clicking on the checkbox.
|
||||
* Setting state to indeterminate via API is always allowed.
|
||||
*/
|
||||
public void setAllowIndeterminate( boolean allowIndeterminate ) {
|
||||
this.allowIndeterminate = allowIndeterminate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether alternative state cycle order should be used.
|
||||
*/
|
||||
public boolean isAltStateCycleOrder() {
|
||||
return altStateCycleOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether alternative state cycle order should be used.
|
||||
*/
|
||||
public void setAltStateCycleOrder( boolean altStateCycleOrder ) {
|
||||
this.altStateCycleOrder = altStateCycleOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent( Graphics g ) {
|
||||
super.paintComponent( g );
|
||||
|
||||
if( state == State.INDETERMINATE && !isThirdStateSupported() ) {
|
||||
g.setColor( Color.magenta );
|
||||
g.drawRect( 0, 0, getWidth() - 1, getHeight() - 1 );
|
||||
}
|
||||
if( state == State.INDETERMINATE && !isIndeterminateStateSupported() )
|
||||
paintIndeterminateState( g );
|
||||
}
|
||||
|
||||
private boolean isThirdStateSupported() {
|
||||
/**
|
||||
* Paints the indeterminate state if the current LaF does not support displaying
|
||||
* the indeterminate state.
|
||||
* The default implementation draws a magenta rectangle around the component.
|
||||
*/
|
||||
protected void paintIndeterminateState( Graphics g ) {
|
||||
g.setColor( Color.magenta );
|
||||
g.drawRect( 0, 0, getWidth() - 1, getHeight() - 1 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the current LaF supports displaying the indeterminate state.
|
||||
* Returns {@code true} for FlatLaf and macOS Aqua.
|
||||
*/
|
||||
protected boolean isIndeterminateStateSupported() {
|
||||
LookAndFeel laf = UIManager.getLookAndFeel();
|
||||
return laf instanceof FlatLaf || laf.getClass().getName().equals( "com.apple.laf.AquaLookAndFeel" );
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user