Problem elaboration
This problem arises when using an AWT or Swing app under the Cygwin X11
server. It only happens if you call setLocation
or
setLocationRelativeTo
while constructing the window object. (But if
you do not call setLocation
, then the window appears in the top-left
corner, which is inconvenient.)
The main symptom is: when a window or dialog box is first shown, and
the user tries to move it by dragging the title bar, after the drag
finishes, the window jumps back to its original position. After that,
moving the window works normally.
Another curious symptom is: after attempting to drag once, and getting "rejected", the menu bar behaves differently, with each menu only remaining open while the mouse button is held down (like old X11 apps). Additionally there is an "offset" to the mouse's effect, with the highlighted menu item not being the one under the cursor, but a distance away related to the attempted drag distance. Moving the window a second time allows the menu to work normally, staying open after pressing and releasing the mouse button, and with no offset effect.
Solution
The solution I found is to programmatically adjust the window's position
slightly just after it is first shown:
// Call this *before* 'setVisible(true)'.
f.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
public void windowOpened(java.awt.event.WindowEvent e) {
final java.awt.Window w = e.getWindow();
javax.swing.Timer t = new javax.swing.Timer(100 /*ms*/,
new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent e) {
// Slightly adjust the window position to work around a
// bug where the window manager "rejects" the first
// attempt to move the window.
int y = w.getLocation().y;
w.setLocation(w.getLocation().x, y+1);
}
});
t.setRepeats(false);
t.start();
}
});
As noted, this must be called before setVisible(true)
, otherwise you
might miss the windowOpened
event. This is also important when using
a JDialog
because, in that case, setVisible(true)
blocks until the
dialog is closed.
Moving by 1 pixel is chosen to minimize disruption, since the adjustment
is usually visible to the user. I say usually because sometimes (maybe
1 in 4 times) the adjustment does not happen, even though it
successfully disarms the underlying bug. Moving by 0 pixels does not
disarm the bug.
The timer is unfortunately necessary to avoid losing a race condition
with the window manager. In my testing, without the timer, the window
will still jump to its original position about 1 in 7 times when the
user first moves it. With a 100 ms timer, I never observed it failing
(after more than 100 tests), but 50 ms is not enough. This will depend
on machine speed of course.
Versions: Java 1.8.0_92 on Linux and cygwin 3.4.7 on Windows 10.
pack()
in order to cause the GUI to be laid out and validated. 3)f.setSize(250, 250);
in counter-productive. Set the size of the GUI according to the rows and columns needed for the text area. – Foreyard