Activate JMenuBar on hover - java

The JMenuBar does not start showing JMenuItems as selected or displaying the JMenu popups until it is first clicked upon. After you click somewhere in the JMenuBar, all these items respond to mouse hovers.
I would like to bypass the initial click required and have it activated automatically upon a mouse hover. Is there a way to do this?

The way is to add a MouseListener on the JMenu and listen on events mouseEntered. In the event handlers, you just need to click on it using doClick. For example,
jMenuFile.addMouseListener(new MouseListener(){
public void mouseEntered(MouseEvent e) {
jMenuFile.doClick();
}
...
});
Once programmatically clicked on the mouse is entered, it opens the popup menu automatically. To activate an entire JMenuBar, you have to add a listener on each JMenu. For this purpose, it is better to create a listener object separately.
I have two menu items on the bar, so I did:
MouseListener ml = new MouseListener(){
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {
((JMenu)e.getSource()).doClick();
}
};
jMenuFile.addMouseListener(ml);
jMenuHelp.addMouseListener(ml);
If you have so many menu items on the bar, you can just iterate it:
for (Component c: jMenuBar1.getComponents()) {
if (c instanceof JMenu){
c.addMouseListener(ml);
}
}

Roman C's initial and accepted answer will not AUTO Close the menu with child MenuItems as part of the JMenuBar.
Running a ((JMenu)e.getSource()).doClick(); on the mouseEntered simulates the click into one of the JMenu parents but can't be simply added to the mouseExited method as the MouseListener needs to be attached to the child MenuItems as well as the JMenu parents. (Which it doesn't do in the normal assignment to the MenuBar - only attaching to the parent JMenu objects).
Additionally, a problem arises due to trying to get the MouseExit listener to fire a "close" method ONLY when the mouse has left the entire Menu structure (ie the Child menu dropdowns).
Below is a fully working answer taken from my live app:
The way I solved the menu close on mouse out was to run a boolean variable "isMouseOut" in the top of the constructor to keep track, and then allocate the MouseListener in a more OO friendly way to keep track of the multiple MouseIn-MouseOut events as a user interacts with the menu. Which calls a separate menuClear method acting upon the state of the boolean "isMouseOut". The class implements MouseListener. This is how its done.
Create an ArrayList adding all the menu items to this array first. Like so:
Font menuFont = new Font("Arial", Font.PLAIN, 12);
JMenuBar menuBar = new JMenuBar();
getContentPane().add(menuBar, BorderLayout.NORTH);
// Array of MenuItems
ArrayList<JMenuItem> aMenuItms = new ArrayList<JMenuItem>();
JMenuItem mntmRefresh = new JMenuItem("Refresh");
JMenuItem mntmNew = new JMenuItem("New");
JMenuItem mntmNormal = new JMenuItem("Normal");
JMenuItem mntmMax = new JMenuItem("Max");
JMenuItem mntmStatus = new JMenuItem("Status");
JMenuItem mntmFeedback = new JMenuItem("Send Feedback");
JMenuItem mntmEtsyTWebsite = new JMenuItem("EtsyT website");
JMenuItem mntmAbout = new JMenuItem("About");
aMenuItms.add(mntmRefresh);
aMenuItms.add(mntmNew);
aMenuItms.add(mntmNormal);
aMenuItms.add(mntmMax);
aMenuItms.add(mntmStatus);
aMenuItms.add(mntmFeedback);
aMenuItms.add(mntmEtsyTWebsite);
aMenuItms.add(mntmAbout);
then iterate over the arrayList at this stage adding a MouseListener using the for() loop:
for (Component c : aMenuItms) {
if (c instanceof JMenuItem) {
c.addMouseListener(ml);
}
}
Now set JMenu parents for the MenuBar:
// Now set JMenu parents on MenuBar
final JMenu mnFile = new JMenu("File");
menuBar.add(mnFile).setFont(menuFont);
final JMenu mnView = new JMenu("View");
menuBar.add(mnView).setFont(menuFont);
final JMenu mnHelp = new JMenu("Help");
menuBar.add(mnHelp).setFont(menuFont);
Then add the dropdown menuItems children to the JMenu parents:
// Now set menuItems as children of JMenu parents
mnFile.add(mntmRefresh).setFont(menuFont);
mnFile.add(mntmNew).setFont(menuFont);
mnView.add(mntmNormal).setFont(menuFont);
mnView.add(mntmMax).setFont(menuFont);
mnHelp.add(mntmStatus).setFont(menuFont);
mnHelp.add(mntmFeedback).setFont(menuFont);
mnHelp.add(mntmEtsyTWebsite).setFont(menuFont);
mnHelp.add(mntmAbout).setFont(menuFont);
Add the mouseListeners to the JMenu parents as a separate step:
for (Component c : menuBar.getComponents()) {
if (c instanceof JMenu) {
c.addMouseListener(ml);
}
}
Now that the child menuItem elements all have their own listeners that are separate to the parent JMenu elements and the MenuBar itself - It is important to identify the object type within the MouseListener() instantiation so that you get the menu auto opening on mouseover (in this example the 3x JMenu parents) BUT ALSO avoids child exception errors and allows clean identification of mouseOUT of the menu structure without trying to monitor where the mouse position is. The MouseListener is as follows:
MouseListener ml = new MouseListener() {
public void mouseClicked(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
isMouseOut = true;
timerMenuClear();
}
public void mouseEntered(MouseEvent e) {
isMouseOut = false;
Object eSource = e.getSource();
if(eSource == mnHelp || eSource == mnView || eSource == mnFile){
((JMenu) eSource).doClick();
}
}
};
The above only simulates the mouse click into the JMenu 'parents' (3x in this example) as they are the triggers for the child menu dropdowns. The timerMenuClear() method calls on the MenuSelectionManager to empty whatever selectedpath point was live at the time of real mouseOUT:
public void timerMenuClear(){
ActionListener task = new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(isMouseOut == true){
System.out.println("Timer");
MenuSelectionManager.defaultManager().clearSelectedPath();
}
}
};
//Delay timer half a second to ensure real mouseOUT
Timer timer = new Timer(1000, task);
timer.setInitialDelay(500);
timer.setRepeats(false);
timer.start();
}
It took me a little testing, monitoring what values I could access within the JVM during its development - but it Works a treat! even with nested menus :) I hope many find this full example very useful.

Related

Tell Swing to recalculate JMenu width when I change text contents of JMenuItem

I've been a little lazy and instead of making another window, I created single menu for my update manager. I plan on making the window, but there are more important tasks to attend, so this must just do for now:
Clicking on that last menu item dynamically changes the text when there's an update. If the user is viewing the menu item at that moment, the size of the JMenu doesn't change:
If I move mouse away and navigate back to the menu, the size is correct:
I need to tell Swing to update size of the JMenuItem when I change the text. I have a class called UpdateMenuItem which extends the JMenuItem with functions like this:
public void setDownloadAvailable(final VersionId version) {
// Checking if the code runs in correct thread
// this should probably also raise a warning if not in the swing thread
if(!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(()->setDownloadAvailable(version));
return;
}
// this is how the menu text changes
setText("Click to download: "+version);
setBackground(Color.ORANGE);
}
I need to add appropriate code after the setText call to have the size of the item correctly updated. What method should I call?
JMenu#getPopupMenu() and JPopupMenu#pack() works fine for me:
import java.awt.*;
import javax.swing.*;
public class JMenuItemPackTest {
public JMenuBar makeMenuBar() {
JMenuItem label = new JMenuItem("Up to date: 3.5-beta");
label.setBackground(Color.GREEN);
// UpdateMenuItem label = new UpdateMenuItem("Up to date: 3.5-beta") {
// #Override public void setDownloadAvailable(final VersionId version) {
// super.setDownloadAvailable(version);
// Container c = SwingUtilities.getUnwrappedParent(this);
// if (c instanceof JPopupMenu) {
// ((JPopupMenu) c).pack();
// }
// }
// };
JMenu update = new JMenu("Updates");
update.add(new JCheckBoxMenuItem("Auto-check for updates"));
update.add(new JCheckBoxMenuItem("Auto-download"));
update.add(label);
JMenu menu = new JMenu("Help");
menu.add(update);
(new Timer(5000, e -> {
//...
label.setText("Click to download: 3.5-beta");
label.setBackground(Color.ORANGE);
update.getPopupMenu().pack();
//or:
//Container c = SwingUtilities.getUnwrappedParent(label);
//if (c instanceof JPopupMenu) {
// ((JPopupMenu) c).pack();
//}
})).start();
JMenuBar menubar = new JMenuBar();
menubar.add(menu);
return menubar;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setJMenuBar(new JMenuItemPackTest().makeMenuBar());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}

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.

Java MenuBar cuts into my applet

Simple question - I am editting a java based MMORPG game and wanted to add a menubar to the gameframe. So I did, but the menubar seems to cut into the game and block some of the content so that it is hidden behind the menubar. There is an example picture right here:
And here is a picture of my code:
public GameFrame(GameWindow gameWindow, int width, int height, String title, boolean resizable, boolean flag1) {
frameOffset = 28;
frameWidth = width;
frameHeight = height - 1;
aGameWindow = gameWindow;
addListeners(this, gameWindow);
if (flag1)
frameOffset = 48;
else
frameOffset = 28;
setTitle(title);
setResizable(resizable);
menubar = new MenuBar();
menu = new java.awt.Menu("Menu");
submenu = new java.awt.Menu("Sub Menu");
m1 = new MenuItem("Menu Item 1");
m2 = new MenuItem("Menu Item 2");
m3 = new MenuItem("Menu Item 3");
m4 = new MenuItem("Menu Item 4");
m5 = new MenuItem("Menu Item 5");
menu.add(m1);
menu.add(m2);
menu.add(m3);
submenu.add(m4);
submenu.add(m5);
menu.add(submenu);
menubar.add(menu);
setMenuBar(menubar);
this.addWindowListener(new WindowListener() {
#Override
public void windowActivated(WindowEvent arg0) {}
#Override
public void windowClosed(WindowEvent arg0) {
System.exit(-1);
}
#Override
public void windowClosing(WindowEvent arg0) {
System.exit(-1);
}
#Override
public void windowDeactivated(WindowEvent arg0) {}
#Override
public void windowDeiconified(WindowEvent arg0) {}
#Override
public void windowIconified(WindowEvent arg0) {}
#Override
public void windowOpened(WindowEvent arg0) {}
});
show();
toFront();
resize(frameWidth, frameHeight);
aGraphics49 = getGraphics();
}
Any ideas on how to fix this problem, or create some extra space up top so that it wont interfere with the game?
Thanks
Instead of a JMenuBar, just do a MenuBar. It will show a menu bar that is above the GUI and will allow your full screen of the GUI to be shown...it acts as a JMenuBar in every way..
MenuBar mB = new MenuBar();
Menu menu = new Menu("Menu");
MenuItem mI1 = new MenuItem("Menu Item 1");
// setting the bar
this.setMenuBar(mB);
//adding the menu onto the bar
mB.addMenu(menu);
// adding the menu item into the menu
menu.addMenuItem(mI1);
//adding action listener
mI1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// do whatever
}
});
You can see much more here:
MenuBar, Menu, and MenuItem help
You could try creating a pause menu using a Jpanel. Every good game needs a pause menu :).
Make sure if you go this route give the JPanel a keyListener so you may setVisible when you press a determined key.
Looks like the game panel does not respect the insets.
instead of adding the menubar to the GameFrame itself, create a top level panel - put the GameFrame inside it at BorderLayout.CENTER and add the menubar to the top level panel.
then add that panel to your applet instead of adding the GameFrame directly.

Why is a WindowBuilder event not getting called?

I created a Java app in WindowBuilder for Eclipse. I built a menu and on one of the menu items I added the mouseclicked event.
JMenuItem mitemAbout = new JMenuItem("About");
mitemAbout.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent arg0) {
JOptionPane.showInternalMessageDialog( form, "Message", "title", JOptionPane.PLAIN_MESSAGE);
}
});
mitemHelp.add(mitemAbout);
I put a breakpoint on the JOptionPane line and when I click on the menu item in debug mode it doesn't even get to it.
Am I completely missing a step here?
Although JMenuItem components offer the addMouseListener method (inherited from java.awt.Component) MouseEvents are only processed for the MenuElements own functional use, i.e. any external MouseEvents will have no effect.
For JMenuItem components, use an ActionListener rather than a MouseListener to listen for user events:
mitemAbout.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
...
}
});
or use:
mitemAbout.setAction(myAction);

Create jpanel in actionperformed listener?

I have 1 JFrame and 10 JPanel components as seperate classes. There is also JMenuBar on jframe. When a menu item clicked, i remove all content of contentPane of jframe (removeAll) and add one of my jpanels.
Here is my code;
// this function changes panel
public static void SwitchPanel(Component comp)
{
Container panel = getContentPane();
panel.removeAll();
panel.add(comp);
panel.revalidate();
panel.repaint();
}
// this function defines menu items and their listeners
public JMenuItem AddMenuItem(JMenu menu, String name, final JPanel toPanel) {
JMenuItem menuItem = new JMenuItem(name);
menu.add(menuItem);
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SwitchPanel(toPanel);
}
});
return menuItem;
}
and i add menu items like that;
AddMenuItem(menu1, "some menu item", new MyPersonalJPanel());
Everything works. BUT i want to create new jpanel when a related menu item clicked. I mean create jpanel if only it is necessary. Current code creates all jpanels first. When i clicked a menu item, shows me jpanel created before.
I think it can be done with Class.forName method, but i couldn't figure it out. Any help?
You should do the new MyPersonalJPanel() in the public void actionPerformed(ActionEvent e) method. That way the panel would be created each time the user click on the menu.
Your code would then be:
// this function defines menu items and their listeners
public JMenuItem AddMenuItem(JMenu menu, String name) {
JMenuItem menuItem = new JMenuItem(name);
menu.add(menuItem);
menuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SwitchPanel(new MyPersonalJPanel());
}
});
return menuItem;
}

Categories

Resources