Application wide focus

Anyone got any ideas how to detect loss of focus to a different application. The JFrame WindowFocusListener notifies when the JFrame loses focus, the trouble is this notification also occurs when focus is lost for example to a JDialog launched from the JFrame. Even adding FocusListeners to all possible application sub-windows doesn't help as the frame loses focus before the sub-window gains it.

Any suggestions welcome...

[438 byte] By [K.Furnella] at [2007-11-27 9:46:47]
# 1

You could try using a WindowListener and handle the windowDeactivated and windowActivated states.

Otherwise maybe the following will work.

1) handle windowActivated by setting an application wide varialbe to true

2) handle windowDeactivated by setting this same variable to false. Now the second part of this method will be wrapped in a SwingUtilities.invokeLater(...) which means the code will be added to the end of the event Thread and executed after all outstanding events on the EDT are executed.

So, if you activate another window in your application the order of events should be:

a) windowDeactivated, variable set to false

b) windowActivated, variable set to true

c) invokeLater() code which can test the variable

If you activate another application the order should be:

a) windowDeactivated, variable set to false

b) invokeLater() code which can test the variable

camickra at 2007-7-12 23:57:47 > top of Java-index,Desktop,Core GUI APIs...
# 2

Thanks for your ideas camickr. The window activated/deactivated events don't in themselves help as they are called in the same order as focus gained/lost.

However combined with your idea of using the event thread to encapsulate the focus transfer as an atomic transaction they do have the desired effect.

Interestingly though, using the same technique with focus listeners doesn't work. The order of events then is:

a) windowFocusLost, variable set to false, invokeLater() called

b) Runnable method executed

c) dialog focusGained, variable set to true

Which is not what I would have expected (!)

K.Furnella at 2007-7-12 23:57:47 > top of Java-index,Desktop,Core GUI APIs...
# 3

Below is some code that demonstrates the succesful solution for others benefit. The discussion above covers the case of distinguishing between losing focus to another window versus losing focus to another application. A second flag is needed to cover the corresponding case of distinugishing between gaining focus from another window versus gaining focus from another application.

import java.awt.event.*;

import javax.swing.*;

public class FocusTest extends JFrame {

public FocusTest() {

JButton button = new JButton("Go");

button.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {

buttonPressed();

}

});

add(button);

addWindowListener(applicationFocusMonitor);

pack();

setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

}

private void buttonPressed() {

JDialog dialog = new JDialog(this, "Test");

dialog.add(new JLabel("Hello"));

dialog.addWindowListener(applicationFocusMonitor);

dialog.pack();

dialog.setVisible(true);

}

public static void main(String[] args) {

new FocusTest().setVisible(true);

}

WindowAdapter applicationFocusMonitor = new WindowAdapter() {

private volatile boolean applicationHasFocus= true;

private volatile boolean focusChangeInProgress = false;

public void windowActivated(WindowEvent e) {

System.out.println("- windowActivated " + e.getWindow().getName());

if (!focusChangeInProgress) {

System.out.println("Application gained focus");

}

applicationHasFocus = true;

}

public void windowDeactivated(WindowEvent e) {

System.out.println("- windowDeactivated " + e.getWindow().getName());

applicationHasFocus = false;

focusChangeInProgress = true;

SwingUtilities.invokeLater(new Runnable() {

public void run() {

if (!applicationHasFocus) {

System.out.println("Application lost focus");

}

focusChangeInProgress = false;

}

});

}

};

}

K.Furnella at 2007-7-12 23:57:47 > top of Java-index,Desktop,Core GUI APIs...