views:

550

answers:

4

I seem to be having problems with my java gui code, and I have no idea why it's not working.

What needs to happen is when the mouse is clicked on the panel or frame - for now lets just say panel; as this is just a test eventually this code will be implemented for another gui component, but I'd like to get this working first - the popup menu needs to become visible and the focus needs to be set on the text field. Then when the user presses enter or the focus on the text field is lost then the popup menu needs to hide and the text reset to blank or whatever I need it to be.

So this is what I wrote:

public class Test {
    private final JFrame frame = new JFrame();
    private final JPanel panel = new JPanel();
    private final JPopupMenu menu = new JPopupMenu();
    private final JTextField field = new JTextField();
    private final Obj obj;

    //... constructor goes here

    public void test(){
        frame.setSize(new Dimension(200,200));
        field.setColumns(10);
        field.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent arg0) {
                obj.method(field.getText());
                menu.setVisible(false);
                field.setText("");
            }
        });
        field.addFocusListener(new FocusListener() {
             public void focusLost(FocusEvent e) {
                 menu.setVisible(false);
                 field.setText("");
             }

             //... focus gained event goes here
        });
        panel.addMouseListener(new MouseListener() {
            public void mouseClicked(MouseEvent e) {
                menu.setLocation(e.getX(), e.getY());
                menu.setVisible(true);
                field.requestFocusInWindow();
            }

            //... other mouse events go here
        });

        menu.add(field);
        frame.getContentPane().add(panel);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }
}

With the code as it is written here the menu automatically hides right after I click. It just flashes on screen briefly and then hides without me doing anything else.

If I change the code to exclude any occurrences of menu.setVisible(false) then the text field will never gain focus.

Is this due to a misuse of JPopupMenu? Where am I going wrong?

Also note that I left out main or Obj. They are in another file and most likely insignificant to this problem. Obj.method() does nothing and main only calls Test's constructor and test() method.

+1  A: 

You should be able to do this by overriding getComponentPopupMenu to return a JPopupMenu. This should handle exactly like you want. It will allow for focus, etc.

EDIT: This is not strictly necessary, although it does allow better for inheritance.

Public JPopupMenu getComponentPopupMenu() {
    return getMenu();
}

Oh, and if you want it to show up on any mouse click, add a mouse listener, and call show on the popupmenu:

public void processMouseEvent(MouseEvent e) {
    popup.show(this, e.getX(), e.getY());
}

That will show it on any mouse click.

or, another option is if you have a mouse listener (calling processMouseEvent) and you only want to call on right-click:

public void processMouseEvent(MouseEvent e) {
    if (e.isPopupTrigger()) {
        popup.show(this, e.getX(), e.getY());
    }
}

The mouse listener would look like this:

panel.addMouseListener(new MouseAdapter() {
    mouseClicked(MouseEvent e) {
        processMouseEvent(e);
    }
}
aperkins
I will update my question, but all i did was change setVisible to show and it works except that the focus doesn't automatically shift to the textfield.It no longer hides tho... thanks.
Victor
Right, and if you use the getPopupMenu, or do popup.show(...) the focus is then handled by Swing, and you get that behavior for free.
aperkins
I added a few pieces to try and make this more clear - the mouse listener calls the mouseEvent methods. Again, with the popup.show you are letting the underlying framework handle focus for you.
aperkins
i don't really understand where to put that, who's getComponentPopupMenu do i overwrite?
Victor
Sorry - the getComponentPopupMenu is the "standard" or "preferred" way to add a popup menu to a component in Swing. you override the component you want to have the popup menu, and have it return your menu. Then, you have a mouse listener, with code similar to process mouse event, that calls something like get getComponentPopupMenu().show(this, e.getX(), e.getY())
aperkins
k i tried everything you said and it works! for the most part. When I use the e.isPopupTrigger() condition I only displays a small dot on the screen.. not sure why... but it woks without it for the most part. here's the problem and it's very strange. When the popup menu overlaps with the edge of the frame, the text field will not automatically gain focus. It will when the menu is entirely within the frame, but when it overlaps, no automatic focus... strange no?
Victor
did you remove the other focus requests you had going on? I know that sometimes playing with focus can cause that problem.
aperkins
I just tested it with a little mini application I wrote, and it worked just fine with a basic JPanel on a JFrame.
aperkins
A: 

When you show a popup it should have the focus (and it probably grabs it) anything else really doesn't make to much sense.

So what's probably happens is this: the Menu shows, and grabs the focus.

with the next command you travers the focus to the textfield. Since the popup has no focus and a popup without focus is useless, it hides again.

Jens Schauder
that doesn't really make sense because it only hides when I have the menu.setVisible(false) in my code, whithout that - even when I have field.requestFocusInWindow() - it doesn't hide.
Victor
+1  A: 

This code should work the way you want it to work (hopefully you follow the use of anonymous classes:

public class Test {

public static void main(String[] args) {
 Test test = new Test();
 test.test();
}

private JFrame frame;
private JPanel panel;
private JPopupMenu menu;
private JTextField field;

public Test() {
 frame = new JFrame();
 frame.setSize(new Dimension(200, 200));
 frame.getContentPane().add(getPanel());
 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}

private JPanel getPanel() {
 if (panel == null) {
  panel = new JPanel();
  panel.setComponentPopupMenu(getMenu());
  panel.addMouseListener(new MouseAdapter() {

   @Override
   public void mouseClicked(MouseEvent e) {
    menu.show(panel, e.getX(), e.getY());
   }
  });
 }
 return panel;
}

private JPopupMenu getMenu() {
 if (menu == null) {
  menu = new JPopupMenu() {

   @Override
   public void setVisible(boolean visible) {
    super.setVisible(visible);
    if (visible) {
     getField().requestFocus();
    }
   }
  };
  menu.add(getField());
 }
 return menu;
}

private JTextField getField() {
 if (field == null) {
  field = new JTextField();
  field.setColumns(10);
  field.addActionListener(new ActionListener() {

   @Override
   public void actionPerformed(ActionEvent e) {
    getMenu().setVisible(false);
   }
  });
 }
 return field;
}

public void test() {
 frame.setVisible(true);
}
}

The key things to notice are when we setup the pop-up menu:

panel.setComponentPopupMenu(getMenu());
panel.addMouseListener(new MouseAdapter() {
    @Override
    public void mouseClicked(MouseEvent event) {
        getMenu().show(getPanel(), event.getX(), event.getY());
    }
});

And how you request focus for the text field when the pop up menu is visible, which is accomplished through the text field requesting focus, but not focus in window since it doesn't exist in the window, only in the menu:

menu = new JPopupMenu() {
    @Override
    public void setVisible(boolean visible) {
        super.setVisible(visible);
        if (visible) {
            getField().requestFocus();
        }
    }
};

And finally how the text field dismisses the pop up menu:

field.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        getMenu().setVisible(false);
    }
});
Peter Nix
A: 

I'd like to point out that I'm discovering through usage of the suggested methods that setComponentPopupMenu() automatically adds a MouseListener to display the given PopupMenu which then consumes the right-click event.
so whatever is inside the if(e.isPopupTrigger()) structure is never run on right clicks because the event is consumed.

So essentially I get the behavour specified in my question just by adding panel.setComponentPopupMenu(getMenu()), but the fact that it's consuming all my right-click events, not just mouseClicked, is extremely limiting.

Victor