X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.diagram%2Fsrc%2Forg%2Fsimantics%2Fdiagram%2Felements%2FResizeNode.java;h=b18be83b3ee30d9d4cd298212659bb257abf47d5;hb=704e41fc543094311b7e70f2d705b2eba9f6574b;hp=79c10c1cd3b435e992d7b98f742a9258bcecf38a;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/ResizeNode.java b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/ResizeNode.java index 79c10c1cd..b18be83b3 100644 --- a/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/ResizeNode.java +++ b/bundles/org.simantics.diagram/src/org/simantics/diagram/elements/ResizeNode.java @@ -1,361 +1,361 @@ -package org.simantics.diagram.elements; - -import java.awt.BasicStroke; -import java.awt.Cursor; -import java.awt.Graphics2D; -import java.awt.Shape; -import java.awt.Stroke; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; - -import org.simantics.g2d.canvas.impl.CanvasContext; -import org.simantics.scenegraph.g2d.G2DNode; -import org.simantics.scenegraph.g2d.events.EventTypes; -import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent; -import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent; -import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin; -import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent; -import org.simantics.scenegraph.utils.NodeUtil; - -/** - * Invisible resize node for resizing rectangular elements - * - * @author Teemu Lempinen - * - */ -public class ResizeNode extends G2DNode { - - private static final long serialVersionUID = 7997312998598071328L; - - - /** - * Interface for listeners listening resize events in nodes - * @author Teemu Lempinen - * - */ - public interface ResizeListener { - - /** - * Triggered when a node has been resized - * @param newBounds new bounds for the node - */ - public void elementResized(Rectangle2D newBounds, AffineTransform transform, boolean synchronizeToBackend); - } - - /** - * Enumeration for indicating which side of the resize bounds should affect translate - * properties. - * @author Teemu Lempinen - * - */ - public enum TranslateEdge { - NONE, NORTH, SOUTH, EAST, WEST; - } - - - private boolean dragging = false; - private ResizeListener resizeListener; - private Rectangle2D bounds; - private Stroke stroke; - private TranslateEdge xTranslateEdge = TranslateEdge.WEST; - private TranslateEdge YTranslateEdge = TranslateEdge.NORTH; - - int cursor = Cursor.DEFAULT_CURSOR; - - /** - * Create a new Resize node with default border width (1) - */ - public ResizeNode() { - this(1); - } - - /** - * Create a new Resize node - * - * @param borderWidth Width of the border for handling mouse dragging - */ - public ResizeNode(float borderWidth) { - this.stroke = new BasicStroke(borderWidth); - } - - @PropertySetter("Bounds") - @SyncField("bounds") - public void setBounds(Rectangle2D bounds) { - assert(bounds != null); - this.bounds = bounds; - } - - @PropertySetter("stroke") - @SyncField("stroke") - public void setStroke(Stroke stroke) { - this.stroke = stroke; - } - - /** - * Is dragging (resizing) active - * @return - */ - public boolean dragging() { - return dragging; - } - - /** - * Set a ResizeListener for this node - * @param listener ResizeListener - */ - public void setResizeListener(ResizeListener listener) { - this.resizeListener = listener; - } - - @Override - public void cleanup() { - removeEventHandler(this); - super.cleanup(); - } - - @Override - public void init() { - super.init(); - addEventHandler(this); - } - - /** - * Outline stroke shape of the bounds of this node - * - * @return Outline stroke shape of the bounds of this node - */ - protected Shape getOutline() { - return stroke.createStrokedShape(new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight())); - } - - /** - * Dragging is started on mouse pressed, if mouse was pressed on the edge of bounds - */ - @Override - protected boolean mouseButtonPressed(MouseButtonPressedEvent event) { - if(bounds != null && NodeUtil.isSelected(this, 1)) { - // get mouse position - Point2D local = controlToLocal( event.controlPosition ); - local = parentToLocal(local); - - // Get outline of this node - Shape outline = getOutline(); - - if (outline.contains(local)) { - dragging = true; - return true; - } - } - return super.mouseButtonPressed(event); - } - - /** - * Get resize cursor for the location where the drag started. - * - * @param local Point of origin for drag - * @return Cursor int - */ - private int getCursorDirection(Point2D local) { - float width = ((BasicStroke)stroke).getLineWidth(); - - // Check the direction of the resize - int cursor = 0; - if (local.getX() >= bounds.getX() - width / 2 && - local.getX() <= bounds.getX() + width / 2) - // West side - cursor = Cursor.W_RESIZE_CURSOR; - else if (local.getX() >= bounds.getMaxX() - width / 2 && - local.getX() <= bounds.getMaxX() + width / 2) - // East size - cursor = Cursor.E_RESIZE_CURSOR; - - if(local.getY() >= bounds.getY() - width / 2 && - local.getY() <= bounds.getY() + width / 2) { - // North side - if(cursor == Cursor.W_RESIZE_CURSOR) - cursor = Cursor.NW_RESIZE_CURSOR; - else if(cursor == Cursor.E_RESIZE_CURSOR) - cursor = Cursor.NE_RESIZE_CURSOR; - else - cursor = Cursor.N_RESIZE_CURSOR; - } else if(local.getY() >= bounds.getMaxY() - width / 2 && - local.getY() <= bounds.getMaxY() + width / 2) { - // South side - if(cursor == Cursor.W_RESIZE_CURSOR) - cursor = Cursor.SW_RESIZE_CURSOR; - else if(cursor == Cursor.E_RESIZE_CURSOR) - cursor = Cursor.SE_RESIZE_CURSOR; - else - cursor = cursor | Cursor.S_RESIZE_CURSOR; - } - return cursor; - } - - double dragTolerance = 0.5; - - @Override - protected boolean mouseMoved(MouseMovedEvent e) { - if(dragging) { - // If dragging is active and mouse is moved enough, resize the element - Point2D local = controlToLocal( e.controlPosition ); - local = parentToLocal(local); - - Rectangle2D bounds = getBoundsInLocal().getBounds2D(); - - if(Math.abs(bounds.getMaxX() - local.getX()) > dragTolerance || - Math.abs(bounds.getMaxY() - local.getY()) > dragTolerance) { - resize(local, false); - } - return true; - } else if(NodeUtil.isSelected(this, 1)){ - // Dragging is not active. Change mouse cursor if entered or exited border - Point2D local = controlToLocal( e.controlPosition ); - local = parentToLocal(local); - - Shape outline = getOutline(); - if (outline.contains(local)) { - cursor = getCursorDirection(local); - CanvasContext ctx = (CanvasContext)e.getContext(); - ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(cursor)); - } else if(cursor != 0) { - cursor = 0; - CanvasContext ctx = (CanvasContext)e.getContext(); - ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - } - - } - return super.mouseMoved(e); - } - - @Override - protected boolean mouseDragged(MouseDragBegin e) { - if(dragging) { - return true; // Consume event - } else { - return false; - } - } - - private static double minSize = 5; // Minimum size for an element - - private void resize(Point2D local, boolean synchronize) { - Rectangle2D bounds = getBoundsInLocal().getBounds2D(); - double x = bounds.getX(); - double y = bounds.getY(); - double w = bounds.getWidth(); - double h = bounds.getHeight(); - double dx = 0, dy = 0; - - /* - * Resize to east and north: - * - * Move x and y coordinates and resize to get maxX and maxY to stay in their place - */ - if(cursor == Cursor.W_RESIZE_CURSOR || cursor == Cursor.NW_RESIZE_CURSOR || cursor == Cursor.SW_RESIZE_CURSOR) { - double dw = local.getX() - x; - if(w - dw < minSize) - dw = w - minSize; - w = w - dw; - - - if(TranslateEdge.WEST.equals(xTranslateEdge)) - dx = dw; - } - - if(cursor == Cursor.N_RESIZE_CURSOR || cursor == Cursor.NW_RESIZE_CURSOR || cursor == Cursor.NE_RESIZE_CURSOR ) { - double dh = local.getY() - y; - if(h - dh < minSize) - dh = h - minSize; - h = h - dh; - - if(TranslateEdge.NORTH.equals(YTranslateEdge)) - dy = dh; - } - - /* - * Resize to west and south: - * - * Adjust width and height - */ - if(cursor == Cursor.E_RESIZE_CURSOR || cursor == Cursor.NE_RESIZE_CURSOR || cursor == Cursor.SE_RESIZE_CURSOR) { - double dw = local.getX() - bounds.getMaxX(); - w = w + dw > minSize ? w + dw : minSize; - - if(TranslateEdge.EAST.equals(xTranslateEdge)) - dx = w - bounds.getWidth(); - } - - if(cursor == Cursor.S_RESIZE_CURSOR || cursor == Cursor.SW_RESIZE_CURSOR || cursor == Cursor.SE_RESIZE_CURSOR) { - double dh = local.getY() - bounds.getMaxY(); - h = h + dh > minSize ? h + dh : minSize; - - if(TranslateEdge.SOUTH.equals(YTranslateEdge)) - dy = h - bounds.getHeight(); - } - - /* - * Set bounds and transform to the element before calling resize listener. - * This prevents unwanted movement due to unsynchronized transform and bounds. - */ - this.bounds.setRect(x, y, w, h); - - AffineTransform at = new AffineTransform(); - at.translate(dx, dy); - if(resizeListener != null) - resizeListener.elementResized(this.bounds, at, synchronize); - } - - @Override - protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) { - if(dragging) { - // Stop resizing and set the size to its final state. - Point2D local = controlToLocal( e.controlPosition ); - local = parentToLocal(local); - - resize(local, true); - dragging = false; - - // Revert cursor to normal. - CanvasContext ctx = (CanvasContext)e.getContext(); - cursor = Cursor.DEFAULT_CURSOR; - ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(cursor)); - } - return super.mouseButtonReleased(e); - } - - @Override - public int getEventMask() { - return super.getEventMask() | EventTypes.MouseButtonPressedMask - | EventTypes.MouseMovedMask - | EventTypes.MouseButtonReleasedMask - ; - } - - @Override - public Rectangle2D getBoundsInLocal() { - if(bounds == null) return null; - return bounds.getBounds2D(); - } - - @Override - public void render(Graphics2D g2d) { - // Do not draw anything - } - - /** - * Set which edge should affect X-translation - * @param xTranslateEdge TranslateEdge.NONE, EAST or WEST - */ - public void setxTranslateEdge(TranslateEdge xTranslateEdge) { - this.xTranslateEdge = xTranslateEdge; - } - - /** - * Set which edge should affect Y-translation - * @param yTranslateEdge TranslateEdge.NONE, SOUTH or NORTH - */ - public void setYTranslateEdge(TranslateEdge yTranslateEdge) { - YTranslateEdge = yTranslateEdge; - } -} +package org.simantics.diagram.elements; + +import java.awt.BasicStroke; +import java.awt.Cursor; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import org.simantics.g2d.canvas.impl.CanvasContext; +import org.simantics.scenegraph.g2d.G2DNode; +import org.simantics.scenegraph.g2d.events.EventTypes; +import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent; +import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent; +import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin; +import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent; +import org.simantics.scenegraph.utils.NodeUtil; + +/** + * Invisible resize node for resizing rectangular elements + * + * @author Teemu Lempinen + * + */ +public class ResizeNode extends G2DNode { + + private static final long serialVersionUID = 7997312998598071328L; + + + /** + * Interface for listeners listening resize events in nodes + * @author Teemu Lempinen + * + */ + public interface ResizeListener { + + /** + * Triggered when a node has been resized + * @param newBounds new bounds for the node + */ + public void elementResized(Rectangle2D newBounds, AffineTransform transform, boolean synchronizeToBackend); + } + + /** + * Enumeration for indicating which side of the resize bounds should affect translate + * properties. + * @author Teemu Lempinen + * + */ + public enum TranslateEdge { + NONE, NORTH, SOUTH, EAST, WEST; + } + + + private boolean dragging = false; + private ResizeListener resizeListener; + private Rectangle2D bounds; + private Stroke stroke; + private TranslateEdge xTranslateEdge = TranslateEdge.WEST; + private TranslateEdge YTranslateEdge = TranslateEdge.NORTH; + + int cursor = Cursor.DEFAULT_CURSOR; + + /** + * Create a new Resize node with default border width (1) + */ + public ResizeNode() { + this(1); + } + + /** + * Create a new Resize node + * + * @param borderWidth Width of the border for handling mouse dragging + */ + public ResizeNode(float borderWidth) { + this.stroke = new BasicStroke(borderWidth); + } + + @PropertySetter("Bounds") + @SyncField("bounds") + public void setBounds(Rectangle2D bounds) { + assert(bounds != null); + this.bounds = bounds; + } + + @PropertySetter("stroke") + @SyncField("stroke") + public void setStroke(Stroke stroke) { + this.stroke = stroke; + } + + /** + * Is dragging (resizing) active + * @return + */ + public boolean dragging() { + return dragging; + } + + /** + * Set a ResizeListener for this node + * @param listener ResizeListener + */ + public void setResizeListener(ResizeListener listener) { + this.resizeListener = listener; + } + + @Override + public void cleanup() { + removeEventHandler(this); + super.cleanup(); + } + + @Override + public void init() { + super.init(); + addEventHandler(this); + } + + /** + * Outline stroke shape of the bounds of this node + * + * @return Outline stroke shape of the bounds of this node + */ + protected Shape getOutline() { + return stroke.createStrokedShape(new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight())); + } + + /** + * Dragging is started on mouse pressed, if mouse was pressed on the edge of bounds + */ + @Override + protected boolean mouseButtonPressed(MouseButtonPressedEvent event) { + if(bounds != null && NodeUtil.isSelected(this, 1)) { + // get mouse position + Point2D local = controlToLocal( event.controlPosition ); + local = parentToLocal(local); + + // Get outline of this node + Shape outline = getOutline(); + + if (outline.contains(local)) { + dragging = true; + return true; + } + } + return super.mouseButtonPressed(event); + } + + /** + * Get resize cursor for the location where the drag started. + * + * @param local Point of origin for drag + * @return Cursor int + */ + private int getCursorDirection(Point2D local) { + float width = ((BasicStroke)stroke).getLineWidth(); + + // Check the direction of the resize + int cursor = 0; + if (local.getX() >= bounds.getX() - width / 2 && + local.getX() <= bounds.getX() + width / 2) + // West side + cursor = Cursor.W_RESIZE_CURSOR; + else if (local.getX() >= bounds.getMaxX() - width / 2 && + local.getX() <= bounds.getMaxX() + width / 2) + // East size + cursor = Cursor.E_RESIZE_CURSOR; + + if(local.getY() >= bounds.getY() - width / 2 && + local.getY() <= bounds.getY() + width / 2) { + // North side + if(cursor == Cursor.W_RESIZE_CURSOR) + cursor = Cursor.NW_RESIZE_CURSOR; + else if(cursor == Cursor.E_RESIZE_CURSOR) + cursor = Cursor.NE_RESIZE_CURSOR; + else + cursor = Cursor.N_RESIZE_CURSOR; + } else if(local.getY() >= bounds.getMaxY() - width / 2 && + local.getY() <= bounds.getMaxY() + width / 2) { + // South side + if(cursor == Cursor.W_RESIZE_CURSOR) + cursor = Cursor.SW_RESIZE_CURSOR; + else if(cursor == Cursor.E_RESIZE_CURSOR) + cursor = Cursor.SE_RESIZE_CURSOR; + else + cursor = cursor | Cursor.S_RESIZE_CURSOR; + } + return cursor; + } + + double dragTolerance = 0.5; + + @Override + protected boolean mouseMoved(MouseMovedEvent e) { + if(dragging) { + // If dragging is active and mouse is moved enough, resize the element + Point2D local = controlToLocal( e.controlPosition ); + local = parentToLocal(local); + + Rectangle2D bounds = getBoundsInLocal().getBounds2D(); + + if(Math.abs(bounds.getMaxX() - local.getX()) > dragTolerance || + Math.abs(bounds.getMaxY() - local.getY()) > dragTolerance) { + resize(local, false); + } + return true; + } else if(NodeUtil.isSelected(this, 1)){ + // Dragging is not active. Change mouse cursor if entered or exited border + Point2D local = controlToLocal( e.controlPosition ); + local = parentToLocal(local); + + Shape outline = getOutline(); + if (outline.contains(local)) { + cursor = getCursorDirection(local); + CanvasContext ctx = (CanvasContext)e.getContext(); + ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(cursor)); + } else if(cursor != 0) { + cursor = 0; + CanvasContext ctx = (CanvasContext)e.getContext(); + ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + + } + return super.mouseMoved(e); + } + + @Override + protected boolean mouseDragged(MouseDragBegin e) { + if(dragging) { + return true; // Consume event + } else { + return false; + } + } + + private static double minSize = 5; // Minimum size for an element + + private void resize(Point2D local, boolean synchronize) { + Rectangle2D bounds = getBoundsInLocal().getBounds2D(); + double x = bounds.getX(); + double y = bounds.getY(); + double w = bounds.getWidth(); + double h = bounds.getHeight(); + double dx = 0, dy = 0; + + /* + * Resize to east and north: + * + * Move x and y coordinates and resize to get maxX and maxY to stay in their place + */ + if(cursor == Cursor.W_RESIZE_CURSOR || cursor == Cursor.NW_RESIZE_CURSOR || cursor == Cursor.SW_RESIZE_CURSOR) { + double dw = local.getX() - x; + if(w - dw < minSize) + dw = w - minSize; + w = w - dw; + + + if(TranslateEdge.WEST.equals(xTranslateEdge)) + dx = dw; + } + + if(cursor == Cursor.N_RESIZE_CURSOR || cursor == Cursor.NW_RESIZE_CURSOR || cursor == Cursor.NE_RESIZE_CURSOR ) { + double dh = local.getY() - y; + if(h - dh < minSize) + dh = h - minSize; + h = h - dh; + + if(TranslateEdge.NORTH.equals(YTranslateEdge)) + dy = dh; + } + + /* + * Resize to west and south: + * + * Adjust width and height + */ + if(cursor == Cursor.E_RESIZE_CURSOR || cursor == Cursor.NE_RESIZE_CURSOR || cursor == Cursor.SE_RESIZE_CURSOR) { + double dw = local.getX() - bounds.getMaxX(); + w = w + dw > minSize ? w + dw : minSize; + + if(TranslateEdge.EAST.equals(xTranslateEdge)) + dx = w - bounds.getWidth(); + } + + if(cursor == Cursor.S_RESIZE_CURSOR || cursor == Cursor.SW_RESIZE_CURSOR || cursor == Cursor.SE_RESIZE_CURSOR) { + double dh = local.getY() - bounds.getMaxY(); + h = h + dh > minSize ? h + dh : minSize; + + if(TranslateEdge.SOUTH.equals(YTranslateEdge)) + dy = h - bounds.getHeight(); + } + + /* + * Set bounds and transform to the element before calling resize listener. + * This prevents unwanted movement due to unsynchronized transform and bounds. + */ + this.bounds.setRect(x, y, w, h); + + AffineTransform at = new AffineTransform(); + at.translate(dx, dy); + if(resizeListener != null) + resizeListener.elementResized(this.bounds, at, synchronize); + } + + @Override + protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) { + if(dragging) { + // Stop resizing and set the size to its final state. + Point2D local = controlToLocal( e.controlPosition ); + local = parentToLocal(local); + + resize(local, true); + dragging = false; + + // Revert cursor to normal. + CanvasContext ctx = (CanvasContext)e.getContext(); + cursor = Cursor.DEFAULT_CURSOR; + ctx.getMouseCursorContext().setCursor(e.mouseId, Cursor.getPredefinedCursor(cursor)); + } + return super.mouseButtonReleased(e); + } + + @Override + public int getEventMask() { + return super.getEventMask() | EventTypes.MouseButtonPressedMask + | EventTypes.MouseMovedMask + | EventTypes.MouseButtonReleasedMask + ; + } + + @Override + public Rectangle2D getBoundsInLocal() { + if(bounds == null) return null; + return bounds.getBounds2D(); + } + + @Override + public void render(Graphics2D g2d) { + // Do not draw anything + } + + /** + * Set which edge should affect X-translation + * @param xTranslateEdge TranslateEdge.NONE, EAST or WEST + */ + public void setxTranslateEdge(TranslateEdge xTranslateEdge) { + this.xTranslateEdge = xTranslateEdge; + } + + /** + * Set which edge should affect Y-translation + * @param yTranslateEdge TranslateEdge.NONE, SOUTH or NORTH + */ + public void setYTranslateEdge(TranslateEdge yTranslateEdge) { + YTranslateEdge = yTranslateEdge; + } +}