Search code examples
javaswingjmenu

The AccessibleSelection in a AccessibleJMenu is returning after the mouse changes JMenu


The code, at the bottom of the question, provides a solution for a fairly specific circumstance where I need to click on a JMenu and it should act like a JButton whilst keeping almost all of the formatting of a JMenu. The difference in formatting between the ActionMenu and the JMenu should be after it is clicked, it should return to its normal state (see image).

enter image description here

With the current code it does do this. However; if you are to move your mouse over a different menu or if you move you mouse down/up off the menu and then hover back over it, the menu will go into its selected state (see image). This should not occur.

enter image description here

My question is why is this occurring after the AccessibleSelection has been cleared and the PopupMenu has been set to not visible?

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;

public class Main extends JFrame {
    private static final long serialVersionUID = 3206847208968227199L;

    private Main() {
        setSize(600, 600);
        setJMenuBar(new MenuBar());
        setVisible(true);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {
        new Main();
    }

    private class MenuBar extends JMenuBar {
        private static final long serialVersionUID = -2055260049565317972L;

        MenuBar() {
            add(new ActionMenu("Menu"));
            add(new JMenu("Another Menu"));
        }
    }

    private class ActionMenu extends JMenu {
        private static final long serialVersionUID = -6885806048559452542L;

        public ActionMenu(String name) {
            setText(name);
            menuItem();
        }

        JButton butInvis;

        private void menuItem() {
            butInvis = new JButton();
            butInvis.addActionListener(new MenuActionListener());
            addMouseListener(new MenuMouseListener());
        }

        private class MenuMouseListener extends MouseAdapter {
            public void mouseClicked(MouseEvent e) {
                clcikComponent(butInvis);
            }

            private void clcikComponent(JButton comp) {
                comp.doClick();
            }
        }

        private class MenuActionListener implements ActionListener {
            public void actionPerformed(ActionEvent e)  {
                AccessibleJMenu aJ = (AccessibleJMenu) getAccessibleContext();
                aJ.clearAccessibleSelection();
                getPopupMenu().setVisible(false);
            }
        }
    }
}

Edit
I forgot to mention that orginally I was doing this with setSelected(false) on the JMenu (see code below); however, that causes the exact same issue when you hover over another JMenu.

private class MenuActionListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
        setSelected(false);
    }
}

Solution

  • To solve this problem you need to use the AccessibleContext of the JMenuBar and not the JMenu. This can be done with the line of code

    getParent().getAccessibleContext().getAccessibleSelection().clearAccessibleSelection();
    

    In this example the code would go in the MenuActionListener and it would look like the following code.

    private class MenuActionListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            getParent().getAccessibleContext().getAccessibleSelection().clearAccessibleSelection();
        }
    }
    

    As mentioned by Andrew Thompson, this should not be done because of the Principle of Least Astonishment, unless you are going to change the formatting of the component (Action Menu in this example) so that it does not look like another component.