private bool MoveSnapFilter(IEvent testEvent, TimelineControl.SnapOptions options) { ITimelineReference movingReference = options.FilterContext as ITimelineReference; if (movingReference == null) { return(true); } IHierarchicalTimeline movingTimeline = movingReference.Target; if (movingTimeline == null) { return(true); } ITimeline owningTimeline = null; if (testEvent is ITimelineReference) { owningTimeline = ((ITimelineReference)testEvent).Parent; } else if (testEvent is IInterval) { owningTimeline = ((IInterval)testEvent).Track.Group.Timeline; } else if (testEvent is IKey) { owningTimeline = ((IKey)testEvent).Track.Group.Timeline; } else if (testEvent is IMarker) { owningTimeline = ((IMarker)testEvent).Timeline; } // Is the reference being compared to an object that it owns? Never snap! if (owningTimeline == movingTimeline) { return(false); } // Is the reference being compared against a sibling object? Snap away. if (owningTimeline == movingReference.Parent) { return(true); } // to-do: support a true hierarchy. We can't support directed acyclic graphs because // the Layout implementation uses a Dictionary of ITimelineObjects and the same object // can't have multiple layout rectangles. We could support a tree hierarchy, though. //// The test object may be a grandchild of the moving reference. Look for a parent. //owningTimeline = owningTimeline.Parent; return(true); }
/// <summary> /// Event handler for a mouse move event on the owning TimelineControl</summary> /// <param name="sender">Sender</param> /// <param name="e">Event args</param> protected virtual void MouseMoveHandler(object sender, MouseEventArgs e) { if (IsMoving) { Matrix worldToView = Owner.Transform; float newPosition = Sce.Atf.GdiUtil.InverseTransform(worldToView, e.Location.X); if (Control.ModifierKeys == Keys.Shift) { // Get snap-from point in world coordinates. float[] movingPoints = new[] { newPosition }; TimelineControl.SnapOptions options = new TimelineControl.SnapOptions(); options.IncludeScrubber = false; options.CheckModifierKeys = false; float snapOffset = Owner.GetSnapOffset(movingPoints, options); newPosition += snapOffset; } Position = newPosition; } }
/// <summary> /// Gets the offset from one of the world snap points to the closest non-selected object's edge</summary> /// <param name="movingPoints">The x-coordinates to snap "from", in world coordinates</param> /// <param name="options">The options to control the behavior. If null, the defaults are used.</param> /// <returns>The value to be added, to GetDragOffset().X, for example. Is in world coordinates.</returns> private float GetSnapOffset(IEnumerable <float> movingPoints, TimelineControl.SnapOptions options) { //we want to recalculate m_snapInfo and reflect changes to the snap-to lines m_snapInfo.Clear(); m_owner.Invalidate(); if (options == null) { options = new TimelineControl.SnapOptions(); } // Check for user-forced snapping and no-snapping. if (options.CheckModifierKeys) { Keys modKeys = Control.ModifierKeys; if (s_deactivatorKeys != Keys.None && (modKeys & s_deactivatorKeys) == s_deactivatorKeys) { return(0.0f); } if (s_activatorKeys != Keys.None && (modKeys & s_activatorKeys) != s_activatorKeys) { return(0.0f); } } // Prepare helper object on each moving point. foreach (float snapping in movingPoints) { m_snapInfo.Add(new SnapOffsetInfo(snapping)); } if (m_snapInfo.Count == 0) { return(0.0f); } // Find the closest IEvent. float worldSnapTolerance = GdiUtil.InverseTransformVector(m_owner.Transform, s_snapTolerance); List <TimelinePath> events = new List <TimelinePath>( TimelineControl.GetObjects <IEvent>(m_owner.Timeline)); // Allow for snapping to a scrubber manipulator. if (m_scrubber != null && options.IncludeScrubber) { events.Add(new TimelinePath(m_scrubber)); } foreach (TimelinePath path in events) { if (options.IncludeSelected || !m_owner.Selection.SelectionContains(path)) { IEvent snapToEvent = (IEvent)path.Last; if (options.Filter == null || options.Filter(snapToEvent, options)) { Matrix localToWorld = TimelineControl.CalculateLocalToWorld(path); float start, length; GetEventDimensions(snapToEvent, localToWorld, out start, out length); foreach (SnapOffsetInfo info in m_snapInfo) { info.Update(start, snapToEvent, worldSnapTolerance); if (length > 0) { info.Update(start + length, snapToEvent, worldSnapTolerance); } } } } } // Keep only the shortest distance snap-to points. Could be multiple in case of tie. SnapOffsetInfo.RemoveInvalid(m_snapInfo); if (m_snapInfo.Count == 0) { return(0.0f); } SnapOffsetInfo topInfo = m_snapInfo[0]; return(topInfo.Offset); }
static SplitManipulator() { s_snapOptions = new TimelineControl.SnapOptions(); s_snapOptions.IncludeSelected = true; }
/// <summary> /// Gets the offset from one of the world snap points to the closest non-selected object's edge</summary> /// <param name="movingPoints">The x-coordinates to snap "from", in world coordinates</param> /// <param name="options">The options to control the behavior. If null, the defaults are used.</param> /// <returns>The value to be added, to GetDragOffset().X, for example. Is in world coordinates.</returns> private float GetSnapOffset(IEnumerable<float> movingPoints, TimelineControl.SnapOptions options) { //we want to recalculate m_snapInfo and reflect changes to the snap-to lines m_snapInfo.Clear(); m_owner.Invalidate(); if (options == null) options = new TimelineControl.SnapOptions(); // Check for user-forced snapping and no-snapping. if (options.CheckModifierKeys) { Keys modKeys = Control.ModifierKeys; if (s_deactivatorKeys != Keys.None && (modKeys & s_deactivatorKeys) == s_deactivatorKeys) return 0.0f; if (s_activatorKeys != Keys.None && (modKeys & s_activatorKeys) != s_activatorKeys) return 0.0f; } // Prepare helper object on each moving point. foreach (float snapping in movingPoints) { m_snapInfo.Add(new SnapOffsetInfo(snapping)); } if (m_snapInfo.Count == 0) return 0.0f; // Find the closest IEvent. float worldSnapTolerance = GdiUtil.InverseTransformVector(m_owner.Transform, s_snapTolerance); List<TimelinePath> events = new List<TimelinePath>( TimelineControl.GetObjects<IEvent>(m_owner.Timeline)); // Allow for snapping to a scrubber manipulator. if (m_scrubber != null && options.IncludeScrubber) events.Add(new TimelinePath(m_scrubber)); foreach (TimelinePath path in events) { if (options.IncludeSelected || !m_owner.Selection.SelectionContains(path)) { IEvent snapToEvent = (IEvent)path.Last; if (options.Filter == null || options.Filter(snapToEvent, options)) { Matrix localToWorld = TimelineControl.CalculateLocalToWorld(path); float start, length; GetEventDimensions(snapToEvent, localToWorld, out start, out length); foreach (SnapOffsetInfo info in m_snapInfo) { info.Update(start, snapToEvent, worldSnapTolerance); if (length > 0) info.Update(start + length, snapToEvent, worldSnapTolerance); } } } } // Keep only the shortest distance snap-to points. Could be multiple in case of tie. SnapOffsetInfo.RemoveInvalid(m_snapInfo); if (m_snapInfo.Count == 0) return 0.0f; SnapOffsetInfo topInfo = m_snapInfo[0]; return topInfo.Offset; }