com.google.livingstories.client.ui.Filmstrip.java Source code

Java tutorial

Introduction

Here is the source code for com.google.livingstories.client.ui.Filmstrip.java

Source

/**
 * Copyright 2010 Google Inc.
 *
 * 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.google.livingstories.client.ui;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Event.NativePreviewEvent;
import com.google.gwt.user.client.Event.NativePreviewHandler;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.livingstories.client.AssetContentItem;
import com.google.livingstories.client.util.SquareImage;

import java.util.List;

/**
 * Widget that shows a strip of smaller widgets, and scrolls between them.
 * Useful for image thumbnails, etc.
 */
public class Filmstrip extends Composite {
    private static final String ENABLED_PREVIOUS_ARROW = "/images/arrow-left.gif";
    private static final String DISABLED_PREVIOUS_ARROW = "/images/arrow-left-disabled.gif";
    private static final String ENABLED_NEXT_ARROW = "/images/arrow-right.gif";
    private static final String DISABLED_NEXT_ARROW = "/images/arrow-right-disabled.gif";
    private static final String FILMSTRIP_STYLE = "filmstrip";
    private static final int VISIBLE_ITEM_COUNT = 5;
    private static final int ITEM_SIZE = 100;
    private static final int ITEM_SPACING = 2;
    private static final int CURRENT_ITEM_BORDER = 4;
    private static final int TOTAL_ITEM_WIDTH = ITEM_SIZE + ITEM_SPACING;

    private HorizontalPanel filmstrip;
    private Image previousArrow;
    private Image nextArrow;
    private HorizontalPanel contentPanel;
    private SimplePanel clippingPane;

    private int currentIndex = 0;

    private HandlerRegistration nativePreviewHandlerRegistration;

    public Filmstrip() {
        previousArrow = new Image(ENABLED_PREVIOUS_ARROW);
        previousArrow.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent e) {
                int newIndex = currentIndex - 1;
                if (newIndex >= 0) {
                    navigate(newIndex);
                }
            }
        });
        nextArrow = new Image(ENABLED_NEXT_ARROW);
        nextArrow.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent e) {
                int newIndex = currentIndex + 1;
                if (newIndex < contentPanel.getWidgetCount()) {
                    navigate(newIndex);
                }
            }
        });

        contentPanel = new HorizontalPanel();
        contentPanel.setSpacing(ITEM_SPACING);
        contentPanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
        DOM.setStyleAttribute(contentPanel.getElement(), "position", "relative");

        clippingPane = new SimplePanel();
        DOM.setStyleAttribute(clippingPane.getElement(), "overflow", "hidden");
        clippingPane.add(contentPanel);

        filmstrip = new HorizontalPanel();
        filmstrip.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
        filmstrip.setStylePrimaryName(FILMSTRIP_STYLE);
        filmstrip.add(previousArrow);
        filmstrip.add(clippingPane);
        filmstrip.add(nextArrow);

        initWidget(filmstrip);
    }

    public void loadImages(List<AssetContentItem> images) {
        contentPanel.clear();
        int i = 0;
        for (AssetContentItem image : images) {
            final SquareImage imageWidget = new SquareImage(image.getPreviewUrl(), ITEM_SIZE);
            final int imageIndex = i++;
            imageWidget.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    navigate(imageIndex);
                }
            });
            contentPanel.add(imageWidget);
        }
        clippingPane.setWidth(TOTAL_ITEM_WIDTH * getVisibleItemRange(0).getExtent() + CURRENT_ITEM_BORDER + "px");
    }

    public int getCurrentIndex() {
        return currentIndex;
    }

    public void startHandlingKeys() {
        stopHandlingKeys();
        nativePreviewHandlerRegistration = Event.addNativePreviewHandler(new NativePreviewHandler() {
            @Override
            public void onPreviewNativeEvent(NativePreviewEvent event) {
                Event nativeEvent = Event.as(event.getNativeEvent());
                if (nativeEvent.getTypeInt() == Event.ONKEYDOWN) {
                    switch (nativeEvent.getKeyCode()) {
                    case KeyCodes.KEY_HOME:
                        navigate(0);
                        break;
                    case KeyCodes.KEY_LEFT:
                        if (currentIndex > 0) {
                            navigate(currentIndex - 1);
                        }
                        break;
                    case KeyCodes.KEY_RIGHT:
                        if (currentIndex < contentPanel.getWidgetCount() - 1) {
                            navigate(currentIndex + 1);
                        }
                        break;
                    case KeyCodes.KEY_END:
                        navigate(contentPanel.getWidgetCount() - 1);
                        break;
                    }
                    // we don't want any sort of keyboard scrolling while the slideshow is up, regardless
                    // of the exact keycode. The combination of preventDefault() and stopPropagation() is
                    // necessary in both FF and IE. As a historical note, one can do without the
                    // stopPropagation() in FF if one also does a preventDefault on keypress,
                    // but this is little more than a curiosity.
                    nativeEvent.preventDefault();
                    nativeEvent.stopPropagation();
                }
            }
        });
    }

    public void stopHandlingKeys() {
        // stop previewing page events
        if (nativePreviewHandlerRegistration != null) {
            nativePreviewHandlerRegistration.removeHandler();
            nativePreviewHandlerRegistration = null;
        }
    }

    public void onNavigate(int newIndex) {
    }

    public void navigate(int newIndex) {
        if (currentIndex >= 0) {
            Widget currentWidget = contentPanel.getWidget(currentIndex);
            currentWidget.removeStyleName("filmstrip-current");
        }

        Widget newWidget = contentPanel.getWidget(newIndex);
        newWidget.addStyleName("filmstrip-current");

        Range visibleItemRange = getVisibleItemRange(newIndex);

        clippingPane.setWidth(TOTAL_ITEM_WIDTH * (visibleItemRange.getExtent()) + CURRENT_ITEM_BORDER + "px");

        StyleEffect slide = new StyleEffect(contentPanel, "left", -TOTAL_ITEM_WIDTH * visibleItemRange.start);
        slide.run(500);

        currentIndex = newIndex;

        if (currentIndex == 0) {
            previousArrow.setUrl(DISABLED_PREVIOUS_ARROW);
        } else {
            previousArrow.setUrl(ENABLED_PREVIOUS_ARROW);
        }

        if (currentIndex == contentPanel.getWidgetCount() - 1) {
            nextArrow.setUrl(DISABLED_NEXT_ARROW);
        } else {
            nextArrow.setUrl(ENABLED_NEXT_ARROW);
        }

        onNavigate(currentIndex);
    }

    private Range getVisibleItemRange(int itemIndex) {
        int start = itemIndex;
        int end = itemIndex + 1;
        while (start > 0 || end < contentPanel.getWidgetCount()) {
            if (start > 0) {
                start--;
            }
            if (end < contentPanel.getWidgetCount()) {
                end++;
            }
            if (end - start == VISIBLE_ITEM_COUNT) {
                break;
            }
        }
        return new Range(start, end);
    }

    private class Range {
        public final int start;
        public final int end;

        public Range(int start, int end) {
            this.start = start;
            this.end = end;
        }

        public int getExtent() {
            return end - start;
        }
    }
}