When I am trying to hide or close popup dialog invoked as a modal, the components disappears as they should, but the grey screen that indicates modality of the window is still visible, until the fist mouse click event at this window area.
WebPopup darkenScreen = new WebPopup(PopupStyle.gray);
ContructPopUP(darkenScreen);
darkenScreen.showPopupAsModal(this);
And popup settings method :
private void ContructPopUP(WebPopup darkenScreen)
{
final JFrame mFrame = this;
final WebTextField inputTime = new WebTextField("(sekundy)");
darkenScreen.setLayout(new GridLayout(3, 1));
darkenScreen.add(new WebLabel("Podaj czas : "));
darkenScreen.add(inputTime);
darkenScreen.add(new WebButton(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
int secTime = Integer.parseInt(inputTime.getText());
if (secTime > 0 && secTime < 7200)
{
Connection.TurnOff(secTime);
System.out.println("clicked!");
}
darkenScreen.hidePopup();
}
}));
}
When invoking as ordinary popup everything disappears as indented. I've tried to close it in many ways but none of them worked.
Before clicking button and executing popup.hide :
after doing it :
Assuming you are using the WebLaF library, I think your problem might be caused by the PopupLayer.hidePopup method. This method is called by the WebPopup.hidePopup method and should hide the modal popup, but as you noticed, the gray layer does not disappear. If you look at PopupLayer.hideAllPopups, all popups are removed in this method and the popup layer is made invisible. I do not have experience with the WebLaF library and it feels hackish, but you might be able to solve your problem by hiding the popup layer yourself:
import com.alee.laf.button.WebButton;
import com.alee.laf.label.WebLabel;
import com.alee.laf.text.WebTextField;
import com.alee.managers.popup.PopupStyle;
import com.alee.managers.popup.WebPopup;
import java.awt.GridLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class ModalWebPopup {
public static void main(final String[] arguments) {
new ModalWebPopup().launchGui();
}
private void launchGui() {
final JFrame frame = new JFrame("Stack Overflow: modal WebPopup");
frame.setBounds(100, 100, 800, 600);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
final JPanel panel = new JPanel();
final JButton button1 = new JButton("Show a modal WebPopup");
panel.add(button1);
frame.getContentPane().add(panel);
button1.addActionListener(actionEvent -> {
final WebPopup darkenScreen = new WebPopup(PopupStyle.gray);
constructPopup(darkenScreen);
darkenScreen.showPopupAsModal(frame);
});
frame.setVisible(true);
}
private void constructPopup(final WebPopup darkenScreen) {
//final JFrame mFrame = this;
final WebTextField inputTime = new WebTextField("(sekundy)");
darkenScreen.setLayout(new GridLayout(3, 1));
darkenScreen.add(new WebLabel("Podaj czas : "));
darkenScreen.add(inputTime);
darkenScreen.add(new WebButton(actionEvent -> {
int secTime = Integer.parseInt(inputTime.getText());
if (secTime > 0 && secTime < 7200) {
//Connection.TurnOff(secTime);
System.out.println("clicked!");
}
System.out.print("Hide the modal WebPopup ");
// Normal way to hide the popup:
//darkenScreen.hidePopup();
System.out.println("by making the parent of the WebPopup invisible.");
// Alternative way to hide the popup:
darkenScreen.getParent().setVisible(false);
// Compare the PopupLayer.hideAllPopups and PopupLayer.hidePopup methods
// for more details.
}));
}
}
Related
When I use JFileChooser then try to add other components, they don't show up. If I remove JFileChooser they do show up. I'm writing in java on eclipse, and there are two files.
I have removed a majority of my code to simplify the problem, but it still exists.
Main.java:
import java.awt.Color;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.swing.JFrame;
public class Main {
public static void main(String args[]) throws InterruptedException, IOException {
int width = 1280;
int height = 720;
Frame f = new Frame(Color.BLACK, width, height);
JFrame frame = new JFrame("Title"); //create a new window and set title on window
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //set the window to close when the cross in the corner is pressed
frame.setSize(width,height);
frame.add(f); //add the content of the game object to the window
frame.setVisible(true);
long interval = (long)10 * 10000000;
long t = 0;
while(true) {
if(System.nanoTime() - t >= interval) { //repaints at a certain fps
t = System.nanoTime();
f.repaint();
}
TimeUnit.NANOSECONDS.sleep(10);
}
}
}
Frame.java:
import java.awt.Color;
import java.awt.Graphics;
import java.io.IOException;
import javax.swing.JSlider;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
public class Frame extends JPanel {
int menuNum = 0;
boolean first = true;
JButton nextButton = new JButton("Next");
JSlider slider = new JSlider(0,255,0);
JFileChooser fileChooser = new JFileChooser();
public Frame(Color background, int w, int h) throws IOException { //initialize
this.setBackground(background);
setFocusable(true);
}
public void paintComponent(Graphics G) {
super.paintComponent(G);
G.setColor(Color.WHITE);
G.drawString("MenuNum: " + menuNum, 1000, 500); //for debugging
if(menuNum == 0) { //first menu
if(first) { //only run once
first = false;
this.removeAll();
this.add(nextButton);
System.out.println("HERE");
}
if(fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) { //if "Done" is selected
menuNum = 1; //go to next menu
first = true;
}
}
if(menuNum == 1) { //second menu
if(first) { //only run once
first = false;
this.removeAll();
this.add(nextButton);
this.add(slider); //<This is the slider that is not showing up
System.out.println("HERE2");
}
}
}
}
If you are running this on your own machine, you can select any file to test it, since it does nothing with the selected file.
I am somewhat new to JPanels and JFrames so any advice will be well appreciated.
Thanks.
First of all there is absolutely no reason to do any custom painting. You should never try to add/remove components from a JPanel in a painting method.
The components should be added to the panel in the constructor of your class. So this means the button should be added to the panel.
Then you add an ActionListener to the button. When the button is clicked you do some processing.
If you want to alter the components on the panel in the ActionListener then the basic logic is:
panel.remove(...);
panel.add(...);
panel.revalidate();
panel.repaint();
So you need the revalidate() to invoke the layout manager. Otherwise the size of the added component is (0, 0), which means there is nothing to paint.
Learn Swing basics by reading the Swing Tutorial. Maybe start with section on:
How to Write an ActionListener
How to Use Sliders
How to Use CardLayout (instead of adding/removing components).
Just follow same idea, you will get
public MyControlPanel() {
initComponents();
JSlider slider = new JSlider();
slider.setMajorTickSpacing(10);
slider.setPaintLabels(true);
slider.setPaintTicks(true);
JTextField boundary_length = new JTextField("Boundary Length");
JTextField area = new JTextField("Area");
setLayout(new FlowLayout());
this.add(slider);
this.add(area);
this.add(boundary_length);
}
I had a similar problem and I found the solution with the updateUI() method. Look below:
private void refresh()
{
if(slider != null)
{
slider.updateUI();
}
}
So, when your JFilechooser closing you must call the refresh() thus:
if(fileChooser.showOpenDialog(null) == 0 // this is the value for JFileChooser.APPROVE_OPTION)
{ //if "Done" is selected
menuNum = 1; //go to next menu
first = true;
}
else {
refresh();
}
I hope this should works.
I have 2 jframes, 1 is kinda like the main menu, i want an attribute to change in the level jframe when a button is pressed so i tried:
SpeelVeld frame = new SpeelVeld();
frame.level = 1;
System.out.println(frame.level);
I used the sout to see what really happens because it wasnt working, but i see that the level goes from 0 to 1 back to 0 and goes on and on, does someone know why and how to fix?
SpeelVeld frame = new SpeelVeld();
frame.setBounds(0,0,519,591);
frame.setLocationRelativeTo(null);
frame.getContentPane().setBackground(Color.WHITE);
frame.setTitle("RWINA");
frame.setVisible(true);
frame.setLevel(1);
this is in the main method of my original GameProject file.
How can i make a jdialog
I have 2 jframes, 1 is kinda like the main menu,
You shouldn't use 2 JFrames for this. The dependent sub-window, likely your main menu window, should in fact be a JDialog, probably a non-modal dialog from the looks of it.
I want an attribute to change in the level jframe when a button is pressed so i tried:
SpeelVeld frame = new SpeelVeld();
frame.level = 1;
System.out.println(frame.level);
and here's a big problem. Understand that in this code, you're creating a new SpeelVeld object, the stress being on the word new. Changing the state of this object will have no effect on the other SeelVeld object that is currently being displayed. Do do that, your second window will need a valid reference to the displayed SeelVeld object. How to do this will depend all on code not yet shown, but often it can be done simply by passing in the displayed SpeelVeld object into the main menu object by use of a constructor parameter or setter method.
For example:
import java.awt.Dialog.ModalityType;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
// JPanel for our main GUI
public class SpeelVeldFoo {
private static void createAndShowGui() {
// JPanel used by the main JFrame
SpeelVeldPanel speelVeldPanel = new SpeelVeldPanel();
// JPanel used by the main menu JDialog. Pass the above into it
MainMenuPanel mainMenuPanel = new MainMenuPanel(speelVeldPanel);
// create your JFrame
JFrame frame = new JFrame("Speel Veld");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(speelVeldPanel); // add the JPanel
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
// create your non-modal JDialog
JDialog menuDialog = new JDialog(frame, "Main Menu", ModalityType.MODELESS);
menuDialog.add(mainMenuPanel); // add the JPanel that holds its "guts"
menuDialog.pack();
menuDialog.setLocationByPlatform(true);
menuDialog.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
createAndShowGui();
});
}
}
#SuppressWarnings("serial")
class SpeelVeldPanel extends JPanel {
private int level = 1; // simple example just has a level int
private JLabel levelLabel = new JLabel("1"); // and displays it in a JLabel
public SpeelVeldPanel() {
add(new JLabel("Level:"));
add(levelLabel);
int ebGap = 50;
setBorder(BorderFactory.createEmptyBorder(ebGap, 2 * ebGap, ebGap, 2 * ebGap));
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
// whenever level is changed, update the display
this.level = level;
levelLabel.setText(String.valueOf(level));
}
}
// class for the JPanel held by the JDialog
#SuppressWarnings("serial")
class MainMenuPanel extends JPanel {
private JSpinner levelSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 5, 1));
private SpeelVeldPanel speelVeldPanel = null; // reference to the main GUI
// note the parameter.... you pass in the displayed main GUI so you can
// change it
public MainMenuPanel(final SpeelVeldPanel speelVeldPanel) {
this.speelVeldPanel = speelVeldPanel; // set the field
// respond when the spinner's data changes
levelSpinner.addChangeListener(new LevelListener());
add(new JLabel("Set the Speel Veld's level:"));
add(levelSpinner);
int ebGap = 10;
setBorder(BorderFactory.createEmptyBorder(ebGap, ebGap, ebGap, ebGap));
}
private class LevelListener implements ChangeListener {
#Override
public void stateChanged(ChangeEvent e) {
// when the spinner's data changes
int level = (int) levelSpinner.getValue(); // get the data
speelVeldPanel.setLevel(level); // and send it to the main GUI
}
}
}
You'll note that I don't like extending JFrame or JDialog if I can avoid it. My feeling is that one can paint oneself into a corner by having your class extend JFrame, forcing you to create and display JFrames, when often more flexibility is called for. More commonly your GUI classes will be geared towards creating JPanels, which can then be placed into JFrames or JDialogs, or JTabbedPanes, or swapped via CardLayouts, wherever needed. This will greatly increase the flexibility of your GUI coding.
You probably want the JFrame to be the top-level container, then have a JPanel that holds your menu. The menu could be whatever you want, I'm using a JTextArea. Then, you need a JButton for the JPanel or JFrame that when pressed, changes the text in the JTextArea. Here is an implementation that you could work from. I'm using the ActionEvent as the trigger for when to mess with the JTextArea:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class SimpleSwing {
public static void main(String[] args) {
JFrame mainFrame = new JFrame();
JPanel mainMenuPanel = new JPanel();
JTextArea textAttribute = new JTextArea("Original Text");
JButton changeAttributeButton = new JButton("Change Attribute");
changeAttributeButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
textAttribute.setText("Whatever new text you want");
}
});
mainMenuPanel.add(textAttribute);
mainMenuPanel.add(changeAttributeButton);
mainFrame.add(mainMenuPanel);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainFrame.setSize(500, 500);
mainFrame.setVisible(true);
}
}
people of the internet.
I want to have a sort of start screen for a game ive been writing. Thus far it features 4 Buttons for each one of the 4 Players that change color on click from red to green and vice versa representing their individual "ready"-status if that makes sense. I used JFrame and JButtons.
Now i want that window to close if every one of those Buttons is currently set to "ready" aka button.getBackground() == Color.GREEN.
Any suggestions as to which EventListeners to use for this/implementation tips/code snippets would be greatly appreciated since my research on Windowclosing on Event didnt bring up much for me.
Thank you in advance and Greetings.
Since you're awaiting and acting on button presses, the most logical listener would be an ActionListener.
Consider making the buttons JToggleButtons, and then in your listener querying each button to see if it is selected (isSelected()) and if so, launch your program. As a side bit, I'd consider making the intro window a JDialog and not a JFrame, either that or making it a JPanel and swapping it out via a CardLayout when necessary.
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 java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class AreWeReady extends JPanel {
List<AbstractButton> buttons = new ArrayList<>();
private int userCount;
public AreWeReady(int userCount) {
this.userCount = userCount;
ButtonListener buttonListener = new ButtonListener();
for (int i = 0; i < userCount; i++) {
JButton btn = new JButton("User " + (i + 1));
buttons.add(btn);
btn.addActionListener(buttonListener);
add(btn);
}
}
private class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
AbstractButton btn = (AbstractButton) e.getSource();
Color c = Color.GREEN.equals(btn.getBackground()) ? null : Color.GREEN;
btn.setBackground(c);
for (AbstractButton button : buttons) {
if (!Color.GREEN.equals(button.getBackground())) {
// if any button does not have a green background
return; // leave this method
}
}
// otherwise if all are green, we're here
Window win = SwingUtilities.getWindowAncestor(btn);
win.dispose();
// else launch your gui
}
}
private static void createAndShowGui() {
int userCount = 4;
AreWeReady areWeReadyPanel = new AreWeReady(userCount);
JFrame frame = new JFrame("Main Application");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(Box.createRigidArea(new Dimension(400, 300)));
frame.pack();
frame.setLocationByPlatform(true);
JDialog dialog = new JDialog(frame, "Are We Ready?", ModalityType.APPLICATION_MODAL);
dialog.add(areWeReadyPanel);
dialog.pack();
dialog.setLocationByPlatform(true);
dialog.setVisible(true);
// this is only reached when the modal dialog above is no longer visible
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I have a problem which is most likely "simple" however I can't figure it out. I am trying to reference my current JFrame so that I can dispose of it, and create a new one, thus "resetting" the program, however I and having trouble figuring out how to reference the JFrame, I have tried, super, this and getParent(), but none of the seem to work. Thanks for any / all help. ^^
Here is my code:
Main Class, just sets up the Jframe and calls the class that creates everything:
public static void main(String args[]) {
JFrame window = new JFrame();
Director director = new Director(window, args);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setLocationRelativeTo(null);
window.pack();
window.setVisible(true);
}
}
Class the creates everything:
import java.awt.Color;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
public class Director extends JFrame implements CollisionListener {
private BrickWall wall;
private JLabel gameTitle, gameScore, gameLives;
private JPanel controlPanel;
private JButton reset, quit;
private JRadioButton hard, normal, easy;
private int score = 6, lives = 5;
private ButtonGroup difficulty;
public Director(JFrame window, String[] args) {
window.getContentPane().add(makeGamePanel(), BorderLayout.CENTER);
window.getContentPane().add(gameControlPanel(), BorderLayout.NORTH);
}
public void collisionDetected(CollisionEvent e) {
wall.setBrick(e.getRow(), e.getColumn(), null);
}
private JComponent makeGamePanel() {
wall = new BrickWall();
wall.addCollisionListener(this);
wall.buildWall(3, 6, 1, wall.getColumns(), Color.GRAY);
return wall;
}
// Reset method I'm trying to dispose of the JFrame in.
private void reset() {
JFrame frame = new JFrame();
frame.getContentPane().add(makeGamePanel(), BorderLayout.CENTER);
frame.getContentPane().add(gameControlPanel(), BorderLayout.NORTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
private JComponent gameControlPanel() {
// CONTROL PANEL PANEL!
controlPanel = new JPanel();
gameTitle = new JLabel("Brickles");
gameScore = new JLabel("Score:" + " " + score);
gameLives = new JLabel("Lives:" + " " + lives);
reset = new JButton("Reset");
quit = new JButton("Quit");
hard = new JRadioButton("Hard", false);
normal = new JRadioButton("Normal", true);
easy = new JRadioButton("Easy", false);
difficulty = new ButtonGroup();
difficulty.add(hard);
difficulty.add(normal);
difficulty.add(easy);
controlPanel.setLayout(new GridLayout(4, 2));
controlPanel.add(gameTitle);
controlPanel.add(gameScore);
controlPanel.add(hard);
controlPanel.add(gameLives);
controlPanel.add(normal);
controlPanel.add(reset);
controlPanel.add(easy);
controlPanel.add(quit);
// Action Listener, where I'm caling the reset method.
reset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
reset();
}
});
return controlPanel;
}
}
You can refer to the "outer this" from a nested class with the following syntax:
reset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Director.this.reset();
}
});
Yes, you can refer to the outer class by specifying it with the class name as noted in DSquare's good answer (1+ to it), but I urge you not to fling JFrame's at the user as you're program is trying to do. I recommend:
Instead of opening and closing multiple JFrames, use only one JFrame as the main application's window.
If you need helper windows, such as modal windows to get critical information that is absolutely needed, before the program can progress, use modal dialogs such as JDialogs or JOptionPanes.
If you need to swap GUI's, instead of swapping JFrames, swap "views" inside the JFrame via a CardLayout.
Gear your code towards creating these JPanel views and not JFrames as it will make your Swing GUI's much more flexible and portable.
I need to display a swing popup with my custom component. The popup should stay visible, until I hide it myself, but shouldn't get focus.
I have a code written by some other developer that does it in the following way:
popupMenu = new JPopupMenu();
popupMenu.add(myCustomComponent, BorderLayout.CENTER);
popupMenu.setFocusable(false);
popupMenu.setVisible(true);
popupMenu.show(parentComponent, x, y);
This seems to work, but has a bug - when the popup is visible, first mouse click outside the component is consumed by the popup. So I need to click twice to set focus to another component.
How can I fix it? Or what is correct way to make the popup?
UPDATE
At last I've managed to reproduce my problem in short code fragment. Thanks to Guillaume Polet for giving me a starting point.
Here's the code:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;
public class TestJPopup {
protected void initUI() {
JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextField textField = new JTextField("Some text field");
frame.add(textField, BorderLayout.WEST);
final JButton buttonToHit = new JButton("Hit me");
buttonToHit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
}
});
frame.add(buttonToHit);
frame.setSize(200, 100);
frame.setVisible(true);
final JPopupMenu popup = new JPopupMenu();
popup.add(new JLabel("<html>Hey!<br>I'm the popup window!</html>"),
BorderLayout.NORTH);
popup.setFocusable(false);
popup.setVisible(true);
popup.show(textField, 60, 60);
// I want to activate popup when user clicks in the text field
textField.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (popup != null) {
popup.show(textField, 60, 60);
}
}
});
}
public static void main(String[] args) throws Exception {
Class lnfClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsLookAndFeel", true,
Thread.currentThread().getContextClassLoader());
LookAndFeel feel = (LookAndFeel) lnfClass.newInstance();
UIManager.setLookAndFeel(feel);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestJPopup().initUI();
}
});
}
}
Two critical moments:
Windows look and feel used (with default not reproducible)
Mouse listener attached to text field in main frame
Not an answer, but just an example SSCCE in which I can't currently reproduce the behaviour you described. Maybe start from this code, try to reproduce the error and the edit your post with modified non-working code.
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class TestJPopup {
protected void initUI() {
JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLabel leftLabel = new JLabel("Left");
frame.add(leftLabel, BorderLayout.WEST);
final JButton buttonToHit = new JButton("Hit me");
buttonToHit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
}
});
frame.add(buttonToHit);
frame.setSize(500, 400);
frame.setVisible(true);
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.add(new JLabel("<html>A Custom<br>component<br>made to<br> simulate <br>your custom component</html>"),
BorderLayout.NORTH);
JTextField textfield = new JTextField(30);
popupMenu.add(textfield);
popupMenu.setFocusable(false);
popupMenu.setVisible(true);
popupMenu.show(leftLabel, 20, 20);
// Let's force the focus to be in a component in the popupMenu
textfield.requestFocusInWindow();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new TestJPopup().initUI();
}
});
}
}
Not a solution, but:
Looks like a bug to me, even a plain componentPopup exhibits the same mis-behaviour (in winLAF and Nimbus, not in Metal):
JTextField field = new JTextField("some popup owner");
JPopupMenu menu = new JPopupMenu();
menu.add("dummy");
field.setComponentPopupMenu(menu);
Action action = new AbstractAction("hit me!") {
#Override
public void actionPerformed(ActionEvent e) {
LOG.info("got hit!");
}
};
JComponent content = new JPanel();
content.add(new JButton(action));
content.add(field);
for quick research and/or for future readers,
this issue is reproducible and presented for,
a) JPopup
b) JMenu
tested on jdk1.6.0_25 and jdk1.7.0_04,
same issue on WinXp and Win7,
for Look and Feel to SystemLookAndFeel / WindowsLookAndFeel,
Here's a possible workaround with JWindow instead of JPopupMenu, that was proposed by mKorbel in comments:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TestJPopup {
protected void initUI() {
final JFrame frame = new JFrame(TestJPopup.class.getSimpleName());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JTextField textField = new JTextField("Some text field");
frame.add(textField, BorderLayout.WEST);
final JButton buttonToHit = new JButton("Hit me");
buttonToHit.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(buttonToHit, "You hit the button successfully");
}
});
frame.add(buttonToHit);
frame.setSize(200, 70);
frame.setVisible(true);
final JWindow popup = new JWindow();
popup.getContentPane().add(new JLabel("<html>Hey!<br>I'm the popup window!</html>"),
BorderLayout.NORTH);
popup.setLocation(frame.getLocation().x + 60, frame.getLocation().y + 60);
popup.pack();
popup.setFocusable(false);
popup.setVisible(true);
// I want to activate popup when user clicks in the text field
textField.addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
if (popup != null) {
popup.setVisible(true);
popup.setLocation(frame.getLocation().x + 60, frame.getLocation().y + 60);
popup.toFront();
}
}
});
textField.addFocusListener(new FocusAdapter() {
#Override
public void focusLost(FocusEvent e) {
if (popup != null) {
popup.setVisible(false);
}
}
});
}
public static void main(String[] args) throws Exception {
Class lnfClass = Class.forName("com.sun.java.swing.plaf.windows.WindowsLookAndFeel", true,
Thread.currentThread().getContextClassLoader());
LookAndFeel feel = (LookAndFeel) lnfClass.newInstance();
UIManager.setLookAndFeel(feel);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TestJPopup().initUI();
}
});
}
}
Here is the magic line that fixes the problem:
UIManager.put("PopupMenu.consumeEventOnClose", Boolean.FALSE);
I found this after looking into the source code for the BasicPopupMenuUI class. Apparently this behaviour is a deliberate design choice according to the following comments in the code, but it sure feels like a bug to me.
// Ask UIManager about should we consume event that closes
// popup. This made to match native apps behaviour.
By the way, it happens in Java 5 and 6 too.