We have a application with two JFrames with two JDesktopPanes.
We need to move an internal frame from one frame to another.
The problem we have is that after we move the internalframe from first window to the second window, when we resize the fist window, the internal frame of the second window also gets resized.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyVetoException;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
class FirstFrame extends JFrame
{
JDesktopPane desktopPane = new JDesktopPane();
SecondFrame secondFrame;
public FirstFrame(SecondFrame secondFrame)
{
this.secondFrame = secondFrame;
setTitle("FirstFrame example");
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(desktopPane);
JMenuBar menuBar = new JMenuBar();
JMenu menu = new JMenu("File");
JMenuItem item = new JMenuItem("Move");
item.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent actionevent)
{
moveFrame();
}
});
menu.add(item);
menuBar.add(menu);
setJMenuBar(menuBar);
}
public void addAnInternalFrame()
{
JInternalFrame frame = new JInternalFrame();
frame.setTitle("An Internal Frame");
desktopPane.add(frame);
frame.setVisible(true);
frame.setMaximizable(true);
try
{
frame.setSelected(true);
frame.setMaximum(true);
}
catch (PropertyVetoException e)
{
e.printStackTrace();
}
}
public void moveFrame()
{
JInternalFrame selectedFrame = desktopPane.getSelectedFrame();
desktopPane.remove(selectedFrame);
desktopPane.repaint();
secondFrame.addInternalFrame(selectedFrame);
}
}
class SecondFrame extends JFrame
{
JDesktopPane desktopPane = new JDesktopPane();
public SecondFrame()
{
setTitle("SecondFrame example");
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(desktopPane);
}
public void addInternalFrame(JInternalFrame frame)
{
desktopPane.add(frame);
}
}
public class DesktopPaneExample
{
public static void main(String args[]) throws PropertyVetoException
{
SecondFrame secondFrame = new SecondFrame();
FirstFrame firstFrame = new FirstFrame(secondFrame);
firstFrame.setSize(400, 400);
firstFrame.setLocation(100, 100);
firstFrame.setVisible(true);
firstFrame.addAnInternalFrame();
secondFrame.setSize(400, 400);
secondFrame.setLocation(520, 100);
secondFrame.setVisible(true);
}
}
In the above sample application, to reproduce
1) click menu File>move
2) resize the first window
NOTE: This is reproducable in Java 1.7 only. I use jdk1.7.0_03.
Update: add more information
This was not reproducible on Java 1.6 (jdk1.6.0_21)
The issue is due to Java 7's tweaking on javax.swing.plaf.basic.BasicInternalFrameUI implementation.
Java 1.6 Code
public void propertyChange(PropertyChangeEvent evt) {
if ((frame.getParent() != null) && !componentListenerAdded) {
f.getParent().addComponentListener(componentListener);
componentListenerAdded = true;
} else if ((newValue == null) && componentListenerAdded) {
if (f.getParent() != null) {
f.getParent()
.removeComponentListener(componentListener);
}
componentListenerAdded = false;
}
Java 1.7 Code
public void propertyChange(PropertyChangeEvent evt) {
if ((frame.getParent() != null) && !componentListenerAdded) {
f.getParent().addComponentListener(componentListener);
componentListenerAdded = true;
}
NOTE: The else if condition was removed. This is the culprit.
I suggest you 2 options:
Option one
JInternalFrame selectedFrame = desktopPane.getSelectedFrame();
desktopPane.remove(selectedFrame);
desktopPane.repaint();
secondFrame.updateUI(); // The magic part, less expensive execution.
secondFrame.addInternalFrame(selectedFrame);
Option two
You may need to recompile javax.swing.plaf.basic.BasicInternalFrameUI.java with above "else if" condition and add to your rt.jar library's javax.swing.plaf.basic location.
I have attached the recompiled files for Java 1.7.0_25 at http://www.datafilehost.com/d/dfb7238c
Hope this helps!!!
Regards,
Nilindra
It seems as if adding the frame while in it's maximum state is the culprit. To maintain it's current size on the 1st frame over to the 2nd frame, try this:
public void moveFrame()
{
JInternalFrame selectedFrame = desktopPane.getSelectedFrame();
Dimension currentSize = selectedFrame.getSize();
try
{
selectedFrame.setMaximum(false);
}
catch (PropertyVetoException ex)
{
ex.printStackTrace();
}
selectedFrame.setSize(currentSize);
desktopPane.remove(selectedFrame);
desktopPane.repaint();
secondFrame.addInternalFrame(selectedFrame);
}
EDIT:
After reading the API for Container#remove(Component c), I got this idea that seems to work:
public void moveFrame()
{
final JInternalFrame selectedFrame = desktopPane.getSelectedFrame();
desktopPane.remove(selectedFrame);
desktopPane.repaint();
SwingUtilities.updateComponentTreeUI(selectedFrame);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
secondFrame.addInternalFrame(selectedFrame);
}
});
}
Related
Once the steps below are taken, the whole application is frozen and the modal dialog cannot be closed.
(This is related to another question but this time we have a reproducible scenario)
The steps:
Open dropdown
Select "Han-Ra" (the last value in the drop down)
After this, trying to resize or close the modal dialog will not succeed. It reproduces 1 out of 3 times (might be easier to reproduce if you do the selection by arrow down but happens with mouse selection also)
This happens on jdk 1.8 (tried 1.8.0_162 and 1.8.0_144) and jdk 10 (10.0.1) but not when using 1.7 (tried 1.7.0_80)
This is just the most obvious case we could find but it randomly (rarely) happens for most modal dialogs.
Anyone else had this problem and found a workaround? We'll report it to Oracle but we'd be more interested in a workaround.
import javax.swing.*;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
public class FreezePleeze {
public static final Object[] ALL_THE_SINGLE_LADIES = {"Rahan", "Crao", "Naouna", "Han-ra"};
public static void main(String[] args) {
new FreezePleeze();
}
public FreezePleeze() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JButton push_me = new JButton("Push me");
JFrame frame = new JFrame("Mmmmm");
JPanel containerPanel = new JPanel();
frame.add(containerPanel);
final JComboBox<Object> comboBox = new JComboBox<>(ALL_THE_SINGLE_LADIES);
containerPanel.add(comboBox);
frame.setSize(300, 300);
comboBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JDialog jDialog = new JDialog((JFrame) null, true);
jDialog.add(push_me);
if (comboBox.getSelectedIndex() == ALL_THE_SINGLE_LADIES.length - 1) {
jDialog.setLocationRelativeTo(frame);
jDialog.setSize(300, 300);
jDialog.setVisible(true);
}
}
});
comboBox.addPopupMenuListener(new PopupMenuListener() {
#Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
}
#Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
try {
Robot robot = new Robot();
robot.keyPress(KeyEvent.VK_WINDOWS);
robot.keyRelease(KeyEvent.VK_WINDOWS);
} catch (AWTException e1) {
e1.printStackTrace();
}
push_me.setText("Finished counting");
}
#Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
});
frame.setVisible(true);
}
});
}
}
I can reproduce your problem. The solution is to submit the correct window to the constructor of your dialog:
Example:
JDialog jDialog = new JDialog(frame, true);
or, if you have no window instance when you create a dialog:
JDialog jDialog = new JDialog(FocusManager.getCurrentKeyboardFocusManager().getActiveWindow(),
ModalityType.APPLICATION_MODAL);
I have this code sample in a separate jDialog (jDialog is in the same package as that of JFrame) which used to check (using a Thread) if the jCheckBox1 in the jFrame is whether visible or not. JDialog is set to visible by clicking a JLabel (Change Password) in JFrame. I have not set the visibility of the JFrame even to false even after I click on the Change Password JLabel.
The problem I encountered is that even if the JFrame is not visible i.e when I run the JDialog separately (without clicking on the Change Password JLabel) it prints the "Visible" and I'm more than sure that the jFrame is not visible and not running.
This is the code snippet (Thread) I have used to check the visibility of the JFrame's jCheckBox1:
LockOptions lock = new LockOptions();
private void setLocation2() {
new Thread() {
public void run() {
boolean running = true;
while (running) {
try {
Thread.sleep(1000);
if (lock.jCheckBox1.isVisible()) {
System.out.println("Visible");
} else {
System.out.println("Not Visible");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
And this is the Code I have written in JFrame's Change Password JLabel:
private void jLabel9MouseClicked(java.awt.event.MouseEvent evt) {
Container c = new ChangePassword(this, rootPaneCheckingEnabled);
if (!c.isShowing()) {
c.setVisible(true);
hideMeToSystemTray();
this.requestFocusInWindow();
}
}
But when I run the JDialog separately (without clicking on the Change Password JLabel) it prints the "Visible"
I have attached a Screenshots of both JFrame and JDialog
JFrame containing jCheckBox1
JDialog:
OK, let's have the simplest possible example.
The following code creates a main frame having a button to create a new frame of class LockOptionsWindow, which extends JFrame.
The class FrameDemo implements Runnable. So can it be accessed on the event dispatching thread using SwingUtilities.invokeLater as mentioned in Swing's Threading Policy. So it is possible creating a new thread checklockoptionswindow which then can check whether the new window created by the button is visible or not visible.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FrameDemo extends WindowAdapter implements ActionListener, Runnable {
private LockOptionsWindow lockoptionswindow;
private Thread checklockoptionswindow = new Thread();
private void showLockOptionsWindow() {
if (lockoptionswindow != null && lockoptionswindow.isDisplayable()) {
lockoptionswindow.setVisible(true);
lockoptionswindow.setExtendedState(Frame.NORMAL);
} else {
lockoptionswindow = new LockOptionsWindow();
lockoptionswindow.setSize(new Dimension(300, 100));
lockoptionswindow.setVisible(true);
lockoptionswindow.setExtendedState(Frame.NORMAL);
}
}
private void startCheckLockOptionsWindow() {
if (!checklockoptionswindow.isAlive()) {
checklockoptionswindow = new Thread() {
public void run() {
boolean running = true;
while (running) {
try {
Thread.sleep(1000);
if (lockoptionswindow.isVisible()) {
if (lockoptionswindow.getExtendedState() == Frame.ICONIFIED) {
System.out.println("Visible iconified");
} else {
System.out.print("Visible on screen ");
int x = lockoptionswindow.getLocation().x;
int y = lockoptionswindow.getLocation().y;
System.out.println("at position " + x + ", " + y);
}
} else {
System.out.println("Not Visible");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
checklockoptionswindow.start();
}
}
public void actionPerformed(ActionEvent e) {
showLockOptionsWindow();
startCheckLockOptionsWindow();
}
public void run() {
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JButton button = new JButton("Show LockOptions frame");
button.addActionListener(this);
Container contentPane = frame.getContentPane();
contentPane.add(button);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new FrameDemo());
}
class LockOptionsWindow extends JFrame {
public LockOptionsWindow() {
super("LockOptions frame");
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
}
}
}
Edited to determine whether the LockOptionsWindow is visible iconified only or is really showed as window on the screen.
I'm quite new on JAVA, and i have a question (i hope my english is not too bad).
Here is my process :
Open a first JFrame in the Main, with a JButton (to open the second
JFrame).
On click, with ActionLister, i call the process to open my second
window, with a black background (this works very well).
BUT, if i add a long process (in my code, just a sleep(5000)) just after setVisible() my second JFrame, this one will appear in white, and waits for the sleep(5000) to end before being black.
Questions :
Can someone tell me why the second JFrames appears white until the
end of process ? Maybe i make something wrong when i build my JFrame
?
Can someone tell me how to show my second JFrame black BEFORE the process ends ?
I searched for a long time, and saw that if my second window is built direct in the main thread it's ok even with the sleep before end of process.
But when i am in another thread (like when i click on the button), that doesn't work good !
SECOND PART :
On click on the button from the first window :
The second window shows up (empty with black background). then, the result's calcul is launched.
Calculate the result cant take 20sec, and will find 1 element each 5 seconds.
Each times an element is found, i want it to be shown in the second window.
For that, i added an observer on this result from the JFrame, which will add an element each time one element is found. I hope you understand.
Here picture of what i want to make :
Process
Here my project .JAR : http://dl.free.fr/b5IUSStBJ
Here my result's calcul :
public void launchCalculateResult(){
String[] tabelements = {"test1","test2", "test3", "test4", "test5"};
for (int i=0; i < 5; i++){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
_elementslist.add(tabelements[i]);
notifyObservers();
}
}
you can see that it adds an element in a list each 2 seconds, and then notify the observers (my second window), then the observer adds an element :
public void refresh(Observable o) {
_otherwindow.addResultElement(_result.getLastElement());
}
The behaviour I got :
The Result calculates good, and in the end the second window looks good, with its 5 elements. But during the result's search, my second windows remains empty and white . . .
I repeat the aim :
Each time an element is added in the Result, i want to show it in my second window.
You're calling the long process on the Swing event thread, and this will tie up the thread preventing it from doing its important jobs, including painting your new JFrame.
The canonical solution is to use a background thread for your long processes, and for Swing GUI's, you'd want to use a SwingWorker -- if the background process needs to communicate with the GUI (which is usually the case).
For the details on this problem and solution, please check out: Concurrency in Swing
Side issue: you'll usually not want to show multiple JFrames in your application. For why this is important and for how you can improve this design, please check out Multiple JFrames
For example
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class SwingExample extends JPanel {
private JButton openDialogBtn = new JButton(new OpenDialogAction("Open Dialog"));
private JDialog dialog;
private DialogPanel dialogPanel = new DialogPanel();
public SwingExample() {
setPreferredSize(new Dimension(400, 400));
add(openDialogBtn);
}
private class OpenDialogAction extends AbstractAction {
public OpenDialogAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
dialogPanel.setText("");
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(SwingExample.this);
dialog = new JDialog(win, "Dialog", ModalityType.MODELESS);
dialog.add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(win);
}
new SwingWorker<Void, Integer> () {
private final int maxI = 5;
#Override
protected Void doInBackground() throws Exception {
for (int i = 0; i < maxI; i++) {
publish(i);
Thread.sleep(1000);
}
return null;
}
protected void process(java.util.List<Integer> chunks) {
for (Integer chunk : chunks) {
dialogPanel.setText("Time: " + chunk);
}
};
protected void done() {
dialogPanel.setText("Done!");
};
}.execute();
dialog.setVisible(true);
}
}
private class DialogPanel extends JPanel {
private JTextField textField = new JTextField(10);
public DialogPanel() {
setBackground(Color.BLACK);
setPreferredSize(new Dimension(200, 200));
add(textField);
}
public void setText(String text) {
textField.setText(text);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
SwingExample mainPanel = new SwingExample();
JFrame frame = new JFrame("SwingExample");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
Example 2: handles Strings being passed into a JList<String> using a SwingWorker<Void, String>
import java.awt.Color;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import javax.swing.*;
#SuppressWarnings("serial")
public class SwingExample extends JPanel {
private JButton openDialogBtn = new JButton(new OpenDialogAction("Open Dialog"));
private JDialog dialog;
private DialogPanel dialogPanel = new DialogPanel();
public SwingExample() {
setPreferredSize(new Dimension(400, 400));
add(openDialogBtn);
}
private class OpenDialogAction extends AbstractAction {
public OpenDialogAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
dialogPanel.clearList();
if (dialog == null) {
Window win = SwingUtilities.getWindowAncestor(SwingExample.this);
dialog = new JDialog(win, "Dialog", ModalityType.MODELESS);
dialog.add(dialogPanel);
dialog.pack();
dialog.setLocationRelativeTo(win);
}
new SwingWorker<Void, String>() {
#Override
protected Void doInBackground() throws Exception {
String[] tabelements = { "test1", "test2", "test3", "test4", "test5" };
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
publish(tabelements[i]);
}
return null;
}
protected void process(java.util.List<String> chunks) {
for (String chunk : chunks) {
dialogPanel.addText(chunk);
}
};
protected void done() {
dialogPanel.addText("Done!");
};
}.execute();
dialog.setVisible(true);
}
}
private class DialogPanel extends JPanel {
private DefaultListModel<String> listModel = new DefaultListModel<>();
private JList<String> jList = new JList<>(listModel);
public DialogPanel() {
jList.setPrototypeCellValue("ABCDEFG HIJKLMNOP");
jList.setVisibleRowCount(6);
JScrollPane scrollPane = new JScrollPane(jList);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
setBackground(Color.BLACK);
setPreferredSize(new Dimension(200, 200));
add(scrollPane);
}
public void clearList() {
listModel.clear();
}
public void addText(String text) {
listModel.addElement(text);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
SwingExample mainPanel = new SwingExample();
JFrame frame = new JFrame("SwingExample");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
//Make constructor class for both JFrame then
//write this code into your JFrame where your button is accesing another JFrame
//Note:- jb=button var name,
// jf=JFrame vatr name,
// addnew()=JFrame Class to be open.
jb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
new addnew();
jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
});
It might work as well.
Creating a transparent and undercoated JDialog with a JFrame as its parent
will lose transparency upon an iconify/deiconify sequence.
Example:
final JFrame f = new JFrame();
f.setSize(200, 200);
final JDialog d = new JDialog(f, false);
d.setSize(200, 200);
d.setUndecorated(true);
d.setOpacity(.8f);
f.setLocationRelativeTo(null);
d.setLocation(f.getLocation().x + 210, f.getLocation().y);
f.setVisible(true);
d.setVisible(true);
Before iconify, the JDialog is created fine. Here is a screenshot.
After an iconify/deiconify sequence, the JDialog is 100% opaque.
I'm looking for solution to this problem and thinking I'm doing something
incorrect or missing some code.
My environment is Java(TM) SE Runtime Environment (build 1.8.0_11-b12)
running on Microsoft Windows [Version 6.1.7601].
2014-11-13: There is an open JDK bug for this problem
JDK-8062946 Transparent JDialog will lose transparency upon iconify/deiconify sequence.
https://bugs.openjdk.java.net/browse/JDK-8062946
Seems like a bit of a bug to me.
The following seems to work using JDK7 on Windows 7:
f.addWindowListener( new WindowAdapter()
{
#Override
public void windowDeiconified(WindowEvent e)
{
d.setVisible(false);
d.setVisible(true);
}
});
Try the following. I've had various problems with the undecorated JDialog and when I find a
bypass to a new symptom I add it to this method. This is a trimmed example. Add error checking, exception handling and modify as necessary to suit your needs ( ie add listener removal code if your app creates and destroys passed parentWindow objects).
package trimmed.stackoverflow.example;
import java.awt.Color;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
public class Helper_Swing {
private static Helper_Swing instance;
private Helper_Swing() {
instance = this;
}
public static Helper_Swing getInstance() {
return instance == null ? new Helper_Swing() : instance;
}
private static class J42WindowAdapter extends WindowAdapter {
final private Window window;
final private JDialog dialog;
private Color colorBG = null;
private float opacity = 0.0f;
private J42WindowAdapter(final Window window, final JDialog dialog) {
super();
this.window = window;
this.dialog = dialog;
this.colorBG = window.getBackground();
this.opacity = window.getOpacity();
}
#Override
public void windowIconified(final WindowEvent e) {
colorBG = dialog.getBackground();
opacity = dialog.getOpacity();
dialog.setOpacity(1.0f); // Must call 1st
dialog.setBackground(Color.BLACK); // Must call 2nd
}
#Override
public void windowDeiconified(final WindowEvent e) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
dialog.setBackground(colorBG);
dialog.setOpacity(opacity);
dialog.setVisible(true);
dialog.repaint();
}
});
}
}
public void bugFix_transparentJDialog(final Window parentWindow, JDialog childWindow) {
if (parentWindow != null && childWindow != null && childWindow.isUndecorated()) {
final WindowListener[] listeners = parentWindow.getWindowListeners();
for (int x = 0; x != listeners.length; x++) {
if (listeners[x] instanceof J42WindowAdapter) {
final J42WindowAdapter adapter = (J42WindowAdapter) listeners[x];
if (adapter.window == parentWindow && adapter.dialog == childWindow) {
childWindow = null;
break;
}
}
}
if (childWindow != null) {
parentWindow.addWindowListener(new J42WindowAdapter(parentWindow, childWindow));
}
}
}
}
Usage example:
Helper_Swing.getInstance().bugFix_transparentJDialog(parentWindow, dialog);
I have a Popup that is shown when a user clicks on a button. I would like to hide the popup when any of the following events occur:
The user clicks somewhere else in the application. (The background panel for example)
The user minimizes the application.
The JPopupMenu has this behavior, but I need more than just JMenuItems. The following code block is a simplified illustration to demonstrate the current usage.
import java.awt.*;
import java.awt.event.ActionEvent;
import javax.swing.*;
public class PopupTester extends JFrame {
public static void main(String[] args) {
final PopupTester popupTester = new PopupTester();
popupTester.setLayout(new FlowLayout());
popupTester.setSize(300, 100);
popupTester.add(new JButton("Click Me") {
#Override
protected void fireActionPerformed(ActionEvent event) {
Point location = getLocationOnScreen();
int y = (int) (location.getY() + getHeight());
int x = (int) location.getX();
JLabel myComponent = new JLabel("Howdy");
Popup popup = PopupFactory.getSharedInstance().getPopup(popupTester, myComponent, x, y);
popup.show();
}
});
popupTester.add(new JButton("No Click Me"));
popupTester.setVisible(true);
popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
Use a JPopupMenu. You can add any component to it, not just menu items.
As pajton noted in a previous comment, Popup is not a JComponent to which listeners can be readily bound. But, as its documentation states, "implementations of Popup are responsible for creating and maintaining their own Components to render [its subject] to the user."
So in using it as your presentation mechanism, your Popup is going to have to present itself as an actual Swing component anyway. Have it register itself to that component. Have it hide itself when the component loses focus.
import java.awt.FlowLayout;
import java.awt.Frame;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.Popup;
public class PopupTester extends JFrame {
private static class MessagePopup extends Popup
implements WindowFocusListener
{
private final JDialog dialog;
public MessagePopup(Frame base, String message) {
super();
dialog = new JOptionPane().createDialog( base, "Message" );
dialog.setModal( false );
dialog.setContentPane( new JLabel( message ) );
}
#Override public void show() {
dialog.addWindowFocusListener( this );
dialog.setVisible( true );
}
#Override public void hide() {
dialog.setVisible( false );
dialog.removeWindowFocusListener( this );
}
public void windowGainedFocus( WindowEvent e ) {
// NO-OP
}
public void windowLostFocus( WindowEvent e ) {
hide();
}
}
public static void main(String[] args) {
final PopupTester popupTester = new PopupTester();
popupTester.setLayout(new FlowLayout());
popupTester.setSize(300, 100);
popupTester.add(new JButton("Click Me") {
#Override
protected void fireActionPerformed(ActionEvent event) {
Point location = getLocationOnScreen();
MessagePopup popup = new MessagePopup( popupTester, "Howdy" );
popup.show();
}
});
popupTester.add(new JButton("No Click Me"));
popupTester.setVisible(true);
popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
You can add MouseListener to your background panel and hide the popup when somebody clicks on the panel.
To react on application minimization, use WindowListener attached to a JFrame.
Etc, etc. May seem tedious, but surely will work.
Thanks pajton and Noel Ang for getting me pointed in the right direction! Here is the solution that I ended up with. I'm just including it here so that others may benefit from it.
I ended up going with a JWindow since it doesn't get the window decorations but does get focus events.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PopupTester extends JFrame {
private static class MessagePopup extends Popup implements WindowFocusListener {
private final JWindow dialog;
public MessagePopup(Frame base, JLabel component, int x, int y) {
super();
dialog = new JWindow(base);
dialog.setFocusable(true);
dialog.setLocation(x, y);
dialog.setContentPane(component);
component.setBorder(new JPopupMenu().getBorder());
dialog.setSize(component.getPreferredSize());
dialog.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
dialog.setVisible(false);
}
}
});
}
#Override
public void show() {
dialog.addWindowFocusListener(this);
dialog.setVisible(true);
}
#Override
public void hide() {
dialog.setVisible(false);
dialog.removeWindowFocusListener(this);
}
public void windowGainedFocus(WindowEvent e) {
// NO-OP
}
public void windowLostFocus(WindowEvent e) {
hide();
}
}
public static void main(String[] args) {
final PopupTester popupTester = new PopupTester();
popupTester.setLayout(new FlowLayout());
popupTester.setSize(300, 100);
popupTester.add(new JButton("Click Me") {
#Override
protected void fireActionPerformed(ActionEvent event) {
Point location = getLocationOnScreen();
int x = (int) location.getX();
int y = (int) (location.getY() + getHeight());
JLabel myComponent = new JLabel("Howdy");
MessagePopup popup = new MessagePopup(popupTester, myComponent, x, y);
popup.show();
}
});
popupTester.add(new JButton("No Click Me"));
popupTester.setVisible(true);
popupTester.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
You could add a FocusListener to your popup-window, and dispose it when it loses focus. However, that will cause you some troubles when the focus loss is due to some other application (new windows comes to the foreground, you switch virtual desktops, etc.)
But perhaps you (a) know that that cannot happen in your case or (b) would want to close the popup in such cases anyway, a focus-based approach may still be interesting to you.
I know this is an old question but I really needed the Popup to work in my case. So I tried a few things and the following is my solution.
Add a FocusListener to the component you add to the popup and program the focusLost event on that component to hide the popup when focus is lost. Call the requestFocus method on your component just after showing the popup.
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.*;
public class PopupTester extends JFrame {
JButton myButton = new JButton("Click Me");
JLabel myComponent = new JLabel("Howdy");
Popup popup = null;
public PopupTester() {
setLayout(new FlowLayout());
setSize(300, 100);
add(myButton);
add(new JButton("No Click Me"));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myComponent.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent e) {
if (popup != null) {
popup.hide();
}
}
});
myButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
if (popup != null) {
popup.hide();
popup = null;
}
Point location = myButton.getLocationOnScreen();
int y = (int) (location.getY() + myButton.getHeight());
int x = (int) location.getX();
popup = PopupFactory.getSharedInstance().getPopup(PopupTester.this, myComponent, x, y);
popup.show();
myComponent.requestFocus();
}
});
}
public static void main(String[] args) {
PopupTester popupTester = new PopupTester();
popupTester.setVisible(true);
}
}