/*
* Copyright (c) 2010 Zhihua (Dennis) Jiang
*
* 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.gwtmobile.ui.client.widgets;
import java.beans.Beans;
import java.util.Iterator;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import com.gwtmobile.ui.client.CSS.StyleNames.Primary;
import com.gwtmobile.ui.client.CSS.StyleNames.Secondary;
import com.gwtmobile.ui.client.event.DragController;
import com.gwtmobile.ui.client.event.DragEvent;
import com.gwtmobile.ui.client.event.DragEventsHandler;
import com.gwtmobile.ui.client.event.SwipeEvent;
import com.gwtmobile.ui.client.event.SwipeEventsHandler;
import com.gwtmobile.ui.client.utils.Utils;
public class ScrollPanel extends PanelBase implements DragEventsHandler, SwipeEventsHandler {
private boolean _withPadding = true;
private boolean _hasTextBox = false;
private boolean _fullHeight = false;
private Element _scrollbar;
protected HTMLPanel _intPanel = new HTMLPanel("");
public ScrollPanel() {
super.add(_intPanel);
setStyleName(Primary.ScrollPanel);
setWithPadding(_withPadding);
this.initScrollbar();
if (Beans.isDesignTime()) {
_intPanel.add(new Label("Empty ScrollPanel. " + getDesignTimeMessage()));
}
}
public boolean isWithPadding() {
return _withPadding;
}
public void setWithPadding(boolean withPadding) {
this._withPadding = withPadding;
if (withPadding){
addStyleName(Secondary.WithPadding);
} else {
removeStyleName(Secondary.WithPadding);
}
}
private void initScrollbar(){
_scrollbar=DOM.createDiv();
_scrollbar.appendChild(DOM.createDiv());
_scrollbar.setClassName(Primary.ScrollBar);
this.getElement().insertFirst(_scrollbar);
this.hideScrollBar();
}
private void adjustScrollbar(double scrollPannerPos){
Element widgetEle = getWidget().getElement();
int scrollPannelHeight = widgetEle.getOffsetHeight();
double scrollHeight=Utils.getHeight(this._scrollbar);
double scrollbarHeight=(scrollHeight*scrollHeight)/scrollPannelHeight;
if(scrollbarHeight>=scrollHeight){
this.hideScrollBar();
return;
}else{
this.showScrollBar();
}
double scrollbarPos=-scrollHeight*scrollPannerPos/scrollPannelHeight;
if(scrollPannerPos>0) {
scrollbarHeight=(scrollbarHeight-scrollPannerPos);
scrollbarPos=0;
if(scrollbarHeight<8) scrollbarHeight=8;
}else if((-scrollPannerPos+scrollHeight)>(scrollPannelHeight)){
double outScroll=(-scrollPannerPos+scrollHeight)-scrollPannelHeight;
scrollbarHeight=scrollbarHeight-outScroll;
if(scrollbarHeight<8) {
scrollbarHeight=8;
}else{
scrollbarPos=scrollbarPos+outScroll;
}
scrollbarPos=(scrollbarPos+scrollHeight)>scrollHeight?scrollHeight-scrollbarHeight:scrollbarPos;
}
Utils.setHeight(this._scrollbar.getFirstChildElement(), scrollbarHeight);
Utils.setTranslateY(this._scrollbar.getFirstChildElement(),scrollbarPos);
}
private void showScrollBar(){
this._scrollbar.setAttribute("style", "opacity:1");
}
private void hideScrollBar(){
this._scrollbar.setAttribute("style", "opacity:0");
}
public void setHasTextBox(boolean hasTextBox) {
hasTextBox = hasTextBox && Utils.isAndroid();
}
public boolean getHasTextBox() {
return _hasTextBox;
}
public boolean isFullHeight() {
return _fullHeight;
}
public void setFullHeight(boolean fullHeight) {
this._fullHeight = fullHeight;
if (fullHeight){
addStyleName(Secondary.FullHeight);
} else {
removeStyleName(Secondary.FullHeight);
}
}
@Override
public void onLoad() {
DragController.get().addDragEventsHandler(this);
DragController.get().addSwipeEventsHandler(this);
}
@Override
public void onUnload() {
DragController.get().removeDragEventsHandler(this);
DragController.get().removeSwipeEventHandler(this);
}
public Widget getWidget() {
//if (Beans.isDesignTime() an
return getIntPanel();
//return super.getWidget(0);
}
public void reset() {
Utils.setTransitionDuration(getWidget().getElement(), 0);
Utils.setTranslateY(getWidget().getElement(), 0);
}
public void setPostionToTop() {
Utils.setTransitionDuration(getWidget().getElement(), 0);
Utils.setTranslateY(getWidget().getElement(), 0);
}
public void setPositionToBottom() {
Utils.setTransitionDuration(getWidget().getElement(), 0);
Utils.setTranslateY(getWidget().getElement(),
this.getElement().getClientHeight() - this.getElement().getScrollHeight());
}
public void setScrollPosition(int pos) {
if (_hasTextBox) {
setStyleTop(pos);
}
else {
Element element = getWidget().getElement();
Utils.setTranslateY(element, pos);
}
this.adjustScrollbar(pos);
}
public int getScrollPosition() {
if (_hasTextBox) {
return getStyleTop();
} else {
Element element = getWidget().getElement();
return Utils.getTranslateY(element);
}
}
public int getScrollToPosition() {
if (_hasTextBox) {
return getStyleTop();
} else {
Element element = getWidget().getElement();
return Utils.getMatrixY(element);
}
}
@Override
public void onDragStart(DragEvent e) {
int matrix = getScrollToPosition();
int current = getScrollPosition();
Utils.setTransitionDuration(getWidget().getElement(), 0);
Utils.setTransitionDuration(this._scrollbar.getFirstChildElement(), 0);
if (current != matrix) { //scroll on going
int diff = current - matrix;
int offset = diff > 2 ? 2 : diff > -2 ? diff : -2;
setScrollPosition(matrix + offset);
DragController.get().suppressNextClick();
}
}
@Override
public void onDragMove(DragEvent e) {
Element widgetEle = getWidget().getElement();
int panelHeight = Utils.getHeight(this.getElement());
int widgetHeight = widgetEle.getOffsetHeight();
int current = getScrollPosition();
if (current > 0) {//exceed top boundary
if (e.OffsetY > 0) { //resist scroll down.
current += (int)(e.OffsetY / 2); // need the cast for production mode.
}
else {
current += e.OffsetY * 2;
}
}
else if (-current + panelHeight > widgetHeight) { //exceed bottom boundary
if (e.OffsetY < 0) { //resist scroll up.
current += (int)(e.OffsetY / 2);
}
else {
current += e.OffsetY * 2;
}
}
else {
current += e.OffsetY;
}
setScrollPosition(current);
}
@Override
public void onDragEnd(DragEvent e) {
Element widgetEle = getWidget().getElement();
int current = getScrollPosition();
if (current == 0) {
return;
}
int panelHeight = Utils.getHeight(this.getElement());
int widgetHeight = widgetEle.getOffsetHeight();
if (current > 0 //exceed top boundary
|| panelHeight > widgetHeight) {
Utils.setTransitionDuration(widgetEle, 500);
Utils.setTransitionDuration(this._scrollbar.getFirstChildElement(), 500);
setScrollPosition(0);
}
else if (-current + panelHeight > widgetHeight) { //exceed bottom boundary
Utils.setTransitionDuration(widgetEle, 500);
Utils.setTransitionDuration(this._scrollbar.getFirstChildElement(), 500);
setScrollPosition(panelHeight - widgetHeight);
}
this.hideScrollBar();
}
@Override
public void onSwipeVertical(SwipeEvent e) {
Element widgetEle = getWidget().getElement();
int panelHeight = Utils.getHeight(this.getElement());
int widgetHeight = widgetEle.getOffsetHeight();
long current = getScrollPosition();
if ((current >= 0) // exceed top boundary
|| (-current + panelHeight >= widgetHeight)) { // exceed bottom boundary
return;
}
double speed = e.getSpeed();
double timeFactor = 3000;
long time = (long) Math.abs(speed * timeFactor);
double dicstanceFactor = 0.25;
long distance = (long) (speed * time * dicstanceFactor);
//Utils.Console("speed " + speed + " time " + time + " distance " + distance + " current " + current);
current += distance;
if (current > 0) {//exceed top boundary
double timeAdj = 1 - (double)current / distance;
time = (long) (time * timeAdj);
current = 0;
}
else if (-current + panelHeight > widgetHeight) { //exceed bottom boundary
long bottom = panelHeight - widgetHeight;
double timeAdj = 1 - (double)(current - bottom) / distance;
time = (long) (time * timeAdj);
current = bottom;
}
Utils.setTransitionDuration(widgetEle, time);
Utils.setTransitionDuration(this._scrollbar.getFirstChildElement(), time);
Utils.addEventListenerOnce(this._scrollbar.getFirstChildElement(),
"webkitTransitionEnd", false, new EventListener() {
@Override
public void onBrowserEvent(Event event) {
hideScrollBar();
}
});
setScrollPosition((int) current);
}
@Override
public void onSwipeHorizontal(SwipeEvent e) {
}
@Override
public void add(Widget w) {
if (Beans.isDesignTime()) {
if (getWidgetCount() == 1 && isDesignTimeEmptyLabel(getWidget(0))) {
clear();
}
}
_intPanel.add(w);
if (Utils.isIOS()) {
Utils.setTranslateY(w.getElement(), 0); //anti-flickering on iOS.
}
}
private int getStyleTop() {
Style style = getWidget().getElement().getStyle();
String top = style.getTop();
if (top.isEmpty()) {
return 0;
}
else {
return Integer.parseInt(top.replace("px", ""));
}
}
private void setStyleTop(int top) {
Style style = getWidget().getElement().getStyle();
style.setTop(top, Unit.PX);
}
public Widget getIntPanel(){
return _intPanel;
}
@Override
public void clear() {
_intPanel.clear();
}
@Override
public Iterator<Widget> iterator() {
return _intPanel.iterator();
}
@Override
public boolean remove(Widget w) {
return _intPanel.remove(w);
}
@Override
public Widget getWidget(int index) {
return _intPanel.getWidget(index);
}
@Override
public int getWidgetCount() {
return _intPanel.getWidgetCount();
}
}