Incorrect cursor when outside of modal JDialog?

醉酒当歌 提交于 2019-12-24 16:00:13

问题


When using the method setCursor(), to change the cursor used by a component, everything works fine for all components, including JFrame and JDialog.

The problem here resides with a modal JDialog. When the mouse is inside the dialog, the cursor is displayed right. But, when the mouse is moved outside the dialog, the cursor is re-set to the OS default, even though the underlying JFrame is using the same custom cursor as the dialog.

I've searched A LOT, and found some related questions, but none has a correct answer to this.

I'm using Windows 10; JDK 1.8.0_40.

SSCCE:

package br.shura.knockback;

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

public class DialogCursorSSCCE extends JFrame {
  public DialogCursorSSCCE() {
    Cursor cursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
    JButton button = new JButton("Click me to open dialog.");

    button.addActionListener(event -> {
      JDialog dialog = new JDialog();
      JLabel label = new JLabel("Move the mouse outside this dialog.");
      int width = label.getFontMetrics(label.getFont()).stringWidth(label.getText());

      label.setPreferredSize(new Dimension(width + 10, 50));
      dialog.add(label);
      dialog.pack();
      dialog.setCursor(cursor);
      dialog.setLocationRelativeTo(button);
      dialog.setModal(true);
      dialog.setTitle("Dialog");
      dialog.setVisible(true);
    });
    button.setAlignmentX(CENTER_ALIGNMENT);
    button.setMaximumSize(new Dimension(400, 100));
    setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
    add(button);
    setCursor(cursor);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setExtendedState(MAXIMIZED_BOTH);
    setTitle("DialogCursorSSCCE");
  }

  public static void main(String[] args) {
    new DialogCursorSSCCE().setVisible(true);
  }
}

回答1:


But, when the mouse is moved outside the dialog, the cursor is re-set to the OS default,

My guess at to what is happening is that the border of a dialog is an OS peer component. So when you leave the Swing component the mouse over event is generated for the OS peer so the cursor is set to the OS default.

When you leave the dialog completely the frame doesn't receive any events since the dialog is modal so the system cursor is still displayed while over the frame.

Note how the cursor changes when it enters the title bar.

Now try the following:

JDialog dialog = new JDialog();
dialog.setUndecorated(true);
dialog.getRootPane().setWindowDecorationStyle(JRootPane.PLAIN_DIALOG);

Now the cursor will only change when it reaches the border because Swing is painting the title bar.

I also tried:

  JDialog dialog = new JDialog();
  dialog.setUndecorated(true);

So there are no decorations but the cursor still changes when it leaves the window.

I don't know any way around it.




回答2:


I hate 'impossible' answers - actually I have to do that. So here are workarounds I found:

1) Use ProgressMonitor (like here: https://docs.oracle.com/javase/tutorial/uiswing/components/progress.html) - not my case as I have to strongly customize it

2) simulate modality. Here is basic idea on what I mean (yes, I know I don't lock main Frame's GUI this way - you can make that too - lucky me: not my case)

        mainFrame.addComponentListener(componentListener = new ComponentListener() {
            @Override
            public void componentShown(ComponentEvent e) {
            }

            @Override
            public void componentResized(ComponentEvent e) {
                placeDialog();
            }

            @Override
            public void componentMoved(ComponentEvent e) {
                placeDialog();
            }

            @Override
            public void componentHidden(ComponentEvent e) {
            }
        });
        ((Window) mainFrame).addWindowListener(windowListener = new WindowListener() {
            @Override
            public void windowOpened(WindowEvent e) {
                placeDialog();
            }

            @Override
            public void windowIconified(WindowEvent e) {
                dialog.setVisible(false);
            }

            @Override
            public void windowDeiconified(WindowEvent e) {
                placeDialog();
            }

            @Override
            public void windowDeactivated(WindowEvent e) {
            }

            @Override
            public void windowClosing(WindowEvent e) {
            }

            @Override
            public void windowClosed(WindowEvent e) {
                closeDialog();
            }

            @Override
            public void windowActivated(WindowEvent e) {
                placeDialog();
            }
        });
    }

    private void placeDialog() {
        dialog.setVisible(true);
        dialog.requestFocus();
        dialog.setLocation(mainFrame.getLocation().x + mainFrame.getWidth()/2 - dialog.getWidth()/2,
        mainFrame.getLocation().y + mainFrame.getHeight()/2 - dialog.getHeight()/2);
    }

Don't forget removing listeners from mainFrame after all (like in Finally section)

Hoping for lot's of downvoters with better solutions :)



来源:https://stackoverflow.com/questions/32929557/incorrect-cursor-when-outside-of-modal-jdialog

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!