// Copyright Epic Games, Inc. All Rights Reserved. #include "SSequencer.h" #include "Engine/Blueprint.h" #include "Filters/Filters/SequencerTrackFilter_CustomText.h" #include "Filters/Filters/SequencerTrackFilters.h" #include "Filters/Menus/SequencerViewOptionsMenu.h" #include "Filters/SequencerFilterBar.h" #include "Filters/SFilterSearchBox.h" #include "Filters/Widgets/SFilterBarClippingHorizontalBox.h" #include "Filters/Widgets/SFilterBarIsolateHideShow.h" #include "Filters/Widgets/SSequencerCustomTextFilterDialog.h" #include "Filters/Widgets/SSequencerFilterBar.h" #include "MovieSceneSequence.h" #include "MVVM/CurveEditorExtension.h" #include "MVVM/ViewModels/CategoryModel.h" #include "MVVM/ViewModels/ChannelModel.h" #include "MVVM/ViewModels/ViewModelIterators.h" #include "MVVM/Extensions/IRenameableExtension.h" #include "MVVM/Extensions/HierarchicalCacheExtension.h" #include "MVVM/ViewModels/SequencerEditorViewModel.h" #include "MVVM/ViewModels/SequencerTrackAreaViewModel.h" #include "MVVM/ViewModels/SequencerOutlinerViewModel.h" #include "MVVM/ViewModels/TrackRowModel.h" #include "MVVM/Selection/Selection.h" #include "MVVM/SharedViewModelData.h" #include "Sections/MovieSceneSubSection.h" #include "MovieScene.h" #include "Framework/MultiBox/MultiBoxDefs.h" #include "UObject/UObjectIterator.h" #include "Widgets/Text/STextBlock.h" #include "Framework/MultiBox/MultiBoxBuilder.h" #include "GameFramework/Actor.h" #include "Misc/FeedbackContext.h" #include "Modules/ModuleManager.h" #include "Editor.h" #include "IDetailsView.h" #include "IKeyArea.h" #include "Widgets/Layout/SBorder.h" #include "ISequencerEditTool.h" #include "Layout/WidgetPath.h" #include "Framework/Application/MenuStack.h" #include "Framework/Application/SlateApplication.h" #include "Widgets/Layout/SSpacer.h" #include "Widgets/Images/SImage.h" #include "Widgets/Images/SLayeredImage.h" #include "Widgets/Layout/SBox.h" #include "Widgets/Layout/SWrapBox.h" #include "Widgets/Layout/SGridPanel.h" #include "Widgets/Layout/SScrollBar.h" #include "Widgets/Layout/SScrollBorder.h" #include "Widgets/Input/SComboButton.h" #include "Widgets/Layout/SSeparator.h" #include "Widgets/Layout/SSplitter.h" #include "Widgets/Input/SSpinBox.h" #include "Widgets/Input/SCheckBox.h" #include "Widgets/Docking/SDockTab.h" #include "SPositiveActionButton.h" #include "Styling/AppStyle.h" #include "Kismet2/KismetEditorUtilities.h" #include "MovieSceneSequenceEditor.h" #include "Engine/Selection.h" #include "LevelEditorViewport.h" #include "Widgets/Navigation/SBreadcrumbTrail.h" #include "SequencerCommands.h" #include "Widgets/Input/SNumericDropDown.h" #include "SequencerCommonHelpers.h" #include "ISequencerWidgetsModule.h" #include "ScopedTransaction.h" #include "SequencerTimeSliderController.h" #include "SequencerToolMenuContext.h" #include "SSequencerSectionOverlay.h" #include "STemporarilyFocusedSpinBox.h" #include "MVVM/Views/STrackAreaView.h" #include "MVVM/Views/SSequencerTrackAreaView.h" #include "DragAndDrop/AssetDragDropOp.h" #include "DragAndDrop/ActorDragDropOp.h" #include "DragAndDrop/ActorDragDropGraphEdOp.h" #include "DragAndDrop/ClassDragDropOp.h" #include "DragAndDrop/FolderDragDropOp.h" #include "DragAndDrop/CompositeDragDropOp.h" #include "Widgets/Input/SSearchBox.h" #include "MVVM/Views/SSequencerOutlinerView.h" #include "MVVM/ViewModels/OutlinerColumns/IOutlinerColumn.h" #include "MVVM/ViewModels/OutlinerColumns/OutlinerColumnTypes.h" #include "MovieSceneTrackEditor.h" #include "SSequencerSplitterOverlay.h" #include "SequencerHotspots.h" #include "SSequencerTimePanel.h" #include "MVVM/ViewModels/VirtualTrackArea.h" #include "Framework/Commands/GenericCommands.h" #include "SequencerContextMenus.h" #include "Math/UnitConversion.h" #include "Widgets/Input/NumericUnitTypeInterface.inl" #include "FrameNumberDetailsCustomization.h" #include "SequencerSettings.h" #include "SSequencerTransformBox.h" #include "SSequencerStretchBox.h" #include "SSequencerDebugVisualizer.h" #include "SSequencerTreeFilterStatusBar.h" #include "ISequencerModule.h" #include "IMovieRendererInterface.h" #include "IVREditorModule.h" #include "EditorFontGlyphs.h" #include "HAL/PlatformApplicationMisc.h" #include "SSequencerPlayRateCombo.h" #include "Camera/CameraActor.h" #include "SCurveEditorPanel.h" #include "Tree/SCurveEditorTree.h" #include "Tree/CurveEditorTreeFilter.h" #include "Tree/SCurveEditorTreeTextFilter.h" #include "SequencerSelectionCurveFilter.h" #include "SCurveKeyDetailPanel.h" #include "MovieSceneTimeHelpers.h" #include "FrameNumberNumericInterface.h" #include "LevelSequence.h" #include "SequencerLog.h" #include "MovieSceneCopyableBinding.h" #include "SObjectBindingTagManager.h" #include "SSequencerGroupManager.h" #include "SSequencerHierarchyBrowser.h" #include "MovieSceneCopyableTrack.h" #include "Tracks/MovieSceneSubTrack.h" #include "IPropertyRowGenerator.h" #include "Fonts/FontMeasure.h" #include "Compilation/MovieSceneCompiledDataManager.h" #include "SequencerCustomizationManager.h" #include "EditorActorFolders.h" #include "Tracks/MovieSceneEventTrack.h" #include "ToolMenus.h" #include "MovieSceneFolder.h" #include "MovieSceneToolHelpers.h" #include "Editor/UnrealEdEngine.h" #include "UnrealEdGlobals.h" #include "UniversalObjectLocators/ActorLocatorFragment.h" #include "SequencerUtilities.h" #include "Sidebar/SidebarDrawerConfig.h" #include "Sidebar/SSidebar.h" #include "Sidebar/SSidebarContainer.h" #include "Widgets/SOverlay.h" #include "Filters/Widgets/SSequencerSearchBox.h" #include "IContentBrowserSingleton.h" #include "ContentBrowserModule.h" #include "Menus/SequencerToolbarUtils.h" #include "Settings/EditorExperimentalSettings.h" #define LOCTEXT_NAMESPACE "Sequencer" FSequencerOutlinerColumnVisibility::FSequencerOutlinerColumnVisibility(TSharedPtr InColumn) : Column(InColumn) , bIsColumnVisible(InColumn->IsColumnVisibleByDefault()) {} FSequencerOutlinerColumnVisibility::FSequencerOutlinerColumnVisibility(TSharedPtr InColumn, bool bInIsColumnVisible) : Column(InColumn) , bIsColumnVisible(bInIsColumnVisible) {} /* SSequencer interface *****************************************************************************/ UE_DISABLE_OPTIMIZATION_SHIP void SSequencer::Construct(const FArguments& InArgs, TSharedRef InSequencer) { using namespace UE::Sequencer; SequencerPtr = InSequencer; bIsActiveTimerRegistered = false; bUserIsSelecting = false; CachedClampRange = TRange::Empty(); CachedViewRange = TRange::Empty(); PendingFocus = FPendingWidgetFocus::MakeNoTextEdit(); OnPlaybackRangeBeginDrag = InArgs._OnPlaybackRangeBeginDrag; OnPlaybackRangeEndDrag = InArgs._OnPlaybackRangeEndDrag; OnSelectionRangeBeginDrag = InArgs._OnSelectionRangeBeginDrag; OnSelectionRangeEndDrag = InArgs._OnSelectionRangeEndDrag; OnMarkBeginDrag = InArgs._OnMarkBeginDrag; OnMarkEndDrag = InArgs._OnMarkEndDrag; OnReceivedFocus = InArgs._OnReceivedFocus; OnInitToolMenuContext = InArgs._OnInitToolMenuContext; RootCustomization.OnReceivedDragOver = InArgs._OnReceivedDragOver; RootCustomization.OnReceivedDrop = InArgs._OnReceivedDrop; RootCustomization.OnAssetsDrop = InArgs._OnAssetsDrop; RootCustomization.OnClassesDrop = InArgs._OnClassesDrop; RootCustomization.OnActorsDrop = InArgs._OnActorsDrop; RootCustomization.OnFoldersDrop = InArgs._OnFoldersDrop; TWeakPtr WeakSelf = StaticCastSharedRef(AsShared()); // Get the desired display format from the user's settings each time. TAttribute GetDisplayFormatAttr = MakeAttributeLambda( [WeakSelf] { if(TSharedPtr Target = WeakSelf.Pin()) { if (USequencerSettings* Settings = Target->GetSequencerSettings()) { return Settings->GetTimeDisplayFormat(); } } return EFrameNumberDisplayFormats::Frames; } ); // Get the number of zero pad frames from the user's settings as well. TAttribute GetZeroPadFramesAttr = MakeAttributeLambda( [WeakSelf]()->uint8 { if(TSharedPtr Target = WeakSelf.Pin()) { if (USequencerSettings* Settings = Target->GetSequencerSettings()) { return Settings->GetZeroPadFrames(); } } return 0; } ); FTimeSliderArgs TimeSliderArgs; { TimeSliderArgs.ViewRange = InArgs._ViewRange; TimeSliderArgs.ClampRange = InArgs._ClampRange; TimeSliderArgs.PlaybackRange = MakeAttributeSP(this, &SSequencer::GetViewSpacePlaybackRange, InArgs._PlaybackRange); TimeSliderArgs.TimeBounds = InArgs._TimeBounds; TimeSliderArgs.DisplayRate = TAttribute(InSequencer, &FSequencer::GetFocusedDisplayRate); TimeSliderArgs.TickResolution = TAttribute(InSequencer, &FSequencer::GetFocusedTickResolution); TimeSliderArgs.SelectionRange = InArgs._SelectionRange; TimeSliderArgs.OnPlaybackRangeChanged = FOnFrameRangeChanged::CreateSP(this, &SSequencer::OnViewSpacePlaybackRangeChanged, InArgs._OnPlaybackRangeChanged); TimeSliderArgs.OnPlaybackRangeBeginDrag = OnPlaybackRangeBeginDrag; TimeSliderArgs.OnPlaybackRangeEndDrag = OnPlaybackRangeEndDrag; TimeSliderArgs.OnSelectionRangeChanged = InArgs._OnSelectionRangeChanged; TimeSliderArgs.OnSelectionRangeBeginDrag = OnSelectionRangeBeginDrag; TimeSliderArgs.OnSelectionRangeEndDrag = OnSelectionRangeEndDrag; TimeSliderArgs.OnMarkBeginDrag = OnMarkBeginDrag; TimeSliderArgs.OnMarkEndDrag = OnMarkEndDrag; TimeSliderArgs.OnViewRangeChanged = InArgs._OnViewRangeChanged; TimeSliderArgs.OnClampRangeChanged = InArgs._OnClampRangeChanged; TimeSliderArgs.OnGetNearestKey = InArgs._OnGetNearestKey; TimeSliderArgs.IsPlaybackRangeLocked = InArgs._IsPlaybackRangeLocked; TimeSliderArgs.OnTogglePlaybackRangeLocked = InArgs._OnTogglePlaybackRangeLocked; TimeSliderArgs.ScrubPosition = InArgs._ScrubPosition; TimeSliderArgs.ScrubPositionText = InArgs._ScrubPositionText; TimeSliderArgs.ScrubPositionParent = InArgs._ScrubPositionParent; TimeSliderArgs.ScrubPositionParentChain = InArgs._ScrubPositionParentChain; TimeSliderArgs.OnScrubPositionParentChanged = InArgs._OnScrubPositionParentChanged; TimeSliderArgs.OnBeginScrubberMovement = InArgs._OnBeginScrubbing; TimeSliderArgs.OnEndScrubberMovement = InArgs._OnEndScrubbing; TimeSliderArgs.OnScrubPositionChanged = InArgs._OnScrubPositionChanged; TimeSliderArgs.PlaybackStatus = InArgs._PlaybackStatus; TimeSliderArgs.SubSequenceRange = InArgs._SubSequenceRange; TimeSliderArgs.VerticalFrames = InArgs._VerticalFrames; TimeSliderArgs.MarkedFrames = InArgs._MarkedFrames; TimeSliderArgs.GlobalMarkedFrames = InArgs._GlobalMarkedFrames; TimeSliderArgs.OnSetMarkedFrame = InArgs._OnSetMarkedFrame; TimeSliderArgs.OnAddMarkedFrame = InArgs._OnAddMarkedFrame; TimeSliderArgs.OnDeleteMarkedFrame = InArgs._OnDeleteMarkedFrame; TimeSliderArgs.OnDeleteAllMarkedFrames = InArgs._OnDeleteAllMarkedFrames; TimeSliderArgs.AreMarkedFramesLocked = InArgs._AreMarkedFramesLocked; TimeSliderArgs.OnToggleMarkedFramesLocked = InArgs._OnToggleMarkedFramesLocked; } OnGetPlaybackSpeeds = InArgs._OnGetPlaybackSpeeds; RootCustomization.AddMenuExtender = InArgs._AddMenuExtender; RootCustomization.ToolbarExtender = InArgs._ToolbarExtender; PlayTimeDisplay = StaticCastSharedRef>(SequencerPtr.Pin()->MakePlayTimeDisplay()); TAttribute ViewRangeAttribute = InArgs._ViewRange; GridPanel = ConstructTrackAreaGridPanel(InArgs, TimeSliderArgs); ViewOptionsMenu = MakeShared(); if (const TSharedPtr FilterBar = GetFilterBar()) { FilterBar->OnStateChanged().AddSP(this, &SSequencer::OnFilterBarStateChanged); FilterBar->OnFiltersChanged().AddSP(this, &SSequencer::OnTrackFiltersChanged); } ChildSlot [ SNew(SVerticalBox) // Toolbar + SVerticalBox::Slot() .AutoHeight() [ SNew(SBox) .Visibility(this, &SSequencer::GetShowSequencerToolbar) .Padding(FMargin(CommonPadding,0.0f,0.0f,0.f)) [ SNew(SWrapBox) .UseAllottedSize(true) .InnerSlotPadding(FVector2D(5, 0)) + SWrapBox::Slot() .FillEmptySpace(true) .FillLineWhenSizeLessThan(600) [ SAssignNew(ToolbarContainer, SBox) ] + SWrapBox::Slot() .FillEmptySpace(true) [ SNew(SHorizontalBox) // Right Aligned Breadcrumbs + SHorizontalBox::Slot() .HAlign(HAlign_Right) .VAlign(VAlign_Center) [ SNew(SSpacer) ] // History Back Button + SHorizontalBox::Slot() .HAlign(HAlign_Right) .VAlign(VAlign_Center) .AutoWidth() [ SNew(SVerticalBox) + SVerticalBox::Slot() .FillHeight(1.0f) [ SNew(SButton) .Visibility_Lambda([this] { return CanNavigateBreadcrumbs() ? EVisibility::Visible : EVisibility::Collapsed; } ) .VAlign(EVerticalAlignment::VAlign_Center) .ButtonStyle(FAppStyle::Get(), "SimpleButton") .ToolTipText_Lambda([this] { return SequencerPtr.Pin()->GetNavigateBackwardTooltip(); }) .ContentPadding(FMargin(1, 0)) .OnClicked_Lambda([this] { return SequencerPtr.Pin()->NavigateBackward(); }) .IsEnabled_Lambda([this] { return SequencerPtr.Pin()->CanNavigateBackward(); }) [ SNew(SBox) // scale up since the default icons are 16x16 .WidthOverride(20) .HeightOverride(20) [ SNew(SImage) .ColorAndOpacity(FSlateColor::UseForeground()) .Image(FAppStyle::Get().GetBrush("Icons.ArrowLeft")) ] ] ] ] // History Forward Button + SHorizontalBox::Slot() .HAlign(HAlign_Right) .VAlign(VAlign_Center) .AutoWidth() [ SNew(SVerticalBox) + SVerticalBox::Slot() .FillHeight(1.0f) [ SNew(SButton) .Visibility_Lambda([this] { return CanNavigateBreadcrumbs() ? EVisibility::Visible : EVisibility::Collapsed; } ) .VAlign(EVerticalAlignment::VAlign_Center) .ButtonStyle(FAppStyle::Get(), "SimpleButton") .ToolTipText_Lambda([this] { return SequencerPtr.Pin()->GetNavigateForwardTooltip(); }) .ContentPadding(FMargin(1, 0)) .OnClicked_Lambda([this] { return SequencerPtr.Pin()->NavigateForward(); }) .IsEnabled_Lambda([this] { return SequencerPtr.Pin()->CanNavigateForward(); }) [ SNew(SBox) // scale up since the default icons are 16x16 .WidthOverride(20) .HeightOverride(20) [ SNew(SImage) .ColorAndOpacity(FSlateColor::UseForeground()) .Image(FAppStyle::Get().GetBrush("Icons.ArrowRight")) ] ] ] ] // Separator + SHorizontalBox::Slot() .AutoWidth() .Padding(3, 0) [ SNew(SSeparator) .Visibility_Lambda([this] { return CanNavigateBreadcrumbs() ? EVisibility::Visible : EVisibility::Collapsed; } ) .Orientation(Orient_Vertical) ] + SHorizontalBox::Slot() .HAlign(HAlign_Right) .VAlign(VAlign_Center) .AutoWidth() [ SAssignNew(BreadcrumbPickerButton, SComboButton) .Visibility_Lambda([this] { return CanNavigateBreadcrumbs() ? EVisibility::Visible : EVisibility::Collapsed; } ) .ButtonStyle(FAppStyle::Get(), "SimpleButton") .OnGetMenuContent_Lambda([this] { return SNew(SSequencerHierarchyBrowser, SequencerPtr); }) .HasDownArrow(false) .ContentPadding(FMargin(3, 3)) .ButtonContent() [ SNew(SImage) .ColorAndOpacity(FSlateColor::UseForeground()) .Image(FAppStyle::Get().GetBrush("Icons.FolderOpen")) ] ] // Right Aligned Breadcrumbs + SHorizontalBox::Slot() .HAlign(HAlign_Right) .VAlign(VAlign_Center) .AutoWidth() [ SAssignNew(BreadcrumbTrail, SBreadcrumbTrail) .Visibility(this, &SSequencer::GetBreadcrumbTrailVisibility) .OnCrumbClicked(this, &SSequencer::OnCrumbClicked) .ButtonStyle(FAppStyle::Get(), "SimpleButton") .DelimiterImage(FAppStyle::Get().GetBrush("Sequencer.BreadcrumbIcon")) .TextStyle(FAppStyle::Get(), "Sequencer.BreadcrumbText") ] // Sequence Locking symbol + SHorizontalBox::Slot() .HAlign(HAlign_Right) .VAlign(VAlign_Center) .AutoWidth() [ SNew(SCheckBox) .Style(FAppStyle::Get(),"ToggleButtonCheckBoxAlt") .Type(ESlateCheckBoxType::CheckBox) // Use CheckBox instead of ToggleType since we're not putting ohter widget inside .Padding(FMargin(0.f)) .IsFocusable(false) .IsChecked_Lambda([this] { return GetIsSequenceReadOnly() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked; } ) .OnCheckStateChanged(this, &SSequencer::OnSetSequenceReadOnly) .ToolTipText_Lambda([this] { return GetIsSequenceReadOnly() ? LOCTEXT("UnlockSequence", "Unlock the animation so that it is editable") : LOCTEXT("LockSequence", "Lock the animation so that it is not editable"); } ) .CheckedImage(FAppStyle::Get().GetBrush("Icons.Lock")) .CheckedHoveredImage(FAppStyle::Get().GetBrush("Icons.Lock")) .CheckedPressedImage(FAppStyle::Get().GetBrush("Icons.Lock")) .UncheckedImage(FAppStyle::Get().GetBrush("Icons.Unlock")) .UncheckedHoveredImage(FAppStyle::Get().GetBrush("Icons.Unlock")) .UncheckedPressedImage(FAppStyle::Get().GetBrush("Icons.Unlock")) ] ] ] ] // Main content body + SVerticalBox::Slot() .FillHeight(1.f) [ SAssignNew(MainContentContainer, SBox) ] ]; RebuildForSidebar(); if (InSequencer->GetHostCapabilities().bSupportsCurveEditor) { TSharedPtr SequencerViewModel = InSequencer->GetViewModel()->CastThisShared(); FCurveEditorExtension* CurveEditorExtension = SequencerViewModel->CastDynamicChecked(); CurveEditorExtension->CreateCurveEditor(TimeSliderArgs); } ApplySequencerCustomization(RootCustomization); InSequencer->GetViewModel()->GetSelection()->KeySelection.OnChanged.AddSP(this, &SSequencer::HandleKeySelectionChanged); InSequencer->GetViewModel()->GetSelection()->Outliner.OnChanged.AddSP(this, &SSequencer::HandleOutlinerNodeSelectionChanged); ResetBreadcrumbs(); } void SSequencer::RebuildForSidebar() { MainContentContainer->SetContent(ConstructSidebarContent()); } TSharedRef SSequencer::ConstructSidebarContent() { const TSharedPtr Sequencer = SequencerPtr.Pin(); check(Sequencer.IsValid()); const bool bSupportsSidebar = Sequencer->GetHostCapabilities().bSupportsSidebar; USequencerSettings* const SequencerSettings = GetSequencerSettings(); check(IsValid(SequencerSettings)); const FSidebarState& SidebarState = SequencerSettings->GetSidebarState(); TSharedPtr OutWidget; FilterBarSplitterContainer = SNew(SBox); // Create the details sidebar only once to avoid having to re-register drawers if (bSupportsSidebar) { if (!DetailsSidebar.IsValid()) { SidebarContainer = SNew(SSidebarContainer); DetailsSidebar = SNew(SSidebar, SidebarContainer.ToSharedRef()) .TabLocation(ESidebarTabLocation::Right) .InitialDrawerSize(SidebarState.GetDrawerSize()) .OnStateChanged(this, &SSequencer::OnSidebarStateChanged) .OnGetContent(FOnGetContent::CreateLambda([this]() { return FilterBarSplitterContainer.ToSharedRef(); })); } SidebarContainer->RebuildSidebar(DetailsSidebar.ToSharedRef(), SidebarState); } else { DetailsSidebar.Reset(); } RebuildFilterBarContent(); if (SidebarState.IsHidden() || !bSupportsSidebar) { OutWidget = FilterBarSplitterContainer.ToSharedRef(); } else if (SidebarState.IsVisible()) { OutWidget = SidebarContainer.ToSharedRef(); } return OutWidget.ToSharedRef(); } void SSequencer::RebuildFilterBarContent() { FilterBarSplitterContainer->SetContent(ConstructFilterBarContent()); } TSharedRef SSequencer::ConstructFilterBarContent() { RebuildSearchAndFilterRow(); if (!IsFilterBarVisible() || GetFilterBarLayout() == EFilterBarLayout::Horizontal) { return ConstructGridOverlayContent(); } const TSharedRef FilterBarWidgetRef = FilterBarWidget.ToSharedRef(); return SNew(SSplitter) .Orientation(Orient_Horizontal) + SSplitter::Slot() .Value(TAttribute::CreateLambda([this]() { const TSharedPtr FilterBar = GetFilterBar(); if (FilterBar.IsValid() && (FilterBar->HasEnabledFilter() || FilterBar->HasEnabledCustomTextFilters())) { return GetSequencerSettings()->GetLastFilterBarSizeCoefficient(); } return 0.f; })) .OnSlotResized_Lambda([this](const float InNewCoefficient) { GetSequencerSettings()->SetLastFilterBarSizeCoefficient(InNewCoefficient); }) [ SFilterBarClippingHorizontalBox::WrapVerticalListWithHeading(FilterBarWidgetRef , FPointerEventHandler::CreateSP(FilterBarWidgetRef, &SSequencerFilterBar::OnMouseButtonUp)) ] + SSplitter::Slot() .Value(0.94f) [ ConstructGridOverlayContent() ]; } TSharedRef SSequencer::ConstructGridOverlayContent() { TAttribute FillCoefficient_0, FillCoefficient_1; { FillCoefficient_0.Bind(TAttribute::FGetter::CreateSP(this, &SSequencer::GetColumnFillCoefficient, 0)); FillCoefficient_1.Bind(TAttribute::FGetter::CreateSP(this, &SSequencer::GetColumnFillCoefficient, 1)); } return SNew(SOverlay) + SOverlay::Slot() [ GridPanel.ToSharedRef() ] + SOverlay::Slot() [ // track area virtual splitter overlay SAssignNew(TreeViewSplitter, SSequencerSplitterOverlay) .Style(FAppStyle::Get(), TEXT("Sequencer.AnimationOutliner.Splitter")) .Visibility(EVisibility::SelfHitTestInvisible) .OnSplitterFinishedResizing(this, &SSequencer::OnSplitterFinishedResizing) + SSplitter::Slot() .Value(FillCoefficient_0) // Can't use a minsize here because the grid panel that is actually being used to // lay out the widgets only supports fill coefficients and this leads to a disparity between the two // .MinSize(200) .OnSlotResized(SSplitter::FOnSlotResized::CreateSP(this, &SSequencer::OnColumnFillCoefficientChanged, 0)) [ SNew(SSpacer) ] + SSplitter::Slot() .Value(FillCoefficient_1) .OnSlotResized(SSplitter::FOnSlotResized::CreateSP(this, &SSequencer::OnColumnFillCoefficientChanged, 1)) [ SNew(SSpacer) ] ]; } TSharedRef SSequencer::ConstructTrackAreaGridPanel(const FArguments& InArgs, const FTimeSliderArgs& InTimeSliderArgs) { using namespace UE::Sequencer; check(SequencerPtr.IsValid()); const TSharedRef SequencerRef = SequencerPtr.Pin().ToSharedRef(); constexpr int32 Column0 = 0, Column1 = 1; constexpr int32 Row0 = 0, Row1 = 1, Row2 = 2, Row3 = 3, Row4 = 4; const FMargin ResizeBarPadding(4.f, 0, 0, 0); TAttribute FillCoefficient_0, FillCoefficient_1; { FillCoefficient_0.Bind(TAttribute::FGetter::CreateSP(this, &SSequencer::GetColumnFillCoefficient, 0)); FillCoefficient_1.Bind(TAttribute::FGetter::CreateSP(this, &SSequencer::GetColumnFillCoefficient, 1)); } USequencerSettings* const SequencerSettings = GetSequencerSettings(); ColumnFillCoefficients[0] = 0.3f; ColumnFillCoefficients[1] = 0.7f; if (IsValid(SequencerSettings)) { const float TreeViewWidth = SequencerSettings->GetTreeViewWidth(); const float TimelineWidth = 1.f - TreeViewWidth; if (TreeViewWidth > 0.f && TimelineWidth > 0.f) { ColumnFillCoefficients[0] = TreeViewWidth; ColumnFillCoefficients[1] = TimelineWidth; } } TimeSliderController = MakeShared(InTimeSliderArgs, SequencerPtr); TSharedRef TimeSliderControllerRef = TimeSliderController.ToSharedRef(); ISequencerWidgetsModule& SequencerWidgets = FModuleManager::Get().LoadModuleChecked(TEXT("SequencerWidgets")); ScrollBar = SNew(SScrollBar) .Thickness(FVector2D(9.f, 9.f)); PinnedAreaScrollBar = SNew(SScrollBar) .Thickness(FVector2D(9.f, 9.f)); TSharedPtr SequencerViewModel = SequencerRef->GetViewModel()->CastThisShared(); SAssignNew(PinnedTrackArea, SSequencerTrackAreaView, SequencerViewModel->GetPinnedTrackArea(), TimeSliderControllerRef); SAssignNew(PinnedTreeView, SSequencerOutlinerView, SequencerViewModel->GetOutliner(), PinnedTrackArea.ToSharedRef()) .Selection(SequencerRef->GetViewModel()->GetSelection()) .ExternalScrollbar(PinnedAreaScrollBar) .Clipping(EWidgetClipping::ClipToBounds); PinnedTrackArea->SetOutliner(PinnedTreeView); PinnedTrackArea->SetShowPinned(true); PinnedTrackArea->SetIsPinned(true); PinnedTreeView->SetShowPinned(true); SAssignNew(TrackArea, SSequencerTrackAreaView, SequencerViewModel->GetTrackArea(), TimeSliderControllerRef); SAssignNew(TreeView, SSequencerOutlinerView, SequencerViewModel->GetOutliner(), TrackArea.ToSharedRef()) .Selection(SequencerRef->GetViewModel()->GetSelection()) .ExternalScrollbar(ScrollBar) .Clipping(EWidgetClipping::ClipToBounds); TrackArea->SetOutliner(TreeView); TreeView->AddPinnedTreeView(PinnedTreeView); if (SequencerSettings) { InitializeOutlinerColumns(); } SequencerViewModel->GetTrackArea()->CastThisChecked()->InitializeDefaultEditTools(*TrackArea); SequencerViewModel->GetPinnedTrackArea()->CastThisChecked()->InitializeDefaultEditTools(*PinnedTrackArea); if (SequencerSettings) { SequencerViewModel->SetViewDensity(SequencerSettings->GetViewDensity()); } // Create the top and bottom sliders bool bMirrorLabels = false; TopTimeSlider = SequencerWidgets.CreateTimeSlider(TimeSliderControllerRef, bMirrorLabels); bMirrorLabels = true; BottomTimeSlider = SequencerWidgets.CreateTimeSlider(TimeSliderControllerRef, TAttribute(this, &SSequencer::GetBottomTimeSliderVisibility), bMirrorLabels); // Create bottom time range slider EShowRange Ranges = EShowRange(EShowRange::WorkingRange | EShowRange::ViewRange); if(InArgs._ShowPlaybackRangeInTimeSlider) { Ranges |= EShowRange::PlaybackRange; } BottomTimeRange = SequencerWidgets.CreateTimeRange( FTimeRangeArgs( Ranges, TimeSliderControllerRef, TAttribute(this, &SSequencer::GetTimeRangeVisibility), MakeAttributeSP(SequencerRef, &FSequencer::GetNumericTypeInterface, UE::Sequencer::ENumericIntent::Position), GetSequencerSettings()->GetPlaybackRangeStartColor(), GetSequencerSettings()->GetPlaybackRangeEndColor() ), SequencerWidgets.CreateTimeRangeSlider(TimeSliderControllerRef) ); static const FName TransportControlsLeft("Sequencer.TransportControls.Left"); static const FName TransportControlsRight("Sequencer.TransportControls.Right"); if (!UToolMenus::Get()->IsMenuRegistered(TransportControlsLeft)) { UToolMenus::Get()->RegisterMenu(TransportControlsLeft, NAME_None, EMultiBoxType::ToolBar); UToolMenus::Get()->RegisterMenu(TransportControlsRight, NAME_None, EMultiBoxType::ToolBar); } USequencerToolMenuContext* ContextObject = NewObject(); ContextObject->WeakSequencer = SequencerRef; return SNew(SGridPanel) .FillRow(2, 1.f) .FillColumn(0, FillCoefficient_0) .FillColumn(1, FillCoefficient_1) + SGridPanel::Slot(Column0, Row1) [ SNew(SBorder) .BorderImage(FAppStyle::GetBrush(TEXT("ToolPanel.GroupBorder"))) [ SNew(SSpacer) ] ] // outliner search box + SGridPanel::Slot(Column0, Row1, SGridPanel::Layer(10)) [ SNew(SBorder) .BorderImage(FAppStyle::GetBrush(TEXT("ToolPanel.GroupBorder"))) .Padding(FMargin(CommonPadding*2, CommonPadding)) .Clipping(EWidgetClipping::ClipToBounds) [ SAssignNew(SearchAndFilterRow, SVerticalBox) ] ] // main sequencer area + SGridPanel::Slot(Column0, Row2, SGridPanel::Layer(10)) .ColumnSpan(2) [ SAssignNew(MainSequencerArea, SVerticalBox) + SVerticalBox::Slot() .AutoHeight() .MaxHeight(TAttribute::Create(TAttribute::FGetter::CreateSP(this, &SSequencer::GetPinnedAreaMaxHeight))) .Padding(FMargin(0.f, 0.f, 0.f, CommonPadding)) [ SNew(SOverlay) .Visibility(this, &SSequencer::GetPinnedAreaVisibility) + SOverlay::Slot() [ SNew(SScrollBorder, PinnedTreeView.ToSharedRef()) [ SNew(SHorizontalBox) // outliner tree + SHorizontalBox::Slot() .FillWidth(FillCoefficient_0) [ PinnedTreeView.ToSharedRef() ] // track area + SHorizontalBox::Slot() .FillWidth(FillCoefficient_1) [ SNew(SBox) .Padding(ResizeBarPadding) .Clipping(EWidgetClipping::ClipToBounds) [ PinnedTrackArea.ToSharedRef() ] ] ] ] + SOverlay::Slot() .HAlign(HAlign_Right) [ PinnedAreaScrollBar.ToSharedRef() ] ] + SVerticalBox::Slot() [ SNew(SOverlay) + SOverlay::Slot() [ SNew(SScrollBorder, TreeView.ToSharedRef()) [ SNew(SHorizontalBox) // outliner tree + SHorizontalBox::Slot() .FillWidth(FillCoefficient_0) [ TreeView.ToSharedRef() ] // track area + SHorizontalBox::Slot() .FillWidth(FillCoefficient_1) [ SNew(SBox) .Padding(ResizeBarPadding) .Clipping(EWidgetClipping::ClipToBounds) [ TrackArea.ToSharedRef() ] ] ] ] + SOverlay::Slot() .HAlign(HAlign_Right) [ ScrollBar.ToSharedRef() ] + SOverlay::Slot() .VAlign(VAlign_Bottom) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() [ SAssignNew(SequencerTreeFilterStatusBar, SSequencerTreeFilterStatusBar, SequencerRef) .Visibility(EVisibility::Hidden) // Initially hidden, visible on hover of the info button ] ] ] ] // Info Button, Transport Controls and Current Frame + SGridPanel::Slot(Column0, Row4, SGridPanel::Layer(10)) [ SNew(SBorder) .BorderImage(FAppStyle::GetBrush(TEXT("ToolPanel.GroupBorder"))) .Clipping(EWidgetClipping::ClipToBounds) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Left) [ SNew(SButton) .VAlign(EVerticalAlignment::VAlign_Center) .ButtonStyle(FAppStyle::Get(), TEXT("SimpleButton")) .ToolTipText_Lambda([this] { return LOCTEXT("ShowStatus", "Show Status"); }) .ContentPadding(FMargin(1, 0)) .Visibility(this, &SSequencer::GetInfoButtonVisibility) .OnHovered_Lambda([this] { SequencerTreeFilterStatusBar->ShowStatusBar(); }) .OnUnhovered_Lambda([this] { SequencerTreeFilterStatusBar->FadeOutStatusBar(); }) .OnClicked_Lambda([this] { SequencerTreeFilterStatusBar->HideStatusBar(); return FReply::Handled(); }) [ SNew(SImage) .ColorAndOpacity(FSlateColor::UseForeground()) .Image(FAppStyle::Get().GetBrush(TEXT("Icons.Info.Small"))) ] ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Left) .Padding(FMargin(CommonPadding, 0.f, 0.f, 0.f)) [ UToolMenus::Get()->GenerateWidget(TransportControlsLeft, FToolMenuContext(SequencerRef->GetCommandBindings(), nullptr, ContextObject)) ] + SHorizontalBox::Slot() .HAlign(HAlign_Center) [ SequencerPtr.Pin()->MakeTransportControls(true) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Right) [ SNew(SButton) .VAlign(EVerticalAlignment::VAlign_Center) .ButtonStyle(FAppStyle::Get(), TEXT("NoBorder")) .ContentPadding(FMargin(1, 0)) [ SNew(SHorizontalBox) + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Right) .Padding(FMargin(CommonPadding, 0.f, 0.f, 0.f)) [ UToolMenus::Get()->GenerateWidget(TransportControlsRight, FToolMenuContext(SequencerRef->GetCommandBindings(), nullptr, ContextObject)) ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Right) .Padding(FMargin(CommonPadding, 0.f, 0.f, 0.f)) [ PlayTimeDisplay.ToSharedRef() ] + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .HAlign(HAlign_Right) .Padding(FMargin(CommonPadding, 0.f, 0.f, 0.f)) [ // Current loop index, if any SAssignNew(LoopIndexDisplay, STextBlock) .Font(FCoreStyle::GetDefaultFontStyle(TEXT("Regular"), 8)) .Text_Lambda([this]() -> FText { TOptional LoopIndex = SequencerPtr.Pin()->GetLocalLoopIndex(); return LoopIndex ? FText::AsNumber(LoopIndex.GetValue()) : FText(); }) ] ] ] ] ] // Second column + SGridPanel::Slot(Column1, Row1) .Padding(ResizeBarPadding) .RowSpan(3) [ SNew(SBorder) .BorderImage(FAppStyle::GetBrush(TEXT("ToolPanel.GroupBorder"))) [ SNew(SSpacer) ] ] + SGridPanel::Slot(Column1, Row1, SGridPanel::Layer(10)) .Padding(ResizeBarPadding) [ SNew(SBorder) .BorderImage(FAppStyle::GetBrush(TEXT("ToolPanel.GroupBorder"))) .BorderBackgroundColor(FLinearColor(.5f, .5f, .5f, 1.f)) .Padding(0) .Clipping(EWidgetClipping::ClipToBounds) [ TopTimeSlider.ToSharedRef() ] ] // Overlay that draws the tick lines + SGridPanel::Slot(Column1, Row2, SGridPanel::Layer(10)) .Padding(ResizeBarPadding) [ SNew(SSequencerSectionOverlay, TimeSliderControllerRef) .Visibility(this, &SSequencer::GetShowTickLines) .DisplayScrubPosition(false) .DisplayTickLines(true) .Clipping(EWidgetClipping::ClipToBounds) ] // Overlay that draws the scrub position + SGridPanel::Slot(Column1, Row2, SGridPanel::Layer(20)) .Padding(ResizeBarPadding) [ SNew(SSequencerSectionOverlay, TimeSliderControllerRef) .Visibility(EVisibility::HitTestInvisible) .DisplayScrubPosition(true) .DisplayTickLines(false) .DisplayMarkedFrames(true) .PaintPlaybackRangeArgs(this, &SSequencer::GetSectionPlaybackRangeArgs) .Clipping(EWidgetClipping::ClipToBounds) ] + SGridPanel::Slot(Column1, Row2, SGridPanel::Layer(30)) .Padding(ResizeBarPadding) .HAlign(HAlign_Left) .VAlign(VAlign_Top) [ // Transform box SAssignNew(TransformBox, SSequencerTransformBox, SequencerRef, *SequencerSettings, SequencerRef->GetNumericTypeInterface().ToSharedRef()) ] + SGridPanel::Slot(Column1, Row2, SGridPanel::Layer(40)) .Padding(ResizeBarPadding) .HAlign(HAlign_Left) .VAlign(VAlign_Top) [ // Stretch box SAssignNew(StretchBox, SSequencerStretchBox, SequencerRef, *SequencerSettings, SequencerRef->GetNumericTypeInterface().ToSharedRef()) ] // debug vis + SGridPanel::Slot(Column1, Row3, SGridPanel::Layer(10)) .Padding(ResizeBarPadding) [ SNew(SSequencerDebugVisualizer, SequencerRef) .ViewRange(FAnimatedRange::WrapAttribute(InArgs._ViewRange)) .Visibility(this, &SSequencer::GetDebugVisualizerVisibility) ] // play range sliders + SGridPanel::Slot(Column1, Row4, SGridPanel::Layer(10)) .Padding(ResizeBarPadding) [ SNew(SBorder) .BorderImage(FAppStyle::GetBrush(TEXT("ToolPanel.GroupBorder"))) .BorderBackgroundColor(FLinearColor(.5f, .5f, .5f, 1.f)) .Clipping(EWidgetClipping::ClipToBounds) .Padding(0) [ SNew(SOverlay) + SOverlay::Slot() [ BottomTimeSlider.ToSharedRef() ] + SOverlay::Slot() [ BottomTimeRange.ToSharedRef() ] ] ]; } UE_ENABLE_OPTIMIZATION_SHIP void SSequencer::BindCommands(TSharedRef SequencerCommandBindings, TSharedRef CurveEditorSharedBindings) { auto CanPasteFromHistory = [this]{ if (!HasFocusedDescendants() && !HasKeyboardFocus()) { return false; } return SequencerPtr.Pin()->GetClipboardStack().Num() != 0; }; auto CanOpenDirectorBlueprint = [this]{ UMovieSceneSequence* RootSequence = SequencerPtr.Pin()->GetRootMovieSceneSequence(); if (RootSequence && RootSequence->GetTypedOuter() == nullptr && UMovieScene::IsTrackClassAllowed(UMovieSceneEventTrack::StaticClass())) { return true; } return false; }; SequencerCommandBindings->MapAction( FGenericCommands::Get().Paste, FExecuteAction::CreateSP(this, &SSequencer::OnPaste), FCanExecuteAction::CreateSP(this, &SSequencer::CanPaste) ); SequencerCommandBindings->MapAction( FSequencerCommands::Get().PasteFromHistory, FExecuteAction::CreateSP(this, &SSequencer::PasteFromHistory), FCanExecuteAction::CreateLambda(CanPasteFromHistory) ); SequencerCommandBindings->MapAction( FSequencerCommands::Get().ToggleShowGotoBox, FExecuteAction::CreateLambda([this] { PlayTimeDisplay->Setup(); FSlateApplication::Get().SetKeyboardFocus(PlayTimeDisplay, EFocusCause::SetDirectly); }) ); SequencerCommandBindings->MapAction( FSequencerCommands::Get().ToggleShowTransformBox, FExecuteAction::CreateLambda([this] { TransformBox->ToggleVisibility(); }) ); // Allow jumping to the Sequencer tree search if you have Sequencer focused SequencerCommandBindings->MapAction( FSequencerCommands::Get().QuickTreeSearch, FExecuteAction::CreateLambda([this] { FSlateApplication::Get().SetKeyboardFocus(SearchBox, EFocusCause::SetDirectly); }) ); SequencerCommandBindings->MapAction( FSequencerCommands::Get().ToggleShowStretchBox, FExecuteAction::CreateLambda([this] { StretchBox->ToggleVisibility(); }) ); auto OpenDirectorBlueprint = [WeakSequencer = SequencerPtr] { UMovieSceneSequence* Sequence = WeakSequencer.Pin()->GetFocusedMovieSceneSequence(); FMovieSceneSequenceEditor* SequenceEditor = Sequence ? FMovieSceneSequenceEditor::Find(Sequence) : nullptr; if (SequenceEditor) { UBlueprint* DirectorBP = SequenceEditor->GetOrCreateDirectorBlueprint(Sequence); if (DirectorBP) { FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(DirectorBP); } } }; SequencerCommandBindings->MapAction( FSequencerCommands::Get().OpenDirectorBlueprint, FExecuteAction::CreateLambda(OpenDirectorBlueprint), FCanExecuteAction::CreateLambda(CanOpenDirectorBlueprint) ); SequencerCommandBindings->MapAction( FSequencerCommands::Get().OpenTaggedBindingManager, FExecuteAction::CreateSP(this, &SSequencer::OpenTaggedBindingManager) ); SequencerCommandBindings->MapAction( FSequencerCommands::Get().OpenNodeGroupsManager, FExecuteAction::CreateSP(this, &SSequencer::OpenNodeGroupsManager) ); } void SSequencer::OpenTickResolutionOptions() { if (TSharedPtr Window = WeakTickResolutionOptionsWindow.Pin()) { Window->DrawAttention(FWindowDrawAttentionParameters()); return; } TSharedRef TickResolutionOptionsWindow = SNew(SWindow) .Title(LOCTEXT("TickResolutionOptions_Title", "Advanced Time Properties")) .SupportsMaximize(false) .ClientSize(FVector2D(600.f, 510.f)) .Content() [ SNew(SSequencerTimePanel, SequencerPtr) ]; TSharedPtr ParentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared()); if (ParentWindow) { FSlateApplication::Get().AddWindowAsNativeChild(TickResolutionOptionsWindow, ParentWindow.ToSharedRef()); } else { FSlateApplication::Get().AddWindow(TickResolutionOptionsWindow); } WeakTickResolutionOptionsWindow = TickResolutionOptionsWindow; } void SSequencer::NotifyPostChange(const FPropertyChangedEvent& PropertyChangedEvent, FEditPropertyChain* PropertyThatChanged) { // @todo sequencer: is this still needed? } /* SSequencer implementation *****************************************************************************/ TRange SSequencer::GetViewSpacePlaybackRange(TAttribute> RangeAttribute) const { TRange Range = RangeAttribute.Get(); TSharedPtr TimeToPixel = TrackArea->GetTimeToPixel(); if (TimeToPixel->NonLinearTransform) { double EndSeconds = Range.GetUpperBoundValue() / TimeToPixel->GetTickResolution(); FFrameNumber EndFrame = (TimeToPixel->NonLinearTransform->SourceToView(EndSeconds) * TimeToPixel->GetTickResolution()).RoundToFrame(); Range.SetUpperBoundValue(EndFrame); } return Range; } void SSequencer::OnViewSpacePlaybackRangeChanged(TRange NewRange, FOnFrameRangeChanged OnPlaybackRangeChanged) { TSharedPtr TimeToPixel = TrackArea->GetTimeToPixel(); if (TimeToPixel->NonLinearTransform) { double EndSeconds = NewRange.GetUpperBoundValue() / TimeToPixel->GetTickResolution(); FFrameNumber EndFrame = (TimeToPixel->NonLinearTransform->ViewToSource(EndSeconds) * TimeToPixel->GetTickResolution()).RoundToFrame(); NewRange.SetUpperBoundValue(EndFrame); } OnPlaybackRangeChanged.ExecuteIfBound(NewRange); } void SSequencer::UpdateOutlinerViewColumns() { using namespace UE::Sequencer; // Save updated column list in settings TArray ColumnVisibilitySettings; for (FSequencerOutlinerColumnVisibility ColumnVisibility : OutlinerColumnVisibilities) { ColumnVisibilitySettings.Add(FColumnVisibilitySetting(ColumnVisibility.Column->GetColumnName(), ColumnVisibility.bIsColumnVisible)); } GetSequencerSettings()->SetOutlinerColumnVisibility(ColumnVisibilitySettings); // Filter out hidden columns to create a list of visible columns for the outliner views TArray> VisibleColumns; for (FSequencerOutlinerColumnVisibility ColumnVisibility : OutlinerColumnVisibilities) { if (ColumnVisibility.bIsColumnVisible) { VisibleColumns.Add(ColumnVisibility.Column); } } // Update both Outliner Views with updated visible outliner columns PinnedTreeView->SetOutlinerColumns(VisibleColumns); TreeView->SetOutlinerColumns(VisibleColumns); } void SSequencer::InitializeOutlinerColumns() { using namespace UE::Sequencer; TSharedPtr Sequencer = SequencerPtr.Pin(); if (!Sequencer.IsValid()) { return; } const TMap>& RegisteredColumns = Sequencer->GetOutlinerColumns(); // Retrieve previously saved column names and visibilities TArray ColumnSettings = GetSequencerSettings()->GetOutlinerColumnSettings(); TSet ColumnNamesFoundInSettings; // Add registered columns found in settings with their saved visibility state for (const FColumnVisibilitySetting& ColumnVisibility : ColumnSettings) { const TSharedPtr* OutlinerColumn = RegisteredColumns.Find(ColumnVisibility.ColumnName); if (OutlinerColumn) { ColumnNamesFoundInSettings.Add(ColumnVisibility.ColumnName); OutlinerColumnVisibilities.Add(FSequencerOutlinerColumnVisibility(*OutlinerColumn, ColumnVisibility.bIsVisible)); } } // Add registered columns not found in settings with their default visibility state for (const TTuple>& RegisteredColumn : RegisteredColumns) { if (!ColumnNamesFoundInSettings.Contains(RegisteredColumn.Key)) { OutlinerColumnVisibilities.Add(FSequencerOutlinerColumnVisibility(RegisteredColumn.Value)); } } Algo::Sort(OutlinerColumnVisibilities, [](const FSequencerOutlinerColumnVisibility& A, const FSequencerOutlinerColumnVisibility& B){ return A.Column->GetPosition() < B.Column->GetPosition(); }); UpdateOutlinerViewColumns(); } /* SSequencer callbacks *****************************************************************************/ void SSequencer::HandleKeySelectionChanged() { } void SSequencer::HandleOutlinerNodeSelectionChanged() { using namespace UE::Sequencer; TSharedPtr Sequencer = SequencerPtr.Pin(); if (!Sequencer.IsValid()) { return; } FViewModelPtr RootModel = Sequencer->GetViewModel(); FCurveEditorExtension* CurveEditorExtension = RootModel->CastDynamic(); if (!CurveEditorExtension) { return; } TSharedPtr CurveEditor = CurveEditorExtension->GetCurveEditor(); if (CurveEditor) { const USequencerSettings* SequencerSettings = GetSequencerSettings(); // If we're isolating to the selection and there is one, add the filter if (SequencerSettings->ShouldIsolateToCurveEditorSelection() && Sequencer->GetViewModel()->GetSelection()->Outliner.Num() != 0) { if (!SequencerSelectionCurveEditorFilter) { SequencerSelectionCurveEditorFilter = MakeShared(); } SequencerSelectionCurveEditorFilter->Update(Sequencer->GetViewModel()->GetSelection(), SequencerSettings->GetAutoExpandNodesOnSelection()); CurveEditor->GetTree()->AddFilter(SequencerSelectionCurveEditorFilter); } // If we're not isolating to the selection (or there is no selection) remove the filter else if (SequencerSelectionCurveEditorFilter) { CurveEditor->GetTree()->RemoveFilter(SequencerSelectionCurveEditorFilter); SequencerSelectionCurveEditorFilter = nullptr; } if (GetSequencerSettings()->ShouldSyncCurveEditorSelection()) { // We schedule a selection synchronization for the next update. This synchronization must happen // after all filters have been applied, because the items we want to select in the curve editor // might be currently filtered out, but will be visible when filters are re-evaluated. This is // why curve editor integration runs in FSequencerNodeTree after filtering. CurveEditorExtension->RequestSyncSelection(); } } if (NodeGroupManager.IsValid()) { NodeGroupManager->SelectItemsSelectedInSequencer(); } if (SequencerTreeFilterStatusBar.IsValid()) { SequencerTreeFilterStatusBar->UpdateText(); } } TSharedRef SSequencer::ConstructSearchAndFilterRow() { const TSharedPtr Sequencer = SequencerPtr.Pin(); if (!Sequencer.IsValid()) { return SNullWidget::NullWidget; } const TSharedPtr FilterBar = GetFilterBar(); if (!FilterBar.IsValid()) { return SNullWidget::NullWidget; } if (!SearchBox.IsValid()) { SearchBox = SNew(SSequencerSearchBox, FilterBar) .AddMetaData(FTagMetaData(TEXT("SequencerFilterSearch"))) .HintText(LOCTEXT("FilterSearch", "Search...")) .ToolTipText(LOCTEXT("FilterSearchHint", "Type here to search (pressing enter selects the results)")) .OnTextChanged(this, &SSequencer::OnOutlinerSearchChanged) .OnTextCommitted(this, &SSequencer::OnOutlinerSearchCommitted) .OnSaveSearchClicked(this, &SSequencer::OnOutlinerSearchSaved); } FilterBarWidget = FilterBar->GenerateWidget(SearchBox, GetFilterBarLayout()); if (!FilterComboButtonWidget.IsValid()) { FilterComboButtonWidget = FilterBar->MakeAddFilterButton(); } FilterBarWidget->SetMuted(FilterBar->AreFiltersMuted()); return SNew(SHorizontalBox) // Add Button + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) [ SNew(SPositiveActionButton) .OnGetMenuContent(this, &SSequencer::MakeAddMenu) .Icon(FAppStyle::Get().GetBrush("Icons.Plus")) .Text(LOCTEXT("Add", "Add")) .IsEnabled_Lambda([this]() { return !SequencerPtr.Pin()->IsReadOnly(); }) ] // Advanced Search Filter Combo Button + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(2.f, 0.f, 0.f, 0.f) [ FilterComboButtonWidget.ToSharedRef() ] // Advanced Search Box + SHorizontalBox::Slot() .VAlign(VAlign_Center) .Padding(2.f, 0.f, 0.f, 0.f) [ SearchBox.ToSharedRef() ] // Isolate / Hide / Show + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(2.f, 0.f, 0.f, 0.f) [ FilterBar->MakeIsolateHideShowPanel() ] // View Options Combo Button + SHorizontalBox::Slot() .AutoWidth() .VAlign(VAlign_Center) .Padding(2.f, 0.f, 0.f, 0.f) [ SAssignNew(ViewOptionsComboButton, SComboButton) .ContentPadding(2.f) .HAlign(HAlign_Center) .VAlign(VAlign_Center) .ToolTipText(LOCTEXT("ViewOptionsToolTip", "View Options")) .ComboButtonStyle(FAppStyle::Get(), TEXT("SimpleComboButtonWithIcon")) .OnGetMenuContent_Lambda([this]() -> TSharedRef { return ViewOptionsMenu->CreateMenu(SequencerPtr); }) .HasDownArrow(false) .ButtonContent() [ SNew(SImage) .ColorAndOpacity(FSlateColor::UseForeground()) .Image(FAppStyle::Get().GetBrush(TEXT("Icons.Settings"))) ] ]; } TSharedRef SSequencer::MakeToolBar() { static const FName MenuName("Sequencer.MainToolBar"); if (!UToolMenus::Get()->IsMenuRegistered(MenuName)) { UToolMenu* Toolbar = UToolMenus::Get()->RegisterMenu(MenuName, NAME_None, EMultiBoxType::ToolBar); Toolbar->AddDynamicSection("PopulateToolBar", FNewToolMenuDelegate::CreateStatic(&SSequencer::PopulateToolBar)); } TArray> AllExtenders; ISequencerModule& SequencerModule = FModuleManager::GetModuleChecked("Sequencer"); AllExtenders.Add(SequencerModule.GetToolBarExtensibilityManager()->GetAllExtenders()); AllExtenders.Append(ToolbarExtenders); TSharedPtr Extender = FExtender::Combine(AllExtenders); TSharedPtr Sequencer = SequencerPtr.Pin(); USequencerToolMenuContext* ContextObject = NewObject(); ContextObject->WeakSequencer = SequencerPtr; FToolMenuContext Context(SequencerPtr.Pin()->GetCommandBindings(), Extender, ContextObject); // Allow any toolkits to initialize their menu context OnInitToolMenuContext.ExecuteIfBound(Context); return UToolMenus::Get()->GenerateWidget(MenuName, Context); } void SSequencer::PopulateToolBar(UToolMenu* InMenu) { const FName SequencerToolbarStyleName = UE::Sequencer::GSequencerToolbarStyleName; USequencerToolMenuContext* ContextObject = InMenu->FindContext(); if (!ContextObject) { return; } TWeakPtr WeakSequencer = StaticCastWeakPtr(ContextObject->WeakSequencer); TSharedPtr Sequencer = WeakSequencer.Pin(); if (!Sequencer) { return; } TSharedRef SequencerWidget = StaticCastSharedRef(Sequencer->GetSequencerWidget()); { FToolMenuSection& Section = InMenu->AddSection("BaseCommands"); if (Sequencer->IsLevelEditorSequencer()) { TAttribute SaveIcon; SaveIcon.Bind(TAttribute::FGetter::CreateLambda([WeakSequencer] { if (WeakSequencer.IsValid()) { TArray MovieScenesToSave; MovieSceneHelpers::GetDescendantMovieScenes(WeakSequencer.Pin()->GetRootMovieSceneSequence(), MovieScenesToSave); for (UMovieScene* MovieSceneToSave : MovieScenesToSave) { UPackage* MovieScenePackageToSave = MovieSceneToSave->GetOuter()->GetOutermost(); if (MovieScenePackageToSave->IsDirty()) { return FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.SaveChanged"); } } } return FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.Save"); })); if (Sequencer->GetHostCapabilities().bSupportsSaveMovieSceneAsset) { FToolMenuEntry SaveEntry = FToolMenuEntry::InitToolBarButton( "Save", FUIAction(FExecuteAction::CreateSP(SequencerWidget, &SSequencer::OnSaveMovieSceneClicked)), LOCTEXT("SaveDirtyPackages", "Save"), LOCTEXT("SaveDirtyPackagesTooltip", "Saves the current sequence and any subsequences"), SaveIcon ); SaveEntry.StyleNameOverride = SequencerToolbarStyleName; Section.AddEntry(SaveEntry); } FToolMenuEntry FindInContentBrowserEntry = FToolMenuEntry::InitToolBarButton(FSequencerCommands::Get().FindInContentBrowser, TAttribute(), TAttribute(), FSlateIcon(FAppStyle::Get().GetStyleSetName(), "SystemWideCommands.FindInContentBrowser")); FindInContentBrowserEntry.StyleNameOverride = SequencerToolbarStyleName; Section.AddEntry(FindInContentBrowserEntry); FToolMenuEntry CreateCameraEntry = FToolMenuEntry::InitToolBarButton(FSequencerCommands::Get().CreateCamera); CreateCameraEntry.StyleNameOverride = SequencerToolbarStyleName; Section.AddEntry(CreateCameraEntry); if (Sequencer->GetHostCapabilities().bSupportsRenderMovie) { FToolMenuEntry RenderMovieEntry = FToolMenuEntry::InitToolBarButton(FSequencerCommands::Get().RenderMovie, TAttribute(), TAttribute(), FSlateIcon(FAppStyle::GetAppStyleSetName(), "LevelEditor.OpenCinematic")); RenderMovieEntry.StyleNameOverride = SequencerToolbarStyleName; Section.AddEntry(RenderMovieEntry); FToolMenuEntry RenderMovieOptionsEntry = FToolMenuEntry::InitComboButton( "RenderMovieOptions", FUIAction(), FOnGetContent::CreateSP(SequencerWidget, &SSequencer::MakeRenderMovieMenu), LOCTEXT("RenderMovieOptions", "Render Movie Options"), LOCTEXT("RenderMovieOptionsToolTip", "Render Movie Options"), TAttribute(), true ); RenderMovieOptionsEntry.StyleNameOverride = SequencerToolbarStyleName; Section.AddEntry(RenderMovieOptionsEntry); } UMovieSceneSequence* RootSequence = Sequencer->GetRootMovieSceneSequence(); if (RootSequence->GetTypedOuter() == nullptr && UMovieScene::IsTrackClassAllowed(UMovieSceneEventTrack::StaticClass())) { // Only show this button where it makes sense (ie, if the sequence is not contained within a blueprint already) FToolMenuEntry OpenDirectorBlueprintEntry = FToolMenuEntry::InitToolBarButton(FSequencerCommands::Get().OpenDirectorBlueprint, TAttribute(), TAttribute(), FSlateIcon(FAppStyle::Get().GetStyleSetName(), "LevelEditor.OpenLevelBlueprint")); OpenDirectorBlueprintEntry.StyleNameOverride = SequencerToolbarStyleName; Section.AddEntry(OpenDirectorBlueprintEntry); } Section.AddSeparator(NAME_None); } FToolMenuEntry ActionsEntry = FToolMenuEntry::InitComboButton( "Actions", FUIAction(), FOnGetContent::CreateSP(SequencerWidget, &SSequencer::MakeActionsMenu), LOCTEXT("Actions", "Actions"), LOCTEXT("ActionsToolTip", "Actions"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "Sequencer.Actions") ); ActionsEntry.StyleNameOverride = SequencerToolbarStyleName; Section.AddEntry(ActionsEntry); FToolMenuEntry ViewOptionsEntry = FToolMenuEntry::InitComboButton( "ViewOptions", FUIAction(), FOnGetContent::CreateSP(SequencerWidget, &SSequencer::MakeViewMenu), LOCTEXT("ViewOptions", "View Options"), LOCTEXT("ViewOptionsToolTip", "View Options"), FSlateIcon(FAppStyle::Get().GetStyleSetName(), "Icons.Visibility") ); ViewOptionsEntry.StyleNameOverride = SequencerToolbarStyleName; Section.AddEntry(ViewOptionsEntry); FToolMenuEntry PlaybackOptionsEntry = FToolMenuEntry::InitComboButton( "PlaybackOptions", FUIAction(), FOnGetContent::CreateSP(SequencerWidget, &SSequencer::MakePlaybackMenu), LOCTEXT("PlaybackOptions", "Playback Options"), LOCTEXT("PlaybackOptionsToolTip", "Playback Options"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "Sequencer.PlaybackOptions") ); PlaybackOptionsEntry.StyleNameOverride = SequencerToolbarStyleName; Section.AddEntry(PlaybackOptionsEntry); Section.AddSeparator(NAME_None); Section.AddEntry(UE::Sequencer::MakeKeyGroupMenuEntry_ToolMenus(WeakSequencer)); Section.AddEntry(UE::Sequencer::MakeAutoKeyMenuEntry(Sequencer)); if (const TOptional AllowEditsEntry = UE::Sequencer::MakeAllowEditsModeMenuEntry(Sequencer)) { Section.AddEntry(*AllowEditsEntry); } Section.AddSeparator(NAME_None); } { if (Sequencer->GetHostCapabilities().bSupportsViewportSelectability) { FToolMenuSection& SelectionSection = InMenu->AddSection(TEXT("Selection")); FToolMenuEntry ToggleLockViewportSelectionEntry = FToolMenuEntry::InitToolBarButton(FSequencerCommands::Get().ToggleLimitViewportSelection); ToggleLockViewportSelectionEntry.StyleNameOverride = SequencerToolbarStyleName; SelectionSection.AddEntry(ToggleLockViewportSelectionEntry); SelectionSection.AddSeparator(NAME_None); } } { FToolMenuSection& SnappingSection = InMenu->AddSection("Snapping"); FToolMenuEntry ToggleIsSnapEnabledEntry = FToolMenuEntry::InitToolBarButton(FSequencerCommands::Get().ToggleIsSnapEnabled, TAttribute(FText::GetEmpty())); ToggleIsSnapEnabledEntry.StyleNameOverride = SequencerToolbarStyleName; SnappingSection.AddEntry(ToggleIsSnapEnabledEntry); FToolMenuEntry SnapOptionsEntry = FToolMenuEntry::InitComboButton( "SnapOptions", FUIAction(), FOnGetContent::CreateSP(SequencerWidget, &SSequencer::MakeSnapMenu), LOCTEXT("SnapOptions", "Options"), LOCTEXT("SnapOptionsToolTip", "Snapping Options"), TAttribute(), true ); SnapOptionsEntry.StyleNameOverride = SequencerToolbarStyleName; SnappingSection.AddEntry(SnapOptionsEntry); FToolMenuEntry ToggleWholeFramesEntry = FToolMenuEntry::InitToolBarButton(FSequencerCommands::Get().ToggleForceWholeFrames); ToggleWholeFramesEntry.StyleNameOverride = SequencerToolbarStyleName; SnappingSection.AddEntry(ToggleWholeFramesEntry); SnappingSection.AddSeparator("PlayRate"); FToolMenuEntry PlayRateEntry = FToolMenuEntry::InitWidget( "PlayRate", SNew(SSequencerPlayRateCombo, Sequencer, SequencerWidget) .Visibility(Sequencer.Get(), &FSequencer::GetPlayRateComboVisibility), LOCTEXT("PlayRate", "PlayRate")); PlayRateEntry.StyleNameOverride = SequencerToolbarStyleName; SnappingSection.AddEntry(PlayRateEntry); } { FToolMenuSection& CurveEditorSection = InMenu->AddSection("CurveEditor"); // Only add the button if supported if (Sequencer->GetHostCapabilities().bSupportsCurveEditor) { FToolMenuEntry ShowCurveEditorEntry = FToolMenuEntry::InitToolBarButton(FSequencerCommands::Get().ToggleShowCurveEditor, TAttribute(), TAttribute(), FSlateIcon(FAppStyle::GetAppStyleSetName(), "GenericCurveEditor")); ShowCurveEditorEntry.StyleNameOverride = SequencerToolbarStyleName; CurveEditorSection.AddEntry(ShowCurveEditorEntry); } } } TSharedRef SSequencer::MakeAddMenu() { using namespace UE::Sequencer; TSharedPtr Extender = FExtender::Combine(AddMenuExtenders); FMenuBuilder MenuBuilder(true, nullptr, Extender); if (SequencerPtr.Pin()->GetHostCapabilities().bSupportsAddFromContentBrowser) { MenuBuilder.AddMenuEntry( LOCTEXT("SelectedFromContentBrowser", "Selection from Content Browser"), LOCTEXT("SelectedFromContentBrowserToolTip", "Add selected content from the content browser"), FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Use"), FUIAction( FExecuteAction::CreateRaw(this, &SSequencer::AddFromContentBrowser), FCanExecuteAction::CreateRaw(this, &SSequencer::CanAddFromContentBrowser))); } { TSharedPtr Sequencer = SequencerPtr.Pin(); Sequencer->GetViewModel()->GetOutliner()->CastThisChecked()->BuildContextMenu(MenuBuilder); } return MenuBuilder.MakeWidget(); } TSharedRef SSequencer::MakeActionsMenu() { using namespace UE::Sequencer; TSharedPtr Sequencer = SequencerPtr.Pin(); TSharedPtr EditorViewModel = Sequencer->GetViewModel(); TArray> AllExtenders; ISequencerModule& SequencerModule = FModuleManager::GetModuleChecked("Sequencer"); AllExtenders.Add(SequencerModule.GetActionsMenuExtensibilityManager()->GetAllExtenders()); AllExtenders.Append(ActionsMenuExtenders); TSharedPtr Extender = FExtender::Combine(AllExtenders); FMenuBuilder MenuBuilder(true, Sequencer->GetCommandBindings(), Extender); MenuBuilder.BeginSection("SequenceOptions", LOCTEXT("SequenceOptionsHeader", "Sequence")); { UMovieSceneSequence* RootSequence = Sequencer->GetRootMovieSceneSequence(); if (RootSequence->GetTypedOuter() == nullptr) { // Only show this button where it makes sense (ie, if the sequence is not contained within a blueprint already) MenuBuilder.AddMenuEntry(FSequencerCommands::Get().OpenDirectorBlueprint, NAME_None, TAttribute(), TAttribute(), FSlateIcon(FAppStyle::Get().GetStyleSetName(), "LevelEditor.OpenLevelBlueprint")); } MenuBuilder.AddMenuEntry(FSequencerCommands::Get().OpenTaggedBindingManager); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().OpenNodeGroupsManager); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().RestoreAnimatedState); MenuBuilder.AddSubMenu(LOCTEXT("AdvancedHeader", "Advanced"), FText::GetEmpty(), FNewMenuDelegate::CreateRaw(this, &SSequencer::FillAdvancedMenu)); } MenuBuilder.EndSection(); // transform actions MenuBuilder.BeginSection("Transform", LOCTEXT("TransformHeader", "Transform")); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().ToggleShowTransformBox); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().ToggleShowStretchBox); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().TranslateLeft); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().TranslateRight); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().TrimOrExtendSectionLeft); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().TrimOrExtendSectionRight); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().AlignSelectionToPlayhead); MenuBuilder.EndSection(); // selection range actions MenuBuilder.BeginSection("SelectionRange", LOCTEXT("SelectionRangeHeader", "Selection Range")); { MenuBuilder.AddMenuEntry(FSequencerCommands::Get().SetSelectionRangeStart); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().SetSelectionRangeEnd); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().ClearSelectionRange); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().SelectKeysInSelectionRange); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().SelectSectionsInSelectionRange); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().SelectAllInSelectionRange); } MenuBuilder.EndSection(); return MenuBuilder.MakeWidget(); } void SSequencer::FillAdvancedMenu(FMenuBuilder& MenuBuilder) { if (SequencerPtr.Pin()->IsLevelEditorSequencer()) { MenuBuilder.BeginSection("Bindings", LOCTEXT("BindingsMenuHeader", "Bindings")); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().RebindPossessableReferences); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().FixPossessableObjectClass); MenuBuilder.EndSection(); } MenuBuilder.BeginSection( "NetworkingOptions", LOCTEXT( "NetworkingOptionsHeader", "Networking" ) ); { auto SetNetworkMode = [WeakSequencer = SequencerPtr](EMovieSceneServerClientMask InMode) { TSharedPtr SequencerPin = WeakSequencer.Pin(); if (SequencerPin) { // When changing the enumlated network mode, we have to re-initialize and re-compile the // sequence data to ensure that the emulation is reading the correct client/server/all data FMovieSceneRootEvaluationTemplateInstance& Template = SequencerPin->GetEvaluationTemplate(); UMovieSceneSequence* RootSequence = Template.GetRootSequence(); // Set the new emulation mode Template.SetEmulatedNetworkMask(InMode); // Since sequencer owns its own compiled data manager, it's ok to override the mask here and reset everything Template.GetCompiledDataManager()->SetEmulatedNetworkMask(InMode); // Reinitialize the template again Template.Initialize(*RootSequence, *SequencerPin, Template.GetCompiledDataManager()); } }; auto IsNetworkModeChecked = [WeakSequencer = SequencerPtr](EMovieSceneServerClientMask InMode) { TSharedPtr SequencerPin = WeakSequencer.Pin(); if (SequencerPin) { return InMode == SequencerPin->GetEvaluationTemplate().GetEmulatedNetworkMask(); } return false; }; MenuBuilder.AddMenuEntry( LOCTEXT("NetworkEmulationAllLabel", "Do not emulate (default)"), LOCTEXT("NetworkEmulationAllTooltip", "Play this sequence with all sub sequences, regardless of their network mask."), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda(SetNetworkMode, EMovieSceneServerClientMask::All), FCanExecuteAction(), FIsActionChecked::CreateLambda(IsNetworkModeChecked, EMovieSceneServerClientMask::All) ), NAME_None, EUserInterfaceActionType::RadioButton); MenuBuilder.AddMenuEntry( LOCTEXT("NetworkEmulationClientLabel", "Emulate as Client"), LOCTEXT("NetworkEmulationClientTooltip", "Plays this sequence as if it were being played on a client (excludes server only cinematics)."), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda(SetNetworkMode, EMovieSceneServerClientMask::Client), FCanExecuteAction(), FIsActionChecked::CreateLambda(IsNetworkModeChecked, EMovieSceneServerClientMask::Client) ), NAME_None, EUserInterfaceActionType::RadioButton); MenuBuilder.AddMenuEntry( LOCTEXT("NetworkEmulationServerLabel", "Emulate as Server"), LOCTEXT("NetworkEmulationServerTooltip", "Plays this sequence as if it were being played on a server (excludes client only cinematics)."), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda(SetNetworkMode, EMovieSceneServerClientMask::Server), FCanExecuteAction(), FIsActionChecked::CreateLambda(IsNetworkModeChecked, EMovieSceneServerClientMask::Server) ), NAME_None, EUserInterfaceActionType::RadioButton); } MenuBuilder.EndSection(); MenuBuilder.BeginSection( "VolatilityOptions", LOCTEXT( "VolatilityOptionsHeader", "Volatility" ) ); { auto ToggleVolatility = [WeakSequencer = SequencerPtr](EMovieSceneSequenceFlags InFlags) { TSharedPtr SequencerPin = WeakSequencer.Pin(); if (SequencerPin) { const FScopedTransaction Transaction(LOCTEXT("ToggleVolatility", "Toggle Volatility")); UMovieSceneSequence* RootSequence = SequencerPin->GetRootMovieSceneSequence(); RootSequence->Modify(); RootSequence->SetSequenceFlags(RootSequence->GetFlags() ^ InFlags); } }; auto IsVolatilityChecked = [WeakSequencer = SequencerPtr](EMovieSceneSequenceFlags InFlags) { TSharedPtr SequencerPin = WeakSequencer.Pin(); if (SequencerPin) { UMovieSceneSequence* RootSequence = SequencerPin->GetRootMovieSceneSequence(); return ((uint8)RootSequence->GetFlags() & (uint8)InFlags) != 0; } return false; }; MenuBuilder.AddMenuEntry( LOCTEXT("VolatilityVolatileLabel", "Volatile"), LOCTEXT("VolatilityVolatileTooltip", "Flag signifying that this sequence can change dynamically at runtime or during the game so the template must be checked for validity and recompiled as necessary before each evaluation. The absence of this flag will result in the same compiled data being used for the duration of the program, as well as being pre-built during cook. As such, any dynamic changes to the sequence will not be reflected in the evaluation itself. This flag *must* be set if *any* procedural changes will be made to the source sequence data in-game."), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda(ToggleVolatility, EMovieSceneSequenceFlags::Volatile), FCanExecuteAction(), FIsActionChecked::CreateLambda(IsVolatilityChecked, EMovieSceneSequenceFlags::Volatile) ), NAME_None, EUserInterfaceActionType::ToggleButton); MenuBuilder.AddMenuEntry( LOCTEXT("VolatilityBlockingEvaluationLabel", "Blocking Evaluation"), LOCTEXT("VolatilityBlockingEvaluationTooltip", "Indicates that a sequence must fully evaluate and apply its state every time it is updated, blocking until complete. Should be used sparingly as it will severely affect performance."), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda(ToggleVolatility, EMovieSceneSequenceFlags::BlockingEvaluation), FCanExecuteAction(), FIsActionChecked::CreateLambda(IsVolatilityChecked, EMovieSceneSequenceFlags::BlockingEvaluation) ), NAME_None, EUserInterfaceActionType::ToggleButton); } MenuBuilder.EndSection(); } TSharedRef SSequencer::MakeViewMenu() { TSharedPtr Sequencer = SequencerPtr.Pin(); TArray> AllExtenders; ISequencerModule& SequencerModule = FModuleManager::GetModuleChecked("Sequencer"); AllExtenders.Add(SequencerModule.GetViewMenuExtensibilityManager()->GetAllExtenders()); AllExtenders.Append(ViewMenuExtenders); TSharedPtr Extender = FExtender::Combine(AllExtenders); FMenuBuilder MenuBuilder(true, Sequencer->GetCommandBindings(), Extender); if (Sequencer->GetHostCapabilities().bSupportsSidebar) { MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleSidebarVisible ); } if (Sequencer->IsLevelEditorSequencer()) { MenuBuilder.BeginSection("PilotCamera", LOCTEXT("PilotCamera", "Pilot Camera")); { MenuBuilder.AddMenuEntry(FSequencerCommands::Get().TogglePilotCamera); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().ToggleRestoreOriginalViewportOnCameraCutUnlock); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().TogglePreviewCameraCutsInSimulate); } MenuBuilder.EndSection(); } MenuBuilder.BeginSection("SequencerSettings", LOCTEXT("SequencerSettings", "Sequencer Settings")); { MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleAutoScroll ); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleShowRangeSlider ); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleLayerBars ); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleKeyBars ); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleChannelColors ); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleShowInfoButton ); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleShowPreAndPostRoll ); MenuBuilder.AddSubMenu(LOCTEXT("ViewDensityMenuLabel", "View Density"), FText::GetEmpty(), FNewMenuDelegate::CreateRaw(this, &SSequencer::FillViewDensityMenu)); // Menu Entry for Outliner Column Visibilities if (OutlinerColumnVisibilities.Num() > 0) { MenuBuilder.AddSubMenu(LOCTEXT("ColumnVisibilityHeader", "Columns"), FText::GetEmpty(), FNewMenuDelegate::CreateRaw(this, &SSequencer::FillColumnVisibilityMenu)); } // Menu entry for zero padding auto OnZeroPadChanged = [this](uint8 NewValue) { GetSequencerSettings()->SetZeroPadFrames(NewValue); }; MenuBuilder.AddWidget( SNew(SHorizontalBox) + SHorizontalBox::Slot() [ SNew(SSpacer) ] + SHorizontalBox::Slot() .AutoWidth() [ SNew(SSpinBox) .Style(&FAppStyle::GetWidgetStyle("Sequencer.HyperlinkSpinBox")) .OnValueCommitted_Lambda([=](uint8 Value, ETextCommit::Type) { OnZeroPadChanged(Value); }) .OnValueChanged_Lambda(OnZeroPadChanged) .MinValue(0) .MaxValue(8) .Value_Lambda([this]() -> uint8 { return GetSequencerSettings()->GetZeroPadFrames(); }) ], LOCTEXT("ZeroPaddingText", "Zero Pad Frame Numbers")); } MenuBuilder.EndSection(); MenuBuilder.BeginSection("MarkedFrames", LOCTEXT("MarkedFramesHeader", "Marked Frames")); { MenuBuilder.AddMenuEntry(FSequencerCommands::Get().ToggleShowMarkedFrames); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().ToggleShowMarkedFramesGlobally); MenuBuilder.AddMenuEntry(FSequencerCommands::Get().ClearGlobalMarkedFrames); } MenuBuilder.EndSection(); return MenuBuilder.MakeWidget(); } void SSequencer::OpenTaggedBindingManager() { if (TSharedPtr Window = WeakExposedBindingsWindow.Pin()) { Window->DrawAttention(FWindowDrawAttentionParameters()); return; } TSharedRef ExposedBindingsWindow = SNew(SWindow) .Title(FText::Format(LOCTEXT("ExposedBindings_Title", "Bindings Exposed in {0}"), FText::FromName(SequencerPtr.Pin()->GetRootMovieSceneSequence()->GetFName()))) .SupportsMaximize(false) .ClientSize(FVector2D(600.f, 500.f)) .Content() [ SNew(SObjectBindingTagManager, SequencerPtr) ]; TSharedPtr ParentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared()); if (ParentWindow) { FSlateApplication::Get().AddWindowAsNativeChild(ExposedBindingsWindow, ParentWindow.ToSharedRef()); } else { FSlateApplication::Get().AddWindow(ExposedBindingsWindow); } WeakExposedBindingsWindow = ExposedBindingsWindow; } void SSequencer::OpenNodeGroupsManager() { if (TSharedPtr Window = WeakNodeGroupWindow.Pin()) { Window->DrawAttention(FWindowDrawAttentionParameters()); return; } TSharedRef NodeGroupManagerWindow = SNew(SWindow) .Title(FText::Format(LOCTEXT("NodeGroup_Title", "Groups in {0}"), FText::FromName(SequencerPtr.Pin()->GetRootMovieSceneSequence()->GetFName()))) .SupportsMaximize(false) .ClientSize(FVector2D(600.f, 500.f)) .Content() [ SAssignNew(NodeGroupManager, SSequencerGroupManager, SequencerPtr) ]; TSharedPtr ParentWindow = FSlateApplication::Get().FindWidgetWindow(AsShared()); if (ParentWindow) { FSlateApplication::Get().AddWindowAsNativeChild(NodeGroupManagerWindow, ParentWindow.ToSharedRef()); } else { FSlateApplication::Get().AddWindow(NodeGroupManagerWindow); } WeakNodeGroupWindow = NodeGroupManagerWindow; } void SSequencer::FillPlaybackSpeedMenu(FMenuBuilder& InMenuBarBuilder) { TArray PlaybackSpeeds = OnGetPlaybackSpeeds.Execute(); InMenuBarBuilder.BeginSection("PlaybackSpeed"); for( int32 PlaybackSpeedIndex = 0; PlaybackSpeedIndex < PlaybackSpeeds.Num(); ++PlaybackSpeedIndex ) { float PlaybackSpeed = PlaybackSpeeds[PlaybackSpeedIndex]; const FText MenuStr = FText::Format( LOCTEXT("PlaybackSpeedStr", "{0}"), FText::AsNumber( PlaybackSpeed ) ); InMenuBarBuilder.AddMenuEntry(MenuStr, FText(), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda( [this, PlaybackSpeed] { SequencerPtr.Pin()->SetPlaybackSpeed(PlaybackSpeed); }), FCanExecuteAction::CreateLambda([] { return true; }), FIsActionChecked::CreateLambda( [this, PlaybackSpeed]{ return SequencerPtr.Pin()->GetPlaybackSpeed() == PlaybackSpeed; }) ), NAME_None, EUserInterfaceActionType::RadioButton ); } InMenuBarBuilder.EndSection(); } void SSequencer::FillViewDensityMenu(FMenuBuilder& InMenuBuilder) { using namespace UE::Sequencer; auto SetViewDensity = [this](EViewDensity InViewDensity){ TSharedPtr Editor = this->SequencerPtr.Pin()->GetViewModel(); Editor->SetViewDensity(InViewDensity); if (USequencerSettings* Settings = this->GetSequencerSettings()) { if (InViewDensity == EViewDensity::Compact) { Settings->SetViewDensity("Compact"); } else if (InViewDensity == EViewDensity::Relaxed) { Settings->SetViewDensity("Relaxed"); } else { Settings->SetViewDensity("Variable"); } } }; auto IsCurrentViewDensity = [this](EViewDensity InViewDensity){ TSharedPtr Editor = this->SequencerPtr.Pin()->GetViewModel(); return Editor->GetViewDensity().Density == InViewDensity; }; InMenuBuilder.AddMenuEntry( LOCTEXT("VariableViewDensity_Label", "Variable"), LOCTEXT("VariableViewDensity_Tooltip", "Change Sequencer to use a variable height view mode withe inner items displaying more condensed than outer items"), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda(SetViewDensity, EViewDensity::Variable), FCanExecuteAction(), FIsActionChecked::CreateLambda(IsCurrentViewDensity, EViewDensity::Variable) ), NAME_None, EUserInterfaceActionType::RadioButton ); InMenuBuilder.AddMenuEntry( LOCTEXT("CompactViewDensity_Label", "Compact"), LOCTEXT("CompactViewDensity_Tooltip", "Change Sequencer to use a compact view mode with uniform track heights"), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda(SetViewDensity, EViewDensity::Compact), FCanExecuteAction(), FIsActionChecked::CreateLambda(IsCurrentViewDensity, EViewDensity::Compact) ), NAME_None, EUserInterfaceActionType::RadioButton ); InMenuBuilder.AddMenuEntry( LOCTEXT("RelaxedViewDensity_Label", "Relaxed"), LOCTEXT("RelaxedViewDensity_Tooltip", "Change Sequencer to use a relaxed view mode with larger uniform track heights"), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda(SetViewDensity, EViewDensity::Relaxed), FCanExecuteAction(), FIsActionChecked::CreateLambda(IsCurrentViewDensity, EViewDensity::Relaxed) ), NAME_None, EUserInterfaceActionType::RadioButton ); } void SSequencer::FillColumnVisibilityMenu(FMenuBuilder& InMenuBuilder) { using namespace UE::Sequencer; const bool bShouldCloseWindowAfterMenuSelection = true; for (FSequencerOutlinerColumnVisibility& ColumnVisibility : OutlinerColumnVisibilities) { if (EnumHasAnyFlags(ColumnVisibility.Column->GetLayout().Flags, EOutlinerColumnFlags::Hidden)) { continue; } auto ToggleVisibility = [this, &ColumnVisibility] { ColumnVisibility.bIsColumnVisible = !ColumnVisibility.bIsColumnVisible; if(ColumnVisibility.bIsColumnVisible) { FName ColumnName = ColumnVisibility.Column->GetColumnName(); FSequencerOutlinerColumnVisibility* AutoDisable = nullptr; // Auto disable mutually exclusive columns if (ColumnName == FCommonOutlinerNames::Nav) { AutoDisable = Algo::FindBy(this->OutlinerColumnVisibilities, FCommonOutlinerNames::KeyFrame, [](const FSequencerOutlinerColumnVisibility& In) { return In.Column->GetColumnName(); }); } else if (ColumnName == FCommonOutlinerNames::KeyFrame) { AutoDisable = Algo::FindBy(this->OutlinerColumnVisibilities, FCommonOutlinerNames::Nav, [](const FSequencerOutlinerColumnVisibility& In) { return In.Column->GetColumnName(); }); } if (AutoDisable) { AutoDisable->bIsColumnVisible = false; } } this->UpdateOutlinerViewColumns(); }; InMenuBuilder.AddMenuEntry( ColumnVisibility.Column->GetColumnLabel(), LOCTEXT("SetColumnVisibilityTooltip", "Enable or disable this outliner column"), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda(ToggleVisibility), FCanExecuteAction(), FIsActionChecked::CreateLambda([&ColumnVisibility] { // Capture by ref here since the array itself cannot be re-allocated while this menu is open return ColumnVisibility.bIsColumnVisible; }) ), NAME_None, EUserInterfaceActionType::ToggleButton ); } } void SSequencer::FillTimeDisplayFormatMenu(UToolMenu* InMenu) { USequencerToolMenuContext* ContextObject = InMenu->FindContext(); if (!ContextObject) { return; } TWeakPtr WeakSequencer = StaticCastWeakPtr(ContextObject->WeakSequencer); TSharedPtr Sequencer = WeakSequencer.Pin(); if (!Sequencer) { return; } bool bShouldDisplayDropFormat = FTimecode::UseDropFormatTimecode(Sequencer->GetFocusedDisplayRate()); const UEnum* FrameNumberDisplayEnum = StaticEnum(); check(FrameNumberDisplayEnum); if (USequencerSettings* Settings = Sequencer->GetSequencerSettings()) { for (int32 Index = 0; Index < FrameNumberDisplayEnum->NumEnums() - 1; Index++) { if (!FrameNumberDisplayEnum->HasMetaData(TEXT("Hidden"), Index)) { EFrameNumberDisplayFormats Value = (EFrameNumberDisplayFormats)FrameNumberDisplayEnum->GetValueByIndex(Index); // Don't show None Drop Frame Timecode when the format support drop format and the engine wants to use the drop format by default. if (Value == EFrameNumberDisplayFormats::NonDropFrameTimecode && bShouldDisplayDropFormat) { continue; } // Don't show Drop Frame Timecode when they're in a format that doesn't support it. if (Value == EFrameNumberDisplayFormats::DropFrameTimecode && !bShouldDisplayDropFormat) { continue; } FToolMenuEntry Entry = FToolMenuEntry::InitMenuEntry( FrameNumberDisplayEnum->GetNameByIndex(Index), FrameNumberDisplayEnum->GetDisplayNameTextByIndex(Index), FrameNumberDisplayEnum->GetToolTipTextByIndex(Index), FSlateIcon(), FToolUIActionChoice( FUIAction( FExecuteAction::CreateUObject(Settings, &USequencerSettings::SetTimeDisplayFormat, Value), FCanExecuteAction(), FIsActionChecked::CreateLambda([Settings, Value] { return Settings->GetTimeDisplayFormat() == Value; }) ) ), EUserInterfaceActionType::RadioButton ); InMenu->AddMenuEntry(NAME_None, Entry); } } } } TSharedRef SSequencer::MakePlaybackMenu() { FMenuBuilder MenuBuilder( true, SequencerPtr.Pin()->GetCommandBindings() ); // playback range options MenuBuilder.BeginSection("PlaybackThisSequence", LOCTEXT("PlaybackThisSequenceHeader", "Playback - This Sequence")); { // Menu entry for the start position auto OnStartChanged = [this](double NewValue){ FFrameNumber ValueAsFrame = FFrameTime::FromDecimal(NewValue).GetFrame(); FFrameNumber PlayStart = ValueAsFrame; FFrameNumber PlayEnd = UE::MovieScene::DiscreteExclusiveUpper(SequencerPtr.Pin()->GetPlaybackRange()); if (PlayStart >= PlayEnd) { FFrameNumber Duration = PlayEnd - UE::MovieScene::DiscreteInclusiveLower(SequencerPtr.Pin()->GetPlaybackRange()); PlayEnd = PlayStart + Duration; } SequencerPtr.Pin()->SetPlaybackRange(TRange(PlayStart, PlayEnd)); TRange PlayRangeSeconds = SequencerPtr.Pin()->GetPlaybackRange() / SequencerPtr.Pin()->GetFocusedTickResolution(); const double AdditionalRange = (PlayRangeSeconds.GetUpperBoundValue() - PlayRangeSeconds.GetLowerBoundValue()) * 0.1; TRange NewClampRange = SequencerPtr.Pin()->GetClampRange(); NewClampRange.SetLowerBoundValue(SequencerPtr.Pin()->GetPlaybackRange().GetLowerBoundValue() / SequencerPtr.Pin()->GetFocusedTickResolution() - AdditionalRange); if (SequencerPtr.Pin()->GetClampRange().GetLowerBoundValue() > NewClampRange.GetLowerBoundValue()) { SequencerPtr.Pin()->SetClampRange(NewClampRange); } TRange NewViewRange = SequencerPtr.Pin()->GetViewRange(); NewViewRange.SetLowerBoundValue(SequencerPtr.Pin()->GetPlaybackRange().GetLowerBoundValue() / SequencerPtr.Pin()->GetFocusedTickResolution() - AdditionalRange); if (SequencerPtr.Pin()->GetViewRange().GetLowerBoundValue() > NewViewRange.GetLowerBoundValue()) { SequencerPtr.Pin()->SetViewRange(NewViewRange); } }; MenuBuilder.AddWidget( SNew(SHorizontalBox) + SHorizontalBox::Slot() [ SNew(SSpacer) ] + SHorizontalBox::Slot() .AutoWidth() [ SNew(SSpinBox) .TypeInterface(SequencerPtr.Pin().Get(), &FSequencer::GetNumericTypeInterface, UE::Sequencer::ENumericIntent::Position) .IsEnabled_Lambda([this]() { return !SequencerPtr.Pin()->IsPlaybackRangeLocked(); }) .Style(&FAppStyle::GetWidgetStyle("Sequencer.HyperlinkSpinBox")) .OnValueCommitted_Lambda([=](double Value, ETextCommit::Type){ OnStartChanged(Value); }) .OnValueChanged_Lambda([=](double Value) { OnStartChanged(Value); }) .OnBeginSliderMovement(OnPlaybackRangeBeginDrag) .OnEndSliderMovement_Lambda([=, this](double Value){ OnStartChanged(Value); OnPlaybackRangeEndDrag.ExecuteIfBound(); }) .MinValue(TOptional()) .MaxValue(TOptional()) .Value_Lambda([this]() -> double { return SequencerPtr.Pin()->GetPlaybackRange().GetLowerBoundValue().Value; }) .Delta(this, &SSequencer::GetSpinboxDelta) .LinearDeltaSensitivity(25) ], LOCTEXT("PlaybackStartLabel", "Start")); // Menu entry for the end position auto OnEndChanged = [this](double NewValue) { FFrameNumber ValueAsFrame = FFrameTime::FromDecimal(NewValue).GetFrame(); FFrameNumber PlayStart = UE::MovieScene::DiscreteInclusiveLower(SequencerPtr.Pin()->GetPlaybackRange()); FFrameNumber PlayEnd = ValueAsFrame; if (PlayEnd <= PlayStart) { FFrameNumber Duration = UE::MovieScene::DiscreteExclusiveUpper(SequencerPtr.Pin()->GetPlaybackRange()) - PlayStart; PlayStart = PlayEnd - Duration; } SequencerPtr.Pin()->SetPlaybackRange(TRange(PlayStart, PlayEnd)); TRange PlayRangeSeconds = SequencerPtr.Pin()->GetPlaybackRange() / SequencerPtr.Pin()->GetFocusedTickResolution(); const double AdditionalRange = (PlayRangeSeconds.GetUpperBoundValue() - PlayRangeSeconds.GetLowerBoundValue()) * 0.1; TRange NewClampRange = SequencerPtr.Pin()->GetClampRange(); NewClampRange.SetUpperBoundValue(SequencerPtr.Pin()->GetPlaybackRange().GetUpperBoundValue() / SequencerPtr.Pin()->GetFocusedTickResolution() + AdditionalRange); if (SequencerPtr.Pin()->GetClampRange().GetUpperBoundValue() < NewClampRange.GetUpperBoundValue()) { SequencerPtr.Pin()->SetClampRange(NewClampRange); } TRange NewViewRange = SequencerPtr.Pin()->GetViewRange(); NewViewRange.SetUpperBoundValue(SequencerPtr.Pin()->GetPlaybackRange().GetUpperBoundValue() / SequencerPtr.Pin()->GetFocusedTickResolution() + AdditionalRange); if (SequencerPtr.Pin()->GetViewRange().GetUpperBoundValue() < NewViewRange.GetUpperBoundValue()) { SequencerPtr.Pin()->SetViewRange(NewViewRange); } }; MenuBuilder.AddWidget( SNew(SHorizontalBox) + SHorizontalBox::Slot() [ SNew(SSpacer) ] + SHorizontalBox::Slot() .AutoWidth() [ SNew(SSpinBox) .TypeInterface(SequencerPtr.Pin().Get(), &FSequencer::GetNumericTypeInterface, UE::Sequencer::ENumericIntent::Position) .IsEnabled_Lambda([this]() { return !SequencerPtr.Pin()->IsPlaybackRangeLocked(); }) .Style(&FAppStyle::GetWidgetStyle("Sequencer.HyperlinkSpinBox")) .OnValueCommitted_Lambda([=](double Value, ETextCommit::Type){ OnEndChanged(Value); }) .OnValueChanged_Lambda([=](double Value) { OnEndChanged(Value); }) .OnBeginSliderMovement(OnPlaybackRangeBeginDrag) .OnEndSliderMovement_Lambda([=, this](double Value){ OnEndChanged(Value); OnPlaybackRangeEndDrag.ExecuteIfBound(); }) .MinValue(TOptional()) .MaxValue(TOptional()) .Value_Lambda([this]() -> double { return SequencerPtr.Pin()->GetPlaybackRange().GetUpperBoundValue().Value; }) .Delta(this, &SSequencer::GetSpinboxDelta) .LinearDeltaSensitivity(25) ], LOCTEXT("PlaybackStartEnd", "End")); MenuBuilder.AddSubMenu(LOCTEXT("PlaybackSpeedHeader", "Playback Speed"), FText::GetEmpty(), FNewMenuDelegate::CreateRaw(this, &SSequencer::FillPlaybackSpeedMenu)); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().TogglePlaybackRangeLocked ); if (SequencerPtr.Pin()->IsLevelEditorSequencer()) { MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleCleanPlaybackMode ); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleRerunConstructionScripts ); } MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleAsyncEvaluation ); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleDynamicWeighting ); } MenuBuilder.EndSection(); MenuBuilder.BeginSection( "PlaybackAllSequences", LOCTEXT( "PlaybackRangeAllSequencesHeader", "Playback Range - All Sequences" ) ); { if (SequencerPtr.Pin()->IsLevelEditorSequencer()) { MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleEvaluateSubSequencesInIsolation ); MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleResetPlayheadWhenNavigating ); } MenuBuilder.AddMenuEntry( FSequencerCommands::Get().ToggleKeepCursorInPlaybackRangeWhileScrubbing ); if (!SequencerPtr.Pin()->IsLevelEditorSequencer()) { MenuBuilder.AddMenuEntry(FSequencerCommands::Get().ToggleKeepPlaybackRangeInSectionBounds); } MenuBuilder.AddMenuEntry(FSequencerCommands::Get().ToggleLinkCurveEditorTimeRange); } MenuBuilder.EndSection(); // Menu entry for the jump frame increment auto OnJumpFrameIncrementChanged = [this](double NewValue) { FFrameRate TickResolution = SequencerPtr.Pin()->GetFocusedTickResolution(); FFrameRate DisplayRate = SequencerPtr.Pin()->GetFocusedDisplayRate(); FFrameNumber JumpFrameIncrement = FFrameRate::TransformTime(FFrameTime::FromDecimal(NewValue), TickResolution, DisplayRate).CeilToFrame(); SequencerPtr.Pin()->GetSequencerSettings()->SetJumpFrameIncrement(JumpFrameIncrement); }; MenuBuilder.AddWidget( SNew(SHorizontalBox) + SHorizontalBox::Slot() [ SNew(SSpacer) ] + SHorizontalBox::Slot() .AutoWidth() [ SNew(SSpinBox) .TypeInterface(SequencerPtr.Pin().Get(), &FSequencer::GetNumericTypeInterface, UE::Sequencer::ENumericIntent::Duration) .Style(&FAppStyle::GetWidgetStyle("Sequencer.HyperlinkSpinBox")) .OnValueCommitted_Lambda([=](double Value, ETextCommit::Type){ OnJumpFrameIncrementChanged(Value); }) .OnValueChanged_Lambda([=](double Value) { OnJumpFrameIncrementChanged(Value); }) .MinValue(TOptional()) .MaxValue(TOptional()) .Value_Lambda([this]() -> double { FFrameNumber JumpFrameIncrement = SequencerPtr.Pin()->GetSequencerSettings()->GetJumpFrameIncrement(); FFrameRate TickResolution = SequencerPtr.Pin()->GetFocusedTickResolution(); FFrameRate DisplayRate = SequencerPtr.Pin()->GetFocusedDisplayRate(); int32 ConvertedValue = FFrameRate::TransformTime(JumpFrameIncrement, DisplayRate, TickResolution).CeilToFrame().Value; return ConvertedValue; }) .Delta(this, &SSequencer::GetSpinboxDelta) .LinearDeltaSensitivity(25) ], LOCTEXT("JumpFrameIncrement", "Jump Frame Increment")); return MenuBuilder.MakeWidget(); } TSharedRef SSequencer::MakeRenderMovieMenu() { FMenuBuilder MenuBuilder( false, SequencerPtr.Pin()->GetCommandBindings() ); MenuBuilder.BeginSection( "RenderMovie", LOCTEXT( "RenderMovieMenuHeader", "Render Movie" ) ); { ISequencerModule& SequencerModule = FModuleManager::LoadModuleChecked("Sequencer"); TArray MovieRendererNames = SequencerModule.GetMovieRendererNames(); for (FString MovieRendererName : MovieRendererNames) { MenuBuilder.AddMenuEntry( FText::FromString(MovieRendererName), FText::FromString(MovieRendererName), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda([this, MovieRendererName] { SequencerPtr.Pin()->GetSequencerSettings()->SetMovieRendererName(MovieRendererName); }), FCanExecuteAction(), FIsActionChecked::CreateLambda([this, MovieRendererName] { return MovieRendererName == SequencerPtr.Pin()->GetMovieRendererName(); })), NAME_None, EUserInterfaceActionType::RadioButton ); } MenuBuilder.AddMenuEntry( LOCTEXT("RenderMovieLegacy", "Movie Scene Capture (Legacy)"), LOCTEXT("RenderMovieTooltip", "Movie Scene Capture (Legacy)"), FSlateIcon(), FUIAction( FExecuteAction::CreateLambda([this] { SequencerPtr.Pin()->GetSequencerSettings()->SetMovieRendererName(TEXT("MovieSceneCapture")); }), FCanExecuteAction(), FIsActionChecked::CreateLambda([this] { return SequencerPtr.Pin()->GetMovieRendererName() == TEXT("MovieSceneCapture"); })), NAME_None, EUserInterfaceActionType::RadioButton ); } MenuBuilder.EndSection(); return MenuBuilder.MakeWidget(); } TSharedRef SSequencer::MakeSnapMenu() { static const FName MenuName("Sequencer.SnapOptions"); if (!UToolMenus::Get()->IsMenuRegistered(MenuName)) { UToolMenu* Toolbar = UToolMenus::Get()->RegisterMenu(MenuName, NAME_None, EMultiBoxType::Menu); Toolbar->bSearchable = false; { FToolMenuSection& Section = Toolbar->AddSection( "KeyAndSectionsSnapping", LOCTEXT( "SnappingMenuKeyAndSectionsHeader", "Key and Sections Snapping" ) ); Section.AddMenuEntry( FSequencerCommands::Get().ToggleSnapKeyTimesToElements ); Section.AddMenuEntry( FSequencerCommands::Get().ToggleSnapSectionTimesToElements); Section.AddMenuEntry( FSequencerCommands::Get().ToggleSnapKeysAndSectionsToPlayRange ); } { FToolMenuSection& Section = Toolbar->AddSection( "PlayHeadSnapping", LOCTEXT( "SnappingMenuPlayHeadHeader", "Play Head Snapping" ) ); Section.AddMenuEntry( FSequencerCommands::Get().ToggleSnapPlayTimeToKeys ); Section.AddMenuEntry( FSequencerCommands::Get().ToggleSnapPlayTimeToSections ); Section.AddMenuEntry( FSequencerCommands::Get().ToggleSnapPlayTimeToMarkers ); Section.AddMenuEntry( FSequencerCommands::Get().ToggleSnapPlayTimeToPressedKey ); Section.AddMenuEntry( FSequencerCommands::Get().ToggleSnapPlayTimeToDraggedKey ); } { FToolMenuSection& Section = Toolbar->AddSection( "CurveSnapping", LOCTEXT( "SnappingMenuCurveHeader", "Curve Snapping" ) ); Section.AddMenuEntry( FSequencerCommands::Get().ToggleSnapCurveValueToInterval ); } } USequencerToolMenuContext* ContextObject = NewObject(); ContextObject->WeakSequencer = SequencerPtr; FToolMenuContext Context(SequencerPtr.Pin()->GetCommandBindings(), nullptr, ContextObject); return UToolMenus::Get()->GenerateWidget(MenuName, Context); } TSharedRef SSequencer::MakeTimeRange(const TSharedRef& InnerContent, bool bShowWorkingRange, bool bShowViewRange, bool bShowPlaybackRange) { ISequencerWidgetsModule& SequencerWidgets = FModuleManager::Get().LoadModuleChecked( "SequencerWidgets" ); EShowRange ShowRange = EShowRange::None; if (bShowWorkingRange) { ShowRange |= EShowRange::WorkingRange; } if (bShowViewRange) { ShowRange |= EShowRange::ViewRange; } if (bShowPlaybackRange) { ShowRange |= EShowRange::PlaybackRange; } FTimeRangeArgs Args( ShowRange, TimeSliderController.ToSharedRef(), EVisibility::Visible, MakeAttributeSP(SequencerPtr.Pin().Get(), &FSequencer::GetNumericTypeInterface, UE::Sequencer::ENumericIntent::Position), GetSequencerSettings()->GetPlaybackRangeStartColor(), GetSequencerSettings()->GetPlaybackRangeEndColor()); return SequencerWidgets.CreateTimeRange(Args, InnerContent); } TSharedPtr SSequencer::GetTopTimeSliderWidget() const { return TopTimeSlider; } SSequencer::~SSequencer() { using namespace UE::Sequencer; USelection::SelectionChangedEvent.RemoveAll(this); TSharedPtr Sequencer = SequencerPtr.Pin(); if(Sequencer && Sequencer->GetToolkitHost() && Sequencer->GetToolkitHost()->GetTabManager()) { if (Sequencer->GetHostCapabilities().bSupportsCurveEditor) { TSharedPtr RootModel = Sequencer->GetViewModel(); FCurveEditorExtension* CurveEditorExtension = RootModel->CastDynamicChecked(); CurveEditorExtension->CloseCurveEditor(); } } if (TSharedPtr Window = WeakExposedBindingsWindow.Pin()) { Window->DestroyWindowImmediately(); } if (TSharedPtr Window = WeakNodeGroupWindow.Pin()) { Window->DestroyWindowImmediately(); NodeGroupManager.Reset(); } // Ensure the FilterBarWidget destructor is called FilterBarSplitterContainer->SetContent(SNullWidget::NullWidget); } void SSequencer::RegisterActiveTimerForPlayback() { if (!bIsActiveTimerRegistered) { bIsActiveTimerRegistered = true; RegisterActiveTimer(0.f, FWidgetActiveTimerDelegate::CreateSP(this, &SSequencer::EnsureSlateTickDuringPlayback)); } } EActiveTimerReturnType SSequencer::EnsureSlateTickDuringPlayback(double InCurrentTime, float InDeltaTime) { if (SequencerPtr.IsValid()) { auto PlaybackStatus = SequencerPtr.Pin()->GetPlaybackStatus(); if (PlaybackStatus == EMovieScenePlayerStatus::Playing || PlaybackStatus == EMovieScenePlayerStatus::Scrubbing) { return EActiveTimerReturnType::Continue; } } bIsActiveTimerRegistered = false; return EActiveTimerReturnType::Stop; } void SSequencer::UpdateLayoutTree() { using namespace UE::Sequencer; TrackArea->Empty(); PinnedTrackArea->Empty(); TSharedPtr Sequencer = SequencerPtr.Pin(); if ( Sequencer.IsValid() ) { // Update the node tree Sequencer->GetNodeTree()->Update(); // This must come after the selection state has been restored so that the tree and curve editor are populated with the correctly selected nodes TreeView->Refresh(); if (!NodePathToRename.IsEmpty()) { TArray> AllNodes; Sequencer->GetNodeTree()->GetAllNodes(AllNodes); for (TSharedPtr Node : AllNodes) { const FString CurNodePath = IOutlinerExtension::GetPathName(Node); if (CurNodePath == NodePathToRename) { GEditor->GetTimerManager()->SetTimerForNextTick([Node] { if (IRenameableExtension* Rename = Node->CastThis()) { Rename->OnRenameRequested().Broadcast(); } }); break; } } NodePathToRename.Empty(); } // Isolate binding object guids after the tree view is refreshed and the new tracks are created if (!NewNodePathsToIsolate.IsEmpty()) { for (const TViewModelPtr& OutlinerItem : Sequencer->GetNodeTree()->GetRootNode()->GetDescendantsOfType()) { const FString ItemPath = IOutlinerExtension::GetPathName(OutlinerItem); if (NewNodePathsToIsolate.Contains(ItemPath)) { Sequencer->GetFilterBar()->IsolateTracks({ OutlinerItem }, true); NewNodePathsToIsolate.Remove(ItemPath); } } NewNodePathsToIsolate.Empty(); } if (Sequencer->GetFocusedMovieSceneSequence()) { bool bAnyChanged = false; TSharedPtr SharedData = Sequencer->GetViewModel()->GetRootModel()->GetSharedData(); FOutlinerCacheExtension* OutlinerCache = SharedData->CastThis(); FDeactiveStateCacheExtension* DeactiveState = SharedData->CastThis(); FMuteStateCacheExtension* MuteState = SharedData->CastThis(); FSoloStateCacheExtension* SoloState = SharedData->CastThis(); check(OutlinerCache && DeactiveState && MuteState && SoloState); // Hack - we shouldn't really not just forcibly update these here, but currently this function is getting forcibly called before // UpdateCachedFlags has a chance to naturally update itself in response to the signature change OutlinerCache->UpdateCachedFlags(); const bool bAnySoloNodes = EnumHasAnyFlags(SoloState->GetRootFlags(), ECachedSoloState::Soloed | ECachedSoloState::PartiallySoloedChildren); for (TViewModelPtr TrackNode : Sequencer->GetNodeTree()->GetRootNode()->GetDescendantsOfType()) { UMovieSceneTrack* const Track = TrackNode->GetTrack(); if (!IsValid(Track)) { continue; } const ECachedMuteState MuteFlags = MuteState->GetCachedFlags(TrackNode); const ECachedSoloState SoloFlags = SoloState->GetCachedFlags(TrackNode); const bool bIsMuted = EnumHasAnyFlags(MuteFlags, ECachedMuteState::Muted | ECachedMuteState::ImplicitlyMutedByParent); const bool bIsSoloed = EnumHasAnyFlags(SoloFlags, ECachedSoloState::Soloed | ECachedSoloState::ImplicitlySoloedByParent); const bool bLocalEvalDisabled = bIsMuted || (bAnySoloNodes && !bIsSoloed); if (const TViewModelPtr TrackRowModel = TrackNode.ImplicitCast()) { // Mute/Solo state (local, non dirtying, mute/solo evaluation) if (bLocalEvalDisabled != Track->IsLocalRowEvalDisabled(TrackNode->GetRowIndex())) { Track->MarkAsChanged(); Track->SetLocalRowEvalDisabled(bLocalEvalDisabled, TrackNode->GetRowIndex()); bAnyChanged = true; } } else { // Mute/Solo state (local, non dirtying, mute/solo evaluation) if (bLocalEvalDisabled != Track->IsLocalEvalDisabled()) { Track->MarkAsChanged(); Track->SetLocalEvalDisabled(bLocalEvalDisabled); bAnyChanged = true; } } } if (bAnyChanged) { Sequencer->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::TrackValueChanged); } } if (NodeGroupManager.IsValid()) { NodeGroupManager->RefreshNodeGroups(); } if (SequencerTreeFilterStatusBar.IsValid()) { SequencerTreeFilterStatusBar->UpdateText(); } } } void SSequencer::UpdateBreadcrumbs() { TSharedPtr Sequencer = SequencerPtr.Pin(); if (!Sequencer.IsValid()) { return; } BreadcrumbTrail->ClearCrumbs(); for (FMovieSceneSequenceID SequenceID : Sequencer->GetSubSequenceHierarchy()) { TAttribute CrumbNameAttribute; if (SequenceID == MovieSceneSequenceID::Root) { CrumbNameAttribute = MakeAttributeSP(this, &SSequencer::GetBreadcrumbTextForSequence, MakeWeakObjectPtr(SequencerPtr.Pin()->GetRootMovieSceneSequence()), true); } else { TWeakObjectPtr SubSection = Sequencer->FindSubSection(SequenceID); CrumbNameAttribute = MakeAttributeSP(this, &SSequencer::GetBreadcrumbTextForSection, SubSection); } BreadcrumbTrail->PushCrumb( CrumbNameAttribute, FSequencerBreadcrumb( SequenceID, CrumbNameAttribute.Get()) ); } } void SSequencer::ResetBreadcrumbs() { BreadcrumbTrail->ClearCrumbs(); TAttribute CrumbNameAttribute = MakeAttributeSP(this, &SSequencer::GetBreadcrumbTextForSequence, MakeWeakObjectPtr(SequencerPtr.Pin()->GetRootMovieSceneSequence()), true); BreadcrumbTrail->PushCrumb(CrumbNameAttribute, FSequencerBreadcrumb(MovieSceneSequenceID::Root, CrumbNameAttribute.Get())); } void SSequencer::PopBreadcrumb() { BreadcrumbTrail->PopCrumb(); } FText SSequencer::GetSearchText() const { if (FilterBarWidget.IsValid()) { if (const TSharedPtr FilterBar = FilterBarWidget->GetFilterBar()) { return FText::FromString(FilterBar->GetTextFilterString()); } } return FText::GetEmpty(); } void SSequencer::SetSearchText(const FText& InSearchText) { if (const TSharedPtr Sequencer = SequencerPtr.Pin()) { Sequencer->GetNodeTree()->SetTextFilterString(InSearchText.ToString()); TreeView->Refresh(); } } void SSequencer::OnOutlinerSearchChanged(const FText& InFilter) { SetSearchText(InFilter); } void SSequencer::OnOutlinerSearchCommitted(const FText& InFilter, ETextCommit::Type InCommitInfo) { SetSearchText(InFilter); } void SSequencer::OnOutlinerSearchSaved(const FText& InFilterText) { if (FilterBarWidget.IsValid()) { FCustomTextFilterData CustomTextFilterData; CustomTextFilterData.FilterString = InFilterText; SSequencerCustomTextFilterDialog::CreateWindow_AddCustomTextFilter(FilterBarWidget->GetFilterBar().ToSharedRef(), CustomTextFilterData); } } void SSequencer::OnDragEnter( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent ) { // @todo sequencer: Add drop validity cue } void SSequencer::OnDragLeave( const FDragDropEvent& DragDropEvent ) { // @todo sequencer: Clear drop validity cue } FReply SSequencer::OnDragOver( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent ) { for (FOptionalOnDragDrop Delegate : OnReceivedDragOver) { if (Delegate.IsBound()) { FReply DelegateReply = FReply::Unhandled(); if (Delegate.Execute(MyGeometry, DragDropEvent, DelegateReply)) { return DelegateReply; } } } bool bIsDragSupported = false; TSharedPtr Operation = DragDropEvent.GetOperation(); if (Operation.IsValid() && ( Operation->IsOfType() || Operation->IsOfType() || Operation->IsOfType() || Operation->IsOfType() ) ) { bIsDragSupported = true; } return bIsDragSupported ? FReply::Handled() : FReply::Unhandled(); } FReply SSequencer::OnDrop( const FGeometry& MyGeometry, const FDragDropEvent& DragDropEvent ) { for (FOptionalOnDragDrop Delegate : OnReceivedDrop) { if (Delegate.IsBound()) { FReply DelegateReply = FReply::Unhandled(); if (Delegate.Execute(MyGeometry, DragDropEvent, DelegateReply)) { return DelegateReply; } } } bool bWasDropHandled = false; // @todo sequencer: Get rid of hard-code assumptions about dealing with ACTORS at this level? // @todo sequencer: We may not want any actor-specific code here actually. We need systems to be able to // register with sequencer to support dropping assets/classes/actors, or OTHER types! // @todo sequencer: Handle drag and drop from other FDragDropOperations, including unloaded classes/asset and external drags! // @todo sequencer: Consider allowing drops into the level viewport to add to the MovieScene as well. // - Basically, when Sequencer is open it would take over drops into the level and auto-add puppets for these instead of regular actors // - This would let people drag smoothly and precisely into the view to drop assets/classes into the scene TSharedPtr Operation = DragDropEvent.GetOperation(); if (Operation.IsValid() ) { if ( Operation->IsOfType() ) { TSharedPtr DragDropOp = StaticCastSharedPtr(Operation); OnAssetsDropped( DragDropOp ); bWasDropHandled = true; } else if( Operation->IsOfType() ) { TSharedPtr DragDropOp = StaticCastSharedPtr(Operation); OnClassesDropped( DragDropOp ); bWasDropHandled = true; } else if( Operation->IsOfType() ) { TSharedPtr DragDropOp = StaticCastSharedPtr(Operation); OnActorsDropped( DragDropOp ); bWasDropHandled = true; } else if (Operation->IsOfType()) { TSharedPtr DragDropOp = StaticCastSharedPtr(Operation); OnFolderDropped(DragDropOp); bWasDropHandled = true; } else if (Operation->IsOfType()) { const TSharedPtr CompositeOp = StaticCastSharedPtr(Operation); if (const TSharedPtr ActorDragDropOp = CompositeOp->GetSubOp()) { OnActorsDropped(ActorDragDropOp); bWasDropHandled = true; } if (const TSharedPtr FolderDragDropOp = CompositeOp->GetSubOp()) { OnFolderDropped(FolderDragDropOp); bWasDropHandled = true; } } } return bWasDropHandled ? FReply::Handled() : FReply::Unhandled(); } FReply SSequencer::OnKeyDown( const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent ) { // A toolkit tab is active, so direct all command processing to it TSharedPtr SequencerPin = SequencerPtr.Pin(); if (SequencerPin.IsValid()) { if (SequencerPin->GetCommandBindings()->ProcessCommandBindings(InKeyEvent)) { return FReply::Handled(); } } if (const TSharedPtr FilterBar = FilterBarWidget->GetFilterBar()) { if (FilterBar->GetCommandList()->ProcessCommandBindings(InKeyEvent)) { return FReply::Handled(); } } return FReply::Unhandled(); } void SSequencer::OnFocusChanging( const FWeakWidgetPath& PreviousFocusPath, const FWidgetPath& NewWidgetPath, const FFocusEvent& InFocusEvent ) { if (NewWidgetPath.ContainsWidget(this)) { OnReceivedFocus.ExecuteIfBound(); } } void SSequencer::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { SCompoundWidget::OnMouseEnter(MyGeometry, MouseEvent); PendingFocus.SetPendingFocusIfNeeded(AsWeak()); } void SSequencer::OnMouseLeave(const FPointerEvent& MouseEvent) { SCompoundWidget::OnMouseLeave(MouseEvent); PendingFocus.ResetPendingFocus(); } void SSequencer::AddFromContentBrowser() { FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); TArray SelectedAssets; ContentBrowserModule.Get().GetSelectedAssets(SelectedAssets); TSharedRef DragDropOp = FAssetDragDropOp::New(SelectedAssets); OnAssetsDropped(DragDropOp.ToSharedPtr()); } bool SSequencer::CanAddFromContentBrowser() const { FSequencer& SequencerRef = *SequencerPtr.Pin(); FContentBrowserModule& ContentBrowserModule = FModuleManager::LoadModuleChecked("ContentBrowser"); TArray SelectedAssets; ContentBrowserModule.Get().GetSelectedAssets(SelectedAssets); for (const FAssetData& AssetData : SelectedAssets) { if (MovieSceneToolHelpers::IsValidAsset(SequencerRef.GetFocusedMovieSceneSequence(), AssetData)) { return true; } } return false; } void SSequencer::OnAssetsDropped(TSharedPtr DragDropOp) { using namespace UE::Sequencer; FSequencer& SequencerRef = *SequencerPtr.Pin(); TArray< UObject* > DroppedObjects; bool bAllAssetsWereLoaded = true; bool bNeedsLoad = false; for (const FAssetData& AssetData : DragDropOp->GetAssets()) { if (!AssetData.IsAssetLoaded()) { bNeedsLoad = true; break; } } if (bNeedsLoad) { GWarn->BeginSlowTask(LOCTEXT("OnDrop_FullyLoadPackage", "Fully Loading Package For Drop"), true, false); } for (const FAssetData& AssetData : DragDropOp->GetAssets()) { if (!MovieSceneToolHelpers::IsValidAsset(SequencerRef.GetFocusedMovieSceneSequence(), AssetData)) { continue; } UObject* Object = AssetData.GetAsset(); if ( Object != nullptr ) { DroppedObjects.Add( Object ); } else { bAllAssetsWereLoaded = false; } } if (bNeedsLoad) { GWarn->EndSlowTask(); } FGuid TargetObjectGuid; // if exactly one object node is selected, we have a target object guid if (SequencerPtr.Pin()->GetViewModel()->GetSelection()->Outliner.Num() == 1) { for (TViewModelPtr ObjectBindingNode : SequencerPtr.Pin()->GetViewModel()->GetSelection()->Outliner.Filter()) { TargetObjectGuid = ObjectBindingNode->GetObjectGuid(); } } ESequencerDropResult DropResult = ESequencerDropResult::Unhandled; const FScopedTransaction Transaction(LOCTEXT("DropAssets", "Drop Assets")); // See if any callback wants to handle this drop. for (FOnAssetsDrop Delegate : OnAssetsDrop) { if (Delegate.IsBound()) { DropResult = Delegate.Execute(DroppedObjects, *DragDropOp); if (DropResult != ESequencerDropResult::Unhandled) { break; } } } // If nobody took care of it, do the default behaviour. if (DropResult == ESequencerDropResult::Unhandled) { FMovieSceneTrackEditor::BeginKeying(SequencerPtr.Pin()->GetLocalTime().Time.FrameNumber); TArray Folders; SequencerRef.GetSelectedFolders(Folders); for (TArray::TConstIterator CurObjectIter = DroppedObjects.CreateConstIterator(); CurObjectIter; ++CurObjectIter) { UObject* CurObject = *CurObjectIter; if (!SequencerRef.OnHandleAssetDropped(CurObject, TargetObjectGuid)) { // Doesn't make sense to drop a level sequence asset into sequencer as a spawnable actor if (CurObject->IsA()) { UE_LOG(LogSequencer, Warning, TEXT("Can't add '%s' as a spawnable"), *CurObject->GetName()); continue; } bool bPreferenceReplaceable = FSlateApplication::Get().GetModifierKeys().IsAltDown(); UE::Sequencer::FCreateBindingParams Params; Params.BindingNameOverride = CurObject->GetName(); Params.bSpawnable = bPreferenceReplaceable ? false : true; Params.bReplaceable = true; Params.ActorFactory = DragDropOp->GetActorFactory(); FGuid Guid = SequencerRef.CreateBinding(*CurObject, Params); if (Guid.IsValid()) { DropResult = ESequencerDropResult::DropHandled; if (Folders.Num() > 0) { Folders[0]->AddChildObjectBinding(Guid); } } } } FMovieSceneTrackEditor::EndKeying(); } if (DropResult == ESequencerDropResult::DropHandled) { // Update the sequencers view of the movie scene data when any object is added SequencerRef.NotifyMovieSceneDataChanged( EMovieSceneDataChangeType::MovieSceneStructureItemAdded ); // Update the tree and synchronize selection UpdateLayoutTree(); SequencerRef.SynchronizeSequencerSelectionWithExternalSelection(); } } void SSequencer::OnClassesDropped(TSharedPtr DragDropOp ) { const FScopedTransaction Transaction(LOCTEXT("DropClasses", "Drop Classes")); ESequencerDropResult DropResult = ESequencerDropResult::Unhandled; for (FOnClassesDrop Delegate : OnClassesDrop) { if (Delegate.IsBound()) { DropResult = Delegate.Execute(DragDropOp->ClassesToDrop, *DragDropOp); if (DropResult != ESequencerDropResult::Unhandled) { break; } } } if (DropResult == ESequencerDropResult::Unhandled) { FSequencer& SequencerRef = *SequencerPtr.Pin(); for (auto ClassIter = DragDropOp->ClassesToDrop.CreateConstIterator(); ClassIter; ++ClassIter) { UClass* Class = (*ClassIter).Get(); if (Class != nullptr) { UObject* Object = Class->GetDefaultObject(); UE::Sequencer::FCreateBindingParams Params; Params.bSpawnable = true; SequencerRef.CreateBinding(*Object, Params); } } } } void SSequencer::OnActorsDropped(TSharedPtr DragDropOp ) { const FScopedTransaction Transaction(LOCTEXT("DropActors", "Drop Actors")); ESequencerDropResult DropResult = ESequencerDropResult::Unhandled; for (FOnActorsDrop Delegate : OnActorsDrop) { if (Delegate.IsBound()) { DropResult = Delegate.Execute(DragDropOp->Actors, *DragDropOp); if (DropResult != ESequencerDropResult::Unhandled) { break; } } } if (DropResult == ESequencerDropResult::Unhandled) { SequencerPtr.Pin()->OnActorsDropped(DragDropOp->Actors); } } void SSequencer::OnFolderDropped(TSharedPtr DragDropOp ) { // Sequencer doesn't support dragging folder with a root object if (!FFolder::IsRootObjectPersistentLevel(DragDropOp->RootObject)) { return; } const FScopedTransaction Transaction(LOCTEXT("DropActors", "Drop Actors")); ESequencerDropResult DropResult = ESequencerDropResult::Unhandled; TArray> DraggedActors; // Find any actors in the global editor world that have any of the dragged paths. // WARNING: Actor iteration can be very slow, so this needs to be optimized TSharedPtr Sequencer = SequencerPtr.Pin(); UObject* PlaybackContext = Sequencer->GetPlaybackContext(); UWorld* World = PlaybackContext ? PlaybackContext->GetWorld() : nullptr; if (World) { FActorFolders::GetWeakActorsFromFolders(*World, DragDropOp->Folders, DraggedActors); } for (FOnFoldersDrop Delegate : OnFoldersDrop) { if (Delegate.IsBound()) { DropResult = Delegate.Execute(DragDropOp->Folders, *DragDropOp); if (DropResult != ESequencerDropResult::Unhandled) { break; } } } if (DropResult == ESequencerDropResult::Unhandled) { SequencerPtr.Pin()->OnActorsDropped(DraggedActors); } } void SSequencer::OnCrumbClicked(const FSequencerBreadcrumb& Item) { if( SequencerPtr.Pin()->GetFocusedTemplateID() != Item.SequenceID ) { SequencerPtr.Pin()->PopToSequenceInstance( Item.SequenceID ); } } FText SSequencer::GetRootAnimationName() const { return SequencerPtr.Pin()->GetRootMovieSceneSequence()->GetDisplayName(); } TSharedPtr SSequencer::GetTreeView() const { return TreeView; } TSharedPtr SSequencer::GetPinnedTreeView() const { return PinnedTreeView; } void SSequencer::OnSaveMovieSceneClicked() { SequencerPtr.Pin()->SaveCurrentMovieScene(); } float SSequencer::GetPinnedAreaMaxHeight() const { if (!MainSequencerArea.IsValid()) { return 0.0f; } // Allow the pinned area to use up to 2/3rds of the sequencer area return MainSequencerArea->GetCachedGeometry().GetLocalSize().Y * 0.666f; } EVisibility SSequencer::GetPinnedAreaVisibility() const { return PinnedTreeView->GetNumRootNodes() > 0 ? EVisibility::Visible : EVisibility::Collapsed; } FText SSequencer::GetBreadcrumbTextForSection(TWeakObjectPtr SubSection) const { UMovieSceneSubSection* SubSectionPtr = SubSection.Get(); return SubSectionPtr ? GetBreadcrumbTextForSequence(SubSectionPtr->GetSequence(), SubSectionPtr->IsActive()) : FText(); } FText SSequencer::GetBreadcrumbTextForSequence(TWeakObjectPtr Sequence, bool bIsActive) const { UMovieSceneSequence* SequencePtr = Sequence.Get(); bool bIsDirty = SequencePtr->GetMovieScene()->GetOuter()->GetPackage()->IsDirty(); if (bIsActive) { if (bIsDirty) { return FText::Format(LOCTEXT("DirtySequenceBreadcrumbFormat", "{0}*"), SequencePtr->GetDisplayName()); } else { return SequencePtr->GetDisplayName(); } } else { if (bIsDirty) { return FText::Format(LOCTEXT("DirtyInactiveSequenceBreadcrumbFormat", "{0}* [{1}]"), SequencePtr->GetDisplayName(), LOCTEXT("InactiveSequenceBreadcrumb", "Inactive")); } else { return FText::Format(LOCTEXT("InactiveSequenceBreadcrumbFormat", "{0} [{1}]"), SequencePtr->GetDisplayName(), LOCTEXT("InactiveSequenceBreadcrumb", "Inactive")); } } } EVisibility SSequencer::GetBreadcrumbTrailVisibility() const { return SequencerPtr.Pin()->IsLevelEditorSequencer() ? EVisibility::Visible : EVisibility::Collapsed; } bool SSequencer::CanNavigateBreadcrumbs() const { if (SequencerPtr.Pin()->IsLevelEditorSequencer()) { UMovieSceneSequence* RootSequence = SequencerPtr.Pin()->GetRootMovieSceneSequence(); UMovieScene* MovieScene = RootSequence ? RootSequence->GetMovieScene() : nullptr; if (RootSequence) { for (UMovieSceneTrack* Track : MovieScene->GetTracks()) { if (Track && Track->IsA()) { return true; } } } } return false; } EVisibility SSequencer::GetBottomTimeSliderVisibility() const { return GetSequencerSettings()->GetShowRangeSlider() ? EVisibility::Hidden : EVisibility::Visible; } EVisibility SSequencer::GetTimeRangeVisibility() const { return GetSequencerSettings()->GetShowRangeSlider() ? EVisibility::Visible : EVisibility::Hidden; } EVisibility SSequencer::GetInfoButtonVisibility() const { return GetSequencerSettings()->GetShowInfoButton() ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden; } EVisibility SSequencer::GetShowTickLines() const { return GetSequencerSettings()->GetShowTickLines() ? EVisibility::SelfHitTestInvisible : EVisibility::Hidden; } EVisibility SSequencer::GetShowSequencerToolbar() const { return GetSequencerSettings()->GetShowSequencerToolbar() ? EVisibility::Visible : EVisibility::Collapsed; } EFrameNumberDisplayFormats SSequencer::GetTimeDisplayFormat() const { return GetSequencerSettings()->GetTimeDisplayFormat(); } void SSequencer::OnSplitterFinishedResizing() { SSplitter::FSlot const& LeftSplitterSlot = TreeViewSplitter->Splitter->SlotAt(0); SSplitter::FSlot const& RightSplitterSlot = TreeViewSplitter->Splitter->SlotAt(1); OnColumnFillCoefficientChanged(LeftSplitterSlot.GetSizeValue(), 0); OnColumnFillCoefficientChanged(RightSplitterSlot.GetSizeValue(), 1); GetSequencerSettings()->SetTreeViewWidth(LeftSplitterSlot.GetSizeValue()); } void SSequencer::OnColumnFillCoefficientChanged(float FillCoefficient, int32 ColumnIndex) { ColumnFillCoefficients[ColumnIndex] = FillCoefficient; } void SSequencer::OnCurveEditorVisibilityChanged(bool bShouldBeVisible) { using namespace UE::Sequencer; TSharedPtr Sequencer = SequencerPtr.Pin(); // Curve Editor may not be supported if (!Sequencer->GetHostCapabilities().bSupportsCurveEditor) { return; } TSharedPtr RootModel = Sequencer->GetViewModel(); FCurveEditorExtension* CurveEditorExtension = RootModel->CastDynamicChecked(); if (bShouldBeVisible) { CurveEditorExtension->OpenCurveEditor(); } else { CurveEditorExtension->CloseCurveEditor(); } } void SSequencer::OnTimeSnapIntervalChanged(float InInterval) { // @todo: sequencer-timecode: Address dealing with different time intervals // TSharedPtr Sequencer = SequencerPtr.Pin(); // if ( Sequencer.IsValid() ) // { // UMovieScene* MovieScene = Sequencer->GetFocusedMovieSceneSequence()->GetMovieScene(); // if (!FMath::IsNearlyEqual(MovieScene->GetFixedFrameInterval(), InInterval)) // { // FScopedTransaction SetFixedFrameIntervalTransaction( NSLOCTEXT( "Sequencer", "SetFixedFrameInterval", "Set scene fixed frame interval" ) ); // MovieScene->Modify(); // MovieScene->SetFixedFrameInterval( InInterval ); // // Update the current time to the new interval // float NewTime = SequencerHelpers::SnapTimeToInterval(Sequencer->GetLocalTime(), InInterval); // Sequencer->SetLocalTime(NewTime); // } // } } FPaintPlaybackRangeArgs SSequencer::GetSectionPlaybackRangeArgs() const { if (GetBottomTimeSliderVisibility() == EVisibility::Visible) { static FPaintPlaybackRangeArgs Args(FAppStyle::GetBrush("Sequencer.Timeline.PlayRange_L"), FAppStyle::GetBrush("Sequencer.Timeline.PlayRange_R"), 6.f); return Args; } else { static FPaintPlaybackRangeArgs Args(FAppStyle::GetBrush("Sequencer.Timeline.PlayRange_Bottom_L"), FAppStyle::GetBrush("Sequencer.Timeline.PlayRange_Bottom_R"), 6.f); return Args; } } UE::Sequencer::FVirtualTrackArea SSequencer::GetVirtualTrackArea(const UE::Sequencer::STrackAreaView* InTrackArea) const { using namespace UE::Sequencer; const STrackAreaView* TargetTrackArea = TrackArea.Get(); TSharedPtr TargetTreeView = TreeView; if (InTrackArea != nullptr) { TargetTrackArea = InTrackArea; TargetTreeView = TargetTrackArea->GetOutliner().Pin(); } TSharedPtr TargetTrackAreaViewModel = TargetTrackArea->GetViewModel(); return FVirtualTrackArea(*TargetTrackAreaViewModel, *TargetTreeView.Get(), TargetTrackArea->GetCachedGeometry()); } FPasteContextMenuArgs SSequencer::GeneratePasteArgs(FFrameNumber PasteAtTime, TSharedPtr Clipboard) { using namespace UE::Sequencer; TSharedPtr Sequencer = SequencerPtr.Pin(); if (GetSequencerSettings()->GetForceWholeFrames()) { FFrameRate TickResolution = Sequencer->GetFocusedTickResolution(); FFrameRate DisplayRate = Sequencer->GetFocusedDisplayRate(); PasteAtTime = ConvertFrameTime(PasteAtTime, TickResolution, DisplayRate).RoundToFrame(); PasteAtTime = ConvertFrameTime(PasteAtTime, DisplayRate, TickResolution).FrameNumber; } // Open a paste menu at the current mouse position FSlateApplication& Application = FSlateApplication::Get(); FVector2D LocalMousePosition = TrackArea->GetCachedGeometry().AbsoluteToLocal(Application.GetCursorPos()); FVirtualTrackArea VirtualTrackArea = GetVirtualTrackArea(); // Paste into the currently selected sections, or hit test the mouse position as a last resort TArray> PasteIntoNodes; { TSharedPtr Selection = Sequencer->GetViewModel()->GetSelection(); TSet Sections = Selection->GetSelectedSections(); for (FKeyHandle Key : Selection->KeySelection) { TSharedPtr Channel = Selection->KeySelection.GetModelForKey(Key); UMovieSceneSection* Section = Channel ? Channel->GetSection() : nullptr; if (Channel) { Sections.Add(Section); } } for (UMovieSceneSection* Section : Sections) { if (TSharedPtr Handle = Sequencer->GetNodeTree()->GetSectionModel(Section)) { TViewModelPtr TrackModel = Handle->GetParentTrackModel().ImplicitCast(); if (TrackModel) { PasteIntoNodes.Add(TrackModel); } } } for (FViewModelPtr SelectedNode : Sequencer->GetViewModel()->GetSelection()->Outliner) { if (SelectedNode->IsA() || SelectedNode->IsA() || SelectedNode->IsA()) { TViewModelPtr TrackModel = CastViewModelChecked(SelectedNode); if (TrackModel) { PasteIntoNodes.Add(TrackModel); } } } } if (PasteIntoNodes.Num() == 0) { TSharedPtr Node = VirtualTrackArea.HitTestNode(LocalMousePosition.Y); if (Node.IsValid()) { PasteIntoNodes.Add(FViewModelPtr(Node).ImplicitCast()); } } return FPasteContextMenuArgs::PasteInto(MoveTemp(PasteIntoNodes), PasteAtTime, Clipboard); } void SSequencer::OnPaste() { using namespace UE::Sequencer; TSharedPtr Sequencer = SequencerPtr.Pin(); if (Sequencer->GetViewModel()->GetSelection()->Outliner.Num() == 0) { if (!OpenPasteMenu()) { DoPaste(); } } else { if (!DoPaste()) { OpenPasteMenu(); } } } bool SSequencer::CanPaste() { TSharedPtr Sequencer = SequencerPtr.Pin(); FString TextToImport; FPlatformApplicationMisc::ClipboardPaste(TextToImport); // Attempts to deserialize the text into object bindings/tracks that Sequencer understands. if (Sequencer->CanPaste(TextToImport)) { return true; } return SequencerPtr.Pin()->GetClipboardStack().Num() != 0; } bool SSequencer::DoPaste() { TSharedPtr Sequencer = SequencerPtr.Pin(); return Sequencer->DoPaste(); } bool SSequencer::OpenPasteMenu() { TSharedPtr ContextMenu; TSharedPtr Sequencer = SequencerPtr.Pin(); if (Sequencer->GetClipboardStack().Num() != 0) { FPasteContextMenuArgs Args = GeneratePasteArgs(Sequencer->GetLocalTime().Time.FrameNumber, Sequencer->GetClipboardStack().Last()); ContextMenu = FPasteContextMenu::CreateMenu(SequencerPtr, Args); } if (!ContextMenu.IsValid() || !ContextMenu->IsValidPaste()) { return false; } else if (ContextMenu->AutoPaste()) { return true; } const bool bShouldCloseWindowAfterMenuSelection = true; TSharedPtr MenuExtender = MakeShared(); FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, SequencerPtr.Pin()->GetCommandBindings(), MenuExtender); ContextMenu->PopulateMenu(MenuBuilder, MenuExtender); FWidgetPath Path; FSlateApplication::Get().FindPathToWidget(AsShared(), Path); FSlateApplication::Get().PushMenu( AsShared(), Path, MenuBuilder.MakeWidget(), FSlateApplication::Get().GetCursorPos(), FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu) ); return true; } void SSequencer::PasteFromHistory() { TSharedPtr Sequencer = SequencerPtr.Pin(); if (Sequencer->GetClipboardStack().Num() == 0) { return; } FPasteContextMenuArgs Args = GeneratePasteArgs(Sequencer->GetLocalTime().Time.FrameNumber); TSharedPtr ContextMenu = FPasteFromHistoryContextMenu::CreateMenu(SequencerPtr, Args); if (ContextMenu.IsValid()) { const bool bShouldCloseWindowAfterMenuSelection = true; TSharedPtr MenuExtender = MakeShared(); FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, Sequencer->GetCommandBindings(), MenuExtender); ContextMenu->PopulateMenu(MenuBuilder, MenuExtender); FWidgetPath Path; FSlateApplication::Get().FindPathToWidget(AsShared(), Path); FSlateApplication::Get().PushMenu( AsShared(), Path, MenuBuilder.MakeWidget(), FSlateApplication::Get().GetCursorPos(), FPopupTransitionEffect(FPopupTransitionEffect::ContextMenu) ); } } EVisibility SSequencer::GetDebugVisualizerVisibility() const { return GetSequencerSettings()->ShouldShowDebugVisualization() ? EVisibility::Visible : EVisibility::Collapsed; } double SSequencer::GetSpinboxDelta() const { TSharedPtr Sequencer = SequencerPtr.Pin(); return Sequencer->GetDisplayRateDeltaFrameCount(); } bool SSequencer::GetIsSequenceReadOnly() const { TSharedPtr Sequencer = SequencerPtr.Pin(); return Sequencer->GetFocusedMovieSceneSequence() ? Sequencer->GetFocusedMovieSceneSequence()->GetMovieScene()->IsReadOnly() : false; } void SSequencer::OnSetSequenceReadOnly(ECheckBoxState CheckBoxState) { TSharedPtr Sequencer = SequencerPtr.Pin(); bool bReadOnly = CheckBoxState == ECheckBoxState::Checked; if (Sequencer->GetFocusedMovieSceneSequence()) { UMovieScene* MovieScene = Sequencer->GetFocusedMovieSceneSequence()->GetMovieScene(); const FScopedTransaction Transaction(CheckBoxState == ECheckBoxState::Checked ? LOCTEXT("LockMovieScene", "Lock Movie Scene") : LOCTEXT("UnlockMovieScene", "Unlock Movie Scene") ); MovieScene->Modify(); MovieScene->SetReadOnly(bReadOnly); TArray DescendantMovieScenes; MovieSceneHelpers::GetDescendantMovieScenes(Sequencer->GetFocusedMovieSceneSequence(), DescendantMovieScenes); for (UMovieScene* DescendantMovieScene : DescendantMovieScenes) { if (DescendantMovieScene && bReadOnly != DescendantMovieScene->IsReadOnly()) { DescendantMovieScene->Modify(); DescendantMovieScene->SetReadOnly(bReadOnly); } } Sequencer->NotifyMovieSceneDataChanged(EMovieSceneDataChangeType::Unknown); } } void SSequencer::SetPlayTimeClampedByWorkingRange(double Frame) { if (SequencerPtr.IsValid()) { TSharedPtr Sequencer = SequencerPtr.Pin(); // Some of our spin boxes need to use an unbounded min/max so that they can drag linearly instead of based on the current value. // We clamp the value here by the working range to emulate the behavior of the Cinematic Level Viewport FFrameRate PlayRate = Sequencer->GetLocalTime().Rate; UMovieScene* MovieScene = Sequencer->GetFocusedMovieSceneSequence()->GetMovieScene(); double StartInSeconds = MovieScene->GetEditorData().WorkStart; double EndInSeconds = MovieScene->GetEditorData().WorkEnd; Frame = FMath::Clamp(Frame, (double)(StartInSeconds*PlayRate).GetFrame().Value, (double)(EndInSeconds*PlayRate).GetFrame().Value); Sequencer->SetLocalTime(FFrameTime::FromDecimal(Frame)); } } void SSequencer::SetPlayTime(double Frame) { if (SequencerPtr.IsValid()) { FFrameTime NewFrame = FFrameTime::FromDecimal(Frame); TSharedPtr Sequencer = SequencerPtr.Pin(); FFrameRate PlayRate = Sequencer->GetLocalTime().Rate; double FrameInSeconds = PlayRate.AsSeconds(NewFrame); TRange NewWorkingRange = Sequencer->GetClampRange(); if (FrameInSeconds < NewWorkingRange.GetLowerBoundValue()) { NewWorkingRange.SetLowerBoundValue(FrameInSeconds); NewWorkingRange.SetLowerBoundValue(UE::MovieScene::ExpandRange(NewWorkingRange, NewWorkingRange.Size() * 0.1f).GetLowerBoundValue()); } if (FrameInSeconds > NewWorkingRange.GetUpperBoundValue()) { NewWorkingRange.SetUpperBoundValue(FrameInSeconds); NewWorkingRange.SetUpperBoundValue(UE::MovieScene::ExpandRange(NewWorkingRange, NewWorkingRange.Size() * 0.1f).GetUpperBoundValue()); } TRange NewViewRange = Sequencer->GetViewRange(); if (FrameInSeconds < NewViewRange.GetLowerBoundValue()) { NewViewRange.SetLowerBoundValue(FrameInSeconds); NewViewRange.SetLowerBoundValue(UE::MovieScene::ExpandRange(NewViewRange, NewViewRange.Size() * 0.1f).GetLowerBoundValue()); } if (FrameInSeconds > NewViewRange.GetUpperBoundValue()) { NewViewRange.SetUpperBoundValue(FrameInSeconds); NewViewRange.SetUpperBoundValue(UE::MovieScene::ExpandRange(NewViewRange, NewViewRange.Size() * 0.1f).GetUpperBoundValue()); } Sequencer->SetClampRange(NewWorkingRange); Sequencer->SetViewRange(NewViewRange); Sequencer->SetLocalTime(NewFrame); // Refocus on the previously focused widget so that user can continue on after setting a time if (PlayTimeDisplay) { PlayTimeDisplay->Refocus(); } } } void SSequencer::ApplySequencerCustomizations(const TArrayView Customizations) { AddMenuExtenders.Reset(); ToolbarExtenders.Reset(); ActionsMenuExtenders.Reset(); ViewMenuExtenders.Reset(); OnReceivedDragOver.Reset(); OnReceivedDrop.Reset(); OnAssetsDrop.Reset(); OnActorsDrop.Reset(); OnClassesDrop.Reset(); OnFoldersDrop.Reset(); ApplySequencerCustomization(RootCustomization); for (const FSequencerCustomizationInfo& Info : Customizations) { ApplySequencerCustomization(Info); } ToolbarContainer->SetContent(MakeToolBar()); } void SSequencer::ApplySequencerCustomization(const FSequencerCustomizationInfo& Customization) { if (Customization.AddMenuExtender != nullptr) { AddMenuExtenders.Add(Customization.AddMenuExtender); } if (Customization.ToolbarExtender != nullptr) { ToolbarExtenders.Add(Customization.ToolbarExtender); } if (Customization.ActionsMenuExtender != nullptr) { ActionsMenuExtenders.Add(Customization.ActionsMenuExtender); } if (Customization.ViewMenuExtender != nullptr) { ViewMenuExtenders.Add(Customization.ViewMenuExtender); } if (Customization.OnReceivedDragOver.IsBound()) { OnReceivedDragOver.Add(Customization.OnReceivedDragOver); } if (Customization.OnReceivedDrop.IsBound()) { OnReceivedDrop.Add(Customization.OnReceivedDrop); } if (Customization.OnAssetsDrop.IsBound()) { OnAssetsDrop.Add(Customization.OnAssetsDrop); } if (Customization.OnActorsDrop.IsBound()) { OnActorsDrop.Add(Customization.OnActorsDrop); } if (Customization.OnClassesDrop.IsBound()) { OnClassesDrop.Add(Customization.OnClassesDrop); } if (Customization.OnFoldersDrop.IsBound()) { OnFoldersDrop.Add(Customization.OnFoldersDrop); } } USequencerSettings* SSequencer::GetSequencerSettings() const { if (SequencerPtr.IsValid()) { return SequencerPtr.Pin()->GetSequencerSettings(); } return nullptr; } bool SSequencer::RegisterDrawer(FSidebarDrawerConfig&& InDrawerConfig) { if (DetailsSidebar.IsValid()) { return DetailsSidebar->RegisterDrawer(MoveTemp(InDrawerConfig)); } return false; } bool SSequencer::UnregisterDrawer(const FName InDrawerId) { if (DetailsSidebar.IsValid()) { return DetailsSidebar->UnregisterDrawer(InDrawerId); } return false; } bool SSequencer::RegisterDrawerSection(const FName InDrawerId, const TSharedPtr& InSection) { if (DetailsSidebar.IsValid()) { return DetailsSidebar->RegisterDrawerSection(InDrawerId, InSection); } return false; } bool SSequencer::UnregisterDrawerSection(const FName InDrawerId, const FName InSectionId) { if (DetailsSidebar.IsValid()) { return DetailsSidebar->UnregisterDrawerSection(InDrawerId, InSectionId); } return false; } void SSequencer::OnSidebarStateChanged(const FSidebarState& InNewState) { USequencerSettings* const SequencerSettings = GetSequencerSettings(); if (IsValid(SequencerSettings)) { SequencerSettings->SetSidebarState(InNewState); } } bool SSequencer::IsSidebarVisible() const { USequencerSettings* const SequencerSettings = GetSequencerSettings(); if (IsValid(SequencerSettings)) { return SequencerSettings->GetSidebarState().IsVisible(); } return false; } void SSequencer::SetSidebarVisible(const bool bInVisible) { USequencerSettings* const SequencerSettings = GetSequencerSettings(); if (!IsValid(SequencerSettings)) { return; } FSidebarState SidebarState = SequencerSettings->GetSidebarState(); SidebarState.SetVisible(bInVisible); SequencerSettings->SetSidebarState(SidebarState); RebuildForSidebar(); } void SSequencer::ToggleSidebarVisible() { SetSidebarVisible(!IsSidebarVisible()); } void SSequencer::ToggleSidebarSelectionDrawerOpen() { if (!DetailsSidebar.IsValid()) { return; } if (DetailsSidebar->IsDrawerDocked(FSequencer::SelectionDrawerId)) { DetailsSidebar->UndockAllDrawers(); } else if (DetailsSidebar->IsDrawerOpened(FSequencer::SelectionDrawerId)) { DetailsSidebar->CloseAllDrawers(); } else { DetailsSidebar->TryOpenDrawer(FSequencer::SelectionDrawerId); } } void SSequencer::ToggleSidebarDrawerDock() { if (!DetailsSidebar.IsValid()) { return; } const FName OpenedDrawerId = DetailsSidebar->GetOpenedDrawerId(); if (!OpenedDrawerId.IsNone()) { DetailsSidebar->SetDrawerDocked(OpenedDrawerId, true); DetailsSidebar->CloseAllDrawers(); } else if (DetailsSidebar->HasDrawerDocked()) { DetailsSidebar->UndockAllDrawers(); } } void SSequencer::EnablePendingFocusOnHovering(const bool InEnabled) { PendingFocus.Enable(InEnabled); EnableCurveEditorPendingFocusOnHovering(InEnabled); } void SSequencer::EnableCurveEditorPendingFocusOnHovering(const bool InEnabled) const { using namespace UE::Sequencer; TSharedPtr Sequencer = SequencerPtr.Pin(); if (!Sequencer.IsValid()) { return; } if (!Sequencer->GetHostCapabilities().bSupportsCurveEditor) { return; } const FCurveEditorExtension* CurveEditorExtension = Sequencer->GetViewModel()->CastDynamic(); const TSharedPtr CurveEditor = CurveEditorExtension ? CurveEditorExtension->GetCurveEditor() : nullptr; const TSharedPtr CurveEditorPanel = CurveEditor.IsValid() ? CurveEditor->GetPanel() : nullptr; if (!CurveEditorPanel) { return; } CurveEditorPanel->EnablePendingFocusOnHovering(InEnabled); } TSharedPtr SSequencer::GetFilterBar() const { if (const TSharedPtr Sequencer = SequencerPtr.Pin()) { return Sequencer->GetFilterBar(); } return nullptr; } TSharedPtr SSequencer::GetFilterBarWidget() const { return FilterBarWidget; } bool SSequencer::IsFilterBarVisible() const { if (!FilterBarWidget.IsValid()) { return false; } USequencerSettings* const SequencerSettings = GetSequencerSettings(); if (ensure(SequencerSettings)) { if (!SequencerSettings->IsFilterBarVisible()) { return false; } } const TSharedPtr FilterBar = FilterBarWidget->GetFilterBar(); if (!FilterBar.IsValid()) { return false; } return FilterBar->HasAnyFiltersEnabled(); } void SSequencer::ToggleFilterBarVisibility() { USequencerSettings* const SequencerSettings = GetSequencerSettings(); if (ensure(SequencerSettings)) { const bool bNewFilterBarVisible = !SequencerSettings->IsFilterBarVisible(); SequencerSettings->SetFilterBarVisible(bNewFilterBarVisible); } RebuildFilterBarContent(); } EFilterBarLayout SSequencer::GetFilterBarLayout() const { USequencerSettings* const SequencerSettings = GetSequencerSettings(); if (ensure(SequencerSettings)) { return SequencerSettings->GetFilterBarLayout(); } return EFilterBarLayout::Vertical; } void SSequencer::SetFilterBarLayout(const EFilterBarLayout InLayout) { USequencerSettings* const SequencerSettings = GetSequencerSettings(); if (ensure(SequencerSettings)) { SequencerSettings->SetFilterBarLayout(InLayout); } RebuildFilterBarContent(); } void SSequencer::OnFilterBarStateChanged(const bool bInIsVisible, const EFilterBarLayout InNewLayout) { RebuildFilterBarContent(); } void SSequencer::OnTrackFiltersChanged(const ESequencerFilterChange InChangeType, const TSharedRef& InFilter) { RebuildFilterBarContent(); if (SequencerTreeFilterStatusBar.IsValid()) { SequencerTreeFilterStatusBar->UpdateText(); } } void SSequencer::RebuildSearchAndFilterRow() { SearchAndFilterRow->ClearChildren(); SearchAndFilterRow->AddSlot() .AutoHeight() [ ConstructSearchAndFilterRow() ]; if (FilterBarWidget.IsValid()) { if (IsFilterBarVisible() && GetFilterBarLayout() == EFilterBarLayout::Horizontal) { SearchAndFilterRow->AddSlot() .AutoHeight() .Padding(0) [ FilterBarWidget.ToSharedRef() ]; } } } #undef LOCTEXT_NAMESPACE