blob: 11d10c3c226031fc02ad173b7544c903aae5ad99 [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Florian Pirchner <florian.pirchner@gmail.com> - Initial implementation
*/
package org.eclipse.osbp.vaadin.addons.absolutelayout.client;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.eclipse.osbp.vaadin.addons.absolutelayout.client.OAlignmentOverlay.AlignmentsLocation;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ui.dd.VDragEvent;
import fi.jasoft.dragdroplayouts.client.ui.Constants;
// TODO: Auto-generated Javadoc
/**
* This class handles alignments for drag and drop and resize operations.
*/
public class OAlignmentManager {
/** The manager. */
private static OAlignmentManager manager;
/** The alignments. */
protected Set<OAlignmentOverlay> alignments = new HashSet<>();
/** The snap. */
protected int snap = 4;
/** The layout. */
private OAbsoluteLayoutWidget layout;
/** The snap info. */
private OAlignmentSnapInfo snapInfo;
/** The resizing widget. */
private OAbsoluteLayoutWidget.ResizableAbsoluteWrapper resizingWidget;
/** The resizing overlay. */
private Element resizingOverlay;
/** The resize location. */
private OResizeLocation resizeLocation;
/** The use alignments. */
private boolean useAlignments;
/**
* Instantiates a new o alignment manager.
*/
private OAlignmentManager() {
}
/**
* Gets the.
*
* @return the o alignment manager
*/
public static OAlignmentManager get() {
if (manager == null) {
manager = new OAlignmentManager();
}
return manager;
}
/**
* Start.
*
* @param layout the layout
*/
protected void start(OAbsoluteLayoutWidget layout) {
this.layout = layout;
}
/**
* Start drag and drop.
*
* @param layout the layout
*/
public void startDragAndDrop(OAbsoluteLayoutWidget layout) {
start(layout);
}
/**
* Start resize.
*
* @param layout the layout
* @param resizingWidget the resizing widget
* @param resizeLocation the resize location
* @param resizingOverlay the resizing overlay
*/
public void startResize(OAbsoluteLayoutWidget layout, OAbsoluteLayoutWidget.ResizableAbsoluteWrapper resizingWidget,
OResizeLocation resizeLocation, Element resizingOverlay) {
this.layout = layout;
this.resizingWidget = resizingWidget;
this.resizeLocation = resizeLocation;
this.resizingOverlay = resizingOverlay;
start(layout);
}
/**
* Checks if is active.
*
* @return true, if is active
*/
public boolean isActive() {
return layout != null;
}
/**
* Stop.
*/
public void stop() {
resetAlignments();
layout = null;
alignments.clear();
resizeLocation = null;
resizingWidget = null;
snapInfo = null;
}
/**
* Update alignments for drop.
*
* @param drag the drag
*/
public void updateAlignmentsForDrop(final VDragEvent drag) {
if (!useAlignments) {
return;
}
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
resetSnap();
resetAlignments();
Map<String, Object> dropDetail = drag.getDropDetails();
if (!dropDetail.containsKey(Constants.DROP_DETAIL_RELATIVE_TOP)) {
return;
}
int top = (int) dropDetail.get(Constants.DROP_DETAIL_RELATIVE_TOP);
int left = (int) dropDetail.get(Constants.DROP_DETAIL_RELATIVE_LEFT);
int width = (int) dropDetail.get(Constants.DROP_DETAIL_COMPONENT_WIDTH);
int height = (int) dropDetail.get(Constants.DROP_DETAIL_COMPONENT_HEIGHT);
int topEdgeY = top;
int leftEdgeX = left;
int rightEdgeX = left + width;
int bottomEdgeY = top + height;
int snapCount = 0;
for (Widget child : layout.getChildren()) {
int childTop = child.getElement().getOffsetTop();
int childLeft = child.getElement().getOffsetLeft();
int childWidth = child.getElement().getOffsetWidth();
int childHeight = child.getElement().getOffsetHeight();
int childTopEdgeY = childTop;
int childLeftEdgeX = childLeft;
int childRightEdgeX = childLeft + childWidth;
int childBottomEdgeY = childTop + childHeight;
boolean isDraggedLeftOf = false;
boolean isDraggedToOf = false;
if (left < child.getAbsoluteLeft()) {
isDraggedLeftOf = true;
}
if (top < child.getAbsoluteTop()) {
isDraggedToOf = true;
}
AlignmentsLocation location = null;
// paired sides -> left to left,...
int top_top_EdgeYDelta = topEdgeY - childTopEdgeY;
int bottom_bottom_EdgeYDelta = bottomEdgeY - childBottomEdgeY;
int left_left_EdgeXDelta = leftEdgeX - childLeftEdgeX;
int right_right_EdgeXDelta = rightEdgeX - childRightEdgeX;
int top_bottom_EdgeYDelta = topEdgeY - childBottomEdgeY;
int bottom_top_EdgeYDelta = bottomEdgeY - childTopEdgeY;
int left_right_EdgeXDelta = leftEdgeX - childRightEdgeX;
int right_left_EdgeXDelta = rightEdgeX - childLeftEdgeX;
OAlignmentSnapInfo snapInfo = null;
if (isInsideSnapBounds(top_top_EdgeYDelta)) {
snapInfo = new OAlignmentSnapInfo(0, top_top_EdgeYDelta);
location = isDraggedLeftOf ? AlignmentsLocation.TOP_EDGE_TOLEFT
: AlignmentsLocation.TOP_EDGE_TORIGHT;
} else if (isInsideSnapBounds(left_left_EdgeXDelta)) {
snapInfo = new OAlignmentSnapInfo(left_left_EdgeXDelta, 0);
location = isDraggedToOf ? AlignmentsLocation.LEFT_EDGE_TOTOP
: AlignmentsLocation.LEFT_EDGE_TOBOTTOM;
} else if (isInsideSnapBounds(right_right_EdgeXDelta)) {
snapInfo = new OAlignmentSnapInfo(right_right_EdgeXDelta, 0);
location = isDraggedToOf ? AlignmentsLocation.RIGHT_EDGE_TOTOP
: AlignmentsLocation.RIGHT_EDGE_TOBOTTOM;
} else if (isInsideSnapBounds(bottom_bottom_EdgeYDelta)) {
snapInfo = new OAlignmentSnapInfo(0, bottom_bottom_EdgeYDelta);
location = isDraggedLeftOf ? AlignmentsLocation.BOTTOM_EDGE_TOLEFT
: AlignmentsLocation.BOTTOM_EDGE_TORIGHT;
// unpaired sides
} else if (isInsideSnapBounds(top_bottom_EdgeYDelta)) {
snapInfo = new OAlignmentSnapInfo(0, top_bottom_EdgeYDelta);
location = isDraggedLeftOf ? AlignmentsLocation.BOTTOM_EDGE_TOLEFT
: AlignmentsLocation.BOTTOM_EDGE_TORIGHT;
} else if (isInsideSnapBounds(left_right_EdgeXDelta)) {
snapInfo = new OAlignmentSnapInfo(left_right_EdgeXDelta, 0);
location = isDraggedToOf ? AlignmentsLocation.RIGHT_EDGE_TOTOP
: AlignmentsLocation.RIGHT_EDGE_TOBOTTOM;
} else if (isInsideSnapBounds(right_left_EdgeXDelta)) {
snapInfo = new OAlignmentSnapInfo(right_left_EdgeXDelta, 0);
location = isDraggedToOf ? AlignmentsLocation.LEFT_EDGE_TOTOP
: AlignmentsLocation.LEFT_EDGE_TOBOTTOM;
} else if (isInsideSnapBounds(bottom_top_EdgeYDelta)) {
snapInfo = new OAlignmentSnapInfo(0, bottom_top_EdgeYDelta);
location = isDraggedLeftOf ? AlignmentsLocation.TOP_EDGE_TOLEFT
: AlignmentsLocation.TOP_EDGE_TORIGHT;
}
if (location != null) {
snapCount++;
if (isFirstSnap(snapCount)) {
registerSnap(snapInfo);
}
OAlignmentOverlay overlay = new OAlignmentOverlay(child, drag.getDragImage(), location,
snapInfo, false, snapCount == 1);
alignments.add(overlay);
overlay.show();
}
}
}
});
}
/**
* Update alignments for resize.
*
* @param draggedElement the resize preview element
* @param dragOverlayElement the drag overlay element
*/
public void updateAlignmentsForResize(final Element draggedElement, final Element dragOverlayElement) {
if (dragOverlayElement == null || !useAlignments) {
return;
}
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
resetSnap();
resetAlignments();
int top = (int) dragOverlayElement.getAbsoluteTop();
int left = (int) dragOverlayElement.getAbsoluteLeft();
int width = (int) dragOverlayElement.getOffsetWidth();
int height = (int) dragOverlayElement.getOffsetHeight();
int topEdgeY = top;
int leftEdgeX = left;
int rightEdgeX = left + width;
int bottomEdgeY = top + height;
int snapCount = 0;
for (Widget child : layout.getChildren()) {
if (child == getResizingWidget()) {
continue;
}
int childTop = child.getElement().getAbsoluteTop();
int childLeft = child.getElement().getAbsoluteLeft();
int childWidth = child.getElement().getOffsetWidth();
int childHeight = child.getElement().getOffsetHeight();
int childTopEdgeY = childTop;
int childLeftEdgeX = childLeft;
int childRightEdgeX = childLeft + childWidth;
int childBottomEdgeY = childTop + childHeight;
boolean isDraggedLeftOf = false;
boolean isDraggedToOf = false;
if (left < child.getAbsoluteLeft()) {
isDraggedLeftOf = true;
}
if (top < child.getAbsoluteTop()) {
isDraggedToOf = true;
}
AlignmentsLocation location = null;
// paired sides -> left to left,...
int top_top_EdgeYDelta = topEdgeY - childTopEdgeY;
int bottom_bottom_EdgeYDelta = bottomEdgeY - childBottomEdgeY;
int left_left_EdgeXDelta = leftEdgeX - childLeftEdgeX;
int right_right_EdgeXDelta = rightEdgeX - childRightEdgeX;
int top_bottom_EdgeYDelta = topEdgeY - childBottomEdgeY;
int bottom_top_EdgeYDelta = bottomEdgeY - childTopEdgeY;
int left_right_EdgeXDelta = leftEdgeX - childRightEdgeX;
int right_left_EdgeXDelta = rightEdgeX - childLeftEdgeX;
OAlignmentSnapInfo snapInfo = null;
OResizeLocation resizeLoc = getResizeLocation();
if (resizeLoc.isTop() && isInsideSnapBounds(top_top_EdgeYDelta)) {
snapInfo = new OAlignmentSnapInfo(0, top_top_EdgeYDelta, resizeLoc);
location = isDraggedLeftOf ? AlignmentsLocation.TOP_EDGE_TOLEFT
: AlignmentsLocation.TOP_EDGE_TORIGHT;
} else if (resizeLoc.isLeft() && isInsideSnapBounds(left_left_EdgeXDelta)) {
snapInfo = new OAlignmentSnapInfo(left_left_EdgeXDelta, 0, resizeLoc);
location = isDraggedToOf ? AlignmentsLocation.LEFT_EDGE_TOTOP
: AlignmentsLocation.LEFT_EDGE_TOBOTTOM;
} else if (resizeLoc.isRight() && isInsideSnapBounds(right_right_EdgeXDelta)) {
snapInfo = new OAlignmentSnapInfo(right_right_EdgeXDelta, 0, resizeLoc);
location = isDraggedToOf ? AlignmentsLocation.RIGHT_EDGE_TOTOP
: AlignmentsLocation.RIGHT_EDGE_TOBOTTOM;
} else if (resizeLoc.isBottom() && isInsideSnapBounds(bottom_bottom_EdgeYDelta)) {
snapInfo = new OAlignmentSnapInfo(0, bottom_bottom_EdgeYDelta, resizeLoc);
location = isDraggedLeftOf ? AlignmentsLocation.BOTTOM_EDGE_TOLEFT
: AlignmentsLocation.BOTTOM_EDGE_TORIGHT;
// unpaired sides
} else if (resizeLoc.isTop() && isInsideSnapBounds(top_bottom_EdgeYDelta)) {
snapInfo = new OAlignmentSnapInfo(0, top_bottom_EdgeYDelta, resizeLoc);
location = isDraggedLeftOf ? AlignmentsLocation.BOTTOM_EDGE_TOLEFT
: AlignmentsLocation.BOTTOM_EDGE_TORIGHT;
} else if (resizeLoc.isLeft() && isInsideSnapBounds(left_right_EdgeXDelta)) {
snapInfo = new OAlignmentSnapInfo(left_right_EdgeXDelta, 0, resizeLoc);
location = isDraggedToOf ? AlignmentsLocation.RIGHT_EDGE_TOTOP
: AlignmentsLocation.RIGHT_EDGE_TOBOTTOM;
} else if (resizeLoc.isRight() && isInsideSnapBounds(right_left_EdgeXDelta)) {
snapInfo = new OAlignmentSnapInfo(right_left_EdgeXDelta, 0, resizeLoc);
location = isDraggedToOf ? AlignmentsLocation.LEFT_EDGE_TOTOP
: AlignmentsLocation.LEFT_EDGE_TOBOTTOM;
} else if (resizeLoc.isBottom() && isInsideSnapBounds(bottom_top_EdgeYDelta)) {
snapInfo = new OAlignmentSnapInfo(0, bottom_top_EdgeYDelta, resizeLoc);
location = isDraggedLeftOf ? AlignmentsLocation.TOP_EDGE_TOLEFT
: AlignmentsLocation.TOP_EDGE_TORIGHT;
}
if (location != null) {
snapCount++;
if (isFirstSnap(snapCount)) {
registerSnap(snapInfo);
}
OAlignmentOverlay overlay = new OAlignmentOverlay(child, dragOverlayElement, location, snapInfo,
true, isFirstSnap(snapCount));
alignments.add(overlay);
overlay.show();
}
}
}
});
}
/**
* Checks if is first snap.
*
* @param snapCount the snap count
* @return true, if is first snap
*/
private boolean isFirstSnap(int snapCount) {
return snapCount == 1;
}
/**
* Resets all alignments.
*/
protected void resetAlignments() {
for (OAlignmentOverlay alignment : alignments) {
alignment.hide();
}
alignments.clear();
}
/**
* Checks if is inside snap bounds.
*
* @param value the value
* @return true, if is inside snap bounds
*/
protected boolean isInsideSnapBounds(int value) {
int snap = getSnapFromCSS();
return value <= snap && value >= -snap;
}
/**
* Reads the snap in pixels from the elements style.
*
* @return the snap from css
*/
protected int getSnapFromCSS() {
String snapStr = layout.getElement().getStyle().getProperty(IOConstants.CSS_SNAP_PROPERTY);
if (snapStr != null) {
return Integer.valueOf(snapStr);
} else {
return snap;
}
}
/**
* Registers the current snap. Only one snap definition can be active at a
* time.
*
* @param snapInfo the snap info
*/
protected void registerSnap(OAlignmentSnapInfo snapInfo) {
this.snapInfo = snapInfo;
}
/**
* Resets the snap info.
*/
protected void resetSnap() {
this.snapInfo = null;
}
/**
* Gets the snap.
*
* @return the snap
*/
protected OAlignmentSnapInfo getSnap() {
return snapInfo != null ? snapInfo : new OAlignmentSnapInfo(0, 0);
}
/**
* Updates the dropDetails after drop event.
*
* @param drag the drag
*/
public void updateDropDetails(VDragEvent drag) {
if (snapInfo != null) {
Map<String, Object> dropDetail = drag.getDropDetails();
int rTop = (int) dropDetail.get(Constants.DROP_DETAIL_RELATIVE_TOP);
int aTop = (int) dropDetail.get(Constants.DROP_DETAIL_ABSOLUTE_TOP);
int rLeft = (int) dropDetail.get(Constants.DROP_DETAIL_RELATIVE_LEFT);
int aLeft = (int) dropDetail.get(Constants.DROP_DETAIL_ABSOLUTE_LEFT);
rTop -= snapInfo.getY();
aTop -= snapInfo.getY();
rLeft -= snapInfo.getX();
aLeft -= snapInfo.getX();
dropDetail.put(Constants.DROP_DETAIL_RELATIVE_TOP, rTop);
dropDetail.put(Constants.DROP_DETAIL_ABSOLUTE_TOP, aTop);
dropDetail.put(Constants.DROP_DETAIL_RELATIVE_LEFT, rLeft);
dropDetail.put(Constants.DROP_DETAIL_ABSOLUTE_LEFT, aLeft);
}
}
/**
* Updates the resized widget after the resizing was finished.
*
* @param accept the accept
* @param oldWidth the old width
* @param oldHeight the old height
*/
public void updateResizedWidget(boolean accept, int oldWidth, int oldHeight) {
if (accept) {
int width = resizingOverlay.getOffsetWidth();
int height = resizingOverlay.getOffsetHeight();
resizingWidget.getElement().getStyle().setWidth(width, Unit.PX);
resizingWidget.getElement().getStyle().setHeight(height, Unit.PX);
resizingWidget.notifyResized(width, height, oldWidth, oldHeight, resizeLocation);
} else {
resizingWidget.notifyResizingCancel();
}
}
/**
* Returns the widget that is currently resized.
*
* @return the resizing widget
*/
public Widget getResizingWidget() {
return resizingWidget;
}
/**
* Returns the resizing operation currently performed.
*
* @return the resize location
*/
public OResizeLocation getResizeLocation() {
return resizeLocation;
}
/**
* Returns true, if alignments should be used.
*
* @return true, if is use alignments
*/
public boolean isUseAlignments() {
return useAlignments;
}
/**
* Sets the use alignments.
*
* @param useAlignments the new use alignments
*/
public void setUseAlignments(boolean useAlignments) {
// if alignments switched off, ensure that pending alignments are being
// removed.
if (this.useAlignments == true && useAlignments == false) {
Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() {
@Override
public void execute() {
resetSnap();
resetAlignments();
}
});
}
this.useAlignments = useAlignments;
}
}