GWT Right-click Context Menu

The Google Web Toolkit has been out for a while now, and yet there is still basic functionality that is missing from the toolkit.  Don’t get me started on the lack of draggable/resizable columns for the FlexTable, because that’s a rant and a half.  Given that GWT’s event handling model isn’t bad, you’d think they’d have included from the get-go the ability to handle right-clicks and bringing up a context menu or popup menu.  Well, even with 1.6 on the doorstep it seems they forgot again or just don’t care.   Now some people will spout out “web apps don’t need or shouldn’t have right-clicks handled or context menus overridden”……and for those I say STFU!  Web apps are used for more than just banking, news, forums and dare I say blogs.  The browser is becoming the new medium for running applications and just because an application is running in a browser doesn’t mean we should limit functionality.  That’s about as narrow minded as saying that we’ve only had one mouse button for this long, why add a second one?  Duh!

Anyway, enough with the blabbing.  I’ve put together a simple example to add a right click context menu and override the default browser context menu using GWT. 

In the box below you can try it out, right-click in there and you can demo it.

In case the iframe doesn’t show up in your browser you can see and try the example here.
Now here’s how it’s done.

I used a DeckPanel to switch between several panels. The popup context menu is used to choose.  To allow the DeckPanel to catch the right-click event (and you can also listen for double-clicks and several other things) I extended the DeckPanel.   For simplicity I added “Adv” (Advanced) in front of the several classes I’ve extended so this one will be AdvDeckPanel.  The first thing to do in the constructor is add sinkEvents(). Then we’ll override onBrowserEvent() in the class.  Here’s the code:

public AdvDeckPanel() {
  super();
  sinkEvents(Event.ONMOUSEUP | Event.ONDBLCLICK | Event.ONCONTEXTMENU);
}
 
public void onBrowserEvent(Event event) {
  GWT.log("onBrowserEvent", null);
  event.cancelBubble(true);//This will stop the event from being propagated
  event.preventDefault();
  switch (DOM.eventGetType(event)) {
    case Event.ONMOUSEUP:
      if (DOM.eventGetButton(event) == Event.BUTTON_LEFT) {
        GWT.log("Event.BUTTON_LEFT", null);
        listener.onClick(this, event);
      }
 
      if (DOM.eventGetButton(event) == Event.BUTTON_RIGHT) {
        GWT.log("Event.BUTTON_RIGHT", null);
        listener.onRightClick(this, event);
      }
      break;
    case Event.ONDBLCLICK:
      break;
 
    case Event.ONCONTEXTMENU:
      GWT.log("Event.ONCONTEXTMENU", null);
      break;
 
    default:
      break; // Do nothing
  }//end switch
}

Notice the two lines in onBrowserEvent():
event.cancelBubble(true);
event.preventDefault();

These are the two lines that tell the browser not to show it’s default context popup menu. Also note that overriding the default context menu doesn’t work in all browsers, I’m not sure if this is a bug in GWT. If you’re using firefox then you’ll have no problems, with IE you may need to add the following to your html:

<body oncontextmenu="return false;">

Other than that there’s just a switch statement that checks the event type, in this case we’re interested with ONMOUSEUP, and we’ll call the listener’s onClick() or onRightClick() based on the Event’s fields.

AdvDeckPanel also has a reference to AdvClickListener which looks like:

public interface AdvClickListener extends ClickListener {
  void onClick(Widget sender, Event event);
  void onRightClick(Widget sender, Event event);
}

This reference is basically the same as ClickListener, but has a separate method to handle the right-click.  I also pass the Event object so I can get the x and y from the click so the context menu shows up at that location instead of the top-left of the screen.

AdvDeckPanel implements AdvClickNotifier which does the same thing as GWT’s SourcesClickEvents interface, but handles the AdvClickListener instead.

public interface AdvClickNotifier {
  public void addClickListener(AdvClickListener listener);
  public void removeClickListener(AdvClickListener listener);
}

So, once you have your widget (in this case the AdvDeckPanel) and the listeners set up to handle the right-click, then we add in the code to build the popup menu and commands that go with it.  I put all of this in the EntryPoint.  I have three widgets (panels) and a Command for each like this:

private AdvDeckPanel deckPanel = new AdvDeckPanel();
final private PopupPanel popupPanel = new PopupPanel(true);
private VerticalPanel defaultPanel = new VerticalPanel();
private SimplePanel imagePanel = new SimplePanel();
private SimplePanel sponserPanel = new SimplePanel();
 
Command showAlertCommand = new Command() {
  public void execute() {
    deckPanel.showWidget(0);
    popupPanel.hide();
    Window.alert("Hope this example helps.");
  }
};
 
Command showImageCommand = new Command() {
  public void execute() {
    deckPanel.showWidget(1);
    popupPanel.hide();
  }
};
 
Command showSponserCommand = new Command() {
  public void execute() {
    deckPanel.showWidget(2);
    popupPanel.hide();
  }
};

A Command is called when the MenuItem is clicked.

Now the code to build the menu, link the commands, and handle the right-click:

private void createPopupMenu() {
  MenuBar popupMenuBar = new MenuBar(true);
  MenuItem alertItem = new MenuItem("Show alert", true, showAlertCommand);
  MenuItem imageItem = new MenuItem("Show Oliver ", true, showImageCommand);
  MenuItem sponserItem = new MenuItem("Show sponser ", true, showSponserCommand);
 
  popupPanel.setStyleName("popup");
  alertItem.addStyleName("popup-item");
  imageItem.addStyleName("popup-item");
  sponserItem.addStyleName("popup-item");
 
  popupMenuBar.addItem(alertItem);
  popupMenuBar.addItem(imageItem);
  popupMenuBar.addItem(sponserItem);
 
  popupMenuBar.setVisible(true);
  popupPanel.add(popupMenuBar);
}
 
public void onRightClick(Widget sender, Event event) {
  int x = DOM.eventGetClientX(event);
  int y = DOM.eventGetClientY(event);
  popupPanel.setPopupPosition(x, y);
  popupPanel.show();
}

Lastly, to make the menu actually look like a popup menu I modified the CSS like so:

.popup {
  background-color: gray;
  border-color: gray gray gray gray;
  border-width: 1px 3px 3px 1px;
  border-style: solid solid solid solid;
}
 
.popup-item {
  font-weight: normal;
  font-size: 80%;
}

What this does is makes it so the border isn’t the thick default GWT blue, and uses a thin border with the right and bottom borders a bit thicker.  This gives the popup that shadowed look.

A couple of books worth checking out are: GWT in Actionand GWT in Practice
Here’s the source and more links.


53 Comments

  1. Ben Howden says:

    never mind I fexed a couple thangs and it werxd.
    here’s a new AudioWidget.java

    Code:

    import com.google.gwt.core.client.GWT;
    import com.google.gwt.user.client.Command;
    import com.google.gwt.user.client.DOM;
    import com.google.gwt.user.client.Event;
    import com.google.gwt.user.client.ui.HTML;
    import com.google.gwt.user.client.ui.MenuBar;
    import com.google.gwt.user.client.ui.MenuItem;
    import com.google.gwt.user.client.ui.PopupPanel;
    
    /**
     *
     * @author fkasoft
     */
    public class AudioWidget extends HTML {
        
        public static String filename = null;
        
        
        public AudioWidget(String sfilename, String iconUrl) {
            AudioWidget.filename=sfilename;
            setPixelSize(138, 130);
            setHTML(""
                +""+filename+"");
        }
        
           @Override
            public void onBrowserEvent(Event event) {
                    if( DOM.eventGetType(event) == Event.ONCLICK
                    && DOM.eventGetCtrlKey(event) ){
    
                    }
                    if (DOM.eventGetButton(event) == Event.BUTTON_RIGHT) {
                        GWT.log("Event.BUTTON_RIGHT", null);
                        onRightClickBroker(this, event);
                    }
                    sinkEvents(Event.ONMOUSEUP | Event.ONDBLCLICK | Event.ONCONTEXTMENU);
                    super.onBrowserEvent(event);
                }
            
     
    
                private void onRightClickBroker(HTML aThis, Event event) {
                    
                    final PopupPanel popupPanel = new PopupPanel();
                    popupPanel.setPixelSize(85, 100);
                    
                    Command showPlayerCommand = new Command() {
                      public void execute() {
                       
                        popupPanel.hide();
                        MainEntryPoint.aPanel.remove(popupPanel);
                        
                      }
                    };
    
                
                    
                      MainEntryPoint.rightClickFired=true;
                      // Window.alert("audio icon right clicked");
                      MenuBar popupMenuBar = new MenuBar(true);
                      MenuItem playerItem = new MenuItem("Play", true, showPlayerCommand);
                    
                      
                        popupMenuBar.addItem(playerItem);
                        popupMenuBar.setVisible(true);
                        popupPanel.add(popupMenuBar);
        
                         DOM.setStyleAttribute(popupPanel.getElement(), "background-color", "gray");
                          DOM.setStyleAttribute(popupPanel.getElement(), "border-color", "gray gray gray gray");
                          DOM.setStyleAttribute(popupPanel.getElement(), "border-width", "1px 3px 3px 1px");
                          DOM.setStyleAttribute(popupPanel.getElement(), "border-style", "solid solid solid solid");
                          DOM.setStyleAttribute(playerItem.getElement(), "font-weight", "normal");
                          DOM.setStyleAttribute(playerItem.getElement(), "font-size", "80%");
                        
                        popupPanel.setVisible(true);
                        MainEntryPoint.aPanel.add(popupPanel, event.getClientX(), event.getClientY());
                      
                   
                    
                }
    }
    
    
    
  2. V says:

    First of all thank you for such a nice tutorial.
    I tried using your code to display a popup in my GWT application and I am facing two challenges:
    1) The default context menu is still getting displayed.
    2) Size of the context menu is so small.

  3. rohith says:

    Here explains how to create a context menu in gwt

    http://theredcubes.blogspot.in/2013/04/context-menu.html

Leave a Reply