Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does one autohide a GWT MenuBar submenu?

We have recently attached a GWT MenuBar to a part of our application for, well, menu purposes.

Basically I want the sub menus to open when you mouse over the top level menu, which is easy enough to do:

menubar.setAutoOpen(true);

I would also like to have the sub menu automatically hide when the user's mouse leaves the sub menu. Ideally with some sort of delay to prevent it vanishing too abruptly, but I'd settle for just the hiding.

This doesn't seem to be built in and the MenuItem object in GWT directly subclasses UIObject which means there isn't a relatively trivial onBrowserEvent() or somewhere to attach mouse listeners. Possibly extending MenuItem and sinking/unsinking events would let me add this behavior, but I am unsure if that is the best approach.

So what would be the best approach to autohiding a GWT submenu?

Thank you.

like image 330
bikesandcode Avatar asked Dec 04 '25 17:12

bikesandcode


2 Answers

After much horrible hacking trying to achieve something similar we wrote our own cascading menu as part of the GWT Portlets framework. It displays menu items and submenus from an HTML template looking something like this:

<a href="#home">Home</a>
<a href="#submenu1()">Sub Menu 1</a>
<a href="#away">Away</a>

<div id="submenu1">
    <a href="#hello_world">Hello World</a>
    <a href="#free_memory">Free Memory</a>
    <a href="#submenu2()">Sub Menu 2</a>
</div>

<div id="submenu2">
    <a href="#command_demo">Command Demo</a>
    <a href="#command1()">Command1</a>
    <a href="#command2(arg1,arg2)">Command2</a>
</div>

The URLs that look like method calls broadcast CommandEvent's. The others trigger a history token change like normal. Have a look at the online demo to see the menu in action.

like image 57
David Tinker Avatar answered Dec 07 '25 08:12

David Tinker


Here's a fairly complete solution, not perfect, explained after code:

public class MyMenuBar extends Composite {

    private class OpenTab implements ScheduledCommand {
        private String wid;

        public OpenTab(String windowId) {
            wid = windowId;
        }

        @Override
        public void execute() {
            WinUtl.newAppTab(wid);
        }
    }

    interface MyMenuBarUiBinder extends UiBinder<Widget, MyMenuBar> {}

    private static MyMenuBarUiBinder uiBinder =
                                GWT.create(MyMenuBarUiBinder.class);

    @UiField MenuBar mainMenu;

    @UiField MenuBar subsMenu;
    @UiField MenuItem subsChoice1;
    @UiField MenuItem subsChoice2;
    @UiField MenuItem subsChoice3;

    @UiField MenuBar svcPrvdrMenu;
    @UiField MenuItem svcPrvdrChoice1;
    @UiField MenuItem svcPrvdrChoice2;

    @UiField MenuBar netMgtMenu;
    @UiField MenuItem netMgtChoice1;

    @UiField MenuBar reportsMenu;
    @UiField MenuItem reportsChoice1;

    @UiField MenuBar auditsMenu;
    @UiField MenuItem auditsChoice1;

    @UiField MenuBar securityMenu;
    @UiField MenuItem securityChoice1;

    @UiField MenuBar helpMenu;
    @UiField MenuItem helpChoice1;

    private boolean subMenuPopped = false;
    private boolean subMenuEntered = false;

    private static Type<MouseOverHandler> OVR_EVT = MouseOverEvent.getType();
    private static Type<MouseOutHandler> OUT_EVT = MouseOutEvent.getType();

    private MouseOverHandler mainOverHandler = new MouseOverHandler() {
        @Override
        public void onMouseOver(MouseOverEvent event) {
            subMenuPopped = true;
        }
    };

    private MouseOutHandler mainOutHandler = new MouseOutHandler() {
        @Override
        public void onMouseOut(MouseOutEvent event) {
            Element e = event.getRelativeElement()
            boolean movedUp = (event.getRelativeY(e) < 0);
            if ((movedUp && subMenuPopped) || subMenuEntered) {
                subMenuPopped = false;
                subMenuEntered = false;
                mainMenu.closeAllChildren(true);
            }
        }
    };

    private MouseOverHandler subOverHandler = new MouseOverHandler() {
        @Override
        public void onMouseOver(MouseOverEvent event) {
            subMenuEntered = true;
        }
    };

    private MouseOutHandler subOutHandler = new MouseOutHandler() {
        @Override
        public void onMouseOut(MouseOutEvent event) {
            subMenuPopped = false;
            subMenuEntered = false;
            mainMenu.closeAllChildren(true);
        }
    };

    public MyMenuBar() {
        initWidget(uiBinder.createAndBindUi(this));

        mainMenu.addStyleName("npac-MenuBar");
        mainMenu.setAutoOpen(true);
        mainMenu.setAnimationEnabled(true);
        mainMenu.setFocusOnHoverEnabled(true);

        subsChoice1.setScheduledCommand(new OpenTab(Names.Wid.NPA));

        mainMenu.addDomHandler(mainOverHandler, OVR_EVT);
        mainMenu.addDomHandler(mainOutHandler, OUT_EVT);

        addHandlers(subsMenu);
        addHandlers(svcPrvdrMenu);
        addHandlers(netMgtMenu);
        addHandlers(reportsMenu);
        addHandlers(auditsMenu);
        addHandlers(securityMenu);
        addHandlers(helpMenu);
    }

    private void addHandlers(MenuBar m) {
        m.addDomHandler(subOverHandler, OVR_EVT);
        m.addDomHandler(subOutHandler, OUT_EVT);
    }
}

This handles the case where mouseOver opens subMenu, user then mouses UP, off mainMenu (subMenu closes). It does not handle the mouse moving diagonally down, past either side of subMenu (submenu stays open) Certainly can be improved, but I just got it to work and wanted to share ;-)

like image 41
user1776933 Avatar answered Dec 07 '25 08:12

user1776933



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!