Files
UnrealEngineUWP/Engine/Source/Editor/Sequencer/Private/SSequencerSectionAreaView.cpp
max chen 2f554a1d49 Sequencer: Speculative fix for tooltip crash when adjusting ease in/out
[FYI] grayson.edge

#ROBOMERGE-AUTHOR: max.chen
#ROBOMERGE-SOURCE: CL 19074328 via CL 19086855 via CL 19087301 via CL 19088529
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v921-19075845)

[CL 19131440 by max chen in ue5-main branch]
2022-02-24 19:02:41 -05:00

289 lines
11 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SSequencerSectionAreaView.h"
#include "Sequencer.h"
#include "Types/PaintArgs.h"
#include "Layout/ArrangedChildren.h"
#include "CommonMovieSceneTools.h"
#include "MovieSceneTrack.h"
#include "MovieScene.h"
namespace SequencerSectionAreaConstants
{
/** Background color of section areas */
const FLinearColor BackgroundColor( .1f, .1f, .1f, 0.5f );
}
namespace SequencerSectionUtils
{
/**
* 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();
const FFrameRate Resolution = Section->GetTypedOuter<UMovieScene>()->GetTickResolution();
// Sections with an infite (open) start bound use the currently visible geometry and time range
float PixelStartX = Section->HasStartFrame() ? TimeToPixelConverter.FrameToPixel( Section->GetInclusiveStartFrame() ) : AllottedGeometry.Position.X;
// Note the -1 pixel at the end is because the section does not actually end at the end time if there is a section starting at that same time. It is more important that a section lines up correctly with it's true start time
float PixelEndX = Section->HasEndFrame() ? TimeToPixelConverter.FrameToPixel( Section->GetExclusiveEndFrame() ) : AllottedGeometry.Position.X + AllottedGeometry.GetLocalSize().X;
const float MinSectionWidth = 1.f;
float SectionLength = FMath::Max(MinSectionWidth, PixelEndX-PixelStartX);
float GripOffset = 0;
if (Section->HasStartFrame() && Section->HasEndFrame())
{
float NewSectionLength = FMath::Max(SectionLength, MinSectionWidth + SectionInterface->GetSectionGripSize() * 2.f);
GripOffset = (NewSectionLength - SectionLength) / 2.f;
SectionLength = NewSectionLength;
}
float ActualHeight = NodeHeight / MaxTracks;
// Compute allotted geometry area that can be used to draw the section
return AllottedGeometry.MakeChild(
FVector2D( PixelStartX-GripOffset, ActualHeight * RowIndex ),
FVector2D( SectionLength, ActualHeight ) );
}
}
void SSequencerSectionAreaView::Construct( const FArguments& InArgs, TSharedRef<FSequencerDisplayNode> Node )
{
ViewRange = InArgs._ViewRange;
check( Node->GetType() == ESequencerNode::Track );
SectionAreaNode = StaticCastSharedRef<FSequencerTrackNode>( Node );
// Generate widgets for sections in this view
GenerateSectionWidgets();
}
FVector2D SSequencerSectionAreaView::ComputeDesiredSize(float) const
{
// Note: X Size is not used
FVector2D Size(100, 0.f);
if (Children.Num())
{
for (int32 Index = 0; Index < Children.Num(); ++Index)
{
Size.Y = FMath::Max(Size.Y, Children[Index]->GetDesiredSize().Y);
}
}
else
{
Size.Y = SectionAreaNode->GetNodeHeight();
}
return Size;
}
void SSequencerSectionAreaView::GenerateSectionWidgets()
{
Children.Empty();
if( SectionAreaNode.IsValid() )
{
TArray< TSharedRef<ISequencerSection> >& Sections = SectionAreaNode->GetSections();
for ( int32 SectionIndex = 0; SectionIndex < Sections.Num(); ++SectionIndex )
{
Children.Add(
SNew( SSequencerSection, SectionAreaNode.ToSharedRef(), SectionIndex )
.Visibility( this, &SSequencerSectionAreaView::GetSectionVisibility, Sections[SectionIndex]->GetSectionObject() )
.IsEnabled(this, &SSequencerSectionAreaView::GetSectionEnabled, Sections[SectionIndex])
.ToolTipText(this, &SSequencerSectionAreaView::GetSectionToolTip, Sections[SectionIndex]));
}
}
}
EVisibility SSequencerSectionAreaView::GetSectionVisibility(UMovieSceneSection* SectionObject) const
{
return EVisibility::Visible;
}
bool SSequencerSectionAreaView::GetSectionEnabled(TSharedRef<ISequencerSection> InSequencerSection) const
{
return !InSequencerSection->IsReadOnly();
}
FText SSequencerSectionAreaView::GetSectionToolTip(TSharedRef<ISequencerSection> InSequencerSection) const
{
const UMovieSceneSection* SectionObject = InSequencerSection->GetSectionObject();
const UMovieScene* MovieScene = SectionObject ? SectionObject->GetTypedOuter<UMovieScene>() : nullptr;
// Optional section specific content to add to tooltip
FText SectionToolTipContent = InSequencerSection->GetSectionToolTip();
FText SectionTitleText = InSequencerSection->GetSectionTitle();
if (!SectionTitleText.IsEmpty())
{
SectionTitleText = FText::Format(FText::FromString(TEXT("{0}\n")), SectionTitleText);
}
// If the objects are valid and the section is not unbounded, add frame information to the tooltip
if (SectionObject && MovieScene && SectionObject->HasStartFrame() && SectionObject->HasEndFrame())
{
FFrameRate TickResolution = MovieScene->GetTickResolution();
FFrameRate DisplayRate = MovieScene->GetDisplayRate();
int32 StartFrame = ConvertFrameTime(SectionObject->GetInclusiveStartFrame(), TickResolution, DisplayRate).RoundToFrame().Value;
int32 EndFrame = ConvertFrameTime(SectionObject->GetExclusiveEndFrame(), TickResolution, DisplayRate).RoundToFrame().Value;
FText SectionToolTip;
if (SectionToolTipContent.IsEmpty())
{
SectionToolTip = FText::Format(NSLOCTEXT("SequencerSection", "TooltipFormat", "{0}{1} - {2} ({3} frames)"), SectionTitleText,
StartFrame,
EndFrame,
EndFrame - StartFrame);
}
else
{
SectionToolTip = FText::Format(NSLOCTEXT("SequencerSection", "TooltipFormatWithSectionContent", "{0}{1} - {2} ({3} frames)\n{4}"), SectionTitleText,
StartFrame,
EndFrame,
EndFrame - StartFrame,
SectionToolTipContent);
}
if (SectionObject->Easing.EaseIn.GetObject() && SectionObject->Easing.GetEaseInDuration() > 0)
{
int32 EaseInFrames = ConvertFrameTime(SectionObject->Easing.GetEaseInDuration(), TickResolution, DisplayRate).RoundToFrame().Value;
FText EaseInText = FText::Format(NSLOCTEXT("SequencerSection", "EaseInFormat", "Ease In: {0} ({1} frames)"), SectionObject->Easing.EaseIn->GetDisplayName(), EaseInFrames);
SectionToolTip = FText::Join(FText::FromString("\n"), SectionToolTip, EaseInText);
}
if (SectionObject->Easing.EaseOut.GetObject() && SectionObject->Easing.GetEaseOutDuration() > 0)
{
int32 EaseOutFrames = ConvertFrameTime(SectionObject->Easing.GetEaseOutDuration(), TickResolution, DisplayRate).RoundToFrame().Value;
FText EaseOutText = FText::Format(NSLOCTEXT("SequencerSection", "EaseOutFormat", "Ease Out: {0} ({1} frames)"), SectionObject->Easing.EaseOut->GetDisplayName(), EaseOutFrames);
SectionToolTip = FText::Join(FText::FromString("\n"), SectionToolTip, EaseOutText);
}
return SectionToolTip;
}
else
{
if (SectionToolTipContent.IsEmpty())
{
return InSequencerSection->GetSectionTitle();
}
else
{
return FText::Format(NSLOCTEXT("SequencerSection", "TooltipSectionContentFormat", "{0}{1}"), SectionTitleText, SectionToolTipContent);
}
}
}
/** SWidget Interface */
int32 SSequencerSectionAreaView::OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
{
FArrangedChildren ArrangedChildren(EVisibility::Visible);
ArrangeChildren(AllottedGeometry, ArrangedChildren);
for (int32 ChildIndex = 0; ChildIndex < ArrangedChildren.Num(); ++ChildIndex)
{
FArrangedWidget& CurWidget = ArrangedChildren[ChildIndex];
FSlateRect ChildClipRect = MyCullingRect.IntersectionWith( CurWidget.Geometry.GetLayoutBoundingRect() );
LayerId = CurWidget.Widget->Paint( Args.WithNewParent(this), CurWidget.Geometry, ChildClipRect, OutDrawElements, LayerId, InWidgetStyle, ShouldBeEnabled( bParentEnabled ) );
}
return LayerId + 1;
}
void SSequencerSectionAreaView::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{
if (Children.Num())
{
StableSort(&Children[0], Children.Num(), [](const TSharedRef<SSequencerSection>& A, const TSharedRef<SSequencerSection>& B){
UMovieSceneSection* SectionA = A->GetSectionInterface()->GetSectionObject();
UMovieSceneSection* SectionB = B->GetSectionInterface()->GetSectionObject();
return SectionA && SectionB && SectionA->GetOverlapPriority() < SectionB->GetOverlapPriority();
});
for( int32 WidgetIndex = 0; WidgetIndex < Children.Num(); ++WidgetIndex )
{
Children[WidgetIndex]->CacheParentGeometry(AllottedGeometry);
}
}
}
void SSequencerSectionAreaView::OnArrangeChildren( const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const
{
int32 MaxRowIndex = 0;
if (SectionAreaNode->GetSubTrackMode() == FSequencerTrackNode::ESubTrackMode::None)
{
for (int32 WidgetIndex = 0; WidgetIndex < Children.Num(); ++WidgetIndex)
{
const TSharedRef<SSequencerSection>& Widget = Children[WidgetIndex];
TSharedPtr<ISequencerSection> SectionInterface = Widget->GetSectionInterface();
if (SectionInterface->GetSectionObject())
{
MaxRowIndex = FMath::Max(MaxRowIndex, SectionInterface->GetSectionObject()->GetRowIndex());
}
}
}
int32 MaxTracks = MaxRowIndex + 1;
FTimeToPixel TimeToPixelConverter = GetTimeToPixel( AllottedGeometry );
for( int32 WidgetIndex = 0; WidgetIndex < Children.Num(); ++WidgetIndex )
{
const TSharedRef<SSequencerSection>& Widget = Children[WidgetIndex];
TSharedPtr<ISequencerSection> SectionInterface = Widget->GetSectionInterface();
if (!SectionInterface->GetSectionObject())
{
continue;
}
int32 RowIndex = SectionAreaNode->GetSubTrackMode() == FSequencerTrackNode::ESubTrackMode::None ? SectionInterface->GetSectionObject()->GetRowIndex() : 0;
EVisibility WidgetVisibility = Widget->GetVisibility();
if( ArrangedChildren.Accepts( WidgetVisibility ) )
{
FGeometry SectionGeometry = SequencerSectionUtils::GetSectionGeometry( AllottedGeometry, RowIndex, MaxTracks, Widget->GetDesiredSize().Y, SectionInterface, TimeToPixelConverter );
ArrangedChildren.AddWidget(
WidgetVisibility,
AllottedGeometry.MakeChild( Widget, FVector2D(SectionGeometry.Position), SectionGeometry.GetLocalSize() )
);
}
}
}
FTimeToPixel SSequencerSectionAreaView::GetTimeToPixel( const FGeometry& AllottedGeometry ) const
{
const UMovieSceneTrack* Track = SectionAreaNode->GetTrack();
if (!Track)
{
return FTimeToPixel(AllottedGeometry, ViewRange.Get(), FFrameRate());
}
const FFrameRate Resolution = Track->GetTypedOuter<UMovieScene>()->GetTickResolution();
return FTimeToPixel( AllottedGeometry, ViewRange.Get(), Resolution );
}
FSequencer& SSequencerSectionAreaView::GetSequencer() const
{
return SectionAreaNode->GetSequencer();
}