Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swing rendering problem of busy cursors when using modal dialogs on Linux

When setting a busy cursor on the glass pane of the application frame after closing a modal dialog, the busy cursor is not always displayed. Sometimes it works (the first time it is mostly always working), sometimes not.

Even better, when setting the busy cursor before opening the dialog. The busy cursor get displayed but when moving the mouse inside and then outside the dialog the busy cursor is not displayed anymore.

Note that I observe the following bug on Linux only. On Mac OS X or Windows the behavior is deterministic and consistent.

Another hint, in the first case of the code sample, when the mouse is NOT entering the dialog and the YES_OPTION is selected using the keyboard, the busy mouse cursor is always shown. Also in this case, the "Please wait..." label on the glass pane get never painted.

Here a SSCCE demonstrating these bugs:

import java.awt.event.*;
import javax.swing.*;

public class TestFrame extends JFrame {

private JPanel panel;
private JPanel glassPane;

public TestFrame() {
    final JButton button1 = new JButton(new AbstractAction("Start activity indicator after closing the dialog") {
        @Override
        public void actionPerformed(ActionEvent e) {
            doAction1();
        }
    });

    final JButton button2 = new JButton(new AbstractAction("Start activity indicator before opening the dialog") {
        @Override
        public void actionPerformed(ActionEvent e) {
            doAction2();
        }
    });

    panel = new JPanel();
    panel.add(button1);
    panel.add(button2);
    getContentPane().add(panel, BorderLayout.NORTH);

    glassPane = (JPanel) getGlassPane();
    glassPane.setLayout(new BorderLayout());
    glassPane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
    glassPane.add(new JLabel("Please Wait..."), BorderLayout.CENTER);

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(800, 600);
    setVisible(true);
}

public void doAction1() {
    System.out.println("IsStartingInEDT?: "+  SwingUtilities.isEventDispatchThread());
    final int response = JOptionPane.showConfirmDialog(this, "Click on the YES_OPTION, busy indicator must start (if it does, try again).");
    if (JOptionPane.YES_OPTION == response) {
        startActivity();
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(200);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        stopActivity();
    }
}

public void doAction2() {
    startActivity();
    System.out.println("IsStartingInEDT?: "+  SwingUtilities.isEventDispatchThread());
    final int response = JOptionPane.showConfirmDialog(this, "Move the mouse inside the dialog (me) and then outside, the busy indicator is not shown anymore");
    if (JOptionPane.YES_OPTION == response) {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(200);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    stopActivity();
}

public void startActivity() {
    System.out.println("TestFrame.startActivity()");
    glassPane.setVisible(true);
}

public void stopActivity() {
    System.out.println("TestFrame.stopActivity()");
    glassPane.setVisible(false);
}

/**
 * @param args
 */
public static void main(String[] args) {
    new TestFrame();
}

}

At the moment I did not find any related issues in the JavaBug parade. I will search further before opening a new one.

I also already read the following article but it is not very convenient as making a good modal dialog from a non-modal one is not straightforward: http://www.javaspecialists.eu/archive/Issue065.html

Can anyone provide some help? Thanks in advance, Pierre

like image 279
PragmaCoder Avatar asked Jan 18 '23 22:01

PragmaCoder


1 Answers

You have some threading issue here.

Is IsStartingInEDT true?

If yes, you are doing it wrong because:

  • You should not sleep in UI thread. This would stop the screen update.

If no, you are doing it wrong because:

  • OptionPane.showConfirmDialog() must be called from the UI thread.

you should do something like this:

public void doAction1() {
    if (!SwingUtilities.isEventDispatchThread())  {
         System.err.println("error, must be edt");
         return;
    }

    final int response = JOptionPane.showConfirmDialog(this, "Click on the YES_OPTION, busy indicator must start (if it does, try again).");

    if (JOptionPane.YES_OPTION == response) {
        startActivity();   // change glass panel in edt    

        // new thread for long standing task
        new Thread( new Runnable() { public void run() {    
           for (int i = 0; i < 5; i++) {
               try {
                   Thread.sleep(200);
               } catch (Exception e) {
                   e.printStackTrace();
               }
           }

           SwingUtilities.invokeAndWait(new Runnable(){ public void run() {
              // changing glass panel need edt
              stopActivity();
           });
        }).start();
    }
}
like image 71
J-16 SDiZ Avatar answered Jan 30 '23 03:01

J-16 SDiZ