Java swing - when cancel button clicked don't loop - java

I have a gui, that is having a Login prompt added.
while(notValidLogIn){
LoginPrompt.getDetails() //a static method that
}
Hwoever, the loginPrompt is a Jdialog, with a parent JFrame. How can I stop looping of cancel clicked, I could put System.exit(0) in cancel action performed. But don't want to stop everything, I want something like :
while(notValidLogIn && LoginPrompt.isNotCancelled()){
LoginPrompt.getDetails(); //a static method that creates an instance of login JDialog()
}

In a recent project I was working on, I've implemented an event based solution. The idea is JDialog notify to its parent JFrame how the login process went and this last one may or may not continue its execution. This way I have no loops and keep separate responsibilities: The schema would be something like this:
LoginEvent:
This is the event itself. Not that complicated:
class LoginEvent extends EventObject {
public static final int LOGIN_SUCCEEDED = 0;
public static final int LOGIN_FAILED = 1;
public static final int LOGIN_DIALOG_CLOSED = 2;
private int id;
public LoginEvent(Object source, int id) {
super(source);
this.id = id;
}
public int getId() {
return id;
}
}
LoginListener
An interface to handle these LoginEvents:
public interface LoginListener extends EventListener {
public void handleLoginEvent(LoginEvent evt);
}
Login Dialog
This class has to mantain a List with subscribed LoginListeners:
class LoginDialog {
List<LoginListener> listeners = new ArrayList<>();
JDialog dialog;
JButton accept;
JButton cancel;
public void show() {
//create and show GUI components
}
public void close() {
if(dialog != null) {
dialog.dispose();
}
}
...
public void addLoginListener(LoginListener loginEventListener) {
if(!listeners.contains(loginEventListener)) {
listeners.add(loginEventListener);
}
}
public void removeLoginListener(LoginListener loginEventListener) {
listeners.remove(loginEventListener);
}
public void dispatchLoginEvent(LoginEvent evt) {
for(LoginListener loginListener: listeners) {
loginListener.handleLoginEvent(evt);
}
}
}
Adding action listeners to accept and cancel buttons:
accept.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// validate login data
if(loginValid) {
dispatchLoginEvent(new LoginEvent(dialog, LoginEvent.LOGIN_SUCCEEDED));
} else {
dispatchLoginEvent(new LoginEvent(dialog, LoginEvent.LOGIN_FAILED));
}
}
});
cancel.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dispatchLoginEvent(new LoginEvent(dialog, LoginEvent.LOGIN_DIALOG_CLOSED));
}
});
Subscribing a LoginListener
In your JFrame:
final LoginDialog dialog = new LoginDialog();
dialog.addLoginListener(new LoginListener() {
#Override
public void handleLoginEvent(LoginEvent evt) {
if(evt.getId() == LoginEvent.LOGIN_SUCCEEDED {
dialog.close();
//continue execution
return;
}
if(evt.getId() == LoginEvent.LOGIN_FAILED) {
JOptionPane.showMessageDialog(null, "Login failed!");
return;
}
if(evt.getId() == LoginEvent.CLOSE_LOGIN_DIALOG) {
dialog.close();
// do something when this dialog is closed
}
}
};
dialog.show();

while(notValidLogIn && LoginPrompt.isNotCancelled()){
LoginPrompt.getDetails(); //a static method that creates an instance of login JDialog()
}
If this loop is inside another thread other than the EDT(event dispatch thread), then you can use SwingUtilities.invokeAndWait(new Runnable()) function: invokeAndWait() blocks the current thread until the EDT is done executing the task given by it. This option is particularly used while we want to await an execution of a thread for taking confirmation from user or other input using JDialogue/JFileChooser etc
while(notValidLogIn && LoginPrompt.isNotCancelled()){
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
LoginPrompt.getDetails() ;
}
});
}
Note: re-stating for emphasizing: you should ensure that this loop is executing inside another Thread: such as using an extended class of Runnable, or by means of anonymous class:
new Thread()
{
// other code of your context
public void run()
{
while(notValidLogIn && LoginPrompt.isNotCancelled()){
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
LoginPrompt.getDetails() ;
}
});
}
}
}.start();

Related

How to change Background color of a JButton from a derived class in Java Swing

I have a base class mainframe and i have keeping the JButton as final static Which its BGcolor going to be changed by a extended class of mainframe namely dataframe. Initially i need to set the BGColor of the JButton to red. Then I need to change it to some other colors from the dataframe. I can able to set the BGColor from the mainframe but not from the dataframe(extended class). I've used mainframe.Button_name.setBackground(color.yellow); but still its not changing
`enter code here`
public class mainframe {
final static JButton Button_name = new JButton("Hi");
public static void main(String[] args)
{
public void run()
{
Button_name.setBackground(color.Red); //This is working
}
}
}
class dataframe extends mainframe implements Runnable
{
public void run()
{
//doing other things
while(some condition)
{
if (another_condition)
{
//from here i need to change that Buttons color
// i've tried this
mainframe.Button_name.setBackground(color.yellow); //Not working
}
}
}
}
Kindly anyone help with this issue
So you want to change the state of a UI component from a different thread in a different class. There are multiple ways you might be able to do this, but first, I would start by defining away for those classes to be able to only effect the change you want them to.
Exposing the entire frame, component or even button is not a good idea, people have a habit of changing things you don't want them to, so instead, we define a simple contract which states what they are allowed to do, for example...
public interface Colorable {
public void setColor(Color color);
}
This immediately decouples your code, meaning that any code that wants to change the state of your UI (or change the color of something else) can do so, without relying on the physical implementation.
Thread
First, we're going to have a look at using a Thread to change the UI...
public class ColorChanger {
private Colorable colorable;
public ColorChanger(Colorable colorable) {
this.colorable = colorable;
}
public void start() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
for (int index = 0; index < 1000; index++) {
if (index % 100 == 0) {
if ((index / 100) % 2 == 0) {
colorable.setColor(Color.GREEN);
} else {
colorable.setColor(Color.RED);
}
}
try {
// This is so you can see the colors changing
Thread.sleep(5);
} catch (InterruptedException ex) {
}
}
System.out.println("Done");
}
});
t.start();
}
}
This is a pretty basic class, it requires an instance of Colorable and will change the state of the color for every 100 counts, based on if it's an even or odd hundred
We use a simple JPanel as our base test class, when you click the button, the ColorChanger is created and started.
public class TestPane extends JPanel implements Colorable {
private JButton btn;
private ColorChanger changer;
public TestPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(20, 20, 20, 20));
btn = new JButton("I am your button");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (changer == null) {
changer = new ColorChanger(TestPane.this);
changer.start();
}
}
});
}
#Override
public void setColor(Color color) {
if (EventQueue.isDispatchThread()) {
btn.setBackground(color);
} else {
System.out.println("Not in the EDT");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setColor(color);
}
});
}
}
}
You will note that the setColor method has a bunch of code in it, this is to ensure that the updates to the UI are executed only from within the context of the Event Dispatching Thread.
SwingWorker
An alternative is to use a SwingWorker, which operates very similarly to a Thread, expect it has the ability to publish content to the EDT
public class ColorChangerWorker extends SwingWorker<Void, Color> {
private Colorable colorable;
public ColorChangerWorker(Colorable colorable) {
this.colorable = colorable;
}
#Override
protected void process(List<Color> chunks) {
colorable.setColor(chunks.get(chunks.size() - 1));
}
#Override
protected Void doInBackground() throws Exception {
for (int index = 0; index < 1000; index++) {
if (index % 100 == 0) {
if ((index / 100) % 2 == 0) {
publish(Color.GREEN);
} else {
publish(Color.RED);
}
}
try {
// This is so you can see the colors changing
Thread.sleep(5);
} catch (InterruptedException ex) {
}
}
System.out.println("Done");
return null;
}
}
You will note here, that when we want to change the color we call publish. The process method is called to let us know that there is more data to be processed, but here, we're only interested in the last change.
And out TestPane...
public class TestPane extends JPanel implements Colorable {
private JButton btn;
private ColorChangerWorker changer;
public TestPane() {
setLayout(new GridBagLayout());
setBorder(new EmptyBorder(20, 20, 20, 20));
btn = new JButton("I am your button");
add(btn);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (changer == null) {
changer = new ColorChangerWorker(TestPane.this);
changer.execute();
}
}
});
}
#Override
public void setColor(Color color) {
if (EventQueue.isDispatchThread()) {
btn.setBackground(color);
} else {
System.out.println("Not in the EDT");
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
setColor(color);
}
});
}
}
}
You will note that the setColor method remains unchanged, this is deliberate, when you test this class, you will note that "Not in the EDT" is never printed, basically meaning we could do away with all that code and just call btn.setBackground(color);, but I want you to see the difference.
The Button...
Now, when I run this code, I get the following output...
Wait a minute, that buttons background is filled?! Actually it is, but many button implementations have a secondary "content area" filling
You can turn this off using something like...
btn.setContentAreaFilled(false);
btn.setOpaque(true);
Which will result in something like...

How force the program wait a Task and show Progress Bar to user?

I use Swing Application Framework in my program. And I have some long-time work. I use org.jdesktop.application.Task for it. Another programmer wrote two Tasks before I took this project (I can not ask him about the programm). When Tasks are executing user sees progress bar without showing percent complete, but what shows "Wait" message and user can not click to a main window while Task does not ended. It is fine! But I could not find place where ProgressBars was created. May be it is described in some xml-file or property-file?
Also I wrote another Tasks and when they run, progress bar which I created is not displayed or displayed incorrectly. I read about ProgressBar and ProgressMonitor, but it does not help me.
Programm continue to run after someTask.execute(), but I want to it displays ProgressBar, ProgressMonitor or something else and user can not click the main window and window will display correctly. Now window has black "blocks" when user change it.
May be I need use org.jdesktop.application.TaskMonitor. I try to use it as here https://kenai.com/projects/bsaf/sources/main/content/other/bsaf_nb/src/examples/StatusBar.java?rev=235 , but my main window is displayed incorrectly and my ProgressBar is not displayed.
I need to when Task is running program waits it, but user can see ProgressBar, can cancel the operation and can not click to the main window. How can I do it?
Here my code:
public class A{
#Action(name = "ActionName", block = Task.BlockingScope.APPLICATION)
public RequestInfoTask requestInfo() {
RequestInfoTask task = new RequestInfoTask(Application.getInstance());
isSuccessedGetInfo=false;
task.addTaskListener(new TaskListener.Adapter<List<InfoDTO>, Void>() {
#Override
public void succeeded(TaskEvent<List<InfoDTO>> listTaskEvent) {
isSuccessedGetResources=true;
}
});
//Here I want to the program shows ProgressMonitor and user can not click to the main window.
//But small window with message "Progress..." is displayed for several seconds and disappear.
ProgressMonitor monitor = new ProgressMonitor(getMainView(), "Wait! Wait!", "I am working!", 0, 100);
int progress = 0;
monitor.setProgress(progress);
while(!task.isDone()){
monitor.setProgress(progress+=5);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
monitor.setProgress(100);
//This code must run after "task" finishes.
if(isSuccessedGetInfo){
MyTask2 task2 = new MyTask2(Application.getInstance());
isSuccessedTask2=false;
task2.addTaskListener(new TaskListener.Adapter<Map<?,?>, Void>(){
#Override
public void succeeded(TaskEvent<Map<String, ICredential>> arg0) {
isSuccessedTask2=true;
}
});
//Do something with results of task2.
}
return task;
}
}
public class RequestInfoTask extends Task<List<InfoDTO>, Void> {
public RequestInfoTask(Application application) {
super(application);
}
#Override
protected List<InfoDTO> doInBackground() throws Exception {
List<InfoDTO> result = someLongerLastingMethod();
return result;
}
}
Part of your problem sounds like it comes from not using the EDT correctly. Any long running task needs to be started in it's own thread to keep the GUI responsive and repainting.
Ideally you'd be following a MVC pattern. In that case you place your Progress Bar in the view, your flag (that indicates whether the task should be running still) in the control, and your long running task in in the Model.
From that point, if your model checks periodically if it should stop (Probably at good stopping points), you can reset everything.
Here's an example with MVC:
import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
public class ProgressBarDemo{
public static class View extends JPanel{
Controller control;
public JProgressBar progressBar = new JProgressBar(0, 100);
JButton button = new JButton("Start Long Running Task");
public View(Controller controlIn){
super();
this.control = controlIn;
setLayout(new BorderLayout());
button.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
//Toggle between running or not
if(control.isRunning){
control.isRunning = false;
button.setText("Canceling...");
button.setEnabled(false);
} else{
control.isRunning = true;
button.setText("Cancel Long Running Task");
control.startTask();
}
}});
progressBar.setStringPainted(true);
add(progressBar);
add(button, BorderLayout.SOUTH);
}
}
//Communications gateway
public static class Controller{
View view = new View(this);
boolean isRunning = false;
public void updateProgress(final int progress){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
view.progressBar.setValue(progress);
}});
}
public void reset(){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
isRunning = false;
view.button.setText("Start Long Running Task");
view.progressBar.setValue(0);
view.button.setEnabled(true);
}});
}
public void startTask(){
LongRunningClass task = new LongRunningClass(this);
new Thread(task).start();
}
}
public static class LongRunningClass implements Runnable{
Controller control;
public LongRunningClass(Controller reference){
this.control = reference;
}
#Override
public void run() {
try {
for(int i = 0; i < 11; i++){
//Monitor the is running flag to see if it should still run
if(control.isRunning == false){
control.reset();
break;
}
control.updateProgress(i * 10);
Thread.sleep(3000);
}
control.reset();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
// Create and set up the window.
JFrame frame = new JFrame("LabelDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Add content to the window.
frame.add(new Controller().view);
// Display the window.
frame.pack();
frame.setVisible(true);
}
}

JDialog invisible, components clickable

I'm using a JDialog instatiated at startup of the application, to show messages several times. Sometimes the dialog and its controls are invisible, but clickable.
The JDialog is instantiated only once and set to visible 'true' each time a message should be shown and then set to visible 'false' till the next message should be shown.
To exlude multithreading related problems, i always use SwingUtilities.invokeLater(...) for ui calls, when a thread creates a message and shows the dialog.
Because its a huge project and my problem isn't related to any specific code, i don't post code but describe the problem. The problems seems not to be reproducible but happens sometimes, so it might be a threading problem despite running each related call on the EDT.
What am I doing wrong?
public class MessageHandler {
private volatile static MessageHandler messageHandler = null;
private List<Message>messages = null;
private volatile WeakReference<MessagesPanelControl> view = null;
private final Object viewSynchronizationObject = new Object();
private MessageHandler() {
messages = new ArrayList<Message>();
}
public static MessageHandler getInstance() {
MessageHandler result = messageHandler;
if (result == null) {
synchronized (MessageHandler.class) {
result = messageHandler;
if (result == null)
messageHandler = result = new MessageHandler();
}
}
return result;
}
public void registerView(MessagesPanelControl view) {
this.view = new WeakReference<MessagesPanelControl>(view);
}
public void addMessage(final Message message) {
synchronized (viewSynchronizationObject) {
messages.add(message);
Collections.sort(messages);
updateView();
}
}
private void updateView() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
synchronized (viewSynchronizationObject) {
if (view == null) {
return;
}
MessagesPanelControl mpc = view.get();
if (mpc != null) {
mpc.updateView();
}
}
}
});
}
}
In the MainFrame class i'm doing this initialization once at startup:
MessagesPanelControl mp = new MessagesPanelControl();
MessageHandler.getInstance().registerView(mp);
LockPane messageBasicPane = new LockPane(this, mp);
And then in different threads this is called to show a message via the MessageHandler Singleton:
MessageHandler.getInstance().addMessage(Message.getSimpleMessage("Error", "Fatal error occured", Message.MessageIcon.ERROR));
I didn't post all details, but all necessary parts to understand the whole problme, hope it makes it more understandable.
The MessagePanelControl (mpc) is a class, that extends JPanel. Its updateView()-method creates the message controlls based on the MessageHandler's message list like buttons, labels and icons. Finally the method sends a Delegate like command to the main frame to show the JDialog containing the MessagePanelControl.
Summarized it does:
messageList.size()>0: create message panels for each message in list in MessageHandler
messageList.size()>0: show JDialog with MessagePanelControl
messageList.size()<=0: hide JDialog with MessagePanelControl
public void updateView() {
synchronized (viewMPCSynchronizationObject) {
Utils.throwExceptionWhenNotOnEDT();
JPanel messagesListPanel = new JPanel();
scrollPane.setViewportView(messagesListPanel);
scrollPane.setBorder(null);
messagesListPanel.setLayout(new BoxLayout(messagesListPanel, BoxLayout.Y_AXIS));
if (MessageHandler.getInstance().getMessages() != null && MessageHandler.getInstance().getMessages().size() > 0) {
[...]
//Create buttons, text icons... for each message
[...]
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MainFrame().showMessageBoard();
}
});
} else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
MainFrame().closeMessageBoard();
}
});
}
repaint();
}
}
MainFrame:
//Show Messageboard
public void showMessageBoard() {
if (messageBasicPane != null) {
messageBasicPane.setVisible(true);
messageBasicPane.repaint();
}
}
[...]
//Close Messageboard
public void closeMessageBoard() {
if (messageBasicPane != null) {
messageBasicPane.setVisible(false);
}
}
This line creates the JDialog, in detail:
[...]
public LockPane(JFrame parentFrame, JComponent componentToShow, Dimension paneSize, float opacity, ModalityType modality) {
super(parentFrame, true);
Utils.throwExceptionWhenNotOnEDT();
createDialog(paneSize, opacity, modality);
if (componentToShow != null) {
add(componentToShow);
}
pack();
}
private void createDialog(Dimension paneSize, float opacity, ModalityType modality) {
Utils.throwExceptionWhenNotOnEDT();
setUndecorated(true);
setModalityType(modality);
if (opacity < 1 && opacity >= 0)
com.sun.awt.AWTUtilities.setWindowOpacity(this, opacity);
setSize(paneSize);
setPreferredSize(paneSize);
setMaximumSize(paneSize);
setBounds(0, 0, paneSize.width, paneSize.height);
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
}
[...]
A new observation with Java VisualVM is, that the AWT-EventQueue isn't blocked, only sometime there are small periods of 'wait' but nothing blocking. Another strange thing is, that sometimes my JDialog is fully transparent (invisible) and sometimes its white with the desired opacity.
In this function, you are essentially trying to await the Runnable passed to the SwingUtilities.invokeLater which invokeLater submits to the EDT to get executed. The lock that you are holding on viewSynchronizationObject will block EDT if it is locked by other application thread which is actually evident from your code as you have used this variable in several other places.
private void updateView() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
synchronized (viewSynchronizationObject) {
if (view == null) {
return;
}
MessagesPanelControl mpc = view.get();
if (mpc != null) {
mpc.updateView();
}
}
}
},false);
}
We should never block EDT from executing it's task other wise our application freezes. Please read my posted answer here for details on how the Swing event and rendering task is performed by EDT and EventQueue.
Although, your application logic is not known to us, you can remove the synchronized (viewSynchronizationObject) {} from invokeLater, instead you can put SwingUtilities.invokeLater(new Runnable() {} inside this synchronized block:
synchronized (viewSynchronizationObject)
{
SwingUtilities.invokeLater(new Runnable() { /* your code */ } );
}

Modal window with progressbar for long tasks

In some cases, I want show to user a modal window with a progress bar when the long running query. (For individual UI items, I use the method setEnabled (true/ false) but I want more elegant solution.)
For example, in the entry point, until all elements not initialized -
public void onModuleLoad() {
// initialization of the interface here
}
And also, for example, when completing the dependent list box (relationship one to many)
#UiHandler("listBox")
public void onListBoxChange(ChangeEvent event) {
someService.findDependencies(id, new AsyncCallback<List<DependencyDTO>>() {
public void onFailure(Throwable caught) {
// exception handling here
}
public void onSuccess(List<DependencyDTO> data) {
// listBox filling here
}
});
}
In Vaadin applications I can added to the listener the following code, for example -
...
Thread thread = new Thread() {
#Override
public void run() {
// some initialization here
window.removeWindow(blockerWindow);
}
};
thread.start();
blockerWindow = new BlockerWindow();
window.addWindow(blockerWindow);
...
In my case, I can use the following method to display a window with a progress bar -
private void freezeInterface() {
blockerWindow = new BlockerWindow()
blockerWindow.setGlassEnabled(true);
blockerWindow.setAnimationEnabled(true);
blockerWindow.setModal(true);
blockerWindow.center();
blockerWindow.show();
}
And the method to hide window -
private void unfreezeInterface() {
blockerWindow.hide();
}
The question is, when hide the window.
For example, at the entry point there are series of queries -
...
service1.findDependenciesForListBox1(id1, new AsyncCallback<List<Dependency1DTO>>() {
public void onFailure(Throwable caught) {
// exception handling here
}
public void onSuccess(List<Dependency1DTO> data) {
// listBox1 filling here
}
});
service2.findDependenciesForListBox2(id2, new AsyncCallback<List<Dependency2DTO>>() {
public void onFailure(Throwable caught) {
// exception handling here
}
public void onSuccess(List<Dependency2DTO> data) {
// listBox2 filling here
}
});
serviceN.findDependenciesForListBoxN(idN, new AsyncCallback<List<DependencyNDTO>>() {
public void onFailure(Throwable caught) {
// exception handling here
}
public void onSuccess(List<DependencyNDTO> data) {
// listBoxN filling here
}
});
And so on.
The answers come in a previously unknown sequence and hide the window in one of the methods onSuccess I can not.
I can use a timer, but I do not know beforehand the time which to pass in schedule.
...
blockerWindow.show();
private void unfreezeInterface() {
timer = new Timer() {
public void run() {
blockerWindow.hide();
}
};
timer.schedule(15000);
}
...
How to properly implement this in GWT?
If you know the number of responses to get, you can use such approach:
class TaskCompletedHandler{ // inner class
private static final int NUMBER_OF_RESPONSES = 4;//just example
private int tasksCompleted;
public void notifyOfCompletedTask(){
tasksCompleted++;
if (tasksCompleted == NUMBER_OF_RESPONSES){
blockerWindow.hide();
}
}
}
create instance of this class before showing modal window and then notify this handler in AsyncCallback
service1.findDependenciesForListBox1(id1, new AsyncCallback<List<Dependency1DTO>>() {
public void onFailure(Throwable caught) {
taskCompletedHandler.notifyOfCompletedTask();
// exception handling here
}
public void onSuccess(List<Dependency1DTO> data) {
taskCompletedHandler.notifyOfCompletedTask();
// listBox1 filling here
}
});

give focus to a panel that is not a child of the focused Window?

I'm working in Java to create an internal tool. The two consists of two JPanels which are anticipated to run on two separate screens. I want to be able to click a JButton which is part of JFrame A which will send a keyboard action to JFrame B.
Unfortunately I can't seem to do this because JFrame B doesn't have the focus and I can't use any of the request Focus methods because JFrame B is not a child of the Focus Window (JFrame A is).
So how can I either give JFrame B focus despite it not being a child of the Focus Window or send Keyboard Events to JFrame B that it will respond to without having focus?
is so hard to manage Focus between two JFrames, better would be create only one JFrame and other Top-level Containers would be JDialogs, create one/two JDialog(s) and reuse that by removing all JComponents
basicaly it would be :
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
someComponent.grabFocus();
someComponent.requestFocus();//or inWindow depends if Swing or Awt
}
});
but between two JFrames is things little bit complicated, just basic and uncompleted example (based on code from old.good.sun.forums.com)
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PMDialog extends JDialog {
private static final long serialVersionUID = 1L;
private boolean modal = false;
private WindowAdapter parentWindowListener;
private Window owner;
private JFrame blockedFrame = new JFrame("Blocked Frame");
private JFrame noBlockedFrame = new JFrame("No Blocked Frame");
public PMDialog() {
noBlockedFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
noBlockedFrame.getContentPane().add(new JButton(new AbstractAction("Test button") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent evt) {
System.out.println("Non blocked button pushed");
/*if (blockedFrame.isVisible()) {
noBlockedFrame.setVisible(false);
} else {
blockedFrame.setVisible(true);
}*/
noBlockedFrame.setVisible(true);
blockedFrame.setVisible(true);
}
}));
noBlockedFrame.setSize(200, 200);
noBlockedFrame.setVisible(true);
blockedFrame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
blockedFrame.getContentPane().add(new JButton(new AbstractAction("Test Button") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent evt) {
final PMDialog pmd = new PMDialog(blockedFrame, "Partial Modal Dialog", true);
pmd.setSize(200, 100);
pmd.setLocationRelativeTo(blockedFrame);
pmd.getContentPane().add(new JButton(new AbstractAction("Test button") {
private static final long serialVersionUID = 1L;
#Override
public void actionPerformed(ActionEvent evt) {
System.out.println("Blocked button pushed");
pmd.setVisible(false);
blockedFrame.setVisible(false);
noBlockedFrame.setVisible(true);
}
}));
pmd.setDefaultCloseOperation(PMDialog.DISPOSE_ON_CLOSE);
pmd.setVisible(true);
System.out.println("Returned from Dialog");
}
}));
blockedFrame.setSize(200, 200);
blockedFrame.setLocation(300, 0);
blockedFrame.setVisible(false);
}
public PMDialog(JDialog parent, String title, boolean isModal) {
super(parent, title, false);
initDialog(parent, title, isModal);
}
public PMDialog(JFrame parent, String title, boolean isModal) {
super(parent, title, false);
initDialog(parent, title, isModal);
}
private void initDialog(Window parent, String title, boolean isModal) {
owner = parent;
modal = isModal;
parentWindowListener = new WindowAdapter() {
#Override
public void windowActivated(WindowEvent e) {
if (isVisible()) {
System.out.println("Dialog.getFocusBack()");
getFocusBack();
}
}
};
}
private void getFocusBack() {
Toolkit.getDefaultToolkit().beep();
super.setVisible(false);
super.pack();
super.setLocationRelativeTo(owner);
super.setVisible(true);
super.toFront();
}
#Override
public void dispose() {
owner.setEnabled(true);
owner.setFocusableWindowState(true);
super.dispose();
}
#Override
#SuppressWarnings("deprecation")
public void hide() {
owner.setEnabled(true);
owner.setFocusableWindowState(true);
super.hide();
}
#Override
public void setVisible(boolean visible) {
boolean blockParent = (visible && modal);
owner.setEnabled(!blockParent);
owner.setFocusableWindowState(!blockParent);
super.setVisible(visible);
if (blockParent) {
System.out.println("Adding listener to parent ...");
owner.addWindowListener(parentWindowListener);
try {
if (SwingUtilities.isEventDispatchThread()) {
System.out.println("EventDispatchThread");
EventQueue theQueue = getToolkit().getSystemEventQueue();
while (isVisible()) {
AWTEvent event = theQueue.getNextEvent();
Object src = event.getSource();
if (event instanceof ActiveEvent) {
((ActiveEvent) event).dispatch();
} else if (src instanceof Component) {
((Component) src).dispatchEvent(event);
}
}
} else {
System.out.println("OUTSIDE EventDispatchThread");
synchronized (getTreeLock()) {
while (isVisible()) {
try {
getTreeLock().wait();
} catch (InterruptedException e) {
break;
}
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
System.out.println("Error from EDT ... : " + ex);
}
} else {
System.out.println("Removing listener from parent ...");
owner.removeWindowListener(parentWindowListener);
owner.setEnabled(true);
owner.setFocusableWindowState(true);
}
}
#Override
public void setModal(boolean modal) {
this.modal = modal;
}
public static void main(String args[]) {
PMDialog pMDialog = new PMDialog();
}
}
EDIT: for How to adds Focus to the JDialog is there excelent Woodoo by camickr Dialog Focus but AncestorListener isn't my cup of Java and too much abstract for me
Is this a Swing GUI (you don't mention or add a tag to a gui library)? If so, consider using Key Bindings which can be more flexible about focus compared with KeyListeners.
Nothing says you can't have one Action send a message to another. In this example, Enter also needs to Clear, so it forwards the ActionEvent. Note also that each NumberButton binds two keystrokes to Click. In your case, you may have to to remove an existing binding and perhaps call toFront() on the other window. Note the caveats that lend support to #mKorbel's answer.

Categories

Resources