Focus issues with java7 modal dialogs on mac osx

2020-04-03 00:39发布

问题:

I've been validating a swing application that runs on an applet for mac osx.

During this validation I found the following issues with the modal dialogs:

  1. When a dialog is open and is setModal(true) it blocks the content of the root window, but if you click somewhere on the root window, the dialog goes under it, but it should remain on the top of the root window.
  2. If the dialog has a JTextInputField it does not receive focus even when you click on it.

So I created a small program to show the problem. Can you please help me to understand what is wrong here?

package com.macosx.tests;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.*;

public class DialogExample extends JApplet{

    private static final long serialVersionUID = 1L;
    private JPanel panel;
    private JButton openDialogBtn;

    private void doStart() {
        panel = new JPanel();
        panel.setPreferredSize(new Dimension(500,500));

        openDialogBtn = new JButton("open dialog");
        openDialogBtn.addActionListener(new ActionListener(){

            @Override
            public void actionPerformed(ActionEvent arg0) {
                ModalDialog dialog = new ModalDialog(panel, true);
                dialog.setVisible(true);
            }

        });
        panel.add(openDialogBtn);
        setContentPane(panel);
    }


    class ModalDialog extends JDialog {
        private static final long serialVersionUID = 1L;

        public ModalDialog(Component parent, boolean modal) {
            Dimension dimensionParentFrame = parent.getSize();
            setSize(new Dimension((parent == null) ? 300 : dimensionParentFrame.width / 2, 75));

            setModal(modal);
            setModalityType(ModalityType.APPLICATION_MODAL);

            JTextField txtField = new JTextField();
            add(txtField, BorderLayout.CENTER);
        }
    }


    @Override
    public void start() {
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                public void run() {
                    doStart();
                }
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

Use the above to create a .jar file (test.jar). Once that is done, create a html file with the following content:

<html>
<head>
<title>Dialog test Applet</title>
</head>
<body>
<applet id="DialogTestApplet" height="800" width="600"
  code="com.macosx.tests.DialogExample"
  archive="test.jar">
</applet>
</div>
</body>
</html>

When this is done, run the html file. You'll see an applet with a gray background and with a button. Then try to:

  1. click on the button to open the dialog. After that, click somewhere on the gray area: the dialog goes under the browser window but it should remain on the top, right?
  2. click on the button to open the dialog. After that click on the textfield of the dialog and try to write something: the textdialog does not receive focus.

So, what am I doing wrong here? Can someone with a mac computer test this please?

Thanks

Specs:

java.vendor    Oracle Corporation
java.version   1.7.0_07
os.name        Mac OS X
os.version     10.7.4

browser        firefox 15

NOTE: please note that this is only happening when the applet runs on the browser and only on mac osx.

回答1:

I found another workaround. When the window is opened, show an optionpane for a few milliseconds and close it. It give the focus to the optionpane and then back to the dialog, allowing to ignore the bug.

Add this snipet of code to your dialog constructor and it should work:

addWindowListener(new WindowAdapter(){
public void windowOpened(WindowEvent e){
    JOptionPane pane = new JOptionPane();
    final JDialog dialog = pane.createDialog("Please Wait");
    Timer timer = new Timer(50, new ActionListener() {
        public void actionPerformed(ActionEvent e) {
            dialog.dispose();
        }
    });
timer.setRepeats(false);
timer.start();
dialog.setVisible(true);
}


回答2:

You should put an "owner" window on your ModalDialog. To do that, you must call super(owner) in your ModalDialog constructor and you can retrieve the parent window of your component parent with SwingUtilities.getWindowAncestor(parent).



回答3:

  • not Mac/OSX user but this is common issue about Focus and JDialog,

  • there are another issues in the case that JDialog is created on Runtime,

  • Focus is asynchronous based on properties came from Native OS

  • create this JDialog only once time and re_use this container for another action

  • JDialog#setVisible should be wrapped into invokeLater() too

  • is possible to force the Focus by JTextField#setText(JTextField#getText()) wrapped into invokeLater()

  • there is Dialog Focus, one of great workaround by @camickr



回答4:

I confirm, I have the same bug with an old applet running in JDK7 on OS X. As the poster mentioned, the bug is seen only with the applet running in the browser (ff) and not with the appletviewer.



回答5:

I can verify that this is a problem for Java 1.7 Update 7+ on the Safari 6 and Firefox running on Mountain Lion. Curiously it is not a problem on earlier versions of Safari that run on Lion but it is a problem in Firefox on the older OS. I am pretty desperate to find a fix for this as a number of my applet users are on Macs. One workaround that I have found (that is not sufficient by any means) is to minify the window and then reopen it. The textfields/textareas then become editable. Hopefully, we can find a better solution that gets around this annoying requirement.



回答6:

I experienced the same problem on Mac with Java 7 update 9 with Safari and Firefox. When I opened a JDialog which contained a JTextField the JTextField was inaccessible.

I did find a solution. I inserted a delay from when the user pressed the “show dialog button” to executing the code that shows the button.

For example:

ActionListener al = new ActionListener(){
    public void actionPerformed(ActionEvent ae){
        TitleDialog dialog = new TitleDialog(main.findParentFrame()); // My JDialog which contains a JTextField.
        dialog.setVisible(true);
    }
};
javax.swing.Timer timer = new javax.swing.Timer(1000, al);
timer.setRepeats(false);
timer.start(); 

I experienced that if the delay was to short the solution would not work.

If one uses SwingUtilities.invokeLater instead of javax.swing.Timer it will not work. Maybe the delay of SwingUtilities.invokeLater is too short.



回答7:

I found one more workaround. When JDialog is invoked from JavaScript it has a focus.

  1. Create an applet's method which will show a dialog
  2. Call this method from JavaScript.

Hope, it helps. By the way, web start samples from Java tutorial have the same issue http://docs.oracle.com/javase/tutorial/uiswing/components/textfield.html



回答8:

I want to use the workaround above (to open dialog from the dialog), but without showing any dialog. Here is a code for not visible dialog.

final JDialog dialog = new JDialog();
dialog.setUndecorated(true);
dialog.setSize(0, 0);
dialog.setModal(true);
dialog.pack();


回答9:

I have found a solution.

GetDirectory varGetDirectory = new GetDirectory(new JFrame(),true);
varGetDirectory.setVisible(true);       

GetDirectory is JDialog containing a JFileChooser.

The weird thing is that all JDialog object should be called using new JFrame() as parent, otherwise clicking from one parent window, will bring the top modal JDialog backwards in the zOrder and somehow it cannot be set on top anymore.

My problem was the same as above. When I have created the JDialog from another JDialog, the new dialog appeared behind the other.

To bring it to top I have set the parent of all JDialogs as described above and it worked according to what expected.