1 /* 2 * Copyright (c) 2012, 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 javafx.scene.Node; 29 import javafx.scene.control.PopupControl; 30 31 import javafx.scene.Scene; 32 import javafx.scene.input.KeyEvent; 33 34 import javafx.beans.value.ChangeListener; 35 import javafx.event.Event; 36 import javafx.event.EventDispatcher; 37 import javafx.event.EventHandler; 38 import javafx.scene.input.MouseEvent; 39 40 41 public class TwoLevelFocusPopupBehavior extends TwoLevelFocusBehavior { 42 43 public TwoLevelFocusPopupBehavior(PopupControl popup) { 44 45 tlPopup = popup; 46 47 setExternalFocus(false); // popups go straight to internal focus 48 49 tlPopup.addEventHandler(KeyEvent.ANY, keyEventListener); 50 tlPopup.addEventHandler(MouseEvent.MOUSE_PRESSED, mouseEventListener); 51 tlPopup.focusedProperty().addListener(focusListener); 52 53 // block ScrollEvent from being passed down to scrollbar's skin 54 origEventDispatcher = tlPopup.getEventDispatcher(); 55 56 tlPopup.setEventDispatcher(tlfEventDispatcher); 57 } 58 59 60 public TwoLevelFocusPopupBehavior(Node node) { 61 62 tlNode = node; 63 64 setExternalFocus(false); // popups go straight to internal focus 65 66 // listen to all keyevents, maybe 67 tlNode.addEventHandler(KeyEvent.ANY, keyEventListener); 68 tlNode.addEventHandler(MouseEvent.MOUSE_PRESSED, mouseEventListener); 69 tlNode.focusedProperty().addListener(focusListener); 70 71 // block ScrollEvent from being passed down to scrollbar's skin 72 origEventDispatcher = tlNode.getEventDispatcher(); 73 74 tlNode.setEventDispatcher(tlfEventDispatcher); 75 } 76 77 /** 78 * Invoked by the behavior when it is disposed, so that any listeners installed by 79 * the TwoLevelFocusBehavior can also be uninstalled 80 */ 81 public void dispose() { 82 tlNode.removeEventHandler(KeyEvent.ANY, keyEventListener); 83 tlNode.removeEventHandler(MouseEvent.MOUSE_PRESSED, mouseEventListener); 84 tlNode.focusedProperty().removeListener(focusListener); 85 tlNode.setEventDispatcher(origEventDispatcher); 86 } 87 88 /* 89 ** don't allow the Node to handle a key event if it is in externalFocus mode. 90 ** the only keyboard actions allowed are the navigation keys...... 91 */ 92 final EventDispatcher preemptiveEventDispatcher = (event, tail) -> { 93 94 // block the event from being passed down to children 95 if (event instanceof KeyEvent && event.getEventType() == KeyEvent.KEY_PRESSED) { 96 if (!((KeyEvent)event).isMetaDown() && !((KeyEvent)event).isControlDown() && !((KeyEvent)event).isAltDown()) { 97 if (isExternalFocus()) { 98 // 99 // don't let the behaviour leak any navigation keys when 100 // we're not in blocking mode.... 101 // 102 Object obj = event.getTarget(); 103 104 switch (((KeyEvent)event).getCode()) { 105 case TAB : 106 if (((KeyEvent)event).isShiftDown()) { 107 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.PREVIOUS); 108 } 109 else { 110 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.NEXT); 111 } 112 event.consume(); 113 break; 114 case UP : 115 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.UP); 116 event.consume(); 117 break; 118 case DOWN : 119 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.DOWN); 120 event.consume(); 121 break; 122 case LEFT : 123 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.LEFT); 124 event.consume(); 125 break; 126 case RIGHT : 127 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.RIGHT); 128 event.consume(); 129 break; 130 case ENTER : 131 setExternalFocus(false); 132 event.consume(); 133 break; 134 default : 135 // this'll kill mnemonics.... unless! 136 Scene s = tlNode.getScene(); 137 Event.fireEvent(s, event); 138 event.consume(); 139 break; 140 } 141 } 142 } 143 } 144 return event; 145 }; 146 final EventDispatcher preemptivePopupEventDispatcher = (event, tail) -> { 147 148 // block the event from being passed down to children 149 if (event instanceof KeyEvent && event.getEventType() == KeyEvent.KEY_PRESSED) { 150 if (!((KeyEvent)event).isMetaDown() && !((KeyEvent)event).isControlDown() && !((KeyEvent)event).isAltDown()) { 151 if (!isExternalFocus()) { 152 // 153 // don't let the behaviour leak any navigation keys when 154 // we're not in blocking mode.... 155 // 156 Object obj = event.getTarget(); 157 switch (((KeyEvent)event).getCode()) { 158 case TAB : 159 case ENTER : 160 event.consume(); 161 break; 162 case UP : 163 case DOWN : 164 break; 165 case LEFT : 166 if (obj instanceof Node) { 167 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.LEFT); 168 event.consume(); 169 } 170 else if (obj instanceof Scene) { 171 Node node = ((Scene)obj).getFocusOwner(); 172 if (node != null) { 173 node.impl_traverse(com.sun.javafx.scene.traversal.Direction.LEFT); 174 event.consume(); 175 } 176 } 177 break; 178 case RIGHT : 179 if (obj instanceof Node) { 180 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.RIGHT); 181 event.consume(); 182 } 183 else if (obj instanceof Scene) { 184 Node node = ((Scene)obj).getFocusOwner(); 185 if (node != null) { 186 node.impl_traverse(com.sun.javafx.scene.traversal.Direction.RIGHT); 187 event.consume(); 188 } 189 } 190 break; 191 192 default : 193 // this'll kill mnemonics.... unless! 194 Scene s = null; 195 if (tlNode != null) { 196 s = tlNode.getScene(); 197 Event.fireEvent(s, event); 198 } 199 event.consume(); 200 break; 201 } 202 } 203 } 204 } 205 return event; 206 }; 207 208 209 final EventDispatcher tlfEventDispatcher = (event, tail) -> { 210 211 if ((event instanceof KeyEvent)) { 212 213 if (isExternalFocus()) { 214 tail = tail.prepend(preemptiveEventDispatcher); 215 return tail.dispatchEvent(event); 216 } 217 else { 218 tail = tail.prepend(preemptivePopupEventDispatcher); 219 tail = tail.prepend(origEventDispatcher); 220 return tail.dispatchEvent(event); 221 } 222 } 223 return origEventDispatcher.dispatchEvent(event, tail); 224 }; 225 226 private Event postDispatchTidyup(Event event) { 227 228 if (event instanceof KeyEvent && event.getEventType() == KeyEvent.KEY_PRESSED) { 229 if (!isExternalFocus()) { 230 // 231 // don't let the behaviour leak any navigation keys when 232 // we're not in blocking mode.... 233 // 234 235 if (!((KeyEvent)event).isMetaDown() && !((KeyEvent)event).isControlDown() && !((KeyEvent)event).isAltDown()) { 236 237 switch (((KeyEvent)event).getCode()) { 238 case TAB : 239 case UP : 240 case DOWN : 241 case LEFT : 242 case RIGHT : 243 event.consume(); 244 break; 245 246 case ENTER : 247 setExternalFocus(true); 248 event.consume(); 249 break; 250 default : 251 break; 252 } 253 } 254 } 255 } 256 return event; 257 } 258 259 260 private final EventHandler<KeyEvent> keyEventListener = e -> { 261 postDispatchTidyup(e); 262 }; 263 264 265 /* 266 ** When a node gets focus, put it in external-focus mode. 267 */ 268 final ChangeListener<Boolean> focusListener = (observable, oldVal, newVal) -> { 269 }; 270 271 private final EventHandler<MouseEvent> mouseEventListener = e -> { 272 setExternalFocus(false); 273 }; 274 }