com.sencha.gxt.widget.core.client.SplitBar.java Source code

Java tutorial

Introduction

Here is the source code for com.sencha.gxt.widget.core.client.SplitBar.java

Source

/**
 * Sencha GXT 4.0.0 - Sencha for GWT
 * Copyright (c) 2006-2015, Sencha Inc.
 *
 * licensing@sencha.com
 * http://www.sencha.com/products/gxt/license/
 *
 * ================================================================================
 * Open Source License
 * ================================================================================
 * This version of Sencha GXT is licensed under the terms of the Open Source GPL v3
 * license. You may use this license only if you are prepared to distribute and
 * share the source code of your application under the GPL v3 license:
 * http://www.gnu.org/licenses/gpl.html
 *
 * If you are NOT prepared to distribute and share the source code of your
 * application under the GPL v3 license, other commercial and oem licenses
 * are available for an alternate download of Sencha GXT.
 *
 * Please see the Sencha GXT Licensing page at:
 * http://www.sencha.com/products/gxt/license/
 *
 * For clarification or additional options, please contact:
 * licensing@sencha.com
 * ================================================================================
 *
 *
 * ================================================================================
 * Disclaimer
 * ================================================================================
 * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 * ================================================================================
 */
package com.sencha.gxt.widget.core.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Cursor;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.dom.client.HasDoubleClickHandlers;
import com.google.gwt.event.logical.shared.AttachEvent;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.Event;
import com.sencha.gxt.core.client.Style.Direction;
import com.sencha.gxt.core.client.Style.LayoutRegion;
import com.sencha.gxt.core.client.dom.XDOM;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.resources.CommonStyles;
import com.sencha.gxt.core.client.util.DelayedTask;
import com.sencha.gxt.core.client.util.Rectangle;
import com.sencha.gxt.core.shared.event.GroupingHandlerRegistration;
import com.sencha.gxt.fx.client.DragCancelEvent;
import com.sencha.gxt.fx.client.DragEndEvent;
import com.sencha.gxt.fx.client.DragHandler;
import com.sencha.gxt.fx.client.DragMoveEvent;
import com.sencha.gxt.fx.client.DragStartEvent;
import com.sencha.gxt.fx.client.Draggable;
import com.sencha.gxt.widget.core.client.event.HideEvent;
import com.sencha.gxt.widget.core.client.event.HideEvent.HideHandler;
import com.sencha.gxt.widget.core.client.event.MoveEvent;
import com.sencha.gxt.widget.core.client.event.MoveEvent.MoveHandler;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent.HasSelectHandlers;
import com.sencha.gxt.widget.core.client.event.SelectEvent.SelectHandler;
import com.sencha.gxt.widget.core.client.event.ShowEvent;
import com.sencha.gxt.widget.core.client.event.ShowEvent.ShowHandler;
import com.sencha.gxt.widget.core.client.event.SplitBarDragEvent;
import com.sencha.gxt.widget.core.client.event.SplitBarDragEvent.HasSplitBarDragHandlers;
import com.sencha.gxt.widget.core.client.event.SplitBarDragEvent.SplitBarDragHandler;

/**
 * Creates a draggable splitter on the side of a widget.
 */
public class SplitBar extends Component
        implements HasClickHandlers, HasDoubleClickHandlers, HasSplitBarDragHandlers, HasSelectHandlers {

    @SuppressWarnings("javadoc")
    public interface SplitBarAppearance {
        String miniClass(Direction direction);

        String miniSelector();

        void onMiniOver(XElement mini, boolean over);

        String proxyClass();

        void render(SafeHtmlBuilder sb, LayoutRegion region);

        int getDefaultBarWidth();
    }

    private class Handler implements AttachEvent.Handler, ResizeHandler, MoveHandler, HideHandler, ShowHandler {

        @Override
        public void onAttachOrDetach(AttachEvent event) {
            if (event.isAttached()) {
                onHandleAttach();
            } else {
                onHandleDetach();
            }
        }

        @Override
        public void onHide(HideEvent event) {
            onHandleHide(event);
        }

        @Override
        public void onMove(MoveEvent event) {
            delay.delay(10);
        }

        @Override
        public void onResize(ResizeEvent event) {
            delay.delay(10);
        }

        @Override
        public void onShow(ShowEvent event) {
            onHandleShow(event);
        }
    }

    private boolean autoSize = true;
    private int yOffset = 0;
    private int xOffset = 0;

    private int minSize = 10;
    private int maxSize = 2000;

    private int handleWidth;
    private int barWidth = 2;
    private XElement resizeEl, miniEl;
    private Component resizeWidget;
    private Component containerWidget;
    private Draggable draggable;
    private Rectangle startBounds;
    private DelayedTask delay;
    protected LayoutRegion region;

    private GroupingHandlerRegistration handlerRegistration;
    private boolean collapsible;
    private final SplitBarAppearance appearance;
    private boolean disableDragging;

    /**
     * Creates a split for bar for the specified layout region and target.
     * 
     * @param region the layout region
     * @param target the split bar container
     */
    public SplitBar(LayoutRegion region, Component target) {
        this(GWT.<SplitBarAppearance>create(SplitBarAppearance.class), region, target);
    }

    /**
     * Creates a new split bar.
     * 
     * @param style the bar location
     * @param resizeWidget the widget being resized
     * @param container the widget the split bar proxy will be sized to
     */
    public SplitBar(LayoutRegion style, Component resizeWidget, Component container) {
        this(style, resizeWidget);
        this.containerWidget = container;
        draggable.setContainer(container);
    }

    /**
     * Creates a new split bar with the specified appearance
     * 
     * @param appearance the split bar appearance
     * @param region the bar location
     * @param resizeWidget the widget being resized
     */
    public SplitBar(SplitBarAppearance appearance, LayoutRegion region, Component resizeWidget) {
        this.appearance = appearance;
        this.region = region;
        this.resizeWidget = resizeWidget;
        this.resizeEl = resizeWidget.getElement();

        this.handleWidth = appearance.getDefaultBarWidth();

        SafeHtmlBuilder builder = new SafeHtmlBuilder();
        this.appearance.render(builder, region);

        setElement((Element) XDOM.create(builder.toSafeHtml()));

        setAllowTextSelection(false);
        getElement().makePositionable(true);

        Handler handler = new Handler();
        handlerRegistration = new GroupingHandlerRegistration();
        handlerRegistration.add(resizeWidget.addAttachHandler(handler));
        handlerRegistration.add(resizeWidget.addMoveHandler(handler));
        handlerRegistration.add(resizeWidget.addResizeHandler(handler));
        handlerRegistration.add(resizeWidget.addHideHandler(handler));
        handlerRegistration.add(resizeWidget.addShowHandler(handler));

        draggable = new Draggable(this);
        draggable.setUpdateZIndex(false);
        draggable.setStartDragDistance(0);
        draggable.setProxyStyle(appearance.proxyClass());

        DragHandler dragHandler = new DragHandler() {
            @Override
            public void onDragCancel(DragCancelEvent event) {
                onCancelDrag(event);
            }

            @Override
            public void onDragEnd(DragEndEvent event) {
                onEndDrag(event);
            }

            @Override
            public void onDragMove(DragMoveEvent event) {

            }

            @Override
            public void onDragStart(DragStartEvent event) {
                onStartDrag(event);
            }
        };

        draggable.addDragHandler(dragHandler);

        if (resizeWidget.isAttached()) {
            onHandleAttach();
        }

        delay = new DelayedTask() {

            @Override
            public void onExecute() {
                sync();
            }
        };

        sinkEvents(Event.ONCLICK | Event.ONMOUSEOVER | Event.ONMOUSEOUT);
    }

    @Override
    public HandlerRegistration addClickHandler(ClickHandler handler) {
        return addDomHandler(handler, ClickEvent.getType());
    }

    @Override
    public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
        return addDomHandler(handler, DoubleClickEvent.getType());
    }

    @Override
    public HandlerRegistration addSelectHandler(SelectHandler handler) {
        return addHandler(handler, SelectEvent.getType());
    }

    @Override
    public HandlerRegistration addSplitBarDragHandler(SplitBarDragHandler handler) {
        return addHandler(handler, SplitBarDragEvent.getType());
    }

    /**
     * Prevents the split bar from being dragged.
     */
    public void disableDragging() {
        if (!disableDragging) {
            disableDragging = true;
            draggable.setEnabled(false);
            draggable.release();
            getElement().getStyle().setCursor(Cursor.DEFAULT);
        }
    }

    /**
     * Returns the split bar appearance.
     * 
     * @return the split bar appearance
     */
    public SplitBarAppearance getAppearance() {
        return appearance;
    }

    /**
     * Returns the bar width.
     * 
     * @return the bar width
     */
    public int getBarWidth() {
        return barWidth;
    }

    /**
     * Returns the container widget.
     * 
     * @return the container widget
     */
    public Component getContainer() {
        return containerWidget;
    }

    /**
     * Returns the split bar's draggable instance.
     * 
     * @return the draggable instance
     */
    public Draggable getDraggable() {
        return draggable;
    }

    /**
     * Returns the handle width.
     * 
     * @return the handle width
     */
    public int getHandleWidth() {
        return handleWidth;
    }

    /**
     * Returns the maximum size.
     * 
     * @return the max size
     */
    public int getMaxSize() {
        return maxSize;
    }

    /**
     * @return the minSize
     */
    public int getMinSize() {
        return minSize;
    }

    /**
     * Returns the resize widget.
     * 
     * @return the resize widget
     */
    public Component getTargetWidget() {
        return resizeWidget;
    }

    /**
     * Returns the x offset.
     * 
     * @return the x offset value
     */
    public int getXOffset() {
        return xOffset;
    }

    /**
     * Returns the y offset.
     * 
     * @return the y offset
     */
    public int getYOffset() {
        return yOffset;
    }

    /**
     * Returns the auto size state.
     * 
     * @return true if auto size is enabled
     */
    public boolean isAutoSize() {
        return autoSize;
    }

    /**
     * Return true if the mini-collapse tool is enabled.
     * 
     * @return true if mini-collapse enabled
     */
    public boolean isCollapsible() {
        return collapsible;
    }

    @Override
    public void onBrowserEvent(Event event) {
        super.onBrowserEvent(event);
        switch (event.getTypeInt()) {
        case Event.ONCLICK:
            onClick(event);
            break;
        case Event.ONMOUSEOVER:
            onMouseOver(event);
            break;

        case Event.ONMOUSEOUT:
            onMouseOut(event);
            break;
        }
    }

    /**
     * Removes the split bar from the resize widget.
     */
    public void release() {
        handlerRegistration.removeHandler();

        removeSplitBar();
        draggable.release();
    }

    /**
     * True to update the size of the the resize widget after a drag operation using a proxy (defaults to true).
     * 
     * @param autoSize the auto size state
     */
    public void setAutoSize(boolean autoSize) {
        this.autoSize = autoSize;
    }

    /**
     * Sets the width of drag proxy during resizing (defaults to 2).
     * 
     * @param barWidth the bar width
     */
    public void setBarWidth(int barWidth) {
        this.barWidth = barWidth;
    }

    /**
     * True to show a mini-collapse tool in the split bar (defaults to false). When clicked, the collapse event is fired.
     * 
     * @param collapsible true to add the mini-collapse tool
     */
    public void setCollapsible(boolean collapsible) {
        this.collapsible = collapsible;
        if (collapsible && miniEl == null) {
            miniEl = XElement.createElement("div");
            miniEl.setClassName(CommonStyles.get().nodrag() + " " + appearance.miniClass(Direction.LEFT));
            getElement().appendChild(miniEl);

        }
        if (miniEl != null) {
            miniEl.setDisplayed(collapsible);
        }
    }

    /**
     * Sets the width of the drag handles (defaults to 5).
     * 
     * @param handleWidth the handle width
     */
    public void setHandleWidth(int handleWidth) {
        this.handleWidth = handleWidth;
    }

    /**
     * Sets the maximum size of the resize widget (defaults to 2000).
     * 
     * @param maxSize the maximum size
     */
    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }

    /**
     * Sets he minimum size of the resize widget (defaults to 10).
     * 
     * @param minSize the minimum size
     */
    public void setMinSize(int minSize) {
        this.minSize = minSize;
    }

    /**
     * The amount of pixels the bar should be offset to the left (defaults to 0).
     * 
     * @param x the xOffset to set
     */
    public void setXOffset(int x) {
        this.xOffset = x;
    }

    /**
     * Sets the amount of pixels the bar should be offset to the top (defaults to 0).
     * 
     * @param y the yOffset to set
     */
    public void setYOffset(int y) {
        this.yOffset = y;
    }

    /**
     * Updates the spitbar's bounds to match the target widget.
     */
    public void sync() {
        if (!isAttached() || !resizeWidget.isAttached()) {
            return;
        }

        Rectangle rect = resizeEl.getBounds();
        int x = rect.getX();
        int y = rect.getY();

        // if (!GXT.isBorderBox()) {
        // y -= resizeEl.getFrameWidth(Side.TOP);
        // x -= resizeEl.getFrameWidth(Side.LEFT);
        // }

        y = Math.max(y, 0);

        int w = rect.getWidth();
        int h = rect.getHeight();

        switch (region) {
        case SOUTH:
            getElement().setBounds(x + getXOffset(), y + h + getYOffset(), w, getHandleWidth(), false);
            break;
        case WEST:
            getElement().setBounds(x - getHandleWidth() + getXOffset(), y + getYOffset(), getHandleWidth(), h,
                    false);
            break;
        case NORTH:
            getElement().setBounds(x + getXOffset(), y - getHandleWidth() + getYOffset(), w, getHandleWidth(),
                    false);
            break;
        case EAST:
            getElement().setBounds(Math.max(0, x + w + getXOffset()), y + getYOffset(), getHandleWidth(), h, false);
            break;
        case CENTER:
            // do nothing
        }

    }

    /**
     * Sets the visual style indicating the direction of the mini collapse tool.
     * 
     * @param direction the collapse direction
     */
    public void updateMini(Direction direction) {
        if (miniEl != null) {
            miniEl.setClassName(CommonStyles.get().nodrag() + " " + appearance.miniClass(direction));
        }
    }

    protected void onClick(Event event) {
        XElement target = event.getEventTarget().<XElement>cast();
        if (target == miniEl) {
            event.stopPropagation();
            onMiniClick();
        }
    }

    protected void onHandleAttach() {
        if (!disabled) {
            resizeWidget.getParent().getElement().appendChild(getElement());
            ComponentHelper.doAttach(SplitBar.this);
            sync();
        }
    }

    protected void onHandleDetach() {
        if (!disabled) {
            removeSplitBar();
        }
    }

    protected void onHandleHide(HideEvent event) {
        hide();
    }

    protected void onHandleShow(ShowEvent event) {
        show();
        sync();
    }

    protected void onMiniClick() {
        fireEvent(new SelectEvent());
    }

    protected void onMouseOut(Event event) {
        if (event.getEventTarget().cast() == miniEl) {
            appearance.onMiniOver(miniEl, false);
        }
    }

    protected void onMouseOver(Event event) {
        if (event.getEventTarget().cast() == miniEl) {
            appearance.onMiniOver(miniEl, true);
        }
    }

    protected void removeSplitBar() {
        ComponentHelper.doDetach(this);
        getElement().removeFromParent();
    }

    private void onCancelDrag(DragCancelEvent be) {
        resizeWidget.enableEvents();
        sync();
    }

    private void onEndDrag(DragEndEvent bee) {
        int x = bee.getX();
        int y = bee.getY();
        int width = resizeWidget.getOffsetWidth();
        int height = resizeWidget.getOffsetHeight();

        int diffY = y - startBounds.getY();
        int diffX = x - startBounds.getX();

        resizeWidget.enableEvents();

        int size = 0;

        switch (region) {
        case NORTH: {
            size = height - diffY;
            if (autoSize) {
                resizeEl.setY(y);
                resizeEl.setHeight(height);
            }
            break;
        }
        case SOUTH: {
            size = height + diffY;
            if (autoSize) {
                resizeWidget.setHeight(diffY);
            }
            break;
        }
        case WEST: {
            size = width - diffX;
            if (autoSize) {
                getElement().setX(x);
                resizeWidget.setWidth(width - diffX);
            }
            break;
        }
        case EAST: {
            size = width + diffX;
            if (autoSize) {
                resizeWidget.setWidth(size);
            }
            break;
        }
        case CENTER:
            // do nothing  
        }
        fireEvent(new SplitBarDragEvent(false, size));
    }

    private void onStartDrag(DragStartEvent de) {
        // adjust width of proxy
        if (region == LayoutRegion.WEST || region == LayoutRegion.EAST) {
            de.setWidth(getBarWidth());
        } else {
            de.setHeight(getBarWidth());
        }

        fireEvent(new SplitBarDragEvent(true, 0));

        resizeWidget.enableEvents();

        if (containerWidget != null) {
            switch (region) {
            case WEST:
            case EAST:
                int h = containerWidget.getOffsetHeight(true);
                de.setHeight(h);
                break;
            case NORTH:
            case SOUTH:
                int w = containerWidget.getOffsetWidth(true);
                de.setWidth(w);
                break;
            case CENTER:
                // do nothing
            }
        }
        startBounds = new Rectangle();
        startBounds.setY(de.getY());
        startBounds.setX(de.getX());

        boolean v = region == LayoutRegion.WEST || region == LayoutRegion.EAST;
        int size;
        if (v) {
            size = resizeEl.getOffsetWidth();
        } else {
            size = resizeEl.getOffsetHeight();
        }

        int c1 = size - getMinSize();
        if (size < getMinSize()) {
            c1 = 0;
        }
        int c2 = Math.max(getMaxSize() - size, 0);
        if (v) {
            draggable.setConstrainVertical(true);
            draggable.setXConstraint(region == LayoutRegion.WEST ? c2 : c1, region == LayoutRegion.WEST ? c1 : c2);
        } else {
            draggable.setConstrainHorizontal(true);
            draggable.setYConstraint(region == LayoutRegion.NORTH ? c2 : c1,
                    region == LayoutRegion.NORTH ? c1 : c2);
        }
    }

}