Package com.extjs.gxt.ui.client.fx

Source Code of com.extjs.gxt.ui.client.fx.Resizable

/*
* Ext GWT 2.2.4 - Ext for GWT
* Copyright(c) 2007-2010, Ext JS, LLC.
* licensing@extjs.com
*
* http://extjs.com/license
*/
package com.extjs.gxt.ui.client.fx;

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

import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.BaseObservable;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.EventType;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.PreviewEvent;
import com.extjs.gxt.ui.client.event.ResizeEvent;
import com.extjs.gxt.ui.client.event.ResizeListener;
import com.extjs.gxt.ui.client.util.BaseEventPreview;
import com.extjs.gxt.ui.client.util.Point;
import com.extjs.gxt.ui.client.util.Rectangle;
import com.extjs.gxt.ui.client.widget.BoxComponent;
import com.extjs.gxt.ui.client.widget.Component;
import com.extjs.gxt.ui.client.widget.ComponentHelper;
import com.extjs.gxt.ui.client.widget.Shim;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.RootPanel;

/**
* Applies drag handles to a widget to make it resizable. The drag handles are
* inserted into the widget and positioned absolute.
* <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>
*
* <dl>
* <dt><b>Events:</b></dt>
*
* <dd><b>ResizeStart</b> : (source, widget, event) <br>
* <div>Fires before a resize operation start. Listeners can cancel the action by
* calling {@link BaseEvent#setCancelled(boolean)}.</div>
* <ul>
* <li>source : this</li>
* <li>component : resize widget</li>
* <li>event : the dom event</li>
* </ul>
* </dd>
*
* <dd><b>ResizeEnd</b> : (source, widget, event)<br>
* <div>Fires after a resize.</div>
* <ul>
* <li>source : this</li>
* <li>widget : resize widget</li>
* <li>event : the dom event</li>
* </ul>
* </dd>
* </dl>
*/
public class Resizable extends BaseObservable {

  protected enum Dir {
    E, N, NE, NW, S, SE, SW, W;
  }

  private class ResizeHandle extends Component {

    public Dir dir;

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

    @Override
    protected void onRender(Element target, int index) {
      super.onRender(target, index);
      setElement(DOM.createDiv(), target, index);
      sinkEvents(Event.MOUSEEVENTS);
    }
  }

  private Dir dir;
  private boolean dynamic;
  private boolean enabled = true;
  private List<ResizeHandle> handleList;
  private String handles;
  private Listener<ComponentEvent> listener;
  private int maxHeight = 2000;
  private int maxWidth = 2000;
  private int minHeight = 50;
  private int minWidth = 50;
  private boolean preserveRatio = false;
  private BaseEventPreview preview;
  private El proxyEl;
  private String proxyStyle = "x-resizable-proxy";
  private BoxComponent resize;
  private boolean resizing;
  private Rectangle startBox;
  private Point startPoint;

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

  /**
   * Creates a new resizable instance.
   *
   * @param resize the resize widget
   * @param handles the resize handle locations separated by spaces
   */
  public Resizable(final BoxComponent resize, String handles) {
    this.resize = resize;
    this.handles = handles;

    listener = new Listener<ComponentEvent>() {
      public void handleEvent(ComponentEvent e) {
        EventType type = e.getType();
        if (type == Events.Render) {
          init();
        } else if (type == Events.Attach) {
          onAttach();
        } else if (type == Events.Detach) {
          onDetach();
        } else if (type == Events.Resize) {
          onComponentResize();
        }
      }
    };

    resize.addListener(Events.Render, listener);
    resize.addListener(Events.Attach, listener);
    resize.addListener(Events.Detach, listener);
    resize.addListener(Events.Resize, listener);

    if (resize.isRendered()) {
      init();
    }

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

  /**
   * Adds a resize listener.
   *
   * @param listener the listener
   */
  public void addResizeListener(ResizeListener listener) {
    addListener(Events.ResizeStart, listener);
    addListener(Events.ResizeEnd, listener);
  }

  /**
   * 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 min height.
   *
   * @return the min height
   */
  public int getMinHeight() {
    return minHeight;
  }

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

  /**
   * Returns the proxy style.
   *
   * @return the proxy style
   */
  public String getProxyStyle() {
    return proxyStyle;
  }

  /**
   * 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() {
    onDetach();

    resize.removeListener(Events.Attach, listener);
    resize.removeListener(Events.Detach, listener);
    resize.removeListener(Events.Render, listener);
    resize.removeListener(Events.Resize, listener);
    if (handleList != null) {
      for (ResizeHandle handle : handleList) {
        DOM.removeChild(resize.getElement(), handle.getElement());
      }
    }
  }

  /**
   * Removes a resize listener.
   *
   * @param listener the listener
   */
  public void removeResizeListener(ResizeListener listener) {
    removeListener(Events.ResizeStart, listener);
    removeListener(Events.ResizeEnd, listener);
  }

  /**
   * 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.el().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 min height (default to 50).
   *
   * @param minHeight the min height
   */
  public void setMinHeight(int minHeight) {
    this.minHeight = minHeight;
  }

  /**
   * Sets the min width (defaults to 50).
   *
   * @param minWidth the min 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;
  }

  /**
   * Sets the style name used for proxy drags (defaults to 'x-resizable-proxy').
   *
   * @param proxyStyle the proxy style
   */
  public void setProxyStyle(String proxyStyle) {
    this.proxyStyle = proxyStyle;
  }

  public void syncHandleHeight() {
    if (resize != null && handleList != null) {
      int height = resize.getHeight(true);
      for (ResizeHandle r : handleList) {
        if (r.dir == Dir.E || r.dir == Dir.W) {
          r.el().setHeight(height);
        }
      }
      resize.el().repaint();
    }
  }

  protected Element createProxy() {
    Element elem = DOM.createDiv();
    El.fly(elem).setStyleName(proxyStyle, true);
    El.fly(elem).disableTextSelection(true);
    return elem;
  }

  protected void init() {
    resize.el().makePositionable();
    if (handleList == null) {
      handleList = new ArrayList<ResizeHandle>();
      if ("all".equals(handles)) {
        handles = "n s e w ne nw se sw";
      }
      String[] temp = handles.split(" ");
      for (int i = 0; i < temp.length; i++) {
        if ("n".equals(temp[i])) {
          create(Dir.N, "north");
        } else if ("nw".equals(temp[i])) {
          create(Dir.NW, "northwest");
        } else if ("e".equals(temp[i])) {
          create(Dir.E, "east");
        } else if ("w".equals(temp[i])) {
          create(Dir.W, "west");
        } else if ("se".equals(temp[i])) {
          create(Dir.SE, "southeast");
        } else if ("s".equals(temp[i])) {
          create(Dir.S, "south");
        } else if ("ne".equals(temp[i])) {
          create(Dir.NE, "northeast");
        } else if ("sw".equals(temp[i])) {
          create(Dir.SW, "southwest");
        }
      }

      preview = new BaseEventPreview() {

        @Override
        public boolean onPreview(PreviewEvent event) {
          event.preventDefault();
          switch (event.getEventTypeInt()) {
            case Event.ONMOUSEMOVE:
              int x = event.getClientX();
              int y = event.getClientY();
              handleMouseMove(x, y);
              break;
            case Event.ONMOUSEUP:
              handleMouseUp(event.getEvent());
              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);
      }
    }
  }

  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, String cls) {
    ResizeHandle rh = new ResizeHandle();
    rh.setStyleName("x-resizable-handle " + "x-resizable-handle-" + cls);
    rh.dir = dir;
    rh.render(resize.getElement());
    handleList.add(rh);
    return rh;
  }

  private void handleMouseDown(Event event, ResizeHandle handle) {
    if (!enabled || !fireEvent(Events.ResizeStart, new ResizeEvent(this, resize, event))) {
      return;
    }

    dir = handle.dir;

    startBox = resize.el().getBounds(false);
    int x = DOM.eventGetClientX(event);
    int y = DOM.eventGetClientY(event);
    startPoint = new Point(x, y);

    resizing = true;

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

      proxyEl.makePositionable(true);
      proxyEl.setLeft(startBox.x).setTop(startBox.y);
      proxyEl.setSize(startBox.width, startBox.height, true);
      proxyEl.setVisible(true);
      proxyEl.updateZIndex(5);
    }

    preview.add();

    Shim.get().cover(false);
    Shim.get().setStyleAttribute("cursor", handle.el().getStyleAttribute("cursor"));
  }

  private void handleMouseMove(int xin, int yin) {
    if (resizing) {
      int x = startBox.x;
      int y = startBox.y;
      float w = startBox.width;
      float h = startBox.height;
      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.x - Math.max(2, eventXY.x));
      int diffY = -(startPoint.y - Math.max(2, eventXY.y));

      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.setSize((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.el().getBounds() : proxyEl.getBounds();

      rect.width = Math.min(rect.width, maxWidth);
      rect.height = Math.min(rect.height, maxHeight);

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

    syncHandleHeight();

    fireEvent(Events.ResizeEnd, new ResizeEvent(this, resize, event));
  }

}
TOP

Related Classes of com.extjs.gxt.ui.client.fx.Resizable

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.