I have a JDialog which popup whenever my main application recieves an event.
The problem I'm facing is that the dialog pops up evenif the main window is minimised. A similar question was asked here but no answer was given as to how to solve this except a link to sun's guide on focus handling Hide JDialog window when the window lost focus.
Suppose I have the function createandshowDialog() such as
public void createAndShowDialog(boolean manualLaunch) {
if (manualLaunch || shouldShowMessage()) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
if (dialog == null) {
dialog = new xyzDialog(getPendingList());
}
GUIHelper.flash(PLAFHelper.getMainFrame(), true, false);
dialog.setVisible(true);
}
});
}
}
And the xyzDialog class is defined as :
public class xyzDialog extends SimpleStandardDialog
{
protected final static Log logger = LogFactory.getLog(xyzDialog.class);
private xyzPanel panel;
public xyzDialog(ObjectArrayList list) {
super(PLAFHelper.getMainFrame(), "Pending Cancel/Replace");
initializeLocalVars();
panel = new xyzPanel(list, mediator);
super.setSize(750,600);
setResizable(true);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setModal(false);//todo: for testing...
if(PUMAGUIOptions.cFocusOnxyzPopup){
setFocusableWindowState(true);
validate();
}
else{
setFocusableWindowState(false);
validate();
}
}
The default behaviour should be such that it should not popup if main window is minimised or we explicitly set cFocusOnxyzPopup as false to force this default behaviour (which is the case when it is open on say secondary monitor and we are working on primary monitor or application is maximised or is in background i.e. is not the focusOwner.
I have set focusableWindowState as false so that it would not satisy the condition for gaining focus and return isFocusable as false if invoked as given in java-docs. But this is not working. Any suggestions?
USe JFrame's method
public synchronized int getExtendedState()
e.g. PLAFHelper.getMainFrame().getExtendedState()
if it's JFrame.ICONIFIED skip the dialog opening.
I have meet the same problem and search the stackoverflow ! finally, I find out the
corrent answer answered by Markus Lausberg in
JDialog lets main application lose focus
just calling the following method during dialog create:
setFocusableWindowState(false);
setFocusable(false);
Related
I'm programming a JToggleButton to load to/discard from memory the configuration of an element (a telescope config), so I've added a JComboBox in a JFrame and near it the button to load the selected item. When the JToggleButton is selected, an hard disk icon is displayed, another icon if otherwise. I'm using the IntelliJ IDEA GUI editor for that. Of course, I've added an ItemListener (as suggested from the web) to that button:
loadTelescopeButton.setSelected(true);
System.out.println(loadTelescopeButton.isSelected());
loadTelescopeButton.addItemListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
System.out.println("LAODACTION " + loadTelescopeButton.isSelected());
try {
if (e.getStateChange() == ItemEvent.SELECTED) {
String selected = telescopesList.getSelectedItem().toString();
if ((selected != null) && (!selected.equals("")) && (ObjUtils.isAlphaNumeric(selected))) {
//...
} else {
showErrorMessage("Invalid id selected!");
}
} else if (e.getStateChange() == ItemEvent.DESELECTED) {
if ((configurationActivity != null) && (configurationActivity.getManager() != null) &&
(configurationActivity.getTelescope() != null) && (configurationActivity.getTelescope().isConnected())) {
//...
} else {
//...
}
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
Output:
true
-> When the window is displayed
LAOD_ACTION false
-> When I click the button
I've made some tests with some new toggle buttons and they gave me same error: the code inside itemStateChanged(ItemEvent e) {...} is repeated forever, without stopping! In that piece of code there are no for and while loops! The result is a great number of message dialogs (only one dialog should be displayed), and if I focus another window in my desktop the screen behind the dialogs becomes black (the area of the parent window). I changed the listener to ActionListener and now everything is executed one time/click.
Why this error? I've copied that code from https://stackoverflow.com/a/7524627/6267019, as you can see.
Full code on GitHub Here, I've highlighted the code for that toggle button. The same error happens with other JToggleButtons in my MainActivity.java file, and also when debugging IntelliJ lets me see that the code in the listener is repeated forever. After some thousand of dialogs Windows shows me a message and closes Java Platform Binary with an error.
EDIT:
The same problem in a new class:
import javax.swing.*;
import java.awt.*;
public class ErrorGUI extends JFrame {
public ErrorGUI() throws HeadlessException {
super("ciao");
JPanel panel1 = new JPanel();
setContentPane(panel1);
JToggleButton ciaoToggleButton = new JToggleButton("cajs");
ciaoToggleButton.setSelected(true);
ciaoToggleButton.addItemListener(e -> {
System.out.println("caiooasfsdvn");
try {
JOptionPane.showMessageDialog(panel1, "skjngksfnb");
} catch (Exception e2) {
e2.printStackTrace();
}
});
panel1.add(ciaoToggleButton);
pack();
setVisible(true);
}
public static void main(String[] args) {
new ErrorGUI();
}
}
Whenever you open a modal dialog, the opening method call will return only after the dialog has been closed. This is crucial for the dialogs that return an entered value or choice.
This implies that while the dialog is open, a new event handling loop has to be started to react on the input in the dialog.
So when you open a modal dialog from a listener, you are stopping the handling of the current event and start processing of subsequent events, which can disturb the handling of the current event significantly. Most notably, the button will suddenly loose the focus when the new dialog is opened.
The nested event handling can be easily demonstrated by changing the listener to
ciaoToggleButton.addItemListener(e -> {
System.out.println("entering");
JOptionPane.showMessageDialog(panel1,
e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected");
System.out.println("leaving");
});
which will print sequences of
entering
entering
leaving
leaving
showing how the contradicting event is generated while the processing of the old one hasn’t completed.
As said by others, you can fix this by opening the dialog after the completion of the even handling, like
ciaoToggleButton.addItemListener(e -> {
System.out.println("entering");
EventQueue.invokeLater(() -> JOptionPane.showMessageDialog(panel1,
e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected"));
System.out.println("leaving");
});
or you enforce a non-modal dialog:
ciaoToggleButton.addItemListener(e -> {
System.out.println("entering");
JDialog d = new JOptionPane(
e.getStateChange()==ItemEvent.SELECTED? "selected": "deselected",
JOptionPane.INFORMATION_MESSAGE)
.createDialog(panel1, UIManager.getString("OptionPane.messageDialogTitle"));
d.setModal(false);
d.setVisible(true);
System.out.println("leaving");
});
(in a real application you would either keep the dialog for later reuse or call dispose after use)
Unfortunately, the danger of opening modal dialogs (or doing anything else that creates a secondary event loop) hasn’t been emphasized enough in the documentation. You can read everywhere that accessing Swing components from other threads can create inconsistencies, but starting new event handling loop while there are incompletely processed events can have a similar impact.
I can't say that I understand why your code is misbehaving, but I agree that what you're seeing doesn't quite make sense, and is likely due to the JOptionPane call somehow affecting the JToggleButton's state change. One way to get around this is by wrapping the JOptionPane call in a Runnable and queuing it on the Swing event queue via SwingUtilities.invokeLater(...). For example:
import javax.swing.*;
import java.awt.*;
#SuppressWarnings("serial")
public class ErrorGUI extends JFrame {
public ErrorGUI() throws HeadlessException {
super("ciao");
JPanel panel1 = new JPanel();
setContentPane(panel1);
JToggleButton ciaoToggleButton = new JToggleButton("cajs");
ciaoToggleButton.setSelected(true);
ciaoToggleButton.addItemListener(e -> {
System.out.println("caiooasfsdvn");
SwingUtilities.invokeLater(() -> {
JOptionPane.showMessageDialog(panel1, "skjngksfnb");
});
// JOptionPane.showMessageDialog(panel1, "skjngksfnb");
});
panel1.add(ciaoToggleButton);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
new ErrorGUI();
});
}
}
An interesting variation:
ciaoToggleButton.setSelected(true);
System.out.println("0:" + ciaoToggleButton.isSelected());
ciaoToggleButton.addItemListener(e -> {
System.out.println("1: " + ciaoToggleButton.isSelected());
if (e.getStateChange() == ItemEvent.SELECTED) {
JOptionPane.showMessageDialog(panel1, "skjngksfnb");
}
System.out.println("2: " + ciaoToggleButton.isSelected());
});
prints out:
0:true
1: false
2: false
1: true
1: false
2: false
2: false
1: true
1: false
2: false
2: false
I am making a game. It uses a jDialog because one Stack Overflow question said I had to use a modal jDialog to always be in focus. My jDialog has an outgoing chat field and a game window. The game window is a jPanel, the field a jTextField. When I press the arrow keys, I want that to go to the jPanel. When I type on the keyboard, I want that to go to the jTextField. When I press enter, I want the jTextField to clear. I do not want to click on the jTextField to make it in focus to type stuff in it and then click on the game screen to to move again.
My code looks like this:
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
jDialog_GUI.singleton_ = new jDialog_GUI(new javax.swing.JFrame(), true);
singleton_.requestFocusInWindow();
singleton_.setVisible(true);
singleton_.addWindowListener(new java.awt.event.WindowAdapter() {
#Override
public void windowClosing(java.awt.event.WindowEvent e) {
System.exit(0);
}
});
}
});
Constructor:
public jDialog_GUI(java.awt.Frame parent, boolean modal) {
super(parent, modal);
initComponents(); // initializes all the components using Swing GUI builder code
this.setModalityType(jDialog_GUI.ModalityType.APPLICATION_MODAL);
if (this.getModalityType() != jDialog_GUI.ModalityType.APPLICATION_MODAL) {
RunGame.printStackTraceAndCrashTheProgramBecause("Not modal.");
}
}
One of the other StackOverflow questions told me that if I use a jDialog and make it modal, then it will maintain focus, but when I do:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if(! jDialog_GUI.get_GUI().isFocusOwner() ) {
RunGame.printStackTraceAndCrashTheProgramBecause("JDialog must always be in focus.");
}
}
});
It crashes and prints "JDialog must always be in focus."
Even when I don't crash the program, none of the key presses will register in my jDialog's key listener.
addKeyListener(new java.awt.event.KeyAdapter() {
public void keyPressed(java.awt.event.KeyEvent evt) {
formKeyPressed(evt);
}
public void keyTyped(java.awt.event.KeyEvent evt) {
formKeyTyped(evt);
}
});
...
private void formKeyPressed(java.awt.event.KeyEvent evt) {
int keyCode = evt.getKeyCode();
System.out.println(keyCode + " " + KeyEvent.getKeyText(keyCode));
}
private void formKeyTyped(java.awt.event.KeyEvent evt) {
char c = evt.getKeyChar();
System.out.println(c);
}
...
Mouse presses register, and button presses register, but the key presses won't register. I even have a I want the focus to be on the frame and the frame to modify its components based on what keys are pressed rather than having one of the components hog all the key input from the other components. How do I fix this?
There is also a formWindowFocused listener that goes off when I run the program, but my jDialog's formFocusGained listener does not.
KeyEvents are dispatched to the component that has focus, so using a KeyListener is not a good solution.
For more flexibility in handling KeyStrokes you need to use Key Bindings. You can map a KeyStroke to an Action even when a component doesn't have focus.
Read the section from the Swing tutorial on How to Use Key Bindings for more information and examples.
I have a method that returns a certain int variable, but this variable should be modified by the user using a JFrame that pops out when this method is called before it's returned.
So I thought of using a timer that would delay the return statement by certain a more-than-needed number of seconds and when the button for example is pressed, the timer would stop and the variable would change
Here is the method:
public static int c(){
x.setVisible(true);// x is the name of the frame
timer.schedule(new TimerTask() {
public void run() {
System.out.println("Text");
}
}, 5000);
return q;
}
And here is the ActionListener set on the button in the constructor:
d.addActionListener(new ActionListener(){ //d is the name of the button
#Override
public void actionPerformed(ActionEvent arg0) {
q=5;
timer.cancel();
x.setVisible(false);
}
});
But all it does is delaying the printing statement inside the run method, and of course i cannot return inside the delayed task since its type is void
Thanks
but this variable should be modified by the user using a JFrame that pops out when this method is called before it's returned
An application should only have a single main JFrame (see: The Use of Multiple JFrames: Good or Bad Practice?). Child windows should be a model JDialog. Then when show the dialog, the code after the setVisible() statement will not execute until the dialog is close.
You can create you own custom JDialog or it may be easier to use a JOptionPane. See the section from the Swing tutorial on How to Make Dialogs for more information and examples.
In a Swing (J)Dialog, setModal sets the modality - that is, whether the dialog should block input to other windows or not. Then, setVisible docs say for modal dialogs:
If the dialog is not already visible, this call will not return until the dialog is hidden by calling setVisible(false) or dispose.
Indeed, setVisible does return right away if the dialog is not modal. Sample code:
JDialog jd = new JDialog();
jd.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
/**
* If set to false, setVisible returns right away.
* If set to true, setVisible blocks until dialog is disposed.
*/
jd.setModal(false);
System.out.println("setting visible");
jd.setVisible(true);
System.out.println("set visible returned");
I want to make a dialog that doesn't block input to other windows, but still does block the caller. What is a good way to do this, now that setVisible doesn't block when the dialog is not modal?
Is there some rationale why setVisible's behavior depends on the modality?
I need to make a dialog that doesn't block input to other windows, but does block the caller so that I know when the dialog has been closed.
I usually solve this not by blocking the caller, but by using a callback of some sort - a simple interface that the dialog invokes when it's done. Let's say your dialog has an "OK" and a "Cancel" button and you need to distinguish which one is pressed. Then you could do something like this:
public interface DialogCallback {
void ok();
void cancel();
}
public class MyModelessDialog extends JDialog {
private final DialogCallback cbk;
private JButton okButton, cancelButton;
public MyModelessDialog(DialogCallback callback) {
cbk = callback;
setModalityType(ModalityType.MODELESS);
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
onOK();
}
};
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
onCancel();
}
};
// Treat closing the dialog the same as pressing "Cancel":
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
onCancel();
}
};
}
private void onOK() {
cbk.ok();
}
private void onCancel() {
cbk.cancel();
}
}
Then you just pass in an instance of DialogCallback to the constructor:
MyModelessDialog dlg = new MyModelessDialog(new DialogCallback() {
public void onOK() {
// react to OK
}
public void onCancel() {
// react to Cancel
}
});
EDIT
Is there some rationale why setVisible's behavior depends on the modality?
Well, that's just how how modal windows are supposed to work, no? A modal window should block the current workflow when displayed, and a non-modal/modeless should not. See e.g. the Wikipedia pages on modal windows or dialog boxes.
Just put this.setModal(true) but not set the parent dialog on the constructor:
MyDialog dlg = new JDialog();
this.setModal(true);
When you call setVisible(true), it wont stop
The direct approach is by:
JDialog dialog = new JDialog(owner, ModalityType.DOCUMENT_MODAL);
I have found another way to do this. In the constructor of my progress bar, which extends javax.swing.JDialog, I added:
setModalityType(ModalityType.APPLICATION_MODAL);
I then overrode the setVisible method:
#Override
public void setVisible(boolean b) {
if (b) {
new Thread(new Runnable() {
#Override
public void run() {
showProgress();
}
}).start();
} else {
super.setVisible(false);
}
}
In the run(), you can see a call to showProgress(). This simply is:
public void showProgress() {
super.setVisible(true);
}
What happens here, is the setVisible() method of JDialog blocks. So I overrode it, and called setVisible() of the JDialog, in a thread. Resulting in it not blocking.
What is the proper way to terminate a Swing application from the code, and what are the pitfalls?
I'd tried to close my application automatically after a timer fires. But just calling dispose() on the JFrame didn't do the trick - the window vanished but the application did not terminate. However when closing the window with the close button, the application does terminate. What should I do?
Your JFrame default close action can be set to "DISPOSE_ON_CLOSE" instead of EXIT_ON_CLOSE (why people keep using EXIT_ON_CLOSE is beyond me).
If you have any undisposed windows or non-daemon threads, your application will not terminate. This should be considered a error (and solving it with System.exit is a very bad idea).
The most common culprits are java.util.Timer and a custom Thread you've created. Both should be set to daemon or must be explicitly killed.
If you want to check for all active frames, you can use Frame.getFrames(). If all Windows/Frames are disposed of, then use a debugger to check for any non-daemon threads that are still running.
I guess a EXIT_ON_CLOSE
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
before System.exit(0) is better since you can write a Window Listener to make some cleaning operations before actually leaving the app.
That window listener allows you to defined:
public void windowClosing(WindowEvent e) {
displayMessage("WindowListener method called: windowClosing.");
//A pause so user can see the message before
//the window actually closes.
ActionListener task = new ActionListener() {
boolean alreadyDisposed = false;
public void actionPerformed(ActionEvent e) {
if (frame.isDisplayable()) {
alreadyDisposed = true;
frame.dispose();
}
}
};
Timer timer = new Timer(500, task); //fire every half second
timer.setInitialDelay(2000); //first delay 2 seconds
timer.setRepeats(false);
timer.start();
}
public void windowClosed(WindowEvent e) {
//This will only be seen on standard output.
displayMessage("WindowListener method called: windowClosed.");
}
Try:
System.exit(0);
Crude, but effective.
May be the safe way is something like:
private JButton btnExit;
...
btnExit = new JButton("Quit");
btnExit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
Container frame = btnExit.getParent();
do
frame = frame.getParent();
while (!(frame instanceof JFrame));
((JFrame) frame).dispose();
}
});
The following program includes code that will terminate a program lacking extraneous threads without explicitly calling System.exit(). In order to apply this example to applications using threads/listeners/timers/etc, one need only insert cleanup code requesting (and, if applicable, awaiting) their termination before the WindowEvent is manually initiated within actionPerformed().
For those who wish to copy/paste code capable of running exactly as shown, a slightly-ugly but otherwise irrelevant main method is included at the end.
public class CloseExample extends JFrame implements ActionListener {
private JButton turnOffButton;
private void addStuff() {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
turnOffButton = new JButton("Exit");
turnOffButton.addActionListener(this);
this.add(turnOffButton);
}
public void actionPerformed(ActionEvent quitEvent) {
/* Iterate through and close all timers, threads, etc here */
this.processWindowEvent(
new WindowEvent(
this, WindowEvent.WINDOW_CLOSING));
}
public CloseExample() {
super("Close Me!");
addStuff();
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
CloseExample cTW = new CloseExample();
cTW.setSize(200, 100);
cTW.setLocation(300,300);
cTW.setVisible(true);
}
});
}
}
If I understand you correctly you want to close the application even if the user did not click on the close button. You will need to register WindowEvents maybe with addWindowListener() or enableEvents() whichever suits your needs better.
You can then invoke the event with a call to processWindowEvent(). Here is a sample code that will create a JFrame, wait 5 seconds and close the JFrame without user interaction.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class ClosingFrame extends JFrame implements WindowListener{
public ClosingFrame(){
super("A Frame");
setSize(400, 400);
//in case the user closes the window
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
//enables Window Events on this Component
this.addWindowListener(this);
//start a timer
Thread t = new Timer();
t.start();
}
public void windowOpened(WindowEvent e){}
public void windowClosing(WindowEvent e){}
//the event that we are interested in
public void windowClosed(WindowEvent e){
System.exit(0);
}
public void windowIconified(WindowEvent e){}
public void windowDeiconified(WindowEvent e){}
public void windowActivated(WindowEvent e){}
public void windowDeactivated(WindowEvent e){}
//a simple timer
class Timer extends Thread{
int time = 10;
public void run(){
while(time-- > 0){
System.out.println("Still Waiting:" + time);
try{
sleep(500);
}catch(InterruptedException e){}
}
System.out.println("About to close");
//close the frame
ClosingFrame.this.processWindowEvent(
new WindowEvent(
ClosingFrame.this, WindowEvent.WINDOW_CLOSED));
}
}
//instantiate the Frame
public static void main(String args[]){
new ClosingFrame();
}
}
As you can see, the processWindowEvent() method causes the WindowClosed event to be fired where you have an oportunity to do some clean up code if you require before closing the application.
Take a look at the Oracle Documentation.
Starting from JDK 1.4 an Application terminates if:
There are no displayable AWT or Swing components.
There are no native events in the native event queue.
There are no AWT events in java EventQueues.
Cornercases:
The document states that some packages create displayable components without releasing them.A program which calls Toolkit.getDefaultToolkit() won't terminate. is among others given as an example.
Also other Processes can keep AWT alive when they, for what ever reason, are sending events into the native event queue.
Also I noticed that on some Systems it takes a coupple of seconds before the Application actually terminates.
I think, the idea is here the WindowListener - you can add any code there that you'd like to run before the thing shuts down
In response to other comments, DISPOSE_ON_CLOSE does not seem to properly exit the application - it only destroys the window, but the application will continue running. If you want to terminate the application use EXIT_ON_CLOSE.