bool FSequencerDragOperation::SnapToTimes(TArray<float> InitialTimes, const TArray<float>& SnapTimes, const FTimeToPixel& TimeToPixelConverter, float& OutInitialTime, float& OutSnapTime) { bool bSuccess = false; float ClosestTimePixelDistance = PixelSnapWidth; for (int32 InitialTimeIndex = 0; InitialTimeIndex < InitialTimes.Num(); ++InitialTimeIndex) { float InitialTime = InitialTimes[InitialTimeIndex]; float PixelXOfTime = TimeToPixelConverter.TimeToPixel(InitialTime); for (int32 SnapTimeIndex = 0; SnapTimeIndex < SnapTimes.Num(); ++SnapTimeIndex) { float SnapTime = SnapTimes[SnapTimeIndex]; float PixelXOfSnapTime = TimeToPixelConverter.TimeToPixel(SnapTime); float PixelDistance = FMath::Abs(PixelXOfTime - PixelXOfSnapTime); if (PixelDistance < ClosestTimePixelDistance) { ClosestTimePixelDistance = PixelDistance; OutInitialTime = InitialTime; OutSnapTime = SnapTime; bSuccess = true; } } } return bSuccess; }
virtual int32 OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const override { float Alpha = Sequencer.Pin()->GetOverlayFadeCurve(); if (Alpha > 0.f) { FTimeToPixel TimeToPixelConverter = FTimeToPixel(AllottedGeometry, ViewRange.Get()); TRange<float> TimeBounds = TRange<float>(TimeToPixelConverter.PixelToTime(0), TimeToPixelConverter.PixelToTime(AllottedGeometry.Size.X)); TArray< TRange<float> > OverlayRanges = ComputeOverlayRanges(TimeBounds, CachedFilteredRanges); for (int32 i = 0; i < OverlayRanges.Num(); ++i) { float LowerBound = TimeToPixelConverter.TimeToPixel(OverlayRanges[i].GetLowerBoundValue()); float UpperBound = TimeToPixelConverter.TimeToPixel(OverlayRanges[i].GetUpperBoundValue()); FSlateDrawElement::MakeBox( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(FVector2D(LowerBound, 0), FVector2D(UpperBound - LowerBound, AllottedGeometry.Size.Y)), FEditorStyle::GetBrush("Sequencer.ShotFilter"), MyClippingRect, ESlateDrawEffect::None, FLinearColor(1.f, 1.f, 1.f, Alpha) ); } } return LayerId; }
/** * Gets the geometry of a section, optionally inflated by some margin * * @param AllottedGeometry The geometry of the area where sections are located * @param NodeHeight The height of the section area (and its children) * @param SectionInterface Interface to the section to get geometry for * @param TimeToPixelConverter Converts time to pixels and vice versa */ FGeometry GetSectionGeometry( const FGeometry& AllottedGeometry, int32 RowIndex, int32 MaxTracks, float NodeHeight, TSharedPtr<ISequencerSection> SectionInterface, const FTimeToPixel& TimeToPixelConverter ) { const UMovieSceneSection* Section = SectionInterface->GetSectionObject(); float StartX, EndX = 0; // If the section is infinite, occupy the entire width of the geometry where the section is located. if (Section->IsInfinite()) { StartX = AllottedGeometry.Position.X; EndX = AllottedGeometry.Position.X + AllottedGeometry.Size.X; } else { StartX = TimeToPixelConverter.TimeToPixel( Section->GetStartTime() ); EndX = TimeToPixelConverter.TimeToPixel( Section->GetEndTime() ); } // Actual section length without grips. float SectionLengthActual = EndX-StartX; float SectionLengthWithGrips = SectionLengthActual+SequencerSectionConstants::SectionGripSize*2; float ActualHeight = NodeHeight / MaxTracks; // Compute allotted geometry area that can be used to draw the section return AllottedGeometry.MakeChild( FVector2D( StartX-SequencerSectionConstants::SectionGripSize, ActualHeight * RowIndex ), FVector2D( SectionLengthWithGrips, ActualHeight ) ); }
FSelectedKey SSection::GetKeyUnderMouse( const FVector2D& MousePosition, const FGeometry& AllottedGeometry ) const { UMovieSceneSection& Section = *SectionInterface->GetSectionObject(); // Search every key area until we find the one under the mouse for( int32 KeyAreaIndex = 0; KeyAreaIndex < KeyAreas.Num(); ++KeyAreaIndex ) { const FKeyAreaElement& Element = KeyAreas[KeyAreaIndex]; TSharedRef<IKeyArea> KeyArea = Element.KeyAreaNode.GetKeyArea( SectionIndex ); // Compute the current key area geometry FGeometry KeyAreaGeometryPadded = GetKeyAreaGeometry( Element, AllottedGeometry ); // Is the key area under the mouse if( KeyAreaGeometryPadded.IsUnderLocation( MousePosition ) ) { FGeometry SectionGeometry = AllottedGeometry.MakeChild(FVector2D(SequencerSectionConstants::SectionGripSize, 0), AllottedGeometry.GetDrawSize() - FVector2D(SequencerSectionConstants::SectionGripSize*2, 0.0f)); FGeometry KeyAreaGeometry = GetKeyAreaGeometry( Element, SectionGeometry ); FVector2D LocalSpaceMousePosition = KeyAreaGeometry.AbsoluteToLocal( MousePosition ); FTimeToPixel TimeToPixelConverter = Section.IsInfinite() ? FTimeToPixel( ParentGeometry, GetSequencer().GetViewRange()) : FTimeToPixel( KeyAreaGeometry, TRange<float>( Section.GetStartTime(), Section.GetEndTime() ) ); // Check each key until we find one under the mouse (if any) TArray<FKeyHandle> KeyHandles = KeyArea->GetUnsortedKeyHandles(); for( int32 KeyIndex = 0; KeyIndex < KeyHandles.Num(); ++KeyIndex ) { FKeyHandle KeyHandle = KeyHandles[KeyIndex]; float KeyPosition = TimeToPixelConverter.TimeToPixel( KeyArea->GetKeyTime(KeyHandle) ); FGeometry KeyGeometry = KeyAreaGeometry.MakeChild( FVector2D( KeyPosition - FMath::TruncToFloat(SequencerSectionConstants::KeySize.X/2.0f), ((KeyAreaGeometry.Size.Y*.5f)-(SequencerSectionConstants::KeySize.Y*.5f)) ), SequencerSectionConstants::KeySize ); if( KeyGeometry.IsUnderLocation( MousePosition ) ) { // The current key is under the mouse return FSelectedKey( Section, KeyArea, KeyHandle ); } } // no key was selected in the current key area but the mouse is in the key area so it cannot possibly be in any other key area return FSelectedKey(); } } // No key was selected in any key area return FSelectedKey(); }
/** * Gets the geometry of a section, optionally inflated by some margin * * @param AllottedGeometry The geometry of the area where sections are located * @param NodeHeight The height of the section area (and its children) * @param SectionInterface Interface to the section to get geometry for * @param TimeToPixelConverter Converts time to pixels and vice versa */ FGeometry GetSectionGeometry( const FGeometry& AllottedGeometry, int32 RowIndex, int32 MaxTracks, float NodeHeight, TSharedPtr<ISequencerSection> SectionInterface, const FTimeToPixel& TimeToPixelConverter ) { const UMovieSceneSection* Section = SectionInterface->GetSectionObject(); // Where to start drawing the section float StartX = TimeToPixelConverter.TimeToPixel( Section->GetStartTime() ); // Where to stop drawing the section float EndX = TimeToPixelConverter.TimeToPixel( Section->GetEndTime() ); // Actual section length without grips. float SectionLengthActual = EndX-StartX; float SectionLengthWithGrips = SectionLengthActual+SequencerSectionConstants::SectionGripSize*2; float ActualHeight = NodeHeight / MaxTracks; // Compute allotted geometry area that can be used to draw the section return AllottedGeometry.MakeChild( FVector2D( StartX-SequencerSectionConstants::SectionGripSize, ActualHeight * RowIndex ), FVector2D( SectionLengthWithGrips, ActualHeight ) ); }
void SSection::PaintKeys( const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle ) const { UMovieSceneSection& SectionObject = *SectionInterface->GetSectionObject(); FSequencer& Sequencer = ParentSectionArea->GetSequencer(); static const FName BackgroundBrushName("Sequencer.SectionArea.Background"); static const FName KeyBrushName("Sequencer.Key"); const FSlateBrush* BackgroundBrush = FEditorStyle::GetBrush(BackgroundBrushName); const FSlateBrush* KeyBrush = FEditorStyle::GetBrush(KeyBrushName); static const FName SelectionColorName("SelectionColor"); static const FName SelectionInactiveColorName("SelectionColorInactive"); static const FName SelectionColorPressedName("SelectionColor_Pressed"); const FLinearColor PressedKeyColor = FEditorStyle::GetSlateColor(SelectionColorPressedName).GetColor( InWidgetStyle ); const FLinearColor SelectedKeyColor = FEditorStyle::GetSlateColor(SelectionColorName).GetColor( InWidgetStyle ); const FLinearColor SelectedInactiveColor = FEditorStyle::GetSlateColor(SelectionInactiveColorName).GetColor( InWidgetStyle ) * FLinearColor(.25, .25, .25, 1); // Make the color a little darker since it's not very visible next to white keyframes. // @todo Sequencer temp color, make hovered brighter than selected. FLinearColor HoveredKeyColor = SelectedKeyColor * FLinearColor(1.5,1.5,1.5,1.0f); // Draw all keys in each key area for( int32 KeyAreaIndex = 0; KeyAreaIndex < KeyAreas.Num(); ++KeyAreaIndex ) { const FKeyAreaElement& Element = KeyAreas[KeyAreaIndex]; // Get the key area at the same index of the section. Each section in this widget has the same layout and the same number of key areas const TSharedRef<IKeyArea>& KeyArea = Element.KeyAreaNode.GetKeyArea( SectionIndex ); FGeometry KeyAreaGeometry = GetKeyAreaGeometry( Element, AllottedGeometry ); FTimeToPixel TimeToPixelConverter = SectionObject.IsInfinite() ? FTimeToPixel( ParentGeometry, GetSequencer().GetViewRange()) : FTimeToPixel( KeyAreaGeometry, TRange<float>( SectionObject.GetStartTime(), SectionObject.GetEndTime() ) ); // Draw a box for the key area // @todo Sequencer - Allow the IKeyArea to do this FSlateDrawElement::MakeBox( OutDrawElements, LayerId, KeyAreaGeometry.ToPaintGeometry(), BackgroundBrush, MyClippingRect, ESlateDrawEffect::None, FLinearColor( .1f, .1f, .1f, 0.7f ) ); int32 KeyLayer = LayerId + 1; TArray<FKeyHandle> KeyHandles = KeyArea->GetUnsortedKeyHandles(); for( int32 KeyIndex = 0; KeyIndex < KeyHandles.Num(); ++KeyIndex ) { FKeyHandle KeyHandle = KeyHandles[KeyIndex]; float KeyTime = KeyArea->GetKeyTime(KeyHandle); // Omit keys which would not be visible if( SectionObject.IsTimeWithinSection( KeyTime ) ) { FLinearColor KeyColor( 1.0f, 1.0f, 1.0f, 1.0f ); // Where to start drawing the key (relative to the section) float KeyPosition = TimeToPixelConverter.TimeToPixel( KeyTime ); FSelectedKey TestKey( SectionObject, KeyArea, KeyHandle ); bool bSelected = Sequencer.GetSelection().IsSelected( TestKey ); bool bActive = Sequencer.GetSelection().GetActiveSelection() == FSequencerSelection::EActiveSelection::KeyAndSection; if( TestKey == PressedKey ) { KeyColor = PressedKeyColor; } else if( TestKey == HoveredKey ) { KeyColor = HoveredKeyColor; } else if( bSelected ) { if (bActive) { KeyColor = SelectedKeyColor; } else { KeyColor = SelectedInactiveColor; } } // Draw the key FSlateDrawElement::MakeBox( OutDrawElements, // always draw selected keys on top of other keys bSelected ? KeyLayer+1 : KeyLayer, // Center the key along Y. Ensure the middle of the key is at the actual key time KeyAreaGeometry.ToPaintGeometry( FVector2D( KeyPosition - FMath::CeilToFloat(SequencerSectionConstants::KeySize.X/2.0f), ((KeyAreaGeometry.Size.Y*.5f)-(SequencerSectionConstants::KeySize.Y*.5f)) ), SequencerSectionConstants::KeySize ), KeyBrush, MyClippingRect, ESlateDrawEffect::None, KeyColor ); } } } }
void FMoveKeys::OnDrag( const FPointerEvent& MouseEvent, const FVector2D& LocalMousePos, const FTimeToPixel& TimeToPixelConverter, TSharedPtr<FTrackNode> SequencerNode ) { // Convert the delta position to a delta time amount that was moved float MouseTime = TimeToPixelConverter.PixelToTime(LocalMousePos.X); float SelectedKeyTime = DraggedKey.KeyArea->GetKeyTime(DraggedKey.KeyHandle.GetValue()); float DistanceMoved = MouseTime - SelectedKeyTime; if( DistanceMoved != 0.0f ) { float TimeDelta = DistanceMoved; // Snapping if ( Settings->GetIsSnapEnabled() ) { bool bSnappedToKeyTime = false; if ( Settings->GetSnapKeyTimesToKeys() ) { TArray<float> OutSnapTimes; GetKeySnapTimes(OutSnapTimes, SequencerNode); TArray<float> InitialTimes; for ( FSelectedKey SelectedKey : SelectedKeys ) { InitialTimes.Add(SelectedKey.KeyArea->GetKeyTime(SelectedKey.KeyHandle.GetValue()) + DistanceMoved); } float OutInitialTime = 0.f; float OutSnapTime = 0.f; if ( SnapToTimes( InitialTimes, OutSnapTimes, TimeToPixelConverter, OutInitialTime, OutSnapTime ) ) { bSnappedToKeyTime = true; TimeDelta = OutSnapTime - (OutInitialTime - DistanceMoved); } } if ( bSnappedToKeyTime == false && Settings->GetSnapKeyTimesToInterval() ) { TimeDelta = Settings->SnapTimeToInterval( MouseTime ) - SelectedKeyTime; } } for( FSelectedKey SelectedKey : SelectedKeys ) { UMovieSceneSection* Section = SelectedKey.Section; TSharedPtr<IKeyArea>& KeyArea = SelectedKey.KeyArea; // Tell the key area to move the key. We reset the key index as a result of the move because moving a key can change it's internal index KeyArea->MoveKey( SelectedKey.KeyHandle.GetValue(), TimeDelta ); // Update the key that moved float NewKeyTime = KeyArea->GetKeyTime( SelectedKey.KeyHandle.GetValue() ); // If the key moves outside of the section resize the section to fit the key // @todo Sequencer - Doesn't account for hitting other sections if( NewKeyTime > Section->GetEndTime() ) { Section->SetEndTime( NewKeyTime ); } else if( NewKeyTime < Section->GetStartTime() ) { Section->SetStartTime( NewKeyTime ); } } } }
void FMoveSection::OnDrag( const FPointerEvent& MouseEvent, const FVector2D& LocalMousePos, const FTimeToPixel& TimeToPixelConverter, TSharedPtr<FTrackNode> SequencerNode ) { if( Section.IsValid() ) { auto Sections = SequencerNode->GetSections(); TArray<UMovieSceneSection*> MovieSceneSections; for (int32 i = 0; i < Sections.Num(); ++i) { MovieSceneSections.Add(Sections[i]->GetSectionObject()); } FVector2D TotalDelta = LocalMousePos - DragOffset; float DistanceMoved = TotalDelta.X / TimeToPixelConverter.GetPixelsPerInput(); float DeltaTime = DistanceMoved; if ( Settings->GetIsSnapEnabled() ) { bool bSnappedToSection = false; if ( Settings->GetSnapSectionTimesToSections() ) { TArray<float> TimesToSnapTo; GetSectionSnapTimes(TimesToSnapTo, Section.Get(), SequencerNode, true); TArray<float> TimesToSnap; TimesToSnap.Add(DistanceMoved + Section->GetStartTime()); TimesToSnap.Add(DistanceMoved + Section->GetEndTime()); float OutSnappedTime = 0.f; float OutNewTime = 0.f; if (SnapToTimes(TimesToSnap, TimesToSnapTo, TimeToPixelConverter, OutSnappedTime, OutNewTime)) { DeltaTime = OutNewTime - (OutSnappedTime - DistanceMoved); bSnappedToSection = true; } } if ( bSnappedToSection == false && Settings->GetSnapSectionTimesToInterval() ) { float NewStartTime = DistanceMoved + Section->GetStartTime(); DeltaTime = Settings->SnapTimeToInterval( NewStartTime ) - Section->GetStartTime(); } } int32 TargetRowIndex = Section->GetRowIndex(); // vertical dragging - master tracks only if (SequencerNode->GetTrack()->SupportsMultipleRows() && Sections.Num() > 1) { float TrackHeight = Sections[0]->GetSectionHeight(); if (LocalMousePos.Y < 0.f || LocalMousePos.Y > TrackHeight) { int32 MaxRowIndex = 0; for (int32 i = 0; i < Sections.Num(); ++i) { if (Sections[i]->GetSectionObject() != Section.Get()) { MaxRowIndex = FMath::Max(MaxRowIndex, Sections[i]->GetSectionObject()->GetRowIndex()); } } TargetRowIndex = FMath::Clamp(Section->GetRowIndex() + FMath::FloorToInt(LocalMousePos.Y / TrackHeight), 0, MaxRowIndex + 1); } } bool bDeltaX = !FMath::IsNearlyZero(TotalDelta.X); bool bDeltaY = TargetRowIndex != Section->GetRowIndex(); if (bDeltaX && bDeltaY && !Section->OverlapsWithSections(MovieSceneSections, TargetRowIndex - Section->GetRowIndex(), DeltaTime)) { Section->MoveSection(DeltaTime, DraggedKeyHandles); Section->SetRowIndex(TargetRowIndex); } else { if (bDeltaY && !Section->OverlapsWithSections(MovieSceneSections, TargetRowIndex - Section->GetRowIndex(), 0.f)) { Section->SetRowIndex(TargetRowIndex); } if (bDeltaX) { if (!Section->OverlapsWithSections(MovieSceneSections, 0, DeltaTime)) { Section->MoveSection(DeltaTime, DraggedKeyHandles); } else { // Find the borders of where you can move to TRange<float> SectionBoundaries = GetSectionBoundaries(Section.Get(), SequencerNode); float LeftMovementMaximum = SectionBoundaries.GetLowerBoundValue() - Section->GetStartTime(); float RightMovementMaximum = SectionBoundaries.GetUpperBoundValue() - Section->GetEndTime(); // Tell the section to move itself and any data it has Section->MoveSection( FMath::Clamp(DeltaTime, LeftMovementMaximum, RightMovementMaximum), DraggedKeyHandles ); } } } } }
void FResizeSection::OnDrag( const FPointerEvent& MouseEvent, const FVector2D& LocalMousePos, const FTimeToPixel& TimeToPixelConverter, TSharedPtr<FTrackNode> SequencerNode ) { bool bIsDilating = MouseEvent.IsControlDown(); if( Section.IsValid() ) { // Convert the current mouse position to a time float NewTime = TimeToPixelConverter.PixelToTime( LocalMousePos.X ); // Find the borders of where you can drag to TRange<float> SectionBoundaries = GetSectionBoundaries(Section.Get(), SequencerNode); // Snapping if ( Settings->GetIsSnapEnabled() ) { bool bSnappedToSection = false; if ( Settings->GetSnapSectionTimesToSections() ) { TArray<float> TimesToSnapTo; GetSectionSnapTimes(TimesToSnapTo, Section.Get(), SequencerNode, bIsDilating); TOptional<float> NewSnappedTime = SnapToTimes(NewTime, TimesToSnapTo, TimeToPixelConverter); if (NewSnappedTime.IsSet()) { NewTime = NewSnappedTime.GetValue(); bSnappedToSection = true; } } if ( bSnappedToSection == false && Settings->GetSnapSectionTimesToInterval() ) { NewTime = Settings->SnapTimeToInterval(NewTime); } } if( bDraggingByEnd ) { // Dragging the end of a section // Ensure we aren't shrinking past the start time NewTime = FMath::Clamp( NewTime, Section->GetStartTime(), SectionBoundaries.GetUpperBoundValue() ); if (bIsDilating) { float NewSize = NewTime - Section->GetStartTime(); float DilationFactor = NewSize / Section->GetTimeSize(); Section->DilateSection(DilationFactor, Section->GetStartTime(), DraggedKeyHandles); } else { Section->SetEndTime( NewTime ); } } else if( !bDraggingByEnd ) { // Dragging the start of a section // Ensure we arent expanding past the end time NewTime = FMath::Clamp( NewTime, SectionBoundaries.GetLowerBoundValue(), Section->GetEndTime() ); if (bIsDilating) { float NewSize = Section->GetEndTime() - NewTime; float DilationFactor = NewSize / Section->GetTimeSize(); Section->DilateSection(DilationFactor, Section->GetEndTime(), DraggedKeyHandles); } else { Section->SetStartTime( NewTime ); } } } }