2

I have a program that is going to do some work (in a background thread) at startup, so I'd like to display a busy cursor for a few moments while a table is being populated. Unfortunately, no matter what I did, I could not get a wait cursor to appear if my window (JFrame) appeared under the pointer. I did some experimentation, and I tracked it down to a simple test case (below). Basically, if there exists a JScrollPanel (e.g. as a parent for a JTable) as a child of the JFrame, then only the default cursor will appear unless I move the pointer out of the window and back in.

Here's the compilable code:

package cursortest;

import java.awt.Cursor;
import java.awt.Dimension;
import javax.swing.*;

public class CursorTest extends JFrame {

    void initPanel() {
        JScrollPane panel = new JScrollPane();
        panel.setMinimumSize(new Dimension(500, 500));
        panel.setMaximumSize(new Dimension(500, 500));
        panel.setPreferredSize(new Dimension(500, 500));

        GroupLayout layout = new GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addComponent(panel)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addComponent(panel)
        );
    }

    public CursorTest() {
        initPanel();
        setMinimumSize(new Dimension(500, 500));
        setMaximumSize(new Dimension(500, 500));
        setPreferredSize(new Dimension(500, 500));
        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        pack();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                new CursorTest().setVisible(true);
            }
        });
    }
}

Launch the program so that when the window appears, the mouse pointer is within the bounds of the window. You may have to fiddle with the sizes to get that to happen (that I know I have forced in an artificial way for this example).

If initPanel is NOT called, then the mouse cursor will immediately appear as a wait cursor.

If initPanel IS called, then the mouse cursor will appear as default until you move the pointer out of the window and then back in.

Also, if the JScrollPane is replaced with some other kind of widget, say a JLabel, then this cursor problem does not manifest.

Can anyone help me to figure to how to fix this problem? I've thought about also setting the busy cursor for the widgets (my JScrollPanel and/or JTable), but this doesn't work. You could try it by adding the line panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));.

Oh, and I should probably mention that I'm doing this on a Mac.

Timothy Miller
  • 1,527
  • 4
  • 28
  • 48

1 Answers1

2

for example

import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import javax.swing.*;

public class CursorTest extends JFrame {

    private static final long serialVersionUID = 1L;
    private javax.swing.Timer timer = null;
    private JScrollPane scroll;

    public CursorTest() {
        scroll = new JScrollPane();
        scroll.setPreferredSize(new Dimension(400,300));
        setTitle("CursorTest");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new FlowLayout());
        add(scroll);
        setLocation(100, 100);
        pack();
        setVisible(true);
        start();
    }

    private void start() {
        scroll.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
        timer = new javax.swing.Timer(5000, stop());
        timer.start();
    }

    public Action stop() {
        return new AbstractAction("Change Cursor Action") {

            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                timer.stop();
                scroll.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
            }
        };
    }

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                new CursorTest().setVisible(true);
            }
        });
    }
}
mKorbel
  • 109,525
  • 20
  • 134
  • 319
  • Your example works, but I cannot understand what is fundamentally different about it. Is it the layout? – Timothy Miller Oct 04 '11 at 19:18
  • Ok, the difference is that the cursor is set AFTER setVisible is called. Please edit your response to include a short explanation of this difference, and I'll mark it as the accepted answer. Thank you! – Timothy Miller Oct 04 '11 at 19:23
  • glad to hepl, but better would be read this thread http://stackoverflow.com/questions/6051755/java-wait-cursor-display-problem , (how to split Event Dispash Thread to the two or more actions) and learning that by yourself, there is example about change Cursor during add records to the JTable – mKorbel Oct 04 '11 at 20:01
  • Thank you for the link. I read it and several things it pointed to, and I learned a lot. I didn't know about glass panes before. However, I don't think those apply to my case. I actually want the UI to continue to respond to inputs. The busy cursor is simply to indicate that not all of the background work has completed, but you can still click on things, and that's okay. In my case, we seem to have a Mac-specific bug where JScrollPane interferes with its parent's cursor, even though it doesn't set a cursor of its own. – Timothy Miller Oct 04 '11 at 21:03
  • That all being said, one might have an argument to make that I'm using the wrong visual cue. But that's a completely separate discussion. :) – Timothy Miller Oct 04 '11 at 21:05