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

javafx.scene.control.cell.CheckBoxTreeTableCell Maven / Gradle / Ivy

There is a newer version: 24-ea+15
Show newest version
/*
 * Copyright (c) 2012, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package javafx.scene.control.cell;

import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.util.Callback;
import javafx.util.StringConverter;

/**
 * A class containing a {@link TreeTableCell} implementation that draws a
 * {@link CheckBox} node inside the cell, optionally with a label to indicate
 * what the checkbox represents.
 *
 * 

By default, the CheckBoxTreeTableCell is rendered with a CheckBox centred in * the TreeTableColumn. If a label is required, it is necessary to provide a * non-null StringConverter instance to the * {@link #CheckBoxTreeTableCell(Callback, StringConverter)} constructor. * *

To construct an instance of this class, it is necessary to provide a * {@link Callback} that, given an object of type T, will return an * {@code ObservableProperty} that represents whether the given item is * selected or not. This ObservableValue will be bound bidirectionally (meaning * that the CheckBox in the cell will set/unset this property based on user * interactions, and the CheckBox will reflect the state of the ObservableValue, * if it changes externally). * *

Note that the CheckBoxTreeTableCell renders the CheckBox 'live', meaning that * the CheckBox is always interactive and can be directly toggled by the user. * This means that it is not necessary that the cell enter its * {@link #editingProperty() editing state} (usually by the user double-clicking * on the cell). A side-effect of this is that the usual editing callbacks * (such as {@link javafx.scene.control.TreeTableColumn#onEditCommitProperty() on edit commit}) * will not be called. If you want to be notified of changes, * it is recommended to directly observe the boolean properties that are * manipulated by the CheckBox.

* * @param The type of the TreeTableView generic type * @param The type of the elements contained within the TreeTableColumn. * @since JavaFX 8.0 */ public class CheckBoxTreeTableCell extends TreeTableCell { /* ************************************************************************* * * * Static cell factories * * * **************************************************************************/ /** * Creates a cell factory for use in a {@link TreeTableColumn} cell factory. * This method requires that the TreeTableColumn be of type {@link Boolean}. * *

When used in a TreeTableColumn, the CheckBoxCell is rendered with a * CheckBox centered in the column. * *

The {@code ObservableValue} contained within each cell in the * column will be bound bidirectionally. This means that the CheckBox in * the cell will set/unset this property based on user interactions, and the * CheckBox will reflect the state of the {@code ObservableValue}, * if it changes externally). * * @param The type of the TreeTableView generic type * @param column the TreeTableColumn of type {@link Boolean} * @return A {@link Callback} that will return a {@link TreeTableCell} that is * able to work on the type of element contained within the TreeTableColumn. */ public static Callback, TreeTableCell> forTreeTableColumn( final TreeTableColumn column) { return forTreeTableColumn(null, null); } /** * Creates a cell factory for use in a {@link TreeTableColumn} cell factory. * This method requires that the TreeTableColumn be of type * {@code ObservableValue}. * *

When used in a TreeTableColumn, the CheckBoxCell is rendered with a * CheckBox centered in the column. * * @param The type of the TreeTableView generic type * @param The type of the elements contained within the {@link TreeTableColumn} * instance. * @param getSelectedProperty A Callback that, given an object of * type {@code TreeTableColumn}, will return an * {@code ObservableValue} * that represents whether the given item is selected or not. This * {@code ObservableValue} will be bound bidirectionally * (meaning that the CheckBox in the cell will set/unset this property * based on user interactions, and the CheckBox will reflect the state of * the {@code ObservableValue}, if it changes externally). * @return A {@link Callback} that will return a {@link TreeTableCell} that is * able to work on the type of element contained within the TreeTableColumn. */ public static Callback, TreeTableCell> forTreeTableColumn( final Callback> getSelectedProperty) { return forTreeTableColumn(getSelectedProperty, null); } /** * Creates a cell factory for use in a {@link TreeTableColumn} cell factory. * This method requires that the TreeTableColumn be of type * {@code ObservableValue}. * *

When used in a TreeTableColumn, the CheckBoxCell is rendered with a * CheckBox centered in the column. * * @param The type of the TreeTableView generic type * @param The type of the elements contained within the {@link TreeTableColumn} * instance. * @param getSelectedProperty A Callback that, given an object of * type {@code TreeTableColumn}, will return an * {@code ObservableValue} * that represents whether the given item is selected or not. This * {@code ObservableValue} will be bound bidirectionally * (meaning that the CheckBox in the cell will set/unset this property * based on user interactions, and the CheckBox will reflect the state of * the {@code ObservableValue}, if it changes externally). * @param showLabel In some cases, it may be desirable to show a label in * the TreeTableCell beside the {@link CheckBox}. By default a label is not * shown, but by setting this to true the item in the cell will also * have toString() called on it. If this is not the desired behavior, * consider using * {@link #forTreeTableColumn(javafx.util.Callback, javafx.util.StringConverter) }, * which allows for you to provide a callback that specifies the label for a * given row item. * @return A {@link Callback} that will return a {@link TreeTableCell} that is * able to work on the type of element contained within the TreeTableColumn. */ public static Callback, TreeTableCell> forTreeTableColumn( final Callback> getSelectedProperty, final boolean showLabel) { StringConverter converter = ! showLabel ? null : CellUtils.defaultStringConverter(); return forTreeTableColumn(getSelectedProperty, converter); } /** * Creates a cell factory for use in a {@link TreeTableColumn} cell factory. * This method requires that the TreeTableColumn be of type * {@code ObservableValue}. * *

When used in a TreeTableColumn, the CheckBoxCell is rendered with a * CheckBox centered in the column. * * @param The type of the TreeTableView generic type * @param The type of the elements contained within the {@link TreeTableColumn} * instance. * @param getSelectedProperty A Callback that, given an object of type * {@code TreeTableColumn}, will return an * {@code ObservableValue} that represents whether the given * item is selected or not. This {@code ObservableValue} will * be bound bidirectionally (meaning that the CheckBox in the cell will * set/unset this property based on user interactions, and the CheckBox * will reflect the state of the {@code ObservableValue}, if * it changes externally). * @param converter A StringConverter that, give an object of type T, will return a * String that can be used to represent the object visually. The default * implementation in {@link #forTreeTableColumn(Callback, boolean)} (when * showLabel is true) is to simply call .toString() on all non-null * items (and to just return an empty string in cases where the given * item is null). * @return A {@link Callback} that will return a {@link TreeTableCell} that is * able to work on the type of element contained within the TreeTableColumn. */ public static Callback, TreeTableCell> forTreeTableColumn( final Callback> getSelectedProperty, final StringConverter converter) { return list -> new CheckBoxTreeTableCell<>(getSelectedProperty, converter); } /* ************************************************************************* * * * Fields * * * **************************************************************************/ private final CheckBox checkBox; private boolean showLabel; private ObservableValue booleanProperty; /* ************************************************************************* * * * Constructors * * * **************************************************************************/ /** * Creates a default CheckBoxTreeTableCell. */ public CheckBoxTreeTableCell() { this(null, null); } /** * Creates a default CheckBoxTreeTableCell with a custom {@link Callback} to * retrieve an ObservableValue for a given cell index. * * @param getSelectedProperty A {@link Callback} that will return an {@link * ObservableValue} given an index from the TreeTableColumn. */ public CheckBoxTreeTableCell( final Callback> getSelectedProperty) { this(getSelectedProperty, null); } /** * Creates a CheckBoxTreeTableCell with a custom string converter. * * @param getSelectedProperty A {@link Callback} that will return a {@link * ObservableValue} given an index from the TreeTableColumn. * @param converter A StringConverter that, given an object of type T, will return a * String that can be used to represent the object visually. */ public CheckBoxTreeTableCell( final Callback> getSelectedProperty, final StringConverter converter) { // we let getSelectedProperty be null here, as we can always defer to the // TreeTableColumn this.getStyleClass().add("check-box-tree-table-cell"); this.checkBox = new CheckBox(); // by default the graphic is null until the cell stops being empty setGraphic(null); setSelectedStateCallback(getSelectedProperty); setConverter(converter); // // alignment is styleable through css. Calling setAlignment // // makes it look to css like the user set the value and css will not // // override. Initializing alignment by calling set on the // // CssMetaData ensures that css will be able to override the value. // final CssMetaData prop = CssMetaData.getCssMetaData(alignmentProperty()); // prop.set(this, Pos.CENTER); } /* ************************************************************************* * * * Properties * * * **************************************************************************/ // --- converter private ObjectProperty> converter = new SimpleObjectProperty<>(this, "converter") { protected void invalidated() { updateShowLabel(); } }; /** * The {@link StringConverter} property. * @return the {@link StringConverter} property */ public final ObjectProperty> converterProperty() { return converter; } /** * Sets the {@link StringConverter} to be used in this cell. * @param value the {@link StringConverter} to be used in this cell */ public final void setConverter(StringConverter value) { converterProperty().set(value); } /** * Returns the {@link StringConverter} used in this cell. * @return the {@link StringConverter} used in this cell */ public final StringConverter getConverter() { return converterProperty().get(); } // --- selected state callback property private ObjectProperty>> selectedStateCallback = new SimpleObjectProperty<>( this, "selectedStateCallback"); /** * Property representing the {@link Callback} that is bound to by the * CheckBox shown on screen. * @return the property representing the {@link Callback} that is bound to * by the CheckBox shown on screen */ public final ObjectProperty>> selectedStateCallbackProperty() { return selectedStateCallback; } /** * Sets the {@link Callback} that is bound to by the CheckBox shown on screen. * @param value the {@link Callback} that is bound to by the CheckBox shown * on screen */ public final void setSelectedStateCallback(Callback> value) { selectedStateCallbackProperty().set(value); } /** * Returns the {@link Callback} that is bound to by the CheckBox shown on screen. * @return the {@link Callback} that is bound to by the CheckBox shown on * screen */ public final Callback> getSelectedStateCallback() { return selectedStateCallbackProperty().get(); } /* ************************************************************************* * * * Public API * * * **************************************************************************/ /** {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public void updateItem(T item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(null); setGraphic(null); } else { StringConverter c = getConverter(); if (showLabel) { setText(c.toString(item)); } setGraphic(checkBox); if (booleanProperty instanceof BooleanProperty) { checkBox.selectedProperty().unbindBidirectional((BooleanProperty)booleanProperty); } ObservableValue obsValue = getSelectedProperty(); if (obsValue instanceof BooleanProperty) { booleanProperty = (ObservableValue) obsValue; checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty); } checkBox.disableProperty().bind(Bindings.not( getTreeTableView().editableProperty().and( getTableColumn().editableProperty()).and( editableProperty()) )); } } /* ************************************************************************* * * * Private implementation * * * **************************************************************************/ private void updateShowLabel() { this.showLabel = converter != null; this.checkBox.setAlignment(showLabel ? Pos.CENTER_LEFT : Pos.CENTER); } private ObservableValue getSelectedProperty() { return getSelectedStateCallback() != null ? getSelectedStateCallback().call(getIndex()) : getTableColumn().getCellObservableValue(getIndex()); } }