// Copyright Epic Games, Inc. All Rights Reserved. #include "AnimTimeline/SAnimTrackArea.h" #include "Types/PaintArgs.h" #include "Layout/ArrangedChildren.h" #include "Rendering/DrawElements.h" #include "Layout/LayoutUtils.h" #include "Widgets/SWeakWidget.h" #include "Styling/AppStyle.h" #include "AnimTimeline/SAnimTrack.h" #include "AnimTimeline/SAnimOutliner.h" #include "AnimTimeline/AnimTimelineTrack.h" #include "AnimTimeline/AnimModel.h" #include "AnimTimeline/AnimTimeSliderController.h" FAnimTrackAreaSlot::FAnimTrackAreaSlot(const TSharedPtr& InSlotContent) : TAlignmentWidgetSlotMixin(HAlign_Fill, VAlign_Top) { TrackWidget = InSlotContent; AttachWidget( SNew(SWeakWidget) .Clipping(EWidgetClipping::ClipToBounds) .PossiblyNullContent(InSlotContent) ); } float FAnimTrackAreaSlot::GetVerticalOffset() const { TSharedPtr PinnedTrackWidget = TrackWidget.Pin(); return PinnedTrackWidget.IsValid() ? PinnedTrackWidget->GetPhysicalPosition() : 0.f; } void SAnimTrackArea::Construct(const FArguments& InArgs, const TSharedRef& InAnimModel, const TSharedRef& InTimeSliderController) { WeakModel = InAnimModel; WeakTimeSliderController = InTimeSliderController; } void SAnimTrackArea::SetOutliner(const TSharedPtr& InOutliner) { WeakOutliner = InOutliner; } void SAnimTrackArea::Empty() { TrackSlots.Empty(); Children.Empty(); } void SAnimTrackArea::AddTrackSlot(const TSharedRef& InTrack, const TSharedPtr& InSlot) { TrackSlots.Add(InTrack, InSlot); Children.AddSlot(FAnimTrackAreaSlot::FSlotArguments(MakeUnique(InSlot))); } TSharedPtr SAnimTrackArea::FindTrackSlot(const TSharedRef& InTrack) { // Remove stale entries TrackSlots.Remove(TWeakPtr()); return TrackSlots.FindRef(InTrack).Pin(); } void SAnimTrackArea::OnArrangeChildren(const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren) const { for (int32 ChildIndex = 0; ChildIndex < Children.Num(); ++ChildIndex) { const FAnimTrackAreaSlot& CurChild = Children[ChildIndex]; const EVisibility ChildVisibility = CurChild.GetWidget()->GetVisibility(); if (!ArrangedChildren.Accepts(ChildVisibility)) { continue; } const FMargin Padding(0, CurChild.GetVerticalOffset(), 0, 0); const AlignmentArrangeResult XResult = AlignChild(static_cast(AllottedGeometry.GetLocalSize().X), CurChild, Padding, 1.0f, false); const AlignmentArrangeResult YResult = AlignChild(static_cast(AllottedGeometry.GetLocalSize().Y), CurChild, Padding, 1.0f, false); ArrangedChildren.AddWidget(ChildVisibility, AllottedGeometry.MakeChild( CurChild.GetWidget(), FVector2D(XResult.Offset, YResult.Offset), FVector2D(XResult.Size, YResult.Size) ) ); } } FVector2D SAnimTrackArea::ComputeDesiredSize( float ) const { FVector2D MaxSize(0.0f, 0.0f); for (int32 ChildIndex = 0; ChildIndex < Children.Num(); ++ChildIndex) { const FAnimTrackAreaSlot& CurChild = Children[ChildIndex]; const EVisibility ChildVisibilty = CurChild.GetWidget()->GetVisibility(); if (ChildVisibilty != EVisibility::Collapsed) { FVector2D ChildDesiredSize = CurChild.GetWidget()->GetDesiredSize(); MaxSize.X = FMath::Max(MaxSize.X, ChildDesiredSize.X); MaxSize.Y = FMath::Max(MaxSize.Y, ChildDesiredSize.Y); } } return MaxSize; } FChildren* SAnimTrackArea::GetChildren() { return &Children; } int32 SAnimTrackArea::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const { // paint the child widgets FArrangedChildren ArrangedChildren(EVisibility::Visible); ArrangeChildren(AllottedGeometry, ArrangedChildren); const FPaintArgs NewArgs = Args.WithNewParent(this); for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex) { FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex]; FSlateRect ChildClipRect = MyCullingRect.IntersectionWith(CurWidget.Geometry.GetLayoutBoundingRect()); const int32 ThisWidgetLayerId = CurWidget.Widget->Paint(NewArgs, CurWidget.Geometry, ChildClipRect, OutDrawElements, LayerId + 2, InWidgetStyle, bParentEnabled); LayerId = FMath::Max(LayerId, ThisWidgetLayerId); } return LayerId; } void SAnimTrackArea::UpdateHoverStates( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { } FReply SAnimTrackArea::OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { TSharedPtr TimeSliderController = WeakTimeSliderController.Pin(); if(TimeSliderController.IsValid()) { WeakOutliner.Pin()->ClearSelection(); WeakModel.Pin()->ClearDetailsView(); TimeSliderController->OnMouseButtonDown(*this, MyGeometry, MouseEvent); } return FReply::Unhandled(); } FReply SAnimTrackArea::OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { TSharedPtr TimeSliderController = WeakTimeSliderController.Pin(); if(TimeSliderController.IsValid()) { return WeakTimeSliderController.Pin()->OnMouseButtonUp(*this, MyGeometry, MouseEvent); } return FReply::Unhandled(); } FReply SAnimTrackArea::OnMouseMove( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { UpdateHoverStates(MyGeometry, MouseEvent); const TSharedPtr TimeSliderController = WeakTimeSliderController.Pin(); if(TimeSliderController.IsValid()) { FReply Reply = WeakTimeSliderController.Pin()->OnMouseMove(*this, MyGeometry, MouseEvent); // Handle right click scrolling on the track area if (Reply.IsEventHandled()) { if (MouseEvent.IsMouseButtonDown(EKeys::RightMouseButton) && HasMouseCapture()) { WeakOutliner.Pin()->ScrollByDelta(static_cast(-MouseEvent.GetCursorDelta().Y)); } } return Reply; } return FReply::Unhandled(); } FReply SAnimTrackArea::OnMouseWheel( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { TSharedPtr TimeSliderController = WeakTimeSliderController.Pin(); if(TimeSliderController.IsValid()) { FReply Reply = WeakTimeSliderController.Pin()->OnMouseWheel(*this, MyGeometry, MouseEvent); if (Reply.IsEventHandled()) { return Reply; } const float ScrollAmount = -MouseEvent.GetWheelDelta() * GetGlobalScrollAmount(); WeakOutliner.Pin()->ScrollByDelta(ScrollAmount); return FReply::Handled(); } return FReply::Unhandled(); } void SAnimTrackArea::OnMouseLeave(const FPointerEvent& MouseEvent) { } FCursorReply SAnimTrackArea::OnCursorQuery( const FGeometry& MyGeometry, const FPointerEvent& CursorEvent ) const { if (CursorEvent.IsMouseButtonDown(EKeys::RightMouseButton) && HasMouseCapture()) { return FCursorReply::Cursor(EMouseCursor::GrabHandClosed); } else { TSharedPtr TimeSliderController = WeakTimeSliderController.Pin(); if(TimeSliderController.IsValid()) { return TimeSliderController->OnCursorQuery(SharedThis(this), MyGeometry, CursorEvent); } } return FCursorReply::Unhandled(); } void SAnimTrackArea::Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) { CachedGeometry = AllottedGeometry; for (int32 Index = 0; Index < Children.Num();) { if (!StaticCastSharedRef(Children[Index].GetWidget())->ChildWidgetIsValid()) { Children.RemoveAt(Index); } else { ++Index; } } }