| /** |
| * |
| * 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; |
| } |
| |
| } |