All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.formdev.flatlaf.extras.components.FlatTriStateCheckBox Maven / Gradle / Ivy

There is a newer version: 3.5.2
Show newest version
/*
 * Copyright 2019 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
 *
 *     http://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.extras.components;

import static com.formdev.flatlaf.FlatClientProperties.*;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ItemEvent;
import javax.swing.JCheckBox;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import com.formdev.flatlaf.FlatLaf;

/**
 * A tri-state check box.
 * 

* The initial state is {@link State#INDETERMINATE}. *

* By default, the third state is allowed and clicking on the checkbox cycles through all * three states. If you want that the user can cycle only through 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. *

* 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 * Tri-State Checkbox Example). *

* 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. *

* If you prefer the alternative state cycle order for all tri-state check boxes, enable it using: *

 * UIManager.put( "FlatTriStateCheckBox.altStateCycleOrder", true );
 * 
*

* 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"}. * 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 */ public class FlatTriStateCheckBox extends JCheckBox { public enum State { UNSELECTED, INDETERMINATE, SELECTED } private State state; private boolean allowIndeterminate = true; private boolean altStateCycleOrder = UIManager.getBoolean( "FlatTriStateCheckBox.altStateCycleOrder" ); public FlatTriStateCheckBox() { this( null ); } public FlatTriStateCheckBox( String text ) { this( text, State.INDETERMINATE ); } public FlatTriStateCheckBox( String text, State initialState ) { super( text ); setModel( new ToggleButtonModel() { @Override public boolean isSelected() { return state != State.UNSELECTED; } @Override public void setSelected( boolean b ) { setState( nextState( state ) ); fireStateChanged(); fireItemStateChanged( new ItemEvent( this, ItemEvent.ITEM_STATE_CHANGED, this, isSelected() ? ItemEvent.SELECTED : ItemEvent.DESELECTED ) ); } } ); setState( initialState ); } /** * Returns the state as {@link State} enum. *

* 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; State oldState = this.state; this.state = state; putClientProperty( SELECTED_STATE, (state == State.INDETERMINATE) ? SELECTED_STATE_INDETERMINATE : null ); firePropertyChange( "state", oldState, state ); repaint(); } /** * 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; } } } /** * Returns the state as {@link Boolean}. * Returns {@code null} if the state is {@link State#INDETERMINATE}. *

* 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; } } /** * 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 public void setSelected( boolean b ) { 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. *

* 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. *

* 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 && !isIndeterminateStateSupported() ) paintIndeterminateState( g ); } /** * 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" ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy