Activate JMenuBar on hover
Asked Answered
B

2

7

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?

Bigg answered 25/8, 2012 at 20:30 Comment(2)
Any good reason why you want to do this ? All menubars in all applications behave like thatCompetitive
Well my menu has a custom look and this behavior fits it more naturally. Also, I believe that this behavior is a lot more interactive for the user.Bigg
P
9

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);
    }
}
Piggott answered 25/8, 2012 at 20:47 Comment(3)
I understand the mouseEntered listener but how do you activate the entire JMenuBar from the mousentered?Bigg
Thanks. That put my on the right track, but I guess I was hoping there was a simpler way to do this like jMenuBar.activate(), rather than individually forcing the click. For JMenuItem you can't call doClick() instead I believe the best way to generate this behavior is setArmed(true)Bigg
Also consider MouseAdapter instead of `MouseListener.Danish
Q
2

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.

Quarrier answered 2/1, 2015 at 1:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.