private IEnumerator <object> Drag(Boundaries boundaries, DragSide side)
        {
            IntVector2?last = null;

            Save(boundaries);
            bool isStretchingMarkers = input.IsKeyPressed(Key.Control);

            using (Document.Current.History.BeginTransaction()) {
                while (input.IsMousePressed())
                {
                    Utils.ChangeCursorIfDefault(MouseCursor.SizeWE);
                    var current = grid.CellUnderMouse();
                    if (current == last)
                    {
                        yield return(null);

                        continue;
                    }
                    Document.Current.History.RollbackTransaction();
                    current.X = Math.Max(current.X, 0);
                    if (side == DragSide.Left)
                    {
                        current.X = Math.Min(current.X, boundaries.Right - 1);
                    }
                    else
                    {
                        current.X = Math.Max(current.X, boundaries.Left + 1);
                    }
                    Stretch(boundaries, side, current.X, stretchMarkers: isStretchingMarkers);
                    if (side == DragSide.Left)
                    {
                        boundaries.Left = current.X;
                    }
                    else
                    {
                        boundaries.Right = current.X;
                    }
                    ClearGridSelection.Perform();
                    for (int i = boundaries.Top; i <= boundaries.Bottom; ++i)
                    {
                        SelectGridSpan.Perform(i, boundaries.Left, boundaries.Right);
                    }
                    SetCurrentColumn.Perform(boundaries.Right);
                    last = current;
                    yield return(null);
                }
                Document.Current.History.CommitTransaction();
            }
            yield return(null);
        }
        private void Save(Boundaries boundaries)
        {
            savedPositions.Clear();
            savedKeyframes.Clear();
            savedMarkerPositions.Clear();
            savedMarkers.Clear();
            var length = boundaries.Right - boundaries.Left - 1;

            for (int i = boundaries.Top; i <= boundaries.Bottom; ++i)
            {
                if (!(Document.Current.Rows[i].Components.Get <NodeRow>()?.Node is IAnimationHost animable))
                {
                    continue;
                }
                foreach (var animator in animable.Animators)
                {
                    savedKeyframes.Add(animator, new List <IKeyframe>());
                    var keys = animator.Keys.Where(k =>
                                                   boundaries.Left <= k.Frame &&
                                                   k.Frame < boundaries.Right
                                                   );
                    foreach (var key in keys)
                    {
                        savedPositions.Add(key, ((double)key.Frame - boundaries.Left) / length);
                        savedKeyframes[animator].Add(key);
                    }
                }
            }
            var markers = Document.Current.Container.Markers.Where(k =>
                                                                   boundaries.Left <= k.Frame &&
                                                                   k.Frame < boundaries.Right);

            foreach (var marker in markers)
            {
                savedMarkerPositions.Add(marker, ((double)marker.Frame - boundaries.Left) / length);
                savedMarkers.Add(marker);
            }
        }
        private void Stretch(Boundaries boundaries, DragSide side, int newPos, bool stretchMarkers)
        {
            int length;

            if (side == DragSide.Left)
            {
                length = boundaries.Right - newPos - 1;
            }
            else
            {
                length = newPos - boundaries.Left - 1;
            }
            int oldLength = boundaries.Right - boundaries.Left - 1;

            for (int i = boundaries.Top; i <= boundaries.Bottom; ++i)
            {
                if (!(Document.Current.Rows[i].Components.Get <NodeRow>()?.Node is IAnimationHost animable))
                {
                    continue;
                }
                foreach (var animator in animable.Animators.ToList())
                {
                    IEnumerable <IKeyframe> saved = savedKeyframes[animator];
                    if (
                        side == DragSide.Left && length < oldLength ||
                        side == DragSide.Right && length > oldLength
                        )
                    {
                        saved = saved.Reverse();
                    }
                    foreach (var key in saved)
                    {
                        RemoveKeyframe.Perform(animator, key.Frame);
                    }
                    foreach (var key in saved)
                    {
                        double relpos = savedPositions[key];
                        int    newFrame;
                        if (side == DragSide.Left)
                        {
                            newFrame = (int)Math.Round(newPos + relpos * length);
                        }
                        else
                        {
                            newFrame = (int)Math.Round(boundaries.Left + relpos * length);
                        }
                        var newKey = key.Clone();
                        newKey.Frame = newFrame;
                        SetAnimableProperty.Perform(
                            animable, animator.TargetPropertyPath, newKey.Value,
                            createAnimatorIfNeeded: true,
                            createInitialKeyframeForNewAnimator: false,
                            newKey.Frame
                            );
                        SetKeyframe.Perform(animable, animator.TargetPropertyPath, Document.Current.AnimationId, newKey);
                    }
                }
            }
            if (stretchMarkers)
            {
                foreach (var marker in savedMarkers)
                {
                    DeleteMarker.Perform(Document.Current.Container, marker, removeDependencies: false);
                }
                foreach (var marker in savedMarkers)
                {
                    double relpos = savedMarkerPositions[marker];
                    int    newFrame;
                    if (side == DragSide.Left)
                    {
                        newFrame = (int)Math.Round(newPos + relpos * length);
                    }
                    else
                    {
                        newFrame = (int)Math.Round(boundaries.Left + relpos * length);
                    }
                    var newMarker = marker.Clone();
                    newMarker.Frame = newFrame;
                    SetMarker.Perform(Document.Current.Container, newMarker, removeDependencies: false);
                }
            }
        }