Package org.apache.isis.viewer.dnd.view.border

Source Code of org.apache.isis.viewer.dnd.view.border.ScrollBorder

/*
*  Licensed to the Apache Software Foundation (ASF) under one
*  or more contributor license agreements.  See the NOTICE file
*  distributed with this work for additional information
*  regarding copyright ownership.  The ASF licenses this file
*  to you 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 org.apache.isis.viewer.dnd.view.border;

import org.apache.isis.core.commons.debug.DebugBuilder;
import org.apache.isis.core.metamodel.spec.ActionType;
import org.apache.isis.viewer.dnd.drawing.Bounds;
import org.apache.isis.viewer.dnd.drawing.Canvas;
import org.apache.isis.viewer.dnd.drawing.Color;
import org.apache.isis.viewer.dnd.drawing.ColorsAndFonts;
import org.apache.isis.viewer.dnd.drawing.Location;
import org.apache.isis.viewer.dnd.drawing.Offset;
import org.apache.isis.viewer.dnd.drawing.Size;
import org.apache.isis.viewer.dnd.interaction.SimpleInternalDrag;
import org.apache.isis.viewer.dnd.view.Click;
import org.apache.isis.viewer.dnd.view.ContentDrag;
import org.apache.isis.viewer.dnd.view.DragEvent;
import org.apache.isis.viewer.dnd.view.DragStart;
import org.apache.isis.viewer.dnd.view.InternalDrag;
import org.apache.isis.viewer.dnd.view.Toolkit;
import org.apache.isis.viewer.dnd.view.UserActionSet;
import org.apache.isis.viewer.dnd.view.View;
import org.apache.isis.viewer.dnd.view.ViewAreaType;
import org.apache.isis.viewer.dnd.view.Workspace;
import org.apache.isis.viewer.dnd.view.base.AbstractViewDecorator;
import org.apache.isis.viewer.dnd.view.base.NullView;
import org.apache.isis.viewer.dnd.view.option.UserActionAbstract;

/**
* A scroll border provides a window on a larger view, providing scrollbars as a
* way of moving the visible part of that view around the actual visible viewing
* area. To achieve this the view is divided up into five main areas, not all of
* which are used. In the centre is the viewing area of the underlying view. At
* the bottom and to the right... At the top and to the left are headers that
*/
public class ScrollBorder extends AbstractViewDecorator {
    private static ScrollBarRender render;
    private static final int CENTER = 3;
    private static final int NORTH = 1;
    private static final int SOUTH = 5;
    private static final int CORNER = 0;
    private static final int SCROLLBAR_WIDTH = 16;
    private static final int WEST = 2;
    private static final int EAST = 4;

    public static void setRender(final ScrollBarRender render) {
        ScrollBorder.render = render;
    }

    private final ScrollBar horizontalScrollBar = new ScrollBar();
    private final ScrollBar verticalScrollBar = new ScrollBar();
    protected int bottom;
    protected int left;
    private View leftHeader;
    protected int right;
    private Size size = new Size();
    protected int top;
    private View topHeader;
    private int dragArea = CENTER;
    private int offsetToThumbEdge;

    public ScrollBorder(final View view) {
        this(view, new NullView(), new NullView());
    }

    /**
     * Note - the leftHeader, if it is specified, view must be the same height
     * as the content view and the rightHeader, if it is specified, must be the
     * same width.
     */
    public ScrollBorder(final View content, final View leftHeader, final View topHeader) {
        super(content);
        bottom = right = SCROLLBAR_WIDTH;
        horizontalScrollBar.setPostion(0);
        verticalScrollBar.setPostion(0);
        setLeftHeader(leftHeader);
        setTopHeader(topHeader);
    }

    public void setTopHeader(final View topHeader) {
        this.topHeader = topHeader;
        topHeader.setParent(getView());
        top = topHeader.getRequiredSize(new Size()).getHeight();
    }

    public void setLeftHeader(final View leftHeader) {
        this.leftHeader = leftHeader;
        leftHeader.setParent(getView());
        left = leftHeader.getRequiredSize(new Size()).getWidth();
    }

    private int adjust(final Click click) {
        return adjust(click.getLocation());
    }

    private int adjust(final ContentDrag drag) {
        return adjust(drag.getTargetLocation());
    }

    private int adjust(final Location location) {
        final Bounds contentArea = viewportArea();
        final Offset offset = offset();
        final int yOffset = offset.getDeltaY();
        final int xOffset = offset.getDeltaX();
        if (contentArea.contains(location)) {
            location.subtract(left, top);
            location.add(xOffset, yOffset);
            return CENTER;
        } else {
            final int x = location.getX();
            final int y = location.getY();

            if (x > contentArea.getX2() && y >= contentArea.getY() && y <= contentArea.getY2()) {
                // vertical scrollbar
                location.subtract(0, contentArea.getY());
                return EAST;
            } else if (y > contentArea.getY2() && x >= contentArea.getX() && x <= contentArea.getX2()) {
                // horzontal scrollbar
                location.subtract(contentArea.getX(), 0);
                return SOUTH;
            } else if (y < contentArea.getY() && x >= contentArea.getX() && x <= contentArea.getX2()) {
                // top border
                location.subtract(left, 0);
                location.add(xOffset, 0);
                return NORTH;
            } else if (x < contentArea.getX() && y >= contentArea.getY() && y <= contentArea.getY2()) {
                // left border
                location.subtract(0, top);
                location.add(0, yOffset);
                return WEST;
            } else {
                // ignore;
                location.setX(-1);
                location.setY(-1);
                return CORNER;
            }
        }

    }

    protected Bounds viewportArea() {
        return new Bounds(left, top, getSize().getWidth() - left - right, getSize().getHeight() - top - bottom);
    }

    @Override
    protected void debugDetails(final DebugBuilder debug) {
        super.debugDetails(debug);
        debug.append("\n           Top header: " + (topHeader == null ? "none" : topHeader.toString()));
        debug.append("\n           Left header: " + (leftHeader == null ? "none" : leftHeader.toString()));

        debug.append("\n           Vertical scrollbar ");
        debug.append("\n             offset " + top);
        debug.append("\n             position " + verticalScrollBar.getPosition());
        debug.append("\n             minimum " + verticalScrollBar.getMinimum());
        debug.append("\n             maximum " + verticalScrollBar.getMaximum());
        debug.append("\n             visible amount " + verticalScrollBar.getVisibleAmount());

        debug.append("\n           Horizontal scrollbar ");
        debug.append("\n             offset " + left);
        debug.append("\n             position " + horizontalScrollBar.getPosition());
        debug.append("\n             minimum " + horizontalScrollBar.getMinimum());
        debug.append("\n             maximum " + horizontalScrollBar.getMaximum());
        debug.append("\n             visible amount " + horizontalScrollBar.getVisibleAmount());
        debug.append("\n           Viewport area " + viewportArea());
        debug.appendln("\n           Offset " + offset());
    }

    @Override
    public void drag(final InternalDrag drag) {
        switch (dragArea) {
        case NORTH:
            drag.getLocation().subtract(offset().getDeltaX(), top);
            topHeader.drag(drag);
            break;

        case WEST:
            drag.getLocation().subtract(left, offset().getDeltaY());
            leftHeader.drag(drag);
            break;

        case CENTER:
            drag.getLocation().subtract(offset());
            wrappedView.drag(drag);
            break;

        case SOUTH:
            final int x = drag.getLocation().getX() - left;
            horizontalScrollBar.setPostion(x - offsetToThumbEdge);
            markDamaged();
            break;

        case EAST:
            final int y = drag.getLocation().getY() - top;
            verticalScrollBar.setPostion(y - offsetToThumbEdge);
            markDamaged();
            break;

        default:
            return;
        }
    }

    @Override
    public DragEvent dragStart(final DragStart drag) {
        final int area = adjust(drag);
        dragArea = area;
        switch (dragArea) {
        case NORTH:
            return topHeader.dragStart(drag);

        case WEST:
            return leftHeader.dragStart(drag);

        case CENTER:
            return wrappedView.dragStart(drag);

        case SOUTH:
            return dragStartSouth(drag);

        case EAST:
            return dragStartEast(drag);

        default:
            return null;
        }
    }

    @Override
    public void dragCancel(final InternalDrag drag) {
        switch (dragArea) {
        case NORTH:
            drag.getLocation().subtract(offset().getDeltaX(), top);
            topHeader.dragCancel(drag);
            break;

        case WEST:
            drag.getLocation().subtract(left, offset().getDeltaY());
            leftHeader.dragCancel(drag);
            break;

        case CENTER:
            drag.getLocation().subtract(offset());
            wrappedView.dragCancel(drag);
            break;
        }
    }

    @Override
    public void dragTo(final InternalDrag drag) {
        switch (dragArea) {
        case NORTH:
            drag.getLocation().subtract(offset().getDeltaX(), top);
            topHeader.dragTo(drag);
            break;

        case WEST:
            drag.getLocation().subtract(left, offset().getDeltaY());
            leftHeader.dragTo(drag);
            break;

        case CENTER:
            drag.getLocation().subtract(offset());
            wrappedView.dragTo(drag);
            break;

        case SOUTH:
        case EAST:
        default:
            // ignore

        }
    }

    @Override
    public View dragFrom(final Location location) {
        adjust(location);
        switch (dragArea) {
        case NORTH:
            return topHeader.dragFrom(location);

        case WEST:
            return leftHeader.dragFrom(location);

        case CENTER:
            return wrappedView.dragFrom(location);
        }

        return null;
    }

    @Override
    public void dragIn(final ContentDrag drag) {
        adjust(drag);
        switch (dragArea) {
        case NORTH:
            topHeader.dragIn(drag);
            break;

        case WEST:
            leftHeader.dragIn(drag);
            break;

        case CENTER:
            wrappedView.dragIn(drag);
            break;

        case SOUTH:
        case EAST:
        default:
            System.out.println(this + " ignored");

            // ignore
        }
    }

    @Override
    public void dragOut(final ContentDrag drag) {
        adjust(drag);
        switch (dragArea) {
        case NORTH:
            topHeader.dragOut(drag);
            break;

        case WEST:
            leftHeader.dragOut(drag);
            break;

        case CENTER:
            wrappedView.dragOut(drag);
            break;

        case SOUTH:
        case EAST:
        default:
            // ignore
        }
    }

    private DragEvent dragStartEast(final DragStart drag) {
        final Location location = drag.getLocation();
        final int y = location.getY();
        if (verticalScrollBar.isOnThumb(y)) {
            // offset is the distance from the left/top of the thumb to the
            // pointer
            offsetToThumbEdge = y - verticalScrollBar.getPosition();
            return new SimpleInternalDrag(this, new Offset(super.getAbsoluteLocation()));
        } else {
            return null;
        }
    }

    private DragEvent dragStartSouth(final DragStart drag) {
        final Location location = drag.getLocation();
        final int x = location.getX();
        if (horizontalScrollBar.isOnThumb(x)) {
            offsetToThumbEdge = x - horizontalScrollBar.getPosition();
            return new SimpleInternalDrag(this, new Offset(super.getAbsoluteLocation()));
        } else {
            return null;
        }
    }

    private int adjust(final DragStart drag) {
        return adjust(drag.getLocation());
    }

    @Override
    public void draw(final Canvas canvas) {
        final Bounds contents = viewportArea();
        final Offset offset = offset();
        final int x = offset.getDeltaX();
        final int y = offset.getDeltaY();

        final int contentWidth = contents.getWidth();
        final int contentHeight = contents.getHeight();

        final Canvas headerCanvasLeft = canvas.createSubcanvas(0, top, left, contentHeight);
        headerCanvasLeft.offset(0, -y);
        leftHeader.draw(headerCanvasLeft);

        final Canvas headerCanvasRight = canvas.createSubcanvas(left, 0, contentWidth, top);
        headerCanvasRight.offset(-x, 0);
        topHeader.draw(headerCanvasRight);

        final Color thumbColor = Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY2);
        drawVerticalScrollBar(canvas, contentWidth, contentHeight, thumbColor);
        drawHorizontalScrollBar(canvas, contentWidth, contentHeight, thumbColor);

        final Canvas contentCanvas = canvas.createSubcanvas(left, top, contentWidth, contentHeight);
        contentCanvas.offset(-x, -y);

        if (Toolkit.debug) {
            canvas.drawRectangle(contents.getX(), contents.getY(), contents.getWidth(), contents.getHeight(), Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BOUNDS_DRAW));
        }

        // drawContent(canvas, contentWidth, contentHeight);
        wrappedView.draw(contentCanvas);

        if (Toolkit.debug) {
            final Size size = getSize();
            canvas.drawRectangle(0, 0, size.getWidth(), size.getHeight(), Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BOUNDS_VIEW));
            canvas.drawLine(0, size.getHeight() / 2, size.getWidth() - 1, size.getHeight() / 2, Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BOUNDS_VIEW));
            canvas.drawLine(0, getBaseline(), size.getWidth() - 1, getBaseline(), Toolkit.getColor(ColorsAndFonts.COLOR_DEBUG_BASELINE));
        }

    }

    // TODO merge these two methods
    private void drawVerticalScrollBar(final Canvas canvas, final int contentWidth, final int contentHeight, final Color color) {
        final int verticalVisibleAmount = verticalScrollBar.getVisibleAmount();
        final int verticalScrollPosition = verticalScrollBar.getPosition();
        if (right > 0 && (verticalScrollPosition > top || verticalVisibleAmount < contentHeight)) {
            final int x = contentWidth + left;
            render.draw(canvas, false, x, top, SCROLLBAR_WIDTH, contentHeight, verticalScrollPosition, verticalVisibleAmount);
            /*
             * canvas.drawSolidRectangle(x + 1, top, SCROLLBAR_WIDTH - 1,
             * contentHeight,
             * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3));
             * canvas.drawSolidRectangle(x + 1, top + verticalScrollPosition,
             * SCROLLBAR_WIDTH - 2, verticalVisibleAmount, color);
             * canvas.drawRectangle(x, top, SCROLLBAR_WIDTH, contentHeight,
             * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2));
             * canvas.drawRectangle(x + 1, top + verticalScrollPosition,
             * SCROLLBAR_WIDTH - 2, verticalVisibleAmount, Toolkit
             * .getColor(ColorsAndFonts.COLOR_SECONDARY1));
             *
             * DrawingUtil.drawHatching(canvas, x + 3, top +
             * verticalScrollPosition + 4, SCROLLBAR_WIDTH - 6,
             * verticalVisibleAmount - 8,
             * Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY1),
             * Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY3));
             */
        }
    }

    private void drawHorizontalScrollBar(final Canvas canvas, final int contentWidth, final int contentHeight, final Color color) {
        final int horizontalScrollPosition = horizontalScrollBar.getPosition();
        final int horizontalVisibleAmount = horizontalScrollBar.getVisibleAmount();
        if (bottom > 0 && (horizontalScrollPosition > left || horizontalVisibleAmount < contentWidth)) {
            final int x = 0; // left + horizontalScrollPosition;
            final int y = contentHeight + top;
            render.draw(canvas, true, x, y, contentWidth, SCROLLBAR_WIDTH, horizontalScrollPosition, horizontalVisibleAmount);
            /*
             * canvas.drawSolidRectangle(left, y + 1, contentWidth,
             * SCROLLBAR_WIDTH - 1,
             * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY3));
             * canvas.drawSolidRectangle(x, y + 1, horizontalVisibleAmount,
             * SCROLLBAR_WIDTH - 2, color); canvas.drawRectangle(left, y,
             * contentWidth, SCROLLBAR_WIDTH,
             * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY2 ));
             * canvas.drawRectangle(x, y + 1, horizontalVisibleAmount,
             * SCROLLBAR_WIDTH - 2,
             * Toolkit.getColor(ColorsAndFonts.COLOR_SECONDARY1));
             *
             * DrawingUtil.drawHatching(canvas, x + 5, y + 3,
             * horizontalVisibleAmount - 10, SCROLLBAR_WIDTH - 6, Toolkit
             * .getColor(ColorsAndFonts.COLOR_PRIMARY1),
             * Toolkit.getColor(ColorsAndFonts.COLOR_PRIMARY3));
             */
        }
    }

    @Override
    public void firstClick(final Click click) {
        final int area = adjust(click);
        switch (area) {
        case NORTH:
            topHeader.firstClick(click);
            break;

        case WEST:
            leftHeader.firstClick(click);
            break;

        case CENTER:
            wrappedView.firstClick(click);
            break;

        case SOUTH:
            // TODO allow modified click to move thumb to the pointer, rather
            // than paging.
            horizontalScrollBar.firstClick(click.getLocation().getX(), click.button3());
            break;

        case EAST:
            verticalScrollBar.firstClick(click.getLocation().getY(), click.button3());
            break;

        default:
            break;
        }
    }

    @Override
    public Location getAbsoluteLocation() {
        final Location location = super.getAbsoluteLocation();
        location.subtract(offset());
        return location;
    }

    @Override
    public Bounds getBounds() {
        return new Bounds(getLocation(), getSize());
    }

    @Override
    public Size getRequiredSize(final Size maximumSize) {
        final Size size = wrappedView.getRequiredSize(new Size(maximumSize));
        if (size.getWidth() > maximumSize.getWidth()) {
            size.extendHeight(SCROLLBAR_WIDTH);
        }
        if (size.getHeight() > maximumSize.getHeight()) {
            size.extendWidth(SCROLLBAR_WIDTH);
        }
        size.extend(left, top);
        size.limitSize(maximumSize);
        return size;
    }

    @Override
    public Size getSize() {
        return new Size(size);
    }

    @Override
    public View identify(final Location location) {
        getViewManager().getSpy().addTrace(this, "mouse location within border", location);
        getViewManager().getSpy().addTrace(this, "non border area", viewportArea());

        final int area = adjust(location);
        switch (area) {
        case NORTH:
            return topHeader.identify(location);

        case WEST:
            return leftHeader.identify(location);

        case CENTER:
            return wrappedView.identify(location);

        case SOUTH:
            getViewManager().getSpy().addTrace(this, "over scroll bar area", viewportArea());
            return getView();

        case EAST:
            getViewManager().getSpy().addTrace(this, "over scroll bar area", viewportArea());
            return getView();

        default:
            return null;
        }
    }

    @Override
    public void limitBoundsWithin(final Size size) {
        super.limitBoundsWithin(size);
        verticalScrollBar.limit();
        horizontalScrollBar.limit();
    }

    @Override
    public void markDamaged(final Bounds bounds) {
        /*
         * TODO this only works for the main content area, not for the headers.
         * how do we figure out which area to adjust for?
         */
        final Offset offset = offset();
        bounds.translate(-offset.getDeltaX(), -offset.getDeltaY());
        bounds.translate(left, top);
        super.markDamaged(bounds);
    }

    @Override
    public void mouseMoved(final Location location) {
        final int area = adjust(location);
        switch (area) {
        case NORTH:
            topHeader.mouseMoved(location);
            break;

        case WEST:
            leftHeader.mouseMoved(location);
            break;

        case CENTER:
            // location.add(offset());
            // location.move(-left, -top);
            wrappedView.mouseMoved(location);
            break;

        case SOUTH:
        case EAST:
        default:
            break;
        }
    }

    private Offset offset() {
        final Bounds contents = viewportArea();
        final int width = contents.getWidth();
        final int x = width == 0 ? 0 : horizontalScrollBar.getPosition() * wrappedView.getRequiredSize(Size.createMax()).getWidth() / width;
        final int height = contents.getHeight();
        final int y = height == 0 ? 0 : verticalScrollBar.getPosition() * wrappedView.getRequiredSize(Size.createMax()).getHeight() / height;
        return new Offset(x, y);
    }

    protected boolean overContent(final Location location) {
        return viewportArea().contains(location);
    }

    public void reset() {
        horizontalScrollBar.reset();
        verticalScrollBar.reset();
    }

    /**
     * Moves the scrollbar to beginning or the end when a double click occurs on
     * that side.
     */
    @Override
    public void secondClick(final Click click) {
        final int area = adjust(click);
        switch (area) {
        case NORTH:
            topHeader.secondClick(click);
            break;

        case WEST:
            leftHeader.secondClick(click);
            break;

        case CENTER:
            wrappedView.secondClick(click);
            break;

        case SOUTH:
            horizontalScrollBar.secondClick(click.getLocation().getX());
            break;

        case EAST:
            verticalScrollBar.secondClick(click.getLocation().getY());
            break;

        default:
            break;
        }
    }

    @Override
    public void thirdClick(final Click click) {
        final int area = adjust(click);
        switch (area) {
        case NORTH:
            topHeader.thirdClick(click);
            break;

        case WEST:
            leftHeader.thirdClick(click);
            break;

        case CENTER:
            wrappedView.thirdClick(click);
            break;

        case SOUTH:
        case EAST:
        default:
            // ignore
            break;
        }
    }

    @Override
    public void setBounds(final Bounds bounds) {
        setLocation(bounds.getLocation());
        setSize(bounds.getSize());
    }

    @Override
    public void setSize(final Size size) {
        // TODO need to restore the offset after size change - see limitBounds
        // float verticalRatio = ((float) verticalScrollPosition) /
        // contentArea().getHeight();

        this.size = new Size(size);

        final Size contentSize = wrappedView.getRequiredSize(Size.createMax());
        wrappedView.setSize(contentSize);

        final int availableHeight2 = size.getHeight() - top;
        final int contentHeight2 = contentSize.getHeight();
        right = availableHeight2 >= contentHeight2 ? 0 : SCROLLBAR_WIDTH;

        final int availableWidth2 = size.getWidth() - left;
        final int contentWidth2 = contentSize.getWidth();
        bottom = availableWidth2 >= contentWidth2 ? 0 : SCROLLBAR_WIDTH;

        final Bounds viewport = viewportArea();

        final int viewportHeight = viewport.getHeight();
        final int maxContentHeight = Math.max(viewportHeight, contentSize.getHeight());

        verticalScrollBar.setSize(viewportHeight, maxContentHeight);
        if (leftHeader != null) {
            leftHeader.setSize(new Size(left, maxContentHeight));
        }

        final int viewportWidth = viewport.getWidth();
        final int maxContentWidth = Math.max(viewportWidth, contentSize.getWidth());

        horizontalScrollBar.setSize(viewportWidth, maxContentWidth);
        if (topHeader != null) {
            topHeader.setSize(new Size(maxContentWidth, top));
        }
    }

    public int getVerticalPosition() {
        return verticalScrollBar.getPosition();
    }

    public int getHorizontalPosition() {
        return horizontalScrollBar.getPosition();
    }

    @Override
    public ViewAreaType viewAreaType(final Location location) {
        final int area = adjust(location);
        switch (area) {
        case NORTH:
            return topHeader.viewAreaType(location);

        case WEST:
            return leftHeader.viewAreaType(location);

        case CENTER:
            return wrappedView.viewAreaType(location);

        case SOUTH:
        case EAST:
        default:
            return ViewAreaType.INTERNAL;
        }
    }

    @Override
    public void viewMenuOptions(final UserActionSet menuOptions) {
        super.viewMenuOptions(menuOptions);
        menuOptions.add(new UserActionAbstract("Reset scroll border", ActionType.DEBUG) {
            @Override
            public void execute(final Workspace workspace, final View view, final Location at) {
                reset();
                invalidateLayout();
            }
        });
    }
}
TOP

Related Classes of org.apache.isis.viewer.dnd.view.border.ScrollBorder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.