How to get notifications when the title bar of a window is clicked / clicked
I am working on an auto-dropdown suggestion box (Google for example) using JWindow (not JPopupMenu). My JWindow dropdown is not customizable or modal (the textbox needs to keep focus while the user is typing).
I would like to close the dropdown when the user clicks on the mouse anywhere outside of the dropdown menu or if the application loses focus or is minimized or the escape key is pressed (basically like JPopupMenu).
It works for me , but I can't figure out how to get an event when the user clicks the title bar of the main frame (which causes the main frame to come out before the dropdown).
I am afraid there is no event for this as I am not getting anything from this listener:
Toolkit.getDefaultToolkit().addAWTEventListener(myTestListener, Integer.MAX_VALUE);
How does JPOPupMenu achieve this behavior?
EDIT: Added SSCCE:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class SSCCE
{
public static void main(String[] args) throws Exception
{
final JFrame frame = new JFrame();
JButton button = new JButton("open popup");
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
openPopup(frame);
}
});
frame.setLayout(new FlowLayout());
frame.add(button);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static void openPopup(JFrame frame)
{
final JWindow popupWindow = new JWindow();
popupWindow.setFocusable(false);
popupWindow.setSize(400, 400);
popupWindow.setLocation(frame.getX() + 200, frame.getY() + 200);
((JComponent) popupWindow.getContentPane()).setBorder(new MatteBorder(1, 1, 1, 1, Color.RED));
popupWindow.setVisible(true);
AWTEventListener awtEventListener = new AWTEventListener() {
@Override
public void eventDispatched(AWTEvent e)
{
System.out.println(e.toString());
if(e instanceof MouseEvent
&& ((MouseEvent) e).getID() == MouseEvent.MOUSE_PRESSED //
|| e instanceof FocusEvent
&& ((FocusEvent) e).getID() == FocusEvent.FOCUS_LOST //
|| e instanceof ComponentEvent
&& ((ComponentEvent) e).getID() == ComponentEvent.COMPONENT_MOVED
&& e.getSource() != popupWindow //
|| e instanceof ComponentEvent && ((ComponentEvent) e).getID() == ComponentEvent.COMPONENT_RESIZED
&& e.getSource() != popupWindow//
|| e instanceof WindowEvent && ((WindowEvent) e).getID() == WindowEvent.WINDOW_STATE_CHANGED //
|| e instanceof WindowEvent && ((WindowEvent) e).getID() == WindowEvent.WINDOW_ACTIVATED //
|| e instanceof WindowEvent && ((WindowEvent) e).getID() == WindowEvent.WINDOW_DEACTIVATED //
|| e instanceof WindowEvent && ((WindowEvent) e).getID() == WindowEvent.WINDOW_GAINED_FOCUS //
|| e instanceof WindowEvent && ((WindowEvent) e).getID() == WindowEvent.WINDOW_LOST_FOCUS //
|| e instanceof KeyEvent && ((KeyEvent) e).getKeyCode() == KeyEvent.VK_ESCAPE //
)
{
popupWindow.setVisible(false);
Toolkit.getDefaultToolkit().removeAWTEventListener(this);
}
}
};
Toolkit.getDefaultToolkit().addAWTEventListener(awtEventListener, 0xFFFFFFFF);
}
}
source to share
First, start by looking at the documentation Toolkit#addAWTEventListener
Parameters:
listener - event listener.
eventMask - bitmask of event types to receive
eventMask
is the bit mask of the event ID you want to receive. Ok, this is less impressive, but what does it mean, you have to pass or a registered list of event IDs you are interested in that have been reported ...
Now the question arises, what events are we interested in and how we get the identifier ...
Well, AWTEvent
contains a list of event IDs that work with Toolkit
, AWTEvent.WINDOW_FOCUS_EVENT_MASK
and might even AWTEvent.FOCUS_EVENT_MASK
be useful.
Toolkit.getDefaultToolkit().addAWTEventListener(myTestListener, AWTEvent.WINDOW_FOCUS_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
For example...
import java.awt.AWTEvent;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Toolkit;
import java.awt.event.AWTEventListener;
import java.awt.event.FocusEvent;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestFocus {
public static void main(String[] args) {
new TestFocus();
}
public TestFocus() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() {
@Override
public void eventDispatched(AWTEvent event) {
if (event instanceof WindowEvent) {
System.out.println("WindowEvent");
WindowEvent evt = (WindowEvent) event;
if (evt.getID() == WindowEvent.WINDOW_GAINED_FOCUS) {
System.out.println("I got you babe");
} else if (evt.getID() == WindowEvent.WINDOW_LOST_FOCUS) {
System.out.println("Don't leave me!");
}
} else if (event instanceof FocusEvent) {
System.out.println("FocusEvent");
}
}
}, AWTEvent.WINDOW_FOCUS_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
}
}
They will tell you when the focus state change occurs, then you will need to check with KeyboardFocusManager#getFocusOwner
and / or KeyboardFocusManager#getGlobalFocusedWindow
to determine if your window has focus or not.
Equally, you can just try KeyboardFocusManager#addPropertyChangeListener
and track changes in KeyboardFocusManager
...
source to share
If that windowActivated()
doesn't work for you, then as a last resort, you can always set the JFrame as unecorated and create your own title bar. It will no longer look like its native title, but you will have more control, including the ability to add a mouse listener.
myFrame.setUndecorated(true);
source to share