tags:

views:

382

answers:

2

I'm in the process of testing my Java application on the Mac and I've run into a very strange issue. Checkboxes that appear in a modal Dialog render incorrectly, though non-modal Dialogs work fine.

For example, say I have a window with 2 radio buttons. When the dialog opens the first one is selected. When I click on the second button it suddenly looks like both are selected. Clicking anywhere else within the dialog will cause the rendering to fix itself and only the selected button will be displayed.

The following code reproduces this for me:

package mactest;

import java.awt.Checkbox;
import java.awt.CheckboxGroup;
import java.awt.Dialog;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class Main {
  public static void main(String[] args) {
    boolean modal = false;
    if(args.length > 0) {
      modal = args[0].toLowerCase().equals("true");
    }

    TestDialog dlg = new TestDialog(new Frame(), modal);

    dlg.setVisible(true);
  }

  private static class TestDialog extends Dialog {
    private Checkbox cb1;
    private Checkbox cb2;

    private CheckboxGroup cbg;

    public TestDialog(Frame owner, boolean modal) {
      super(owner);

      cbg = new CheckboxGroup();

      cb1 = new Checkbox("One", true, cbg);
      cb2 = new Checkbox("Two", false, cbg);

      this.setLayout(new FlowLayout());
      this.add(cb1);
      this.add(cb2);

      this.setModal(modal);
      this.pack();

      this.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          TestDialog.this.setVisible(false);
          System.exit(0);
        }
      });
    }
  }
}

If I call this like so:

java -cp MacTest.jar mactest.Main false

the dialog is not modal and everything works fine. However, If I tell it to be modal:

java -cp MacTest.jar mactest.Main true

then the rendering issues occur.

I've tried every trick I can think of to try to fix the problem (invalidate, doLayout, requesting focus, explicitly setting the state of every button when one is selected, etc)., but so far the only thing I've come up with that works is to make the dialog not be modal. Unfortunately, that's not an option in my application.

In case it matters, this is on a MacBook running OS X 10.5 running Java 1.5.0_16.

A: 

Are you asking about checkboxes or radiobuttons? You mention both.

To create a multiple-exclusion scope you have to use ButtonGroup class

eugener
The class is "Checkbox", but it's used for both checkboxes and radio buttons (by adding them to a checkbox group they act and display as radio buttons). I don't believe AWT has a separate radio button class.
Herms
A: 

I found something that seems to fix it, though it's an ugly hack. I added item listeners to each of the Checkboxes. When those trigger I resize the window by 1 pixel, then do an invokeLater() on pack() (my dialogs aren't resizable). Here's the modified test code:

package mactest;

import java.awt.Checkbox;
import java.awt.CheckboxGroup;
import java.awt.Dialog;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Rectangle;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class Main {
  public static void main(String[] args) {
    boolean modal = false;
    boolean tweak = false;
    if(args.length > 0) {
      modal = args[0].toLowerCase().equals("true");
    }
    if(args.length > 1) {
      tweak = args[1].toLowerCase().equals("true");
    }

    TestDialog dlg = new TestDialog(new Frame(), modal, tweak);

    dlg.setVisible(true);
  }

  private static class TestDialog extends Dialog {
    private Checkbox cb1;
    private Checkbox cb2;

    private CheckboxGroup cbg;

    public TestDialog(Frame owner, boolean modal, boolean tweak) {
      super(owner);

      cbg = new CheckboxGroup();

      cb1 = new Checkbox("One", true, cbg);
      cb2 = new Checkbox("Two", false, cbg);

      this.setLayout(new FlowLayout());
      this.add(cb1);
      this.add(cb2);

      this.setModal(modal);
      this.pack();

      if(tweak) {
        cb1.addItemListener(new ItemListener() {
          public void itemStateChanged(ItemEvent e) {
            onSelection();
          }
        });
        cb2.addItemListener(new ItemListener() {
          public void itemStateChanged(ItemEvent e) {
            onSelection();
          }
        });
      }

      this.addWindowListener(new WindowAdapter() {
        public void windowClosing(WindowEvent e) {
          TestDialog.this.setVisible(false);
          System.exit(0);
        }
      });
    }

    private void onSelection() {
      Rectangle bounds = this.getBounds();
      this.setBounds(bounds.x, bounds.y, bounds.width + 1, bounds.height);
      EventQueue.invokeLater(new Runnable() {
        public void run() {
          TestDialog.this.pack();
        }
      });
    }
  }
}

For resizable components you'd need to store the size pre-tweak and restore that instead of calling pack().

In windows I could see the dialog flicker every once in a while when I changed the selection, though on the mac it wasn't noticeable at all.

Herms