/*
* Copyright 2010 Daniel Kurka
*
* 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.googlecode.mgwt.ui.client.widget.dialog.overlay;
import com.google.gwt.core.shared.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.dom.client.TouchCancelEvent;
import com.google.gwt.event.dom.client.TouchCancelHandler;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchEndHandler;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchMoveHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.googlecode.mgwt.dom.client.event.mouse.HandlerRegistrationCollection;
import com.googlecode.mgwt.dom.client.event.tap.HasTapHandlers;
import com.googlecode.mgwt.dom.client.event.tap.TapHandler;
import com.googlecode.mgwt.dom.client.event.touch.HasTouchHandlers;
import com.googlecode.mgwt.dom.client.event.touch.TouchHandler;
import com.googlecode.mgwt.ui.client.util.MGWTUtil;
import com.googlecode.mgwt.ui.client.widget.animation.Animation;
import com.googlecode.mgwt.ui.client.widget.animation.AnimationEndCallback;
import com.googlecode.mgwt.ui.client.widget.animation.AnimationWidget;
import com.googlecode.mgwt.ui.client.widget.dialog.Dialog;
import com.googlecode.mgwt.ui.client.widget.panel.flex.FlexPropertyHelper.Alignment;
import com.googlecode.mgwt.ui.client.widget.panel.flex.FlexPropertyHelper.Justification;
import com.googlecode.mgwt.ui.client.widget.panel.flex.RootFlexPanel;
import com.googlecode.mgwt.ui.client.widget.touch.TouchDelegate;
import java.util.Iterator;
/**
* Baseclass for creating dialogs that are animated
*
* @author Daniel Kurka
*/
public abstract class DialogOverlay implements HasWidgets, HasTouchHandlers, HasTapHandlers,
Dialog {
private static class NoopAnimationEndCallback implements AnimationEndCallback {
public void onAnimationEnd() {
}
}
private class InternalTouchHandler implements TouchHandler {
private final Element shadow;
private Element startTarget;
private InternalTouchHandler(Element shadow) {
this.shadow = shadow;
}
@Override
public void onTouchCancel(TouchCancelEvent event) {
startTarget = null;
}
@Override
public void onTouchEnd(TouchEndEvent event) {
EventTarget eventTarget = event.getNativeEvent().getEventTarget();
if (eventTarget != null) {
// no textnode or element node
if (Node.is(eventTarget)) {
if (Element.is(eventTarget)) {
Element endTarget = eventTarget.cast();
if (endTarget == shadow && startTarget == shadow) {
maybeHide();
}
}
}
}
startTarget = null;
}
@Override
public void onTouchMove(TouchMoveEvent event) {
}
@Override
public void onTouchStart(TouchStartEvent event) {
EventTarget eventTarget = event.getNativeEvent().getEventTarget();
if (eventTarget != null) {
// no textnode or element node
if (Node.is(eventTarget)) {
if (Element.is(eventTarget)) {
startTarget = eventTarget.cast();
}
}
}
}
}
private static final NoopAnimationEndCallback NOOP_CALLBACK = new NoopAnimationEndCallback();
public static final DialogOverlayAppearance DEFAULT_APPEARANCE = GWT
.create(DialogOverlayAppearance.class);
protected final DialogOverlayAppearance appearance;
private RootFlexPanel container;
private HasWidgets panelToOverlay;
private AnimationWidget display;
private boolean centerChildren;
private boolean autoHide;
private boolean isVisible;
private TouchDelegate touchDelegateForDisplay;
public DialogOverlay() {
this(DEFAULT_APPEARANCE);
}
public DialogOverlay(DialogOverlayAppearance appearance) {
this.appearance = appearance;
display = new AnimationWidget();
display.addStyleName(appearance.overlayCss().dialogOverlay());
touchDelegateForDisplay = new TouchDelegate(display);
display.addStyleName(appearance.overlayCss().animationContainerShadow());
container = new RootFlexPanel();
addTouchHandler(new InternalTouchHandler(container.getElement()));
}
@Override
public void add(Widget w) {
container.add(w);
}
@Override
public HandlerRegistration addTouchStartHandler(TouchStartHandler handler) {
return touchDelegateForDisplay.addTouchStartHandler(handler);
}
@Override
public HandlerRegistration addTouchMoveHandler(TouchMoveHandler handler) {
return touchDelegateForDisplay.addTouchMoveHandler(handler);
}
@Override
public HandlerRegistration addTouchCancelHandler(TouchCancelHandler handler) {
return touchDelegateForDisplay.addTouchCancelHandler(handler);
}
@Override
public HandlerRegistration addTouchEndHandler(TouchEndHandler handler) {
return touchDelegateForDisplay.addTouchEndHandler(handler);
}
@Override
public HandlerRegistration addTouchHandler(TouchHandler handler) {
HandlerRegistrationCollection handlerRegistrationCollection =
new HandlerRegistrationCollection();
handlerRegistrationCollection.addHandlerRegistration(addTouchCancelHandler(handler));
handlerRegistrationCollection.addHandlerRegistration(addTouchStartHandler(handler));
handlerRegistrationCollection.addHandlerRegistration(addTouchEndHandler(handler));
handlerRegistrationCollection.addHandlerRegistration(addTouchMoveHandler(handler));
return handlerRegistrationCollection;
}
public HandlerRegistration addTapHandler(TapHandler handler) {
return touchDelegateForDisplay.addTapHandler(handler);
}
/**
* Show the dialog centered
*/
public void center() {
centerChildren = true;
show();
}
@Override
public void clear() {
container.clear();
}
/**
* get the panel that the dialog overlays
*
* @return the panel that is overlayed by this dialog
*/
public HasWidgets getPanelToOverlay() {
if (panelToOverlay == null) {
panelToOverlay = RootPanel.get();
}
return panelToOverlay;
}
/**
* hide the dialog if it is visible
*/
public void hide() {
if (!isVisible)
return;
isVisible = false;
Animation animation = getHideAnimation();
display.animate(animation, false, new AnimationEndCallback() {
@Override
public void onAnimationEnd() {
HasWidgets panel = getPanelToOverlay();
panel.remove(display.asWidget());
// see issue 247 => http://code.google.com/p/mgwt/issues/detail?id=247
MGWTUtil.forceFullRepaint();
}
});
}
/**
* Should the dialog hide itself if there is a tap outside the dialog
*
* @return true if the dialog automatically hides, otherwise false
*/
public boolean isHideOnBackgroundClick() {
return autoHide;
}
@Override
public Iterator<Widget> iterator() {
return container.iterator();
}
@Override
public boolean remove(Widget w) {
return container.remove(w);
}
/**
* Should the content of the dialog be centered
*
* @param centerContent true to center content
*/
public void setCenterContent(boolean centerContent) {
this.centerChildren = centerContent;
}
/**
* Should the dialog hide itself if there is a tap outside the dialog
*
* @param hideOnBackgroundClick true if the dialog automatically hides, otherwise false
*/
public void setHideOnBackgroundClick(boolean hideOnBackgroundClick) {
this.autoHide = hideOnBackgroundClick;
}
/**
* set the panel that should be overlayed by the dialog
*
* @param panel the area to be overlayed
*/
public void setPanelToOverlay(HasWidgets panel) {
this.panelToOverlay = panel;
}
/**
* should the dialog add a shadow over the area that it covers
*
* @param shadow true to add a shadow
*/
public void setShadow(boolean shadow) {
if (shadow) {
display.asWidget().addStyleName(appearance.overlayCss().animationContainerShadow());
} else {
display.asWidget().removeStyleName(appearance.overlayCss().animationContainerShadow());
}
}
public void show() {
if (isVisible) {
return;
}
isVisible = true;
// add overlay to DOM
HasWidgets panel = getPanelToOverlay();
panel.add(display.asWidget());
if (centerChildren) {
container.setAlignment(Alignment.CENTER);
container.setJustification(Justification.CENTER);
} else {
container.clearAlignment();
container.clearJustification();
}
display.setFirstWidget(container);
// and animiate
display.animate(getShowAnimation(), true, NOOP_CALLBACK);
}
protected abstract Animation getShowAnimation();
protected abstract Animation getHideAnimation();
protected void maybeHide() {
if (autoHide) {
hide();
}
}
@Override
public void fireEvent(GwtEvent<?> event) {
display.fireEvent(event);
}
}