122 *
123 * public MoneyFormatCell() { }
124 *
125 * @Override protected void updateItem(Number item, boolean empty) {
126 * // calling super here is very important - don't skip this!
127 * super.updateItem(item, empty);
128 *
129 * // format the number as if it were a monetary value using the
130 * // formatting relevant to the current locale. This would format
131 * // 43.68 as "$43.68", and -23.67 as "-$23.67"
132 * setText(item == null ? "" : NumberFormat.getCurrencyInstance().format(item));
133 *
134 * // change the text fill based on whether it is positive (green)
135 * // or negative (red). If the cell is selected, the text will
136 * // always be white (so that it can be read against the blue
137 * // background), and if the value is zero, we'll make it black.
138 * if (item != null) {
139 * double value = item.doubleValue();
140 * setTextFill(isSelected() ? Color.WHITE :
141 * value == 0 ? Color.BLACK :
142 * value < 0 ? Color.RED : Color.GREEN);
143 * }
144 * }
145 * }</pre>
146 *
147 * This class could then be used inside a ListView as such:
148 *
149 * <pre>
150 * ObservableList<Number> money = ...;
151 * final ListView<Number> listView = new ListView<Number>(money);
152 * listView.setCellFactory(new Callback<ListView<Number>, ListCell<Number>>() {
153 * @Override public ListCell<Number> call(ListView<Number> list) {
154 * return new MoneyFormatCell();
155 * }
156 * });</pre>
157 *
158 * In this example an anonymous inner class is created, that simply returns
159 * instances of MoneyFormatCell whenever it is called. The MoneyFormatCell class
160 * extends {@link ListCell}, overriding the
161 * {@link #updateItem(java.lang.Object, boolean) updateItem} method. This method
162 * is called whenever the item in the cell changes, for example when the user
376
377
378
379 /***************************************************************************
380 * *
381 * Properties *
382 * *
383 **************************************************************************/
384
385 // --- item
386 private ObjectProperty<T> item = new SimpleObjectProperty<T>(this, "item");
387
388 /**
389 * The data value associated with this Cell. This value is set by the
390 * virtualized Control when the Cell is created or updated. This represents
391 * the raw data value.
392 *
393 * <p>This value should only be set in subclasses of Cell by the virtualised
394 * user interface controls that know how to properly work with the Cell
395 * class.
396 */
397 public final ObjectProperty<T> itemProperty() { return item; }
398
399 /**
400 * Sets the item to the given value - should not be called directly as the
401 * item is managed by the virtualized control.
402 */
403 public final void setItem(T value) { item.set(value); }
404
405 /**
406 * Returns the data value associated with this Cell.
407 */
408 public final T getItem() { return item.get(); }
409
410
411
412 // --- empty
413 private ReadOnlyBooleanWrapper empty = new ReadOnlyBooleanWrapper(true) {
414 @Override protected void invalidated() {
415 final boolean active = get();
416 pseudoClassStateChanged(PSEUDO_CLASS_EMPTY, active);
417 pseudoClassStateChanged(PSEUDO_CLASS_FILLED, !active);
418 }
419
420 @Override
421 public Object getBean() {
422 return Cell.this;
423 }
424
425 @Override
426 public String getName() {
427 return "empty";
428 }
429 };
430
431 /**
432 * A property used to represent whether the cell has any contents.
433 * If true, then the Cell contains no data and is not associated with any
434 * data item in the virtualized Control.
435 *
436 * <p>When a cell is empty, it can be styled differently via the 'empty'
437 * CSS pseudo class state. For example, it may not receive any
438 * alternate row highlighting, or it may not receive hover background
439 * fill when hovered.
440 */
441 public final ReadOnlyBooleanProperty emptyProperty() { return empty.getReadOnlyProperty(); }
442
443 private void setEmpty(boolean value) { empty.set(value); }
444
445 /**
446 * Returns a boolean representing whether the cell is considered to be empty
447 * or not.
448 */
449 public final boolean isEmpty() { return empty.get(); }
450
451
452
453 // --- selected
454 private ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper() {
455 @Override protected void invalidated() {
456 pseudoClassStateChanged(PSEUDO_CLASS_SELECTED, get());
457 }
458
459 @Override
460 public Object getBean() {
461 return Cell.this;
462 }
463
464 @Override
465 public String getName() {
466 return "selected";
467 }
468 };
469
470 /**
471 * Indicates whether or not this cell has been selected. For example, a
472 * ListView defines zero or more cells as being the "selected" cells.
473 */
474 public final ReadOnlyBooleanProperty selectedProperty() { return selected.getReadOnlyProperty(); }
475
476 void setSelected(boolean value) { selected.set(value); }
477
478 /**
479 * Returns whether this cell is currently selected or not.
480 * @return True if the cell is selected, false otherwise.
481 */
482 public final boolean isSelected() { return selected.get(); }
483
484
485
486 // --- Editing
487 private ReadOnlyBooleanWrapper editing;
488
489 private void setEditing(boolean value) {
490 editingPropertyImpl().set(value);
491 }
492
493 /**
494 * Represents whether the cell is currently in its editing state or not.
495 */
496 public final boolean isEditing() {
497 return editing == null ? false : editing.get();
498 }
499
500 /**
501 * Property representing whether this cell is currently in its editing state.
502 */
503 public final ReadOnlyBooleanProperty editingProperty() {
504 return editingPropertyImpl().getReadOnlyProperty();
505 }
506
507 private ReadOnlyBooleanWrapper editingPropertyImpl() {
508 if (editing == null) {
509 editing = new ReadOnlyBooleanWrapper(this, "editing");
510 }
511 return editing;
512 }
513
514
515
516 // --- Editable
517 private BooleanProperty editable;
518
519 /**
520 * Allows for certain cells to not be able to be edited. This is useful in
521 * cases where, say, a List has 'header rows' - it does not make sense for
522 * the header rows to be editable, so they should have editable set to
523 * false.
524 *
525 * @param value A boolean representing whether the cell is editable or not.
526 * If true, the cell is editable, and if it is false, the cell can not
527 * be edited.
528 */
529 public final void setEditable(boolean value) {
530 editableProperty().set(value);
531 }
532
533 /**
534 * Returns whether this cell is allowed to be put into an editing state.
535 */
536 public final boolean isEditable() {
537 return editable == null ? true : editable.get();
538 }
539
540 /**
541 * A property representing whether this cell is allowed to be put into an
542 * editing state. By default editable is set to true in Cells (although for
543 * a subclass of Cell to be allowed to enter its editing state, it may have
544 * to satisfy additional criteria. For example, ListCell requires that the
545 * ListView {@link ListView#editableProperty() editable} property is also
546 * true.
547 */
548 public final BooleanProperty editableProperty() {
549 if (editable == null) {
550 editable = new SimpleBooleanProperty(this, "editable", true);
551 }
552 return editable;
553 }
554
555
556
557 /***************************************************************************
558 * *
559 * Public API *
560 * *
561 **************************************************************************/
562
563 /**
564 * Call this function to transition from a non-editing state into an editing
565 * state, if the cell is editable. If this cell is already in an editing
566 * state, it will stay in it.
656 * setText(item.toString());
657 * }
658 * }
659 * </pre>
660 *
661 * <p>Note in this code sample two important points:
662 * <ol>
663 * <li>We call the super.updateItem(T, boolean) method. If this is not
664 * done, the item and empty properties are not correctly set, and you are
665 * likely to end up with graphical issues.</li>
666 * <li>We test for the <code>empty</code> condition, and if true, we
667 * set the text and graphic properties to null. If we do not do this,
668 * it is almost guaranteed that end users will see graphical artifacts
669 * in cells unexpectedly.</li>
670 * </ol>
671 *
672 * @param item The new item for the cell.
673 * @param empty whether or not this cell represents data from the list. If it
674 * is empty, then it does not represent any domain data, but is a cell
675 * being used to render an "empty" row.
676 * @expert
677 */
678 protected void updateItem(T item, boolean empty) {
679 setItem(item);
680 setEmpty(empty);
681 if (empty && isSelected()) {
682 updateSelected(false);
683 }
684 }
685
686 /**
687 * Updates whether this cell is in a selected state or not.
688 * @expert
689 * @param selected whether or not to select this cell.
690 */
691 public void updateSelected(boolean selected) {
692 if (selected && isEmpty()) return;
693 boolean wasSelected = isSelected();
694 setSelected(selected);
695
696 if (wasSelected != selected) {
697 markCellDirty();
698 }
699 }
700
701 /**
702 * This method is called by Cell subclasses so that certain CPU-intensive
703 * actions (specifically, calling {@link #updateItem(Object, boolean)}) are
704 * only performed when necessary (that is, they are only performed
705 * when the currently set {@link #itemProperty() item} is considered to be
706 * different than the proposed new item that could be set).
707 *
708 * <p>The default implementation of this method tests against equality, but
|
122 *
123 * public MoneyFormatCell() { }
124 *
125 * @Override protected void updateItem(Number item, boolean empty) {
126 * // calling super here is very important - don't skip this!
127 * super.updateItem(item, empty);
128 *
129 * // format the number as if it were a monetary value using the
130 * // formatting relevant to the current locale. This would format
131 * // 43.68 as "$43.68", and -23.67 as "-$23.67"
132 * setText(item == null ? "" : NumberFormat.getCurrencyInstance().format(item));
133 *
134 * // change the text fill based on whether it is positive (green)
135 * // or negative (red). If the cell is selected, the text will
136 * // always be white (so that it can be read against the blue
137 * // background), and if the value is zero, we'll make it black.
138 * if (item != null) {
139 * double value = item.doubleValue();
140 * setTextFill(isSelected() ? Color.WHITE :
141 * value == 0 ? Color.BLACK :
142 * value < 0 ? Color.RED : Color.GREEN);
143 * }
144 * }
145 * }</pre>
146 *
147 * This class could then be used inside a ListView as such:
148 *
149 * <pre>
150 * ObservableList<Number> money = ...;
151 * final ListView<Number> listView = new ListView<Number>(money);
152 * listView.setCellFactory(new Callback<ListView<Number>, ListCell<Number>>() {
153 * @Override public ListCell<Number> call(ListView<Number> list) {
154 * return new MoneyFormatCell();
155 * }
156 * });</pre>
157 *
158 * In this example an anonymous inner class is created, that simply returns
159 * instances of MoneyFormatCell whenever it is called. The MoneyFormatCell class
160 * extends {@link ListCell}, overriding the
161 * {@link #updateItem(java.lang.Object, boolean) updateItem} method. This method
162 * is called whenever the item in the cell changes, for example when the user
376
377
378
379 /***************************************************************************
380 * *
381 * Properties *
382 * *
383 **************************************************************************/
384
385 // --- item
386 private ObjectProperty<T> item = new SimpleObjectProperty<T>(this, "item");
387
388 /**
389 * The data value associated with this Cell. This value is set by the
390 * virtualized Control when the Cell is created or updated. This represents
391 * the raw data value.
392 *
393 * <p>This value should only be set in subclasses of Cell by the virtualised
394 * user interface controls that know how to properly work with the Cell
395 * class.
396 * @return the data value associated with this cell
397 */
398 public final ObjectProperty<T> itemProperty() { return item; }
399
400 /**
401 * Sets the item to the given value - should not be called directly as the
402 * item is managed by the virtualized control.
403 * @param value the data value to this item
404 */
405 public final void setItem(T value) { item.set(value); }
406
407 /**
408 * Returns the data value associated with this Cell.
409 * @return the data value associated with this cell
410 */
411 public final T getItem() { return item.get(); }
412
413
414
415 // --- empty
416 private ReadOnlyBooleanWrapper empty = new ReadOnlyBooleanWrapper(true) {
417 @Override protected void invalidated() {
418 final boolean active = get();
419 pseudoClassStateChanged(PSEUDO_CLASS_EMPTY, active);
420 pseudoClassStateChanged(PSEUDO_CLASS_FILLED, !active);
421 }
422
423 @Override
424 public Object getBean() {
425 return Cell.this;
426 }
427
428 @Override
429 public String getName() {
430 return "empty";
431 }
432 };
433
434 /**
435 * A property used to represent whether the cell has any contents.
436 * If true, then the Cell contains no data and is not associated with any
437 * data item in the virtualized Control.
438 *
439 * <p>When a cell is empty, it can be styled differently via the 'empty'
440 * CSS pseudo class state. For example, it may not receive any
441 * alternate row highlighting, or it may not receive hover background
442 * fill when hovered.
443 * @return the representation of whether this cell has any contents
444 */
445 public final ReadOnlyBooleanProperty emptyProperty() { return empty.getReadOnlyProperty(); }
446
447 private void setEmpty(boolean value) { empty.set(value); }
448
449 /**
450 * Returns a boolean representing whether the cell is considered to be empty
451 * or not.
452 * @return true if cell is empty, otherwise false
453 */
454 public final boolean isEmpty() { return empty.get(); }
455
456
457
458 // --- selected
459 private ReadOnlyBooleanWrapper selected = new ReadOnlyBooleanWrapper() {
460 @Override protected void invalidated() {
461 pseudoClassStateChanged(PSEUDO_CLASS_SELECTED, get());
462 }
463
464 @Override
465 public Object getBean() {
466 return Cell.this;
467 }
468
469 @Override
470 public String getName() {
471 return "selected";
472 }
473 };
474
475 /**
476 * Indicates whether or not this cell has been selected. For example, a
477 * ListView defines zero or more cells as being the "selected" cells.
478 * @return the representation of whether this cell has been selected
479 */
480 public final ReadOnlyBooleanProperty selectedProperty() { return selected.getReadOnlyProperty(); }
481
482 void setSelected(boolean value) { selected.set(value); }
483
484 /**
485 * Returns whether this cell is currently selected or not.
486 * @return True if the cell is selected, false otherwise.
487 */
488 public final boolean isSelected() { return selected.get(); }
489
490
491
492 // --- Editing
493 private ReadOnlyBooleanWrapper editing;
494
495 private void setEditing(boolean value) {
496 editingPropertyImpl().set(value);
497 }
498
499 /**
500 * Represents whether the cell is currently in its editing state or not.
501 * @return true if this cell is currently in its editing state, otherwise
502 * false
503 */
504 public final boolean isEditing() {
505 return editing == null ? false : editing.get();
506 }
507
508 /**
509 * Property representing whether this cell is currently in its editing state.
510 * @return the representation of whether this cell is currently in its
511 * editing state
512 */
513 public final ReadOnlyBooleanProperty editingProperty() {
514 return editingPropertyImpl().getReadOnlyProperty();
515 }
516
517 private ReadOnlyBooleanWrapper editingPropertyImpl() {
518 if (editing == null) {
519 editing = new ReadOnlyBooleanWrapper(this, "editing");
520 }
521 return editing;
522 }
523
524
525
526 // --- Editable
527 private BooleanProperty editable;
528
529 /**
530 * Allows for certain cells to not be able to be edited. This is useful in
531 * cases where, say, a List has 'header rows' - it does not make sense for
532 * the header rows to be editable, so they should have editable set to
533 * false.
534 *
535 * @param value A boolean representing whether the cell is editable or not.
536 * If true, the cell is editable, and if it is false, the cell can not
537 * be edited.
538 */
539 public final void setEditable(boolean value) {
540 editableProperty().set(value);
541 }
542
543 /**
544 * Returns whether this cell is allowed to be put into an editing state.
545 * @return true if this cell is allowed to be put into an editing state,
546 * otherwise false
547 */
548 public final boolean isEditable() {
549 return editable == null ? true : editable.get();
550 }
551
552 /**
553 * A property representing whether this cell is allowed to be put into an
554 * editing state. By default editable is set to true in Cells (although for
555 * a subclass of Cell to be allowed to enter its editing state, it may have
556 * to satisfy additional criteria. For example, ListCell requires that the
557 * ListView {@link ListView#editableProperty() editable} property is also
558 * true.
559 * @return the representation of whether this cell is allowed to be put into
560 * an editing state
561 */
562 public final BooleanProperty editableProperty() {
563 if (editable == null) {
564 editable = new SimpleBooleanProperty(this, "editable", true);
565 }
566 return editable;
567 }
568
569
570
571 /***************************************************************************
572 * *
573 * Public API *
574 * *
575 **************************************************************************/
576
577 /**
578 * Call this function to transition from a non-editing state into an editing
579 * state, if the cell is editable. If this cell is already in an editing
580 * state, it will stay in it.
670 * setText(item.toString());
671 * }
672 * }
673 * </pre>
674 *
675 * <p>Note in this code sample two important points:
676 * <ol>
677 * <li>We call the super.updateItem(T, boolean) method. If this is not
678 * done, the item and empty properties are not correctly set, and you are
679 * likely to end up with graphical issues.</li>
680 * <li>We test for the <code>empty</code> condition, and if true, we
681 * set the text and graphic properties to null. If we do not do this,
682 * it is almost guaranteed that end users will see graphical artifacts
683 * in cells unexpectedly.</li>
684 * </ol>
685 *
686 * @param item The new item for the cell.
687 * @param empty whether or not this cell represents data from the list. If it
688 * is empty, then it does not represent any domain data, but is a cell
689 * being used to render an "empty" row.
690 */
691 protected void updateItem(T item, boolean empty) {
692 setItem(item);
693 setEmpty(empty);
694 if (empty && isSelected()) {
695 updateSelected(false);
696 }
697 }
698
699 /**
700 * Updates whether this cell is in a selected state or not.
701 * @param selected whether or not to select this cell.
702 */
703 public void updateSelected(boolean selected) {
704 if (selected && isEmpty()) return;
705 boolean wasSelected = isSelected();
706 setSelected(selected);
707
708 if (wasSelected != selected) {
709 markCellDirty();
710 }
711 }
712
713 /**
714 * This method is called by Cell subclasses so that certain CPU-intensive
715 * actions (specifically, calling {@link #updateItem(Object, boolean)}) are
716 * only performed when necessary (that is, they are only performed
717 * when the currently set {@link #itemProperty() item} is considered to be
718 * different than the proposed new item that could be set).
719 *
720 * <p>The default implementation of this method tests against equality, but
|