com.google.code.gwt.crop.client.GWTCropper Maven / Gradle / Ivy
Show all versions of gwt-crop Show documentation
/**
* Copyright 2011 ArcBees Inc.
*
* Licensed 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 com.google.code.gwt.crop.client;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Cursor;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.dom.client.Touch;
import com.google.gwt.event.dom.client.LoadEvent;
import com.google.gwt.event.dom.client.LoadHandler;
import com.google.gwt.event.dom.client.MouseDownEvent;
import com.google.gwt.event.dom.client.MouseDownHandler;
import com.google.gwt.event.dom.client.MouseMoveEvent;
import com.google.gwt.event.dom.client.MouseMoveHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseUpEvent;
import com.google.gwt.event.dom.client.MouseUpHandler;
import com.google.gwt.event.dom.client.TouchEndEvent;
import com.google.gwt.event.dom.client.TouchEndHandler;
import com.google.gwt.event.dom.client.TouchMoveEvent;
import com.google.gwt.event.dom.client.TouchMoveHandler;
import com.google.gwt.event.dom.client.TouchStartEvent;
import com.google.gwt.event.dom.client.TouchStartHandler;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Widget;
/**
*
* GWT Cropper
* GWT Cropper - widget that allows you to select some area on top of a picture and retrieve coordinates
* of this selection. It might be useful, if you want to crop a picture.
*
* Example
* Usage example:
*
* final GWTCropper crop = new GWTCropper("url/to/your/uncropped/image.jpg");
* crop.setAspectRatio(1); // square selection (optional)
* panel.add(crop);
*
*
*
* @author [email protected] (Ilja Hämäläinen)
*
* @see GWT Cropper home page
*
*/
public class GWTCropper extends HTMLPanel implements MouseMoveHandler, MouseUpHandler, MouseOutHandler,
TouchMoveHandler, TouchEndHandler {
private final ICropperStyleSource bundleResources = GWT.create(ICropperStyleSource.class);
// canvas sizes
private int nOuterWidth = -1;
private int nOuterHeight = -1;
// selection coordinates
private int nInnerX = -1;
private int nInnerY = -1;
private int nInnerWidth = -1;
private int nInnerHeight = -1;
private boolean isDown = false;
private byte action = Constants.DRAG_NONE;
// initials to provide crop actions
private int initW = -1;
private int initH = -1;
// X and Y coordinates of cursor before dragging
private int initX = -1;
private int initY = -1;
private int offsetX = -1;
private int offsetY = -1;
// instances to canvas and selection area, available for the cropper
private final AbsolutePanelImpl _container;
private AbsolutePanel handlesContainer;
private HTML draggableBackground;
private LoadHandler onCavasLoadHandler;
// settings
private float aspectRatio = 0;
// minimum size of height or width. Just to prevent selection area to be shrunk to a dot
private int MIN_WIDTH = 30;
private int MIN_HEIGHT = 30;
private AbsolutePanelImpl selectionContainer = new AbsolutePanelImpl();
/**
* Constructor with mandatory parameter of image's URL.
*
* @param strImageURL - URL of an uncropped image
*/
public GWTCropper(String strImageURL) {
super("");
bundleResources.css().ensureInjected();
this._container = new AbsolutePanelImpl();
this.addCanvas(strImageURL);
addDomHandler(this, MouseMoveEvent.getType());
addDomHandler(this, MouseUpEvent.getType());
addDomHandler(this, MouseOutEvent.getType());
addDomHandler(this, TouchMoveEvent.getType());
addDomHandler(this, TouchEndEvent.getType());
}
// ---------- Public API ------------------
/**
* Sets the aspect ratio (proportion of width to height) for the selection.
*
* Examples:
*
* - Default is 0 that means the selection can have any shape.
* - Ratio is 1/1=1 that means the selection has a square shape.
* - Ratio 2/1=2 the selection has a rectangular shape where width is twice as longer as height
* - Ratio 1/2=0.5 the selection has a rectangular shape where height is twice as higher as width
*
*
*
* Usage example: You can declare a side proportion in this way:
*
cropper.setAspectRatio( (float) 1/2);
*
*
* @param acpectRatio - float value, proportion width/height
*/
public void setAspectRatio(float acpectRatio) {
this.aspectRatio = acpectRatio;
}
public float getAspectRatio() {
return this.aspectRatio;
}
/**
* Get the X coordinate of the selection top left corner
*
* @return X coordinate
*/
public int getSelectionXCoordinate() {
return this.nInnerX;
}
/**
* Get the Y coordinate of the selection top left corner
*
* @return Y coordinate
*/
public int getSelectionYCoordinate() {
return this.nInnerY;
}
/**
* Get the width of the selection area
*
* @return width in pixels
*/
public int getSelectionWidth() {
return this.nInnerWidth;
}
/**
* Get the height of the selection area
*
* @return height in pixels
*/
public int getSelectionHeight() {
return this.nInnerHeight;
}
/**
* Get the canvas height (original image you wish to crop)
*
* @return height in PX
*/
public int getCanvasHeight() {
return this.nOuterHeight;
}
/**
* Get the canvas width (original image you wish to crop)
*
* @return width in PX
*/
public int getCanvasWidth() {
return this.nOuterWidth;
}
/**
* Shortcut for the {@link com.google.code.gwt.crop.client.GWTCropper#setInitialSelection(int, int, int, int, boolean) setInitialSelection(x, y, width, height, boolean)}
* method. This method sets aspect ratio 0, that means the selection may have any shape.
*
* @param x initial X coordinate. Will be ignored if it is out of a canvas.
* @param y initial Y coordinate. Will be ignored if it is out of a canvas.
* @param width initial selection width in pixels (will be ignored, if bigger, than canvas width)
* @param height initial selection height in pixels (will be ignored if higher, than canvas height)
*/
public void setInitialSelection(int x, int y, int width, int height) {
this.setInitialSelection(x, y, width, height, false);
}
/**
* Sets the initial size and position for the selected area.
*
* Note, that all the incoming data will be validated. Thus, these requirements *must* be fulfilled:
*
* canvas width > (initial selection X + initial selection width)
* canvas height > (initial selection Y + initial selection height)
*
*
* Otherwise default values will be used.
*
*
* @param x initial X coordinate. Will be ignored if it is out of a canvas.
* @param y initial Y coordinate. Will be ignored if it is out of a canvas.
* @param width initial selection width in pixels (will be ignored, if bigger, than canvas width)
* @param height initial selection height in pixels (will be ignored if higher, than canvas height)
* @param shouldKeepAspectRatio if true
, then initial aspect ratio will be used for the selection and it will keep it's shape;
* if false
then the selection could have any shape.
*/
public void setInitialSelection(int x, int y, int width, int height, boolean shouldKeepAspectRatio) {
if (shouldKeepAspectRatio)
this.setAspectRatio((float) width/height);
if (width > MIN_WIDTH)
this.nInnerWidth = width;
if (height > MIN_HEIGHT)
this.nInnerHeight = height;
if (x >= 0)
this.nInnerX = x;
if (y >= 0)
this.nInnerY = y;
}
/**
* Sets the minimal width for the selection (default value is 30px).
* Will be ignored if the value is greater than the initial selection width
*
* @param width in pixels
*/
public void setMinimalWidth(int width) {
if (width > 30) this.MIN_WIDTH = width;
}
/**
* Sets the minimal height for the selection (default value is 30px).
* Will be ignored if the value is greater than the initial selection height.
* @param height in pixels
*/
public void setMinimalHeight(int height) {
if (height > 30) this.MIN_HEIGHT = height;
}
/**
* Adds the {@link LoadEvent} handler to canvas. This event will be fired, when the canvas
* image is loaded and its dimensions are available.
*
* @param handler
*/
public void addCanvasLoadHandler(LoadHandler handler) {
this.onCavasLoadHandler = handler;
}
/**
* Sets the cropper's size.
*
* @param width integer in px
* @param height integer in px
*/
public void setSize(int width, int height) {
// size of parent panel, that holds this widget
super.setSize(width + "px", height + "px");
this.nOuterWidth = width;
this.nOuterHeight = height;
};
/**
* Deprecated. This method sets the size only for parent element, but not for the whole widget.
* Use method {@link com.google.code.gwt.crop.client.GWTCropper#setSize(int, int) setSize(int width, int height)} instead.
*
*
*
* {@inheritDoc}
*
* @see com.google.gwt.user.client.ui.UIObject#setSize(java.lang.String, java.lang.String)
*/
@Override
@Deprecated
public void setSize(String width, String height) {
super.setSize(width, height);
};
// --------- private methods ------------
/**
* Adds a canvas with background image.
*
* @param src - image URL
*/
private void addCanvas(final String src) {
super.setStyleName(bundleResources.css().base());
final Image image = new Image(src);
image.setStyleName(bundleResources.css().imageCanvas());
image.addLoadHandler(new LoadHandler() {
public void onLoad(LoadEvent event) {
// get original image size
if (nOuterWidth == -1) nOuterWidth = image.getWidth();
if (nOuterHeight == -1) nOuterHeight = image.getHeight();
DOM.setElementProperty(image.getElement(), "width", nOuterWidth + "");
DOM.setElementProperty(image.getElement(), "height", nOuterHeight + "");
_container.setWidth(nOuterWidth + "px");
_container.setHeight(nOuterHeight + "px");
addSelection(src);
if (null != onCavasLoadHandler)
onCavasLoadHandler.onLoad(event);
}
});
this._container.add(image, 0, 0);
this.add(this._container);
}
/**
* Adds initial selection
*
*/
private void addSelection(final String src) {
selectionContainer.addStyleName(this.bundleResources.css().selection());
this.validateInitialData();
selectionContainer.setWidth(this.nInnerWidth + "px");
selectionContainer.setHeight(this.nInnerHeight + "px");
// add background image for the selection
Image imgSelectionBg = new Image(src);
if (nOuterWidth != -1) DOM.setElementProperty(imgSelectionBg.getElement(), "width", nOuterWidth + "");
if (nOuterHeight != -1) DOM.setElementProperty(imgSelectionBg.getElement(), "height", nOuterHeight + "");
selectionContainer.add(imgSelectionBg, -this.nInnerX - 1, -this.nInnerY - 1);
this._container.add(selectionContainer, this.nInnerX, this.nInnerY);
this.buildSelectionArea();
this._container.add(this.handlesContainer, this.nInnerX, this.nInnerY);
}
/**
* Validates all initial data. This method is called after the canvas image becomes loaded and we know, what are its actual
* dimensions. If any of data are incorrect, then set the default values.
*/
private void validateInitialData() {
final boolean isDefaultWidth = this.nInnerX == -1 && this.nInnerWidth == -1;
final boolean isInvalidWidthAndX = this.nOuterWidth < (this.nInnerX + this.nInnerWidth);
if (isDefaultWidth || isInvalidWidthAndX) {
this.nInnerX = (int) (nOuterWidth * 0.2);
this.nInnerWidth = (int) (nOuterWidth * 0.2);
}
// minimal width couldn't be more, than initial selection.
if (this.MIN_WIDTH > this.nInnerWidth) this.MIN_WIDTH = 30;
final boolean isDefaultHeight = this.nInnerY == -1 && this.nInnerHeight == -1;
final boolean isInvalidHeightY = this.nOuterHeight < (this.nInnerY + this.nInnerHeight);
if (isDefaultHeight || isInvalidHeightY) {
this.nInnerY = (int) (nOuterHeight * 0.2);
this.nInnerHeight = (int) ( (this.aspectRatio == 0) ? (nOuterHeight * 0.2) : (nInnerWidth / aspectRatio) );
}
// minimal height couldn't be more, than initial selectin.
if (this.MIN_HEIGHT > this.nInnerHeight) this.MIN_HEIGHT = 30;
}
/**
* Add special handles. User can drag these handle and change form of the selected area
*
* @return container with handles and needed attached event listeners
*/
private AbsolutePanel buildSelectionArea() {
// add selection handles
this.handlesContainer = new AbsolutePanel();
this.handlesContainer.setWidth(this.nInnerWidth + "px");
this.handlesContainer.setHeight(this.nInnerHeight + "px");
this.handlesContainer.setStyleName(this.bundleResources.css().handlesContainer());
this.handlesContainer.getElement().getStyle().setOverflow(Overflow.VISIBLE);
// append background
this.draggableBackground = this.appendDraggableBackground();
// find the center of draggable handle to make an offset for the positioning
final int h = this.bundleResources.css().handleSize() / 2;
// append top left corner handler
this.appendHandle(Cursor.NW_RESIZE, Constants.DRAG_TOP_LEFT_CORNER, -h, 0, 0, -h);
// append top right corner handler
this.appendHandle(Cursor.NE_RESIZE, Constants.DRAG_TOP_RIGHT_CORNER, -h, -h, 0, 0);
// append bottom left corner handler
this.appendHandle(Cursor.SW_RESIZE, Constants.DRAG_BOTTOM_LEFT_CORNER, 0, 0, -h, -h);
// append bottom right corner handler
this.appendHandle(Cursor.SE_RESIZE, Constants.DRAG_BOTTOM_RIGHT_CORNER, 0, -h, -h, 0);
return handlesContainer;
}
/**
* Creates small draggable selection handle and appends it to the corner of selection area. User can drag
* this handle and change shape of the selection area
*
* @param cursor cursor type for the CSS
* @param actionType action type for the event processor
* @param top top value in PX
* @param right right value in PX
* @param bottom bottom value in PX
* @param left left value in PX
*/
private void appendHandle(Cursor cursor, final byte actionType, int top, int right, int bottom, int left) {
HTML handle = new HTML();
handle.setStyleName(this.bundleResources.css().handle());
handle.getElement().getStyle().setCursor(cursor);
handle.addMouseDownHandler(new MouseDownHandler() {
public void onMouseDown(MouseDownEvent event) {
isDown = true;
action = actionType;
}
});
handle.addTouchStartHandler(new TouchStartHandler() {
public void onTouchStart(TouchStartEvent event) {
isDown = true;
action = actionType;
}
});
if (top != 0) handle.getElement().getStyle().setTop(top, Unit.PX);
if (right != 0) handle.getElement().getStyle().setRight(right, Unit.PX);
if (bottom != 0) handle.getElement().getStyle().setBottom(bottom, Unit.PX);
if (left != 0) handle.getElement().getStyle().setLeft(left, Unit.PX);
this.handlesContainer.add(handle);
}
/**
* Append draggable selection background
*
* @param sc - container of selection
* @param hc - container of handles
*/
private HTML appendDraggableBackground() {
final HTML backgroundHandle = new HTML();
backgroundHandle.setWidth(this.nInnerWidth + "px");
backgroundHandle.setHeight(this.nInnerHeight + "px");
backgroundHandle.getElement().getStyle().setCursor(Cursor.MOVE);
backgroundHandle.addStyleName(this.bundleResources.css().selectionDraggableBackground());
backgroundHandle.addMouseDownHandler(new MouseDownHandler() {
public void onMouseDown(MouseDownEvent event) {
isDown = true;
action = Constants.DRAG_BACKGROUND;
}
});
backgroundHandle.addTouchStartHandler(new TouchStartHandler() {
public void onTouchStart(TouchStartEvent event) {
isDown = true;
action = Constants.DRAG_BACKGROUND;
}
});
this.handlesContainer.add(backgroundHandle, 0, 0);
return backgroundHandle;
}
/**
* provides dragging action
*
* @param cursorX - cursor X-position relatively the canvas
* @param cursorY - cursor Y-position relatively the canvas
*/
private void provideDragging(int cursorX, int cursorY) {
Element elH = null; // handle's container
Element elS = null; // selection's container
Element elImg = null;
int futureWidth = 0;
int futureHeight = 0;
switch (this.action) {
case Constants.DRAG_BACKGROUND:
if (offsetX == -1) {
offsetX = cursorX - _container.getWidgetLeft(this.handlesContainer);
}
if (offsetY == -1) {
offsetY = cursorY - _container.getWidgetTop(this.handlesContainer);
}
elH = this.handlesContainer.getElement();
this.nInnerX = cursorX - offsetX;
this.nInnerY = cursorY - offsetY;
// don't drag selection out of the canvas borders
if (this.nInnerX < 0) this.nInnerX = 0;
if (this.nInnerY < 0) this.nInnerY = 0;
if (this.nInnerX + this.nInnerWidth > this.nOuterWidth) this.nInnerX = this.nOuterWidth - this.nInnerWidth;
if (this.nInnerY + this.nInnerHeight > this.nOuterHeight) this.nInnerY = this.nOuterHeight - this.nInnerHeight;
elH.getStyle().setLeft(this.nInnerX, Unit.PX);
elH.getStyle().setTop(this.nInnerY, Unit.PX);
elS = this.selectionContainer.getElement();
elS.getStyle().setLeft(this.nInnerX, Unit.PX);
elS.getStyle().setTop(this.nInnerY, Unit.PX);
elImg = ((Image) this.selectionContainer.getWidget(0)).getElement();
elImg.getStyle().setLeft(-this.nInnerX - 1, Unit.PX);
elImg.getStyle().setTop(-this.nInnerY - 1, Unit.PX);
break;
case Constants.DRAG_TOP_LEFT_CORNER:
if (initX == -1) {
initX = _container.getWidgetLeft(this.handlesContainer);
initW = nInnerWidth;
}
if (initY == -1) {
initY = _container.getWidgetTop(this.handlesContainer);
initH = nInnerHeight;
}
futureWidth = initW + (initX - cursorX);
futureHeight = initH + (initY - cursorY);
if (futureWidth < this.MIN_WIDTH || futureHeight < this.MIN_HEIGHT) {
return;
}
this.nInnerWidth = futureWidth;
this.nInnerHeight = futureHeight;
this.nInnerX = cursorX;
this.nInnerY = cursorY;
// compensation for specified aspect ratio
if (this.aspectRatio != 0) {
if (abs(this.initX - this.nInnerX) > abs(this.initY - this.nInnerY)) {
int newHeight = (int) (this.nInnerWidth / this.aspectRatio);
this.nInnerY -= newHeight - this.nInnerHeight;
// to prevent resizing out of the canvas on the Y axes
if (this.nInnerY <= 0) {
this.nInnerY = 0;
newHeight = this.initY + this.initH;
this.nInnerWidth = (int) (newHeight * this.aspectRatio);
this.nInnerX = this.initX - (int) (this.initY * this.aspectRatio);
}
this.nInnerHeight = newHeight;
}
else {
int newWidth = (int) (this.nInnerHeight * this.aspectRatio);
this.nInnerX -= newWidth - this.nInnerWidth;
// to prevent resizing out of the canvas on the X axis
if (this.nInnerX < 0) {
this.nInnerX = 0;
newWidth = this.initX + this.initW;
this.nInnerHeight = (int) (newWidth / this.aspectRatio);
this.nInnerY = this.initY - (int) (this.initX / this.aspectRatio);
}
this.nInnerWidth = newWidth;
}
}
elH = this.handlesContainer.getElement();
elH.getStyle().setLeft(this.nInnerX, Unit.PX);
elH.getStyle().setTop(this.nInnerY, Unit.PX);
elH.getStyle().setWidth(nInnerWidth, Unit.PX);
elH.getStyle().setHeight(nInnerHeight, Unit.PX);
elS = this.selectionContainer.getElement();
elS.getStyle().setLeft(this.nInnerX, Unit.PX);
elS.getStyle().setTop(this.nInnerY, Unit.PX);
elS.getStyle().setWidth(nInnerWidth, Unit.PX);
elS.getStyle().setHeight(nInnerHeight, Unit.PX);
elImg = ((Image) this.selectionContainer.getWidget(0)).getElement();
elImg.getStyle().setLeft(-this.nInnerX - 1, Unit.PX);
elImg.getStyle().setTop(-this.nInnerY - 1, Unit.PX);
Element el3 = this.draggableBackground.getElement();
el3.getStyle().setWidth(nInnerWidth, Unit.PX);
el3.getStyle().setHeight(nInnerHeight, Unit.PX);
break;
case Constants.DRAG_TOP_RIGHT_CORNER:
if (initX == -1) {
initX = _container.getWidgetLeft(this.handlesContainer) + nInnerWidth;
initW = nInnerWidth;
}
if (initY == -1) {
initY = _container.getWidgetTop(this.handlesContainer);
initH = nInnerHeight;
}
futureWidth = initW + (cursorX - initX);
futureHeight = initH + (initY - cursorY);
if (futureWidth < this.MIN_WIDTH || futureHeight < this.MIN_HEIGHT) {
return;
}
nInnerWidth = futureWidth;
nInnerHeight = futureHeight;
// compensation for specified aspect ratio
if (this.aspectRatio != 0) {
if (abs(initX - cursorX) > abs(initY - cursorY)) {
// move cursor right, top side has been adjusted automatically
int newHeight = (int) (nInnerWidth / this.aspectRatio);
cursorY -= newHeight - nInnerHeight;
// to prevent resizing out of the canvas on the Y axes
if (cursorY <= 0) {
cursorY = 0;
newHeight = this.initY + this.initH;
this.nInnerWidth = (int) (newHeight * this.aspectRatio);
}
nInnerHeight = newHeight;
}
else {
// move cursor up, right side has been adjusted automatically
nInnerWidth = (int) (nInnerHeight * this.aspectRatio);
// to prevent resizing out of the canvas on the X axis
if ((this.nInnerWidth + this.nInnerX) >= this.nOuterWidth) {
this.nInnerWidth = this.nOuterWidth - this.nInnerX;
this.nInnerHeight = (int) (this.nInnerWidth / this.aspectRatio);
this.nInnerY = this.initY - (int) ((this.nOuterWidth - this.nInnerX - this.initW) / this.aspectRatio);
cursorY = this.nInnerY;
}
}
}
this.nInnerY = cursorY;
elH = this.handlesContainer.getElement();
elH.getStyle().setTop(cursorY, Unit.PX);
elH.getStyle().setWidth(nInnerWidth, Unit.PX);
elH.getStyle().setHeight(nInnerHeight, Unit.PX);
elS = this.selectionContainer.getElement();
elS.getStyle().setTop(cursorY, Unit.PX);
elS.getStyle().setWidth(nInnerWidth, Unit.PX);
elS.getStyle().setHeight(nInnerHeight, Unit.PX);
elImg = ((Image) this.selectionContainer.getWidget(0)).getElement();
elImg.getStyle().setTop(-cursorY - 1, Unit.PX);
el3 = this.draggableBackground.getElement();
el3.getStyle().setWidth(nInnerWidth, Unit.PX);
el3.getStyle().setHeight(nInnerHeight, Unit.PX);
break;
case Constants.DRAG_BOTTOM_LEFT_CORNER:
if (initX == -1) {
initX = _container.getWidgetLeft(this.handlesContainer);
initW = nInnerWidth;
}
if (initY == -1) {
initY = _container.getWidgetTop(this.handlesContainer) + nInnerHeight;
initH = nInnerHeight;
}
futureWidth = initW + (initX - cursorX);
futureHeight = initH + (cursorY - initY);
if (futureWidth < this.MIN_WIDTH || futureHeight < this.MIN_HEIGHT) {
return;
}
nInnerWidth = futureWidth;
nInnerHeight = futureHeight;
// compensation for specified aspect ratio
if (this.aspectRatio != 0) {
if (abs(initX - cursorX) > abs(initY - cursorY)) {
// cursor goes left, bottom side goes down...
nInnerHeight = (int) (nInnerWidth / this.aspectRatio);
// to prevent resizing out of the canvas on the Y axis
if ((this.nInnerHeight + this.nInnerY) >= this.nOuterHeight) {
this.nInnerHeight = this.nOuterHeight - this.nInnerY;
this.nInnerWidth = (int) (this.nInnerHeight * this.aspectRatio);
this.nInnerY = this.nOuterHeight - this.nInnerHeight;
cursorX = this.initX - (int) ((this.nOuterHeight - this.initY) * this.aspectRatio);
}
}
else {
// cursor goes down, left side goes to left
int newWidth = (int) (nInnerHeight * this.aspectRatio);
cursorX -= newWidth - nInnerWidth;
// to prevent resizing out of the canvas on the X axis
if (cursorX <= 0) {
newWidth = this.nInnerWidth + this.initX;
this.nInnerHeight = (int) (newWidth / this.aspectRatio);
cursorX = 0;
}
nInnerWidth = newWidth;
}
}
this.nInnerX = cursorX;
elH = this.handlesContainer.getElement();
elH.getStyle().setLeft(cursorX, Unit.PX);
elH.getStyle().setWidth(nInnerWidth, Unit.PX);
elH.getStyle().setHeight(nInnerHeight, Unit.PX);
elS = this.selectionContainer.getElement();
elS.getStyle().setLeft(cursorX, Unit.PX);
elS.getStyle().setWidth(nInnerWidth, Unit.PX);
elS.getStyle().setHeight(nInnerHeight, Unit.PX);
elImg = ((Image) this.selectionContainer.getWidget(0)).getElement();
elImg.getStyle().setLeft(-cursorX - 1, Unit.PX);
el3 = this.draggableBackground.getElement();
el3.getStyle().setWidth(nInnerWidth, Unit.PX);
el3.getStyle().setHeight(nInnerHeight, Unit.PX);
break;
case Constants.DRAG_BOTTOM_RIGHT_CORNER:
if (initX == -1) {
initX = _container.getWidgetLeft(this.handlesContainer) + nInnerWidth;
initW = nInnerWidth;
}
if (initY == -1) {
initY = _container.getWidgetTop(this.handlesContainer) + nInnerHeight;
initH = nInnerHeight;
}
futureWidth = initW + (cursorX - initX);
futureHeight = initH + (cursorY - initY);
if (futureWidth < this.MIN_WIDTH || futureHeight < this.MIN_HEIGHT) {
return;
}
nInnerWidth = futureWidth;
nInnerHeight = futureHeight;
// compensation for specified aspect ratio
if (this.aspectRatio != 0) {
if (abs(initX - cursorX) > abs(initY - cursorY)) {
// cursor goes right, bottom side goes down...
nInnerHeight = (int) (nInnerWidth / this.aspectRatio);
// to prevent resizing out of the canvas on the Y axis
if ((this.nInnerHeight + this.nInnerY) >= this.nOuterHeight) {
this.nInnerHeight = this.nOuterHeight - this.nInnerY;
this.nInnerWidth = (int) (this.nInnerHeight * this.aspectRatio);
this.nInnerY = this.nOuterHeight - this.nInnerHeight;
cursorX = this.nOuterWidth;
}
}
else {
// cursor goes down, right side goes to right
nInnerWidth = (int) (nInnerHeight * this.aspectRatio);
// to prevent resizing out of the canvas on the X axis
if (this.nInnerWidth + this.nInnerX >= this.nOuterWidth) {
this.nInnerWidth = this.nOuterWidth - this.nInnerX;
this.nInnerHeight = (int) (this.nInnerWidth / this.aspectRatio);
cursorX = this.nOuterHeight;
}
}
}
elH = this.handlesContainer.getElement();
elH.getStyle().setWidth(nInnerWidth, Unit.PX);
elH.getStyle().setHeight(nInnerHeight, Unit.PX);
elS = this.selectionContainer.getElement();
elS.getStyle().setWidth(nInnerWidth, Unit.PX);
elS.getStyle().setHeight(nInnerHeight, Unit.PX);
el3 = this.draggableBackground.getElement();
el3.getStyle().setWidth(nInnerWidth, Unit.PX);
el3.getStyle().setHeight(nInnerHeight, Unit.PX);
break;
default:
break;
}
}
/**
* Resets all initial values.
*
*/
private void reset() {
this.initX = -1;
this.initY = -1;
this.initW = -1;
this.initH = -1;
this.offsetX = -1;
this.offsetY = -1;
this.action = Constants.DRAG_NONE;
}
/**
* Returns absolute value
*
* @param value
* @return absolute value
*/
private int abs(int value) {
return value >= 0 ? value : -value;
}
// DOM HANDLERS
/**
* {@inheritDoc}
*/
public void onMouseMove(MouseMoveEvent event) {
if (this.isDown) {
this.provideDragging(event.getRelativeX(this._container.getElement()),
event.getRelativeY(this._container.getElement()));
}
}
/**
* {@inheritDoc}
*/
public void onTouchMove(TouchMoveEvent event) {
if (this.isDown) {
JsArray touches = event.getTouches();
if (touches.length() > 0) {
int x = touches.get(0).getRelativeX(this._container.getElement());
int y = touches.get(0).getRelativeY(this._container.getElement());
if (x < 0 || y < 0 || x > nOuterWidth || y > nOuterHeight) {
/*
* There is no such method as "onMouseOut" for touching, so we can't rely
* on event handler. We should process manually these cases, when finger (i.e. cursor) is
* out of the cropper area.
*
* If user moves his finger out of the canvas, then "stick" the selection to the canvas edges
*/
if (x < 0) x = 0;
if (x > nOuterWidth) x = nOuterWidth;
if (y < 0) y = 0;
if (y > nOuterHeight) y = nOuterHeight;
}
this.provideDragging(x, y);
}
}
}
/**
* Resets the dragging state. After this method, the cropper
* presumes that the dragging action is finished.
*/
private void resetDraggingState() {
if (this.isDown) {
this.isDown = false;
this.reset();
}
}
/**
* {@inheritDoc}
*/
public void onMouseUp(MouseUpEvent event) {
this.resetDraggingState();
}
/**
* {@inheritDoc}
*/
public void onMouseOut(MouseOutEvent event) {
/*
* if cursor is out of canvas and the mouse is clicked,
* then we want to "unclick" the mouse button programmatically.
* Otherwise the selection would become "sticky".
*/
this.resetDraggingState();
}
/**
* {@inheritDoc}
*/
public void onTouchEnd(TouchEndEvent event) {
this.resetDraggingState();
}
/**
* Absolute panel with one overrided method, that prevents a static positioning
* when a widget has (-1, -1) coordinates.
*
* @author [email protected] (Ilja Hämäläinen)
*
*/
private static class AbsolutePanelImpl extends AbsolutePanel {
/**
* Original method causes the child widget to be positioned statically,
* if the left and top are (-1, -1). In our case the background could be positioned
* (-1,-1), when the selected area is placed on the top left corner.
*
* @See com.google.gwt.user.client.ui.AbsolutePanel.setWidgetPosition(Widget, int, int)
*/
@Override
protected void setWidgetPositionImpl(Widget w, int left, int top) {
com.google.gwt.user.client.Element h = w.getElement();
DOM.setStyleAttribute(h, "position", "absolute");
DOM.setStyleAttribute(h, "left", left + "px");
DOM.setStyleAttribute(h, "top", top + "px");
}
}
}