@@ -176,8 +176,14 @@ public static void exportKeyBindings(ToolOptions keyBindingOptions) {
176
176
/**
177
177
* Changes the given key event to the new source component and then dispatches that event.
178
178
* This method is intended for clients that wish to effectively take a key event given to
179
- * one component and give it to another component. This is seldom-used code; if you don't
180
- * know when to use this code, then don't.
179
+ * one component and give it to another component.
180
+ *
181
+ * <p>This method exists to deal with the complicated nature of key event processing and
182
+ * how our (not Java's) framework processes key event bindings to trigger actions. If not
183
+ * for our special processing of action key bindings, then this method would not be
184
+ * necessary.
185
+ *
186
+ * <p><b>This is seldom-used code; if you don't know when to use this code, then don't.</b>
181
187
*
182
188
* @param newSource the new target of the event
183
189
* @param e the existing event
@@ -190,9 +196,44 @@ public static void retargetEvent(Component newSource, KeyEvent e) {
190
196
191
197
KeyEvent newEvent = new KeyEvent (newSource , e .getID (), e .getWhen (), e .getModifiersEx (),
192
198
e .getKeyCode (), e .getKeyChar (), e .getKeyLocation ());
193
- e .consume ();
199
+
200
+ /*
201
+ Unusual Code Alert!
202
+
203
+ The KeyboardFocusManager is a complicated beast. Here we use knowledge of one such
204
+ complication to correctly route key events. If the client of this method passes
205
+ a component whose 'isShowing()' returns false, then the manager will not send the
206
+ event to that component. Almost all clients will pass fully attached/realized
207
+ components to the manager. We, however, will sometimes pass components that are not
208
+ attached; for example, when we are using said components with a renderer to perform
209
+ our own painting. In the case of non-attached components, we must call the
210
+ redispatchEvent() method ourselves.
211
+
212
+ Why don't we just always call redispatchEvent()? Well, that
213
+ method will not pass the new cloned event we just created back through the full
214
+ key event pipeline. This means that tool-level (our Tool API, not Java)
215
+ actions will not work, as tool-level actions are handled at the beginning of the
216
+ key event pipeline, not by the components themselves.
217
+
218
+ Also, we have here guilty knowledge that the aforementioned tool-level key processing
219
+ will check to see if the event was consumed. If consumed, then no further processing
220
+ will happen; if not consumed, then the framework will continue to process the event
221
+ passed into this method. Thus, after we send the new event, we will update the
222
+ original event to match the consumed state of our new event. This means that the
223
+ component passed to this method must, somewhere in its processing, consume the key
224
+ event we dispatch here, if they do not wish for any further processing to take place.
225
+ */
194
226
KeyboardFocusManager kfm = KeyboardFocusManager .getCurrentKeyboardFocusManager ();
195
- kfm .dispatchEvent (newEvent );
227
+ if (newSource .isShowing ()) {
228
+ kfm .dispatchEvent (newEvent );
229
+ }
230
+ else {
231
+ kfm .redispatchEvent (newSource , newEvent );
232
+ }
233
+
234
+ if (newEvent .isConsumed ()) {
235
+ e .consume ();
236
+ }
196
237
}
197
238
198
239
/**
0 commit comments