/*
* Copyright 2016 cruxframework.org.
*
* 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 org.cruxframework.crux.smartfaces.client.slider;
import org.cruxframework.crux.core.client.css.transition.Transition;
import org.cruxframework.crux.core.client.css.transition.Transition.Callback;
import org.cruxframework.crux.core.client.event.HasSelectHandlers;
import org.cruxframework.crux.core.client.event.SelectEvent;
import org.cruxframework.crux.core.client.event.SelectHandler;
import org.cruxframework.crux.core.client.event.TouchEventsHandler;
import org.cruxframework.crux.smartfaces.client.backbone.common.FacesBackboneResourcesCommon;
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.NativeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
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.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.HasCloseHandlers;
import com.google.gwt.event.logical.shared.HasOpenHandlers;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
/**
* @author Thiago da Rosa de Bustamante
*
*/
public class SlideOutPanel extends Composite implements HasSlideStartHandlers, HasSlideEndHandlers,
HasSelectHandlers, HasOpenHandlers<SlideOutPanel>,
HasCloseHandlers<SlideOutPanel>,
TouchEventsHandler
{
public static final String DEFAULT_STYLE_NAME = "faces-SlideOutPanel";
private static final int DEFAULT_SLIDE_SENSITIVITY = 5;
private static final String SLIDE_OUT_MAIN_STYLE_NAME = "main";
private static final String SLIDE_OUT_MENU_STYLE_NAME = "menu";
protected boolean autoHideMenu = false;
protected FlowPanel contentPanel;
protected SimplePanel mainPanel;
protected MenuOrientation menuOrientation;
protected SimplePanel menuPanel;
protected boolean open = false;
protected boolean preventDefaultTouchEvents = false;
protected boolean slideEnabled;
protected int slideSensitivity = DEFAULT_SLIDE_SENSITIVITY;
protected int slideTransitionDuration = 250;
protected boolean sliding = false;
protected boolean stopPropagationTouchEvents = false;
protected SimplePanel touchPanel;
private HandlerRegistration autoHideSelectHandler;
private SlideOutPanelEventHandlers eventHandlers;
private boolean hasSelectHandlers = false;
/**
* Constructor
*/
public SlideOutPanel()
{
FacesBackboneResourcesCommon.INSTANCE.css().ensureInjected();
touchPanel = new SimplePanel();
contentPanel = new FlowPanel();
touchPanel.add(contentPanel);
initWidget(touchPanel);
setStyleName(DEFAULT_STYLE_NAME);
contentPanel.setStyleName(FacesBackboneResourcesCommon.INSTANCE.css().facesSlideOutContentPanel());
menuPanel = new SimplePanel();
menuPanel.setStyleName(SLIDE_OUT_MENU_STYLE_NAME);
menuPanel.addStyleName(FacesBackboneResourcesCommon.INSTANCE.css().facesSlideOutMenu());
contentPanel.add(menuPanel);
mainPanel = new SimplePanel();
mainPanel.setStyleName(SLIDE_OUT_MAIN_STYLE_NAME);
mainPanel.addStyleName(FacesBackboneResourcesCommon.INSTANCE.css().facesSlideOutMain());
contentPanel.add(mainPanel);
setSlideEnabled(true);
setMenuOrientation(MenuOrientation.left);
}
@Override
public HandlerRegistration addCloseHandler(CloseHandler<SlideOutPanel> handler)
{
return addHandler(handler, CloseEvent.getType());
}
@Override
public HandlerRegistration addOpenHandler(OpenHandler<SlideOutPanel> handler)
{
return addHandler(handler, OpenEvent.getType());
}
@Override
public HandlerRegistration addSelectHandler(SelectHandler handler)
{
if (eventHandlers == null)
{
eventHandlers = GWT.create(SlideOutPanelEventHandlers.class);
eventHandlers.handleEvents(this);
}
this.hasSelectHandlers = true;
return addHandler(handler, SelectEvent.getType());
}
@Override
public HandlerRegistration addSlideEndHandler(SlideEndHandler handler)
{
return addHandler(handler, SlideEndEvent.getType());
}
@Override
public HandlerRegistration addSlideStartHandler(SlideStartHandler handler)
{
return addHandler(handler, SlideStartEvent.getType());
}
/**
* Hide the nav widget, sliding back horizontally to it.
*/
public void close()
{
if (open)
{
slide(0, true, false);
}
}
/**
* Retrieve the menu orientation
* @return
*/
public MenuOrientation getMenuOrientation()
{
return menuOrientation;
}
public int getSlideSensitivity()
{
return slideSensitivity;
}
/**
* Gets the duration of the slide animations in milliseconds.
* @return animations duration
*/
public int getSlideTransitionDuration()
{
return slideTransitionDuration;
}
public boolean isAutoHideMenu()
{
return autoHideMenu;
}
/**
* Verify if the slide movement is horizontally orientated
* @return
*/
public boolean isHorizontalOrientation()
{
return menuOrientation == MenuOrientation.right || menuOrientation == MenuOrientation.left;
}
/**
* Check if the panel is open
* @return
*/
public boolean isOpen()
{
return open;
}
/**
* If enabled, slide will be available for touch devices
* @return
*/
public boolean isSlideEnabled()
{
return slideEnabled;
}
/**
* Check if the panel is slinding any widget
* @return true if sliding
*/
public boolean isSliding()
{
return sliding;
}
/**
* Show the hidden widget, sliding horizontally to it.
*/
public void open()
{
if (!open && hasHiddenWidget())
{
int slideBy;
switch (menuOrientation)
{
case left:
slideBy = menuPanel.getElement().getOffsetWidth();
break;
case right:
slideBy = -menuPanel.getElement().getOffsetWidth();
break;
case top:
slideBy = menuPanel.getElement().getOffsetHeight();
break;
case bottom:
slideBy = -menuPanel.getElement().getOffsetHeight();
break;
default:
slideBy = 0;
break;
}
slide(slideBy, true, true);
}
}
public void setAutoHideMenu(boolean autoHideMenu)
{
this.autoHideMenu = autoHideMenu;
if (autoHideMenu)
{
autoHideSelectHandler = addSelectHandler(new SelectHandler()
{
@Override
public void onSelect(SelectEvent event)
{
if (isOpen())
{
close();
}
}
});
}
else
{
if (autoHideSelectHandler != null)
{
autoHideSelectHandler.removeHandler();
autoHideSelectHandler = null;
}
}
}
/**
* Sets the widget to be displayed into the main area
* @param w
*/
public void setMainWidget(IsWidget w)
{
setMainWidget(w.asWidget());
}
/**
* Sets the widget to be displayed into the main area
* @param widget
*/
public void setMainWidget(Widget widget)
{
mainPanel.add(widget);
}
/**
* Define the menu orientation
* @param menuOrientation
*/
public void setMenuOrientation(MenuOrientation menuOrientation)
{
this.menuOrientation = menuOrientation;
switch (menuOrientation)
{
case left:
menuPanel.getElement().getStyle().setProperty("left", "0px");
menuPanel.getElement().getStyle().setProperty("right", "");
menuPanel.getElement().getStyle().setProperty("top", "");
menuPanel.getElement().getStyle().setProperty("bottom", "");
break;
case right:
menuPanel.getElement().getStyle().setProperty("left", "");
menuPanel.getElement().getStyle().setProperty("right", "0px");
menuPanel.getElement().getStyle().setProperty("top", "");
menuPanel.getElement().getStyle().setProperty("bottom", "");
break;
case top:
menuPanel.getElement().getStyle().setProperty("left", "");
menuPanel.getElement().getStyle().setProperty("right", "");
menuPanel.getElement().getStyle().setProperty("top", "0px");
menuPanel.getElement().getStyle().setProperty("bottom", "");
break;
case bottom:
menuPanel.getElement().getStyle().setProperty("left", "");
menuPanel.getElement().getStyle().setProperty("right", "");
menuPanel.getElement().getStyle().setProperty("top", "");
menuPanel.getElement().getStyle().setProperty("bottom", "0px");
break;
}
}
/**
* Sets the widget to be displayed into the menu area
* @param w
*/
public void setMenuWidget(IsWidget w)
{
setMenuWidget(w.asWidget());
}
/**
* Sets the widget to be displayed into the menu area
* @param widget
*/
public void setMenuWidget(Widget widget)
{
menuPanel.add(widget);
}
/**
* Defines the menu area's width
* @param width
*/
public void setMenuWidth(String width)
{
menuPanel.setWidth(width);
}
public void setPreventDefaultTouchEvents(boolean preventDefaultTouchEvents)
{
this.preventDefaultTouchEvents = preventDefaultTouchEvents;
}
public void setSlideEnabled(boolean enabled)
{
if (eventHandlers != null && !enabled && !hasSelectHandlers)
{
eventHandlers.releaseEvents();
eventHandlers = null;
}
if (eventHandlers == null && enabled)
{
eventHandlers = GWT.create(SlideOutPanelEventHandlers.class);
eventHandlers.handleEvents(this);
}
this.slideEnabled = enabled;
}
public void setSlideSensitivity(int slideSensitivity)
{
this.slideSensitivity = slideSensitivity;
}
/**
* Sets the duration of the slide animations in milliseconds.
* @param slideTransitionDuration
*/
public void setSlideTransitionDuration(int transitionDuration)
{
this.slideTransitionDuration = transitionDuration;
}
public void setStopPropagationTouchEvents(boolean stopPropagationTouchEvents)
{
this.stopPropagationTouchEvents = stopPropagationTouchEvents;
}
protected HandlerRegistration addClickHandler(ClickHandler handler)
{
return addDomHandler(handler, ClickEvent.getType());
}
protected HandlerRegistration addTouchEndHandler(TouchEndHandler handler)
{
return addDomHandler(handler, TouchEndEvent.getType());
}
protected HandlerRegistration addTouchMoveHandler(TouchMoveHandler handler)
{
return addDomHandler(handler, TouchMoveEvent.getType());
}
protected HandlerRegistration addTouchStartHandler(TouchStartHandler handler)
{
return addDomHandler(handler, TouchStartEvent.getType());
}
/**
* Verify if the hidden panel has a next widget to show.
* @return true if has next widget
*/
boolean hasHiddenWidget()
{
return menuPanel.getWidget() != null;
}
void slide(final int slideBy, boolean fireSlidingStartEvent, final boolean openMenu)
{
sliding = true;
if (fireSlidingStartEvent)
{
SlideStartEvent.fire(this);
}
if (isHorizontalOrientation())
{
Transition.translateX(mainPanel, slideBy, slideTransitionDuration, new Callback()
{
@Override
public void onTransitionCompleted()
{
SlideEndEvent.fire(SlideOutPanel.this);
sliding = false;
open = openMenu;
if (open)
{
OpenEvent.fire(SlideOutPanel.this, SlideOutPanel.this);
}
else
{
CloseEvent.fire(SlideOutPanel.this, SlideOutPanel.this);
}
}
});
}
else
{
Transition.translateY(mainPanel, slideBy, slideTransitionDuration, new Callback()
{
@Override
public void onTransitionCompleted()
{
SlideEndEvent.fire(SlideOutPanel.this);
sliding = false;
open = openMenu;
if (open)
{
OpenEvent.fire(SlideOutPanel.this, SlideOutPanel.this);
}
else
{
CloseEvent.fire(SlideOutPanel.this, SlideOutPanel.this);
}
}
});
}
}
public static enum MenuOrientation {bottom, left, right, top}
static class SlideOutPanelEventHandlers
{
protected SlideOutPanel slideOutPanel;
public void releaseEvents()
{
this.slideOutPanel = null;
}
protected boolean eventTargetsMenu(NativeEvent event)
{
EventTarget target = event.getEventTarget();
if (Element.is(target))
{
return slideOutPanel.menuPanel.getElement().isOrHasChild(Element.as(target));
}
return false;
}
protected void handleEvents(SlideOutPanel slideOutPanel)
{
this.slideOutPanel = slideOutPanel;
}
}
}