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;
}
Пример #2
0
    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 ) );
	}
Пример #4
0
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 ) );
	}
Пример #6
0
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 );
			}
		}
	}
}