On the Linux platform, Frame::getBounds and Frame::setBounds do not work consistently. This has already been reported in 2003(!), see here:
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4806603
For convenience, I have simplified the stated code that results in a bug and paste it as:
import java.awt.Button;
import java.awt.Frame;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/** Demonstrates a bug in the java.awt.Frame.getBounds() method.
* @author Mirko Raner, PTSC
* @version 1.0 (2003-01-22) **/
public class GetBoundsBug extends Frame implements ActionListener {
public static void main(String[] arg) {
GetBoundsBug frame = new GetBoundsBug();
Button button = new Button("Click here!");
button.addActionListener(frame);
frame.add(button);
frame.setSize(300, 300);
frame.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent event) {
Rectangle bounds = getBounds();
bounds.y--;
setBounds(bounds);
bounds.y++;
setBounds(bounds);
}
}
Unexpected behavior: Upon clicking the button the window is shifted slightly below! (On my system by 28 pixels each click.)
Here is a screen recording: https://youtu.be/4qOf99LJOf8
This behavior has been around for 13+ years, so probably there won't be any change from the official side.
Does anybody have a workaround for this bug? Specifically, I would like to store and restore the window/frame/dialog at the previous location reliably on all platforms.
PS: My java installation is jdk1.8.0_102 for amd64 by Oracle on Ubuntu 16 Linux. Since I recently migrated from Windows to Ubuntu, I know that on Windows, the code above works as expected.
The adaptation to Swing using SwingWorker produces the same effect:
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingWorker;
public class GetBoundsBug extends JFrame implements ActionListener {
public static void main(String[] arg) {
GetBoundsBug myJFrame = new GetBoundsBug();
JButton myJButton = new JButton("Click here!");
myJButton.addActionListener(myJFrame);
myJFrame.setContentPane(myJButton);
myJFrame.setSize(300, 300);
myJFrame.setVisible(true);
}
@Override
public void actionPerformed(ActionEvent event) {
SwingWorker<Void, Void> mySwingWorker = new SwingWorker<Void, Void>() {
@Override
public Void doInBackground() {
Rectangle myRectangle = getBounds();
myRectangle.y--;
setBounds(myRectangle);
myRectangle.y++;
setBounds(myRectangle);
return null;
}
};
mySwingWorker.execute();
}
}
Well, in the original bug database entry this is marked as "Won't fix" and explained as a quirk in the window manager, rather than the JDK.
What window manager are you using?
Just an additional note. I noticed that your code failed to do very much on the Event Dispatch Thread. All of Java's drawing APIs are single threaded by design, meaning that your application should not be expected to work correctly unless you dispatch GUI updates to the Event Dispatch Thread.
This means you need to (in your main) create a new Runnable that when evaluated will present your widgets, and submit that to the EDT.
Also, your action listener updates the component state within the action, bypassing the repaint request and ignoring the typical safeties required to ensure dispatch to the EDT.
In both of these ways, you code is not valid GUI code in a Java environment, and the bug that you identify may have nothing to do with your behavior, as your program is violating the GUI toolkit design before you even know if the bug impacts it.
Also, awt only wraps components. If the components lie, then the lie trickles into Java. Not much can be done about that (but I no longer think it's the primary thing to worry about). If you don't like that, use Swing, which is a much more sane / stable environment.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With