com.gwtm.ui.client.widgets.SlidingPanel.java Source code

Java tutorial

Introduction

Here is the source code for com.gwtm.ui.client.widgets.SlidingPanel.java

Source

/*
 * 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.gwtm.ui.client.widgets;

import java.beans.Beans;
import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.gwtm.ui.client.core.Utils;
import com.gwtm.ui.client.core.widgets.CssFactory.BoxAlign;
import com.gwtm.ui.client.core.widgets.CssFactory.BoxOrient;
import com.gwtm.ui.client.core.widgets.CssFactory.BoxPack;
import com.gwtm.ui.client.core.widgets.PanelBase;
import com.gwtm.ui.client.events.DraggingEvent;
import com.gwtm.ui.client.events.SwipeEvent;
import com.gwtm.ui.client.themes.ThemeManager;

public class SlidingPanel extends ScrollPanel implements ResizeHandler, HasValueChangeHandlers<Integer> {

    public enum IndicatorVisibility {
        none, alwaysvisible, autohide
    };

    public enum IndicatorPosition {
        top, bottom
    };

    public enum IndicatorType {
        dot, square, numeric, slidetitles
    };

    private int defaultSlide = 1;
    private int currentSlide = 1;
    private BoxPanel innerPanel;
    private int slideWidth = 0;
    private int savedCurrentSlide = 1;
    private int savedScrollPosition = 0;
    private double swipeThreshold = 0.15;
    private boolean movingFlag = false;
    private IndicatorRow indicator = null;
    private IndicatorVisibility indicatorVisibility = IndicatorVisibility.autohide;
    private IndicatorPosition indicatorPosition = IndicatorPosition.bottom;
    private IndicatorType indicatorType = IndicatorType.dot;

    public SlidingPanel() {
        // set primary css class
        Utils.Widgets.setPrimaryCssClass(this, ThemeManager.getTheme().slidingpanel());
        // set scroll direction of super scrollpanel
        setScrollDirection(ScrollDirection.Horizontal);
        // create the inner panel for slides and add it to super panel
        innerPanel = new BoxPanel(BoxOrient.horizontal, BoxPack.start, BoxAlign.start);
        innerPanel.setFullHeight(true);
        super.add(innerPanel);
        // know the available width before anything else, so this should give us without deferingcommands
        slideWidth = RootPanel.get().getOffsetWidth() - getElement().getAbsoluteLeft()
                - getElement().getAbsoluteRight();

        // necessary to recalculate the width for the slides
        addResizeHandler(this);

        if (getIndicatorVisibility() != IndicatorVisibility.none) {
            indicator = new IndicatorRow(); //.createIndicator(indicatorVisibility, indicatorPosition, indicatorType);
            addRootWidget(indicator);

        }

        // define designtime message
        addDesignTimeMessage("Add Slides to the panel.");
        addDesignTimeMessage("Set the 'currentSlide' property to switch slide.");

    }

    @Override
    public void onLoad() {
        super.onLoad();
        //refreshSlidesWidth();
        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
            @Override
            public void execute() {
                if (getIndicatorVisibility() != IndicatorVisibility.none) {
                    indicator.setActiveIndicator(getDefaultSlide());
                    indicator.show();
                }
                refreshSlidesWidth();
            }
        });
    }

    public IndicatorVisibility getIndicatorVisibility() {
        return indicatorVisibility;
    }

    public void setIndicatorVisibility(IndicatorVisibility indicatorVisibility) {
        this.indicatorVisibility = indicatorVisibility;
    }

    public IndicatorPosition getIndicatorPosition() {
        return indicatorPosition;
    }

    public void setIndicatorPosition(IndicatorPosition indicatorPosition) {
        this.indicatorPosition = indicatorPosition;
    }

    public IndicatorType getIndicatorType() {
        return indicatorType;
    }

    public void setIndicatorType(IndicatorType indicatorType) {
        this.indicatorType = indicatorType;
    }

    public int getSlideCount() {
        return innerPanel.getWidgetCount();
    }

    public void setDefaultSlide(int slide) {
        this.defaultSlide = slide;
        if (slide > 0 && slide < (getSlideCount() + 1)) {
            setCurrentSlide(slide);
        }
    }

    public int getDefaultSlide() {
        return this.defaultSlide;
    }

    public Slide getSlide(int index) {
        return (Slide) innerPanel.getWidget(index);
    }

    public int getSlideIndex(Slide slide) {
        return innerPanel.getWidgetIndex(slide);
    }

    @Override
    public void onDraggingStart(DraggingEvent e) {
        super.onDraggingStart(e);
        savedScrollPosition = (Utils.Fx.getTranslateX(this.getElement().getFirstChildElement()) * -1);
        savedCurrentSlide = calculateCurrentSlide();
        if (getIndicatorVisibility() != IndicatorVisibility.none)
            indicator.show();
        movingFlag = true;
    }

    @Override
    public void onSwipeHorizontal(SwipeEvent e) {
        // bypass swipe implementation from super.scrollpanel
        // this panel doesnt need it and actually it will break the sliding effect
        e.stopPropagation();
    }

    @Override
    public void onDraggingEnd(DraggingEvent e) {
        int currentScroll = (Utils.Fx.getTranslateX(this.getElement().getFirstChildElement()) * -1);
        int currentSlide = calculateCurrentSlide();
        int threshold = (int) (slideWidth * swipeThreshold);

        if (savedScrollPosition > currentScroll) {
            if (currentScroll < ((savedCurrentSlide * slideWidth - threshold) - slideWidth))
                savedCurrentSlide = currentSlide;
        } else {
            if (currentScroll > ((savedCurrentSlide * slideWidth + threshold) - slideWidth))
                savedCurrentSlide = currentSlide + 1;
        }

        Utils.Fx.setTransitionDuration(this.getElement().getFirstChildElement(), 500);
        setCurrentSlide(savedCurrentSlide);

        if (getIndicatorVisibility() == IndicatorVisibility.autohide) {
            Scheduler.get().scheduleFixedPeriod(new RepeatingCommand() {
                @Override
                public boolean execute() {
                    if (!movingFlag) {
                        indicator.hide();
                        return false;
                    }
                    return true;
                }
            }, 700);
        }

        movingFlag = false;
    }

    private int calculateCurrentSlide() {
        int currentScroll = (Utils.Fx.getTranslateX(this.getElement().getFirstChildElement()) * -1);
        int maxScroll = slideWidth * getSlideCount();
        int currentSlide = ((currentScroll != 0) ? currentScroll * getSlideCount() / maxScroll : 0) + 1;
        return currentSlide;
    }

    public void setCurrentSlide(int x) {

        if (x > getSlideCount())
            x = getSlideCount();

        if (x > currentSlide)
            x = currentSlide + 1;
        else if (x < currentSlide)
            x = currentSlide - 1;

        currentSlide = x;

        Utils.Fx.setTranslateX(this.getElement().getFirstChildElement(), -((slideWidth * x) - slideWidth));
        ValueChangeEvent.fire(this, x);

        indicator.setActiveIndicator(x);

    }

    public int getCurrentSlide() {
        return currentSlide;
    }

    @Override
    public void add(Widget w) {
        // assert (w instanceof Slide) :
        // "Can only add Slide widgets to SlidePanel.";
        // we can't assert because gwtdesign adds a Label by default
        if (Beans.isDesignTime()) {
            if (!(w instanceof Slide)) {
                addDesignTimeError("Can only add Slides to SlidePanel. Remove the "
                        + Utils.getSimpleName(w.getClass()) + " widget.");
                return;
            }
        }
        // set the calculated width for each added slide panel
        w.setWidth(slideWidth + "px");
        // add indicator 
        if (getIndicatorVisibility() != IndicatorVisibility.none)
            indicator.addIndicator();
        // add slide to inner panel
        innerPanel.add(w);

    }

    @Override
    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Integer> handler) {
        return this.addHandler(handler, ValueChangeEvent.getType());
    }

    @Override
    public void onResize(ResizeEvent event) {
        if (event != null && slideWidth == getElement().getOffsetWidth())
            return;
        refreshSlidesWidth();
    }

    protected void refreshSlidesWidth() {
        // update height for inner scroller
        Utils.Fx.setTransitionDuration(getIntPanel().getElement(), 0);
        // get new width
        if (getElement().getParentElement().getParentElement() != null)
            slideWidth = getElement().getParentElement().getParentElement().getOffsetWidth();
        else
            slideWidth = getElement().getParentElement().getOffsetWidth();
        //      slideWidth = getParent().getParent().getOffsetWidth();
        // update all slides width
        for (int i = 0; i < innerPanel.getWidgetCount(); i++)
            innerPanel.getWidget(i).setWidth(slideWidth + "px");
        // set/reset the scroller position by setting it again
        setCurrentSlide(getCurrentSlide());
    }

    class IndicatorRow extends PanelBase {

        static final String dotActive = "&#9679;";
        static final String dotInactive = "&#9675;";
        static final String squareActive = "&#9642;";
        static final String squareInactive = "&#9643;";

        IndicatorVisibility previousIndicatorVisibility = null;
        IndicatorPosition previousIndicatorPosition = null;
        IndicatorType previousIndicatorType = null;

        IndicatorRow() {
            //super("");
            Utils.Widgets.setSecondaryCssClass(this, ThemeManager.getTheme().slideindicator());
            configInficator();
        }

        private void configInficator() {
            if (indicatorVisibility != previousIndicatorVisibility) {
                if (previousIndicatorVisibility != null)
                    Utils.Widgets.removeCssClass(this, previousIndicatorVisibility.toString());
                Utils.Widgets.addCssClass(this, indicatorVisibility.toString());
            }
            if (indicatorPosition != previousIndicatorPosition) {
                if (previousIndicatorPosition != null)
                    Utils.Widgets.removeCssClass(this, previousIndicatorPosition.toString());
                Utils.Widgets.addCssClass(this, indicatorPosition.toString());
            }
            if (indicatorType != previousIndicatorType) {
                if (previousIndicatorType != null)
                    Utils.Widgets.removeCssClass(this, previousIndicatorType.toString());
                Utils.Widgets.addCssClass(this, indicatorType.toString());
                if (previousIndicatorType != null && previousIndicatorType == IndicatorType.slidetitles) {
                    setIndicatorEvent(false);
                } else if (previousIndicatorType != null && indicatorType == IndicatorType.slidetitles) {
                    setIndicatorEvent(true);
                }

            }
            previousIndicatorVisibility = indicatorVisibility;
            previousIndicatorPosition = indicatorPosition;
            previousIndicatorType = indicatorType;
        }

        List<JavaScriptObject> eventStack = new ArrayList<JavaScriptObject>();

        void setIndicatorEvent(boolean addEvent) {
            for (int i = 0; i < getWidgetCount(); i++)
                if (addEvent) {
                    eventStack.add(Utils.Eventing.addEventListener(getWidget(i).getElement(), "click", true,
                            new IndicatorClickListener(i + 1)));
                } else {
                    if (eventStack.size() == getWidgetCount())
                        Utils.Eventing.removeEventListener(getWidget(i).getElement(), "click", true,
                                eventStack.get(i));
                }
            if (!addEvent)
                eventStack.clear();
        }

        void addIndicator() {
            HTML ind = new HTML();
            //if (indicatorType == IndicatorType.slidetitles)
            //   Utils.Eventing.addEventListener(ind.getElement(), "click", true, new IndicatorClickListener(getWidgetCount()+1));
            add(ind);
        }

        void hide() {
            Utils.Widgets.removeCssClass(this, "showing");
        }

        void show() {
            Utils.Widgets.addCssClass(this, "showing");
        }

        void setActiveIndicator(int activeIndex) {
            configInficator();
            Element eTemp = null;
            for (int i = 0; i < getWidgetCount(); i++) {
                eTemp = Element.as(getWidget(i).getElement());
                if (eTemp == null)
                    continue;
                eTemp.removeClassName("active");
                eTemp.removeClassName("active-before");
                eTemp.removeClassName("active-after");
                if (indicatorType == IndicatorType.dot)
                    eTemp.setInnerHTML(dotInactive);
                if (indicatorType == IndicatorType.square)
                    eTemp.setInnerHTML(squareInactive);
                if (indicatorType == IndicatorType.numeric)
                    eTemp.setInnerHTML((i + 1) + "");
                if (indicatorType == IndicatorType.slidetitles && (i + 1) >= (activeIndex - 1)
                        && (i + 1) <= (activeIndex + 1)) {
                    eTemp.setInnerHTML(getSlide(i).getSlideTitle());
                    eTemp.getStyle().clearDisplay();
                } else if (indicatorType == IndicatorType.slidetitles) {
                    eTemp.getStyle().setDisplay(Display.NONE);
                }
                if (activeIndex == (i + 1)) {
                    eTemp.addClassName("active");
                    if (indicatorType == IndicatorType.dot)
                        eTemp.setInnerHTML(dotActive);
                    if (indicatorType == IndicatorType.square)
                        eTemp.setInnerHTML(squareActive);
                } else if ((i + 1) == (activeIndex - 1)) {
                    if (indicatorType == IndicatorType.slidetitles)
                        eTemp.addClassName("active-before");
                } else if ((i + 1) == (activeIndex + 1)) {
                    if (indicatorType == IndicatorType.slidetitles)
                        eTemp.addClassName("active-after");
                }
            }
        }

    }

    class IndicatorClickListener implements EventListener {

        private final int activeSlide;

        IndicatorClickListener(int activeSlide) {
            this.activeSlide = activeSlide;
        }

        @Override
        public void onBrowserEvent(Event event) {
            if (event.getTypeInt() == Event.ONCLICK) {
                if (activeSlide > getCurrentSlide()) {
                    setCurrentSlide(getCurrentSlide() + 1);
                } else if (activeSlide < getCurrentSlide()) {
                    setCurrentSlide(getCurrentSlide() - 1);
                }
                //event.preventDefault();//stopPropagation();
                //Utils.Console("IndicatorClickListener: " + (activeSlide) + " - " + getCurrentSlide());
                //setCurrentSlide(activeSlide);
            }
        }

    }

}