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
|