// Copyright 1998-2014 Epic Games, Inc. All Rights Reserved. #include "SequencerPrivatePCH.h" #include "SAnimationOutlinerView.h" #include "ScopedTransaction.h" #include "Sequencer.h" #include "MovieScene.h" #define LOCTEXT_NAMESPACE "AnimationOutliner" void SAnimationOutlinerTreeNode::Construct( const FArguments& InArgs, TSharedRef Node ) { DisplayNode = Node; OnSelectionChanged = InArgs._OnSelectionChanged; SelectedBrush = FEditorStyle::GetBrush( "Sequencer.AnimationOutliner.SelectionBorder" ); NotSelectedBrush = FEditorStyle::GetBrush( "NoBorder" ); ExpandedBrush = FEditorStyle::GetBrush( "TreeArrow_Expanded" ); CollapsedBrush = FEditorStyle::GetBrush( "TreeArrow_Collapsed" ); // Choose the font. If the node is a root node, we show a larger font for it. FSlateFontInfo NodeFont = Node->GetParent().IsValid() ? FEditorStyle::GetFontStyle("Sequencer.AnimationOutliner.NormalFont") : FEditorStyle::GetFontStyle("Sequencer.AnimationOutliner.BoldFont"); TSharedRef TextWidget = SNew( STextBlock ) .Text( Node->GetDisplayName() ) .Font( NodeFont ); TSharedRef FinalWidget = SNew( SHorizontalBox ) + SHorizontalBox::Slot() .AutoWidth() .VAlign( VAlign_Center ) [ SNew(SButton) .Visibility( this, &SAnimationOutlinerTreeNode::GetExpanderVisibility ) .OnClicked( this, &SAnimationOutlinerTreeNode::OnExpanderClicked ) .ClickMethod( EButtonClickMethod::MouseDown ) .ButtonStyle( FEditorStyle::Get(), "NoBorder" ) .HAlign( HAlign_Center ) .VAlign( VAlign_Center ) [ SNew( SImage ) .Image( this, &SAnimationOutlinerTreeNode::OnGetExpanderImage) ] ] + SHorizontalBox::Slot() .AutoWidth() .VAlign( VAlign_Center ) [ TextWidget ]; ChildSlot [ SNew( SBorder ) .VAlign( VAlign_Center ) .Padding(1.0f) .BorderImage( this, &SAnimationOutlinerTreeNode::GetNodeBorderImage ) [ FinalWidget ] ]; SetVisibility( TAttribute( this, &SAnimationOutlinerTreeNode::GetNodeVisibility ) ); } FReply SAnimationOutlinerTreeNode::OnMouseButtonDown( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { if( MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && DisplayNode->IsSelectable() ) { bool bSelected = DisplayNode->IsSelected(); if( MouseEvent.IsControlDown() ) { const bool bDeselectOtherNodes = false; // Select the node if we were clicked on DisplayNode->SetSelectionState( !bSelected, bDeselectOtherNodes ); } else { const bool bDeselectOtherNodes = true; // Select the node if we were clicked on DisplayNode->SetSelectionState( true, bDeselectOtherNodes ); } OnSelectionChanged.ExecuteIfBound( DisplayNode ); return FReply::Handled(); } else if( MouseEvent.GetEffectingButton() == EKeys::RightMouseButton ) { return FReply::Handled().CaptureMouse(SharedThis(this)); } return FReply::Unhandled(); } FReply SAnimationOutlinerTreeNode::OnMouseButtonUp( const FGeometry& MyGeometry, const FPointerEvent& MouseEvent ) { if( MouseEvent.GetEffectingButton() == EKeys::RightMouseButton && HasMouseCapture() ) { TSharedPtr MenuContent = DisplayNode->OnSummonContextMenu(MyGeometry, MouseEvent); if (MenuContent.IsValid()) { FSlateApplication::Get().PushMenu( AsShared(), MenuContent.ToSharedRef(), MouseEvent.GetScreenSpacePosition(), FPopupTransitionEffect( FPopupTransitionEffect::ContextMenu ) ); return FReply::Handled().ReleaseMouseCapture().SetKeyboardFocus( MenuContent.ToSharedRef(), EKeyboardFocusCause::SetDirectly ); } return FReply::Handled().ReleaseMouseCapture(); } return FReply::Unhandled(); } FReply SAnimationOutlinerTreeNode::OnExpanderClicked() { DisplayNode->ToggleExpansion(); return FReply::Handled(); } const FSlateBrush* SAnimationOutlinerTreeNode::GetNodeBorderImage() const { // Display a highlight when the node is selected const bool bIsSelected = DisplayNode->IsSelected(); return bIsSelected ? SelectedBrush : NotSelectedBrush; } const FSlateBrush* SAnimationOutlinerTreeNode::OnGetExpanderImage() const { const bool bIsExpanded = DisplayNode->IsExpanded(); return bIsExpanded ? ExpandedBrush : CollapsedBrush; } EVisibility SAnimationOutlinerTreeNode::GetNodeVisibility() const { return DisplayNode->IsVisible() ? EVisibility::Visible : EVisibility::Collapsed; } EVisibility SAnimationOutlinerTreeNode::GetExpanderVisibility() const { return DisplayNode->GetNumChildren() > 0 ? EVisibility::Visible : EVisibility::Hidden; } void SAnimationOutlinerView::Construct( const FArguments& InArgs, TSharedRef InRootNode, TSharedRef InSequencer ) { SAnimationOutlinerViewBase::Construct( SAnimationOutlinerViewBase::FArguments(), InRootNode ); Sequencer = InSequencer; GenerateWidgetForNode( InRootNode ); SetVisibility( TAttribute( this, &SAnimationOutlinerView::GetNodeVisibility ) ); } SAnimationOutlinerView::~SAnimationOutlinerView() { } EVisibility SAnimationOutlinerView::GetNodeVisibility() const { return RootNode->IsVisible() ? EVisibility::Visible : EVisibility::Collapsed; } void SAnimationOutlinerView::GenerateWidgetForNode( TSharedRef& InLayoutNode ) { Children.Add( SNew( SAnimationOutlinerTreeNode, InLayoutNode) .OnSelectionChanged( this, &SAnimationOutlinerView::OnSelectionChanged ) ); // Object nodes do not generate widgets for their children if( InLayoutNode->GetType() != ESequencerNode::Object ) { const TArray< TSharedRef >& ChildNodes = InLayoutNode->GetChildNodes(); for( int32 ChildIndex = 0; ChildIndex < ChildNodes.Num(); ++ChildIndex ) { TSharedRef ChildNode = ChildNodes[ChildIndex]; GenerateWidgetForNode( ChildNode ); } } } void SAnimationOutlinerView::OnArrangeChildren( const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const { const float Padding = SequencerLayoutConstants::NodePadding; const float IndentAmount = SequencerLayoutConstants::IndentAmount; const float Scale = 1.0f; float CurrentHeight = 0; for (int32 WidgetIndex = 0; WidgetIndex < Children.Num(); ++WidgetIndex) { const TSharedRef& Widget = Children[WidgetIndex]; EVisibility Visibility = Widget->GetVisibility(); if( ArrangedChildren.Accepts( Visibility ) ) { const TSharedPtr& DisplayNode = Widget->GetDisplayNode(); // How large to make this node float HeightIncrement = DisplayNode->GetNodeHeight(); // How far to indent the widget float WidgetIndentOffset = IndentAmount*DisplayNode->GetTreeLevel(); // Place the widget at the current height, at the nodes desired size ArrangedChildren.AddWidget( Visibility, AllottedGeometry.MakeChild( Widget, FVector2D( WidgetIndentOffset, CurrentHeight ), FVector2D( AllottedGeometry.GetDrawSize().X-WidgetIndentOffset, HeightIncrement ), Scale ) ); // Compute the start height for the next widget CurrentHeight += HeightIncrement+Padding; } } } void SAnimationOutlinerView::OnSelectionChanged( TSharedPtr AffectedNode ) { // Select objects bound to the object node if( AffectedNode->GetType() == ESequencerNode::Object ) { const TSharedPtr ObjectNode = StaticCastSharedPtr( AffectedNode ); // Get the bound objects TArray RuntimeObjects; Sequencer.Pin()->GetRuntimeObjects( Sequencer.Pin()->GetFocusedMovieSceneInstance(), ObjectNode->GetObjectBinding(), RuntimeObjects ); if( RuntimeObjects.Num() > 0 ) { const bool bNotifySelectionChanged = false; const bool bDeselectBSP = true; const bool bWarnAboutTooManyActors = false; // Clear selection GEditor->SelectNone(bNotifySelectionChanged,bDeselectBSP,bWarnAboutTooManyActors); GEditor->GetSelectedActors()->BeginBatchSelectOperation(); // Select each actor bool bActorSelected = false; for( int32 ObjectIndex = 0; ObjectIndex < RuntimeObjects.Num(); ++ObjectIndex ) { AActor* Actor = Cast( RuntimeObjects[ObjectIndex] ); if( Actor ) { const bool bSelectActor = true; GEditor->SelectActor(Actor, bSelectActor, bNotifySelectionChanged ); bActorSelected = true; } } GEditor->GetSelectedActors()->EndBatchSelectOperation(); if( bActorSelected ) { GEditor->NoteSelectionChange(); } } } } #undef LOCTEXT_NAMESPACE