Spec-Zone .ru
спецификации, руководства, описания, API
|
001/* 002 * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package javafx.scene.control.cell; 027 028import javafx.scene.control.CheckBoxTreeItem; 029import javafx.beans.property.BooleanProperty; 030import javafx.beans.property.ObjectProperty; 031import javafx.beans.property.SimpleObjectProperty; 032import javafx.beans.value.ObservableValue; 033import javafx.scene.control.CheckBox; 034import javafx.scene.control.TreeCell; 035import javafx.scene.control.TreeItem; 036import javafx.scene.control.TreeView; 037import javafx.util.Callback; 038import javafx.util.StringConverter; 039 040/** 041 * A class containing a {@link TreeCell} implementation that draws a 042 * {@link CheckBox} node inside the cell, along with support for common 043 * interactions (discussed in more depth shortly). 044 * 045 * <p>To make creating TreeViews with CheckBoxes easier, a convenience class 046 * called {@link CheckBoxTreeItem} is provided. It is <b>highly</b> recommended 047 * that developers use this class, rather than the regular {@link TreeItem} 048 * class, when constructing their TreeView tree structures. Refer to the 049 * CheckBoxTreeItem API documentation for an example on how these two classes 050 * can be combined. 051 * 052 * <p>When used in a TreeView, the CheckBoxCell is rendered with a CheckBox to 053 * the right of the 'disclosure node' (i.e. the arrow). The item stored in 054 * {@link CheckBoxTreeItem#getValue()} will then have the StringConverter called 055 * on it, and this text will take all remaining horizontal space. Additionally, 056 * by using {@link CheckBoxTreeItem}, the TreeView will automatically handle 057 * situations such as: 058 * 059 * <ul> 060 * <li>Clicking on the {@link CheckBox} beside an item that has children will 061 * result in all children also becoming selected/unselected. 062 * <li>Clicking on the {@link CheckBox} beside an item that has a parent will 063 * possibly toggle the state of the parent. For example, if you select a 064 * single child, the parent will become indeterminate (indicating partial 065 * selection of children). If you proceed to select all children, the 066 * parent will then show that it too is selected. This is recursive, with 067 * all parent nodes updating as expected. 068 * </ul> 069 * 070 * If it is decided that using {@link CheckBoxTreeItem} is not desirable, 071 * then it is necessary to call one of the constructors where a {@link Callback} 072 * is provided that can return an {@code ObservableValue<Boolean>} 073 * given a {@link TreeItem} instance. This {@code ObservableValue<Boolean>} 074 * should represent the boolean state of the given {@link TreeItem}. 075 * 076 * @param <T> The type of the elements contained within the TreeView TreeItem 077 * instances. 078 * @since 2.2 079 */ 080public class CheckBoxTreeCell<T> extends TreeCell<T> { 081 082 /*************************************************************************** 083 * * 084 * Static cell factories * 085 * * 086 **************************************************************************/ 087 088 /** 089 * Creates a cell factory for use in a TreeView control, although there is a 090 * major assumption when used in a TreeView: this cell factory assumes that 091 * the TreeView root, and <b>all</b> children are instances of 092 * {@link CheckBoxTreeItem}, rather than the default {@link TreeItem} class 093 * that is used normally. 094 * 095 * <p>When used in a TreeView, the CheckBoxCell is rendered with a CheckBox 096 * to the right of the 'disclosure node' (i.e. the arrow). The item stored 097 * in {@link CheckBoxTreeItem#getValue()} will then have the StringConverter 098 * called on it, and this text will take all remaining horizontal space. 099 * Additionally, by using {@link CheckBoxTreeItem}, the TreeView will 100 * automatically handle situations such as: 101 * 102 * <ul> 103 * <li>Clicking on the {@link CheckBox} beside an item that has children 104 * will result in all children also becoming selected/unselected.</li> 105 * <li>Clicking on the {@link CheckBox} beside an item that has a parent 106 * will possibly toggle the state of the parent. For example, if you 107 * select a single child, the parent will become indeterminate (indicating 108 * partial selection of children). If you proceed to select all 109 * children, the parent will then show that it too is selected. This is 110 * recursive, with all parent nodes updating as expected.</li> 111 * </ul> 112 * 113 * <p>Unfortunately, due to limitations in Java, it is necessary to provide 114 * an explicit cast when using this method. For example: 115 * 116 * <pre> 117 * {@code 118 * final TreeView<String> treeView = new TreeView<String>(); 119 * treeView.setCellFactory(CheckBoxCell.<String>forTreeView());}</pre> 120 * 121 * @param <T> The type of the elements contained within the 122 * {@link CheckBoxTreeItem} instances. 123 * @return A {@link Callback} that will return a TreeCell that is able to 124 * work on the type of element contained within the TreeView root, and 125 * all of its children (recursively). 126 */ 127 public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView() { 128 Callback<TreeItem<T>, ObservableValue<Boolean>> getSelectedProperty = 129 new Callback<TreeItem<T>, ObservableValue<Boolean>>() { 130 @Override public ObservableValue<Boolean> call(TreeItem<T> item) { 131 if (item instanceof CheckBoxTreeItem<?>) { 132 return ((CheckBoxTreeItem<?>)item).selectedProperty(); 133 } 134 return null; 135 } 136 }; 137 return forTreeView(getSelectedProperty, 138 CellUtils.<T>defaultTreeItemStringConverter()); 139 } 140 141 /** 142 * Creates a cell factory for use in a TreeView control. Unlike 143 * {@link #forTreeView()}, this method does not assume that all TreeItem 144 * instances in the TreeView are {@link CheckBoxTreeItem} instances. 145 * 146 * <p>When used in a TreeView, the CheckBoxCell is rendered with a CheckBox 147 * to the right of the 'disclosure node' (i.e. the arrow). The item stored 148 * in {@link CheckBoxTreeItem#getValue()} will then have the StringConverter 149 * called on it, and this text will take all remaining horizontal space. 150 * 151 * <p>Unlike {@link #forTreeView()}, this cell factory does not handle 152 * updating the state of parent or children TreeItems - it simply toggles 153 * the {@code ObservableValue<Boolean>} that is provided, and no more. Of 154 * course, this functionality can then be implemented externally by adding 155 * observers to the {@code ObservableValue<Boolean>}, and toggling the state 156 * of other properties as necessary. 157 * 158 * @param <T> The type of the elements contained within the {@link TreeItem} 159 * instances. 160 * @param getSelectedProperty A {@link Callback} that, given an object of 161 * type TreeItem<T>, will return an {@code ObservableValue<Boolean>} 162 * that represents whether the given item is selected or not. This 163 * {@code ObservableValue<Boolean>} will be bound bidirectionally 164 * (meaning that the CheckBox in the cell will set/unset this property 165 * based on user interactions, and the CheckBox will reflect the state 166 * of the {@code ObservableValue<Boolean>}, if it changes externally). 167 * @return A {@link Callback} that will return a TreeCell that is able to 168 * work on the type of element contained within the TreeView root, and 169 * all of its children (recursively). 170 */ 171 public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView( 172 final Callback<TreeItem<T>, 173 ObservableValue<Boolean>> getSelectedProperty) { 174 return forTreeView(getSelectedProperty, CellUtils.<T>defaultTreeItemStringConverter()); 175 } 176 177 /** 178 * Creates a cell factory for use in a TreeView control. Unlike 179 * {@link #forTreeView()}, this method does not assume that all TreeItem 180 * instances in the TreeView are {@link CheckBoxTreeItem}. 181 * 182 * <p>When used in a TreeView, the CheckBoxCell is rendered with a CheckBox 183 * to the right of the 'disclosure node' (i.e. the arrow). The item stored 184 * in {@link TreeItem#getValue()} will then have the the StringConverter 185 * called on it, and this text will take all remaining horizontal space. 186 * 187 * <p>Unlike {@link #forTreeView()}, this cell factory does not handle 188 * updating the state of parent or children TreeItems - it simply toggles 189 * the {@code ObservableValue<Boolean>} that is provided, and no more. Of 190 * course, this functionality can then be implemented externally by adding 191 * observers to the {@code ObservableValue<Boolean>}, and toggling the state 192 * of other properties as necessary. 193 * 194 * @param <T> The type of the elements contained within the {@link TreeItem} 195 * instances. 196 * @param getSelectedProperty A Callback that, given an object of 197 * type TreeItem<T>, will return an {@code ObservableValue<Boolean>} 198 * that represents whether the given item is selected or not. This 199 * {@code ObservableValue<Boolean>} will be bound bidirectionally 200 * (meaning that the CheckBox in the cell will set/unset this property 201 * based on user interactions, and the CheckBox will reflect the state of 202 * the {@code ObservableValue<Boolean>}, if it changes externally). 203 * @param converter A StringConverter that, give an object of type TreeItem<T>, 204 * will return a String that can be used to represent the object 205 * visually. The default implementation in {@link #forTreeView(Callback)} 206 * is to simply call .toString() on all non-null items (and to just 207 * return an empty string in cases where the given item is null). 208 * @return A {@link Callback} that will return a TreeCell that is able to 209 * work on the type of element contained within the TreeView root, and 210 * all of its children (recursively). 211 */ 212 public static <T> Callback<TreeView<T>, TreeCell<T>> forTreeView( 213 final Callback<TreeItem<T>, ObservableValue<Boolean>> getSelectedProperty, 214 final StringConverter<TreeItem<T>> converter) { 215 return new Callback<TreeView<T>, TreeCell<T>>() { 216 @Override public TreeCell<T> call(TreeView<T> tree) { 217 return new CheckBoxTreeCell<T>(getSelectedProperty, converter); 218 } 219 }; 220 } 221 222 223 224 225 /*************************************************************************** 226 * * 227 * Fields * 228 * * 229 **************************************************************************/ 230 private final CheckBox checkBox; 231 232 private ObservableValue<Boolean> booleanProperty; 233 234 private BooleanProperty indeterminateProperty; 235 236 237 238 /*************************************************************************** 239 * * 240 * Constructors * 241 * * 242 **************************************************************************/ 243 244 /** 245 * Creates a default {@link CheckBoxTreeCell} that assumes the TreeView is 246 * constructed with {@link CheckBoxTreeItem} instances, rather than the 247 * default {@link TreeItem}. 248 * By using {@link CheckBoxTreeItem}, it will internally manage the selected 249 * and indeterminate state of each item in the tree. 250 */ 251 public CheckBoxTreeCell() { 252 // getSelectedProperty as anonymous inner class to deal with situation 253 // where the user is using CheckBoxTreeItem instances in their tree 254 this(new Callback<TreeItem<T>, ObservableValue<Boolean>>() { 255 @Override public ObservableValue<Boolean> call(TreeItem<T> item) { 256 if (item instanceof CheckBoxTreeItem<?>) { 257 return ((CheckBoxTreeItem<?>)item).selectedProperty(); 258 } 259 return null; 260 } 261 }); 262 } 263 264 /** 265 * Creates a {@link CheckBoxTreeCell} for use in a TreeView control via a 266 * cell factory. Unlike {@link CheckBoxTreeCell#CheckBoxTreeCell()}, this 267 * method does not assume that all TreeItem instances in the TreeView are 268 * {@link CheckBoxTreeItem}. 269 * 270 * <p>To call this method, it is necessary to provide a 271 * {@link Callback} that, given an object of type TreeItem<T>, will return 272 * an {@code ObservableValue<Boolean>} that represents whether the given 273 * item is selected or not. This {@code ObservableValue<Boolean>} will be 274 * bound bidirectionally (meaning that the CheckBox in the cell will 275 * set/unset this property based on user interactions, and the CheckBox will 276 * reflect the state of the {@code ObservableValue<Boolean>}, if it changes 277 * externally). 278 * 279 * <p>If the items are not {@link CheckBoxTreeItem} instances, it becomes 280 * the developers responsibility to handle updating the state of parent and 281 * children TreeItems. This means that, given a TreeItem, this class will 282 * simply toggles the {@code ObservableValue<Boolean>} that is provided, and 283 * no more. Of course, this functionality can then be implemented externally 284 * by adding observers to the {@code ObservableValue<Boolean>}, and toggling 285 * the state of other properties as necessary. 286 * 287 * @param getSelectedProperty A {@link Callback} that will return an 288 * {@code ObservableValue<Boolean>} that represents whether the given 289 * item is selected or not. 290 */ 291 public CheckBoxTreeCell( 292 final Callback<TreeItem<T>, ObservableValue<Boolean>> getSelectedProperty) { 293 this(getSelectedProperty, CellUtils.<T>defaultTreeItemStringConverter(), null); 294 } 295 296 /** 297 * Creates a {@link CheckBoxTreeCell} for use in a TreeView control via a 298 * cell factory. Unlike {@link CheckBoxTreeCell#CheckBoxTreeCell()}, this 299 * method does not assume that all TreeItem instances in the TreeView are 300 * {@link CheckBoxTreeItem}. 301 * 302 * <p>To call this method, it is necessary to provide a {@link Callback} 303 * that, given an object of type TreeItem<T>, will return an 304 * {@code ObservableValue<Boolean>} that represents whether the given item 305 * is selected or not. This {@code ObservableValue<Boolean>} will be bound 306 * bidirectionally (meaning that the CheckBox in the cell will set/unset 307 * this property based on user interactions, and the CheckBox will reflect 308 * the state of the {@code ObservableValue<Boolean>}, if it changes 309 * externally). 310 * 311 * <p>If the items are not {@link CheckBoxTreeItem} instances, it becomes 312 * the developers responsibility to handle updating the state of parent and 313 * children TreeItems. This means that, given a TreeItem, this class will 314 * simply toggles the {@code ObservableValue<Boolean>} that is provided, and 315 * no more. Of course, this functionality can then be implemented externally 316 * by adding observers to the {@code ObservableValue<Boolean>}, and toggling 317 * the state of other properties as necessary. 318 * 319 * @param getSelectedProperty A {@link Callback} that will return an 320 * {@code ObservableValue<Boolean>} that represents whether the given 321 * item is selected or not. 322 * @param converter A StringConverter that, give an object of type TreeItem<T>, will 323 * return a String that can be used to represent the object visually. 324 */ 325 public CheckBoxTreeCell( 326 final Callback<TreeItem<T>, ObservableValue<Boolean>> getSelectedProperty, 327 final StringConverter<TreeItem<T>> converter) { 328 this(getSelectedProperty, converter, null); 329 } 330 331 private CheckBoxTreeCell( 332 final Callback<TreeItem<T>, ObservableValue<Boolean>> getSelectedProperty, 333 final StringConverter<TreeItem<T>> converter, 334 final Callback<TreeItem<T>, ObservableValue<Boolean>> getIndeterminateProperty) { 335 this.getStyleClass().add("check-box-tree-cell"); 336 setSelectedStateCallback(getSelectedProperty); 337 setConverter(converter); 338 339 this.checkBox = new CheckBox(); 340 this.checkBox.setAllowIndeterminate(false); 341 setGraphic(checkBox); 342 } 343 344 345 346 /*************************************************************************** 347 * * 348 * Properties * 349 * * 350 **************************************************************************/ 351 352 // --- converter 353 private ObjectProperty<StringConverter<TreeItem<T>>> converter = 354 new SimpleObjectProperty<StringConverter<TreeItem<T>>>(this, "converter"); 355 356 /** 357 * The {@link StringConverter} property. 358 */ 359 public final ObjectProperty<StringConverter<TreeItem<T>>> converterProperty() { 360 return converter; 361 } 362 363 /** 364 * Sets the {@link StringConverter} to be used in this cell. 365 */ 366 public final void setConverter(StringConverter<TreeItem<T>> value) { 367 converterProperty().set(value); 368 } 369 370 /** 371 * Returns the {@link StringConverter} used in this cell. 372 */ 373 public final StringConverter<TreeItem<T>> getConverter() { 374 return converterProperty().get(); 375 } 376 377 378 379 // --- selected state callback property 380 private ObjectProperty<Callback<TreeItem<T>, ObservableValue<Boolean>>> 381 selectedStateCallback = 382 new SimpleObjectProperty<Callback<TreeItem<T>, ObservableValue<Boolean>>>( 383 this, "selectedStateCallback"); 384 385 /** 386 * Property representing the {@link Callback} that is bound to by the 387 * CheckBox shown on screen. 388 */ 389 public final ObjectProperty<Callback<TreeItem<T>, ObservableValue<Boolean>>> selectedStateCallbackProperty() { 390 return selectedStateCallback; 391 } 392 393 /** 394 * Sets the {@link Callback} that is bound to by the CheckBox shown on screen. 395 */ 396 public final void setSelectedStateCallback(Callback<TreeItem<T>, ObservableValue<Boolean>> value) { 397 selectedStateCallbackProperty().set(value); 398 } 399 400 /** 401 * Returns the {@link Callback} that is bound to by the CheckBox shown on screen. 402 */ 403 public final Callback<TreeItem<T>, ObservableValue<Boolean>> getSelectedStateCallback() { 404 return selectedStateCallbackProperty().get(); 405 } 406 407 408 409 /*************************************************************************** 410 * * 411 * Public API * 412 * * 413 **************************************************************************/ 414 415 /** {@inheritDoc} */ 416 @Override public void updateItem(T item, boolean empty) { 417 super.updateItem(item, empty); 418 419 if (empty) { 420 setText(null); 421 setGraphic(null); 422 } else { 423 StringConverter c = getConverter(); 424 425 TreeItem<T> treeItem = getTreeItem(); 426 427 // update the node content 428 setText(c != null ? c.toString(treeItem) : (treeItem == null ? "" : treeItem.toString())); 429 checkBox.setGraphic(treeItem == null ? null : treeItem.getGraphic()); 430 setGraphic(checkBox); 431 432 // uninstall bindings 433 if (booleanProperty != null) { 434 checkBox.selectedProperty().unbindBidirectional((BooleanProperty)booleanProperty); 435 } 436 if (indeterminateProperty != null) { 437 checkBox.indeterminateProperty().unbindBidirectional(indeterminateProperty); 438 } 439 440 // install new bindings. 441 // We special case things when the TreeItem is a CheckBoxTreeItem 442 if (treeItem instanceof CheckBoxTreeItem) { 443 CheckBoxTreeItem<T> cbti = (CheckBoxTreeItem<T>) treeItem; 444 booleanProperty = cbti.selectedProperty(); 445 checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty); 446 447 indeterminateProperty = cbti.indeterminateProperty(); 448 checkBox.indeterminateProperty().bindBidirectional(indeterminateProperty); 449 } else { 450 Callback<TreeItem<T>, ObservableValue<Boolean>> callback = getSelectedStateCallback(); 451 if (callback == null) { 452 throw new NullPointerException( 453 "The CheckBoxTreeCell selectedStateCallbackProperty can not be null"); 454 } 455 456 booleanProperty = callback.call(treeItem); 457 if (booleanProperty != null) { 458 checkBox.selectedProperty().bindBidirectional((BooleanProperty)booleanProperty); 459 } 460 } 461 } 462 } 463}