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 }