modules/controls/src/main/java/com/sun/javafx/scene/control/behavior/MenuButtonBehaviorBase.java

Print this page
rev 9240 : 8076423: JEP 253: Prepare JavaFX UI Controls & CSS APIs for Modularization
   1 /*
   2  * Copyright (c) 2010, 2014, 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 com.sun.javafx.scene.control.behavior;
  27 
  28 import static javafx.scene.input.KeyCode.UP;
  29 import static javafx.scene.input.KeyCode.DOWN;
  30 import static javafx.scene.input.KeyCode.LEFT;
  31 import static javafx.scene.input.KeyCode.RIGHT;
  32 import static javafx.scene.input.KeyCode.CANCEL;
  33 import static javafx.scene.input.KeyCode.ESCAPE;
  34 import static javafx.scene.input.KeyEvent.KEY_PRESSED;
  35 
  36 import java.util.ArrayList;
  37 import java.util.List;
  38 
  39 import javafx.geometry.NodeOrientation;
  40 import javafx.geometry.Side;
  41 import javafx.scene.control.MenuButton;


  42 import javafx.scene.input.MouseButton;
  43 import javafx.scene.input.MouseEvent;
  44 



  45 /**
  46  * The base behavior for a MenuButton.
  47  */
  48 public abstract class MenuButtonBehaviorBase<C extends MenuButton> extends ButtonBehavior<C> {
  49 
  50     /***************************************************************************
  51      *                                                                         *
  52      * Constructors                                                            *
  53      *                                                                         *
  54      **************************************************************************/
  55 
  56     public MenuButtonBehaviorBase(final C menuButton, List<KeyBinding> bindings) {
  57         super(menuButton, bindings);
  58     }
  59 
  60     /***************************************************************************
  61      *                                                                         *
  62      * Key event handling                                                      *
  63      *                                                                         *
  64      **************************************************************************/
  65 
  66     /**
  67      * Opens the popup menu.
  68      */
  69     protected static final String OPEN_ACTION = "Open";
  70 
  71     /**
  72      * Closes the popup menu.
  73      */
  74     protected static final String CLOSE_ACTION = "Close";



  75 
  76     /**
  77      * The base key bindings for a MenuButton. These basically just define the
  78      * bindings to close an open menu. Subclasses will tell you what can be done
  79      * to open it.
  80      */
  81     protected static final List<KeyBinding> BASE_MENU_BUTTON_BINDINGS = new ArrayList<KeyBinding>();
  82     static {
  83         BASE_MENU_BUTTON_BINDINGS.add(new KeyBinding(UP, "TraverseUp"));
  84         BASE_MENU_BUTTON_BINDINGS.add(new KeyBinding(DOWN, "TraverseDown"));
  85         BASE_MENU_BUTTON_BINDINGS.add(new KeyBinding(LEFT, "TraverseLeft"));
  86         BASE_MENU_BUTTON_BINDINGS.add(new KeyBinding(RIGHT, "TraverseRight"));
  87         BASE_MENU_BUTTON_BINDINGS.add(new KeyBinding(ESCAPE, KEY_PRESSED, CLOSE_ACTION));
  88         BASE_MENU_BUTTON_BINDINGS.add(new KeyBinding(CANCEL, KEY_PRESSED, CLOSE_ACTION));
  89     }
  90 
  91     /**
  92      * Invokes the given named action.
  93      *
  94      * @param name the name of the action to invoke
  95      */
  96     @Override protected void callAction(String name) {
  97         MenuButton button = getControl();
  98         Side popupSide = button.getPopupSide();
  99 
 100         if (CLOSE_ACTION.equals(name)) {
 101             button.hide();
 102         } else if (OPEN_ACTION.equals(name)) {
 103             if (button.isShowing()) {
 104                 button.hide();
 105             } else {
 106                 button.show();
 107             }
 108         } else if (!button.isShowing() &&
 109                    ("TraverseUp".equals(name)    && popupSide == Side.TOP)    ||
 110                    ("TraverseDown".equals(name)  && (popupSide == Side.BOTTOM || popupSide == Side.TOP))  ||
 111                    ("TraverseLeft".equals(name)  && (popupSide == Side.RIGHT  || popupSide == Side.LEFT)) ||
 112                    ("TraverseRight".equals(name) && (popupSide == Side.RIGHT  || popupSide == Side.LEFT))) {











 113             // Show the menu when arrow key matches the popupSide
 114             // direction -- but also allow RIGHT key for LEFT position and
 115             // DOWN key for TOP position. To be symmetrical, we also allow for
 116             // the LEFT key to work when in the RIGHT position. This is needed
 117             // because the skin only paints right- and down-facing arrows in
 118             // these cases.
 119             button.show();






 120         } else {
 121             super.callAction(name);
 122         }
 123     }
 124 
 125     /***************************************************************************
 126      *                                                                         *
 127      * Mouse event handling                                                    *
 128      *                                                                         *
 129      **************************************************************************/
 130 
 131     /**
 132      * When a mouse button is pressed, we either want to behave like a button or
 133      * show the popup.  This will be called by the skin.
 134      *
 135      * @param e the mouse press event
 136      * @param behaveLikeButton if true, this should act just like a button
 137      */
 138     public void mousePressed(MouseEvent e, boolean behaveLikeButton) {
 139         final C control = getControl();
 140 
 141         /*
 142          * Behaving like a button is easy - we just call super. But, we cannot
 143          * call super if all we want to do is show the popup. The reason for
 144          * this is that super also handles all the arm/disarm/fire logic, and
 145          * this can inadvertently cause actions to fire when we don't want them
 146          * to fire. So, we unfortunately need to duplicate the focus
 147          * handling code here.
 148          */
 149         if (behaveLikeButton) {
 150             if (control.isShowing()) {
 151                 control.hide();
 152             }
 153             super.mousePressed(e);
 154         } else {
 155             if (!control.isFocused() && control.isFocusTraversable()) {
 156                 control.requestFocus();
 157             }
 158             if (control.isShowing()) {
 159                 control.hide();
 160             } else {
 161                 if (e.getButton() == MouseButton.PRIMARY) {
 162                     control.show();    
 163                 }
 164             }
 165         }
 166     }
 167 
 168     @Override public void mouseReleased(MouseEvent e) {
 169         // Overriding to not call fire() on mouseReleased.
 170         // The event is handled by the skin instead, which calls
 171         // the method below.
 172     }
 173 
 174     /**
 175      * Handles mouse release events.  This will be called by the skin.
 176      *
 177      * @param e the mouse press event
 178      * @param behaveLikeButton if true, this should act just like a button
 179      */
 180     public void mouseReleased(MouseEvent e, boolean behaveLikeButton) {
 181         if (behaveLikeButton) {
 182             super.mouseReleased(e);
 183         } else {
 184             if (getControl().isShowing() && !getControl().contains(e.getX(), e.getY())) {
 185                 getControl().hide();
 186             }
 187             getControl().disarm();
 188         }
 189     }
 190 }
   1 /*
   2  * Copyright (c) 2010, 2015, 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 com.sun.javafx.scene.control.behavior;
  27 












  28 import javafx.geometry.Side;
  29 import javafx.scene.control.MenuButton;
  30 import com.sun.javafx.scene.control.inputmap.InputMap;
  31 import javafx.scene.input.KeyEvent;
  32 import javafx.scene.input.MouseButton;
  33 import javafx.scene.input.MouseEvent;
  34 
  35 import static com.sun.javafx.scene.control.inputmap.InputMap.KeyMapping;
  36 import static javafx.scene.input.KeyCode.*;
  37 
  38 /**
  39  * The base behavior for a MenuButton.
  40  */
  41 public abstract class MenuButtonBehaviorBase<C extends MenuButton> extends ButtonBehavior<C> {
  42 
  43     private final InputMap<C> buttonInputMap;








  44 
  45     /***************************************************************************
  46      *                                                                         *
  47      * Constructors                                                            *
  48      *                                                                         *
  49      **************************************************************************/
  50 
  51     public MenuButtonBehaviorBase(final C menuButton) {
  52         super(menuButton);


  53 
  54         // pull down the parent input map, no need to add focus traversal
  55         // mappings - added in ButtonBehavior.
  56         buttonInputMap = super.getInputMap();
  57 
  58         // We want to remove the maping for MOUSE_RELEASED, as the event is
  59         // handled by the skin instead, which calls the mouseReleased method below.
  60         removeMapping(MouseEvent.MOUSE_RELEASED);
  61 
  62         /**
  63          * The base key bindings for a MenuButton. These basically just define the
  64          * bindings to close an open menu. Subclasses will tell you what can be done
  65          * to open it.
  66          */
  67         addDefaultMapping(new KeyMapping(ESCAPE, e -> getNode().hide()));
  68         addDefaultMapping(new KeyMapping(CANCEL, e -> getNode().hide()));







  69 
  70         // we create a child input map, as we want to override some of the
  71         // focus traversal behaviors (and child maps take precedence over parent maps)
  72         InputMap<C> customFocusInputMap = new InputMap<>(menuButton);
  73         addDefaultMapping(customFocusInputMap, new KeyMapping(UP, this::overrideTraversalInput));
  74         addDefaultMapping(customFocusInputMap, new KeyMapping(DOWN, this::overrideTraversalInput));
  75         addDefaultMapping(customFocusInputMap, new KeyMapping(LEFT, this::overrideTraversalInput));
  76         addDefaultMapping(customFocusInputMap, new KeyMapping(RIGHT, this::overrideTraversalInput));
  77         addDefaultChildMap(buttonInputMap, customFocusInputMap);








  78     }
  79 
  80 
  81     /***************************************************************************
  82      *                                                                         *
  83      * Key event handling                                                      *
  84      *                                                                         *
  85      **************************************************************************/
  86 
  87     private void overrideTraversalInput(KeyEvent event) {
  88         final MenuButton button = getNode();
  89         final Side popupSide = button.getPopupSide();
  90         if (!button.isShowing() &&
  91                 (event.getCode() == UP    && popupSide == Side.TOP)    ||
  92                 (event.getCode() == DOWN  && (popupSide == Side.BOTTOM || popupSide == Side.TOP))  ||
  93                 (event.getCode() == LEFT  && (popupSide == Side.RIGHT  || popupSide == Side.LEFT)) ||
  94                 (event.getCode() == RIGHT && (popupSide == Side.RIGHT  || popupSide == Side.LEFT))) {
  95             // Show the menu when arrow key matches the popupSide
  96             // direction -- but also allow RIGHT key for LEFT position and
  97             // DOWN key for TOP position. To be symmetrical, we also allow for
  98             // the LEFT key to work when in the RIGHT position. This is needed
  99             // because the skin only paints right- and down-facing arrows in
 100             // these cases.
 101             button.show();
 102         }
 103     }
 104 
 105     protected void openAction() {
 106         if (getNode().isShowing()) {
 107             getNode().hide();
 108         } else {
 109             getNode().show();
 110         }
 111     }
 112 
 113     /***************************************************************************
 114      *                                                                         *
 115      * Mouse event handling                                                    *
 116      *                                                                         *
 117      **************************************************************************/
 118 
 119     /**
 120      * When a mouse button is pressed, we either want to behave like a button or
 121      * show the popup.  This will be called by the skin.
 122      *
 123      * @param e the mouse press event
 124      * @param behaveLikeButton if true, this should act just like a button
 125      */
 126     public void mousePressed(MouseEvent e, boolean behaveLikeButton) {
 127         final C control = getNode();
 128 
 129         /*
 130          * Behaving like a button is easy - we just call super. But, we cannot
 131          * call super if all we want to do is show the popup. The reason for
 132          * this is that super also handles all the arm/disarm/fire logic, and
 133          * this can inadvertently cause actions to fire when we don't want them
 134          * to fire. So, we unfortunately need to duplicate the focus
 135          * handling code here.
 136          */
 137         if (behaveLikeButton) {
 138             if (control.isShowing()) {
 139                 control.hide();
 140             }
 141             super.mousePressed(e);
 142         } else {
 143             if (!control.isFocused() && control.isFocusTraversable()) {
 144                 control.requestFocus();
 145             }
 146             if (control.isShowing()) {
 147                 control.hide();
 148             } else {
 149                 if (e.getButton() == MouseButton.PRIMARY) {
 150                     control.show();    
 151                 }
 152             }
 153         }
 154     }
 155 






 156     /**
 157      * Handles mouse release events.  This will be called by the skin.
 158      *
 159      * @param e the mouse press event
 160      * @param behaveLikeButton if true, this should act just like a button
 161      */
 162     public void mouseReleased(MouseEvent e, boolean behaveLikeButton) {
 163         if (behaveLikeButton) {
 164             super.mouseReleased(e);
 165         } else {
 166             if (getNode().isShowing() && !getNode().contains(e.getX(), e.getY())) {
 167                 getNode().hide();
 168             }
 169             getNode().disarm();
 170         }
 171     }
 172 }