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

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

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -21,68 +21,83 @@
  * 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.ControlAcceleratorSupport;
+import com.sun.javafx.scene.control.LabeledImpl;
+import com.sun.javafx.scene.control.skin.Utils;
 import javafx.collections.ListChangeListener;
 import javafx.event.ActionEvent;
 import javafx.scene.control.ContextMenu;
 import javafx.scene.control.MenuButton;
 import javafx.scene.control.MenuItem;
+import javafx.scene.control.SkinBase;
 import javafx.scene.input.MouseEvent;
 import javafx.scene.layout.Region;
 import javafx.scene.layout.StackPane;
 import com.sun.javafx.scene.control.behavior.MenuButtonBehaviorBase;
 
 /**
  * Base class for MenuButtonSkin and SplitMenuButtonSkin. It consists of the
  * label, the arrowButton with its arrow shape, and the popup.
  */
-public abstract class MenuButtonSkinBase<C extends MenuButton, B extends MenuButtonBehaviorBase<C>> extends BehaviorSkinBase<C, B> {
+public class MenuButtonSkinBase<C extends MenuButton> extends SkinBase<C> {
 
     /***************************************************************************
      *                                                                         *
-     * UI Subcomponents                                                        *
+     * Private fields                                                          *
      *                                                                         *
      **************************************************************************/
 
-    protected final LabeledImpl label;
-    protected final StackPane arrow;
-    protected final StackPane arrowButton;
-    protected ContextMenu popup;
+    final LabeledImpl label;
+    final StackPane arrow;
+    final StackPane arrowButton;
+    ContextMenu popup;
 
     /**
      * If true, the control should behave like a button for mouse button events.
      */
-    protected boolean behaveLikeButton = false;
+    boolean behaveLikeButton = false;
     private ListChangeListener<MenuItem> itemsChangedListener;
 
 
 
-
-
     /***************************************************************************
      *                                                                         *
      * Constructors                                                            *
      *                                                                         *
      **************************************************************************/
 
-    public MenuButtonSkinBase(final C control, final B behavior) {
-        super(control, behavior);
+    /**
+     * Creates a new instance of MenuButtonSkinBase, although note that this
+     * instance does not handle any behavior / input mappings - this needs to be
+     * handled appropriately by subclasses.
+     *
+     * @param control The control that this skin should be installed onto.
+     */
+    public MenuButtonSkinBase(final C control) {
+        super(control);
 
         if (control.getOnMousePressed() == null) {
             control.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> {
-                getBehavior().mousePressed(e, behaveLikeButton);
+                MenuButtonBehaviorBase behavior = getBehavior();
+                if (behavior != null) {
+                    behavior.mousePressed(e, behaveLikeButton);
+                }
             });
         }
 
         if (control.getOnMouseReleased() == null) {
             control.addEventHandler(MouseEvent.MOUSE_RELEASED, e -> {
-                getBehavior().mouseReleased(e, behaveLikeButton);
+                MenuButtonBehaviorBase behavior = getBehavior();
+                if (behavior != null) {
+                    behavior.mouseReleased(e, behaveLikeButton);
+                }
             });
         }
 
         /*
          * Create the objects we will be displaying.

@@ -138,79 +153,31 @@
 //
 //                }
 //            });
 
         // Register listeners
-        registerChangeListener(control.showingProperty(), "SHOWING");
-        registerChangeListener(control.focusedProperty(), "FOCUSED");
-        registerChangeListener(control.mnemonicParsingProperty(), "MNEMONIC_PARSING");
-        registerChangeListener(popup.showingProperty(), "POPUP_VISIBLE");
-    }
-
-     /** {@inheritDoc} */
-    @Override public void dispose() { 
-        getSkinnable().getItems().removeListener(itemsChangedListener);
-        super.dispose();
-        if (popup != null ) {
-            if (popup.getSkin() != null && popup.getSkin().getNode() != null) {
-                ContextMenuContent cmContent = (ContextMenuContent)popup.getSkin().getNode();
-                cmContent.dispose();
-                cmContent = null;
-            }
-            popup.setSkin(null);
-            popup = null;
-        }
-    }
-    /***************************************************************************
-     *                                                                         *
-     * Control change handlers                                                 *
-     *                                                                         *
-     **************************************************************************/
-
-    private void show() {
-        if (!popup.isShowing()) {
-            popup.show(getSkinnable(), getSkinnable().getPopupSide(), 0, 0);
-            
-//            if (getSkinnable().isOpenVertically()) {
-//                // FIXME ugly hack - need to work out why we need '12' for
-//                // MenuButton/SplitMenuButton, but not for Menus
-//                double indent = getSkinnable().getStyleClass().contains("menu") ? 0 : 12;
-//                popup.show(getSkinnable(), Side.BOTTOM, indent, 0);
-//            } else {
-//                popup.show(getSkinnable(), Side.RIGHT, 0, 12);
-//            }
-        }
-    }
-
-    private void hide() {
-        if (popup.isShowing()) {
-            popup.hide();
-//            popup.getAnchor().requestFocus();
-        }
-    }
-
-    /**
-     * Handles changes to properties of the MenuButton.
-     */
-    @Override protected void handleControlPropertyChanged(String p) {
-        super.handleControlPropertyChanged(p);
-
-        if ("SHOWING".equals(p)) {
+        registerChangeListener(control.showingProperty(), e -> {
             if (getSkinnable().isShowing()) {
                 show();
             } else {
                 hide();
             }
-        } else if ("FOCUSED".equals(p)) {
+        });
+        registerChangeListener(control.focusedProperty(), e -> {
            // Handle tabbing away from an open MenuButton
            if (!getSkinnable().isFocused() && getSkinnable().isShowing()) {
                hide();
            }
            if (!getSkinnable().isFocused() && popup.isShowing()) {
                hide();
            }
-        } else if ("POPUP_VISIBLE".equals(p)) {
+        });
+        registerChangeListener(control.mnemonicParsingProperty(), e -> {
+            label.setMnemonicParsing(getSkinnable().isMnemonicParsing());
+            getSkinnable().requestLayout();
+        });
+        registerChangeListener(popup.showingProperty(), e -> {
             if (!popup.isShowing() && getSkinnable().isShowing()) {
                 // Popup was dismissed. Maybe user clicked outside or typed ESCAPE.
                 // Make sure button is in sync.
                 getSkinnable().hide();
             }

@@ -219,66 +186,126 @@
                 Utils.addMnemonics(popup, getSkinnable().getScene(), getSkinnable().impl_isShowMnemonics());
             }
             else {
                 Utils.removeMnemonics(popup, getSkinnable().getScene());
             }
+        });
+    }
 
 
-        } else if ("MNEMONIC_PARSING".equals(p)) {
-            label.setMnemonicParsing(getSkinnable().isMnemonicParsing());
-            getSkinnable().requestLayout();
-        }
-    }
 
     /***************************************************************************
      *                                                                         *
-     * Layout                                                                  *
+     * Private implementation                                                  *
      *                                                                         *
      **************************************************************************/
     
+    /** {@inheritDoc} */
+    @Override public void dispose() {
+        getSkinnable().getItems().removeListener(itemsChangedListener);
+        super.dispose();
+        if (popup != null ) {
+            if (popup.getSkin() != null && popup.getSkin().getNode() != null) {
+                ContextMenuContent cmContent = (ContextMenuContent)popup.getSkin().getNode();
+                cmContent.dispose();
+            }
+            popup.setSkin(null);
+            popup = null;
+        }
+    }
+
+    /** {@inheritDoc} */
     @Override protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
         return leftInset
                 + label.minWidth(height)
                 + snapSize(arrowButton.minWidth(height))
                 + rightInset;
     }
 
+    /** {@inheritDoc} */
     @Override protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
         return topInset
                 + Math.max(label.minHeight(width), snapSize(arrowButton.minHeight(-1)))
                 + bottomInset;
     }
 
+    /** {@inheritDoc} */
     @Override protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
         return leftInset
                 + label.prefWidth(height)
                 + snapSize(arrowButton.prefWidth(height))
                 + rightInset;
     }
 
+    /** {@inheritDoc} */
     @Override protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
         return topInset
                 + Math.max(label.prefHeight(width), snapSize(arrowButton.prefHeight(-1)))
                 + bottomInset;
     }
 
+    /** {@inheritDoc} */
     @Override protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {
         return getSkinnable().prefWidth(height);
     }
 
+    /** {@inheritDoc} */
     @Override protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {
         return getSkinnable().prefHeight(width);
     }
 
+    /** {@inheritDoc} */
     @Override protected void layoutChildren(final double x, final double y,
                                             final double w, final double h) {
         final double arrowButtonWidth = snapSize(arrowButton.prefWidth(-1));
         label.resizeRelocate(x, y, w - arrowButtonWidth, h);
-        arrowButton.resizeRelocate(x+(w-arrowButtonWidth), y, arrowButtonWidth, h);
+        arrowButton.resizeRelocate(x + (w - arrowButtonWidth), y, arrowButtonWidth, h);
     }
 
-    private class MenuLabeledImpl extends LabeledImpl {
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Private implementation                                                  *
+     *                                                                         *
+     **************************************************************************/
+
+    MenuButtonBehaviorBase<C> getBehavior() {
+        return null;
+    }
+
+    private void show() {
+        if (!popup.isShowing()) {
+            popup.show(getSkinnable(), getSkinnable().getPopupSide(), 0, 0);
+            
+//            if (getSkinnable().isOpenVertically()) {
+//                // FIXME ugly hack - need to work out why we need '12' for
+//                // MenuButton/SplitMenuButton, but not for Menus
+//                double indent = getSkinnable().getStyleClass().contains("menu") ? 0 : 12;
+//                popup.show(getSkinnable(), Side.BOTTOM, indent, 0);
+//            } else {
+//                popup.show(getSkinnable(), Side.RIGHT, 0, 12);
+//            }
+        }
+    }
+
+    private void hide() {
+        if (popup.isShowing()) {
+            popup.hide();
+//            popup.getAnchor().requestFocus();
+        }
+    }
+
+
+
+    /***************************************************************************
+     *                                                                         *
+     * Support classes                                                         *
+     *                                                                         *
+     **************************************************************************/
+
+    private static class MenuLabeledImpl extends LabeledImpl {
         MenuButton button;
         public MenuLabeledImpl(MenuButton b) {
             super(b);
             button = b;
             addEventHandler(ActionEvent.ACTION, e -> {