JMenu consumes focuslost event in Windows7 LAF Java7 - java

If a popup menu is still open when another component is clicked, then the component does not get the event, because it's probably consumed by the popup. This happens for all JPopupmenus in general.
This happens only in Java 7 with windows LAF (Windows7). Is there a workaround? Is it a known bug?
import javax.swing.*;
import java.awt.event.*;
public class Test
{
public static void main(String[] s)
throws Exception
{
String lookAnfFeelClassName = UIManager.getSystemLookAndFeelClassName();
UIManager.setLookAndFeel(lookAnfFeelClassName);
JMenu menu = new JMenu("TEST Menu");
JMenuItem menuItem = new JMenuItem("Menu Item 1");
JMenuBar menuBar = new JMenuBar();
menu.add(menuItem);
menuBar.add(menu);
final JButton b = new JButton("Test");
b.setBounds(5, 50, 60, 20);
b.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
//If the Menu is open when I press the button, the putton is not pressed
//so I have to press it again.
JOptionPane.showMessageDialog(b, "Button Pressed");
}
}
);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(150, 150);
frame.setJMenuBar(menuBar);
frame.getContentPane().setLayout(null);
frame.getContentPane().add(b);
frame.setVisible(true);
}
}

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.

Related

invisible JMenuBar, Accelerator not working

My Program has a JMenuBar with JMenuItems.
They have a ActionListener, and I set a Shortcut with setAccelerator.
Now I am hiding the menu bar when the window become unfocused, to get more space for a displayed image.
But after the first hiding of the menubar, the hotkeys just stop working.
How can I fix that?
I created a little example code to illustrate that strange behavior:
import javax.swing.*;
import java.awt.event.*;
class Example extends JFrame{
public static void main(String[] args) {
new Example(); //main is static
}
static JMenuBar menubar; //be accessable for the ActionListener
Example() {
//JPanel
this.setSize(50,50);
this.setVisible(true);
//Menubar, static
menubar = new JMenuBar();
this.setJMenuBar(menubar);
//Menu
JMenu filemenu = new JMenu("File");
menubar.add(filemenu);
//Item
JMenuItem menuitem = new JMenuItem("Do Something...");
filemenu.add(menuitem);
menuitem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.SHIFT_DOWN_MASK)); // Shift + D
menuitem.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Action!");
}
});
JButton button = new JButton("Show/Hide menubar");
this.add(button);
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Toggle Bar!");
menubar.setVisible(!menubar.isVisible()); //Toggle
}
});
}
}
For reference:
I'm using Java 1.7.0_60-ea (Java 7) on a Mac.
But this error occurs independent of using the Mac native menu bar or the normal java menu bar inside the JFrame.
You could try to add global keybindings. How to add keybindings is explained here.
Here is an example of what you could do:
//Any component that is always visible in the window (like the image)
JComponent c;
//Get input and action map
InputMap imap = c.getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap amap = c.getActionMap();
//Put keybinding and action
imap.put(KeyStroke.getKeyStroke("shift D"), "doSomething");
amap.put("doSomething", anAction);
Note that it only works in the focused window. But should work regardless of the menubar being visible or not.

Add accelerator description to JMenuItem in GUI, without actually adding an accelerator

I'm trying to set some text to be displayed where the accelerator binding is usually displayed, for a JMenuItem.
The demarcated Ctrl+Z text in the following image is an example of what I'm trying to set, for another JMenuItem.
I don't actually want to set an accelerator for this JMenuItem, though.
I've poked around the source for several classes, like JMenuItem and BasicMenuItemUI, to no avail.
What's the simplest way to achieve this?
Thanks in advance :)
I assume the reason you want this is so you can prevent the menu from triggering the undo action a second time, when the key combination is already bound on a component on the frame, but this shouldn't be necessary. If the component consumes the key event, the menu won't detect it.
Here's an example with a JTextArea to see what I mean:
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JMenuBar menu = new JMenuBar();
frame.setJMenuBar(menu);
JMenu menuEdit = new JMenu("Edit");
menu.add(menuEdit);
JMenuItem menuEditUndo = new JMenuItem("Undo");
menuEdit.add(menuEditUndo);
menuEditUndo.setAccelerator(KeyStroke.getKeyStroke("control Z"));
menuEditUndo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("menu");
}
});
JTextArea textArea = new JTextArea(20, 40);
textArea.getInputMap().put(KeyStroke.getKeyStroke("control Z"), "undo");
textArea.getActionMap().put("undo", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("text");
}
});
frame.add(new JScrollPane(textArea));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Both the text area and the menu item have bound to the same key combo, but pressing Ctrl+Z while the text area has focus prints only "text" and never "menu". I.e., the action does not happen twice. Although this uses a JTextArea, it should be true of any component.

Java JmenuItem do something when clicked not working

I am currently trying to make a GUI with a menu that has 2 options you can select from. One being "Default Settings" and one being "Custom Settings." When you click on either one, it will take you to the new jPanel that will display the proper windows, text boxes, etc for that panel. However, I cannot seem to get the mouseClicked action to actually switch between the panels. As a test, I have a simple jLabel on each panel that says "Default" for the default panel and "custom" for the custom panel, and each menu item, when clicked respectively, should switch between them. Here is my current code:
frmLegitServerAdder = new JFrame();
frmLegitServerAdder.setTitle("Legit Server Adder 5 Million");
frmLegitServerAdder.setBounds(100, 100, 546, 468);
frmLegitServerAdder.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JMenuBar menuBar = new JMenuBar();
frmLegitServerAdder.setJMenuBar(menuBar);
JMenu mnNewMenu = new JMenu("Settings");
menuBar.add(mnNewMenu);
JMenuItem menuItemDefaultSettings = new JMenuItem("Default Settings");
mnNewMenu.add(menuItemDefaultSettings);
JMenuItem menuItemCustomSettings = new JMenuItem("Custom Logon Settings");
mnNewMenu.add(menuItemCustomSettings);
frmLegitServerAdder.getContentPane().setLayout(new CardLayout(0, 0));
final JPanel defaultSettingsPanel = new JPanel();
frmLegitServerAdder.getContentPane().add(defaultSettingsPanel, "name_416522810155567");
defaultSettingsPanel.setLayout(null);
JLabel lblDefaultArea = new JLabel("Default Area");
lblDefaultArea.setBounds(217, 11, 90, 14);
defaultSettingsPanel.add(lblDefaultArea);
final JPanel customSettingsPanel = new JPanel();
frmLegitServerAdder.getContentPane().add(customSettingsPanel, "name_416549691176064");
customSettingsPanel.setLayout(null);
JLabel lblCustomArea = new JLabel("Custom Area");
lblCustomArea.setBounds(235, 21, 46, 14);
customSettingsPanel.add(lblCustomArea);
menuItemDefaultSettings.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
defaultSettingsPanel.setVisible(true);
customSettingsPanel.setVisible(false);
}
});
menuItemCustomSettings.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
defaultSettingsPanel.setVisible(false);
customSettingsPanel.setVisible(true);
}
});
The code runs and the GUI displays just fine, but nothing actually happens when I click on either menu items, as it should. Any ideas?
You should NOT be using a MouseListener. Instead you should be adding an ActionListener to the menu item. Read the section from the Swing tutorial on How to Use Menus for more information.
You should be using a CardLayout when you want to swap components. See How to Use Card Layout from the same tutorial.
You need ActionListener
menuItemDefaultSettings.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
defaultSettingsPanel.setVisible(true);
customSettingsPanel.setVisible(false);
}
});
Hope this helps.

How do I open new windows from menubars?

This is my code so far:
public static void main(String[] args){
JFrame frame = new JFrame("Breakout!");
frame.setLocation(300, 300);
//Making the menubar
JMenuBar mb = new JMenuBar();
frame.setJMenuBar(mb);
JMenu fileMenu = new JMenu("File");
mb.add(fileMenu);
JMenuItem newAction = new JMenuItem("About");
fileMenu.add(newAction);
newAction.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
System.out.println("open new window here");
}
});
BreakoutCourt c = new BreakoutCourt();
frame.add(c);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
I am making a Breakout game. I want to make an About window that displays information about the game (like, how to play it and so on). How would I go about doing that? Thank you for the help! I'm very new to Java Swing, so yeah.
You could do a simple one with the message type of JOptionPane:
JOptionPane.showMessageDialog(frame, "Breakout! v1.0");
(You'll need to make the frame final to do this, since it's being accessed from within the anonymous action listener).
For more control over what is displayed, and whether it should be modal or not, you could look at JDialog. There's an overview of using dialogs in Swing here: http://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html.

Focus on first jmenubar item on "alt" key press

Is there any way to auto-select a jmenu within my jmenubar when the user's pressing the "ALT" key ? (Like windows softwares)
The problem is that the default behavior of my jframe, when the "ALT" key is pressed, is to show up a menu containing the following actions : restore, move, size, reduce, ...
What I want my java application to do, is to select my jmenu first item when "alt" is pressed.
(Like it would do with a mnemonic : "alt + f")
Add the action to the ActionMap and InputMap of your JRootPane. See below:
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
public class MenuExample {
private void setupMenuKey(final JFrame frame) {
Action menuAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
JRootPane rootPane = frame.getRootPane();
JMenuBar jMenuBar = rootPane.getJMenuBar();
JMenu menu = jMenuBar.getMenu(0);
menu.doClick();
}
};
JRootPane rootPane = frame.getRootPane();
ActionMap actionMap = rootPane.getActionMap();
final String MENU_ACTION_KEY = "expand_that_first_menu_please";
actionMap.put(MENU_ACTION_KEY, menuAction);
InputMap inputMap = rootPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ALT, 0, true), MENU_ACTION_KEY);
}
private JFrame build() {
JFrame frame = new JFrame("Hello");
frame.setSize(300, 300);
JMenuBar bar = new JMenuBar();
List<String> letters = Arrays.asList("A", "B", "C");
for (int i = 0; i < 3; i++) {
JMenu menu = new JMenu("Menu " + i);
for (String string : letters) {
menu.add(new JMenuItem(String.format("Menu %s - %s", i, string)));
}
bar.add(menu);
}
frame.setJMenuBar(bar);
JButton b = new JButton("click");
JPanel p = new JPanel();
p.add(b);
frame.add(p);
setupMenuKey(frame);
return frame;
}
public static void main(String[] args) {
MenuExample menuExample = new MenuExample();
JFrame frame = menuExample.build();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Use actionmap and inputmap of JComponent to do this job. Since JFrame is not a descendant of JCompoenent i would suggest you adding JPanel to your jframe and then,
Action action = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
jMenu1.doClick();
}
};
jPanel1.getActionMap().put("myAction", action);
jPanel1.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_ALT, KeyEvent.ALT_DOWN_MASK), "myAction");
I've found the response very useful, but also with a drawback.
Imagine you're using Cross PlatForm L&F (Metal) and you've defined mnemonics to all the menus; you know that those menus are invoked against pressing . So, if we put the solution provided by black panda the results on a pressing are first we watch the desired menu deployed but immediately after switches to the first menu.
I think that the best way would be on ALT pressing only the first menu should be selected but without showing its contents (Those contents shoud appear on a down key pressing). Is there any way to do this in Cross Platform L&F?.
Alternatively there is another way to proceed without any extra code. If we invoke the System L&F (Windows in my case) the ALT key behaves as desired. See stackoverflow question 13474555 for details.
I have found just by using frame.setJMenuBar(jmenubar); where frame is your JFrame and jmenubar is your JMenuBar, it'll automatically do this. You don't even have to add it to your layout manager.

Categories

Resources