modules/controls/src/main/java/javafx/scene/control/skin/ChoiceBoxSkin.java

Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization

@@ -21,13 +21,20 @@
  * 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 com.sun.javafx.scene.control.skin;
+package javafx.scene.control.skin;
 
+import com.sun.javafx.scene.control.ContextMenuContent;
+import com.sun.javafx.scene.control.behavior.BehaviorBase;
 import javafx.beans.WeakInvalidationListener;
+import javafx.scene.Node;
+import javafx.scene.control.Accordion;
+import javafx.scene.control.Button;
+import javafx.scene.control.Control;
+import javafx.scene.control.SkinBase;
 import javafx.util.StringConverter;
 import javafx.beans.InvalidationListener;
 import javafx.collections.ListChangeListener;
 import javafx.collections.ObservableList;
 import javafx.geometry.HPos;

@@ -48,28 +55,22 @@
 import com.sun.javafx.scene.control.behavior.ChoiceBoxBehavior;
 import javafx.collections.WeakListChangeListener;
 
 
 /**
- * ChoiceBoxSkin - default implementation
+ * Default skin implementation for the {@link ChoiceBox} control.
+ *
+ * @see ChoiceBox
+ * @since 9
  */
-public class ChoiceBoxSkin<T> extends BehaviorSkinBase<ChoiceBox<T>, ChoiceBoxBehavior<T>> {
+public class ChoiceBoxSkin<T> extends SkinBase<ChoiceBox<T>> {
 
-    public ChoiceBoxSkin(ChoiceBox<T> control) {
-        super(control, new ChoiceBoxBehavior<T>(control));
-        initialize();
-
-        itemsObserver = observable -> updateChoiceBoxItems();
-        control.itemsProperty().addListener(new WeakInvalidationListener(itemsObserver));
-
-        control.requestLayout();
-        registerChangeListener(control.selectionModelProperty(), "SELECTION_MODEL");
-        registerChangeListener(control.showingProperty(), "SHOWING");
-        registerChangeListener(control.itemsProperty(), "ITEMS");
-        registerChangeListener(control.getSelectionModel().selectedItemProperty(), "SELECTION_CHANGED");
-        registerChangeListener(control.converterProperty(), "CONVERTER");
-    }
+    /***************************************************************************
+     *                                                                         *
+     * Private fields                                                          *
+     *                                                                         *
+     **************************************************************************/
 
     private ObservableList<T> choiceBoxItems;
 
     private ContextMenu popup;
 

@@ -84,10 +85,20 @@
      */
     private SelectionModel<T> selectionModel;
 
     private Label label;
 
+    private final BehaviorBase<ChoiceBox<T>> behavior;
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Listeners                                                               *
+     *                                                                         *
+     **************************************************************************/
+
     private final ListChangeListener<T> choiceBoxItemsListener = new ListChangeListener<T>() {
         @Override public void onChanged(Change<? extends T> c) {
             while (c.next()) {
                 if (c.getRemovedSize() > 0 || c.wasPermutated()) {
                     toggleGroup.getToggles().clear();

@@ -112,10 +123,186 @@
     private final WeakListChangeListener<T> weakChoiceBoxItemsListener =
             new WeakListChangeListener<T>(choiceBoxItemsListener);
 
     private final InvalidationListener itemsObserver;
 
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Constructors                                                            *
+     *                                                                         *
+     **************************************************************************/
+
+    /**
+     * Creates a new ChoiceBoxSkin instance, installing the necessary child
+     * nodes into the Control {@link Control#getChildren() children} list, as
+     * well as the necessary input mappings for handling key, mouse, etc events.
+     *
+     * @param control The control that this skin should be installed onto.
+     */
+    public ChoiceBoxSkin(ChoiceBox<T> control) {
+        super(control);
+
+        // install default input map for the ChoiceBox control
+        behavior = new ChoiceBoxBehavior<>(control);
+//        control.setInputMap(behavior.getInputMap());
+
+        initialize();
+
+        itemsObserver = observable -> updateChoiceBoxItems();
+        control.itemsProperty().addListener(new WeakInvalidationListener(itemsObserver));
+
+        control.requestLayout();
+        registerChangeListener(control.selectionModelProperty(), e -> updateSelectionModel());
+        registerChangeListener(control.showingProperty(), e -> {
+            if (getSkinnable().isShowing()) {
+                MenuItem item = null;
+
+                SelectionModel sm = getSkinnable().getSelectionModel();
+                if (sm == null) return;
+
+                long currentSelectedIndex = sm.getSelectedIndex();
+                int itemInControlCount = choiceBoxItems.size();
+                boolean hasSelection = currentSelectedIndex >= 0 && currentSelectedIndex < itemInControlCount;
+                if (hasSelection) {
+                    item = popup.getItems().get((int) currentSelectedIndex);
+                    if (item != null && item instanceof RadioMenuItem) ((RadioMenuItem)item).setSelected(true);
+                } else {
+                    if (itemInControlCount > 0) item = popup.getItems().get(0);
+                }
+
+                // This is a fix for RT-9071. Ideally this won't be necessary in
+                // the long-run, but for now at least this resolves the
+                // positioning
+                // problem of ChoiceBox inside a Cell.
+                getSkinnable().autosize();
+                // -- End of RT-9071 fix
+
+                double y = 0;
+
+                if (popup.getSkin() != null) {
+                    ContextMenuContent cmContent = (ContextMenuContent)popup.getSkin().getNode();
+                    if (cmContent != null && currentSelectedIndex != -1) {
+                        y = -(cmContent.getMenuYOffset((int)currentSelectedIndex));
+                    }
+                }
+
+                popup.show(getSkinnable(), Side.BOTTOM, 2, y);
+            } else {
+                popup.hide();
+            }
+        });
+        registerChangeListener(control.itemsProperty(), e -> {
+            updateChoiceBoxItems();
+            updatePopupItems();
+            updateSelectionModel();
+            updateSelection();
+            if(selectionModel != null && selectionModel.getSelectedIndex() == -1) {
+                label.setText(""); // clear label text when selectedIndex is -1
+            }
+        });
+        registerChangeListener(control.getSelectionModel().selectedItemProperty(), e -> {
+            if (getSkinnable().getSelectionModel() != null) {
+                int index = getSkinnable().getSelectionModel().getSelectedIndex();
+                if (index != -1) {
+                    MenuItem item = popup.getItems().get(index);
+                    if (item instanceof RadioMenuItem) ((RadioMenuItem)item).setSelected(true);
+                }
+            }
+        });
+        registerChangeListener(control.converterProperty(), e -> {
+            updateChoiceBoxItems();
+            updatePopupItems();
+        });
+    }
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Public API                                                              *
+     *                                                                         *
+     **************************************************************************/
+
+    /** {@inheritDoc} */
+    @Override public void dispose() {
+        super.dispose();
+
+        if (behavior != null) {
+            behavior.dispose();
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void layoutChildren(final double x, final double y,
+                                            final double w, final double h) {
+        // open button width/height
+        double obw = openButton.prefWidth(-1);
+
+        ChoiceBox<T> control = getSkinnable();
+        label.resizeRelocate(x, y, w, h);
+        openButton.resize(obw, openButton.prefHeight(-1));
+        positionInArea(openButton, (x+w) - obw,
+                y, obw, h, /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        final double boxWidth = label.minWidth(-1) + openButton.minWidth(-1);
+        final double popupWidth = popup.minWidth(-1);
+        return leftInset + Math.max(boxWidth, popupWidth) + rightInset;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+        final double displayHeight = label.minHeight(-1);
+        final double openButtonHeight = openButton.minHeight(-1);
+        return topInset + Math.max(displayHeight, openButtonHeight) + bottomInset;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        final double boxWidth = label.prefWidth(-1)
+                + openButton.prefWidth(-1);
+        double popupWidth = popup.prefWidth(-1);
+        if (popupWidth <= 0) { // first time: when the popup has not shown yet
+            if (popup.getItems().size() > 0){
+                popupWidth = (new Text(((MenuItem)popup.getItems().get(0)).getText())).prefWidth(-1);
+            }
+        }
+        return (popup.getItems().size() == 0) ? 50 : leftInset + Math.max(boxWidth, popupWidth)
+                + rightInset;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+        final double displayHeight = label.prefHeight(-1);
+        final double openButtonHeight = openButton.prefHeight(-1);
+        return topInset
+                + Math.max(displayHeight, openButtonHeight)
+                + bottomInset;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
+        return getSkinnable().prefHeight(width);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
+        return getSkinnable().prefWidth(height);
+    }
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Private implementation                                                  *
+     *                                                                         *
+     **************************************************************************/
+
     private void initialize() {
         updateChoiceBoxItems();
 
         label = new Label();
         label.setMnemonicParsing(false);  // ChoiceBox doesn't do Mnemonics

@@ -176,74 +363,10 @@
     // Test only purpose    
     String getChoiceBoxSelectedText() {
         return label.getText();
     }
 
-    @SuppressWarnings("rawtypes")
-    @Override protected void handleControlPropertyChanged(String p) {
-        super.handleControlPropertyChanged(p);
-        if ("ITEMS".equals(p)) {
-            updateChoiceBoxItems();
-            updatePopupItems();
-            updateSelectionModel();
-            updateSelection();
-            if(selectionModel != null && selectionModel.getSelectedIndex() == -1) {
-                label.setText(""); // clear label text when selectedIndex is -1
-            }
-        } else if (("SELECTION_MODEL").equals(p)) {
-            updateSelectionModel();
-        } else if ("SELECTION_CHANGED".equals(p)) {
-            if (getSkinnable().getSelectionModel() != null) {
-                int index = getSkinnable().getSelectionModel().getSelectedIndex();
-                if (index != -1) {
-                    MenuItem item = popup.getItems().get(index);
-                    if (item instanceof RadioMenuItem) ((RadioMenuItem)item).setSelected(true);
-                }
-            }
-        } else if ("SHOWING".equals(p)) {
-            if (getSkinnable().isShowing()) {
-                MenuItem item = null;
-
-                SelectionModel sm = getSkinnable().getSelectionModel();
-                if (sm == null) return;
-
-                long currentSelectedIndex = sm.getSelectedIndex();
-                int itemInControlCount = choiceBoxItems.size();
-                boolean hasSelection = currentSelectedIndex >= 0 && currentSelectedIndex < itemInControlCount;
-                if (hasSelection) {
-                    item = popup.getItems().get((int) currentSelectedIndex);
-                    if (item != null && item instanceof RadioMenuItem) ((RadioMenuItem)item).setSelected(true);
-                } else {
-                    if (itemInControlCount > 0) item = popup.getItems().get(0);
-                }
-
-                // This is a fix for RT-9071. Ideally this won't be necessary in
-                // the long-run, but for now at least this resolves the
-                // positioning
-                // problem of ChoiceBox inside a Cell.
-                getSkinnable().autosize();
-                // -- End of RT-9071 fix
-
-                double y = 0;
-
-                if (popup.getSkin() != null) {
-                    ContextMenuContent cmContent = (ContextMenuContent)popup.getSkin().getNode();
-                    if (cmContent != null && currentSelectedIndex != -1) {
-                        y = -(cmContent.getMenuYOffset((int)currentSelectedIndex));
-                    }
-                }
-
-                popup.show(getSkinnable(), Side.BOTTOM, 2, y);
-            } else {
-                popup.hide();
-            }
-        } else if ("CONVERTER".equals(p)) {
-            updateChoiceBoxItems();
-            updatePopupItems();
-        }
-    }
-
     private void addPopupItem(final T o, int i) {
         MenuItem popupItem = null;
         if (o instanceof Separator) {
             // We translate the Separator into a SeparatorMenuItem...
             popupItem = new SeparatorMenuItem();

@@ -311,59 +434,6 @@
                 // update the label
                 label.setText(popup.getItems().get(selectedIndex).getText());
             }
         }
     }
-
-    @Override protected void layoutChildren(final double x, final double y,
-            final double w, final double h) {
-        // open button width/height
-        double obw = openButton.prefWidth(-1);
-
-        ChoiceBox<T> control = getSkinnable();
-        label.resizeRelocate(x, y, w, h);
-        openButton.resize(obw, openButton.prefHeight(-1));
-        positionInArea(openButton, (x+w) - obw,
-                y, obw, h, /*baseline ignored*/0, HPos.CENTER, VPos.CENTER);
-    }
-
-    @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
-        final double boxWidth = label.minWidth(-1) + openButton.minWidth(-1);
-        final double popupWidth = popup.minWidth(-1);
-        return leftInset + Math.max(boxWidth, popupWidth) + rightInset;
-    }
-
-    @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
-        final double displayHeight = label.minHeight(-1);
-        final double openButtonHeight = openButton.minHeight(-1);
-        return topInset + Math.max(displayHeight, openButtonHeight) + bottomInset;
-    }
-
-    @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
-        final double boxWidth = label.prefWidth(-1)
-                + openButton.prefWidth(-1);
-        double popupWidth = popup.prefWidth(-1);
-        if (popupWidth <= 0) { // first time: when the popup has not shown yet
-            if (popup.getItems().size() > 0){
-                popupWidth = (new Text(((MenuItem)popup.getItems().get(0)).getText())).prefWidth(-1);
-            }
-        }
-        return (popup.getItems().size() == 0) ? 50 : leftInset + Math.max(boxWidth, popupWidth)
-                + rightInset;
-    }
-
-    @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
-        final double displayHeight = label.prefHeight(-1);
-        final double openButtonHeight = openButton.prefHeight(-1);
-        return topInset
-                + Math.max(displayHeight, openButtonHeight)
-                + bottomInset;
-    }
-    
-    @Override protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
-        return getSkinnable().prefHeight(width);
-    }
-    
-    @Override protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
-        return getSkinnable().prefWidth(height);
-    }
 }