Lory A Lory A - 1 month ago 6
Java Question

Avoid JOptionPane interference with action listeners

I have noticed that all

JOptionPane
method "interfere" with
ActionListeners
.
I need ActionListener to remain active after a JOptionPane has been opened.

For example:

I have a
JButton
, I register the mouse being pressed and draw the button red. Upon being released, I draw it blue.


  • If I just click it, the button will turn blue. Ok

  • If I hold it clicked, the button will stay red. Ok

  • If I click it and set it to open a
    JOptionPane
    dialog, it stays red, even though I have released the mouse. Not Ok



I haven't been able to find any documentation on this specific behaviour, can someone point me in the right direction?

I do really need to use
JOptionPane
.

Answer

One option -- queue the call to open the JOptionPane on the Swing event queue. This will delay the opening of the modal JOptionPane just a little bit, allowing other button actions to be performed.

Another option is to extract the JDialog from the JOptionPane, and call it in a non-modal way.

For example:

import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class TestOptionPane extends JPanel {
    private static final Color FOREGROUND = Color.RED;
    private static final Color PRESSED_FG = Color.BLUE;
    private JButton button1 = new JButton(new Button1Action());
    private JButton button2 = new JButton(new Button1Action());

    public TestOptionPane() {
        setPreferredSize(new Dimension(600, 450));
        button1.getModel().addChangeListener(new ButtonModelListener(button1));
        button1.setForeground(FOREGROUND);
        add(button1);
        button2.getModel().addChangeListener(new ButtonModelListener(button2));
        button2.setForeground(FOREGROUND);
        add(button2);
    }

    private class Button1Action extends AbstractAction {
        public Button1Action() {
            super("Queue JOptionPane on Swing event thread");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            SwingUtilities.invokeLater(() -> {
                JOptionPane.showMessageDialog(TestOptionPane.this, "hello");
            });
        }
    }

    private class Button2Action extends AbstractAction {
        public Button2Action() {
            super("Show non-modal JOptionPane");
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            SwingUtilities.invokeLater(() -> {
                Component parentComponent = TestOptionPane.this;
                JOptionPane optionPane = new JOptionPane("Hello", JOptionPane.PLAIN_MESSAGE);
                JDialog dialog = optionPane.createDialog(parentComponent, "Fubars Rule!");
                dialog.setModalityType(ModalityType.MODELESS);
                dialog.setLocationRelativeTo(parentComponent);
                dialog.setVisible(true);
            });
        }
    }

    private class ButtonModelListener implements ChangeListener {
        private JButton button;

        public ButtonModelListener(JButton button) {
            this.button = button;
        }

        @Override
        public void stateChanged(ChangeEvent e) {
            ButtonModel model = (ButtonModel) e.getSource();
            if (model.isPressed()) {
                button.setForeground(PRESSED_FG);
            } else {
                button.setForeground(FOREGROUND);
            }
        }

    }

    private static void createAndShowGui() {
        JFrame frame = new JFrame("TestOptionPane");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new TestOptionPane());
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> createAndShowGui());
    }
}