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:
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.
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:
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?
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.
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);
}
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).
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
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.
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.
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.
I found one more workaround. When JDialog is invoked from JavaScript it has a focus.
Create an applet's method which will show a dialog
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
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();
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.
Related
We have an javafx application developed mainly in java 1.7 and tested in 1.8. It was running fine until java 1.8u35. Now we discovered, JavaFx windows are not going to open in 1.8u40 after upgrade. Even worse, the modal windows are blocking the entire tab/ browser of being used. So the user is just able to close the browser using the task manager.
We use javafx.embed.swing.JFXPanel to embed jfx-code into swing legacy code.
I have completely no clue what might be the problem, as there are no errors displayed in client's java console.
UPDATE:
I reviewed the known issues list for java1.8 here. The only thing I would probably link to our issue is this bug:
BUG-RT-32597: The SwingNode class does not support High DPI displays.
So I tried lowering the screen resolution (1280x1024 to 800x600) but without success.
Does anyone faced a similar issue before and knows what might help?
UPDATE:
I tried to better track down the problem but with not much luck.
To make it more visible, this is basically whats going on on window loading:
public static void initWindow(JDialog dialog){
final JFXPanel jfx = new JFXPanel();
Platform.runLater(new Runnable() {
public void run() {
System.out.println("JFXPanel");
}
});
Runnable r = new Runnable() {
public void run() {
AnchorPane root = new AnchorPane;
//... do some content loading
Scene scene = new Scene(root,width,height);
System.out.println("test");
}
};
dialog.add(jfx);
System.out.println("added jfx panel.");
dialog.pack();
System.out.println("packed jfx panel.");
dialog.setLocationRelativeTo(null);
System.out.println("loaded.");
}
I thought execution is going to stop somewhere, but its running through the entire function as usual. Nevertheless the window is not showing up.
UPDATE:
Not completely correct, my last comment, as I found out:
Around the above function, the following happens:
initWindow(this); //this is extending java.swing.JDialog
System.out.println("this comment is printed to console");
super.setVisible(true); //this is not executed properly. if removed, browser will not be blocked, but window doesnt show up either
System.out.println("this comment is not printed to console";
So, in general, there is the JDialog which gets packed with a JfxPanel. When calling setVisible() method from the JDialog class, the Application gets blocked but the window doesnt show up. Actually, in the thumbnail screen (alt+tab) it is shown as a container inside the application.
When removing the setVisible call, the browser does not get blocked, but also the window does not show up. Unfortunately, I did not find the JDialog class code to look up, what's going on inside setVisible().
Any ideas, what might be wrong with our setup or the setVisible method?
We had a similar problem. While comparing the Java sources of 1.8.0_31 and 1.8.0_45 we found out that there were some changes in the JFXPanel source code introduced with 1.8.0_45 that may cause problems in the following situation:
initialize modal JDialog with JFXPanel (executed on Swing's EDT)
initialize and set FX scene on JFXPanel in FX task (executed on FX Thread)
wait for FX task to be finished (wait on EDT)
pack() and show() JDialog (continued on EDT, blocks program execution)
continue with program execution after user closed JDialog (on EDT)
We use this workflow in order to wait for some user input being shown in a new modal JDialog and continue normal programm execution on EDT afterwards.
In 1.8.0_31, the preferred size of JFXPanel seems to be set in FX thread which allows JDialog.pack() to determine the correct bounds.
In 1.8.0_45, the preferred size of JFXPanel seems to be set not in FX thread anymore but in EDT after all pending AWT events are executed. So, when (4) is executed, JDialog.pack() does not know about the preferred size of the scene. As a consequence, the dialog has no content, or does not show up if undecorated as described in the original question above.
Here is a complete example to reproduce the different behaviour:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// create JDialog on EDT
final JDialog dialog = new JDialog((JDialog)null, "JDialog");
// initialize FX platform and create JFXPanel
final JFXPanel jfxPanel = new JFXPanel();
// add resize listener for JFXPanel
jfxPanel.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e) {
// not called in 1.8.0_45
System.out.println("JFXPanel.getSize(): "+jfxPanel.getSize());
}
});
// set FX Scene on JFXPanel and wait until finished
runAndWait(new Runnable() {
public void run() {
Text text = TextBuilder.create().text("JavaFx content").y(20).build();
Group root = new Group(text);
Scene scene = new Scene(root);
jfxPanel.setScene(scene);
}
});
// show undecorated modal JDialog with FX content
System.out.println("JFXPanel.getPreferredSize(): "+jfxPanel.getPreferredSize());
dialog.setUndecorated(true);
dialog.add(jfxPanel);
dialog.pack();
dialog.setModal(true);
System.out.println("JDialog.setVisible()");
dialog.setVisible(true);
}
});
}
private static void runAndWait(Runnable r) {
try {
FutureTask<Object> task = new FutureTask<Object>(r, null);
Platform.runLater(task);
task.get();
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
When running this program, componentResized() is called only in 1.8.0_31 but not in 1.8.0_45.
A possible fix while keeping the synchronous program workflow on EDT is to replace JDialog.pack() with JDialog.setSize(...), e. g. by setting a constant size or by using the size of the FX scene that could be determined using root.getBoundsInLocal().
I encountered the same behaviour described by #Peter using 1.8.0_121.
I was able to get dialog.pack() to work using a window listener.
dialog.addWindowListener( new WindowAdapter() {
#Override
public void windowOpened(WindowEvent e) {
((JDialog)e.getSource()).pack();
}
});
simple question here but can't find any resources to help me.
In an ActionListener I've made I bring up a new window. I've guessed you keep the main window visibility to false. but how do I close the newly opened window and set the visibility of the old window back to true?
here is my main window which brings up the new window.
JFrame window = new JFrame();
public void actionPerformed(ActionEvent e) {
window.setVisible(false);
Clock clock = new Clock(initialSize);
while(clock.isVisible()){
window.setVisible(false);
}
window.setVisible(true);
}
});
and here is my new windows ActonListener which closes that one and opens old window.
public void actionPerformed(ActionEvent ea){
jframe.setVisible(false);
}
I'm new to this, so it may be an obvious answer here which I am unaware of. the current code here is a desperate attempt. Thank you.
but how do I close the newly opened window and set the visibility of
the old window back to true?
add WindowListener to JFrame/JDialog
override windowClosing event, here you can to call setVisible(false) for JDialog and setVisible(true) for JFrame
change default close operation for JFrame/JDialog to HIDE or NOTHING_ON_CLOSE
setParent for JDialog
I decided to put "close" buttons on my JFrames. If I test just that JFrame by itself, it works fine, but when I try to close it after opening it through another class, it won't close.
Here is the code:
JButton btnClose = new JButton("Close");
btnClose.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
frame.dispose();
}
});
btnClose.setBounds(282, 666, 96, 50);
contentPane.add(btnClose);
I have tried using frame.dispose() and frame.close() and super.dispose() but the only one that works is system.exit(0); but then that exits the whole program.
The issue:
If I test the JFrame by itself, the frame closes fine.
If I open the program and navigate to that specific JFrame, the close button does nothing.
Please advise.
Did you add a display statement in the ActionListener to make sure the code is being executed?
If the code is being executed, then the problem is probably that the frame variable has in invalid reference.
You don't need to keep a reference to the frame. Instead use something like:
Window window = SwingUtilities.windowForComponent( e.getSource() );
window.dispose();
Use setVisible(false); on the frame. Then call dispose.
We have the following code running in an applet running in JRE 1.6.0_26.
Desktop.getDesktop().browse(new URI("www.google.com")
On some Windows 7 64-bit, IE8 machines this command opens two windows but not on others which should be the same setup.
Does anyone have any idea what would cause this?
I strongly suspect this was just some coding carelessness (which I'm as guilty of as anyone else).
Consider the following code:
JButton myButton1 = new JButton("Click 1");
myButton1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Desktop.getDesktop().browse(new URI("www.google.com"));
}
});
JButton myButton2 = new JButton("Click 2");
myButton1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Desktop.getDesktop().browse(new URI("www.google.com"));
}
});
Notice the bug here?
The intention was to assign one action to each button, but because of a careless copy/paste after the definition of myButton2, we mistakenly assign a second action to myButton1.
What will happen now is when the user clicks on myButton1, the first action will be invoked resulting in the display of a browser with www.google.com showing, and then the second action will be invoked resulting in either another tab in that window or a whole other window (depending on your browser setup).
I have problem currently for my swing reminder application, which able to minimize to tray on close. My problem here is, I need JOptionPane dialog to pop up on time according to what I set, but problem here is, when I minimize it, the dialog will pop up, but not in the top of windows when other application like explorer, firefox is running, anyone know how to pop up the dialog box on top of windows no matter what application is running?
Create an empty respectively dummy JFrame, set it always on top and use it as the component for the JOptionPane instead of null. So the JOptionPane remains always on top over all other windows of an application. You can also determine where the JOptionPane appears on screen with the location of the dummy JFrame.
JFrame frmOpt; //dummy JFrame
private void question() {
if (frmOpt == null) {
frmOpt = new JFrame();
}
frmOpt.setVisible(true);
frmOpt.setLocation(100, 100);
frmOpt.setAlwaysOnTop(true);
String[] options = {"delete", "hide", "break"};
int response = JOptionPane.showOptionDialog(frmOpt, msg, title, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, "delete");
if (response == JOptionPane.YES_OPTION) {
removeRow();
}
frmOpt.dispose();
}
Old post, but I was struggling with this.
My problem was more with Javafx allowing the JOptionPane to go behind the current Java window.
Therefore I used the following which does what the original poster asked by putting the JOptionPane in front of all windows; even JAVAFX.
Firstly the old JOptionPane:
JOptionPane.showMessageDialog(null, "Here I am");
Now an JOptionPane that stays in front:
final JDialog dialog = new JDialog();
dialog.setAlwaysOnTop(true);
JOptionPane.showMessageDialog(dialog, "Here I am");
And for fun here is everything in one long line:
JOptionPane.showMessageDialog(
((Supplier<JDialog>) () -> {final JDialog dialog = new JDialog(); dialog.setAlwaysOnTop(true); return dialog;}).get()
, "Here I am");
You can make a static method some where that will return the JDialog for you and then just call it in the JOptionPane to clean up your code a bit.
Are you using one of the canned JOptionPanes? (Like JOptionPane.showCOnfirmDialog(...))
You may want to look at extending JDialog and making your own dialog panel, and then calling myDialog.setAlwaysOnTop(true);
Windows is blocking this operation since XP.
The scenario before was like:
Your a tiping in some text in an editor and not recognize that another dialog is coming to front when you are tipping the text. The coming dialog gets the focus and you are tiping in the new dialog. Maybe you click enter after you are ready and do this in the wrong dialog, which is asking whether you realy want to delet your hard disk ;)
The come to front call in java is only working for java windows.
The possibibilty to notify the user of a new window is to implement a Frame, which will highlighted/flashing in the windows task bar.
Correction the post above..
I have resolve my problem as below:
this.setVisible(true); // show main frame
MyDialog dialog = New MyDialog(this, true); // show my custom dialog
dialog.setVisible(true);
this.setVisible(false);
it works fine for me :)
You might think about using a JFrame instead. It may give you a little more flexibility.
If you are using a JFrame and you want it to popup on top of the other windows use:
myFrame.setVisible(true);
myFrame.setState(Frame.NORMAL);
The setState will show the window to the user if it was in minimized state previously.