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

Print this page
rev 9038 : RT-34620: [ComboBox, DatePicker] Buttons set to default/cancel are not reacting to ComboBox enter/esc keys


  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.event.EventTarget;
  29 import javafx.scene.Node;

  30 import javafx.scene.control.ComboBoxBase;


  31 import javafx.scene.input.KeyEvent;
  32 import javafx.scene.input.MouseButton;
  33 import javafx.scene.input.MouseEvent;
  34 import java.util.ArrayList;
  35 import java.util.List;
  36 import static javafx.scene.input.KeyCode.DOWN;
  37 import static javafx.scene.input.KeyCode.ENTER;

  38 import static javafx.scene.input.KeyCode.F4;

  39 import static javafx.scene.input.KeyCode.SPACE;
  40 import static javafx.scene.input.KeyCode.UP;
  41 import static javafx.scene.input.KeyEvent.KEY_PRESSED;
  42 import static javafx.scene.input.KeyEvent.KEY_RELEASED;
  43 
  44 public class ComboBoxBaseBehavior<T> extends BehaviorBase<ComboBoxBase<T>> {
  45 
  46     /***************************************************************************
  47      *                                                                         *
  48      * Constructors                                                            *
  49      *                                                                         *
  50      **************************************************************************/
  51 
  52     private TwoLevelFocusComboBehavior tlFocus;
  53     
  54     /**






  55      * 
  56      */
  57     public ComboBoxBaseBehavior(final ComboBoxBase<T> comboBox, final List<KeyBinding> bindings) {
  58         super(comboBox, bindings);
  59 
  60         // Only add this if we're on an embedded platform that supports 5-button navigation
  61         if (com.sun.javafx.scene.control.skin.Utils.isTwoLevelFocus()) {
  62             tlFocus = new TwoLevelFocusComboBehavior(comboBox); // needs to be last.
  63         }
  64     }
  65 
  66     @Override public void dispose() {
  67         if (tlFocus != null) tlFocus.dispose();
  68         super.dispose();
  69     }
  70 
  71     /***************************************************************************
  72      *                                                                         *
  73      * Focus change handling                                                   *
  74      *                                                                         *


  95      * event (this could be space bar for example). As long as keyDown is true,
  96      * we are also armed, and will ignore mouse events related to arming.
  97      * Note this is made package private solely for the sake of testing.
  98      */
  99     private boolean keyDown;
 100 
 101     private static final String PRESS_ACTION = "Press";
 102     private static final String RELEASE_ACTION = "Release";
 103 
 104     protected static final List<KeyBinding> COMBO_BOX_BASE_BINDINGS = new ArrayList<KeyBinding>();
 105     static {
 106         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(F4, KEY_RELEASED, "togglePopup"));
 107         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(UP, "togglePopup").alt());
 108         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(DOWN, "togglePopup").alt());
 109 
 110         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(SPACE, KEY_PRESSED, PRESS_ACTION));
 111         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(SPACE, KEY_RELEASED, RELEASE_ACTION));
 112 
 113         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(ENTER, KEY_PRESSED, PRESS_ACTION));
 114         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(ENTER, KEY_RELEASED, RELEASE_ACTION));




 115     }
 116 
 117     @Override protected void callActionForEvent(KeyEvent e) {
 118         // If popup is shown, KeyEvent causes popup to close

 119         showPopupOnMouseRelease = true;
 120         super.callActionForEvent(e);
 121     }
 122 
 123     @Override protected void callAction(String name) {
 124         if (PRESS_ACTION.equals(name)) {
 125             keyPressed();
 126         } else if (RELEASE_ACTION.equals(name)) {
 127             keyReleased();
 128         } else if ("showPopup".equals(name)) {
 129             show();
 130         } else if ("togglePopup".equals(name)) {
 131             if (getControl().isShowing()) hide();
 132             else show();




 133         } else {
 134             super.callAction(name);
 135         }
 136     }
 137 
 138     /**
 139      * This function is invoked when an appropriate keystroke occurs which
 140      * causes this button to be armed if it is not already armed by a mouse
 141      * press.
 142      */
 143     private void keyPressed() {
 144         if (com.sun.javafx.scene.control.skin.Utils.isTwoLevelFocus()) {
 145             show();
 146             if (tlFocus != null) {
 147                 tlFocus.setExternalFocus(false);
 148             }
 149         }
 150         else {
 151             if (! getControl().isPressed() && ! getControl().isArmed()) {
 152                 keyDown = true;
 153                 getControl().arm();
 154             }
 155         }
 156     }
 157 
 158     /**
 159      * Invoked when a valid keystroke release occurs which causes the button
 160      * to fire if it was armed by a keyPress.
 161      */
 162     private void keyReleased() {
 163         if (!com.sun.javafx.scene.control.skin.Utils.isTwoLevelFocus()) {
 164             if (keyDown) {
 165                 keyDown = false;
 166                 if (getControl().isArmed()) {
 167                     getControl().disarm();
 168                 }
 169             }
 170         }
 171     }
 172     





 173     



















 174     
 175     /**************************************************************************
 176      *                                                                        *
 177      * Mouse Events                                                           *
 178      *                                                                        *
 179      *************************************************************************/
 180 
 181     @Override public void mousePressed(MouseEvent e) {
 182         super.mousePressed(e);
 183         arm(e);
 184     }
 185 
 186     @Override public void mouseReleased(MouseEvent e) {
 187         super.mouseReleased(e);
 188 
 189         disarm();
 190 
 191         // The showPopupOnMouseRelease boolean was added to resolve
 192         // RT-18151: namely, clicking on the comboBox button shouldn't hide, 
 193         // and then immediately show the popup, which was occurring because we




  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.event.EventTarget;
  29 import javafx.scene.Node;
  30 import javafx.scene.control.ComboBox;
  31 import javafx.scene.control.ComboBoxBase;
  32 import javafx.scene.control.DatePicker;
  33 import javafx.scene.control.TextField;
  34 import javafx.scene.input.KeyEvent;
  35 import javafx.scene.input.MouseButton;
  36 import javafx.scene.input.MouseEvent;
  37 import java.util.ArrayList;
  38 import java.util.List;
  39 import static javafx.scene.input.KeyCode.DOWN;
  40 import static javafx.scene.input.KeyCode.ENTER;
  41 import static javafx.scene.input.KeyCode.ESCAPE;
  42 import static javafx.scene.input.KeyCode.F4;
  43 import static javafx.scene.input.KeyCode.F10;
  44 import static javafx.scene.input.KeyCode.SPACE;
  45 import static javafx.scene.input.KeyCode.UP;
  46 import static javafx.scene.input.KeyEvent.KEY_PRESSED;
  47 import static javafx.scene.input.KeyEvent.KEY_RELEASED;
  48 
  49 public class ComboBoxBaseBehavior<T> extends BehaviorBase<ComboBoxBase<T>> {
  50 
  51     /***************************************************************************
  52      *                                                                         *
  53      * Constructors                                                            *
  54      *                                                                         *
  55      **************************************************************************/
  56 
  57     private TwoLevelFocusComboBehavior tlFocus;
  58     
  59     /**
  60      * Used to keep track of the most recent key event. This is used when
  61      * the event needs to be forwarded to the parent for bubbling up.
  62      */
  63     private KeyEvent lastEvent;
  64 
  65     /**
  66      * 
  67      */
  68     public ComboBoxBaseBehavior(final ComboBoxBase<T> comboBox, final List<KeyBinding> bindings) {
  69         super(comboBox, bindings);
  70 
  71         // Only add this if we're on an embedded platform that supports 5-button navigation
  72         if (com.sun.javafx.scene.control.skin.Utils.isTwoLevelFocus()) {
  73             tlFocus = new TwoLevelFocusComboBehavior(comboBox); // needs to be last.
  74         }
  75     }
  76 
  77     @Override public void dispose() {
  78         if (tlFocus != null) tlFocus.dispose();
  79         super.dispose();
  80     }
  81 
  82     /***************************************************************************
  83      *                                                                         *
  84      * Focus change handling                                                   *
  85      *                                                                         *


 106      * event (this could be space bar for example). As long as keyDown is true,
 107      * we are also armed, and will ignore mouse events related to arming.
 108      * Note this is made package private solely for the sake of testing.
 109      */
 110     private boolean keyDown;
 111 
 112     private static final String PRESS_ACTION = "Press";
 113     private static final String RELEASE_ACTION = "Release";
 114 
 115     protected static final List<KeyBinding> COMBO_BOX_BASE_BINDINGS = new ArrayList<KeyBinding>();
 116     static {
 117         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(F4, KEY_RELEASED, "togglePopup"));
 118         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(UP, "togglePopup").alt());
 119         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(DOWN, "togglePopup").alt());
 120 
 121         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(SPACE, KEY_PRESSED, PRESS_ACTION));
 122         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(SPACE, KEY_RELEASED, RELEASE_ACTION));
 123 
 124         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(ENTER, KEY_PRESSED, PRESS_ACTION));
 125         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(ENTER, KEY_RELEASED, RELEASE_ACTION));
 126 
 127         // The following keys are forwarded to the parent container
 128         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(ESCAPE, "Cancel"));
 129         COMBO_BOX_BASE_BINDINGS.add(new KeyBinding(F10, "ToParent"));
 130     }
 131 
 132     @Override protected void callActionForEvent(KeyEvent e) {
 133         // If popup is shown, KeyEvent causes popup to close
 134         lastEvent = e;
 135         showPopupOnMouseRelease = true;
 136         super.callActionForEvent(e);
 137     }
 138 
 139     @Override protected void callAction(String name) {
 140         if (PRESS_ACTION.equals(name)) {
 141             keyPressed();
 142         } else if (RELEASE_ACTION.equals(name)) {
 143             keyReleased();
 144         } else if ("showPopup".equals(name)) {
 145             show();
 146         } else if ("togglePopup".equals(name)) {
 147             if (getControl().isShowing()) hide();
 148             else show();
 149         } else if ("Cancel".equals(name)) {
 150             cancelEdit(lastEvent);
 151         } else if ("ToParent".equals(name)) {
 152             forwardToParent(lastEvent);
 153         } else {
 154             super.callAction(name);
 155         }
 156     }
 157 
 158     /**
 159      * This function is invoked when an appropriate keystroke occurs which
 160      * causes this button to be armed if it is not already armed by a mouse
 161      * press.
 162      */
 163     private void keyPressed() {
 164         if (com.sun.javafx.scene.control.skin.Utils.isTwoLevelFocus()) {
 165             show();
 166             if (tlFocus != null) {
 167                 tlFocus.setExternalFocus(false);
 168             }
 169         }
 170         else {
 171             if (! getControl().isPressed() && ! getControl().isArmed()) {
 172                 keyDown = true;
 173                 getControl().arm();
 174             }
 175         }
 176     }
 177 
 178     /**
 179      * Invoked when a valid keystroke release occurs which causes the button
 180      * to fire if it was armed by a keyPress.
 181      */
 182     private void keyReleased() {
 183         if (!com.sun.javafx.scene.control.skin.Utils.isTwoLevelFocus()) {
 184             if (keyDown) {
 185                 keyDown = false;
 186                 if (getControl().isArmed()) {
 187                     getControl().disarm();
 188                 }
 189             }
 190         }
 191     }
 192     
 193     protected void forwardToParent(KeyEvent event) {
 194         if (getControl().getParent() != null) {
 195             getControl().getParent().fireEvent(event);
 196         }
 197     }
 198 
 199     protected void cancelEdit(KeyEvent event) {
 200         /**
 201          * This can be cleaned up if the editor property is moved up
 202          * to ComboBoxBase.
 203          */
 204         ComboBoxBase comboBoxBase = getControl();
 205         TextField textField = null;
 206         if (comboBoxBase instanceof DatePicker) {
 207             textField = ((DatePicker)comboBoxBase).getEditor();
 208         } else if (comboBoxBase instanceof ComboBox) {
 209             textField = comboBoxBase.isEditable() ? ((ComboBox)comboBoxBase).getEditor() : null;
 210         }
 211 
 212         if (textField != null && textField.getTextFormatter() != null) {
 213             textField.cancelEdit();
 214         } else {
 215             forwardToParent(event);
 216         }
 217     }
 218 
 219     /**************************************************************************
 220      *                                                                        *
 221      * Mouse Events                                                           *
 222      *                                                                        *
 223      *************************************************************************/
 224 
 225     @Override public void mousePressed(MouseEvent e) {
 226         super.mousePressed(e);
 227         arm(e);
 228     }
 229 
 230     @Override public void mouseReleased(MouseEvent e) {
 231         super.mouseReleased(e);
 232 
 233         disarm();
 234 
 235         // The showPopupOnMouseRelease boolean was added to resolve
 236         // RT-18151: namely, clicking on the comboBox button shouldn't hide, 
 237         // and then immediately show the popup, which was occurring because we