// Copyright Epic Games, Inc. All Rights Reserved. #include "Widgets/SSequencerLayerBar.h" #include "Tools/EditToolDragOperations.h" #include "Tools/DragOperation_Stretch.h" #include "Tools/SequencerEditTool_Movement.h" #include "MVVM/ObjectBindingModelStorageExtension.h" #include "MVVM/ViewModels/LayerBarModel.h" #include "MVVM/ViewModels/ObjectBindingModel.h" #include "MVVM/ViewModels/SequenceModel.h" #include "MVVM/ViewModels/SequencerEditorViewModel.h" #include "MVVM/ViewModels/TrackAreaViewModel.h" #include "MVVM/Selection/Selection.h" #include "MVVM/ViewModels/ViewModel.h" #include "MVVM/Views/STrackAreaView.h" #include "SequencerCommonHelpers.h" #include "MovieSceneTimeHelpers.h" #include "Sequencer.h" #include "ISequencerSection.h" #include "Widgets/Layout/SSpacer.h" #include "Styling/AppStyle.h" #define LOCTEXT_NAMESPACE "SSequencerLayerBar" namespace UE { namespace Sequencer { /** A hotspot representing a section */ struct FLayerBarHotspot : ITrackAreaHotspot , IMouseHandlerHotspot { UE_SEQUENCER_DECLARE_CASTABLE(FLayerBarHotspot, ITrackAreaHotspot, IMouseHandlerHotspot); FLayerBarHotspot(TSharedPtr InLayerBar, TSharedPtr InSequencer) : LayerBar(InLayerBar) , WeakSequencer(InSequencer) {} void UpdateOnHover(FTrackAreaViewModel& InTrackArea) const override { InTrackArea.AttemptToActivateTool(FSequencerEditTool_Movement::Identifier); } TOptional GetTime() const override { return LayerBar->ComputeRange().GetLowerBoundValue(); } TOptional GetOffsetTime() const override { return TOptional(); } TSharedPtr InitiateDrag(const FPointerEvent& MouseEvent) override { TSharedPtr Sequencer = WeakSequencer.Pin(); FSequencerSelection& Selection = Sequencer->GetSelection(); if (!Selection.TrackArea.IsSelected(LayerBar)) { Selection.KeySelection.Empty(); Selection.TrackArea.Empty(); Selection.TrackArea.Select(LayerBar); } if (!MouseEvent.IsShiftDown()) { return MakeShareable( new FMoveKeysAndSections( *Sequencer, ESequencerMoveOperationType::MoveSections | ESequencerMoveOperationType::MoveKeys) ); } return nullptr; } bool PopulateContextMenu(FMenuBuilder& MenuBuilder, TSharedPtr MenuExtender, FFrameTime MouseDownTime) override { return true; } void HandleMouseSelection(FHotspotSelectionManager& SelectionManager) override { if (SelectionManager.MouseEvent->GetEffectingButton() == EKeys::LeftMouseButton) { SelectionManager.DefaultModelSelection(LayerBar); } } TSharedPtr LayerBar; TWeakPtr WeakSequencer; }; /** A hotspot representing a section */ struct FLayerBarStretchHotspot : ITrackAreaHotspot , IMouseHandlerHotspot { UE_SEQUENCER_DECLARE_CASTABLE(FLayerBarStretchHotspot, ITrackAreaHotspot, IMouseHandlerHotspot); FLayerBarStretchHotspot(TSharedPtr InLayerBar, EStretchConstraint InStretchConstraint, TSharedPtr InSequencer) : LayerBar(InLayerBar) , WeakSequencer(InSequencer) , StretchConstraint(InStretchConstraint) {} void UpdateOnHover(FTrackAreaViewModel& InTrackArea) const override { InTrackArea.AttemptToActivateTool(FSequencerEditTool_Movement::Identifier); } TOptional GetTime() const override { TRange Range = LayerBar->ComputeRange(); return StretchConstraint == EStretchConstraint::AnchorToStart ? Range.GetUpperBoundValue() : Range.GetLowerBoundValue(); } TOptional GetOffsetTime() const override { return TOptional(); } TSharedPtr InitiateDrag(const FPointerEvent& MouseEvent) override { TSharedPtr Sequencer = WeakSequencer.Pin(); FSequencerSelection& Selection = Sequencer->GetSelection(); if (!Selection.TrackArea.IsSelected(LayerBar)) { Selection.KeySelection.Empty(); Selection.TrackArea.Empty(); Selection.TrackArea.Select(LayerBar); } return MakeShared(Sequencer.Get(), StretchConstraint, GetTime().GetValue()); } bool PopulateContextMenu(FMenuBuilder& MenuBuilder, TSharedPtr MenuExtender, FFrameTime MouseDownTime) override { return true; } void HandleMouseSelection(FHotspotSelectionManager& SelectionManager) override { if (SelectionManager.MouseEvent->GetEffectingButton() == EKeys::LeftMouseButton) { SelectionManager.SelectModelExclusive(LayerBar); } } FCursorReply GetCursor() const override { return FCursorReply::Cursor(EMouseCursor::ResizeLeftRight); } int32 Priority() const override { return 10; } TSharedPtr LayerBar; TWeakPtr WeakSequencer; EStretchConstraint StretchConstraint; }; UE_SEQUENCER_DEFINE_VIEW_MODEL_TYPE_ID(FLayerBarHotspot); UE_SEQUENCER_DEFINE_VIEW_MODEL_TYPE_ID(FLayerBarStretchHotspot); class SSequencerLayerBarHandle : public SCompoundWidget { public: SLATE_BEGIN_ARGS(SSequencerLayerBarHandle) {} SLATE_ATTRIBUTE(const FSlateBrush*, BackgroundBrush) SLATE_END_ARGS() void Construct(const FArguments& InArgs, TWeakPtr InWeakTrackArea, TWeakPtr InWeakParent, EStretchConstraint InHandleType) { WeakTrackArea = InWeakTrackArea; WeakParent = InWeakParent; HandleType = InHandleType; ChildSlot [ SNew(SBorder) .BorderImage(InArgs._BackgroundBrush) ]; } void OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override { TSharedPtr LayerBarWidget = WeakParent.Pin(); TSharedPtr Sequencer = LayerBarWidget->GetSequencer(); if (LayerBarWidget) { TSharedPtr LayerBar = LayerBarWidget->GetLayerBarModel(); if (LayerBar) { TSharedPtr TrackArea = WeakTrackArea.Pin(); TrackArea->AddHotspot(MakeShared(LayerBar, HandleType, Sequencer)); } } SCompoundWidget::OnMouseEnter(MyGeometry, MouseEvent); } void OnMouseLeave(const FPointerEvent& MouseEvent) override { TSharedPtr LayerBarWidget = WeakParent.Pin(); TSharedPtr LayerBar = LayerBarWidget->GetLayerBarModel(); if (LayerBarWidget) { TSharedPtr TrackArea = WeakTrackArea.Pin(); TrackArea->RemoveHotspot(FLayerBarStretchHotspot::ID); } SCompoundWidget::OnMouseLeave(MouseEvent); } TWeakPtr WeakTrackArea; TWeakPtr WeakParent; EStretchConstraint HandleType; }; void SSequencerLayerBar::Construct(const FArguments& InArgs, TWeakPtr InWeakTrackArea, TWeakPtr InWeakEditor, TWeakPtr InWeakLayerBar) { WeakLayerBar = InWeakLayerBar; WeakEditor = InWeakEditor; WeakTrackArea = InWeakTrackArea; TSharedPtr TrackArea = WeakTrackArea.Pin(); ChildSlot [ SNew(SHorizontalBox) .Visibility(this, &SSequencerLayerBar::GetHandleVisibility) + SHorizontalBox::Slot() .Padding(FMargin(1.f, 2.f)) .AutoWidth() [ SNew(SBox) .WidthOverride(8.f) [ SNew(SSequencerLayerBarHandle, TrackArea->GetViewModel(), SharedThis(this), EStretchConstraint::AnchorToEnd) .BackgroundBrush(FAppStyle::Get().GetBrush("Sequencer.LayerBar.HandleLeft")) ] ] + SHorizontalBox::Slot() [ SNew(SSpacer) ] + SHorizontalBox::Slot() .Padding(FMargin(1.f, 2.f)) .AutoWidth() [ SNew(SBox) .WidthOverride(8.f) [ SNew(SSequencerLayerBarHandle, TrackArea->GetViewModel(), SharedThis(this), EStretchConstraint::AnchorToStart) .BackgroundBrush(FAppStyle::Get().GetBrush("Sequencer.LayerBar.HandleRight")) ] ] ]; } TSharedPtr SSequencerLayerBar::GetLayerBarModel() const { return WeakLayerBar.Pin(); } TSharedPtr SSequencerLayerBar::GetSequencer() const { if (TSharedPtr Editor = WeakEditor.Pin()) { return Editor->GetSequencerImpl(); } return nullptr; } TSharedRef SSequencerLayerBar::AsWidget() const { return AsShared(); } FTrackLaneScreenAlignment SSequencerLayerBar::GetAlignment(const ITrackLaneWidgetSpace& InScreenSpace, const FGeometry& InParentGeometry) const { if (TSharedPtr LayerBar = WeakLayerBar.Pin()) { return LayerBar->ArrangeVirtualTrackLaneView().ToScreen(InScreenSpace.GetScreenSpace(), InParentGeometry); } return FTrackLaneScreenAlignment{}; } EVisibility SSequencerLayerBar::GetHandleVisibility() const { return IsHovered() ? EVisibility::Visible : EVisibility::Collapsed; } int32 SSequencerLayerBar::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const { const ESlateDrawEffect DrawEffects = bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect; static const FSlateBrush* LayerBarBackgroundBrush = FAppStyle::Get().GetBrush("Sequencer.LayerBar.Background"); static const FSlateBrush* SelectedSectionOverlay = FAppStyle::Get().GetBrush("Sequencer.Section.CollapsedSelectedSectionOverlay"); TViewModelPtr LayerBar = WeakLayerBar.Pin(); TViewModelPtr OutlinerItem = LayerBar ? LayerBar->GetLinkedOutlinerItem() : nullptr; float TotalNodeHeight = OutlinerItem ? OutlinerItem->GetOutlinerSizing().GetTotalHeight() : 0.0f; // Draw the background FSlateDrawElement::MakeBox( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry( FVector2f(AllottedGeometry.GetLocalSize().X, TotalNodeHeight), FSlateLayoutTransform() ), LayerBarBackgroundBrush, DrawEffects ); ++LayerId; TSharedPtr Sequencer = GetSequencer(); FSequencerSelection& Selection = Sequencer->GetSelection(); FSequencerSelectionPreview& SelectionPreview = Sequencer->GetSelectionPreview(); ESelectionPreviewState SelectionPreviewState = SelectionPreview.GetSelectionState(WeakLayerBar); const bool bIsPreviewUnselected = SelectionPreviewState == ESelectionPreviewState::NotSelected; const bool bIsUnSelected = SelectionPreviewState == ESelectionPreviewState::Undefined && !Selection.TrackArea.IsSelected(WeakLayerBar); if (!bIsPreviewUnselected && !bIsUnSelected) { FLinearColor SelectionColor = FAppStyle::GetSlateColor(SequencerSectionConstants::SelectionColorName).GetColor(FWidgetStyle()); // Use a muted selection color for selection previews if (SelectionPreviewState == ESelectionPreviewState::Selected) { SelectionColor = SelectionColor.LinearRGBToHSV(); SelectionColor.R += 0.1f; // +10% hue SelectionColor.G = 0.6f; // 60% saturation SelectionColor = SelectionColor.HSVToLinearRGB(); } // Draw a selection highlight FSlateDrawElement::MakeBox( OutDrawElements, LayerId, AllottedGeometry.ToPaintGeometry(FVector2f(AllottedGeometry.GetLocalSize().X - 2.f, TotalNodeHeight-3.f), FSlateLayoutTransform(FVector2f(1.f, 1.f))), SelectedSectionOverlay, DrawEffects, SelectionColor.CopyWithNewOpacity(0.8f) ); ++LayerId; } return SCompoundWidget::OnPaint(Args, AllottedGeometry.MakeChild(FVector2f(AllottedGeometry.GetLocalSize().X, TotalNodeHeight), FSlateLayoutTransform()), MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled); } FReply SSequencerLayerBar::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { return FReply::Unhandled(); } void SSequencerLayerBar::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { TSharedPtr LayerBar = WeakLayerBar.Pin(); if (LayerBar) { TSharedPtr Sequencer = GetSequencer(); TSharedPtr TrackArea = WeakTrackArea.Pin()->GetViewModel(); TrackArea->AddHotspot(MakeShared(LayerBar, Sequencer)); } SCompoundWidget::OnMouseEnter(MyGeometry, MouseEvent); } FReply SSequencerLayerBar::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { return FReply::Unhandled(); } void SSequencerLayerBar::OnMouseLeave(const FPointerEvent& MouseEvent) { TSharedPtr LayerBar = WeakLayerBar.Pin(); if (LayerBar) { TSharedPtr TrackArea = WeakTrackArea.Pin()->GetViewModel(); TrackArea->RemoveHotspot(FLayerBarHotspot::ID); } SCompoundWidget::OnMouseLeave(MouseEvent); } } // namespace Sequencer } // namespace UE #undef LOCTEXT_NAMESPACE