Procházet zdrojové kódy

增加ViewTree的EditorStyle

maboren před 2 měsíci
rodič
revize
f8c57bc22f

+ 470 - 0
Ability/Plugins/EzAbility/Source/EzAbilityEditor/Private/AbleTreeEditorStyle.cpp

@@ -0,0 +1,470 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "AbleTreeEditorStyle.h"
+#include "Brushes/SlateBoxBrush.h"
+#include "Styling/SlateStyleRegistry.h"
+#include "Brushes/SlateImageBrush.h"
+#include "Styling/CoreStyle.h"
+#include "Brushes/SlateRoundedBoxBrush.h"
+#include "Styling/SlateTypes.h"
+#include "Misc/Paths.h"
+#include "Styling/StyleColors.h"
+//#include "StateTreeTypes.h"
+#include "Styling/SlateStyleMacros.h"
+
+
+namespace UE::AbleTree::Editor
+{
+
+class FAbleContentRootScope
+{
+public:
+	FAbleContentRootScope(FAbleTreeEditorStyle* InStyle, const FString& NewContentRoot)
+		: Style(InStyle)
+		, PreviousContentRoot(InStyle->GetContentRootDir())
+	{
+		Style->SetContentRoot(NewContentRoot);
+	}
+
+	~FAbleContentRootScope()
+	{
+		Style->SetContentRoot(PreviousContentRoot);
+	}
+private:
+	FAbleTreeEditorStyle* Style;
+	FString PreviousContentRoot;
+};
+
+}; 
+
+FAbleTreeEditorStyle::FAbleTreeEditorStyle()
+	: FSlateStyleSet(TEXT("AbleTreeEditorStyle"))
+{
+	const FString EngineSlateContentDir = FPaths::EngineContentDir() / TEXT("Slate");
+	const FString EngineEditorSlateContentDir = FPaths::EngineContentDir() / TEXT("Editor/Slate");
+	SetCoreContentRoot(EngineSlateContentDir);
+
+	const FString StateTreePluginContentDir = FPaths::EnginePluginsDir() / TEXT("Runtime/StateTree/Resources");
+	SetContentRoot(StateTreePluginContentDir);
+
+	const FScrollBarStyle ScrollBar = FAppStyle::GetWidgetStyle<FScrollBarStyle>("ScrollBar");
+	const FTextBlockStyle& NormalText = FAppStyle::Get().GetWidgetStyle<FTextBlockStyle>("NormalText");
+
+	// State
+	{
+		const FTextBlockStyle StateIcon = FTextBlockStyle(NormalText)
+			.SetFont(FAppStyle::Get().GetFontStyle("FontAwesome.12"))
+			.SetColorAndOpacity(FLinearColor(230.0f / 255.0f, 230.0f / 255.0f, 230.0f / 255.0f, 0.5f));
+		Set("StateTree.Icon", StateIcon);
+
+		const FTextBlockStyle StateTitle = FTextBlockStyle(NormalText)
+			.SetFont(DEFAULT_FONT("Bold", 12))
+			.SetColorAndOpacity(FLinearColor(230.0f / 255.0f, 230.0f / 255.0f, 230.0f / 255.0f, 0.9f));
+		Set("StateTree.State.Title", StateTitle);
+
+		const FEditableTextBoxStyle StateTitleEditableText = FEditableTextBoxStyle()
+			.SetTextStyle(NormalText)
+			.SetFont(DEFAULT_FONT("Bold", 12))
+			.SetBackgroundImageNormal(CORE_BOX_BRUSH("Common/TextBox", FMargin(4.0f / 16.0f)))
+			.SetBackgroundImageHovered(CORE_BOX_BRUSH("Common/TextBox_Hovered", FMargin(4.0f / 16.0f)))
+			.SetBackgroundImageFocused(CORE_BOX_BRUSH("Common/TextBox_Hovered", FMargin(4.0f / 16.0f)))
+			.SetBackgroundImageReadOnly(CORE_BOX_BRUSH("Common/TextBox_ReadOnly", FMargin(4.0f / 16.0f)))
+			.SetBackgroundColor(FLinearColor(0,0,0,0.1f))
+			.SetPadding(FMargin(0))
+			.SetScrollBarStyle(ScrollBar);
+		Set("StateTree.State.TitleEditableText", StateTitleEditableText);
+
+		Set("StateTree.State.TitleInlineEditableText", FInlineEditableTextBlockStyle()
+			.SetTextStyle(StateTitle)
+			.SetEditableTextBoxStyle(StateTitleEditableText));
+
+		Set("StateTree.State.Border", new FSlateBorderBrush(NAME_None, FMargin(2.0f)));
+
+		Set("StateTree.State", new FSlateRoundedBoxBrush(FLinearColor::White, 2.0f));
+	}
+
+	// Details
+	{
+		const FTextBlockStyle Details = FTextBlockStyle(NormalText)
+			.SetFont(DEFAULT_FONT("Regular", 10))
+			.SetColorAndOpacity(FLinearColor(230.0f / 255.0f, 230.0f / 255.0f, 230.0f / 255.0f, 0.75f));
+		Set("StateTree.Details", Details);
+
+		Set("StateTree.Node.Label", new FSlateRoundedBoxBrush(FStyleColors::AccentGray, 6.f));
+
+		// For multi selection with mixed values for a given property
+		const FLinearColor Color = FStyleColors::Hover.GetSpecifiedColor();
+		const FLinearColor HollowColor = Color.CopyWithNewOpacity(0.0);
+		Set("StateTree.Node.Label.Mixed", new FSlateRoundedBoxBrush(HollowColor, 6.0f, Color, 1.0f));
+
+		const FTextBlockStyle DetailsCategory = FTextBlockStyle(NormalText)
+			.SetFont(DEFAULT_FONT("Bold", 8));
+		Set("StateTree.Category", DetailsCategory);
+	}
+
+	// Task
+	{
+		const FLinearColor ForegroundCol =  FStyleColors::Foreground.GetSpecifiedColor();
+
+		Set("StateTree.Task.Title", FTextBlockStyle(NormalText)
+			.SetFont(DEFAULT_FONT("Regular", 10))
+			.SetColorAndOpacity(ForegroundCol.CopyWithNewOpacity(0.8f)));
+
+		Set("StateTree.Task.Title.Bold", FTextBlockStyle(NormalText)
+			.SetFont(DEFAULT_FONT("Bold", 10))
+			.SetColorAndOpacity(ForegroundCol.CopyWithNewOpacity(0.8f)));
+
+		Set("StateTree.Task.Title.Subdued", FTextBlockStyle(NormalText)
+			.SetFont(DEFAULT_FONT("Regular", 10))
+			.SetColorAndOpacity(ForegroundCol.CopyWithNewOpacity(0.4f)));
+
+		// Tasks to be show up a bit darker than the state
+		Set("StateTree.Task.Rect", new FSlateColorBrush(FLinearColor(FVector3f(0.67f))));
+	}
+
+	// Details rich text
+	{
+		Set("Details.Normal", FTextBlockStyle(NormalText)
+			.SetFont(FAppStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"))));
+
+		Set("Details.Bold", FTextBlockStyle(NormalText)
+			.SetFont(FAppStyle::GetFontStyle(TEXT("PropertyWindow.BoldFont"))));
+
+		Set("Details.Italic", FTextBlockStyle(NormalText)
+			.SetFont(FAppStyle::GetFontStyle(TEXT("PropertyWindow.ItalicFont"))));
+
+		Set("Details.Subdued", FTextBlockStyle(NormalText)
+			.SetColorAndOpacity(FSlateColor::UseSubduedForeground())
+			.SetFont(FAppStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"))));
+	}
+
+	// Normal rich text
+	{
+		Set("Normal.Normal", FTextBlockStyle(NormalText)
+			.SetColorAndOpacity(FSlateColor::UseForeground())
+			.SetFont(DEFAULT_FONT("Regular", 10)));
+
+		Set("Normal.Bold", FTextBlockStyle(NormalText)
+			.SetColorAndOpacity(FSlateColor::UseForeground())
+			.SetFont(DEFAULT_FONT("Bold", 10)));
+
+		Set("Normal.Italic", FTextBlockStyle(NormalText)
+			.SetColorAndOpacity(FSlateColor::UseForeground())
+			.SetFont(DEFAULT_FONT("Italic", 10)));
+
+		Set("Normal.Subdued", FTextBlockStyle(NormalText)
+			.SetColorAndOpacity(FSlateColor::UseSubduedForeground())
+			.SetFont(DEFAULT_FONT("Regular", 10)));
+	}
+
+	// Transition rich text
+	{
+		const FLinearColor ForegroundCol =  FStyleColors::White.GetSpecifiedColor();
+		Set("Transition.Normal", FTextBlockStyle(NormalText)
+			.SetColorAndOpacity(ForegroundCol.CopyWithNewOpacity(0.9f))
+			.SetFont(DEFAULT_FONT("Regular", 11)));
+
+		Set("Transition.Bold", FTextBlockStyle(NormalText)
+			.SetColorAndOpacity(ForegroundCol.CopyWithNewOpacity(0.9f))
+			.SetFont(DEFAULT_FONT("Bold", 11)));
+
+		Set("Transition.Italic", FTextBlockStyle(NormalText)
+			.SetColorAndOpacity(ForegroundCol.CopyWithNewOpacity(0.9f))
+			.SetFont(DEFAULT_FONT("Italic", 11)));
+
+		Set("Transition.Subdued", FTextBlockStyle(NormalText)
+			.SetColorAndOpacity(ForegroundCol.CopyWithNewOpacity(0.5f))
+			.SetFont(DEFAULT_FONT("Regular", 11)));
+	}
+
+	// Diff tool
+	{
+		Set("DiffTools.Added", FLinearColor(0.3f, 1.f, 0.3f)); // green
+		Set("DiffTools.Removed", FLinearColor(1.0f, 0.2f, 0.3f)); // red
+		Set("DiffTools.Changed", FLinearColor(0.85f, 0.71f, 0.25f)); // yellow
+		Set("DiffTools.Moved", FLinearColor(0.5f, 0.8f, 1.f)); // light blue
+		Set("DiffTools.Enabled", FLinearColor(0.7f, 1.f, 0.7f)); // light green
+		Set("DiffTools.Disabled", FLinearColor(1.0f, 0.6f, 0.5f)); // light red
+		Set("DiffTools.Properties", FLinearColor(0.2f, 0.4f, 1.f)); // blue
+	}
+
+	// Debugger
+	{
+		Set("StateTreeDebugger.Element.Normal",
+			FTextBlockStyle(NormalText)
+			.SetFont(DEFAULT_FONT("Regular", 10)));
+
+		Set("StateTreeDebugger.Element.Bold",
+			FTextBlockStyle(NormalText)
+			.SetFont(DEFAULT_FONT("Bold", 10)));
+
+		Set("StateTreeDebugger.Element.Subdued",
+			FTextBlockStyle(NormalText)
+			.SetFont(DEFAULT_FONT("Regular", 10))
+			.SetColorAndOpacity(FSlateColor::UseSubduedForeground()));
+	}
+	
+	const FLinearColor SelectionColor = FColor(0, 0, 0, 32);
+	const FTableRowStyle& NormalTableRowStyle = FAppStyle::Get().GetWidgetStyle<FTableRowStyle>("TableView.Row");
+	Set("StateTree.Selection",
+		FTableRowStyle(NormalTableRowStyle)
+		.SetActiveBrush(CORE_IMAGE_BRUSH("Common/Selection", CoreStyleConstants::Icon8x8, SelectionColor))
+		.SetActiveHoveredBrush(CORE_IMAGE_BRUSH("Common/Selection", CoreStyleConstants::Icon8x8, SelectionColor))
+		.SetInactiveBrush(CORE_IMAGE_BRUSH("Common/Selection", CoreStyleConstants::Icon8x8, SelectionColor))
+		.SetInactiveHoveredBrush(CORE_IMAGE_BRUSH("Common/Selection", CoreStyleConstants::Icon8x8, SelectionColor))
+		.SetSelectorFocusedBrush(CORE_IMAGE_BRUSH("Common/Selection", CoreStyleConstants::Icon8x8, SelectionColor))
+	);
+
+	const FComboButtonStyle& ComboButtonStyle = FCoreStyle::Get().GetWidgetStyle<FComboButtonStyle>("ComboButton");
+
+	// Expression Operand combo button
+	const FButtonStyle OperandButton = FButtonStyle()
+		.SetNormal(FSlateRoundedBoxBrush(FStyleColors::AccentGreen.GetSpecifiedColor().Desaturate(0.3f), 4.0f))
+		.SetHovered(FSlateRoundedBoxBrush(FStyleColors::AccentGreen.GetSpecifiedColor().Desaturate(0.2f), 4.0f))
+		.SetPressed(FSlateRoundedBoxBrush(FStyleColors::AccentGreen.GetSpecifiedColor().Desaturate(0.1f), 4.0f))
+		.SetNormalForeground(FStyleColors::Foreground)
+		.SetHoveredForeground(FStyleColors::ForegroundHover)
+		.SetPressedForeground(FStyleColors::ForegroundHover)
+		.SetDisabledForeground(FStyleColors::ForegroundHover)
+		.SetNormalPadding(FMargin(2, 2, 2, 2))
+		.SetPressedPadding(FMargin(2, 3, 2, 1));
+
+	Set("StateTree.Node.Operand.ComboBox", FComboButtonStyle(ComboButtonStyle).SetButtonStyle(OperandButton));
+
+	Set("StateTree.Node.Operand", FTextBlockStyle(NormalText)
+		.SetFont(FAppStyle::GetFontStyle(TEXT("PropertyWindow.BoldFont")))
+		.SetFontSize(8));
+
+	Set("StateTree.Node.Parens", FTextBlockStyle(NormalText)
+		.SetFont(FAppStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont")))
+		.SetFontSize(12));
+
+	// Parameter labels
+	Set("StateTree.Param.Label", FTextBlockStyle(NormalText)
+		.SetFont(FAppStyle::GetFontStyle(TEXT("PropertyWindow.BoldFont")))
+		.SetFontSize(7));
+
+	Set("StateTree.Param.Background", new FSlateRoundedBoxBrush(FStyleColors::Hover, 6.f));
+	
+	// Expression Indent combo button
+	const FButtonStyle IndentButton = FButtonStyle()
+		.SetNormal(FSlateRoundedBoxBrush(FLinearColor::Transparent, 2.0f))
+		.SetHovered(FSlateRoundedBoxBrush(FStyleColors::Background, 2.0f, FStyleColors::InputOutline, 1.0f))
+		.SetPressed(FSlateRoundedBoxBrush(FStyleColors::Background, 2.0f, FStyleColors::Hover, 1.0f))
+		.SetNormalForeground(FStyleColors::Transparent)
+		.SetHoveredForeground(FStyleColors::Hover)
+		.SetPressedForeground(FStyleColors::Foreground)
+		.SetNormalPadding(FMargin(2, 2, 2, 2))
+		.SetPressedPadding(FMargin(2, 3, 2, 1));
+	
+	Set("StateTree.Node.Indent.ComboBox", FComboButtonStyle(ComboButtonStyle).SetButtonStyle(IndentButton));
+
+	
+	// Node text styles
+	{
+		FEditableTextStyle EditableTextStyle = FEditableTextStyle(FAppStyle::GetWidgetStyle<FEditableTextStyle>("NormalEditableText"));
+		EditableTextStyle.Font = FAppStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"));
+		EditableTextStyle.Font.Size = 10.0f;
+		Set("StateTree.Node.Editable", EditableTextStyle);
+
+		FEditableTextBoxStyle EditableTextBlockStyle = FEditableTextBoxStyle(FAppStyle::GetWidgetStyle<FEditableTextBoxStyle>("NormalEditableTextBox"));
+		EditableTextStyle.Font = FAppStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont"));
+		EditableTextStyle.Font.Size = 10.0f;
+		Set("StateTree.Node.EditableTextBlock", EditableTextBlockStyle);
+
+		const FTextBlockStyle StateNodeNormalText = FTextBlockStyle(NormalText)
+			.SetFont(FAppStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont")))
+			.SetFontSize(10);
+		Set("StateTree.Node.Normal", StateNodeNormalText);
+
+		Set("StateTree.Node.Bold", FTextBlockStyle(NormalText)
+			.SetFont(FAppStyle::GetFontStyle(TEXT("PropertyWindow.BoldFont")))
+			.SetFontSize(10));
+
+		Set("StateTree.Node.Subdued", FTextBlockStyle(NormalText)
+			.SetColorAndOpacity(FSlateColor::UseSubduedForeground())
+			.SetFont(FAppStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont")))
+			.SetFontSize(10));
+
+		Set("StateTree.Node.TitleInlineEditableText", FInlineEditableTextBlockStyle()
+			.SetTextStyle(StateNodeNormalText)
+			.SetEditableTextBoxStyle(EditableTextBlockStyle));
+	}
+
+	
+	// Command icons
+	{
+		// From generic Engine
+		UE::AbleTree::Editor::FAbleContentRootScope Scope(this, EngineSlateContentDir);
+		Set("StateTreeEditor.CutStates", new IMAGE_BRUSH_SVG("Starship/Common/Cut", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.CopyStates", new IMAGE_BRUSH_SVG("Starship/Common/Copy", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.DuplicateStates", new IMAGE_BRUSH_SVG("Starship/Common/Duplicate", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.DeleteStates", new IMAGE_BRUSH_SVG("Starship/Common/Delete", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.RenameState", new IMAGE_BRUSH_SVG("Starship/Common/Rename", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.AutoScroll", new IMAGE_BRUSH_SVG("Starship/Insights/AutoScrollRight_20", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.Debugger.ResetTracks", new IMAGE_BRUSH_SVG("Starship/Common/Delete", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.Debugger.State.Enter", new CORE_IMAGE_BRUSH_SVG("Starship/Common/arrow-right", CoreStyleConstants::Icon16x16, FStyleColors::Foreground));
+		Set("StateTreeEditor.Debugger.State.Exit", new CORE_IMAGE_BRUSH_SVG("Starship/Common/arrow-left", CoreStyleConstants::Icon16x16, FStyleColors::Foreground));
+		Set("StateTreeEditor.Debugger.State.Selected", new CORE_IMAGE_BRUSH_SVG("Starship/Common/arrow-right", CoreStyleConstants::Icon16x16, FStyleColors::AccentYellow));
+		Set("StateTreeEditor.Debugger.State.Completed", new CORE_IMAGE_BRUSH_SVG("Starship/Common/check", CoreStyleConstants::Icon16x16, FStyleColors::AccentGreen));
+
+		Set("StateTreeEditor.Debugger.Task.Enter", new CORE_IMAGE_BRUSH_SVG("Starship/Common/arrow-right", CoreStyleConstants::Icon16x16, FStyleColors::Foreground));
+		Set("StateTreeEditor.Debugger.Task.Exit", new CORE_IMAGE_BRUSH_SVG("Starship/Common/arrow-left", CoreStyleConstants::Icon16x16, FStyleColors::Foreground));
+		Set("StateTreeEditor.Debugger.Task.Failed", new CORE_IMAGE_BRUSH_SVG("Starship/Common/close-small", CoreStyleConstants::Icon16x16, FStyleColors::AccentRed));
+		Set("StateTreeEditor.Debugger.Task.Succeeded", new CORE_IMAGE_BRUSH_SVG("Starship/Common/check", CoreStyleConstants::Icon16x16, FStyleColors::AccentGreen));
+		Set("StateTreeEditor.Debugger.Task.Stopped", new CORE_IMAGE_BRUSH_SVG("Starship/Common/close-small", CoreStyleConstants::Icon16x16, FStyleColors::AccentRed));
+
+		Set("StateTreeEditor.Debugger.Condition.Passed", new CORE_IMAGE_BRUSH_SVG("Starship/Common/check", CoreStyleConstants::Icon16x16, FStyleColors::AccentGreen));
+		Set("StateTreeEditor.Debugger.Condition.Failed", new CORE_IMAGE_BRUSH_SVG("Starship/Common/close-small", CoreStyleConstants::Icon16x16, FStyleColors::AccentRed));
+		Set("StateTreeEditor.Debugger.Condition.OnEvaluating", new CORE_IMAGE_BRUSH_SVG("Starship/Common/Update", CoreStyleConstants::Icon16x16, FStyleColors::AccentYellow));
+
+		Set("StateTreeEditor.Debugger.Unset", new CORE_IMAGE_BRUSH_SVG("Starship/Common/help", CoreStyleConstants::Icon16x16, FStyleColors::AccentBlack));
+
+		// Common Node Icons
+		Set("Node.EnableDisable", new CORE_IMAGE_BRUSH_SVG("Starship/Common/check-circle", CoreStyleConstants::Icon16x16));
+		Set("Node.Time", new CORE_IMAGE_BRUSH_SVG("Starship/Common/Recent", CoreStyleConstants::Icon16x16));
+		Set("Node.Sync", new CORE_IMAGE_BRUSH_SVG("Starship/Common/Update", CoreStyleConstants::Icon16x16));
+	}
+
+	{
+		// From generic Engine Editor
+		UE::AbleTree::Editor::FAbleContentRootScope Scope(this, EngineEditorSlateContentDir);
+
+		Set("StateTreeEditor.Debugger.StartRecording", new IMAGE_BRUSH("Sequencer/Transport_Bar/Record_24x", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Debugger.StopRecording", new IMAGE_BRUSH("Sequencer/Transport_Bar/Recording_24x", CoreStyleConstants::Icon16x16));
+		
+		Set("StateTreeEditor.Debugger.PreviousFrameWithStateChange", new IMAGE_BRUSH("Sequencer/Transport_Bar/Go_To_Front_24x", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Debugger.PreviousFrameWithEvents", new IMAGE_BRUSH("Sequencer/Transport_Bar/Step_Backwards_24x", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Debugger.NextFrameWithEvents", new IMAGE_BRUSH("Sequencer/Transport_Bar/Step_Forward_24x", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Debugger.NextFrameWithStateChange", new IMAGE_BRUSH("Sequencer/Transport_Bar/Go_To_End_24x", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.Debugger.ToggleOnEnterStateBreakpoint", new IMAGE_BRUSH_SVG("Starship/Blueprints/Breakpoint_Valid", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Debugger.EnableOnEnterStateBreakpoint", new IMAGE_BRUSH_SVG("Starship/Blueprints/Breakpoint_Valid", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Debugger.EnableOnExitStateBreakpoint", new IMAGE_BRUSH_SVG("Starship/Blueprints/Breakpoint_Valid", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.DebugOptions", new IMAGE_BRUSH_SVG("Starship/Common/Bug", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.Debugger.OwnerTrack", new IMAGE_BRUSH_SVG("Starship/AssetIcons/AIController_64", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Debugger.InstanceTrack", new IMAGE_BRUSH_SVG("Starship/AssetIcons/AnimInstance_64", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.EnableStates", new IMAGE_BRUSH("Icons/Empty_16x", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Debugger.Breakpoint.EnabledAndValid", new IMAGE_BRUSH_SVG( "Starship/Blueprints/Breakpoint_Valid", CoreStyleConstants::Icon16x16, FStyleColors::AccentRed));
+		Set("StateTreeEditor.Debugger.ResumeDebuggerAnalysis", new IMAGE_BRUSH_SVG("Starship/Common/Timeline", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.Transition.None", new CORE_IMAGE_BRUSH_SVG("Starship/Common/x-circle", CoreStyleConstants::Icon16x16, FSlateColor::UseSubduedForeground()));
+		Set("StateTreeEditor.Transition.Succeeded", new CORE_IMAGE_BRUSH_SVG("Starship/Common/check", CoreStyleConstants::Icon16x16, FStyleColors::AccentGreen));
+		Set("StateTreeEditor.Transition.Failed", new CORE_IMAGE_BRUSH_SVG("Starship/Common/close-small", CoreStyleConstants::Icon16x16, FStyleColors::AccentRed));
+
+		Set("StateTreeEditor.Transition.Succeeded", new CORE_IMAGE_BRUSH_SVG("Starship/Common/check", CoreStyleConstants::Icon16x16, FStyleColors::AccentGreen));
+		Set("StateTreeEditor.Transition.Failed", new CORE_IMAGE_BRUSH_SVG("Starship/Common/close-small", CoreStyleConstants::Icon16x16, FStyleColors::AccentRed));
+
+		// Common Node Icons
+		Set("Node.Navigation", new IMAGE_BRUSH_SVG("Starship/Common/Navigation", CoreStyleConstants::Icon16x16));
+		Set("Node.Event", new IMAGE_BRUSH_SVG("Starship/Common/Event", CoreStyleConstants::Icon16x16));
+		Set("Node.Animation", new IMAGE_BRUSH_SVG("Starship/Common/Animation", CoreStyleConstants::Icon16x16));
+		Set("Node.Debug", new IMAGE_BRUSH_SVG("Starship/Common/Debug", CoreStyleConstants::Icon16x16));
+		Set("Node.Find", new IMAGE_BRUSH_SVG("Starship/Common/Find", CoreStyleConstants::Icon16x16));
+	}
+
+	{
+		// From plugin
+		Set("StateTreeEditor.AddSiblingState", new IMAGE_BRUSH_SVG("Icons/Sibling_State", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.AddChildState", new IMAGE_BRUSH_SVG("Icons/Child_State", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.PasteStatesAsSiblings", new IMAGE_BRUSH_SVG("Icons/Sibling_State", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.PasteStatesAsChildren", new IMAGE_BRUSH_SVG("Icons/Child_State", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.SelectNone", new IMAGE_BRUSH_SVG("Icons/Select_None", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.TryEnterState", new IMAGE_BRUSH_SVG("Icons/Try_Enter_State", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.TrySelectChildrenInOrder", new IMAGE_BRUSH_SVG("Icons/Try_Select_Children_In_Order", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.TrySelectChildrenAtRandom", new IMAGE_BRUSH_SVG("Icons/Try_Select_Children_At_Random", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.TryFollowTransitions", new IMAGE_BRUSH_SVG("Icons/Try_Follow_Transitions", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.StateConditions", new IMAGE_BRUSH_SVG("Icons/State_Conditions", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.Conditions", new IMAGE_BRUSH_SVG("Icons/Conditions", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Conditions.Large", new IMAGE_BRUSH_SVG("Icons/Conditions", CoreStyleConstants::Icon24x24));
+		Set("StateTreeEditor.Evaluators", new IMAGE_BRUSH_SVG("Icons/Evaluators", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Parameters", new IMAGE_BRUSH_SVG("Icons/Parameters", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Utility", new IMAGE_BRUSH_SVG("Icons/Utility", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Utility.Large", new IMAGE_BRUSH_SVG("Icons/Utility", CoreStyleConstants::Icon24x24));
+		Set("StateTreeEditor.Tasks", new IMAGE_BRUSH_SVG("Icons/Tasks", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.Tasks.Large", new IMAGE_BRUSH_SVG("Icons/Tasks", CoreStyleConstants::Icon24x24));
+		Set("StateTreeEditor.Transitions", new IMAGE_BRUSH_SVG("Icons/Transitions", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.StateSubtree", new IMAGE_BRUSH_SVG("Icons/State_Subtree", CoreStyleConstants::Icon16x16));
+		Set("StateTreeEditor.StateLinked", new IMAGE_BRUSH_SVG("Icons/State_Linked", CoreStyleConstants::Icon16x16));
+
+		Set("StateTreeEditor.Transition.Dash", new IMAGE_BRUSH_SVG("Icons/Transition_Dash", CoreStyleConstants::Icon16x16, FStyleColors::Foreground));
+		Set("StateTreeEditor.Transition.Goto", new IMAGE_BRUSH_SVG("Icons/Transition_Goto", CoreStyleConstants::Icon16x16, FStyleColors::Foreground));
+		Set("StateTreeEditor.Transition.Next", new IMAGE_BRUSH_SVG("Icons/Transition_Next", CoreStyleConstants::Icon16x16, FStyleColors::Foreground));
+		Set("StateTreeEditor.Transition.Parent", new IMAGE_BRUSH_SVG("Icons/Transition_Parent", CoreStyleConstants::Icon16x16, FStyleColors::Foreground));
+
+		Set("StateTreeEditor.Transition.Condition", new IMAGE_BRUSH_SVG("Icons/State_Conditions", CoreStyleConstants::Icon16x16, FStyleColors::AccentGray));
+		Set("StateTreeEditor.Debugger.Condition.OnTransition", new IMAGE_BRUSH_SVG("Icons/State_Conditions", CoreStyleConstants::Icon16x16, FStyleColors::AccentGray));
+
+		Set("StateTreeEditor.Debugger.Log.Warning", new IMAGE_BRUSH_SVG("Icons/Alert", CoreStyleConstants::Icon16x16, FStyleColors::AccentYellow));
+		Set("StateTreeEditor.Debugger.Log.Error", new IMAGE_BRUSH_SVG("Icons/Failure", CoreStyleConstants::Icon16x16, FStyleColors::AccentRed));
+
+		// Common Node Icons
+		Set("Node.Movement", new IMAGE_BRUSH_SVG("Icons/Movement", CoreStyleConstants::Icon16x16));
+		Set("Node.Tag", new IMAGE_BRUSH_SVG("Icons/Tag", CoreStyleConstants::Icon16x16));
+		Set("Node.RunParallel", new IMAGE_BRUSH_SVG("Icons/RunParallel", CoreStyleConstants::Icon16x16));
+		Set("Node.Task", new IMAGE_BRUSH_SVG("Icons/Task", CoreStyleConstants::Icon16x16));
+		Set("Node.Text", new IMAGE_BRUSH_SVG("Icons/Text", CoreStyleConstants::Icon16x16));
+		Set("Node.Function", new IMAGE_BRUSH_SVG("Icons/Function", CoreStyleConstants::Icon16x16));
+	}
+}
+
+void FAbleTreeEditorStyle::Register()
+{
+	FSlateStyleRegistry::RegisterSlateStyle(Get());
+}
+
+void FAbleTreeEditorStyle::Unregister()
+{
+	FSlateStyleRegistry::UnRegisterSlateStyle(Get());
+}
+
+FAbleTreeEditorStyle& FAbleTreeEditorStyle::Get()
+{
+	static FAbleTreeEditorStyle Instance;
+	return Instance;
+}
+
+const FSlateBrush* FAbleTreeEditorStyle::GetBrushForSelectionBehaviorType()
+{	
+// 	if (InBehaviour == EStateTreeStateSelectionBehavior::None)
+// 	{
+// 		return Get().GetBrush("StateTreeEditor.SelectNone");
+// 	}
+// 	else if (InBehaviour == EStateTreeStateSelectionBehavior::TryEnterState)
+// 	{
+// 		return Get().GetBrush("StateTreeEditor.TryEnterState");			
+// 	}
+// 	else if (InBehaviour == EStateTreeStateSelectionBehavior::TrySelectChildrenInOrder
+// 		|| InBehaviour == EStateTreeStateSelectionBehavior::TrySelectChildrenWithHighestUtility
+// 		|| InBehaviour == EStateTreeStateSelectionBehavior::TrySelectChildrenAtRandomWeightedByUtility)
+// 	{
+// 		if (!bHasChildren
+// 			|| StateType == EStateTreeStateType::Linked
+// 			|| StateType == EStateTreeStateType::LinkedAsset)
+// 		{
+// 			return Get().GetBrush("StateTreeEditor.TryEnterState");			
+// 		}
+// 		else
+// 		{
+// 			return Get().GetBrush("StateTreeEditor.TrySelectChildrenInOrder");
+// 		}
+// 	}
+// 	else if (InBehaviour == EStateTreeStateSelectionBehavior::TrySelectChildrenAtRandom)
+// 	{
+// 		return Get().GetBrush("StateTreeEditor.TrySelectChildrenAtRandom");
+// 	}
+// 	else if (InBehaviour == EStateTreeStateSelectionBehavior::TryFollowTransitions)
+// 	{
+// 		return Get().GetBrush("StateTreeEditor.TryFollowTransitions");
+// 	}
+
+	return nullptr;
+}

+ 215 - 0
Ability/Plugins/EzAbility/Source/EzAbilityEditor/Private/SAbleTreeExpanderArrow.cpp

@@ -0,0 +1,215 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#include "SAbleTreeExpanderArrow.h"
+#include "Framework/Application/SlateApplication.h"
+#include "Widgets/Images/SImage.h"
+#include "Widgets/Input/SButton.h"
+#include "Widgets/Views/STableRow.h"
+
+SAbleTreeExpanderArrow::SAbleTreeExpanderArrow()
+{
+}
+
+void SAbleTreeExpanderArrow::Construct( const FArguments& InArgs, const TSharedPtr<class ITableRow>& TableRow  )
+{
+	OwnerRowPtr = TableRow;
+	IndentAmount = InArgs._IndentAmount;
+	BaseIndentLevel = InArgs._BaseIndentLevel;
+	StyleSet = InArgs._StyleSet;
+	
+	WireColor = InArgs._WireColorAndOpacity;
+	ImageSize = InArgs._ImageSize;
+	ImagePadding = InArgs._ImagePadding;
+
+	this->ChildSlot
+	[
+		SAssignNew(ExpanderArrow, SButton)
+		.ButtonStyle(FCoreStyle::Get(), "NoBorder")
+		.Visibility(this, &SAbleTreeExpanderArrow::GetExpanderVisibility)
+		.ClickMethod(EButtonClickMethod::MouseDown )
+		.OnClicked(this, &SAbleTreeExpanderArrow::OnArrowClicked)
+		.ForegroundColor(FLinearColor(1,1,1,0.75f))
+		.IsFocusable(false)
+		.ContentPadding(TAttribute<FMargin>( this, &SAbleTreeExpanderArrow::GetExpanderPadding )) //FMargin(0))
+		.HAlign(HAlign_Left)
+		.VAlign(VAlign_Top)
+		[
+			SNew(SImage)
+			.DesiredSizeOverride(FVector2D(InArgs._ImageSize))
+			.Image(this, &SAbleTreeExpanderArrow::GetExpanderImage)
+			.ColorAndOpacity(FLinearColor(1,1,1,0.5f))
+		]
+	];
+}
+
+int32 SAbleTreeExpanderArrow::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
+{
+	static constexpr float WireThickness = 2.0f;
+	static constexpr float HalfWireThickness = WireThickness / 2.0f;
+
+	// We want to support drawing wires for the tree
+	//                 Needs Wire Array
+	//   v-[A]         {}
+	//   |-v[B]        {1}
+	//   | '-v[B]      {1,1}
+	//   |   |--[C]    {1,0,1}
+	//   |   |--[D]    {1,0,1}
+	//   |   '--[E]    {1,0,1} 
+	//   |>-[F]        {}
+	//   '--[G]        {}
+	//   
+	//
+
+	const float Indent = IndentAmount;
+	const FSlateBrush* VerticalBarBrush = FAppStyle::GetBrush("WhiteBrush");;
+
+	const float OffsetX = ImageSize.Y * 0.5f + ImagePadding.Left;;
+	const float VerticalWireLoc = ImageSize.Y * 0.5f + ImagePadding.Top;
+
+	if (VerticalBarBrush != nullptr)
+	{
+		const TSharedPtr<ITableRow> OwnerRow = OwnerRowPtr.Pin();
+		const FLinearColor WireTint = WireColor.GetSpecifiedColor();
+
+		// Draw vertical wires to indicate paths to parent nodes.
+		const TBitArray<>& NeedsWireByLevel = OwnerRow->GetWiresNeededByDepth();
+		const int32 NumLevels = NeedsWireByLevel.Num();
+		for (int32 Level = 1; Level < NumLevels; Level++)
+		{
+			const float CurrentIndent = Indent * static_cast<float>(Level - 1);
+
+			if (NeedsWireByLevel[Level])
+			{
+				const FVector2f Offset(CurrentIndent + OffsetX, 0);
+				const FVector2f Size(WireThickness, AllottedGeometry.Size.Y);
+				FSlateDrawElement::MakeBox(
+					OutDrawElements,
+					LayerId,
+					AllottedGeometry.ToPaintGeometry(Size, FSlateLayoutTransform(Offset)),
+					VerticalBarBrush,
+					ESlateDrawEffect::None,
+					WireTint
+				);
+			}
+		}
+		
+		// For items that are the last expanded child in a list, we need to draw a special angle connector wire.
+		if (OwnerRow->IsLastChild())
+		{
+			const float CurrentIndent = Indent * static_cast<float>(NumLevels - 2);
+			const FVector2f Offset(CurrentIndent + OffsetX, 0);
+			const FVector2f Size(WireThickness, VerticalWireLoc + HalfWireThickness);
+			FSlateDrawElement::MakeBox(
+				OutDrawElements,
+				LayerId,
+				AllottedGeometry.ToPaintGeometry(Size, FSlateLayoutTransform(Offset)),
+				VerticalBarBrush,
+				ESlateDrawEffect::None,
+				WireTint
+			);
+		}
+
+		// If this item is expanded, we need to draw a 1/2-height the line down to its first child cell.
+		if (OwnerRow->IsItemExpanded() && OwnerRow->DoesItemHaveChildren())
+		{
+			const float CurrentIndent = Indent * static_cast<float>(NumLevels - 1);
+			const FVector2f Offset(CurrentIndent + OffsetX, ImageSize.Y + ImagePadding.Top);
+			const FVector2f Size(WireThickness,  (AllottedGeometry.Size.Y - (ImageSize.Y + ImagePadding.Top)));
+			FSlateDrawElement::MakeBox(
+				OutDrawElements,
+				LayerId,
+				AllottedGeometry.ToPaintGeometry(Size, FSlateLayoutTransform(Offset)),
+				VerticalBarBrush,
+				ESlateDrawEffect::None,
+				WireTint
+			);
+		}
+
+		// Draw horizontal connector from parent wire to child.
+		if (NumLevels > 1)
+		{
+			const float HorizontalWireStart = static_cast<float>(NumLevels - 2) * Indent + OffsetX;
+			const float HorizontalWireEnd = static_cast<float>(NumLevels - 1) * Indent + ImagePadding.Left - WireThickness + (OwnerRow->DoesItemHaveChildren() ? 0.0f : ImageSize.X);
+			const FVector2f Offset(HorizontalWireStart + WireThickness, VerticalWireLoc - WireThickness * 0.5f);
+			const FVector2f Size(HorizontalWireEnd - HorizontalWireStart, WireThickness);
+			FSlateDrawElement::MakeBox(
+				OutDrawElements,
+				LayerId,
+				AllottedGeometry.ToPaintGeometry(Size, FSlateLayoutTransform(Offset)),
+				VerticalBarBrush,
+				ESlateDrawEffect::None,
+				WireTint
+			);
+		}	
+	}
+
+	return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId + 1, InWidgetStyle, bParentEnabled);
+}
+
+/** Invoked when the expanded button is clicked (toggle item expansion) */
+FReply SAbleTreeExpanderArrow::OnArrowClicked()
+{
+	// Recurse the expansion if "shift" is being pressed
+	const FModifierKeysState ModKeyState = FSlateApplication::Get().GetModifierKeys();
+	if(ModKeyState.IsShiftDown())
+	{
+		OwnerRowPtr.Pin()->Private_OnExpanderArrowShiftClicked();
+	}
+	else
+	{
+		OwnerRowPtr.Pin()->ToggleExpansion();
+	}
+
+	return FReply::Handled();
+}
+
+/** @return Visible when has children; invisible otherwise */
+EVisibility SAbleTreeExpanderArrow::GetExpanderVisibility() const
+{
+	return OwnerRowPtr.Pin()->DoesItemHaveChildren() ? EVisibility::Visible : EVisibility::Hidden;
+}
+
+/** @return the name of an image that should be shown as the expander arrow */
+const FSlateBrush* SAbleTreeExpanderArrow::GetExpanderImage() const
+{
+	const bool bIsItemExpanded = OwnerRowPtr.Pin()->IsItemExpanded();
+
+	FName ResourceName;
+	if (bIsItemExpanded)
+	{
+		if ( ExpanderArrow->IsHovered() )
+		{
+			static FName ExpandedHoveredName = "TreeArrow_Expanded_Hovered";
+			ResourceName = ExpandedHoveredName;
+		}
+		else
+		{
+			static FName ExpandedName = "TreeArrow_Expanded";
+			ResourceName = ExpandedName;
+		}
+	}
+	else
+	{
+		if ( ExpanderArrow->IsHovered() )
+		{
+			static FName CollapsedHoveredName = "TreeArrow_Collapsed_Hovered";
+			ResourceName = CollapsedHoveredName;
+		}
+		else
+		{
+			static FName CollapsedName = "TreeArrow_Collapsed";
+			ResourceName = CollapsedName;
+		}
+	}
+
+	return StyleSet->GetBrush(ResourceName);
+}
+
+/** @return the margin corresponding to how far this item is indented */
+FMargin SAbleTreeExpanderArrow::GetExpanderPadding() const
+{
+	const int32 NestingDepth = FMath::Max(0, OwnerRowPtr.Pin()->GetIndentLevel() - BaseIndentLevel);
+	FMargin Padding = ImagePadding;
+	Padding.Left += static_cast<float>(NestingDepth) * IndentAmount;
+	return Padding;
+}

+ 455 - 8
Ability/Plugins/EzAbility/Source/EzAbilityEditor/Private/SEzAbleTreeViewRowWidget.cpp

@@ -3,24 +3,471 @@
 
 #include "SEzAbleTreeViewRowWidget.h"
 #include "SlateOptMacros.h"
+#include "SAbleTreeExpanderArrow.h"
+#include "AbleTreeEditorStyle.h"
+//#include "SInlineEditableTextBlock.h"
 
 #define LOCTEXT_NAMESPACE "AbleTreeViewRow"
 BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
 void SEzAbleTreeViewRowWidget::Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, TWeakObjectPtr<UEzAbilityState> InState, const TSharedPtr<SScrollBox>& ViewBox)
 {
+	WeakState = InState;
+
 	ConstructInternal(STableRow::FArguments()
 		.Padding(5.0f)
 		, InOwnerTableView);
 
-	ChildSlot
-	[
-		SNew(SButton)
-		.VAlign(VAlign_Center)
-		.HAlign(HAlign_Center)
-		.Text(LOCTEXT("SplitSprites", "Split Sprites"))
-		.ToolTipText(LOCTEXT("SplitSprites_Tooltip", "Splits all sprite instances into separate sprite actors or components"))
-	];
+	static constexpr FLinearColor IconTint = FLinearColor(1, 1, 1, 0.5f);
+
+	const float ExpanderArrowTopPadding = FMath::FloorToFloat((UE::EzStateTree::Editor::StateRowHeight - 16.f) / 2.f);
+
+
+// 	auto MakeTransitionWidget = [this](const EStateTreeTransitionTrigger Trigger, const FSlateBrush* Icon)
+// 		{
+// 			FTransitionDescFilterOptions FilterOptions;
+// 			FilterOptions.bUseMask = EnumHasAnyFlags(Trigger, EStateTreeTransitionTrigger::OnTick | EStateTreeTransitionTrigger::OnEvent);
+// 
+// 			return SNew(SBox)
+// 				.HeightOverride(UE::EzStateTree::Editor::StateRowHeight)
+// 				.Visibility(this, &SStateTreeViewRow::GetTransitionsVisibility, Trigger)
+// 				[
+// 					SNew(SHorizontalBox)
+// 						+ SHorizontalBox::Slot()
+// 						.VAlign(VAlign_Center)
+// 						.AutoWidth()
+// 						.Padding(FMargin(0.f, 0.f, 0.f, 0.f))
+// 						[
+// 							SNew(SHorizontalBox)
+// 
+// 								+ SHorizontalBox::Slot()
+// 								.VAlign(VAlign_Center)
+// 								.AutoWidth()
+// 								[
+// 									SNew(SImage)
+// 										.Image(Icon)
+// 								]
+// 
+// 								+ SHorizontalBox::Slot()
+// 								.VAlign(VAlign_Center)
+// 								.AutoWidth()
+// 								[
+// 									SNew(SImage)
+// 										.Image(this, &SStateTreeViewRow::GetTransitionsIcon, Trigger)
+// 										.ColorAndOpacity(IconTint)
+// 								]
+// 						]
+// 						+ SHorizontalBox::Slot()
+// 						.VAlign(VAlign_Center)
+// 						.AutoWidth()
+// 						.Padding(FMargin(4.f, 0.f, 12.f, 0.f))
+// 						[
+// 							SNew(SOverlay)
+// 								+ SOverlay::Slot()
+// 								[
+// 									MakeTransitionWidgets(Trigger, FilterOptions)
+// 								]
+// 								+ SOverlay::Slot()
+// 								[
+// 									// Breakpoint box
+// 									SNew(SHorizontalBox)
+// 										+ SHorizontalBox::Slot()
+// 										.VAlign(VAlign_Top)
+// 										.HAlign(HAlign_Left)
+// 										.AutoWidth()
+// 										[
+// 											SNew(SBox)
+// 												.Padding(FMargin(-4.f, -4.f, 0.f, 0.f))
+// 												[
+// 													SNew(SImage)
+// 														.DesiredSizeOverride(FVector2D(10.f, 10.f))
+// 														.Image(FStateTreeEditorStyle::Get().GetBrush(TEXT("StateTreeEditor.Debugger.Breakpoint.EnabledAndValid")))
+// 														.Visibility(this, &SStateTreeViewRow::GetTransitionsBreakpointVisibility, Trigger)
+// 														.ToolTipText_Lambda([this, Trigger, InFilterOptions = FilterOptions]
+// 															{
+// 																FTransitionDescFilterOptions FilterOptions = InFilterOptions;
+// 																FilterOptions.WithBreakpoint = ETransitionDescRequirement::RequiredTrue;
+// 
+// 																return FText::Format(LOCTEXT("TransitionBreakpointTooltip", "Break when executing transition: {0}"),
+// 																	GetTransitionsDesc(Trigger, FilterOptions));
+// 															})
+// 												]
+// 										]
+// 								]
+// 
+// 						]
+// 				];
+// 	};
+
+	this->ChildSlot
+		.HAlign(HAlign_Fill)
+		[
+			SNew(SBox)
+				.MinDesiredWidth_Lambda([WeakOwnerViewBox = ViewBox.ToWeakPtr()]()
+					{
+						// Captured as weak ptr so we don't prevent our parent widget from being destroyed (circular pointer reference).
+						if (const TSharedPtr<SScrollBox> OwnerViewBox = WeakOwnerViewBox.Pin())
+						{
+							// Make the row at least as wide as the view.
+							// The -1 is needed or we'll see a scrollbar.
+							return OwnerViewBox->GetTickSpaceGeometry().GetLocalSize().X - 1;
+						}
+						return 0.f;
+					})
+				.Padding(FMargin(0, 0, 0, 0))
+				[
+					SNew(SHorizontalBox)
+						+ SHorizontalBox::Slot()
+						.VAlign(VAlign_Fill)
+						.HAlign(HAlign_Left)
+						.AutoWidth()
+						[
+							SNew(SAbleTreeExpanderArrow, SharedThis(this))
+								.IndentAmount(24.f)
+								.BaseIndentLevel(0)
+								.ImageSize(FVector2f(16, 16))
+								.ImagePadding(FMargin(9, 14, 0, 0))
+								.Image(this, &SEzAbleTreeViewRowWidget::GetSelectorIcon) // SStateTreeViewRow::GetSelectorIcon
+								.ColorAndOpacity(FLinearColor(1, 1, 1, 0.2f))
+								.WireColorAndOpacity(FLinearColor(1, 1, 1, 0.2f))
+						]
+
+						+ SHorizontalBox::Slot()
+						.VAlign(VAlign_Fill)
+						.HAlign(HAlign_Left)
+						.AutoWidth()
+						.Padding(FMargin(0, 6, 0, 6))
+						[
+							// State and tasks
+							SNew(SVerticalBox)
+								+ SVerticalBox::Slot()
+								.AutoHeight()
+								[
+									// State
+									SNew(SBox)
+										.HeightOverride(UE::EzStateTree::Editor::StateRowHeight)
+										.HAlign(HAlign_Left)
+										[
+											SNew(SHorizontalBox)
+
+												// State Box
+												+ SHorizontalBox::Slot()
+												.VAlign(VAlign_Center)
+												//.FillWidth(1.f)
+												.AutoWidth()
+												[
+													SNew(SBox)
+														.HeightOverride(UE::EzStateTree::Editor::StateRowHeight)
+														.VAlign(VAlign_Fill)
+														[
+															SNew(SBorder)
+																.BorderImage(FAbleTreeEditorStyle::Get().GetBrush("StateTree.State.Border"))
+																//.BorderBackgroundColor(this, &SStateTreeViewRow::GetActiveStateColor)
+																[
+																	SNew(SBorder)
+																		.BorderImage(FAbleTreeEditorStyle::Get().GetBrush("StateTree.State"))
+																		//.BorderBackgroundColor(this, &SStateTreeViewRow::GetTitleColor, 1.0f, 0.0f)
+																		.Padding(FMargin(0.f, 0.f, 12.f, 0.f))
+																		.IsEnabled_Lambda([InState]
+																			{
+																				const UEzAbilityState* State = InState.Get();
+																				return State != nullptr && State->bEnabled;
+																			})
+																		[
+																			SNew(SOverlay)
+																				+ SOverlay::Slot()
+																				[
+																					SNew(SHorizontalBox)
+
+																						// Sub tree marker
+																						+ SHorizontalBox::Slot()
+																						.VAlign(VAlign_Center)
+																						.AutoWidth()
+																						.Padding(FMargin(0, 0, 0, 0))
+																						[
+																							SNew(SBox)
+																								.WidthOverride(4.f)
+																								.HeightOverride(UE::EzStateTree::Editor::StateRowHeight)
+																								//.Visibility(this, &SStateTreeViewRow::GetSubTreeVisibility)
+																								.VAlign(VAlign_Fill)
+																								.HAlign(HAlign_Fill)
+																								[
+																									SNew(SBorder)
+																										.BorderImage(FAppStyle::GetBrush("WhiteBrush"))
+																										.BorderBackgroundColor(FLinearColor(1, 1, 1, 0.25f))
+																								]
+																						]
+
+																						// Conditions icon
+																						+ SHorizontalBox::Slot()
+																						.VAlign(VAlign_Center)
+																						.AutoWidth()
+																						[
+																							SNew(SBox)
+																								.Padding(FMargin(4.f, 0.f, -4.f, 0.f))
+																								//.Visibility(this, &SStateTreeViewRow::GetConditionVisibility)
+																								[
+																									SNew(SImage)
+																										.ColorAndOpacity(IconTint)
+																										.Image(FAbleTreeEditorStyle::Get().GetBrush("StateTreeEditor.StateConditions"))
+																										.ToolTipText(LOCTEXT("StateHasEnterConditions", "State selection is guarded with enter conditions."))
+																								]
+																						]
+
+																						// Selector icon
+																						+ SHorizontalBox::Slot()
+																						.VAlign(VAlign_Center)
+																						.AutoWidth()
+																						[
+																							SNew(SBox)
+																								.Padding(FMargin(4.f, 0.f, 0.f, 0.f))
+																								[
+																									SNew(SImage)
+																										//.Image(this, &SStateTreeViewRow::GetSelectorIcon)
+																										.ColorAndOpacity(IconTint)
+																										//.ToolTipText(this, &SStateTreeViewRow::GetSelectorTooltip)
+																								]
+																						]
+
+																						// Warnings
+																						+ SHorizontalBox::Slot()
+																						.VAlign(VAlign_Center)
+																						.AutoWidth()
+																						[
+																							SNew(SBox)
+																								.Padding(FMargin(2.f, 0.f, 2.f, 1.f))
+																								//.Visibility(this, &SStateTreeViewRow::GetWarningsVisibility)
+																								[
+																									SNew(SImage)
+																										.Image(FAppStyle::Get().GetBrush("Icons.Warning"))
+																										//.ToolTipText(this, &SStateTreeViewRow::GetWarningsTooltipText)
+																								]
+																						]
+
+																						// State Name
+																						+ SHorizontalBox::Slot()
+																						.VAlign(VAlign_Center)
+																						.AutoWidth()
+// 																						[
+// 																							SAssignNew(NameTextBlock, SInlineEditableTextBlock)
+// 																								.Style(FAbleTreeEditorStyle::Get(), "StateTree.State.TitleInlineEditableText")
+// 																								//.OnTextCommitted(this, &SStateTreeViewRow::HandleNodeLabelTextCommitted)
+// 																								//.OnVerifyTextChanged(this, &SStateTreeViewRow::HandleVerifyNodeLabelTextChanged)
+// 																								//.Text(this, &SStateTreeViewRow::GetStateDesc)
+// 																								//.ToolTipText(this, &SStateTreeViewRow::GetStateTypeTooltip)
+// 																								.Clipping(EWidgetClipping::ClipToBounds)
+// 																								//.IsSelected(this, &SStateTreeViewRow::IsStateSelected)
+// 																						]
+
+																						// Linked State
+																						+ SHorizontalBox::Slot()
+																						.VAlign(VAlign_Center)
+																						.AutoWidth()
+																						[
+																							SNew(SBox)
+																								.HeightOverride(UE::EzStateTree::Editor::StateRowHeight)
+																								.VAlign(VAlign_Fill)
+																								//.Visibility(this, &SStateTreeViewRow::GetLinkedStateVisibility)
+																								[
+																									// Link icon
+																									SNew(SHorizontalBox)
+																										+ SHorizontalBox::Slot()
+																										.VAlign(VAlign_Center)
+																										.AutoWidth()
+																										.Padding(FMargin(4.f, 0.f, 4.f, 0.f))
+																										[
+																											SNew(SImage)
+																												.ColorAndOpacity(IconTint)
+																												.Image(FAbleTreeEditorStyle::Get().GetBrush("StateTreeEditor.StateLinked"))
+																										]
+
+																										// Linked State
+																										+ SHorizontalBox::Slot()
+																										.VAlign(VAlign_Center)
+																										.AutoWidth()
+																										[
+																											SNew(STextBlock)
+																												//.Text(this, &SStateTreeViewRow::GetLinkedStateDesc)
+																												.TextStyle(FAbleTreeEditorStyle::Get(), "StateTree.Details")
+																										]
+																								]
+																						]
+
+																						// State ID
+																						+ SHorizontalBox::Slot()
+																						.VAlign(VAlign_Center)
+																						.AutoWidth()
+																						[
+																							SNew(STextBlock)
+// 																								.Visibility_Lambda([]()
+// 																									{
+// 																										return UE::StateTree::Editor::GbDisplayItemIds ? EVisibility::Visible : EVisibility::Collapsed;
+// 																									})
+																								//.Text(this, &SStateTreeViewRow::GetStateIDDesc)
+																								.TextStyle(FAbleTreeEditorStyle::Get(), "StateTree.Details")
+																						]
+																				]
+																				+ SOverlay::Slot()
+																				[
+																					SNew(SHorizontalBox)
+
+																						// State breakpoint box
+																						+ SHorizontalBox::Slot()
+																						.VAlign(VAlign_Top)
+																						.HAlign(HAlign_Left)
+																						.AutoWidth()
+																						[
+																							SNew(SBox)
+																								.Padding(FMargin(-12.f, -6.f, 0.f, 0.f))
+																								[
+																									SNew(SImage)
+																										.DesiredSizeOverride(FVector2D(12.f, 12.f))
+																										.Image(FAbleTreeEditorStyle::Get().GetBrush(TEXT("StateTreeEditor.Debugger.Breakpoint.EnabledAndValid")))
+																										//.Visibility(this, &SStateTreeViewRow::GetStateBreakpointVisibility)
+																										//.ToolTipText(this, &SStateTreeViewRow::GetStateBreakpointTooltipText)
+																								]
+																						]
+																				]
+																		]
+																]
+														]
+												]
+
+												+ SHorizontalBox::Slot()
+												.VAlign(VAlign_Fill)
+												.HAlign(HAlign_Left)
+												[
+													// Transitions
+													SNew(SHorizontalBox)
+
+														+ SHorizontalBox::Slot()
+														.VAlign(VAlign_Top)
+														.AutoWidth()
+														[
+															SNew(SBox)
+																.HeightOverride(UE::EzStateTree::Editor::StateRowHeight)
+																//.Visibility(this, &SStateTreeViewRow::GetTransitionDashVisibility)
+																.VAlign(VAlign_Center)
+																[
+																	SNew(SImage)
+																		.Image(FAbleTreeEditorStyle::Get().GetBrush("StateTreeEditor.Transition.Dash"))
+																		.ColorAndOpacity(IconTint)
+																]
+														]
+
+														// Completed transitions
+														+ SHorizontalBox::Slot()
+														.VAlign(VAlign_Top)
+														.AutoWidth()
+// 														[
+// 															MakeTransitionWidget(EStateTreeTransitionTrigger::OnStateCompleted, nullptr)
+// 														]
+
+														// Succeeded transitions
+														+ SHorizontalBox::Slot()
+														.VAlign(VAlign_Top)
+														.AutoWidth()
+// 														[
+// 															MakeTransitionWidget(EStateTreeTransitionTrigger::OnStateSucceeded, FStateTreeEditorStyle::Get().GetBrush("StateTreeEditor.Transition.Succeeded"))
+// 														]
+
+														// Failed transitions
+														+ SHorizontalBox::Slot()
+														.VAlign(VAlign_Top)
+														.AutoWidth()
+// 														[
+// 															MakeTransitionWidget(EStateTreeTransitionTrigger::OnStateFailed, FStateTreeEditorStyle::Get().GetBrush("StateTreeEditor.Transition.Failed"))
+// 														]
+
+														// Transitions
+														+ SHorizontalBox::Slot()
+														.VAlign(VAlign_Top)
+														.AutoWidth()
+// 														[
+// 															MakeTransitionWidget(EStateTreeTransitionTrigger::OnTick | EStateTreeTransitionTrigger::OnEvent, FStateTreeEditorStyle::Get().GetBrush("StateTreeEditor.Transition.Condition"))
+// 														]
+												]
+										]
+								]
+								+ SVerticalBox::Slot()
+								.AutoHeight()
+								.Padding(FMargin(0, 2, 0, 0))
+// 								[
+// 									MakeConditionsWidget(ViewBox)
+// 								]
+								+ SVerticalBox::Slot()
+								.AutoHeight()
+								.Padding(FMargin(0, 2, 0, 0))
+// 								[
+// 									MakeTasksWidget(ViewBox)
+// 								]
+						]
+				]
+		];
+
+
+// 	ChildSlot
+// 	[
+// 		SNew(SBox)
+// 		 	.MinDesiredWidth_Lambda([WeakOwnerViewBox = ViewBox.ToWeakPtr()]()
+// 		 	{
+// 		 		// Captured as weak ptr so we don't prevent our parent widget from being destroyed (circular pointer reference).
+// 		 		if (const TSharedPtr<SScrollBox> OwnerViewBox = WeakOwnerViewBox.Pin())
+// 		 		{
+// 		 			// Make the row at least as wide as the view.
+// 		 			// The -1 is needed or we'll see a scrollbar.
+// 		 			return OwnerViewBox->GetTickSpaceGeometry().GetLocalSize().X - 1;
+// 		 		}
+// 		 		return 0.f;
+// 		 	})
+// 			.Padding(FMargin(0, 0, 0, 0))
+// 			[
+// 				SNew(SHorizontalBox)
+// 					+ SHorizontalBox::Slot()
+// 					.VAlign(VAlign_Fill)
+// 					.HAlign(HAlign_Left)
+// 					.AutoWidth()
+// 					[
+// 						SNew(SAbleTreeExpanderArrow, SharedThis(this))
+// 							.IndentAmount(24.f)
+// 							.BaseIndentLevel(0)
+// 							.ImageSize(FVector2f(16, 16))
+// 							.ImagePadding(FMargin(9, 14, 0, 0))
+// 							.Image(this, &SEzAbleTreeViewRowWidget::GetSelectorIcon) // SStateTreeViewRow::GetSelectorIcon
+// 							.ColorAndOpacity(FLinearColor(1, 1, 1, 0.2f))
+// 							.WireColorAndOpacity(FLinearColor(1, 1, 1, 0.2f))
+// 					]
+// 					+ SHorizontalBox::Slot()
+// 					.VAlign(VAlign_Fill)
+// 					.HAlign(HAlign_Left)
+// 					.AutoWidth()
+// 					.Padding(FMargin(0, 6, 0, 6))
+// 					[
+// 						SNew(SBorder)
+// 						.BorderImage(FAbleTreeEditorStyle::Get().GetBrush("StateTree.State"))
+// 						//.BorderBackgroundColor(this, &SStateTreeViewRow::GetTitleColor, 1.0f, 0.0f)
+// 						.Padding(FMargin(0.f, 0.f, 12.f, 0.f))
+// 						.IsEnabled_Lambda([InState]
+// 						{
+// 							const UEzAbilityState* State = InState.Get();
+// 							return State != nullptr && State->bEnabled;
+// 						})
+// 					]
+// 			]
+// 	];
 	
 }
+
+const FSlateBrush* SEzAbleTreeViewRowWidget::GetSelectorIcon() const
+{
+	if (const UEzAbilityState* State = WeakState.Get())
+	{
+		FSlateBrush* SlateBrush = new FSlateBrush();
+		return SlateBrush;
+		//return FStateTreeEditorStyle::GetBrushForSelectionBehaviorType(State->SelectionBehavior, !State->Children.IsEmpty(), State->Type);
+	}
+
+	return nullptr;
+}
+
 END_SLATE_FUNCTION_BUILD_OPTIMIZATION
 #undef LOCTEXT_NAMESPACE

+ 27 - 0
Ability/Plugins/EzAbility/Source/EzAbilityEditor/Public/AbleTreeEditorStyle.h

@@ -0,0 +1,27 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "Styling/SlateStyle.h"
+
+enum class EAbleTreeStateSelectionBehavior : uint8;
+enum class EAbleTreeStateType : uint8;
+
+class ISlateStyle;
+
+class  FAbleTreeEditorStyle : public FSlateStyleSet
+{
+public:
+	static FAbleTreeEditorStyle& Get();
+	
+	static const FSlateBrush* GetBrushForSelectionBehaviorType(); //EAbleTreeStateSelectionBehavior InBehaviour, bool bHasChildren, EAbleTreeStateType StateType	
+
+protected:
+	//friend class FStateTreeEditorModule;
+
+	static void Register();
+	static void Unregister();
+
+private:
+	FAbleTreeEditorStyle();
+};

+ 85 - 0
Ability/Plugins/EzAbility/Source/EzAbilityEditor/Public/SAbleTreeExpanderArrow.h

@@ -0,0 +1,85 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include "Misc/Attribute.h"
+#include "Input/Reply.h"
+#include "Layout/Visibility.h"
+#include "Widgets/DeclarativeSyntaxSupport.h"
+#include "Layout/Margin.h"
+#include "Widgets/SCompoundWidget.h"
+#include "Styling/CoreStyle.h"
+
+class ITableRow;
+class SButton;
+class SImage;
+
+/**
+ * Bespoke implementation of expander arrow for State Tree.
+ */
+class SAbleTreeExpanderArrow : public SCompoundWidget
+{
+public:
+
+	SLATE_BEGIN_ARGS( SAbleTreeExpanderArrow )
+		: _StyleSet(&FCoreStyle::Get())
+		, _IndentAmount(10)
+		, _BaseIndentLevel(0)
+		, _Image(nullptr)
+		, _ImageSize(16.f, 16.f)
+		, _ImagePadding(FMargin())
+	{ }
+		SLATE_ARGUMENT(const ISlateStyle*, StyleSet)
+		SLATE_ARGUMENT(float, IndentAmount)
+		SLATE_ARGUMENT(int32, BaseIndentLevel)
+		SLATE_ATTRIBUTE(const FSlateBrush*, Image)
+		SLATE_ATTRIBUTE(FSlateColor, ColorAndOpacity)
+		SLATE_ARGUMENT(FVector2f, ImageSize)
+		SLATE_ARGUMENT(FSlateColor, WireColorAndOpacity)
+		SLATE_ARGUMENT(FMargin, ImagePadding)
+
+	SLATE_END_ARGS()
+
+	SAbleTreeExpanderArrow();
+	void Construct( const FArguments& InArgs, const TSharedPtr<class ITableRow>& TableRow );
+
+protected:
+
+	virtual int32 OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const override;
+
+	/** Invoked when the expanded button is clicked (toggle item expansion) */
+	FReply OnArrowClicked();
+
+	/** @return Visible when has children; invisible otherwise */
+	EVisibility GetExpanderVisibility() const;
+
+	/** @return the name of an image that should be shown as the expander arrow */
+	const FSlateBrush* GetExpanderImage() const;
+
+	/** @return the margin corresponding to how far this item is indented */
+	FMargin GetExpanderPadding() const;
+
+	/** Pointer to the owning row. */
+	TWeakPtr<class ITableRow> OwnerRowPtr;
+
+	/** The amount of space to indent at each level */
+	float IndentAmount = 10.0f;
+
+	/** The level in the tree that begins the indention amount */
+	int32 BaseIndentLevel = 0;
+
+	/** Color for the wires */
+	FSlateColor WireColor;
+
+	/** Size of the expander image. */
+	FVector2f ImageSize = FVector2f(16, 16);
+
+	/** Padding for the expander image. */
+	FMargin ImagePadding;
+	
+	/** A reference to the expander button */
+	TSharedPtr<SButton> ExpanderArrow;
+
+	/** The slate style to use */
+	const ISlateStyle* StyleSet = nullptr;
+};

+ 14 - 0
Ability/Plugins/EzAbility/Source/EzAbilityEditor/Public/SEzAbleTreeViewRowWidget.h

@@ -10,6 +10,13 @@
 /**
  * 
  */
+namespace UE::EzStateTree::Editor
+{
+	static constexpr float StateRowHeight = 32.0f;
+	static constexpr float TaskRowHeight = 16.0f;
+
+} 
+
 class EZABILITYEDITOR_API SEzAbleTreeViewRowWidget : public STableRow<TWeakObjectPtr<UEzAbilityState>> //public SCompoundWidget
 {
 public:
@@ -19,4 +26,11 @@ public:
 
 	/** Constructs this widget with InArgs */
 	void Construct(const FArguments& InArgs, const TSharedRef<STableViewBase>& InOwnerTableView, TWeakObjectPtr<UEzAbilityState> InState, const TSharedPtr<SScrollBox>& ViewBox);
+
+private:
+
+	const FSlateBrush* GetSelectorIcon() const;
+
+	TWeakObjectPtr<UEzAbilityState> WeakState;
+	TSharedPtr<SInlineEditableTextBlock> NameTextBlock;
 };