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 30 import javafx.scene.Scene; 31 import javafx.scene.input.KeyEvent; 32 33 import javafx.beans.value.ChangeListener; 34 import javafx.event.Event; 35 import javafx.event.EventDispatcher; 36 import javafx.event.EventHandler; 37 import javafx.scene.input.MouseEvent; 38 39 40 public class TwoLevelFocusComboBehavior extends TwoLevelFocusBehavior { 41 42 public TwoLevelFocusComboBehavior(Node node) { 43 44 tlNode = node; 45 46 // listen to all keyevents, maybe 47 tlNode.addEventHandler(KeyEvent.ANY, keyEventListener); 48 tlNode.addEventHandler(MouseEvent.MOUSE_PRESSED, mouseEventListener); 49 tlNode.focusedProperty().addListener(focusListener); 50 51 // block ScrollEvent from being passed down to scrollbar's skin 52 origEventDispatcher = tlNode.getEventDispatcher(); 53 tlNode.setEventDispatcher(tlfEventDispatcher); 54 } 55 56 /** 57 * Invoked by the behavior when it is disposed, so that any listeners installed by 58 * the TwoLevelFocusBehavior can also be uninstalled 59 */ 60 public void dispose() { 61 tlNode.removeEventHandler(KeyEvent.ANY, keyEventListener); 62 tlNode.removeEventHandler(MouseEvent.MOUSE_PRESSED, mouseEventListener); 63 tlNode.focusedProperty().removeListener(focusListener); 64 tlNode.setEventDispatcher(origEventDispatcher); 65 } 66 67 /* 68 ** don't allow the Node handle a key event if it is in externalFocus mode. 69 ** the only keyboard actions allowed are the navigation keys...... 70 */ 71 final EventDispatcher preemptiveEventDispatcher = (event, tail) -> { 72 73 // block the event from being passed down to children 74 if (event instanceof KeyEvent && event.getEventType() == KeyEvent.KEY_PRESSED) { 75 if (!((KeyEvent)event).isMetaDown() && !((KeyEvent)event).isControlDown() && !((KeyEvent)event).isAltDown()) { 76 if (isExternalFocus()) { 77 // 78 // don't let the behaviour leak any navigation keys when 79 // we're not in blocking mode.... 80 // 81 Object obj = event.getTarget(); 82 83 switch (((KeyEvent)event).getCode()) { 84 case TAB : 85 if (((KeyEvent)event).isShiftDown()) { 86 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.PREVIOUS); 87 } 88 else { 89 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.NEXT); 90 } 91 event.consume(); 92 break; 93 case UP : 94 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.UP); 95 event.consume(); 96 break; 97 case DOWN : 98 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.DOWN); 99 event.consume(); 100 break; 101 case LEFT : 102 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.LEFT); 103 event.consume(); 104 break; 105 case RIGHT : 106 ((Node)obj).impl_traverse(com.sun.javafx.scene.traversal.Direction.RIGHT); 107 event.consume(); 108 break; 109 case ENTER : 110 setExternalFocus(false); 111 origEventDispatcher.dispatchEvent(event, tail); 112 break; 113 default : 114 // this'll kill mnemonics.... unless! 115 Scene s = tlNode.getScene(); 116 Event.fireEvent(s, event); 117 event.consume(); 118 break; 119 } 120 } 121 } 122 } 123 return event; 124 }; 125 126 final EventDispatcher tlfEventDispatcher = (event, tail) -> { 127 if ((event instanceof KeyEvent)) { 128 if (isExternalFocus()) { 129 tail = tail.prepend(preemptiveEventDispatcher); 130 return tail.dispatchEvent(event); 131 } 132 } 133 return origEventDispatcher.dispatchEvent(event, tail); 134 }; 135 136 private Event postDispatchTidyup(Event event) { 137 138 // block the event from being passed down to children 139 if (event instanceof KeyEvent && event.getEventType() == KeyEvent.KEY_PRESSED) { 140 if (!isExternalFocus()) { 141 // 142 // don't let the behaviour leak any navigation keys when 143 // we're not in blocking mode.... 144 // 145 if (!((KeyEvent)event).isMetaDown() && !((KeyEvent)event).isControlDown() && !((KeyEvent)event).isAltDown()) { 146 switch (((KeyEvent)event).getCode()) { 147 case TAB : 148 case UP : 149 case DOWN : 150 case LEFT : 151 case RIGHT : 152 event.consume(); 153 break; 154 155 case ENTER : 156 setExternalFocus(true); 157 event.consume(); 158 break; 159 default : 160 break; 161 } 162 } 163 } 164 } 165 return event; 166 } 167 168 169 private final EventHandler<KeyEvent> keyEventListener = e -> { 170 postDispatchTidyup(e); 171 }; 172 173 174 /* 175 ** When a node gets focus, put it in external-focus mode. 176 */ 177 final ChangeListener<Boolean> focusListener = (observable, oldVal, newVal) -> { 178 setExternalFocus(true); 179 }; 180 181 private final EventHandler<MouseEvent> mouseEventListener = e -> { 182 setExternalFocus(false); 183 }; 184 }