Forums Register Login

Preference Between Input/Action Maps and KeyEventDispatchers?

+Pie Number of slices to send: Send
My program displays a GUI that includes a JTable. When the JTable has the focus, pressing ENTER moves the selected cell. I want ENTER to cause an action to occur (the same one that goes with a menu item in my top-level JFrame). So far, I have found two ways to approach this. The first is to map the ENTER key for the JTable to an action with this code:



This works perfectly. The second way is to capture the ENTER key at the top level and redirect it, with code like this:


and in my main() routine:



This also works perfectly, putting me in the rare situation of having two different ways to do something I need to do, both of which seem to work just fine, with neither of them showing me any easy way to choose which I should use. I am inclined to use the latter, as its effect is application-wide, and I always want the ENTER key to do the same thing, no matter what the state of my program is, and no matter what has the focus. The former would appear to require that I implement an Input/Action map for each component that might get the focus (or, more correctly, would consume the ENTER key-pressed event). Being the lazy slob I am, I prefer the one that looks like less work, but, before I make a choice, I'm hoping someone here with prior experience can let me know of any lurking problems in either choice, or any other guidance they can offer on how to choose.

Thanks!
+Pie Number of slices to send: Send
 

Stevens Miller wrote:The former would appear to require that I implement an Input/Action map for each component that might get the focus (or, more correctly, would consume the ENTER key-pressed event).


You're mistaken there. getInputMap is overloaded; the other version can take a constant that will catch the key binding if the component is an ancestor of the component has focus.
+Pie Number of slices to send: Send
 

Rob Spoor wrote:

Stevens Miller wrote:The former would appear to require that I implement an Input/Action map for each component that might get the focus (or, more correctly, would consume the ENTER key-pressed event).


You're mistaken there. getInputMap is overloaded; the other version can take a constant that will catch the key binding if the component is an ancestor of the component has focus.


Hey, you're right! Although it appears you can't just use the with-argument method on the JFrame, as it doesn't exist for JFrames, so I'll still need get/put Map calls for each top-level ancestor component in my GUI.

Okay, with that in mind, is there any way to choose between the Map and the Dispatcher approaches? Maybe a better question would be, why is the mechanism for catching keystrokes different for a JFrame than it is for a JComponent? The Dispatcher approach appears to be the only option (at least, of the two I am considering) for a JFrame, while the Map approach is the only option for a JComponent. Neither is descended from the other in the object hierarchy, so I can see you need to implement something for each of them, unless you are going to implement this at a higher level class they have in common. The lowest one they share is java.awt.Component (which does provide an addKeyListener() method; are the Maps/Dispatchers implemented based on that?).

This is the kind of thing that still confuses the heck out of me. The standard libraries seem to offer a number of ways to do the same thing, so I'm always fearful that I've chosen the wrong one. What do you recommend, Maps or Dispatchers?
+Pie Number of slices to send: Send
 

Stevens Miller wrote:Although it appears you can't just use the with-argument method on the JFrame, as it doesn't exist for JFrames, so I'll still need get/put Map calls for each top-level ancestor component in my GUI.


You're only partly right. A JFrame (and JDialog) has a content pane. This can be any Container, but by default is a JPanel. So you can usually get the content pane, cast it to JPanel (or a bit safer, JComponent) and apply the key binding (Map) to it.

What do you recommend, Maps or Dispatchers?


I find it's a bit easier to use key bindings, as the KeyStroke part is easier to create than check for the proper modifiers. For example, let's say you want to implement your own focus switching behaviour. With a KeyListener, you need one KeyListener that listens for TAB and then inside the handling method check if Shift was pressed or not. With a key binding you can simply create two different KeyStrokes with their own Actions.
+Pie Number of slices to send: Send
Thanks again. The bindings do look more attractive, for the reasons you mention (particularly in light of the fact that I have that one ancestor component that will allow me to bind everything in my JFrame; had forgotten all about that). Also, the manager would intercept ENTER-pressed events even for transient dialog boxes (and that is something I don't want).

Very helpful replies, Rob. I appreciate it.
+Pie Number of slices to send: Send
You're welcome
+Pie Number of slices to send: Send
Well, I must still be getting something wrong, Rob. I have changed my code to set the WHEN_ANCESTOR_OF_FOCUSED_COMPONENT map of the JFrame's content pane, and it works precisely as you predicted when, say, a button on the JFrame has the focus, and I press the mapped key. But when the focus is set to the JTable, my actionPerformed isn't called for the same key.

Here's my code:



GetFrameAction is a class derived from AbstractAction that just prints a line when the action is performed:

(I'm saving a reference to the JFrame when this object is contructed for later use; it's not relevant to this example.)

In my initComponents method, I create a JButton and a JTable. Now, I didn't write this. NetBeans wrote it for me. I have looked it over and don't see that it is doing anything that should prevent the mapping from taking effect. I'm sorry it's a bit long, but here it is:



In the MainJFrame constructor, if I change these two lines:


so they refer to the JTable directly, instead of to the JFrame's content pane, the actionPerformed method does get called:



I've even used this:



to verify that the JTable is a descendant of the contentPane, and it does print "true."

Can you see what I'm doing wrong?
+Pie Number of slices to send: Send
Here's a much simpler example:

+Pie Number of slices to send: Send
Ah. Problem appears to be that, first of all, the JTable's own WHEN_ANCESTOR_OF_FOCUSED_COMPONENT is mapped to an action that moves the selected table row, and that gets first crack at the KeyEvent before the JFrame's contentPane's WHEN_ANCESTOR_OF_FOCUSED_COMPONENT map gets its chance. This is compounded by the fact that the JTable's ActionMap has a parent ActionMap, that steps in and supplies the same action, even if you set the JTable's ActionMap's action to null for the VK_ENTER KeyEvent. You can set them both to null, which does stop the selected row from moving, but now it seems to activate a cell editor, which I also don't want.

This brings me full-circle, to setting the ActionMap for each object that is processing the Enter key so that its action is my action, not its own action. That's fine, but I had hoped there was some way to take control somewhat more globally. That is, I had hoped that setting the WHEN_ANCESTOR_OF_FOCUSED_COMPONENT map of the ancestor would supersede the setting of the descendant's map, but I can see why that's not always what you want. (Is there a way to reverse the priority, though, so the ancestor does supersede the descendant, or does that just cause more problems?)
Get me the mayor's office! I need to tell her about this tiny ad:
a bit of art, as a gift, that will fit in a stocking
https://gardener-gift.com


reply
reply
This thread has been viewed 1538 times.
Similar Threads
JTable tabbing
Disable Enter key in a JTable
JTable size
handling TAB in JTable celleditor
Tab Key Binding JTextField
More...

All times above are in ranch (not your local) time.
The current ranch time is
Mar 28, 2024 07:50:21.