com.vaadin.client.ui.VContextMenu Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaadin-client Show documentation
Show all versions of vaadin-client Show documentation
Vaadin is a web application framework for Rich Internet Applications (RIA).
Vaadin enables easy development and maintenance of fast and
secure rich web
applications with a stunning look and feel and a wide browser support.
It features a server-side architecture with the majority of the logic
running
on the server. Ajax technology is used at the browser-side to ensure a
rich
and interactive user experience.
/*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.client.ui;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.dom.client.TableSectionElement;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.HasBlurHandlers;
import com.google.gwt.event.dom.client.HasFocusHandlers;
import com.google.gwt.event.dom.client.HasKeyDownHandlers;
import com.google.gwt.event.dom.client.HasKeyPressHandlers;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.impl.FocusImpl;
import com.vaadin.client.Focusable;
import com.vaadin.client.WidgetUtil;
public class VContextMenu extends VOverlay implements SubPartAware {
private ActionOwner actionOwner;
private final CMenuBar menu = new CMenuBar();
private int left;
private int top;
private Element focusedElement;
private VLazyExecutor delayedImageLoadExecutioner = new VLazyExecutor(100,
new ScheduledCommand() {
@Override
public void execute() {
imagesLoaded();
}
});
/**
* This method should be used only by Client object as only one per client
* should exists. Request an instance via client.getContextMenu();
*
* @param cli
* to be set as an owner of menu
*/
public VContextMenu() {
super(true, false);
setWidget(menu);
setStyleName("v-contextmenu");
getElement().setId(DOM.createUniqueId());
addCloseHandler(new CloseHandler() {
@Override
public void onClose(CloseEvent event) {
Element currentFocus = WidgetUtil.getFocusedElement();
if (focusedElement != null && (currentFocus == null
|| menu.getElement().isOrHasChild(currentFocus)
|| RootPanel.getBodyElement().equals(currentFocus))) {
focusedElement.focus();
focusedElement = null;
}
}
});
}
protected void imagesLoaded() {
if (isVisible()) {
show();
}
}
/**
* Sets the element from which to build menu
*
* @param ao
*/
public void setActionOwner(ActionOwner ao) {
actionOwner = ao;
}
/**
* Shows context menu at given location IF it contain at least one item.
*
* @param left
* @param top
*/
public void showAt(int left, int top) {
final Action[] actions = actionOwner.getActions();
if (actions == null || actions.length == 0) {
// Only show if there really are actions
return;
}
this.left = left;
this.top = top;
menu.clearItems();
for (int i = 0; i < actions.length; i++) {
final Action a = actions[i];
menu.addItem(new MenuItem(a.getHTML(), true, a));
}
// Attach onload listeners to all images
WidgetUtil.sinkOnloadForImages(menu.getElement());
// Store the currently focused element, which will be re-focused when
// context menu is closed
focusedElement = WidgetUtil.getFocusedElement();
// reset height (if it has been previously set explicitly)
setHeight("");
setPopupPositionAndShow(new PositionCallback() {
@Override
public void setPosition(int offsetWidth, int offsetHeight) {
// mac FF gets bad width due GWT popups overflow hacks,
// re-determine width
offsetWidth = menu.getOffsetWidth();
int left = VContextMenu.this.left;
int top = VContextMenu.this.top;
if (offsetWidth + left > Window.getClientWidth()) {
left = left - offsetWidth;
if (left < 0) {
left = 0;
}
}
if (offsetHeight + top > Window.getClientHeight()) {
top = Math.max(0, Window.getClientHeight() - offsetHeight);
}
if (top == 0) {
setHeight(Window.getClientHeight() + "px");
}
setPopupPosition(left, top);
/*
* Move keyboard focus to menu, deferring the focus setting so
* the focus is certainly moved to the menu in all browser after
* the positioning has been done.
*/
Scheduler.get().scheduleDeferred(new Command() {
@Override
public void execute() {
// Focus the menu.
menu.setFocus(true);
// Unselect previously selected items
menu.selectItem(null);
}
});
}
});
}
public void showAt(ActionOwner ao, int left, int top) {
setActionOwner(ao);
showAt(left, top);
}
/**
* Extend standard Gwt MenuBar to set proper settings and to override
* onPopupClosed method so that PopupPanel gets closed.
*/
class CMenuBar extends MenuBar
implements HasFocusHandlers, HasBlurHandlers, HasKeyDownHandlers,
HasKeyPressHandlers, Focusable, LoadHandler, KeyUpHandler {
public CMenuBar() {
super(true);
addDomHandler(this, LoadEvent.getType());
addDomHandler(this, KeyUpEvent.getType());
}
@Override
public void onPopupClosed(PopupPanel sender, boolean autoClosed) {
super.onPopupClosed(sender, autoClosed);
// make focusable, as we don't need access key magic we don't need
// to
// use FocusImpl.createFocusable
getElement().setTabIndex(0);
hide();
}
/*
* public void onBrowserEvent(Event event) { // Remove current selection
* when mouse leaves if (DOM.eventGetType(event) == Event.ONMOUSEOUT) {
* Element to = DOM.eventGetToElement(event); if
* (!DOM.isOrHasChild(getElement(), to)) { DOM.setElementProperty(
* super.getSelectedItem().getElement(), "className",
* super.getSelectedItem().getStylePrimaryName()); } }
*
* super.onBrowserEvent(event); }
*/
private MenuItem getItem(int index) {
return super.getItems().get(index);
}
@Override
public HandlerRegistration addFocusHandler(FocusHandler handler) {
return addDomHandler(handler, FocusEvent.getType());
}
@Override
public HandlerRegistration addBlurHandler(BlurHandler handler) {
return addDomHandler(handler, BlurEvent.getType());
}
@Override
public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
return addDomHandler(handler, KeyDownEvent.getType());
}
@Override
public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
return addDomHandler(handler, KeyPressEvent.getType());
}
public void setFocus(boolean focus) {
if (focus) {
FocusImpl.getFocusImplForPanel().focus(getElement());
} else {
FocusImpl.getFocusImplForPanel().blur(getElement());
}
}
@Override
public void focus() {
setFocus(true);
}
@Override
public void onLoad(LoadEvent event) {
// Handle icon onload events to ensure shadow is resized correctly
delayedImageLoadExecutioner.trigger();
}
@Override
public void onKeyUp(KeyUpEvent event) {
// Allow to close context menu with ESC
if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) {
hide();
}
}
}
@Override
public com.google.gwt.user.client.Element getSubPartElement(
String subPart) {
int index = Integer.parseInt(subPart.substring(6));
// ApplicationConnection.getConsole().log(
// "Searching element for selection index " + index);
MenuItem item = menu.getItem(index);
// ApplicationConnection.getConsole().log("Item: " + item);
// Item refers to the td, which is the parent of the clickable element
return item.getElement().getFirstChildElement().cast();
}
@Override
public String getSubPartName(
com.google.gwt.user.client.Element subElement) {
if (getElement().isOrHasChild(subElement)) {
com.google.gwt.dom.client.Element e = subElement;
{
while (e != null
&& !e.getTagName().toLowerCase().equals("tr")) {
e = e.getParentElement();
// ApplicationConnection.getConsole().log("Found row");
}
}
com.google.gwt.dom.client.TableSectionElement parentElement = (TableSectionElement) e
.getParentElement();
NodeList rows = parentElement.getRows();
for (int i = 0; i < rows.getLength(); i++) {
if (rows.getItem(i) == e) {
// ApplicationConnection.getConsole().log(
// "Found index for row" + 1);
return "option" + i;
}
}
return null;
} else {
return null;
}
}
/**
* Hides context menu if it is currently shown by given action owner.
*
* @param actionOwner
*/
public void ensureHidden(ActionOwner actionOwner) {
if (this.actionOwner == actionOwner) {
hide();
}
}
}