fr.fg.client.openjwt.ui.JSScrollPane.java Source code

Java tutorial

Introduction

Here is the source code for fr.fg.client.openjwt.ui.JSScrollPane.java

Source

/*
Copyright 2010 Jeremie Gottero
    
This file is part of Fallen Galaxy.
    
Fallen Galaxy is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
Fallen Galaxy is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
    
You should have received a copy of the GNU Affero General Public License
along with Fallen Galaxy. If not, see <http://www.gnu.org/licenses/>.
*/

package fr.fg.client.openjwt.ui;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventPreview;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.MouseListener;
import com.google.gwt.user.client.ui.MouseWheelListener;
import com.google.gwt.user.client.ui.MouseWheelListenerCollection;
import com.google.gwt.user.client.ui.SourcesMouseWheelEvents;
import com.google.gwt.user.client.ui.Widget;

import fr.fg.client.openjwt.OpenJWT;

public class JSScrollPane extends JSComponent
        implements ClickListener, SourcesMouseWheelEvents, EventPreview, MouseListener {
    // ------------------------------------------------------- CONSTANTES -- //

    public final static String UI_CLASS_ID = "ScrollPane";

    public final static int SCROLLBAR_AS_NEEDED = 0, SCROLLBAR_NEVER = 1, SCROLLBAR_ALWAYS = 2;

    /**
     * Vitesse de dfilement du scrolling, en pixels.
     */
    public final static int SCROLL_SPEED = 50;

    static {
        setDefaultProperty(UI_CLASS_ID, OpenJWT.ELEMENT, "div");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.INNER_HTML, "");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.DEFAULT_WIDTH, "-1");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.DEFAULT_HEIGHT, "-1");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.LINE_HEIGHT, "false");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.HORIZONTAL_MARGIN, "0");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.VERTICAL_MARGIN, "0");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.CSS_CLASS, "scrollPane");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.SCROLL_BUBBLE_WIDTH, "-1");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.SCROLL_BUBBLE_MIN_HEIGHT, "-1");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.SCROLL_UP_BUTTON_WIDTH, "-1");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.SCROLL_UP_BUTTON_HEIGHT, "-1");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.SCROLL_DOWN_BUTTON_WIDTH, "-1");
        setDefaultProperty(UI_CLASS_ID, OpenJWT.SCROLL_DOWN_BUTTON_HEIGHT, "-1");
    }

    // -------------------------------------------------------- ATTRIBUTS -- //

    private MouseWheelListenerCollection mouseWheelListeners;

    private JSButton scrollUpButton, scrollDownButton, scrollBubble;

    private AbsolutePanel scrollTrack, viewPort, scrollBar;

    private int scrollBarPolicy;

    private double top;

    private double bubbleHeight;

    private boolean dragging;

    private int mouseY;

    private int viewHeight;

    // ---------------------------------------------------- CONSTRUCTEURS -- //

    public JSScrollPane() {
        super(UI_CLASS_ID);

        this.scrollBarPolicy = SCROLLBAR_AS_NEEDED;
        this.dragging = false;
        this.viewHeight = -1;

        // Boutons pour scroller
        scrollUpButton = new JSButton();
        String style = scrollUpButton.getStylePrimaryName();
        scrollUpButton.setStylePrimaryName("scrollUp");
        scrollUpButton.addStyleName(style);
        scrollUpButton.addClickListener(this);
        scrollUpButton.setPixelSize(getUIPropertyInt(OpenJWT.SCROLL_UP_BUTTON_WIDTH),
                getUIPropertyInt(OpenJWT.SCROLL_UP_BUTTON_HEIGHT));

        scrollDownButton = new JSButton();
        scrollDownButton.setStylePrimaryName("scrollDown");
        scrollDownButton.addStyleName(style);
        scrollDownButton.addClickListener(this);
        scrollDownButton.setPixelSize(getUIPropertyInt(OpenJWT.SCROLL_DOWN_BUTTON_WIDTH),
                getUIPropertyInt(OpenJWT.SCROLL_DOWN_BUTTON_HEIGHT));

        // Scroll bubble
        scrollBubble = new JSButton();
        scrollBubble.setStylePrimaryName("scrollBubble");
        scrollBubble.addStyleName(style);
        scrollBubble.setPixelWidth(getUIPropertyInt(OpenJWT.SCROLL_BUBBLE_WIDTH));
        scrollBubble.addMouseListener(this);

        // Scroll track
        scrollTrack = new AbsolutePanel();
        scrollTrack.getElement().setAttribute("unselectable", "on");
        scrollTrack.addStyleName("scrollTrack");
        scrollTrack.add(scrollBubble, 0, 0);

        // Scroll bar
        scrollBar = new AbsolutePanel();
        scrollBar.getElement().setAttribute("unselectable", "on");
        scrollBar.addStyleName("scrollBar");
        scrollBar.add(scrollTrack, -1, -1);
        scrollBar.add(scrollUpButton, -1, -1);
        scrollBar.add(scrollDownButton, -1, -1);

        viewPort = new AbsolutePanel();
        viewPort.addStyleName("viewport");
        scrollBar.getElement().setAttribute("unselectable", "on");

        add(viewPort, -1, -1);
        add(scrollBar, -1, -1);

        sinkEvents(Event.ONMOUSEWHEEL);
    }

    // --------------------------------------------------------- METHODES -- //

    public Widget getView() {
        return viewPort.getWidgetCount() > 0 ? viewPort.getWidget(0) : null;
    }

    public void setView(Widget widget) {
        if (viewPort.getWidgetCount() > 0)
            viewPort.remove(0);
        if (widget != null)
            viewPort.add(widget, 0, 0);
        if (isAttached())
            update();
    }

    public double getScroll() {
        double scrollableHeight = viewHeight - getPixelHeight();
        return scrollableHeight > 0 ? -this.top / scrollableHeight : 0;
    }

    public double getScrollOffset() {
        return top;
    }

    public void setScrollOffset(double offset) {
        if (offset < 0)
            offset = 0;

        this.top = offset;
        updateBubble();
    }

    public void scrollUp(double dy) {
        if (dy < 0)
            dy = SCROLL_SPEED;

        this.top += dy;
        updateBubble();
    }

    public void scrollDown(double dy) {
        if (dy < 0)
            dy = SCROLL_SPEED;

        this.top -= dy;
        updateBubble();
    }

    public int getScrollBarPolicy() {
        return scrollBarPolicy;
    }

    public void setScrollBarPolicy(int scrollBarPolicy) {
        this.scrollBarPolicy = scrollBarPolicy;
        updateBubble();
    }

    public void onLoad() {
        super.onLoad();
        update();
    }

    public void onClick(Widget sender) {
        JSComponent component = (JSComponent) sender;

        if (component.getId().equals(scrollUpButton.getId())) {
            scrollUp(SCROLL_SPEED);
        } else if (component.getId().equals(scrollDownButton.getId())) {
            scrollDown(SCROLL_SPEED);
        }
    }

    public void setPixelWidth(int width) {
        super.setPixelWidth(width);
        if (isAttached())
            update();
    }

    public void setPixelHeight(int height) {
        super.setPixelHeight(height);
        if (isAttached())
            update();
    }

    public boolean onEventPreview(Event event) {
        switch (DOM.eventGetType(event)) {
        case Event.ONMOUSEMOVE:
            if (dragging) {
                int mouseY = DOM.eventGetClientY(event);

                // Dplacement en fonction de la hauteur de l'ascenseur
                int trackHeight = getPixelHeight() - scrollUpButton.getPixelHeight()
                        - scrollDownButton.getPixelHeight();
                double factor = trackHeight / this.bubbleHeight;

                if (mouseY > this.mouseY)
                    scrollDown((mouseY - this.mouseY) * factor);
                else if (mouseY < this.mouseY)
                    scrollUp((this.mouseY - mouseY) * factor);

                this.mouseY = mouseY;
            }
            break;
        case Event.ONMOUSEUP:
            if (dragging) {
                dragging = false;
                DOM.removeEventPreview(this);
            }
            break;
        }

        return true;
    }

    @Override
    public void onBrowserEvent(Event event) {
        super.onBrowserEvent(event);

        switch (DOM.eventGetType(event)) {
        case Event.ONMOUSEWHEEL:
            update();
            int wheelDelta = DOM.eventGetMouseWheelVelocityY(event);

            if (wheelDelta < 0)
                this.scrollUp(SCROLL_SPEED);
            else
                this.scrollDown(SCROLL_SPEED);

            if (mouseWheelListeners != null)
                mouseWheelListeners.fireMouseWheelEvent(this, event);

            DOM.eventPreventDefault(event);
            DOM.eventCancelBubble(event, true);
            break;
        case Event.ONMOUSEDOWN:
        case Event.ONMOUSEMOVE:
        case Event.ONMOUSEUP:
            DOM.eventPreventDefault(event);
            DOM.eventCancelBubble(event, true);
            break;
        }
    }

    public void addMouseWheelListener(MouseWheelListener listener) {
        if (mouseWheelListeners == null)
            mouseWheelListeners = new MouseWheelListenerCollection();
        mouseWheelListeners.add(listener);
    }

    public void removeMouseWheelListener(MouseWheelListener listener) {
        if (mouseWheelListeners != null)
            mouseWheelListeners.remove(listener);
    }

    public void onMouseDown(Widget sender, int x, int y) {
        if (!dragging) {
            DOM.addEventPreview(this);
            this.mouseY = y + DOM.getAbsoluteTop(scrollBubble.getElement());
            this.dragging = true;
            update();
        }
    }

    public void onMouseEnter(Widget sender) {
        // Sans effet
    }

    public void onMouseLeave(Widget sender) {
        // Sans effet
    }

    public void onMouseMove(Widget sender, int x, int y) {
        // Sans effet
    }

    public void onMouseUp(Widget sender, int x, int y) {
        // Sans effet
    }

    public void update() {
        int scrollUpButtonWidth = scrollUpButton.getPixelWidth();
        int scrollBubbleWidth = scrollBubble.getPixelWidth();
        int scrollDownButtonWidth = scrollDownButton.getPixelWidth();

        viewHeight = getViewHeight();
        scrollTrack.setWidth(scrollBubbleWidth + "px");

        int maxWidth = Math.max(Math.max(scrollUpButtonWidth, scrollBubbleWidth), scrollDownButtonWidth);
        int viewWidth = Math.max(0, getPixelWidth() - maxWidth);

        scrollBar.setSize(maxWidth + "px", getPixelHeight() + "px");
        viewPort.setSize(viewWidth + "px", getPixelHeight() + "px");

        Widget view = getView();
        if (view != null)
            view.setWidth(viewWidth + "px");

        updateBubble();
    }

    // ------------------------------------------------- METHODES PRIVEES -- //

    protected void updateBubble() {
        // Hauteur des lments du scrollpane
        int scrollPaneHeight = getPixelHeight();
        int trackHeight = scrollPaneHeight - scrollUpButton.getPixelHeight() - scrollDownButton.getPixelHeight();
        if (trackHeight < 0)
            trackHeight = 0;

        Widget view = getView();
        if (viewHeight == -1)
            viewHeight = getViewHeight();

        // Positionne l'ascenseur en fonction de la position des lments du
        // scrollpane
        this.bubbleHeight = scrollPaneHeight >= viewHeight ? trackHeight
                : trackHeight * scrollPaneHeight / (double) viewHeight;
        int bubbleMinHeight = getUIPropertyInt(OpenJWT.SCROLL_BUBBLE_MIN_HEIGHT);

        if (this.top < -viewHeight + scrollPaneHeight)
            this.top = -viewHeight + scrollPaneHeight;
        if (this.top > 0)
            this.top = 0;

        if (view != null)
            viewPort.setWidgetPosition(getView(), 0, (int) top);

        if (bubbleHeight < bubbleMinHeight) {
            int offset = (bubbleMinHeight - (int) Math.ceil(bubbleHeight)) / 2;
            scrollBubble.setPixelHeight(bubbleMinHeight);

            int position = (int) Math.floor(-this.top * (viewHeight > 0 ? trackHeight / (double) viewHeight : 1))
                    - offset;
            if (position < 0)
                position = 0;
            if (position + bubbleMinHeight > trackHeight)
                position = trackHeight - bubbleMinHeight;

            scrollTrack.setWidgetPosition(scrollBubble, 0, position);
        } else {
            scrollBubble.setPixelHeight((int) Math.ceil(bubbleHeight));
            scrollTrack.setWidgetPosition(scrollBubble, 0,
                    (int) Math.floor(-this.top * (viewHeight > 0 ? trackHeight / (double) viewHeight : 1)));
        }

        // Affiche ou masque la scrollbar
        switch (scrollBarPolicy) {
        case SCROLLBAR_AS_NEEDED:
            scrollBar.setVisible(scrollPaneHeight < viewHeight);
            break;
        case SCROLLBAR_NEVER:
            scrollBar.setVisible(false);
            break;
        case SCROLLBAR_ALWAYS:
            scrollBar.setVisible(true);
            break;
        }

        // Recalcule l'espace disponible pour la vue
        int viewWidth = getPixelWidth() - (scrollBar.isVisible() ? scrollUpButton.getPixelWidth() : 0);
        viewPort.setWidth(viewWidth + "px");
        if (view != null)
            view.setWidth(viewWidth + "px");
    }

    private int getViewHeight() {
        Widget view = getView();

        if (view != null) {
            if (view instanceof JSComponent)
                return ((JSComponent) view).getPixelHeight();
            else
                return view.getOffsetHeight();
        } else {
            return 0;
        }
    }
}