// Copyright Epic Games, Inc. All Rights Reserved. #include "SectionLayout.h" #include "Layout/Geometry.h" #include "MVVM/ViewModels/ViewModelIterators.h" #include "MVVM/ViewModels/SectionModel.h" #include "MVVM/ViewModels/CategoryModel.h" #include "MVVM/ViewModels/TrackModel.h" #include "MVVM/ViewModels/ChannelModel.h" #include "Algo/Sort.h" namespace UE { namespace Sequencer { bool FSectionLayoutElementKeyFuncs::Matches(const FSectionLayoutElement& A, const FSectionLayoutElement& B) { if (A.GetModel() != B.GetModel()) { return false; } TArrayView> ChannelsA = A.GetChannels(), ChannelsB = B.GetChannels(); if (ChannelsA.Num() != ChannelsB.Num()) { return false; } for (int32 Index = 0; Index < ChannelsA.Num(); ++Index) { if (ChannelsA[Index] != ChannelsB[Index]) { return false; } } return true; } uint32 FSectionLayoutElementKeyFuncs::GetKeyHash(const FSectionLayoutElement& Key) { uint32 Hash = GetTypeHash(Key.GetModel()) ; for (TWeakPtr Channel : Key.GetChannels()) { Hash = HashCombine(GetTypeHash(Channel), Hash); } return Hash; } FSectionLayoutElement FSectionLayoutElement::FromGroup(const TSharedPtr& InGroup, const TSharedPtr& InChannelRoot, float InOffset, float InHeight) { FSectionLayoutElement Tmp; Tmp.Type = Group; for (TSharedPtr OutlinerItemExtension: InChannelRoot->GetDescendantsOfType(true /*bIncludeThis*/)) { if (TViewModelPtr Outliner = OutlinerItemExtension->GetLinkedOutlinerItem()) { if (Outliner->IsFilteredOut() == false) { TSharedPtr Model = OutlinerItemExtension->GetLinkedOutlinerItem().AsModel(); for (TSharedPtr ChannelGroup : Model->GetDescendantsOfType()) { TArrayView> Channels = ChannelGroup->GetChannels(); for (const TWeakViewModelPtr < FChannelModel>& Channel : Channels) { Tmp.WeakChannels.Add(Channel); } } } } } Tmp.LocalOffset = InOffset; Tmp.Height = InHeight; Tmp.DataModel = InGroup; return Tmp; } FSectionLayoutElement FSectionLayoutElement::FromChannel(const TSharedPtr& InChannel, float InOffset, float InHeight) { FSectionLayoutElement Tmp; Tmp.Type = Single; Tmp.WeakChannels.Add(InChannel); Tmp.LocalOffset = InOffset; Tmp.Height = InHeight; Tmp.DataModel = InChannel; return Tmp; } FSectionLayoutElement FSectionLayoutElement::EmptySpace(const TSharedPtr& InModel, float InOffset, float InHeight) { FSectionLayoutElement Tmp; Tmp.Type = Single; Tmp.LocalOffset = InOffset; Tmp.Height = InHeight; Tmp.DataModel = InModel; return Tmp; } FSectionLayoutElement::EType FSectionLayoutElement::GetType() const { return Type; } float FSectionLayoutElement::GetOffset() const { return LocalOffset; } float FSectionLayoutElement::GetHeight() const { return Height; } TArrayView> FSectionLayoutElement::GetChannels() const { return WeakChannels; } TSharedPtr FSectionLayoutElement::GetModel() const { return DataModel.Pin(); } FGeometry FSectionLayoutElement::ComputeGeometry(const FGeometry& SectionAreaGeometry) const { return SectionAreaGeometry.MakeChild( FVector2D(0, LocalOffset), FVector2D(SectionAreaGeometry.GetLocalSize().X, Height) ); } FSectionLayout::FSectionLayout(TSharedPtr SectionModel) : Height(0.f) { using namespace UE::Sequencer; float VerticalOffset = 0.f; bool bLayoutChildren = true; // First layout the parent if (TSharedPtr ParentTrackModel = StaticCastSharedPtr(SectionModel->GetParent())) { const bool bIsExpanded = ParentTrackModel->IsExpanded(); const FOutlinerSizing ParentSizing = ParentTrackModel->GetOutlinerSizing(); VerticalOffset += ParentSizing.PaddingTop; const float ParentModelHeight = ParentSizing.Height; if (!bIsExpanded) { Elements.Add(FSectionLayoutElement::FromGroup(ParentTrackModel, SectionModel, VerticalOffset, ParentModelHeight)); } else { Elements.Add(FSectionLayoutElement::EmptySpace(ParentTrackModel, VerticalOffset, ParentModelHeight)); } VerticalOffset += ParentModelHeight + ParentSizing.PaddingBottom; Height = VerticalOffset; // Don't layout children if the parent is collapsed or filtered out. bLayoutChildren = bIsExpanded && !ParentTrackModel->IsFilteredOut(); } if (!bLayoutChildren) { // No need to sort out layout elements since there's only one. return; } // Then layout the children for (FParentFirstChildIterator ChildIt = SectionModel->GetDescendants(); ChildIt; ++ChildIt) { TSharedPtr Model = *ChildIt; bLayoutChildren = true; FOutlinerSizing ChildSizing; if (FLinkedOutlinerExtension* LinkedOutlinerExtension = Model->CastThis()) { TSharedPtr LinkedOutlinerItem = LinkedOutlinerExtension->GetLinkedOutlinerItem(); const bool bIsFilteredOut = !LinkedOutlinerItem || LinkedOutlinerItem->IsFilteredOut(); if (bIsFilteredOut) { continue; } bLayoutChildren = LinkedOutlinerItem->IsExpanded(); ChildSizing = LinkedOutlinerItem->GetOutlinerSizing(); } else if (IGeometryExtension* GeometryExtension = Model->CastThis()) { FVirtualGeometry Geometry = GeometryExtension->GetVirtualGeometry(); ChildSizing = FOutlinerSizing(Geometry.GetHeight()); } else { ensure(false); } VerticalOffset += ChildSizing.PaddingTop; const float ChildModelHeight = ChildSizing.Height; if (TSharedPtr Channel = Model->CastThisShared()) { Elements.Add(FSectionLayoutElement::FromChannel(Channel, VerticalOffset, ChildModelHeight)); } else if (Model->HasChildren() && !bLayoutChildren) { Elements.Add(FSectionLayoutElement::FromGroup(Model, Model, VerticalOffset, ChildModelHeight)); } else { Elements.Add(FSectionLayoutElement::EmptySpace(Model, VerticalOffset, ChildModelHeight)); } VerticalOffset += ChildModelHeight + ChildSizing.PaddingBottom; Height = VerticalOffset; if (!bLayoutChildren) { ChildIt.IgnoreCurrentChildren(); } } Algo::SortBy(Elements, &FSectionLayoutElement::GetOffset); } const TArray& FSectionLayout::GetElements() const { return Elements; } float FSectionLayout::GetTotalHeight() const { return Height; } } // namespace Sequencer } //namespace UE