Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Workaround for bug_id=4806603 regarding getBounds() and setBounds() on Linux?

Tags:

java

linux

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();
  }
}
like image 393
datahaki Avatar asked Aug 22 '16 17:08

datahaki


2 Answers

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?

like image 164
raner Avatar answered Nov 12 '22 11:11

raner


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.

like image 1
Edwin Buck Avatar answered Nov 12 '22 12:11

Edwin Buck