Ok, so I've been changing this practice program of mine every which way, and I'm still stuck on the same issue. Hoping for some help.
NOTE: I'm doing my best to simply everything, using basic code segments and simple abbreviations to describe various elements of my program. Basic description will come first, code segments will appear as needed.
THE PROBLEM. In my GUI, I have three main class. My Window, Panel A, and Panel B. In Panel B, I have a search field that either appears or disappears depending on if it's needed. There are three buttons: Find (reveals the search field), Search (runs the search, hides the field), and Cancel (just hides the field again). In addition, I'm using Input/Action Map key bindings so that my search text field on pressing ENTER will duplicate the Search button behavior, and on ESCAPE will duplicate the Cancel button behavior.
In summary, these components perform two key behaviors:
1) Show or Hide the search field
2) Start a SwingWorker which runs the search in the background and updates the GUI when its done.
Originally, I had an Action designed to do all of this, and it worked really well. However, as I went along I realized that after running the search, there were occasional instances where I would want the data displayed in Panel A to be updated to match what was being loaded to be displayed. This wouldn't happen often, but it would occasionally create an incongruity that I want to prevent.
My Action, at the time, was an inner class in Panel B. However, for the SwingWorker to affect both Panel A & B, I need to pass them both through as parameters to the SwingWorker's constructor, as so:
public SampleSwingWorker(String text, JPanel panelA, JPanel panelB){
this.text = text;
this.panelA = panelA;
this.panelB = panelB;
}
As an Inner class in Panel B, I can't pass Panel A as a parameter.
I then proceeded to try a lot of new configurations for how to set up these actions. Ultimately I started to realize that the only issue was the SwingWorker. Its the only part of the code that needs access to both Panel A & Panel B.
So I restructured the whole setup. Up above, I listed two behaviors. My Action now only affects behavior 1), whether or not the search field is shown or hidden. Meanwhile, I created an ActionListener in my Window class and passed it through to Panel B through its constructor. The result is code that looks like this. The ActionListener handles behavior 2). The resulting code looks like this:
public class SampleWindow {
private PanelA panelA;
private PanelB panelB;
private JPanel createContentPane(){
JPanel contentPane = new JPanel(new MigLayout());
panelA = new PanelA();
contentPane.add(panelA, "dock left");
panelB = new PanelB(new SampleActionListener());
contentPane.add(panelB, "dock center");
return contentPane;
}
private class SampleActionListener implements ActionListener{
public void actionPerformed(ActionEvent ae){
SampleSwingWorker ssw = new SampleSwingWorker("text", panelA, panelB);
ssw.execute();
}
}
}
public class PanelB {
private ActionListener al;
public PanelB(ActionListener al){
this.al = al;
addComponents();
}
private void addComponents(){
JTextField searchField = new JTextField();
addActionMap(searchField);
searchField.addActionListener(al);
//...add other components and buttons, etc, then add the listener to them too
}
private void addActionMap(JComponent component){
InputMap im = component.getInputMap();
ActionMap am = component.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "HideAction");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "HideAction");
am.put("HideAction", new SampleAction(SampleAction.HIDE_FIELD));
}
private class SampleAction extends AbstractAction{
public static final int SHOW_FIELD = 1;
public static final int HIDE_FIELD = 2;
private int actionType;
public SampleAction(Object param1, Object param2, int actionType){
this.actionType = actionType;
//...params are for JButtons, which require name, icon, etc
//to be passed to Action if using an Action
}
public void actionPerformed(ActionEvent ae){
if(actionType == SHOW_FIELD){
//...show the field
}else{
//...hide the field
}
}
}
}
Now nearly everything works, with one problem remaining, and that brings me to the core of my post. As you can see from my code segment, I use Input/Action Maps to assign the Action to my search field, so that pressing ENTER/ESCAPE will hide the field. However, when I press enter in the search field, it now only triggers the action, not the action listener to run the SwingWorker.
So I'm trying to figure out how to have both happen. How to make pressing enter trigger both the Action and the ActionListener.
Also, I explained the whole process here so that if I'm just doing this all wrong, someone can point out where in my logic I got lost lol.
Your code appears slightly entangled to me, it's a problem of nesting.
Using
ActionMap
andInputMap
is a good idea, though I'd expect these actions to call methods likefind()
andhide()
of your Find Panel (Panel B).Then ESCAPE
KeyEvent
can callhide()
and ENTERKeyEvent
can callfind()
andhide()
successively. You can have your button actions point to these methods as well.