Package com.sencha.gxt.widget.core.client

Source Code of com.sencha.gxt.widget.core.client.Resizable$ResizeHandle

/**
* Sencha GXT 3.1.0-beta - Sencha for GWT
* Copyright(c) 2007-2014, Sencha, Inc.
* licensing@sencha.com
*
* http://www.sencha.com/products/gxt/license/
*/
package com.sencha.gxt.widget.core.client;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
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.event.shared.SimpleEventBus;
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.ui.RootPanel;
import com.sencha.gxt.core.client.GXT;
import com.sencha.gxt.core.client.dom.XElement;
import com.sencha.gxt.core.client.util.BaseEventPreview;
import com.sencha.gxt.core.client.util.Point;
import com.sencha.gxt.core.client.util.Rectangle;
import com.sencha.gxt.core.shared.event.GroupingHandlerRegistration;
import com.sencha.gxt.fx.client.Shim;
import com.sencha.gxt.widget.core.client.event.ResizeEndEvent;
import com.sencha.gxt.widget.core.client.event.ResizeEndEvent.HasResizeEndHandlers;
import com.sencha.gxt.widget.core.client.event.ResizeEndEvent.ResizeEndHandler;
import com.sencha.gxt.widget.core.client.event.ResizeStartEvent;
import com.sencha.gxt.widget.core.client.event.ResizeStartEvent.HasResizeStartHandlers;
import com.sencha.gxt.widget.core.client.event.ResizeStartEvent.ResizeStartHandler;

/**
* Applies drag handles to a widget to make it resizable. The drag handles are inserted into the widget and positioned
* absolute.
*
* <p />
* Only <code>Component</code> instances can be used with <code>Resizable</code> as <code>Widget</code>'s do not fire
* resize events. Widgets can be wrapped in a {@link WidgetComponent} instance to use with <code>Resizable</code>.
*
* <p>
* Here is the list of valid resize handles:
* </p>
*
* <pre>
* Value   Description
* ------  -------------------
* 'n'     north
* 's'     south
* 'e'     east
* 'w'     west
* 'nw'    northwest
* 'sw'    southwest
* 'se'    southeast
* 'ne'    northeast
* 'all'   all
* </pre>
*/
public class Resizable implements HasResizeStartHandlers, HasResizeEndHandlers {

  /**
   * The location of the resize handle in standard compass points.
   */
  @SuppressWarnings("javadoc")
  public enum Dir {
    E, N, NE, NW, S, SE, SW, W
  }

  @SuppressWarnings("javadoc")
  public interface ResizableAppearance {

    Element createProxy();

    String getHandleStyles(Dir dir);
  }

  private class Handler implements ResizeHandler, AttachEvent.Handler {

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

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

  }

  private class ResizeHandle extends Component {

    public Dir dir;

    private ResizeHandle() {
      setElement(Document.get().createDivElement());
      sinkEvents(Event.MOUSEEVENTS);
    }

    @Override
    public void onBrowserEvent(Event event) {
      switch (DOM.eventGetType(event)) {
        case Event.ONMOUSEDOWN:
          DOM.eventCancelBubble(event, true);
          event.preventDefault();
          handleMouseDown(event, this);
          break;
      }
    }

  }

  private Dir dir;
  private boolean dynamic;
  private boolean enabled = true;
  private List<ResizeHandle> handleList;
  private Dir[] handles;
  private int maxHeight = 2000;
  private int maxWidth = 2000;
  private int minHeight = 50;
  private int minWidth = 50;
  private boolean preserveRatio = false;
  private BaseEventPreview preview;
  private XElement proxyEl;
  private Component resize;
  private boolean resizing;
  private Rectangle startBox;
  private Point startPoint;
  private SimpleEventBus eventBus;

  private GroupingHandlerRegistration registration;
  private final ResizableAppearance appearance;

  /**
   * Creates a new resizable instance with 8-way resizing.
   *
   * @param resize the resize widget
   */
  public Resizable(Component resize) {
    this(resize, Dir.values());
  }

  /**
   * Creates a new resizable instance with the default appearance.
   *
   * @param resize the resize widget
   * @param handles the resize handle locations
   */
  public Resizable(final Component resize, Dir... handles) {
    this(GWT.<ResizableAppearance> create(ResizableAppearance.class), resize, handles);
  }

  /**
   * Creates a new resizable instance with the specified appearance.
   *
   * @param appearance the appearance of the resizable
   * @param resize the resize widget
   * @param handles the resize handle locations
   */
  public Resizable(ResizableAppearance appearance, final Component resize, Dir... handles) {
    this.resize = resize;
    this.handles = handles;
    this.appearance = appearance;

    registration = new GroupingHandlerRegistration();
    Handler handler = new Handler();
    registration.add(resize.addAttachHandler(handler));
    registration.add(resize.addResizeHandler(handler));

    init();

    if (resize.isAttached()) {
      onAttach();
      onComponentResize();
    }
  }

  @Override
  public HandlerRegistration addResizeEndHandler(ResizeEndHandler handler) {
    return ensureHandlers().addHandler(ResizeEndEvent.getType(), handler);
  }

  @Override
  public HandlerRegistration addResizeStartHandler(ResizeStartHandler handler) {
    return ensureHandlers().addHandler(ResizeStartEvent.getType(), handler);
  }

  public ResizableAppearance getAppearance() {
    return appearance;
  }

  /**
   * Returns the max height
   *
   * @return the max height
   */
  public int getMaxHeight() {
    return maxHeight;
  }

  /**
   * Returns the max width.
   *
   * @return the max width
   */
  public int getMaxWidth() {
    return maxWidth;
  }

  /**
   * Returns the minimum height.
   *
   * @return the minimum height
   */
  public int getMinHeight() {
    return minHeight;
  }

  /**
   * Returns the minimum width.
   *
   * @return the minimum width
   */
  public int getMinWidth() {
    return minWidth;
  }

  /**
   * Returns true if widget is being resized directly.
   *
   * @return the dynamic state
   */
  public boolean isDynamic() {
    return dynamic;
  }

  /**
   * Returns true if the aspect ratio is being preserved.
   *
   * @return true if the aspect ratio is being preserved
   */
  public boolean isPreserveRatio() {
    return preserveRatio;
  }

  /**
   * Returns <code>true</code> if if resizing.
   *
   * @return the resize state
   */
  public boolean isResizing() {
    return resizing;
  }

  /**
   * Removes the drag handles.
   */
  public void release() {
    registration.removeHandler();

    if (handleList != null) {
      for (ResizeHandle handle : handleList) {
        handle.getElement().removeFromParent();
      }
    }
   
    onDetach();
  }

  /**
   * True to resize the widget directly instead of using a proxy (defaults to false).
   *
   * @param dynamic true to resize directly
   */
  public void setDynamic(boolean dynamic) {
    this.dynamic = dynamic;
  }

  /**
   * Enables or disables the drag handles.
   *
   * @param enable <code>true</code> to enable
   */
  public void setEnabled(boolean enable) {
    if (enabled != enable && handleList != null) {
      for (ResizeHandle handle : handleList) {
        handle.getElement().setVisibility(enable);
      }
      if (enable) {
        syncHandleHeight();
      }
    }
    this.enabled = enable;
  }

  /**
   * Sets the max height (defaults to 2000).
   *
   * @param maxHeight the max height
   */
  public void setMaxHeight(int maxHeight) {
    this.maxHeight = maxHeight;
  }

  /**
   * Sets the max width (defaults to 2000).
   *
   * @param maxWidth the max width
   */
  public void setMaxWidth(int maxWidth) {
    this.maxWidth = maxWidth;
  }

  /**
   * Sets the minimum height (default to 50).
   *
   * @param minHeight the minimum height
   */
  public void setMinHeight(int minHeight) {
    this.minHeight = minHeight;
  }

  /**
   * Sets the minimum width (defaults to 50).
   *
   * @param minWidth the minimum width
   */
  public void setMinWidth(int minWidth) {
    this.minWidth = minWidth;
  }

  /**
   * True to preserve the original ratio between height and width during resize (defaults to false).
   *
   * @param preserveRatio true to preserve the original aspect ratio
   */
  public void setPreserveRatio(boolean preserveRatio) {
    this.preserveRatio = preserveRatio;
  }

  protected Element createProxy() {
    return appearance.createProxy();
  }

  protected void init() {
    resize.getElement().makePositionable();
    if (handleList == null) {
      handleList = new ArrayList<ResizeHandle>();
      for (Dir handle : handles) {
        create(handle);
      }

      preview = new BaseEventPreview() {

        @Override
        public boolean onPreview(NativePreviewEvent event) {
          event.getNativeEvent().preventDefault();
          switch (event.getTypeInt()) {
            case Event.ONMOUSEMOVE:
              int x = event.getNativeEvent().getClientX();
              int y = event.getNativeEvent().getClientY();
              handleMouseMove(x, y);
              break;
            case Event.ONMOUSEUP:
              handleMouseUp(event.getNativeEvent().<Event> cast());
              break;
          }
          return true;
        }

      };
      preview.setAutoHide(false);
    }

    syncHandleHeight();
    setEnabled(enabled);
  }

  protected void onAttach() {
    if (handleList != null) {
      for (ResizeHandle handle : handleList) {
        ComponentHelper.doAttach(handle);
      }
    }
  }

  protected void onComponentResize() {
    syncHandleHeight();
  }

  protected void onDetach() {
    if (handleList != null) {
      for (ResizeHandle handle : handleList) {
        ComponentHelper.doDetach(handle);
      }
    }
  }

  protected void syncHandleHeight() {
    if (GXT.isIE6()) {
      if (resize != null && handleList != null) {
        int height = resize.getOffsetHeight(true);
        for (ResizeHandle r : handleList) {
          if (r.dir == Dir.E || r.dir == Dir.W) {
            r.getElement().setHeight(height);
          }
        }
        resize.getElement().repaint();
      }
    }
  }

  SimpleEventBus ensureHandlers() {
    return eventBus == null ? eventBus = new SimpleEventBus() : eventBus;
  }

  private int constrain(int v, int diff, int m, int mx) {
    if (v - diff < m) {
      diff = v - m;
    } else if (v - diff > mx) {
      diff = mx - v;
    }
    return diff;
  }

  private ResizeHandle create(Dir dir) {
    ResizeHandle rh = new ResizeHandle();
    rh.setStyleName(appearance.getHandleStyles(dir));
    rh.dir = dir;
    resize.getElement().appendChild(rh.getElement());
    handleList.add(rh);
    return rh;
  }

  private void handleMouseDown(Event event, ResizeHandle handle) {
    ResizeStartEvent evt = new ResizeStartEvent(handle, event);
    ensureHandlers().fireEventFromSource(evt, this);

    if (!enabled || evt.isCancelled()) {
      return;
    }

    dir = handle.dir;

    startBox = resize.getElement().getBounds(false);

    int x = event.getClientX();
    int y = event.getClientY();
    startPoint = new Point(x, y);

    resizing = true;

    if (dynamic) {
      if (proxyEl != null) {
        proxyEl.setVisible(false);
      }
    } else {
      if (proxyEl == null) {
        proxyEl = XElement.as(createProxy());
      }
      Element body = RootPanel.getBodyElement();
      DOM.appendChild(body, proxyEl);

      proxyEl.makePositionable(true);
      proxyEl.setLeft(startBox.getX());
      proxyEl.setTop(startBox.getY());
      proxyEl.setSize(startBox.getWidth(), startBox.getHeight(), true);
      proxyEl.setVisible(true);
      proxyEl.updateZIndex(5);
    }

    preview.add();

    Shim.get().cover(false);
    Shim.get().setStyleAttribute("cursor", handle.getElement().getStyle().getCursor());
  }

  private void handleMouseMove(int xin, int yin) {
    if (resizing) {
      int x = startBox.getX();
      int y = startBox.getY();
      float w = startBox.getWidth();
      float h = startBox.getHeight();
      float ow = w, oh = h;
      int mw = minWidth;
      int mh = minHeight;
      int mxw = maxWidth;
      int mxh = maxHeight;

      Point eventXY = new Point(xin, yin);

      int diffX = -(startPoint.getX() - Math.max(2, eventXY.getX()));
      int diffY = -(startPoint.getY() - Math.max(2, eventXY.getY()));

      switch (dir) {
        case E:
          w += diffX;
          w = Math.min(Math.max(mw, w), mxw);
          break;
        case S:
          h += diffY;
          h = Math.min(Math.max(mh, h), mxh);
          break;
        case SE:
          w += diffX;
          h += diffY;
          w = Math.min(Math.max(mw, w), mxw);
          h = Math.min(Math.max(mh, h), mxh);
          break;
        case N:
          diffY = constrain((int) h, diffY, mh, mxh);
          y += diffY;
          h -= diffY;
          break;
        case W:
          diffX = constrain((int) w, diffX, mw, mxw);
          x += diffX;
          w -= diffX;
          break;
        case NE:
          w += diffX;
          w = Math.min(Math.max(mw, w), mxw);
          diffY = constrain((int) h, diffY, mh, mxh);
          y += diffY;
          h -= diffY;
          break;
        case NW:
          diffX = constrain((int) w, diffX, mw, mxw);
          diffY = constrain((int) h, diffY, mh, mxh);
          y += diffY;
          h -= diffY;
          x += diffX;
          w -= diffX;
          break;
        case SW:
          diffX = constrain((int) w, diffX, mw, mxw);
          h += diffY;
          h = Math.min(Math.max(mh, h), mxh);
          x += diffX;
          w -= diffX;
          break;
      }
      if (preserveRatio) {
        switch (dir) {
          case SE:
          case E:
            h = oh * (w / ow);
            h = Math.min(Math.max(mh, h), mxh);
            w = ow * (h / oh);
            break;
          case S:
            w = ow * (h / oh);
            w = Math.min(Math.max(mw, w), mxw);
            h = oh * (w / ow);
            break;
          case NE:
            w = ow * (h / oh);
            w = Math.min(Math.max(mw, w), mxw);
            h = oh * (w / ow);
            break;
          case N: {
            float tw = w;
            w = ow * (h / oh);
            w = Math.min(Math.max(mw, w), mxw);
            h = oh * (w / ow);
            x += (tw - w) / 2;
          }
            break;
          case SW: {
            h = oh * (w / ow);
            h = Math.min(Math.max(mh, h), mxh);
            float tw = w;
            w = ow * (h / oh);
            x += tw - w;
            break;
          }
          case W: {
            float th = h;
            h = oh * (w / ow);
            h = Math.min(Math.max(mh, h), mxh);
            y += (th - h) / 2;
            float tw = w;
            w = ow * (h / oh);
            x += tw - w;
            break;
          }
          case NW: {
            float tw = w;
            float th = h;
            h = oh * (w / ow);
            h = Math.min(Math.max(mh, h), mxh);
            w = ow * (h / oh);
            y += th - h;
            x += tw - w;
            break;
          }
        }
      }

      if (dynamic) {
        resize.setPagePosition(x, y);
        resize.setPixelSize((int) w, (int) h);
      } else {
        proxyEl.setLeftTop(x, y);
        proxyEl.setSize((int) w, (int) h, true);
      }

    }
  }

  private void handleMouseUp(Event event) {
    resizing = false;
    preview.remove();
    Shim.get().uncover();

    if (!dynamic) {
      Rectangle rect = dynamic ? resize.getElement().getBounds() : proxyEl.getBounds();

      rect.setWidth(Math.min(rect.getWidth(), maxWidth));
      rect.setHeight(Math.min(rect.getHeight(), maxHeight));

      proxyEl.disableTextSelection(false);
      proxyEl.setVisible(false);
      proxyEl.removeFromParent();
      resize.setBounds(rect);
    }

    syncHandleHeight();

    ensureHandlers().fireEventFromSource(new ResizeEndEvent(resize, event), this);
  }

}
TOP

Related Classes of com.sencha.gxt.widget.core.client.Resizable$ResizeHandle

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.