问题
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