开发者

Java Swing: Focus issue

开发者 https://www.devze.com 2023-03-31 05:29 出处:网络
I\'m making a level editor for my game. I have a property panel where I can modify the selected object its properties. I also have a Save button to write the level xml.

I'm making a level editor for my game. I have a property panel where I can modify the selected object its properties. I also have a Save button to write the level xml.

A field-edit is submitted(*) when the editor component lost the focus or Enter is pressed. This is working great, but the only problem is that when I have this sequence of actions:

  1. Edit a field
  2. Press the save button

Because, what happens is this:

  1. I edit the field
  2. I press the save button
  3. The level is saved
  4. The field lost the focus
  5. The edit is submitted

As you can see, this is the wrong ord开发者_开发技巧er. Of course I want the field to lose its focus, which causes the submit and then save the level.

Is there a trick, hack or workaround to make the field first lose the focus and then perform the action listener of the save button?

Thanks in advance.

(* submit = the edit to the field is also made in the object property)


EDIT: For the field I'm using a FocusAdapter with focusLost:

FocusAdapter focusAdapter = new FocusAdapter()
{

    @Override
    public void focusLost(FocusEvent e)
    {
        compProperties.setProperty(i, getColor());
        record(); // For undo-redo mechanism
    }
};

And for the button a simple ActionListener with actionPerformed`.

btnSave.addActionListener(new java.awt.event.ActionListener() {
     public void actionPerformed(java.awt.event.ActionEvent evt) {
         // Save the level
     }
});


Hmm ... can't reproduce: in the snippet below the lost is always notified before the actionPerfomed, independent on whether I click the button or use the mnemonic:

    final JTextField field = new JTextField("some text to change");
    FocusAdapter focus = new FocusAdapter() {

        @Override
        public void focusLost(FocusEvent e) {
            LOG.info("lost: " + field.getText());
        }

    };
    field.addFocusListener(focus);

    Action save = new AbstractAction("save") {

        @Override
        public void actionPerformed(ActionEvent e) {
            LOG.info("save: " + field.getText());
        }
    };
    save.putValue(Action.MNEMONIC_KEY, KeyEvent.VK_S);
    JButton button = new JButton(save);
    JComponent box = Box.createHorizontalBox();
    box.add(field);
    box.add(button);

On the other hand, focus is a tricky property to rely on, the ordering might be system-dependent (mine is win vista). Check how the snippet behave on yours.

  • If you see the same sequence as I do, the problem is somewhere else
  • if you get the save before the lost, try to wrap the the save action into invokeLater (which puts it at the end of the EventQueue, so it's executed after all pending events)

    Action save = new AbstractAction("save") {
    
        @Override
        public void actionPerformed(ActionEvent e) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    LOG.info("save: " + field.getText());
                }
            });
        }
    };
    


Normally, wrapping your save code into an SwingUtilities.invokeLater() should do the trick. As you already mentioned, this doesn't work? Try this:

private boolean editFocus = false;
FocusAdapter focusAdapter = new FocusAdapter()
{
   @Override
   public void focusGained(FocusEvent e){
        editFocus = true;
   }
   @Override
   public void focusLost(FocusEvent e){
       compProperties.setProperty(i, getColor());
       record(); // For undo-redo mechanism
       editFocus = false;
       if (saveRequested){
           save();               
       }
   }
};

and for your button:

private boolean saveRequested = false;

btnSave.addActionListener(new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent evt) {
         if (editFocus){
             saveRequested = true;
             return;
         } else {
             save();
         }
    }
});

and then your save method:

private void save(){
    // do your saving work
    saveRequested = false;
}

This only works when your focusLost gets called after your button's action. If suddenly the order is correct, this code will get save() called twice.

But again, wrapping your save() code in your original approach should work, because the save code will execute after processing all events. That is after processing your button click and your focusLost events. Because your focusLost code executes immediately (it's not wrapped in an invokeLater()), the focusLost code should be executed always before your save code. This does not mean that the event order will be correct! But the code associated to the events will executed in the right order.

0

精彩评论

暂无评论...
验证码 换一张
取 消