javafx.scene.control.cell.CheckBoxTreeTableCell Maven / Gradle / Ivy
Show all versions of openjfx-78-backport Show documentation
/*
* Copyright (c) 2012, 2013, 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.css.CssMetaData;
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).
*
* @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).
*
* @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 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 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 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 new Callback, TreeTableCell>() {
@Override public TreeTableCell call(TreeTableColumn list) {
return 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();
setGraphic(checkBox);
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.
*/
public final ObjectProperty> converterProperty() {
return converter;
}
/**
* Sets 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.
*/
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.
*/
public final ObjectProperty>> selectedStateCallbackProperty() {
return selectedStateCallback;
}
/**
* Sets 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.
*/
public final Callback> getSelectedStateCallback() {
return selectedStateCallbackProperty().get();
}
/***************************************************************************
* *
* Public API *
* *
**************************************************************************/
/** {@inheritDoc} */
@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 = 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());
}
}