// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved. #include "SequencerCommonHelpers.h" #include "SequencerSelectedKey.h" #include "DisplayNodes/SequencerSectionKeyAreaNode.h" #include "DisplayNodes/SequencerTrackNode.h" #include "SSequencer.h" #include "GroupedKeyArea.h" #include "ISequencerHotspot.h" #include "SSequencerTreeView.h" #include "VirtualTrackArea.h" #include "SequencerContextMenus.h" void SequencerHelpers::GetAllKeyAreas(TSharedPtr DisplayNode, TSet>& KeyAreas) { TArray> NodesToCheck; NodesToCheck.Add(DisplayNode); while (NodesToCheck.Num() > 0) { TSharedPtr NodeToCheck = NodesToCheck[0]; NodesToCheck.RemoveAt(0); if (NodeToCheck->GetType() == ESequencerNode::Track) { TSharedPtr TrackNode = StaticCastSharedPtr(NodeToCheck); TArray> KeyAreaNodes; TrackNode->GetChildKeyAreaNodesRecursively(KeyAreaNodes); for (TSharedRef KeyAreaNode : KeyAreaNodes) { for (TSharedPtr KeyArea : KeyAreaNode->GetAllKeyAreas()) { KeyAreas.Add(KeyArea); } } } else { if (NodeToCheck->GetType() == ESequencerNode::KeyArea) { TSharedPtr KeyAreaNode = StaticCastSharedPtr(NodeToCheck); for (TSharedPtr KeyArea : KeyAreaNode->GetAllKeyAreas()) { KeyAreas.Add(KeyArea); } } for (TSharedRef ChildNode : NodeToCheck->GetChildNodes()) { NodesToCheck.Add(ChildNode); } } } } void SequencerHelpers::GetDescendantNodes(TSharedRef DisplayNode, TSet>& Nodes) { for (auto ChildNode : DisplayNode.Get().GetChildNodes()) { Nodes.Add(ChildNode); GetDescendantNodes(ChildNode, Nodes); } } void SequencerHelpers::GetAllSections(TSharedRef DisplayNode, TSet>& Sections) { TSet > AllNodes; AllNodes.Add(DisplayNode); GetDescendantNodes(DisplayNode, AllNodes); for (auto NodeToCheck : AllNodes) { TSet > KeyAreas; GetAllKeyAreas(NodeToCheck, KeyAreas); for (auto KeyArea : KeyAreas) { UMovieSceneSection* OwningSection = KeyArea->GetOwningSection(); if (OwningSection != nullptr) { Sections.Add(OwningSection); } } if (NodeToCheck->GetType() == ESequencerNode::Track) { TSharedRef TrackNode = StaticCastSharedRef( NodeToCheck ); UMovieSceneTrack* Track = TrackNode->GetTrack(); if (Track != nullptr) { for (TSharedRef TrackSection : TrackNode->GetSections()) { if (UMovieSceneSection* Section = TrackSection->GetSectionObject()) { Sections.Add(Section); } } } } } } bool SequencerHelpers::FindObjectBindingNode(TSharedRef DisplayNode, TSharedRef& ObjectBindingNode) { TArray< TSharedPtr > ParentNodes; ParentNodes.Add(DisplayNode); while (DisplayNode->GetParent().IsValid()) { ParentNodes.Add(DisplayNode->GetParent()); DisplayNode = DisplayNode->GetParent().ToSharedRef(); } for (int32 ParentNodeIndex = ParentNodes.Num() - 1; ParentNodeIndex >= 0; --ParentNodeIndex) { if (ParentNodes[ParentNodeIndex]->GetType() == ESequencerNode::Object) { ObjectBindingNode = ParentNodes[ParentNodeIndex].ToSharedRef(); return true; } } return false; } int32 SequencerHelpers::TimeToFrame(float Time, float FrameRate) { float Frame = Time * FrameRate; return FMath::RoundToInt(Frame); } float SequencerHelpers::FrameToTime(int32 Frame, float FrameRate) { return Frame / FrameRate; } float SequencerHelpers::SnapTimeToInterval(float InTime, float InFrameRate) { return InFrameRate > 0 ? FMath::RoundToInt( InTime / InFrameRate ) * InFrameRate : InTime; } bool IsSectionSelectedInNode(FSequencer& Sequencer, TSharedRef InNode) { if (InNode->GetType() == ESequencerNode::Track) { TSharedRef TrackNode = StaticCastSharedRef(InNode); for (auto Section : TrackNode->GetSections()) { if (Sequencer.GetSelection().IsSelected(Section->GetSectionObject())) { return true; } } } return false; } bool AreKeysSelectedInNode(FSequencer& Sequencer, TSharedRef InNode) { TSharedRef TrackNode = StaticCastSharedRef(InNode); TSet> KeyAreas; SequencerHelpers::GetAllKeyAreas(InNode, KeyAreas); for (auto KeyArea : KeyAreas) { for (auto KeyHandle : KeyArea->GetUnsortedKeyHandles()) { FSequencerSelectedKey TestKey(*KeyArea->GetOwningSection(), KeyArea, KeyHandle); if (Sequencer.GetSelection().IsSelected(TestKey)) { return true; } } } for (int32 KeyGroupingIndex = 0; KeyGroupingIndex < InNode->GetNumKeyGroupings(); ++KeyGroupingIndex) { TSharedRef KeyGrouping = InNode->GetKeyGrouping(KeyGroupingIndex); for (auto KeyHandle : KeyGrouping->GetUnsortedKeyHandles()) { FSequencerSelectedKey TestKey(*KeyGrouping->GetOwningSection(), KeyGrouping, KeyHandle); if (Sequencer.GetSelection().IsSelected(TestKey)) { return true; } } } return false; } void SequencerHelpers::ValidateNodesWithSelectedKeysOrSections(FSequencer& Sequencer) { for (auto Node : Sequencer.GetSelection().GetNodesWithSelectedKeysOrSections()) { if (!IsSectionSelectedInNode(Sequencer, Node) && !AreKeysSelectedInNode(Sequencer, Node)) { Sequencer.GetSelection().RemoveFromNodesWithSelectedKeysOrSections(Node); } } } void SequencerHelpers::UpdateHoveredNodeFromSelectedSections(FSequencer& Sequencer) { FSequencerSelection& Selection = Sequencer.GetSelection(); TSharedRef SequencerWidget = StaticCastSharedRef(Sequencer.GetSequencerWidget()); TSharedPtr HoveredNode = SequencerWidget->GetTreeView()->GetNodeTree()->GetHoveredNode(); if (!HoveredNode.IsValid()) { return; } if (IsSectionSelectedInNode(Sequencer, HoveredNode.ToSharedRef())) { Selection.AddToNodesWithSelectedKeysOrSections(HoveredNode.ToSharedRef()); } else { Selection.RemoveFromNodesWithSelectedKeysOrSections(HoveredNode.ToSharedRef()); } } void SequencerHelpers::UpdateHoveredNodeFromSelectedKeys(FSequencer& Sequencer) { FSequencerSelection& Selection = Sequencer.GetSelection(); TSharedRef SequencerWidget = StaticCastSharedRef(Sequencer.GetSequencerWidget()); TSharedPtr HoveredNode = SequencerWidget->GetTreeView()->GetNodeTree()->GetHoveredNode(); if (!HoveredNode.IsValid()) { return; } if (AreKeysSelectedInNode(Sequencer, HoveredNode.ToSharedRef())) { Selection.AddToNodesWithSelectedKeysOrSections(HoveredNode.ToSharedRef()); } else { Selection.RemoveFromNodesWithSelectedKeysOrSections(HoveredNode.ToSharedRef()); } } void SequencerHelpers::PerformDefaultSelection(FSequencer& Sequencer, const FPointerEvent& MouseEvent) { FSequencerSelection& Selection = Sequencer.GetSelection(); // @todo: selection in transactions auto ConditionallyClearSelection = [&]{ if (!MouseEvent.IsShiftDown() && !MouseEvent.IsControlDown()) { Selection.EmptySelectedSections(); Selection.EmptySelectedKeys(); Selection.EmptyNodesWithSelectedKeysOrSections(); } }; TSharedPtr Hotspot = Sequencer.GetHotspot(); if (!Hotspot.IsValid()) { ConditionallyClearSelection(); return; } // Handle right-click selection separately since we never deselect on right click (except for clearing on exclusive selection) if (MouseEvent.GetEffectingButton() == EKeys::RightMouseButton) { if (Hotspot->GetType() == ESequencerHotspot::Key) { FSequencerSelectedKey Key = static_cast(Hotspot.Get())->Key; if (!Selection.IsSelected(Key)) { ConditionallyClearSelection(); Selection.AddToSelection(Key); } } else if (Hotspot->GetType() == ESequencerHotspot::Section) { UMovieSceneSection* Section = static_cast(Hotspot.Get())->Section.GetSectionObject(); if (!Selection.IsSelected(Section)) { ConditionallyClearSelection(); Selection.AddToSelection(Section); } } else if (Hotspot->GetType() == ESequencerHotspot::SectionResize_L || Hotspot->GetType() == ESequencerHotspot::SectionResize_R) { UMovieSceneSection* Section = static_cast(Hotspot.Get())->Section.GetSectionObject(); if (!Selection.IsSelected(Section)) { ConditionallyClearSelection(); Selection.AddToSelection(Section); } } if (Hotspot->GetType() == ESequencerHotspot::Key) { UpdateHoveredNodeFromSelectedKeys(Sequencer); } else { UpdateHoveredNodeFromSelectedSections(Sequencer); } return; } // Normal selection ConditionallyClearSelection(); bool bForceSelect = !MouseEvent.IsControlDown(); if (Hotspot->GetType() == ESequencerHotspot::Key) { FSequencerSelectedKey Key = static_cast(Hotspot.Get())->Key; if (bForceSelect || !Selection.IsSelected(Key)) { Selection.AddToSelection(Key); } else { Selection.RemoveFromSelection(Key); } } else if (Hotspot->GetType() == ESequencerHotspot::Section) { UMovieSceneSection* Section = static_cast(Hotspot.Get())->Section.GetSectionObject(); // Never allow infinite sections to be selected through normal click (they're only selectable through right click) if (!Section->IsInfinite()) { if (bForceSelect || !Selection.IsSelected(Section)) { Selection.AddToSelection(Section); } else { Selection.RemoveFromSelection(Section); } } } if (Hotspot->GetType() == ESequencerHotspot::Key) { UpdateHoveredNodeFromSelectedKeys(Sequencer); } else { UpdateHoveredNodeFromSelectedSections(Sequencer); } } TSharedPtr SequencerHelpers::SummonContextMenu(FSequencer& Sequencer, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) { // @todo sequencer replace with UI Commands instead of faking it // Attempt to paste into either the current node selection, or the clicked on track TSharedRef SequencerWidget = StaticCastSharedRef(Sequencer.GetSequencerWidget()); const float PasteAtTime = Sequencer.GetLocalTime(); const bool bShouldCloseWindowAfterMenuSelection = true; FMenuBuilder MenuBuilder(bShouldCloseWindowAfterMenuSelection, Sequencer.GetCommandBindings()); TSharedPtr Hotspot = Sequencer.GetHotspot(); if (Hotspot.IsValid()) { if (Hotspot->GetType() == ESequencerHotspot::Section || Hotspot->GetType() == ESequencerHotspot::Key) { Hotspot->PopulateContextMenu(MenuBuilder, Sequencer, PasteAtTime); return MenuBuilder.MakeWidget(); } } else if (Sequencer.GetClipboardStack().Num() != 0) { TSharedPtr PasteMenu = FPasteContextMenu::CreateMenu(Sequencer, SequencerWidget->GeneratePasteArgs(PasteAtTime)); if (PasteMenu.IsValid() && PasteMenu->IsValidPaste()) { PasteMenu->PopulateMenu(MenuBuilder); return MenuBuilder.MakeWidget(); } } return nullptr; }