
com.vaadin.terminal.gwt.client.ui.VOverlay Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of vaadin Show documentation
Show all versions of vaadin 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.
The newest version!
/*
* Copyright 2011 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.terminal.gwt.client.ui;
import com.google.gwt.animation.client.Animation;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.terminal.gwt.client.BrowserInfo;
/**
* In Vaadin UI this Overlay should always be used for all elements that
* temporary float over other components like context menus etc. This is to deal
* stacking order correctly with VWindow objects.
*/
public class VOverlay extends PopupPanel implements CloseHandler {
/*
* The z-index value from where all overlays live. This can be overridden in
* any extending class.
*/
public static int Z_INDEX = 20000;
private static int leftFix = -1;
private static int topFix = -1;
/*
* Shadow element style. If an extending class wishes to use a different
* style of shadow, it can use setShadowStyle(String) to give the shadow
* element a new style name.
*/
public static final String CLASSNAME_SHADOW = "v-shadow";
/*
* The shadow element for this overlay.
*/
private Element shadow;
/*
* Creator of VOverlow (widget that made the instance, not the layout
* parent)
*/
private Widget owner;
/**
* The HTML snippet that is used to render the actual shadow. In consists of
* nine different DIV-elements with the following class names:
*
*
* .v-shadow[-stylename]
* ----------------------------------------------
* | .top-left | .top | .top-right |
* |---------------|-----------|----------------|
* | | | |
* | .left | .center | .right |
* | | | |
* |---------------|-----------|----------------|
* | .bottom-left | .bottom | .bottom-right |
* ----------------------------------------------
*
*
* See default theme 'shadow.css' for implementation example.
*/
private static final String SHADOW_HTML = "";
private boolean sinkShadowEvents = false;
public VOverlay() {
super();
adjustZIndex();
}
public VOverlay(boolean autoHide) {
super(autoHide);
adjustZIndex();
}
public VOverlay(boolean autoHide, boolean modal) {
super(autoHide, modal);
adjustZIndex();
}
public VOverlay(boolean autoHide, boolean modal, boolean showShadow) {
super(autoHide, modal);
setShadowEnabled(showShadow);
adjustZIndex();
}
/**
* Method to controle whether DOM elements for shadow are added. With this
* method subclasses can control displaying of shadow also after the
* constructor.
*
* @param enabled
* true if shadow should be displayed
*/
protected void setShadowEnabled(boolean enabled) {
if (enabled != isShadowEnabled()) {
if (enabled) {
shadow = DOM.createDiv();
shadow.setClassName(CLASSNAME_SHADOW);
shadow.setInnerHTML(SHADOW_HTML);
DOM.setStyleAttribute(shadow, "position", "absolute");
addCloseHandler(this);
} else {
removeShadowIfPresent();
shadow = null;
}
}
}
protected boolean isShadowEnabled() {
return shadow != null;
}
private void removeShadowIfPresent() {
if (isShadowAttached()) {
shadow.getParentElement().removeChild(shadow);
// Remove event listener from the shadow
unsinkShadowEvents();
}
}
private boolean isShadowAttached() {
return isShadowEnabled() && shadow.getParentElement() != null;
}
private void adjustZIndex() {
setZIndex(Z_INDEX);
}
/**
* Set the z-index (visual stack position) for this overlay.
*
* @param zIndex
* The new z-index
*/
protected void setZIndex(int zIndex) {
DOM.setStyleAttribute(getElement(), "zIndex", "" + zIndex);
if (isShadowEnabled()) {
DOM.setStyleAttribute(shadow, "zIndex", "" + zIndex);
}
}
@Override
public void setPopupPosition(int left, int top) {
// TODO, this should in fact be part of
// Document.get().getBodyOffsetLeft/Top(). Would require overriding DOM
// for all permutations. Now adding fix as margin instead of fixing
// left/top because parent class saves the position.
Style style = getElement().getStyle();
style.setMarginLeft(-adjustByRelativeLeftBodyMargin(), Unit.PX);
style.setMarginTop(-adjustByRelativeTopBodyMargin(), Unit.PX);
super.setPopupPosition(left, top);
updateShadowSizeAndPosition(isAnimationEnabled() ? 0 : 1);
}
private static int adjustByRelativeTopBodyMargin() {
if (topFix == -1) {
topFix = detectRelativeBodyFixes("top");
}
return topFix;
}
private native static int detectRelativeBodyFixes(String axis)
/*-{
try {
var b = $wnd.document.body;
var cstyle = b.currentStyle ? b.currentStyle : getComputedStyle(b);
if(cstyle && cstyle.position == 'relative') {
return b.getBoundingClientRect()[axis];
}
} catch(e){}
return 0;
}-*/;
private static int adjustByRelativeLeftBodyMargin() {
if (leftFix == -1) {
leftFix = detectRelativeBodyFixes("left");
}
return leftFix;
}
@Override
public void show() {
super.show();
if (isShadowEnabled()) {
if (isAnimationEnabled()) {
ShadowAnimation sa = new ShadowAnimation();
sa.run(200);
} else {
updateShadowSizeAndPosition(1.0);
}
}
}
@Override
protected void onDetach() {
super.onDetach();
// Always ensure shadow is removed when the overlay is removed.
removeShadowIfPresent();
}
@Override
public void setVisible(boolean visible) {
super.setVisible(visible);
if (isShadowEnabled()) {
shadow.getStyle().setProperty("visibility",
visible ? "visible" : "hidden");
}
}
@Override
public void setWidth(String width) {
super.setWidth(width);
updateShadowSizeAndPosition(1.0);
}
@Override
public void setHeight(String height) {
super.setHeight(height);
updateShadowSizeAndPosition(1.0);
}
/**
* Sets the shadow style for this overlay. Will override any previous style
* for the shadow. The default style name is defined by CLASSNAME_SHADOW.
* The given style will be prefixed with CLASSNAME_SHADOW.
*
* @param style
* The new style name for the shadow element. Will be prefixed by
* CLASSNAME_SHADOW, e.g. style=='foobar' -> actual style
* name=='v-shadow-foobar'.
*/
protected void setShadowStyle(String style) {
if (isShadowEnabled()) {
shadow.setClassName(CLASSNAME_SHADOW + "-" + style);
}
}
/*
* Extending classes should always call this method after they change the
* size of overlay without using normal 'setWidth(String)' and
* 'setHeight(String)' methods (if not calling super.setWidth/Height).
*/
public void updateShadowSizeAndPosition() {
updateShadowSizeAndPosition(1.0);
}
/**
* Recalculates proper position and dimensions for the shadow element. Can
* be used to animate the shadow, using the 'progress' parameter (used to
* animate the shadow in sync with GWT PopupPanel's default animation
* 'PopupPanel.AnimationType.CENTER').
*
* @param progress
* A value between 0.0 and 1.0, indicating the progress of the
* animation (0=start, 1=end).
*/
private void updateShadowSizeAndPosition(final double progress) {
// Don't do anything if overlay element is not attached
if (!isAttached() || shadow == null) {
return;
}
// Calculate proper z-index
String zIndex = null;
try {
// Odd behaviour with Windows Hosted Mode forces us to use
// this redundant try/catch block (See dev.vaadin.com #2011)
zIndex = DOM.getStyleAttribute(getElement(), "zIndex");
} catch (Exception ignore) {
// Ignored, will cause no harm
zIndex = "1000";
}
if (zIndex == null) {
zIndex = "" + Z_INDEX;
}
// Calculate position and size
if (BrowserInfo.get().isIE()) {
// Shake IE
getOffsetHeight();
getOffsetWidth();
}
int x = getAbsoluteLeft();
int y = getAbsoluteTop();
/* This is needed for IE7 at least */
// Account for the difference between absolute position and the
// body's positioning context.
x -= Document.get().getBodyOffsetLeft();
y -= Document.get().getBodyOffsetTop();
x -= adjustByRelativeLeftBodyMargin();
y -= adjustByRelativeTopBodyMargin();
int width = getOffsetWidth();
int height = getOffsetHeight();
if (width < 0) {
width = 0;
}
if (height < 0) {
height = 0;
}
// Animate the shadow size
x += (int) (width * (1.0 - progress) / 2.0);
y += (int) (height * (1.0 - progress) / 2.0);
width = (int) (width * progress);
height = (int) (height * progress);
// Opera needs some shaking to get parts of the shadow showing
// properly
// (ticket #2704)
if (BrowserInfo.get().isOpera()) {
// Clear the height of all middle elements
DOM.getChild(shadow, 3).getStyle().setProperty("height", "auto");
DOM.getChild(shadow, 4).getStyle().setProperty("height", "auto");
DOM.getChild(shadow, 5).getStyle().setProperty("height", "auto");
}
// Update correct values
DOM.setStyleAttribute(shadow, "zIndex", zIndex);
DOM.setStyleAttribute(shadow, "width", width + "px");
DOM.setStyleAttribute(shadow, "height", height + "px");
DOM.setStyleAttribute(shadow, "top", y + "px");
DOM.setStyleAttribute(shadow, "left", x + "px");
DOM.setStyleAttribute(shadow, "display", progress < 0.9 ? "none" : "");
// Opera fix, part 2 (ticket #2704)
if (BrowserInfo.get().isOpera()) {
// We'll fix the height of all the middle elements
DOM.getChild(shadow, 3)
.getStyle()
.setPropertyPx("height",
DOM.getChild(shadow, 3).getOffsetHeight());
DOM.getChild(shadow, 4)
.getStyle()
.setPropertyPx("height",
DOM.getChild(shadow, 4).getOffsetHeight());
DOM.getChild(shadow, 5)
.getStyle()
.setPropertyPx("height",
DOM.getChild(shadow, 5).getOffsetHeight());
}
// Attach to dom if not there already
if (!isShadowAttached()) {
RootPanel.get().getElement().insertBefore(shadow, getElement());
sinkShadowEvents();
}
}
protected class ShadowAnimation extends Animation {
@Override
protected void onUpdate(double progress) {
updateShadowSizeAndPosition(progress);
}
}
public void onClose(CloseEvent event) {
removeShadowIfPresent();
}
@Override
public void sinkEvents(int eventBitsToAdd) {
super.sinkEvents(eventBitsToAdd);
// Also sink events on the shadow if present
sinkShadowEvents();
}
private void sinkShadowEvents() {
if (isSinkShadowEvents() && isShadowAttached()) {
// Sink the same events as the actual overlay has sunk
DOM.sinkEvents(shadow, DOM.getEventsSunk(getElement()));
// Send events to VOverlay.onBrowserEvent
DOM.setEventListener(shadow, this);
}
}
private void unsinkShadowEvents() {
if (isShadowAttached()) {
DOM.setEventListener(shadow, null);
DOM.sinkEvents(shadow, 0);
}
}
/**
* Enables or disables sinking the events of the shadow to the same
* onBrowserEvent as events to the actual overlay goes.
*
* Please note, that if you enable this, you can't assume that e.g.
* event.getEventTarget returns an element inside the DOM structure of the
* overlay
*
* @param sinkShadowEvents
*/
protected void setSinkShadowEvents(boolean sinkShadowEvents) {
this.sinkShadowEvents = sinkShadowEvents;
if (sinkShadowEvents) {
sinkShadowEvents();
} else {
unsinkShadowEvents();
}
}
protected boolean isSinkShadowEvents() {
return sinkShadowEvents;
}
/**
* Get owner (Widget that made this VOverlay, not the layout parent) of
* VOverlay
*
* @return Owner (creator) or null if not defined
*/
public Widget getOwner() {
return owner;
}
/**
* Set owner (Widget that made this VOverlay, not the layout parent) of
* VOverlay
*
* @param owner
* Owner (creator) of VOverlay
*/
public void setOwner(Widget owner) {
this.owner = owner;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy