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 /**
46 * The Cell API is used for virtualized controls such as {@link ListView},
47 * {@link TreeView}, and {@link TableView}.
48 * A Cell is a {@link Labeled} {@link Control}, and is used to render a single
49 * "row" inside a ListView, TreeView or TableView. Cells are also used for each
50 * individual 'cell' inside a TableView (i.e. each row/column intersection). See
51 * the JavaDoc for each control separately for more detail.
52 * <p>
53 * Every Cell is associated with a single data item (represented by the
54 * {@link #itemProperty() item} property). The Cell is responsible for
55 * rendering that item and, where appropriate, for editing the item. An item
56 * within a Cell may be represented by text or some other control such as a
57 * {@link CheckBox}, {@link ChoiceBox} or any other {@link Node} such as a
58 * {@link HBox}, {@link GridPane}, or even a {@link Rectangle}.
59 * <p>
60 * Because TreeView, ListView, TableView and other such controls can potentially
61 * be used for displaying incredibly large amounts of data, it is not feasible
62 * to create an actual Cell for every single item in the control.
63 * We represent extremely large data sets using only very few Cells. Each Cell
64 * is "recycled", or reused. This is what we mean when we say that these controls
342 * *
343 **************************************************************************/
344
345 /**
346 * Creates a default Cell with the default style class of 'cell'.
347 */
348 public Cell() {
349 setText(null); // default to null text, to match the null item
350 // focusTraversable is styleable through css. Calling setFocusTraversable
351 // makes it look to css like the user set the value and css will not
352 // override. Initializing focusTraversable by calling set on the
353 // CssMetaData ensures that css will be able to override the value.
354 ((StyleableProperty<Boolean>)(WritableValue<Boolean>)focusTraversableProperty()).applyStyle(null, Boolean.FALSE);
355 getStyleClass().addAll(DEFAULT_STYLE_CLASS);
356
357 /**
358 * Indicates whether or not this cell has focus. For example, a
359 * ListView defines zero or one cell as being the "focused" cell. This cell
360 * would have focused set to true.
361 */
362 super.focusedProperty().addListener(new InvalidationListener() {
363 @Override public void invalidated(Observable property) {
364 pseudoClassStateChanged(PSEUDO_CLASS_FOCUSED, isFocused()); // TODO is this necessary??
365
366 // The user has shifted focus, so we should cancel the editing on this cell
367 if (!isFocused() && isEditing()) {
368 cancelEdit();
369 }
370 }
371 });
372
373 // initialize default pseudo-class state
374 pseudoClassStateChanged(PSEUDO_CLASS_EMPTY, true);
375 }
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
613 * implementations - it should be sufficient to simply call this method
614 * when appropriate (e.g. when the user pressed the Enter key, you may do something
615 * like {@code cell.commitEdit(converter.fromString(textField.getText()));}</p>
616 *
617 * @param newValue The value as input by the end user, which should be
618 * persisted in the relevant way given the data source underpinning the
619 * user interface and the install edit commit handler of the UI control.
620 */
621 public void commitEdit(T newValue) {
622 if (isEditing()) {
623 setEditing(false);
624 }
625 }
626
627 /** {@inheritDoc} */
628 @Override protected void layoutChildren() {
629 if (itemDirty) {
630 updateItem(getItem(), isEmpty());
631 itemDirty = false;
632 }
633 super.layoutChildren();
634 }
635
636
637
638 /***************************************************************************
639 * *
640 * Expert API *
641 * *
642 **************************************************************************/
643
644 /**
645 * The updateItem method should not be called by developers, but it is the
646 * best method for developers to override to allow for them to customise the
647 * visuals of the cell. To clarify, developers should never call this method
648 * in their code (they should leave it up to the UI control, such as the
649 * {@link javafx.scene.control.ListView} control) to call this method. However,
650 * the purpose of having the updateItem method is so that developers, when
651 * specifying custom cell factories (again, like the ListView
652 * {@link javafx.scene.control.ListView#cellFactoryProperty() cell factory}),
719 *
720 * <p>The default implementation of this method tests against equality, but
721 * developers are able to override this method to perform checks in other ways
722 * that are specific to their domain.</p>
723 *
724 * @param oldItem The currently-set item contained within the cell (i.e. it is
725 * the same as what is available via {@link #getItem()}).
726 * @param newItem The item that will be set in the cell, if this method
727 * returns true. If this method returns false, it may not be
728 * set.
729 * @return Returns true if the new item is considered to be different than
730 * the old item. By default this method tests against equality, but
731 * subclasses may alter the implementation to test appropriate to
732 * their needs.
733 * @since JavaFX 8u40
734 */
735 protected boolean isItemChanged(T oldItem, T newItem) {
736 return oldItem != null ? !oldItem.equals(newItem) : newItem != null;
737 }
738
739
740
741 /***************************************************************************
742 * *
743 * Private Implementation *
744 * *
745 **************************************************************************/
746
747 // itemDirty and markCellDirty introduced as a solution for JDK-8145588.
748 // In the fullness of time, a more fully developed solution can be developed
749 // that offers a public API around this lazy-dirty impl.
750 private boolean itemDirty = false;
751 private final void markCellDirty() {
752 itemDirty = true;
753 requestLayout();
754 }
755
756
757 /***************************************************************************
758 * *
759 * Stylesheet Handling *
760 * *
761 **************************************************************************/
762
763 private static final String DEFAULT_STYLE_CLASS = "cell";
764 private static final PseudoClass PSEUDO_CLASS_SELECTED =
765 PseudoClass.getPseudoClass("selected");
766 private static final PseudoClass PSEUDO_CLASS_FOCUSED =
767 PseudoClass.getPseudoClass("focused");
768 private static final PseudoClass PSEUDO_CLASS_EMPTY =
769 PseudoClass.getPseudoClass("empty");
770 private static final PseudoClass PSEUDO_CLASS_FILLED =
771 PseudoClass.getPseudoClass("filled");
772
773 /**
774 * Returns the initial focus traversable state of this control, for use
775 * by the JavaFX CSS engine to correctly set its initial value. This method
776 * is overridden as by default UI controls have focus traversable set to true,
777 * but that is not appropriate for this control.
778 *
779 * @since 9
780 */
781 @Override protected Boolean getInitialFocusTraversable() {
782 return Boolean.FALSE;
783 }
784 }
|
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
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
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}),
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 }
|