Files
UnrealEngineUWP/Engine/Source/Editor/Sequencer/Private/SAnimationOutlinerTreeNode.cpp
lauren barnes d5b97bd584 Selected sequencer track color is now the style's selection color instead of the previous orange selection color, the text has been adjusted to UseForeground rather than pure white so it is more readable when selected.
#rb Max.Chen
#jira none
#preflight 6065d5323a79480001343828

#ROBOMERGE-SOURCE: CL 15889489 in //UE5/Release-5.0-EarlyAccess/...
#ROBOMERGE-BOT: STARSHIP (Release-5.0-EarlyAccess -> Main) (v786-15839533)

[CL 15890452 by lauren barnes in ue5-main branch]
2021-04-01 12:15:51 -04:00

466 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SAnimationOutlinerTreeNode.h"
#include "Fonts/SlateFontInfo.h"
#include "Types/SlateStructs.h"
#include "Widgets/SBoxPanel.h"
#include "Widgets/SOverlay.h"
#include "Styling/SlateTypes.h"
#include "Widgets/Layout/SBorder.h"
#include "Widgets/Layout/SSpacer.h"
#include "Widgets/Images/SImage.h"
#include "Widgets/Layout/SBox.h"
#include "Widgets/Input/SButton.h"
#include "Widgets/Views/SExpanderArrow.h"
#include "EditorStyleSet.h"
#include "DisplayNodes/SequencerTrackNode.h"
#include "SSequencer.h"
#include "ScopedTransaction.h"
#include "SSequencerTreeView.h"
#include "Widgets/Colors/SColorPicker.h"
#include "SequencerSectionPainter.h"
#include "ObjectBindingTagCache.h"
#include "Widgets/Text/SInlineEditableTextBlock.h"
#include "Framework/SlateDelegates.h"
#include "MovieSceneTrack.h"
#include "Styling/StyleColors.h"
#define LOCTEXT_NAMESPACE "AnimationOutliner"
SAnimationOutlinerTreeNode::~SAnimationOutlinerTreeNode()
{
DisplayNode->OnRenameRequested().RemoveAll(this);
}
void SAnimationOutlinerTreeNode::Construct( const FArguments& InArgs, TSharedRef<FSequencerDisplayNode> Node, const TSharedRef<SSequencerTreeViewRow>& InTableRow )
{
DisplayNode = Node;
bIsOuterTopLevelNode = !Node->GetParent().IsValid();
bIsInnerTopLevelNode = Node->GetType() != ESequencerNode::Folder && Node->GetParent().IsValid() && Node->GetParent()->GetType() == ESequencerNode::Folder;
if (bIsOuterTopLevelNode)
{
ExpandedBackgroundBrush = FEditorStyle::GetBrush( "Sequencer.AnimationOutliner.TopLevelBorder_Expanded" );
CollapsedBackgroundBrush = FEditorStyle::GetBrush( "Sequencer.AnimationOutliner.TopLevelBorder_Collapsed" );
}
else
{
ExpandedBackgroundBrush = FEditorStyle::GetBrush( "Sequencer.AnimationOutliner.DefaultBorder" );
CollapsedBackgroundBrush = FEditorStyle::GetBrush( "Sequencer.AnimationOutliner.DefaultBorder" );
}
FMargin InnerNodePadding;
if ( bIsInnerTopLevelNode )
{
InnerBackgroundBrush = FEditorStyle::GetBrush( "Sequencer.AnimationOutliner.TopLevelBorder_Expanded" );
InnerNodePadding = FMargin(0.f, 1.f);
}
else
{
InnerBackgroundBrush = FEditorStyle::GetBrush( "Sequencer.AnimationOutliner.TransparentBorder" );
InnerNodePadding = FMargin(0.f);
}
TableRowStyle = &FEditorStyle::Get().GetWidgetStyle<FTableRowStyle>("TableView.Row");
EditableLabel = SNew(SInlineEditableTextBlock)
.IsReadOnly(this, &SAnimationOutlinerTreeNode::IsNodeLabelReadOnly)
.Font(this, &SAnimationOutlinerTreeNode::GetDisplayNameFont)
.ColorAndOpacity(this, &SAnimationOutlinerTreeNode::GetDisplayNameColor)
.OnVerifyTextChanged(this, &SAnimationOutlinerTreeNode::VerifyNodeTextChanged)
.OnTextCommitted(this, &SAnimationOutlinerTreeNode::HandleNodeLabelTextCommitted)
.Text(this, &SAnimationOutlinerTreeNode::GetDisplayName)
.ToolTipText(this, &SAnimationOutlinerTreeNode::GetDisplayNameToolTipText)
.Clipping(EWidgetClipping::ClipToBounds)
.IsSelected(FIsSelected::CreateSP(InTableRow, &SSequencerTreeViewRow::IsSelectedExclusively));
TSharedRef<SWidget> LabelContent = EditableLabel.ToSharedRef();
if (TSharedPtr<SWidget> AdditionalLabelContent = Node->GetAdditionalOutlinerLabel())
{
LabelContent = SNew(SHorizontalBox)
+ SHorizontalBox::Slot()
.AutoWidth()
.Padding(FMargin(0.f, 0.f, 5.f, 0.f))
[
LabelContent
]
+ SHorizontalBox::Slot()
[
AdditionalLabelContent.ToSharedRef()
];
LabelContent->SetClipping(EWidgetClipping::ClipToBounds);
}
Node->OnRenameRequested().AddRaw(this, &SAnimationOutlinerTreeNode::EnterRenameMode);
auto NodeHeight = [=]() -> FOptionalSize { return DisplayNode->GetNodeHeight(); };
SetForegroundColor(MakeAttributeSP(this, &SAnimationOutlinerTreeNode::GetForegroundBasedOnSelection));
TSharedRef<SWidget> FinalWidget =
SNew( SBorder )
.VAlign( VAlign_Center )
.BorderImage( this, &SAnimationOutlinerTreeNode::GetNodeBorderImage )
.BorderBackgroundColor( this, &SAnimationOutlinerTreeNode::GetNodeBackgroundTint )
.Padding(FMargin(0, Node->GetNodePadding().Combined() / 2))
[
SNew( SHorizontalBox )
+ SHorizontalBox::Slot()
[
SNew(SBox)
.HeightOverride_Lambda(NodeHeight)
.Padding(FMargin(5.0f, 0.0f))
[
SNew( SHorizontalBox )
// Expand track lanes button
+ SHorizontalBox::Slot()
.Padding(FMargin(2.f, 0.f, 2.f, 0.f))
.VAlign( VAlign_Center )
.AutoWidth()
[
SNew(SExpanderArrow, InTableRow).IndentAmount(SequencerLayoutConstants::IndentAmount)
]
+ SHorizontalBox::Slot()
.Padding( InnerNodePadding )
[
SNew( SBorder )
.BorderImage( FEditorStyle::GetBrush( "Sequencer.AnimationOutliner.TopLevelBorder_Collapsed" ) )
.BorderBackgroundColor( this, &SAnimationOutlinerTreeNode::GetNodeInnerBackgroundTint )
.Padding( FMargin(0) )
[
SNew( SHorizontalBox )
// Icon
+ SHorizontalBox::Slot()
.Padding(FMargin(0.f, 0.f, 4.f, 0.f))
.VAlign(VAlign_Center)
.AutoWidth()
[
SNew(SOverlay)
+ SOverlay::Slot()
[
SNew(SImage)
.Image(InArgs._IconBrush)
.ColorAndOpacity(InArgs._IconColor)
]
+ SOverlay::Slot()
.VAlign(VAlign_Top)
.HAlign(HAlign_Right)
[
SNew(SImage)
.Image(InArgs._IconOverlayBrush)
]
+ SOverlay::Slot()
[
SNew(SSpacer)
.Visibility(EVisibility::Visible)
.ToolTipText(InArgs._IconToolTipText)
]
]
// Label Slot
+ SHorizontalBox::Slot()
.VAlign(VAlign_Center)
.Padding(FMargin(0.f, 0.f, 4.f, 0.f))
[
LabelContent
]
// Arbitrary customization slot
+ SHorizontalBox::Slot()
.AutoWidth()
[
InArgs._CustomContent.Widget
]
]
]
]
]
+ SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SButton)
.ContentPadding(0)
.VAlign(VAlign_Fill)
.IsFocusable(false) // Intentionally false so that it's easier to tab to the next numeric input
.IsEnabled(!DisplayNode->GetSequencer().IsReadOnly())
.ButtonStyle(FEditorStyle::Get(), "Sequencer.AnimationOutliner.ColorStrip")
.OnClicked(this, &SAnimationOutlinerTreeNode::OnSetTrackColor)
.Content()
[
SNew(SBox)
.WidthOverride(6.f)
[
SNew(SImage)
.Image(FEditorStyle::GetBrush("WhiteBrush"))
.ColorAndOpacity(this, &SAnimationOutlinerTreeNode::GetTrackColorTint)
]
]
]
];
ChildSlot
[
FinalWidget
];
}
// We store these for when the Color Picker is canceled so we can restore the old value.
namespace AnimationOutlinerTreeNode
{
FLinearColor InitialTrackColor;
bool bFolderPickerWasCancelled;
}
FReply SAnimationOutlinerTreeNode::OnSetTrackColor()
{
AnimationOutlinerTreeNode::InitialTrackColor = GetTrackColorTint().GetSpecifiedColor();
AnimationOutlinerTreeNode::bFolderPickerWasCancelled = false;
FColorPickerArgs PickerArgs;
PickerArgs.bUseAlpha = false;
PickerArgs.DisplayGamma = TAttribute<float>::Create(TAttribute<float>::FGetter::CreateUObject(GEngine, &UEngine::GetDisplayGamma));
PickerArgs.InitialColorOverride = AnimationOutlinerTreeNode::InitialTrackColor;
PickerArgs.ParentWidget = GetParentWidget();
PickerArgs.OnColorCommitted = FOnLinearColorValueChanged::CreateSP(this, &SAnimationOutlinerTreeNode::OnColorPickerPicked);
PickerArgs.OnColorPickerWindowClosed = FOnWindowClosed::CreateSP(this, &SAnimationOutlinerTreeNode::OnColorPickerClosed);
PickerArgs.OnColorPickerCancelled = FOnColorPickerCancelled::CreateSP(this, &SAnimationOutlinerTreeNode::OnColorPickerCancelled);
OpenColorPicker(PickerArgs);
return FReply::Handled();
}
void SAnimationOutlinerTreeNode::OnColorPickerPicked(FLinearColor NewFolderColor)
{
UMovieSceneTrack* Track = GetTrackFromNode();
if (Track)
{
// This is called every time the user adjusts the UI so we don't want to create a transaction for it, just directly
// modify the track so we can see the change immediately.
Track->SetColorTint(NewFolderColor.ToFColor(true));
}
}
void SAnimationOutlinerTreeNode::OnColorPickerClosed(const TSharedRef<SWindow>& Window)
{
// Under Unreal UX terms, closing the Color Picker (via the UI) is the same as confirming it since we've been live updating
// the color. The track already has the latest color change so we undo the change before calling Modify so that Undo sets us
// to the original color. This is also called in the event of pressing cancel so we need to detect if it was canceled or not.
if (!AnimationOutlinerTreeNode::bFolderPickerWasCancelled)
{
UMovieSceneTrack* Track = GetTrackFromNode();
if(Track)
{
const FScopedTransaction Transaction(LOCTEXT("SetTrackColor", "Set Track Color"));
FSlateColor CurrentColor = GetTrackColorTint();
Track->SetColorTint(AnimationOutlinerTreeNode::InitialTrackColor.ToFColor(true));
Track->Modify();
Track->SetColorTint(CurrentColor.GetSpecifiedColor().ToFColor(true));
}
}
}
void SAnimationOutlinerTreeNode::OnColorPickerCancelled(FLinearColor NewFolderColor)
{
AnimationOutlinerTreeNode::bFolderPickerWasCancelled = true;
// Restore the original color of the track. No transaction will be created when the OnColorPickerClosed callback is called.
UMovieSceneTrack* Track = GetTrackFromNode();
if (Track)
{
Track->SetColorTint(AnimationOutlinerTreeNode::InitialTrackColor.ToFColor(true));
}
}
void SAnimationOutlinerTreeNode::EnterRenameMode()
{
EditableLabel->EnterEditingMode();
}
void SAnimationOutlinerTreeNode::GetAllDescendantNodes(TSharedPtr<FSequencerDisplayNode> RootNode, TArray<TSharedRef<FSequencerDisplayNode> >& AllNodes)
{
if (!RootNode.IsValid())
{
return;
}
AllNodes.Add(RootNode.ToSharedRef());
const FSequencerDisplayNode* RootNodeC = RootNode.Get();
for (TSharedRef<FSequencerDisplayNode> ChildNode : RootNodeC->GetChildNodes())
{
AllNodes.Add(ChildNode);
GetAllDescendantNodes(ChildNode, AllNodes);
}
}
void SAnimationOutlinerTreeNode::OnMouseEnter(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
DisplayNode->GetParentTree().SetHoveredNode(DisplayNode);
SWidget::OnMouseEnter(MyGeometry, MouseEvent);
}
void SAnimationOutlinerTreeNode::OnMouseLeave(const FPointerEvent& MouseEvent)
{
DisplayNode->GetParentTree().SetHoveredNode(nullptr);
SWidget::OnMouseLeave(MouseEvent);
}
const FSlateBrush* SAnimationOutlinerTreeNode::GetNodeBorderImage() const
{
return DisplayNode->IsExpanded() ? ExpandedBackgroundBrush : CollapsedBackgroundBrush;
}
FSlateColor SAnimationOutlinerTreeNode::GetNodeBackgroundTint() const
{
FSequencer& Sequencer = DisplayNode->GetSequencer();
const bool bIsSelected = Sequencer.GetSelection().IsSelected(DisplayNode.ToSharedRef());
if (bIsSelected)
{
return FStyleColors::Select;
}
else if (Sequencer.GetSelection().NodeHasSelectedKeysOrSections(DisplayNode.ToSharedRef()))
{
return FStyleColors::Header;
}
else if (DisplayNode->IsHovered())
{
return bIsOuterTopLevelNode ? FLinearColor(FColor(52, 52, 52, 255)) : FLinearColor(FColor(72, 72, 72, 255));
}
else
{
return bIsOuterTopLevelNode ? FLinearColor(FColor(48, 48, 48, 255)) : FLinearColor(FColor(62, 62, 62, 255));
}
}
FSlateColor SAnimationOutlinerTreeNode::GetNodeInnerBackgroundTint() const
{
if ( bIsInnerTopLevelNode )
{
FSequencer& Sequencer = DisplayNode->GetSequencer();
const bool bIsSelected = Sequencer.GetSelection().IsSelected( DisplayNode.ToSharedRef() );
if ( bIsSelected )
{
return FStyleColors::Select;
}
else if ( Sequencer.GetSelection().NodeHasSelectedKeysOrSections( DisplayNode.ToSharedRef() ) )
{
return FStyleColors::Header;
}
else if ( DisplayNode->IsHovered() )
{
return FLinearColor( FColor( 52, 52, 52, 255 ) );
}
else
{
return FLinearColor( FColor( 48, 48, 48, 255 ) );
}
}
else
{
return FLinearColor( 0.f, 0.f, 0.f, 0.f );
}
}
UMovieSceneTrack* SAnimationOutlinerTreeNode::GetTrackFromNode() const
{
UMovieSceneTrack* Track = nullptr;
FSequencerDisplayNode* Current = DisplayNode.Get();
while (Current && Current->GetType() != ESequencerNode::Object)
{
if (Current->GetType() == ESequencerNode::Track)
{
Track = static_cast<FSequencerTrackNode*>(Current)->GetTrack();
if (Track)
{
break;
}
}
Current = Current->GetParent().Get();
}
return Track;
}
FSlateColor SAnimationOutlinerTreeNode::GetTrackColorTint() const
{
UMovieSceneTrack* Track = GetTrackFromNode();
if (Track)
{
return FSequencerSectionPainter::BlendColor(Track->GetColorTint());
}
return FLinearColor::Transparent;
}
FSlateColor SAnimationOutlinerTreeNode::GetForegroundBasedOnSelection() const
{
FSequencer& Sequencer = DisplayNode->GetSequencer();
const bool bIsSelected = Sequencer.GetSelection().IsSelected(DisplayNode.ToSharedRef());
return bIsSelected ? TableRowStyle->SelectedTextColor : TableRowStyle->TextColor;
}
EVisibility SAnimationOutlinerTreeNode::GetExpanderVisibility() const
{
return DisplayNode->GetNumChildren() > 0 ? EVisibility::Visible : EVisibility::Hidden;
}
FSlateColor SAnimationOutlinerTreeNode::GetDisplayNameColor() const
{
return DisplayNode->GetDisplayNameColor();
}
FSlateFontInfo SAnimationOutlinerTreeNode::GetDisplayNameFont() const
{
return DisplayNode->GetDisplayNameFont();
}
FText SAnimationOutlinerTreeNode::GetDisplayNameToolTipText() const
{
return DisplayNode->GetDisplayNameToolTipText();
}
FText SAnimationOutlinerTreeNode::GetDisplayName() const
{
return DisplayNode->GetDisplayName();
}
bool SAnimationOutlinerTreeNode::IsNodeLabelReadOnly() const
{
return DisplayNode->GetSequencer().IsReadOnly() || !DisplayNode->CanRenameNode();
}
bool SAnimationOutlinerTreeNode::VerifyNodeTextChanged(const FText& NewLabel, FText& OutErrorMessage)
{
return DisplayNode->ValidateDisplayName(NewLabel, OutErrorMessage);
}
void SAnimationOutlinerTreeNode::HandleNodeLabelTextCommitted(const FText& NewLabel, ETextCommit::Type CommitType)
{
DisplayNode->SetDisplayName(NewLabel);
}
#undef LOCTEXT_NAMESPACE