1 /* 2 * Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene.control; 27 28 import javafx.beans.InvalidationListener; 29 import javafx.beans.Observable; 30 import javafx.beans.property.BooleanProperty; 31 import javafx.beans.property.ObjectProperty; 32 import javafx.beans.property.SimpleBooleanProperty; 33 import javafx.beans.property.SimpleObjectProperty; 34 import javafx.collections.ObservableList; 35 import javafx.scene.Node; 36 import javafx.scene.layout.GridPane; 37 import javafx.scene.layout.HBox; 38 import javafx.scene.shape.Rectangle; 39 import javafx.css.PseudoClass; 40 import javafx.beans.property.ReadOnlyBooleanProperty; 41 import javafx.beans.property.ReadOnlyBooleanWrapper; 42 import javafx.beans.value.WritableValue; 43 import javafx.css.StyleableProperty; 44 45 import java.util.Optional; 46 47 /** 48 * The Cell API is used for virtualized controls such as {@link ListView}, 49 * {@link TreeView}, and {@link TableView}. 50 * A Cell is a {@link Labeled} {@link Control}, and is used to render a single 51 * "row" inside a ListView, TreeView or TableView. Cells are also used for each 52 * individual 'cell' inside a TableView (i.e. each row/column intersection). See 53 * the JavaDoc for each control separately for more detail. 54 * <p> 55 * Every Cell is associated with a single data item (represented by the 56 * {@link #itemProperty() item} property). The Cell is responsible for 57 * rendering that item and, where appropriate, for editing the item. An item 58 * within a Cell may be represented by text or some other control such as a 59 * {@link CheckBox}, {@link ChoiceBox} or any other {@link Node} such as a 60 * {@link HBox}, {@link GridPane}, or even a {@link Rectangle}. 61 * <p> 62 * Because TreeView, ListView, TableView and other such controls can potentially 63 * be used for displaying incredibly large amounts of data, it is not feasible 64 * to create an actual Cell for every single item in the control. 65 * We represent extremely large data sets using only very few Cells. Each Cell 66 * is "recycled", or reused. This is what we mean when we say that these controls 67 * are virtualized. 68 * <p> 69 * Since Cell is a Control, it is essentially a "model". Its Skin is responsible 70 * for defining the look and layout, while the Behavior is responsible for 71 * handling all input events and using that information to modify the Control 72 * state. Also, the Cell is styled from CSS just like any other Control. 73 * However, it is not necessary to implement a Skin for most uses of a Cell. 74 * This is because a cell factory can be set - this is detailed more shortly. 75 * <p> 76 * Because by far the most common use case for cells is to show text to a user, 77 * this use case is specially optimized for within Cell. This is done by Cell 78 * extending from {@link Labeled}. This means that subclasses of Cell need only 79 * set the {@link #textProperty() text} property, rather than create a separate 80 * {@link Label} and set that within the Cell. However, for situations where 81 * something more than just plain text is called for, it is possible to place 82 * any {@link Node} in the Cell {@link #graphicProperty() graphic} property. 83 * Despite the term, a graphic can be any Node, and will be fully interactive. 84 * For example, a ListCell might be configured with a {@link Button} as its 85 * graphic. The Button text could then be bound to the cells 86 * {@link #itemProperty() item} property. In this way, whenever the item in the 87 * Cell changes, the Button text is automatically updated. 88 * <p> 89 * Cell sets focusTraversable to false. 90 * </p> 91 * <p> 92 * <b>Cell Factories</b> 93 * <p> 94 * The default representation of the Cell <code>item</code> is up to the various 95 * virtualized container's skins to render. For example, the ListView by default 96 * will convert the item to a String and call {@link #setText(java.lang.String)} 97 * with this value. If you want to specialize the Cell used for the 98 * ListView (for example), then you must provide an implementation of the 99 * {@link ListView#cellFactoryProperty() cellFactory} callback function defined 100 * on the ListView. Similar API exists on most controls that use Cells (for example, 101 * {@link TreeView#cellFactoryProperty() TreeView}, 102 * {@link TableView#rowFactoryProperty() TableView}, 103 * {@link TableColumn#cellFactoryProperty() TableColumn} and 104 * {@link ListView#cellFactoryProperty() ListView}. 105 * <p> 106 * The cell factory is called by the platform whenever it determines that a new 107 * cell needs to be created. For example, perhaps your ListView has 10 million 108 * items. Creating all 10 million cells would be prohibitively expensive. So 109 * instead the ListView skin implementation might only create just enough cells 110 * to fit the visual space. If the ListView is resized to be larger, the system 111 * will determine that it needs to create some additional cells. In this case 112 * it will call the cellFactory callback function (if one is provided) to create 113 * the Cell implementation that should be used. If no cell factory is provided, 114 * the built-in default implementation will be used. 115 * <p> 116 * The implementation of the cell factory is then responsible not just for 117 * creating a Cell instance, but also configuring that Cell such that it reacts 118 * to changes in its state. For example, if I were to create 119 * a custom Cell which formatted Numbers such that they would appear as currency 120 * types, I might do so like this: 121 * 122 * <pre> 123 * public class MoneyFormatCell extends ListCell<Number> { 124 * 125 * public MoneyFormatCell() { } 126 * 127 * @Override protected void updateItem(Number item, boolean empty) { 128 * // calling super here is very important - don't skip this! 129 * super.updateItem(item, empty); 130 * 131 * // format the number as if it were a monetary value using the 132 * // formatting relevant to the current locale. This would format 133 * // 43.68 as "$43.68", and -23.67 as "-$23.67" 134 * setText(item == null ? "" : NumberFormat.getCurrencyInstance().format(item)); 135 * 136 * // change the text fill based on whether it is positive (green) 137 * // or negative (red). If the cell is selected, the text will 138 * // always be white (so that it can be read against the blue 139 * // background), and if the value is zero, we'll make it black. 140 * if (item != null) { 141 * double value = item.doubleValue(); 142 * setTextFill(isSelected() ? Color.WHITE : 143 * value == 0 ? Color.BLACK : 144 * value < 0 ? Color.RED : Color.GREEN); 145 * } 146 * } 147 * }</pre> 148 * 149 * This class could then be used inside a ListView as such: 150 * 151 * <pre> 152 * ObservableList<Number> money = ...; 153 * final ListView<Number> listView = new ListView<Number>(money); 154 * listView.setCellFactory(new Callback<ListView<Number>, ListCell<Number>>() { 155 * @Override public ListCell<Number> call(ListView<Number> list) { 156 * return new MoneyFormatCell(); 157 * } 158 * });</pre> 159 * 160 * In this example an anonymous inner class is created, that simply returns 161 * instances of MoneyFormatCell whenever it is called. The MoneyFormatCell class 162 * extends {@link ListCell}, overriding the 163 * {@link #updateItem(java.lang.Object, boolean) updateItem} method. This method 164 * is called whenever the item in the cell changes, for example when the user 165 * scrolls the ListView or the content of the underlying data model changes 166 * (and the cell is reused to represent some different item in the ListView). 167 * Because of this, there is no need to manage bindings - simply react to the 168 * change in items when this method occurs. In the example above, whenever the 169 * item changes, we update the cell text property, and also modify the text fill 170 * to ensure that we get the correct visuals. In addition, if the cell is "empty" 171 * (meaning it is used to fill out space in the ListView but doesn't have any 172 * data associated with it), then we just use the empty String. 173 * <p> 174 * Note that there are additional 175 * methods prefixed with 'update' that may be of interest, so be 176 * sure to read the API documentation for Cell, and subclasses of Cell, closely. 177 * <p> 178 * Of course, we can also use the binding API rather than overriding the 179 * 'update' methods. Shown below is a very trivial example of how this could 180 * be achieved. 181 * 182 * 183 * <pre> 184 * public class BoundLabelCell extends ListCell<String> { 185 * 186 * public BoundLabelCell() { 187 * textProperty().bind(itemProperty()); 188 * } 189 * } 190 * </pre> 191 * 192 * <h3>Key Design Goals</h3> 193 * <ul> 194 * <li>Both time and memory efficient for large data sets</li> 195 * <li>Easy to build and use libraries for custom cells</li> 196 * <li>Easy to customize cell visuals</li> 197 * <li>Easy to customize display formatting (12.34 as $12.34 or 1234% etc)</li> 198 * <li>Easy to extend for custom visuals</li> 199 * <li>Easy to have "panels" of data for the visuals</li> 200 * <li>Easy to animate the cell size or other properties</li> 201 * </ul> 202 * 203 * <h3>Key Use Cases</h3> 204 * Following are a number of key use cases used to drive the Cell API design, 205 * along with code examples showing how those use cases are satisfied by this 206 * API. This is by no means to be considered the definitive list of capabilities 207 * or features supported, but rather, to provide some guidance as to how to use 208 * the Cell API. The examples below are focused on the ListView, but the same 209 * philosophy applies to TreeCells or other kinds of cells. 210 * <p> 211 * <b>Changing the Cell's Colors</b> 212 * <p> 213 * This should be extraordinarily simple in JavaFX. Each Cell can be styled 214 * directly from CSS. So for example, if you wanted to change the default 215 * background of every cell in a ListView to be WHITE you could do the 216 * following CSS: 217 * 218 * <pre> 219 * .list-cell { 220 * -fx-padding: 3 3 3 3; 221 * -fx-background-color: white; 222 * }</pre> 223 * 224 * If you wanted to set the color of selected ListView cells to be blue, you 225 * could add this to your CSS file: 226 * 227 * <pre> 228 * .list-cell:selected { 229 * -fx-background-color: blue; 230 * }</pre> 231 * 232 * Most Cell implementations extend from {@link IndexedCell} rather than Cell. 233 * IndexedCell adds two other pseudoclass states: "odd" and "even". Using this 234 * you can get alternate row striping by doing something like this in your CSS 235 * file: 236 * 237 * <pre> 238 * .list-cell:odd { 239 * -fx-background-color: grey; 240 * }</pre> 241 * 242 * Each of these examples require no code changes. Simply update your CSS 243 * file to alter the colors. You can also use the "hover" and other 244 * pseudoclasses in CSS the same as with other controls. 245 * <p> 246 * Another approach to the first example above (formatting a list of numbers) would 247 * be to use style classes. Suppose you had an {@link ObservableList} of Numbers 248 * to display in a ListView and wanted to color all of the negative values red 249 * and all positive or 0 values black. 250 * One way to achieve this is with a custom cellFactory which changes the 251 * styleClass of the Cell based on whether the value is negative or positive. This 252 * is as simple as adding code to test if the number in the cell is negative, 253 * and adding a "negative" styleClass. If the number is not negative, the "negative" 254 * string should be removed. This approach allows for the colors to be defined 255 * from CSS, allowing for simple customization. The CSS file would then include 256 * something like the following: 257 * 258 * <pre> 259 * .list-cell { 260 * -fx-text-fill: black; 261 * } 262 * 263 * .list-cell .negative { 264 * -fx-text-fill: red; 265 * }</pre> 266 * 267 * <h3>Editing</h3> 268 * <p>Most virtualized controls that use the Cell architecture (e.g. {@link ListView}, 269 * {@link TreeView}, {@link TableView} and {@link TreeTableView}) all support 270 * the notion of editing values directly via the cell. You can learn more about 271 * the control-specific details by going to the 'editing' section in the class 272 * documentation for the controls linked above. The remainder of this section 273 * will cover some of the finer details of editing with cells.</p> 274 * 275 * <p>The general flow of editing is as follows (note that in these steps the 276 * {@link ListView} control is used as an example, but similar API exists for 277 * all controls mentioned above, and the process is exactly the same in general):</p> 278 * 279 * <ol> 280 * <li>User requests a cell enter editing mode (via keyboard or mouse commands), 281 * or the developer requests that a cell enter editing mode (by calling a 282 * method such as the ListView {@link ListView#edit(int) edit} method. 283 * <strong>Note:</strong> If the user double-clicks or fires an appropriate 284 * keyboard command to initiate editing, then they are effectively calling 285 * the appropriate edit method on the control (i.e. the entry method for 286 * user-initiated and developer-initiated editing is the same).</li> 287 * <li>Each cell in the visible region of the control is notified that the 288 * current {@link javafx.scene.control.ListView#editingIndexProperty() editing cell} 289 * has changed, and checks to see if it is itself. At this point one of three 290 * things can happen: 291 * <ol> 292 * <li>If the editing index is the same index as the cell, 293 * {@link #startEdit()} will be called on this cell. Some pointers: 294 * <ol> 295 * <li>It is recommended that subclasses of Cell override the {@link #startEdit()} 296 * method to update the visuals of the cell when enters the editing state. Note 297 * however that it is very important that subclasses that override the 298 * {@link #startEdit()} method continue to call {@code super.startEdit()} so 299 * that parent classes can update additional state that is necessary for 300 * editing to be successful.</li> 301 * <li>Within the {@link #startEdit()} method is an ideal 302 * time to change the visuals of the cell. For example (and 303 * note that this example is more fleshed out in the UI control 304 * javadocs for {@link ListView}, etc), you may set the 305 * {@link #graphicProperty()} of the cell to a 306 * {@link TextField} and set the {@link #textProperty()} 307 * to null. This would allow end users to then type in input 308 * and make changes to your data model.</li> 309 * <li>When the user has completed editing, they will want 310 * to commit or cancel their change. This is your responsibility 311 * to handle (e.g. by having the Enter key 312 * {@link #commitEdit(Object) commit the edit} 313 * and the ESC key {@link #cancelEdit() cancel the edit}). 314 * You do this by attaching the appropriate event listeners 315 * to the nodes you show whilst in the editing state.</li> 316 * </ol> 317 * </li> 318 * <li>If the editing index is not the same index as the cell, and 319 * if the cell is currently in the {@link #isEditing() editing state}, 320 * {@link #cancelEdit()} will be called on this cell. As with the 321 * {@link #startEdit()} method, you should override this method to 322 * clean up the visuals of the cell (and most probably return the 323 * {@link #graphicProperty()} back to null and set the 324 * {@link #textProperty()} to its (possibly new) value. Again, 325 * be sure to always call {@code super.cancelEdit()} to make sure all 326 * state is correctly updated.</li> 327 * <li>If the editing index is not the same index as the cell, and 328 * if the cell is not currently in the {@link #isEditing()} editing state}, 329 * then nothing will happen on this cell.</li> 330 * </ol> 331 * </li> 332 * </ol> 333 * 334 * 335 * @param <T> The type of the item contained within the Cell. 336 * 337 * @since JavaFX 2.0 338 */ 339 public class Cell<T> extends Labeled { 340 341 /*************************************************************************** 342 * * 343 * Constructors * 344 * * 345 **************************************************************************/ 346 347 /** 348 * Creates a default Cell with the default style class of 'cell'. 349 */ 350 public Cell() { 351 setText(null); // default to null text, to match the null item 352 // focusTraversable is styleable through css. Calling setFocusTraversable 353 // makes it look to css like the user set the value and css will not 354 // override. Initializing focusTraversable by calling set on the 355 // CssMetaData ensures that css will be able to override the value. 356 ((StyleableProperty<Boolean>)(WritableValue<Boolean>)focusTraversableProperty()).applyStyle(null, Boolean.FALSE); 357 getStyleClass().addAll(DEFAULT_STYLE_CLASS); 358 359 /** 360 * Indicates whether or not this cell has focus. For example, a 361 * ListView defines zero or one cell as being the "focused" cell. This cell 362 * would have focused set to true. 363 */ 364 super.focusedProperty().addListener(o -> { 365 // The user has shifted focus, so we should cancel the editing on this cell 366 if (!isFocused() && isEditing()) { 367 attemptEditCommit(); 368 } 369 }); 370 371 // initialize default pseudo-class state 372 pseudoClassStateChanged(PSEUDO_CLASS_EMPTY, true); 373 } 374 375 376 377 /*************************************************************************** 378 * * 379 * Properties * 380 * * 381 **************************************************************************/ 382 383 // --- item 384 private ObjectProperty<T> item = new SimpleObjectProperty<T>(this, "item"); 385 386 /** 387 * The data value associated with this Cell. This value is set by the 388 * virtualized Control when the Cell is created or updated. This represents 389 * the raw data value. 390 * 391 * <p>This value should only be set in subclasses of Cell by the virtualised 392 * user interface controls that know how to properly work with the Cell 393 * class. 394 * @return the data value associated with this cell 395 */ 396 public final ObjectProperty<T> itemProperty() { return item; } 397 398 /** 399 * Sets the item to the given value - should not be called directly as the 400 * item is managed by the virtualized control. 401 * @param value the new data value to set in this cell 402 */ 403 public final void setItem(T value) { item.set(value); } 404 405 /** 406 * Returns the data value associated with this Cell. 407 * @return the data value associated with this cell 408 */ 409 public final T getItem() { return item.get(); } 410 411 412 413 // --- empty 414 private ReadOnlyBooleanWrapper empty = new ReadOnlyBooleanWrapper(true) { 415 @Override protected void invalidated() { 416 final boolean active = get(); 417 pseudoClassStateChanged(PSEUDO_CLASS_EMPTY, active); 418 pseudoClassStateChanged(PSEUDO_CLASS_FILLED, !active); 419 } 420 421 @Override 422 public Object getBean() { 423 return Cell.this; 424 } 425 426 @Override 427 public String getName() { 428 return "empty"; 429 } 430 }; 431 432 /** 433 * A property used to represent whether the cell has any contents. 434 * If true, then the Cell contains no data and is not associated with any 435 * data item in the virtualized Control. 436 * 437 * <p>When a cell is empty, it can be styled differently via the 'empty' 438 * CSS pseudo class state. For example, it may not receive any 439 * alternate row highlighting, or it may not receive hover background 440 * fill when hovered. 441 * @return the representation of whether this cell has any contents 442 */ 443 public final ReadOnlyBooleanProperty emptyProperty() { return empty.getReadOnlyProperty(); } 444 445 private void setEmpty(boolean value) { empty.set(value); } 446 447 /** 448 * Returns a boolean representing whether the cell is considered to be empty 449 * or not. 450 * @return true if cell is empty, otherwise false 451 */ 452 public final boolean isEmpty() { return empty.get(); } 453 454 455 456 // --- selected 457 private ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper() { 458 @Override protected void invalidated() { 459 pseudoClassStateChanged(PSEUDO_CLASS_SELECTED, get()); 460 } 461 462 @Override 463 public Object getBean() { 464 return Cell.this; 465 } 466 467 @Override 468 public String getName() { 469 return "selected"; 470 } 471 }; 472 473 /** 474 * Indicates whether or not this cell has been selected. For example, a 475 * ListView defines zero or more cells as being the "selected" cells. 476 * @return the representation of whether this cell has been selected 477 */ 478 public final ReadOnlyBooleanProperty selectedProperty() { return selected.getReadOnlyProperty(); } 479 480 void setSelected(boolean value) { selected.set(value); } 481 482 /** 483 * Returns whether this cell is currently selected or not. 484 * @return True if the cell is selected, false otherwise. 485 */ 486 public final boolean isSelected() { return selected.get(); } 487 488 489 490 // --- Editing 491 private ReadOnlyBooleanWrapper editing; 492 493 private void setEditing(boolean value) { 494 editingPropertyImpl().set(value); 495 } 496 497 /** 498 * Represents whether the cell is currently in its editing state or not. 499 * @return true if this cell is currently in its editing state, otherwise 500 * false 501 */ 502 public final boolean isEditing() { 503 return editing == null ? false : editing.get(); 504 } 505 506 /** 507 * Property representing whether this cell is currently in its editing state. 508 * @return the representation of whether this cell is currently in its 509 * editing state 510 */ 511 public final ReadOnlyBooleanProperty editingProperty() { 512 return editingPropertyImpl().getReadOnlyProperty(); 513 } 514 515 private ReadOnlyBooleanWrapper editingPropertyImpl() { 516 if (editing == null) { 517 editing = new ReadOnlyBooleanWrapper(this, "editing"); 518 } 519 return editing; 520 } 521 522 523 524 // --- Editable 525 private BooleanProperty editable; 526 527 /** 528 * Allows for certain cells to not be able to be edited. This is useful in 529 * cases where, say, a List has 'header rows' - it does not make sense for 530 * the header rows to be editable, so they should have editable set to 531 * false. 532 * 533 * @param value A boolean representing whether the cell is editable or not. 534 * If true, the cell is editable, and if it is false, the cell can not 535 * be edited. 536 */ 537 public final void setEditable(boolean value) { 538 editableProperty().set(value); 539 } 540 541 /** 542 * Returns whether this cell is allowed to be put into an editing state. 543 * @return true if this cell is allowed to be put into an editing state, 544 * otherwise false 545 */ 546 public final boolean isEditable() { 547 return editable == null ? true : editable.get(); 548 } 549 550 /** 551 * A property representing whether this cell is allowed to be put into an 552 * editing state. By default editable is set to true in Cells (although for 553 * a subclass of Cell to be allowed to enter its editing state, it may have 554 * to satisfy additional criteria. For example, ListCell requires that the 555 * ListView {@link ListView#editableProperty() editable} property is also 556 * true. 557 * @return the representation of whether this cell is allowed to be put into 558 * an editing state 559 */ 560 public final BooleanProperty editableProperty() { 561 if (editable == null) { 562 editable = new SimpleBooleanProperty(this, "editable", true); 563 } 564 return editable; 565 } 566 567 568 569 /*************************************************************************** 570 * * 571 * Public API * 572 * * 573 **************************************************************************/ 574 575 /** 576 * Call this function to transition from a non-editing state into an editing 577 * state, if the cell is editable. If this cell is already in an editing 578 * state, it will stay in it. 579 */ 580 public void startEdit() { 581 if (isEditable() && !isEditing() && !isEmpty()) { 582 setEditing(true); 583 } 584 } 585 586 /** 587 * Call this function to transition from an editing state into a non-editing 588 * state, without saving any user input. 589 */ 590 public void cancelEdit() { 591 if (isEditing()) { 592 setEditing(false); 593 } 594 } 595 596 /** 597 * Call this function when appropriate (based on the user interaction requirements 598 * of your cell editing user interface) to do two things: 599 * 600 * <ol> 601 * <li>Fire the appropriate events back to the backing UI control (e.g. 602 * {@link ListView}). This will begin the process of pushing this edit 603 * back to the relevant data source / property (although it does not 604 * guarantee that this will be successful - that is dependent upon the 605 * specific edit commit handler being used). Refer to the UI control 606 * class javadoc for more detail.</li> 607 * <li>Begin the transition from an editing state into a non-editing state.</li> 608 * </ol> 609 * 610 * <p>In general there is no need to override this method in custom cell 611 * implementations - it should be sufficient to simply call this method 612 * when appropriate (e.g. when the user pressed the Enter key, you may do something 613 * like {@code cell.commitEdit(converter.fromString(textField.getText()));}</p> 614 * 615 * @param newValue The value as input by the end user, which should be 616 * persisted in the relevant way given the data source underpinning the 617 * user interface and the install edit commit handler of the UI control. 618 */ 619 public void commitEdit(T newValue) { 620 if (isEditing()) { 621 setEditing(false); 622 } 623 } 624 625 /** {@inheritDoc} */ 626 @Override protected void layoutChildren() { 627 if (itemDirty) { 628 updateItem(getItem(), isEmpty()); 629 itemDirty = false; 630 } 631 632 super.layoutChildren(); 633 } 634 635 636 637 /*************************************************************************** 638 * * 639 * Expert API * 640 * * 641 **************************************************************************/ 642 643 /** 644 * The updateItem method should not be called by developers, but it is the 645 * best method for developers to override to allow for them to customise the 646 * visuals of the cell. To clarify, developers should never call this method 647 * in their code (they should leave it up to the UI control, such as the 648 * {@link javafx.scene.control.ListView} control) to call this method. However, 649 * the purpose of having the updateItem method is so that developers, when 650 * specifying custom cell factories (again, like the ListView 651 * {@link javafx.scene.control.ListView#cellFactoryProperty() cell factory}), 652 * the updateItem method can be overridden to allow for complete customisation 653 * of the cell. 654 * 655 * <p>It is <strong>very important</strong> that subclasses 656 * of Cell override the updateItem method properly, as failure to do so will 657 * lead to issues such as blank cells or cells with unexpected content 658 * appearing within them. Here is an example of how to properly override the 659 * updateItem method: 660 * 661 * <pre> 662 * protected void updateItem(T item, boolean empty) { 663 * super.updateItem(item, empty); 664 * 665 * if (empty || item == null) { 666 * setText(null); 667 * setGraphic(null); 668 * } else { 669 * setText(item.toString()); 670 * } 671 * } 672 * </pre> 673 * 674 * <p>Note in this code sample two important points: 675 * <ol> 676 * <li>We call the super.updateItem(T, boolean) method. If this is not 677 * done, the item and empty properties are not correctly set, and you are 678 * likely to end up with graphical issues.</li> 679 * <li>We test for the <code>empty</code> condition, and if true, we 680 * set the text and graphic properties to null. If we do not do this, 681 * it is almost guaranteed that end users will see graphical artifacts 682 * in cells unexpectedly.</li> 683 * </ol> 684 * 685 * @param item The new item for the cell. 686 * @param empty whether or not this cell represents data from the list. If it 687 * is empty, then it does not represent any domain data, but is a cell 688 * being used to render an "empty" row. 689 */ 690 protected void updateItem(T item, boolean empty) { 691 setItem(item); 692 setEmpty(empty); 693 if (empty && isSelected()) { 694 updateSelected(false); 695 } 696 } 697 698 /** 699 * Updates whether this cell is in a selected state or not. 700 * @param selected whether or not to select this cell. 701 */ 702 public void updateSelected(boolean selected) { 703 if (selected && isEmpty()) return; 704 boolean wasSelected = isSelected(); 705 setSelected(selected); 706 707 if (wasSelected != selected) { 708 markCellDirty(); 709 } 710 } 711 712 /** 713 * This method is called by Cell subclasses so that certain CPU-intensive 714 * actions (specifically, calling {@link #updateItem(Object, boolean)}) are 715 * only performed when necessary (that is, they are only performed 716 * when the currently set {@link #itemProperty() item} is considered to be 717 * different than the proposed new item that could be set). 718 * 719 * <p>The default implementation of this method tests against equality, but 720 * developers are able to override this method to perform checks in other ways 721 * that are specific to their domain.</p> 722 * 723 * @param oldItem The currently-set item contained within the cell (i.e. it is 724 * the same as what is available via {@link #getItem()}). 725 * @param newItem The item that will be set in the cell, if this method 726 * returns true. If this method returns false, it may not be 727 * set. 728 * @return Returns true if the new item is considered to be different than 729 * the old item. By default this method tests against equality, but 730 * subclasses may alter the implementation to test appropriate to 731 * their needs. 732 * @since JavaFX 8u40 733 */ 734 protected boolean isItemChanged(T oldItem, T newItem) { 735 return oldItem != null ? !oldItem.equals(newItem) : newItem != null; 736 } 737 738 /** 739 * Developers of custom cell implementations should override this method when 740 * the cell provides editing functionality, with this method returning the 741 * user input after the user has interacted with the editing components. 742 * The form of the returned data (wrapped in an {@link Optional}) should 743 * be the same as the form of the underlying data model, hence the 744 * use of the {@code T} generic type. If no value is available, or if the 745 * value to be returned is invalid, {@code Optional.empty()} should be returned 746 * to indicate that the commit should not proceed.. 747 * 748 * @return The value provided by the user into this cell when it was in its 749 * editing state, in the form of the underlying data model. If the value 750 * is invalid or unable to be determined, {@code Optional.empty()} should 751 * be returned. 752 * @since 10 753 */ 754 protected Optional<T> getEditorValue() { 755 return Optional.empty(); 756 } 757 758 759 760 /*************************************************************************** 761 * * 762 * Private Implementation * 763 * * 764 **************************************************************************/ 765 766 // itemDirty and markCellDirty introduced as a solution for JDK-8145588. 767 // In the fullness of time, a more fully developed solution can be developed 768 // that offers a public API around this lazy-dirty impl. 769 private boolean itemDirty = false; 770 private final void markCellDirty() { 771 itemDirty = true; 772 requestLayout(); 773 } 774 775 void attemptEditCommit() { 776 // The user has shifted focus, so we should cancel the editing on this cell 777 getEditorValue().ifPresentOrElse(this::commitEdit, this::cancelEdit); 778 } 779 780 781 /*************************************************************************** 782 * * 783 * Stylesheet Handling * 784 * * 785 **************************************************************************/ 786 787 private static final String DEFAULT_STYLE_CLASS = "cell"; 788 private static final PseudoClass PSEUDO_CLASS_SELECTED = 789 PseudoClass.getPseudoClass("selected"); 790 private static final PseudoClass PSEUDO_CLASS_EMPTY = 791 PseudoClass.getPseudoClass("empty"); 792 private static final PseudoClass PSEUDO_CLASS_FILLED = 793 PseudoClass.getPseudoClass("filled"); 794 795 /** 796 * Returns the initial focus traversable state of this control, for use 797 * by the JavaFX CSS engine to correctly set its initial value. This method 798 * is overridden as by default UI controls have focus traversable set to true, 799 * but that is not appropriate for this control. 800 * 801 * @since 9 802 */ 803 @Override protected Boolean getInitialFocusTraversable() { 804 return Boolean.FALSE; 805 } 806 }