// Copyright Epic Games, Inc. All Rights Reserved. #include "SSequencerHierarchyBrowser.h" #include "Sequencer.h" #include "MovieSceneSequence.h" #include "MovieScene.h" #include "Tracks/MovieSceneCinematicShotTrack.h" #include "Tracks/MovieSceneSubTrack.h" #include "Sections/MovieSceneCinematicShotSection.h" #include "Sections/MovieSceneSubSection.h" #include "SlateOptMacros.h" #include "Widgets/SNullWidget.h" #include "Widgets/SBoxPanel.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Layout/SScrollBorder.h" #include "Widgets/Images/SImage.h" #include "Widgets/SOverlay.h" #include "Widgets/Text/STextBlock.h" #include "EditorFontGlyphs.h" #include "Framework/Application/SlateApplication.h" #define LOCTEXT_NAMESPACE "SSequencerHierarchyBrowser" struct FSequencerHierarchyNode { FSequencerHierarchyNode(const FText& InLabel, UMovieSceneSubSection* InSubSection) : Label(InLabel) , SubSection(InSubSection) {} virtual ~FSequencerHierarchyNode() {} FText Label; TWeakObjectPtr SubSection; TArray> Children; }; class SSequencerHierarchyNodeRow : public STableRow> { SLATE_BEGIN_ARGS(SSequencerHierarchyNodeRow) {} SLATE_END_ARGS() public: void Construct(const FArguments& InArgs, const TSharedRef& InOwnerTableView, TWeakPtr InWeakSequencerHierarchyNode, TWeakPtr InWeakSequencerHierarchyBrowser) { WeakSequencerHierarchyBrowser = InWeakSequencerHierarchyBrowser; WeakSequencerHierarchyNode = InWeakSequencerHierarchyNode; STableRow>::ConstructInternal(STableRow::FArguments() .Padding(5.f) , InOwnerTableView); TSharedPtr SequencerHierarchyNode = WeakSequencerHierarchyNode.Pin(); if (!SequencerHierarchyNode) { return; } TSharedPtr SequencerHierarchyBrowser = WeakSequencerHierarchyBrowser.Pin(); this->ChildSlot [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SExpanderArrow, SharedThis(this)) ] + SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() [ SNew(SOverlay) + SOverlay::Slot() [ SNew(SImage) .Image_Lambda([SequencerHierarchyNode] { if (!SequencerHierarchyNode->SubSection.IsValid()) { return FAppStyle::GetNoBrush(); } else if (SequencerHierarchyNode->SubSection.Get()->IsA()) { return FAppStyle::GetBrush("Sequencer.Tracks.CinematicShot"); } else { return FAppStyle::GetBrush("Sequencer.Tracks.Sub"); } }) ] ] + SHorizontalBox::Slot() .VAlign(VAlign_Center) .Padding(FMargin(5.f, 5.f, 5.f, 5.f)) [ SNew(STextBlock) .Text(SequencerHierarchyNode->Label) ] ]; } private: TWeakPtr WeakSequencerHierarchyBrowser; TWeakPtr WeakSequencerHierarchyNode; }; UMovieScene* SSequencerHierarchyBrowser::GetMovieScene() const { TSharedPtr Sequencer = WeakSequencer.Pin(); UMovieSceneSequence* Sequence = Sequencer ? Sequencer->GetFocusedMovieSceneSequence() : nullptr; UMovieScene* MovieScene = Sequence ? Sequence->GetMovieScene() : nullptr; return MovieScene; } void SSequencerHierarchyBrowser::Construct(const FArguments& InArgs, TWeakPtr InWeakSequencer) { WeakSequencer = InWeakSequencer; UMovieScene* MovieScene = GetMovieScene(); if (!ensure(MovieScene)) { return; } TWeakPtr WeakTabManager = SharedThis(this); auto HandleGenerateRow = [WeakTabManager](TSharedPtr InNode, const TSharedRef& InOwnerTableView) -> TSharedRef { return SNew(SSequencerHierarchyNodeRow, InOwnerTableView, InNode, WeakTabManager); }; auto HandleGetChildren = [](TSharedPtr InParent, TArray>& OutChildren) { OutChildren.Append(InParent->Children); }; FDisplayMetrics DisplayMetrics; FSlateApplication::Get().GetCachedDisplayMetrics(DisplayMetrics); TreeView = SNew(STreeView>) .OnGenerateRow_Lambda(HandleGenerateRow) .OnGetChildren_Lambda(HandleGetChildren) .TreeItemsSource(&NodeGroupsTree) .OnSelectionChanged(this, &SSequencerHierarchyBrowser::HandleTreeSelectionChanged); ChildSlot [ SNew(SBorder) .BorderImage(FAppStyle::GetBrush("ToolPanel.GroupBorder")) [ SNew(SVerticalBox) + SVerticalBox::Slot() .MaxHeight(DisplayMetrics.PrimaryDisplayHeight * 0.5) [ SNew(SScrollBorder, TreeView.ToSharedRef()) [ TreeView.ToSharedRef() ] ] ] ]; UpdateTree(); } SSequencerHierarchyBrowser::~SSequencerHierarchyBrowser() { } void SSequencerHierarchyBrowser::AddChildren(TSharedRef ParentNode, UMovieSceneSequence* Sequence) { UMovieScene* MovieScene = Sequence->GetMovieScene(); for (UMovieSceneTrack* Track : MovieScene->GetTracks()) { if (UMovieSceneCinematicShotTrack* CinematicShotTrack = Cast(Track)) { TArray ShotSections; for (UMovieSceneSection* Section : CinematicShotTrack->GetAllSections()) { if (UMovieSceneCinematicShotSection* ShotSection = Cast(Section)) { if (ShotSection->GetSequence()) { ShotSections.Add(ShotSection); } } } ShotSections.Sort([](const UMovieSceneCinematicShotSection& A, const UMovieSceneCinematicShotSection& B) { return A.GetShotDisplayName().Compare(B.GetShotDisplayName()) < 0; }); for (UMovieSceneCinematicShotSection* ShotSection : ShotSections) { TSharedPtr ChildNode = MakeShared(FText::FromString(ShotSection->GetShotDisplayName()), ShotSection); ParentNode->Children.Add(ChildNode); AddChildren(ChildNode.ToSharedRef(), ShotSection->GetSequence()); } } else if (UMovieSceneSubTrack* SubTrack = Cast(Track)) { TArray SubSections; for (UMovieSceneSection* Section : SubTrack->GetAllSections()) { if (UMovieSceneSubSection* SubSection = Cast(Section)) { if (SubSection->GetSequence()) { SubSections.Add(SubSection); } } } SubSections.Sort([](const UMovieSceneSubSection& A, const UMovieSceneSubSection& B) { return A.GetSequence()->GetDisplayName().CompareTo(B.GetSequence()->GetDisplayName()) < 0; }); for (UMovieSceneSubSection* SubSection : SubSections) { TSharedPtr ChildNode = MakeShared(SubSection->GetSequence()->GetDisplayName(), SubSection); ParentNode->Children.Add(ChildNode); AddChildren(ChildNode.ToSharedRef(), SubSection->GetSequence()); } } } } void SSequencerHierarchyBrowser::UpdateTree() { TSharedPtr Sequencer = WeakSequencer.Pin(); UMovieSceneSequence* Sequence = Sequencer ? Sequencer->GetRootMovieSceneSequence() : nullptr; if (!ensure(Sequencer) || !ensure(Sequence)) { return; } TSharedRef NodeTree = Sequencer->GetNodeTree(); NodeGroupsTree.Empty(); // Make nodes from this parent sequence TSharedPtr ParentNode = MakeShared(Sequence->GetDisplayName(), nullptr); NodeGroupsTree.Add(ParentNode); AddChildren(ParentNode.ToSharedRef(), Sequence); NodeGroupsTree.Sort([](const TSharedPtr& A, const TSharedPtr& B) { return A->Label.CompareTo(B->Label) < 0; }); TreeView->SetTreeItemsSource(&NodeGroupsTree); TreeView->SetItemExpansion(ParentNode, true); } void SSequencerHierarchyBrowser::HandleTreeSelectionChanged(TSharedPtr InSelectedNode, ESelectInfo::Type SelectionType) { TSharedPtr Sequencer = WeakSequencer.Pin(); if (!ensure(Sequencer)) { return; } if (!InSelectedNode.IsValid()) { return; } if (!InSelectedNode->SubSection.IsValid()) { Sequencer->PopToSequenceInstance(MovieSceneSequenceID::Root); } else { Sequencer->FocusSequenceInstance(*InSelectedNode->SubSection.Get()); } FSlateApplication::Get().FindWidgetWindow(AsShared())->RequestDestroyWindow(); } #undef LOCTEXT_NAMESPACE