// If no current snap action, set it to the action. // Otherwise, if set, we're possibly undoing the last snap action (these are always opposite attach/detach actions), // if re-attaching or re-detaching from the connection point. // Lastly, if attaching to a different connection point, buffer the last snap action (which would always be a detach) // and set the current snap action to what will always be the attach to another connection point. protected void SetCurrentAction(SnapAction action) { if (currentSnapAction == null) { currentSnapAction = action; } else { // Connecting to a different shape? if (action.TargetShape != currentSnapAction.TargetShape // connecting to a different endpoint on the connector? || action.GripType != currentSnapAction.GripType // connecting to a different connection point on the shape? || action.ShapeConnectionPoint != currentSnapAction.ShapeConnectionPoint) { snapActions.Add(currentSnapAction); currentSnapAction = action; } else { // User is undoing the last action by re-connecting or disconnecting from the shape to which we just connected / disconnected. currentSnapAction = null; } } }
public void Reset() { snapActions.Clear(); nearElements.Clear(); currentlyNear.Clear(); runningDelta = Point.Empty; currentSnapAction = null; }
public bool SnapCheck(GripType gripType, Point delta, Action <Point> update, bool isByKeyPress = false) { SnapAction action = Snap(gripType, delta, isByKeyPress); if (action != null) { if (action.SnapType == SnapAction.Action.Attach) { runningDelta = runningDelta.Add(action.Delta); update(action.Delta); // Controller.DragSelectedElements(action.Delta); // Don't attach at this point, as this will be handled by the mouse-up action. SetCurrentAction(action); } else if (action.SnapType == SnapAction.Action.Detach) { runningDelta = runningDelta.Add(action.Delta); update(action.Delta); // Controller.DragSelectedElements(action.Delta); // Don't detach at this point, as this will be handled by the mouse-up action. SetCurrentAction(action); } else // Attached { // The mouse move had no affect in detaching because it didn't have sufficient velocity. // The problem here is that the mouse moves, affecting the total delta, but the shape doesn't move. // This affects the computation in the MouseUp handler: // Point delta = CurrentMousePosition.Delta(startedDraggingShapesAt); // =================== // We could set the mouse cursor position, which isn't a bad idea, as it keeps the mouse with the shape: //Controller.Canvas.MouseMove -= HandleMouseMoveEvent; //Cursor.Position = Controller.Canvas.PointToScreen(LastMousePosition); //Application.DoEvents(); // sigh - we need the event to trigger, even though it's unwired. //Controller.Canvas.MouseMove += HandleMouseMoveEvent; // The above really doesn't work well because I think we can get multiple move events, and this only handles the first event. // =================== // =================== // Or we could add a "compensation" accumulator for dealing with the deltas that don't move the shape. // This works better, except the attached compensation has to be stored for each detach. // attachedCompensation = attachedCompensation.Add(delta); // That doesn't work either, as the attachedCompensation is treated as a move even though there is no actual movement of the connector! // =================== // Final implementation is to use the runningDelta instead of the CurrentMouseMosition - startDraggingShapesAt difference. // startedDraggingShapesAt = CurrentMousePosition; } } return(action != null); }
public SnapAction Clone() { SnapAction ret = new SnapAction(); ret.SnapType = SnapType; ret.connector = connector; ret.gripType = gripType; ret.targetShape = targetShape; ret.lineConnectionPoint = lineConnectionPoint; ret.shapeConnectionPoint = shapeConnectionPoint; ret.Delta = Delta; return(ret); }
protected void DoUndoSnapAction(UndoStack undoStack, SnapAction action) { SnapAction closureAction = action.Clone(); // Do/undo/redo as part of of the move group. if (closureAction.SnapType == SnapAction.Action.Attach) { undoStack.UndoRedo("Attach", () => closureAction.Attach(), () => closureAction.Detach(), false); } else { undoStack.UndoRedo("Detach", () => closureAction.Detach(), () => closureAction.Attach(), false); } }
protected SnapAction Snap(GripType type, Point delta, bool isByKeyPress) { SnapAction action = null; // Snapping permitted only when one and only one element is selected. if (controller.SelectedElements.Count != 1) { return(null); } if (controller.IsSnapToBeIgnored) { return(null); } // bool snapped = false; GraphicElement selectedElement = controller.SelectedElements[0]; // Look for connection points on nearby elements. // If a connection point is nearby, and the delta is moving toward that connection point, then snap to that connection point. // So, it seems odd that we're using the connection points of the line, rather than the anchors. // However, this is actually simpler, and a line's connection points should at least include the endpoint anchors. IEnumerable <ConnectionPoint> connectionPoints = selectedElement.GetConnectionPoints().Where(p => type == GripType.None || p.Type == type); nearElements = GetNearbyElements(connectionPoints); ShowConnectionPoints(nearElements.Select(e => e.NearElement), true); ShowConnectionPoints(currentlyNear.Where(e => !nearElements.Any(e2 => e.NearElement == e2.NearElement)).Select(e => e.NearElement), false); currentlyNear = nearElements; // Issue #6 // TODO: Again, sort of kludgy. UpdateWithNearElementConnectionPoints(nearElements); nearElements = nearElements.OrderBy(si => si.AbsDx + si.AbsDy).ToList(); // abs(dx) + abs(dy) as a fast "distance" sorter, no need for sqrt(dx^2 + dy^2) foreach (SnapInfo si in nearElements) { ConnectionPoint nearConnectionPoint = si.NearElement.GetConnectionPoints().FirstOrDefault(cp => cp.Point.IsNear(si.LineConnectionPoint.Point, SNAP_CONNECTION_POINT_RANGE)); if (nearConnectionPoint != null) { Point sourceConnectionPoint = si.LineConnectionPoint.Point; int neardx = nearConnectionPoint.Point.X - sourceConnectionPoint.X; // calculate to match possible delta sign int neardy = nearConnectionPoint.Point.Y - sourceConnectionPoint.Y; int neardxsign = neardx.Sign(); int neardysign = neardy.Sign(); int deltaxsign = delta.X.Sign(); int deltaysign = delta.Y.Sign(); // Are we attached already or moving toward the shape's connection point? if ((neardxsign == 0 || deltaxsign == 0 || neardxsign == deltaxsign) && (neardysign == 0 || deltaysign == 0 || neardysign == deltaysign)) { // If attached, are we moving away from the connection point to detach it? // Keyboard overrides the velocity check so we immediately detach if moving away. if (neardxsign == 0 && neardxsign == 0 && ((delta.X.Abs() >= SNAP_DETACH_VELOCITY || delta.Y.Abs() >= SNAP_DETACH_VELOCITY) || (isByKeyPress && (neardxsign != deltaxsign || neardysign != deltaysign)))) { // Detach: action = new SnapAction(SnapAction.Action.Detach, selectedElement, type, si.NearElement, si.LineConnectionPoint, nearConnectionPoint, delta); break; } else { // Not already connected? if (neardxsign != 0 || neardysign != 0) { // Attach: action = new SnapAction(SnapAction.Action.Attach, selectedElement, type, si.NearElement, si.LineConnectionPoint, nearConnectionPoint, new Point(neardx, neardy)); } else { action = new SnapAction(); break; } // delta = new Point(neardx, neardy); // snapped = true; break; } } } } return(action); }