I have a JComboBox (among other components) inside a JPopupMenu. It turns out that whenever I open the combo box's popup (to select an item), the parent JPopupMenu closes. I've been trying to find a way to override this feature, to no avail.
Does anyone have any suggestions to prevent closing the parent JPopupMenu? Thanks!
that not possible directly, its very hard to override known bug, in other hands Swing doesn't allows two lightwieght popup components in same time
import javax.swing.*;
import java.awt.event.*;
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(400, 400);
frame.setVisible(true);
String[] list = {"1", "2", "3", "4",};
JComboBox comb = new JComboBox(list);
final JPopupMenu pop = new JPopupMenu();
pop.add(comb);
frame.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
System.out.println("mousePressed");
pop.show(e.getComponent(), e.getX(), e.getY());
}
});
}
}
but workaround is very simple use JWindows or un-decorated JDialog with JComboBox instead of JPopup
Related
I'm implementing an "in app" search engine with Swing and I want it to behave exactly like Windows 10's search box.
The search box should:
Open above and to the right of the search button, touching the button's edge.
Have the focus when open.
Close (if open) on a press of the search button.
Close (if open) when pressing with the mouse anywhere out of the search box.
It was perfect if JPopUpMenu could have JDialog as it's child but since it can't I need to implement the behaviors from scratch (or do I?).
This is my first time using Swing and I'm having difficulties implementing everything by myself.
I tried looking for examples online but I couldn't find much helpful information.
Is there a workaround to the fact that JPopUpMenu can't host JDialog?
Are there examples of implementing the behaviors I described?
Thanks
===============================Edit============================
Thanks for the comments so far. I've managed to get the behavior I wanted except one issue.
The following code creates a frame with a button:
public static void main(String[] args){
JFrame mainWindow = new JFrame();
mainWindow.setSize(420,420);
mainWindow.setVisible(true);
JFrame popUp = new JFrame();
popUp.setSize(210, 210);
JButton button = new JButton("button");
mainWindow.add(button);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(!button.isSelected()){
button.setSelected(true);
popUp.setVisible(true);
}
else{
button.setSelected(false);
popUp.setVisible(false);
}
}
});
popUp.addWindowFocusListener(new WindowAdapter() {
#Override
public void windowLostFocus(WindowEvent e) {
popUp.setVisible(false);
}
});
}
When I click the button, a pop-up window appears and if I click out of the main window the pop up disappear but then when I want to re-open the pop-up I need to press the button twice.
How can I get the button to operate correctly when the pop-up was closed due to lose of focus?
Your "solution" is very brittle. Try moving the main JFrame before left-clicking on the JButton.
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Netbeans section. Study the rest of the tutorial.
I created a main JFrame that pops up a JDialog. I put the close JButton on the JDialog. The JDialog is modal, meaning you cannot access the main JFrame while the JDialog is visible.
You can place the JDialog anywhere you wish on the screen. Normally, you have a JDialog appear in the center of the parent JFrame. That's where users expect a dialog to appear. I placed the JDialog towards the upper left, just to show you how it's done.
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class PopupExample {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new PopupExample().createAndShowGUI());
}
private JFrame mainWindow;
public void createAndShowGUI() {
mainWindow = new JFrame("Main Window");
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.add(createMainPanel(), BorderLayout.CENTER);
mainWindow.pack();
mainWindow.setLocationByPlatform(true);
mainWindow.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(200, 200, 200, 200));
JButton button = new JButton("button");
panel.add(button);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
createAndShowDialog(mainWindow);
}
});
return panel;
}
private void createAndShowDialog(JFrame frame) {
JDialog dialog = new JDialog(frame, "Dialog", true);
dialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
dialog.add(createDialogPanel(dialog), BorderLayout.CENTER);
dialog.pack();
// Here's where you set the location of the JDialog relative
// to the main JFrame
Point origin = frame.getLocation();
dialog.setLocation(new Point(origin.x + 30, origin.y + 30));
dialog.setVisible(true);
}
private JPanel createDialogPanel(JDialog dialog) {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(100, 100, 100, 100));
JButton button = new JButton("Close");
panel.add(button);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
dialog.dispose();
}
});
return panel;
}
}
I'm making a program that has a popup menu with two buttons, one of which should close the popup menu, but I have no idea how to do that and googling hasn't gone too well.
I've tried using popup.hide() but then the menu wouldn't come back, despite doing so when I tried just moving the popup. It also required me to put a SuppressWarning in that case and it took a few seconds for it to close at all. Is there any better way of doing it?
I'm not sure what kind of code is relevant, but here's the relevant buttons and their roles in this(I skipped all the creating the GUI parts that didn't seem relevant, everything looks good and I know that the buttons are working):
package test;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
interface CustomButton {
JButton create();
void react(JPopupMenu popup, JFrame frame);
}
class ErrandsButton implements CustomButton {
private JButton errands = new JButton("Errands");
public JButton create() {
return errands;
}
public void react(JPopupMenu popup, JFrame frame) {
errands.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
popup.show(frame, 120, 65);
}
});
}
}
class Test {
static JFrame frame = new JFrame("List");
static CustomButton errands = new ErrandsButton();
static JButton cancelTask = new JButton("Cancel");
static JPopupMenu popup = new JPopupMenu();
static void cancelTask() {
cancelTask.addActionListener(new ActionListener() {
#SuppressWarnings("deprecation")
public void actionPerformed(ActionEvent e) {
popup.hide();
}
});
}
public static void main(String args[]) {
createInterface();
cancelTask();
errands.react(popup, frame);
}
static void createInterface() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
JPanel popup1 = new JPanel();
JPanel button = new JPanel();
popup1.add(cancelTask);
popup.add(popup1);
frame.add(popup);
button.add(errands.create());
frame.getContentPane().add(BorderLayout.CENTER, button);
frame.setVisible(true);
}
}
Use popup.setVisible(true) and popup.setVisible(false).
frame.add(popup); is the problem. Do not add a JPopupMenu to a Container. Instead, use setComponentPopupMenu.
Alternatively, you could do the work yourself by adding a MouseListener whose mousePressed, mouseReleased and mouseClicked methods call isPopupTrigger and show. (It is vital that you do this in all three of those methods—different platforms have different conditions for showing popup menus.)
But really, using setComponentPopupMenu is easier.
I've written a JWindow that acts a bit like a fancy menu in my application, popping up when a button is pressed. However, I'd like it to disappear if the user clicks anywhere in the main window. I can of course add a mouse listener to the main window, but that doesn't add it to all the components on the window itself, and looping over all the components seems like a bit of a brute force solution (and can't be guaranteed to work if the components on the window change.)
What's the best way of going about doing something like this?
Try to use Toolkit.getDefaultToolkit().addAWTEventListener(listener, eventMask). Find eventMask that filters only mouse clicks. This AWT listener is global for whole application, so you can see all events that happen.
I'd like it to disappear if the user clicks anywhere in the main window
Add a WindowListener to the child window and then handle the windowDeactiveated() event and invoke setVisible(false) on the child window.
Working example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class DialogDeactivated
{
public static void main(String[] args)
{
final WindowListener wl = new WindowAdapter()
{
public void windowDeactivated(WindowEvent e)
{
e.getWindow().setVisible(false);
}
};
JButton button = new JButton("Show Popup");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JButton button = (JButton)e.getSource();
JFrame frame = (JFrame) SwingUtilities.windowForComponent(button);
JDialog dialog = new JDialog(frame, false);
dialog.setUndecorated(true);
dialog.add( new JButton("Dummy Button") );
dialog.pack();
dialog.setLocationRelativeTo( frame );
dialog.setVisible( true );
dialog.addWindowListener( wl );
}
});
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(button, BorderLayout.NORTH);
frame.setSize(400, 400);
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
I'm trying to use a compound Swing component as part of a Menu.
Everything works just fine, apart from one detail: The component contains JComboBoxes and whenever the user clicks on one of them to open its dropdown, the dropdown opens but the menu disappears. Is it possible to make the menu stay open when a JComboBox is clicked?
I sub-classed JMenu. This is the corresponding code:
public class FilterMenu extends JMenu {
public FilterMenu(String name) {
super(name);
final JPopupMenu pm = this.getPopupMenu();
final FilterPanel filterPanel = new FilterPanel(pm) {
#Override
public void updateTree() {
super.updateTree();
pm.pack();
}
};
pm.add(filterPanel);
}
}
FilterPanel is the custom compound component. The pm.pack() is called to adapt the size of the JPopupMenu when the filterPanel changes in size.
Thanks for your help
are you meaning this bug
import javax.swing.*;
import java.awt.event.*;
public class Test {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(400, 400);
frame.setVisible(true);
String[] list = {"1", "2", "3", "4",};
JComboBox comb = new JComboBox(list);
final JPopupMenu pop = new JPopupMenu();
pop.add(comb);
frame.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
System.out.println("mousePressed");
pop.show(e.getComponent(), e.getX(), e.getY());
}
});
}
}
Look at Jide OSS' PopupWindow. This provides an easy-to-use solution for this problem. Works fine for me.
Javadoc is here.
I prepared a small test case below. My problem is when i right click on the window. JPopupMenu show up but if i click anywhere outside the JWindow menu does not disappear. I have to click somewhere on the window to get rid of it which is not the expected behavior.
EDIT:
after reading akf's answer i switched to JFrame, when frame is in focus and pop up menu is showing it disappears when you click on another window. but if the window does not have focus and you click somewhere menu does not disappear.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class test {
static class window extends JWindow
implements MouseListener, MouseMotionListener{
JPopupMenu popMenu;
JPanel panel = new JPanel();
Point location;
MouseEvent pressed;
public window(){
addMouseListener( this );
addMouseMotionListener( this );
JLabel label = new JLabel("JWindow", JLabel.CENTER);
initPopMenu();
add(label);
setVisible(true);
setAlwaysOnTop(true);
setLocationRelativeTo(null);
pack();
}
public void initPopMenu(){
popMenu = new JPopupMenu();
JMenuItem item;
item = new JMenuItem( "Title" );
item.setEnabled(false);
popMenu.add(item);
popMenu.addSeparator();
item = new JMenuItem( "Item One" );
popMenu.add(item);
item = new JMenuItem( "Item 2" );
popMenu.add(item);
item = new JMenuItem( "Item 3" );
popMenu.add(item);
}
public void mousePressed(MouseEvent e)
{
pressed = e;
int nModifier = e.getModifiers();
if (((nModifier & InputEvent.BUTTON2_MASK) != 0)||
((nModifier & InputEvent.BUTTON3_MASK) != 0))
popMenu.show( this, e.getX(), e.getY() );
}
public void mouseClicked(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {}
public void mouseDragged(MouseEvent me){
}
public void mouseMoved(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
public static void main(String[] args) {
window dw = new window();
}
}
Take a look at the Java Doc for JWindow.isFocusableWindow
A JWindow cannot be the focused window unless it has an owner and the owner is visible.
You're using the default constructor, so your JWindow has the shared owner asn is not focusable. When it is not focusable, it cannot detect the loss of focus when you click somewhere else.
I changed JWindow to JFrame and added a call to setUndecorated(true); before the call to setVisible and it's working for me. If these changes do not make it work for you, please post the version of Java you are using: java -fullversion
In Java 6 on Windows, I cannot get the popup to even display with the code you have provided. On the other hand, if I change your superclass to JFrame, it works as desired, with the popup going away when I click outside of the window. Is there a reason why you are using JWindow as your superclass and not JFrame? If you wish to have a border-less/title-less window, you can call setUndecorated(true) on your JFrame (before you set visible and pack, of course.)
What about hiding the menu if it's visible from within the MouseExited method?