Thursday, March 21, 2013

Taming the GWT PopupPanel

I was working on a feature of a GWT application today, and decided to fix a problem that had been bugging me.  While the issue was not a server crashing bug, it was a usability nuisance.

Between the main content of the app, and the navigation bar, there was an Anchor.  When a user moused over the Anchor, a PopupPanel would be shown with some user data in it.  Everytime i moved my mouse up to the navigation bar, I'd mouse over the anchor, and have this annoying PopupPanel in my way.  To compund the issue, there was a RPC call each time the panel was popped up.

To combat this issue, I decided to use the GWT Timer class, along with the MouseOverEvent and MouseOutEvent.  This was incredibly simple after I moved the presenter and view to use the UiField, and UiHandler annotations along with the UiBinder.

The Code:

@UiHandler("popupAnchor")
void showPopup(ClickEvent e)
{
 showPopup();
}

private long mouseOverTime = 0;
private int loadPopupTime = 300;

@UiHandler("popupAnchor")
void onMouseOut(MouseOutEvent e)
{
 mouseOverTime = 0;
}

@UiHandler("popupAnchor")
void onMouseOver(MouseOverEvent e)
{
 mouseOverTime = System.currentTimeMillis();
 Timer t = new Timer() {
       public void run() {
        /*
         * this ensures that the popup
         * will not trigger if the mouse
         * merely went over the anchor. 
                   */
        if(mouseOverTime != 0)
         showPopup();
       }
     };
 t.schedule(loadPopupTime);
}

private void showPopup()
{
 popup.getPopup().setPopupPosition(
   popupAnchor.getAbsoluteLeft(), 
   popupAnchor.getAbsoluteTop());
 popup.getPopup().show();
 
}

How this works:


When the user mouses over the anchor, a MouseOverEvent is fired. The handler onMouseOver is run, which sets mouseOverTime to the current time in milliseconds.  A Timer is also created, and set to run in loadPopupTime milliseconds.  

Now one of two things can happen:

  1. The user can stay hovering on the Anchor.  The timer will run, and see that mouseOverTime is not equal to zero, and the popup will be shown.
  2. The user can move the mose off the Anchor, which triggers the MouseOutEvent.  This sets mouseOverTime equal to zero.  When the timer runs, it sees that mouseOverTime is equal to zero, and the popup will not be shown. 
I also added in a click handler for the users that can't wait 300ms. I'm in this group for sure.

Pretty simple eh!?