com.googlecode.mgwt.ui.client.widget.carousel.Carousel.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.mgwt.ui.client.widget.carousel.Carousel.java

Source

/*
 * Copyright 2012 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.carousel;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget;
import com.google.web.bindery.event.shared.HandlerRegistration;

import com.googlecode.mgwt.collection.shared.LightArrayInt;
import com.googlecode.mgwt.dom.client.event.orientation.OrientationChangeEvent;
import com.googlecode.mgwt.dom.client.event.orientation.OrientationChangeHandler;
import com.googlecode.mgwt.ui.client.MGWT;
import com.googlecode.mgwt.ui.client.widget.carousel.CarouselAppearance.CarouselCss;
import com.googlecode.mgwt.ui.client.widget.panel.flex.FlexPanel;
import com.googlecode.mgwt.ui.client.widget.panel.flex.FlexPropertyHelper.Justification;
import com.googlecode.mgwt.ui.client.widget.panel.flex.FlexPropertyHelper.Orientation;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollEndEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollMoveEvent;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollPanel;
import com.googlecode.mgwt.ui.client.widget.panel.scroll.ScrollRefreshEvent;
import com.googlecode.mgwt.ui.client.widget.touch.TouchWidget;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

/**
 * A carousel renders its children in a row.
 * A user can select a different child by swiping between them.
 *
 */
public class Carousel extends Composite implements HasWidgets, HasSelectionHandlers<Integer> {

    private static class CarouselIndicatorContainer extends Composite {
        private FlexPanel main;
        private final CarouselCss css;
        private ArrayList<CarouselIndicator> indicators;
        private int selectedIndex;

        public CarouselIndicatorContainer(CarouselCss css, int numberOfPages) {
            if (numberOfPages < 0) {
                throw new IllegalArgumentException();
            }
            this.css = css;
            main = new FlexPanel();
            initWidget(main);
            main.setOrientation(Orientation.HORIZONTAL);
            main.setJustification(Justification.CENTER);

            main.addStyleName(this.css.indicatorMain());

            FlexPanel container = new FlexPanel();
            container.addStyleName(this.css.indicatorContainer());
            container.setOrientation(Orientation.HORIZONTAL);
            main.add(container);

            indicators = new ArrayList<Carousel.CarouselIndicator>(numberOfPages);
            selectedIndex = 0;

            for (int i = 0; i < numberOfPages; i++) {
                CarouselIndicator indicator = new CarouselIndicator(css);
                indicators.add(indicator);
                container.add(indicator);
            }

            setSelectedIndex(selectedIndex);
        }

        public void setSelectedIndex(int index) {
            if (indicators.isEmpty()) {
                selectedIndex = -1;
                return;
            }
            if (selectedIndex != -1) {
                indicators.get(selectedIndex).setActive(false);
            }
            selectedIndex = index;
            if (selectedIndex != -1) {
                indicators.get(selectedIndex).setActive(true);

            }
        }
    }

    private static class CarouselIndicator extends TouchWidget {
        private final CarouselCss css;

        public CarouselIndicator(CarouselCss css) {
            this.css = css;
            setElement(Document.get().createDivElement());

            addStyleName(css.indicator());

        }

        public void setActive(boolean active) {
            if (active) {
                addStyleName(css.indicatorActive());
            } else {
                removeStyleName(css.indicatorActive());
            }
        }
    }

    @UiField
    public FlexPanel main;
    @UiField
    public ScrollPanel scrollPanel;
    @UiField
    public FlowPanel container;
    private CarouselIndicatorContainer carouselIndicatorContainer;
    private boolean isVisibleCarouselIndicator = true;

    private int currentPage;

    private Map<Widget, Widget> childToHolder;
    private HandlerRegistration refreshHandler;

    private static final CarouselImpl IMPL = GWT.create(CarouselImpl.class);

    private static final CarouselAppearance DEFAULT_APPEARANCE = GWT.create(CarouselAppearance.class);
    private final CarouselAppearance appearance;
    private boolean hasScollData;

    public Carousel() {
        this(DEFAULT_APPEARANCE);
    }

    public Carousel(CarouselAppearance appearance) {

        this.appearance = appearance;
        initWidget(this.appearance.carouselBinder().createAndBindUi(this));
        childToHolder = new HashMap<Widget, Widget>();

        scrollPanel.setSnap(true);
        scrollPanel.setSnapThreshold(50);
        scrollPanel.setMomentum(false);
        scrollPanel.setShowVerticalScrollBar(false);
        scrollPanel.setShowHorizontalScrollBar(false);
        scrollPanel.setScrollingEnabledY(true);
        scrollPanel.setAutoHandleResize(false);

        currentPage = 0;

        scrollPanel.addScrollEndHandler(new ScrollEndEvent.Handler() {

            @Override
            public void onScrollEnd(ScrollEndEvent event) {
                int page = scrollPanel.getCurrentPageX();

                carouselIndicatorContainer.setSelectedIndex(page);
                currentPage = page;
                SelectionEvent.fire(Carousel.this, currentPage);

            }
        });

        scrollPanel.addScrollMoveHandler(new ScrollMoveEvent.Handler() {

            @Override
            public void onScrollMove(ScrollMoveEvent event) {
                TouchMoveEvent moveEvent = event.getEvent();
                moveEvent.stopPropagation();
                moveEvent.preventDefault();
            }
        });

        MGWT.addOrientationChangeHandler(new OrientationChangeHandler() {

            @Override
            public void onOrientationChanged(OrientationChangeEvent event) {
                refresh();
            }
        });

        addSelectionHandler(new SelectionHandler<Integer>() {

            @Override
            public void onSelection(SelectionEvent<Integer> event) {

                carouselIndicatorContainer.setSelectedIndex(currentPage);

            }
        });

        if (MGWT.getOsDetection().isDesktop()) {
            Window.addResizeHandler(new ResizeHandler() {

                @Override
                public void onResize(ResizeEvent event) {
                    refresh();
                }
            });
        }

    }

    @Override
    public void add(Widget w) {

        FlowPanel widgetHolder = new FlowPanel();
        widgetHolder.addStyleName(this.appearance.cssCarousel().carouselHolder());
        widgetHolder.add(w);

        childToHolder.put(w, widgetHolder);

        container.add(widgetHolder);

        IMPL.adjust(main, container);

    }

    @Override
    public void clear() {
        container.clear();
        childToHolder.clear();
    }

    @Override
    public Iterator<Widget> iterator() {
        Set<Widget> keySet = childToHolder.keySet();
        return keySet.iterator();
    }

    @Override
    public boolean remove(Widget w) {
        Widget holder = childToHolder.remove(w);
        if (holder != null) {
            return container.remove(holder);
        }
        return false;

    }

    @Override
    protected void onLoad() {
        refresh();
    }

    /**
     * refresh the carousel widget, this is necessary after changing child elements
     */
    public void refresh() {
        hasScollData = false;
        final int delay = MGWT.getOsDetection().isAndroid() ? 200 : 1;
        IMPL.adjust(main, container);
        // allow layout to happen..
        new Timer() {

            @Override
            public void run() {
                IMPL.adjust(main, container);

                scrollPanel.setScrollingEnabledX(true);
                scrollPanel.setScrollingEnabledY(false);

                scrollPanel.setShowVerticalScrollBar(false);
                scrollPanel.setShowHorizontalScrollBar(false);

                if (carouselIndicatorContainer != null) {
                    carouselIndicatorContainer.removeFromParent();

                }

                int widgetCount = container.getWidgetCount();

                carouselIndicatorContainer = new CarouselIndicatorContainer(appearance.cssCarousel(), widgetCount);

                if (isVisibleCarouselIndicator) {
                    main.add(carouselIndicatorContainer);
                }

                if (currentPage >= widgetCount) {
                    currentPage = widgetCount - 1;
                }

                carouselIndicatorContainer.setSelectedIndex(currentPage);

                refreshHandler = scrollPanel.addScrollRefreshHandler(new ScrollRefreshEvent.Handler() {

                    @Override
                    public void onScrollRefresh(ScrollRefreshEvent event) {
                        refreshHandler.removeHandler();
                        refreshHandler = null;
                        LightArrayInt pagesX = scrollPanel.getPagesX();
                        if (currentPage < 0) {
                            currentPage = 0;
                        } else if (currentPage >= pagesX.length()) {
                            currentPage = pagesX.length() - 1;
                        }
                        scrollPanel.scrollToPage(currentPage, 0, 0);
                        hasScollData = true;
                    }
                });
                scrollPanel.refresh();
            }

        }.schedule(delay);

    }

    public void setSelectedPage(int index) {
        setSelectedPage(index, true);
    }

    public void setSelectedPage(int index, boolean issueEvent) {
        if (isAttached() && hasScollData) {
            LightArrayInt pagesX = scrollPanel.getPagesX();
            if (index < 0 || index >= pagesX.length()) {
                throw new IllegalArgumentException("invalid value for index: " + index);
            }
            currentPage = index;
            scrollPanel.scrollToPage(index, 0, 300, issueEvent);
        } else {
            currentPage = index;
        }
    }

    public int getSelectedPage() {
        return currentPage;
    }

    @Override
    public com.google.gwt.event.shared.HandlerRegistration addSelectionHandler(SelectionHandler<Integer> handler) {
        return addHandler(handler, SelectionEvent.getType());
    }

    interface CarouselImpl {
        void adjust(Widget main, FlowPanel container);
    }

    // GWT rebinding
    @SuppressWarnings("unused")
    private static class CarouselImplSafari implements CarouselImpl {

        @Override
        public void adjust(Widget main, FlowPanel container) {
            int widgetCount = container.getWidgetCount();

            double scaleFactor = 100d / widgetCount;

            for (int i = 0; i < widgetCount; i++) {
                Widget w = container.getWidget(i);
                w.setWidth(scaleFactor + "%");
                w.getElement().getStyle().setLeft(i * scaleFactor, Unit.PCT);
            }

            container.setWidth((widgetCount * 100) + "%");
            container.getElement().getStyle().setHeight(main.getOffsetHeight(), Unit.PX);
        }

    }

    //GWT rebinding
    @SuppressWarnings("unused")
    private static class CarouselImplGecko implements CarouselImpl {

        @Override
        public void adjust(Widget main, FlowPanel container) {
            int widgetCount = container.getWidgetCount();
            int offsetWidth = main.getOffsetWidth();

            container.setWidth(widgetCount * offsetWidth + "px");

            for (int i = 0; i < widgetCount; i++) {
                container.getWidget(i).setWidth(offsetWidth + "px");
            }
        }
    }

    /**
     * Set if carousel indicator is displayed.
     */
    public void setShowCarouselIndicator(boolean isVisibleCarouselIndicator) {
        if (!isVisibleCarouselIndicator && carouselIndicatorContainer != null) {
            carouselIndicatorContainer.removeFromParent();
        }
        this.isVisibleCarouselIndicator = isVisibleCarouselIndicator;
    }

    public ScrollPanel getScrollPanel() {
        return scrollPanel;
    }

    @UiFactory
    public CarouselAppearance getAppearance() {
        return appearance;
    }
}