In my java app, I've been using the popupMenuWillBecomeVisible() event to decide on which menu-items within my JPopupMenu will be enabled/disabled/visible/hidden, etc.
I've used the JPopupMenu as a right-click context menu of a JList, and I decide on the state of the menu-items depending on which item in the list was right-clicked.
This is all working fine. The only gotchya I have is for the case where the list is empty, or the right-click was triggered when no item was selected.
For this case, I was hoping that I could cancel the appearance of the JPopupMenu from within the popupMenuWillBecomeVisible() event, as that's where I currently perform my existing tests.
Is there a way to do this? Perhaps some sort of way to 'consume' the event?
If not, perhaps my only other option will be to try alternatives, such as moving the testing logic somewhere else, prior to the right-click.
Still, my preference at this stage was to keep the logic within popupMenuWillBecomeVisible(), unless that proves to be impossible.
Anyone got any ideas?
SwingUtilities.InvokeLater will wait until all AWT operations are finished before starting the runnable, which means this runnable will be called after the popupMenuWillBecomeVisible event is finished and the popup menu is visible or in queue to be painted.
final JPopupMenu popupMenu = new JPopupMenu();
popupMenu.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuWillBecomeVisible(final PopupMenuEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
((JPopupMenu)e.getSource()).setVisible(false);
}
});
}
Related
I am very new to Swing.
I have
itemActionButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg) {
itemAction();
}
});
But when the button is clicked, instead of running this action on another thread, I would like the parent's form's thread to wait until it the action is completed before refreshing, allowing additional clicks, etc.
How can I do this?
Code in the ActionListener executes on the EDT, which prevents the GUI from repainting and responding to other events.
If you have a long running task and you don't want to block the EDT then you need to use another Thread.
Read the section from the Swing tutorial on Concurrency for more information and a solution by using a SwingWorker.
I have a Blackjack game that I've made in Java and I want to signal the start of the game by clicking a button. All my action listeners work just fine and all that, but the problem lies in that I can't figure out how to start the game without it running completely within the actionPerformed method. Obviously, a function continuously running within the actionPerformed method will effectively disable the rest of my GUI. Here's a code snippet....
go.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// START GAME SOMEHOW but must run outside of action listener
}
});
Obviously, a function continuously running within the actionPerformed method will effectively disable the rest of my GUI.
This is a valid observation and shows that you have understand the fundamental rule when working with Swing.
Your game is most likely event driven (correct me if I'm wrong) so the action performed by the button should just set the program in a new state, waiting for further events. This is nothing that should be time consuming, and is typically done directly by the EDT.
Of course, if you want to do a fancy start-new-game animation, that needs to be performed in a separate thread, in which case you simply start the animation thread (I would recommend using a SwingWorker though) from within the actionPerformed method, and then return.
In code, I imagine it would look something like this:
go.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Remove the menu components
someJPanel.removeAll();
// Show the game table
someJPanel.add(new GamePanel());
someJPanel.revalidate();
someJPanel.repaint();
// done. Wait for further user actions.
}
});
You game should probably start in its own thread and manage that itself (hard to say), but to get you going you could start your game in a new "external" thread, something like this in your actionPerformed:
public void actionPerformed(ActionEvent e) {
Thread thread = new Thread("Game thread") {
public void run() {
startGame(); //or however you start your game
}
};
thread.start();
}
I believe that you want to extend javax.swing.SwingWorker.
The non-ui start-up functionality would run in doInBackground and the done method would be called when it finishes to update the ui.
There's even an example in the javadoc Class Description to update a progressbar with the status of what's happening in start-up.
I've inserted a JMenu (instance named: exitMenu) without any JMenuItem, so my intention is to make available a way to EXIT the program without access unnecessary menu items, since my program has just one JMenu object (Someone might says: WTF!!! but...).
Thus, to capture the event occurred in this specific JMenu component, my class implements the MenuListener interface. As everybody knows, there are three mandatory implementation methods, although I need to use just one, the menuSelected() method.
To make my program a little bit intuitive, undoubtedly, once the user selects the exitMenu, the (in)famous popup JOptionPane.showConfirmDialog() presents itself where he/she needs to choose between the YES or NO option.
If the chosen option is YES, no problem at all, since the program is finished through System.exit(0). The problem is the NO option, when the focus returns to the program, the exitMenu remains selected, off course, since I've selected previously. The "thing" I'd like to do is to remove the object selection right after the NO option is chosen, so the user'll be able to click on it again.
Even using exitMenu.setSelected(false) within the three mandatory methods (one calling another), although exitMenu component is "deselected" it's necessary to click on it twice to call its event listener.
Any suggestion?
Thanks in advance.
One thing I attempted is to simply call setSelected(false) from within the menuSelected(...) method, but this has side effects. For one, the menu doesn't appear to be selected, and for another, it doesn't work all the time.
One possible solution that does work is to deselect the menu in a Swing Timer. Something like:
#Override
public void menuSelected(MenuEvent mEvt) {
// show JOptionPane
// if yes selected, exit.
// Otherwise...
final JMenu menu = (JMenu) mEvt.getSource();
new Timer(200, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
menu.setSelected(false);
((Timer)e.getSource()).stop();
}
}).start();
}
There are two levels, for
JMenu there is MenuListener
JMenuItem there is ButtonModel
In 2 different action listeners, a dialog will be shown when some conditions are met.
If both of the action listeners need to show dialog, 2 dialogs will be shown at the same time. But I want them to be shown one by one.
Simplified code:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(getTopLevelAncestor(), "dialog 1");
}
});
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(getTopLevelAncestor(), "dialog 2");
}
});
Those 2 "SwingUtilities.invokeLater" invocations are in different classes.
Make a class that keeps track about that; this class would contain a queue of dialogs to display; whenever a dialog is closed, the first one of the queue is shown and removed from the queue. When another class needs to show a dialog, it's immediately shown if the queue is empty, or inserted into the queue else.
This is related to the modality of dialogs. There is quite useful article about this topic http://java.sun.com/developer/technicalArticles/J2SE/Desktop/javase6/modality/. Dialogs have different modality types with different priorities. You can solve your problem by creating the second dialog with lower priority:
JOptionPane pane = new JOptionPane("dialog 2", JOptionPane.INFORMATION_MESSAGE);
JDialog dialog = pane.createDialog("Message");
dialog.setModalityType(Dialog.ModalityType.DOCUMENT_MODAL);
dialog.setVisible(true);
Hopefully this helps.
You need invokeAndWait(), this method waits until the Runnable has been finished.
Or in your situation, when the first dialog has been closed.
What's the programmatic equivalent of clicking the close (x) button in the upper right corner of a JFrame?
There's the dispose() method but that's not the same thing, since a JFrame can be set to do several different things upon closing (not to mention if there's a WindowListener involved)
You tell the component to dispatch an event. In this case, you want it do dispatch a Window Closing event.
private void exit() {
this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
}
When you hit the x on a JFrame, the system can be set to do various things. The default is that the window is simply hidden with setVisible(false) I believe.
You can set a frame to do different things on close--you can have it dispose, hide or call code based on setDefaultCloseOperation. Here are the options:
DO_NOTHING_ON_CLOSE: Don't do anything; require the program to handle the operation in the windowClosing method of a registered WindowListener object.
HIDE_ON_CLOSE: Automatically hide the frame after invoking any registered WindowListener objects.
DISPOSE_ON_CLOSE: Automatically hide and dispose the frame after invoking any registered WindowListener objects.
EXIT_ON_CLOSE: Exit the application using the System exit method. Use this only in applications.
But I think what you are after is setVisible(false).
You have to insert the call into the AWT message queue so all the timing happens correctly, otherwise it will not dispatch the correct event sequence, especially in a multi-threaded program.
public void closeWindow()
{
if(awtWindow_ != null) {
EventQueue.invokeLater(new Runnable() {
public void run() {
awtWindow_.dispatchEvent(new WindowEvent(awtWindow_, WindowEvent.WINDOW_CLOSING));
}
});
}
}