OldBlueWater/BlueWater/Assets/Doozy/Editor/Mody/Drawers/ModyActions/BaseModyActionDrawer.cs
2023-08-02 15:08:03 +09:00

436 lines
20 KiB
C#

// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved.
// This code can only be used under the standard Unity Asset Store End User License Agreement
// A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms
using System.Collections.Generic;
using Doozy.Editor.EditorUI;
using Doozy.Editor.EditorUI.Components;
using Doozy.Editor.EditorUI.Components.Internal;
using Doozy.Editor.EditorUI.ScriptableObjects.Colors;
using Doozy.Editor.EditorUI.Utils;
using Doozy.Editor.Mody.Components;
using Doozy.Editor.UIElements;
using Doozy.Runtime.Colors;
using Doozy.Runtime.Common.Utils;
using Doozy.Runtime.Mody;
using Doozy.Runtime.Reactor.Internal;
using Doozy.Runtime.UIElements;
using Doozy.Runtime.UIElements.Extensions;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
// ReSharper disable MemberCanBePrivate.Global
namespace Doozy.Editor.Mody.Drawers.ModyActions
{
public abstract class BaseModyActionDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {}
protected static Color accentColor => EditorColors.Mody.Action;
protected static EditorSelectableColorInfo selectableAccentColor => EditorSelectableColors.Mody.Action;
public static Color idleColor => EditorColors.Mody.StateIdle;
public static Color startDelayColor => EditorColors.Mody.StateIdle;
public static Color runningColor => EditorColors.Mody.StateActive;
public static Color cooldownColor => EditorColors.Mody.StateCooldown;
protected static EdgeValues animatedContainerFluidContainerPadding => new EdgeValues(22, DesignUtils.k_Spacing, 0, DesignUtils.k_EndOfLineSpacing);
private static EnumField NewInvisibleEnumField(string bindingPath) => new EnumField().SetBindingPath(bindingPath).SetStyleDisplay(DisplayStyle.None);
protected static FluidToggleIconButton NewExpandCollapseActionButton(FluidComponentHeader header, FluidAnimatedContainer openedDrawerAnimatedContainer)
{
FluidToggleIconButton button =
FluidToggleIconButton.Get()
.SetElementSize(ElementSize.Tiny)
.SetStyleAlignSelf(Align.Center)
.SetAnimationTrigger(IconAnimationTrigger.OnValueChanged)
.SetStylePaddingLeft(DesignUtils.k_Spacing)
.SetStylePaddingRight(DesignUtils.k_Spacing)
.SetIcon(EditorSpriteSheets.EditorUI.Components.CarretRightToDown);
button.SetToggleAccentColor(selectableAccentColor);
button.iconReaction.SetDuration(FluidAnimatedContainer.k_ReactionDuration);
button.SetOnValueChanged(evt =>
{
header.rotatedIconReaction?.Play();
openedDrawerAnimatedContainer.Toggle(evt.newValue, evt.animateChange);
});
return button;
}
protected static FluidToggleSwitch NewDisableActionSwitch(SerializedProperty property) =>
FluidToggleSwitch.Get()
.SetTooltip("Disable this Action and send it to the Available Actions drawer (on top)")
.SetStyleFlexShrink(0)
.SetStyleAlignSelf(Align.Center)
.SetStyleMarginLeft(DesignUtils.k_Spacing)
.BindToProperty(property.FindPropertyRelative("ActionEnabled"));
protected static FluidComponentHeader NewActionHeader(ModyAction modyAction)
{
string componentTypeName = "Action";
if (modyAction.HasValue && modyAction.ValueType != null)
componentTypeName += $" <{modyAction.ValueType.PrettyName()}>";
FluidComponentHeader header =
FluidComponentHeader.Get()
.SetElementSize(ElementSize.Tiny)
.SetRotatedIcon(EditorSpriteSheets.Mody.Icons.ModyAction)
.SetAccentColor(accentColor)
.SetComponentNameText(modyAction.actionName)
.SetComponentTypeText(componentTypeName);
if (EditorApplication.isPlayingOrWillChangePlaymode)
{
header
.barRightContainer
.SetStyleDisplay(DisplayStyle.Flex)
.AddChild(GetActionStateIndicator(modyAction));
}
return header;
}
protected static void ConnectHeaderToExpandCollapseButton(FluidComponentHeader header, FluidToggleIconButton expandCollapseButton)
{
header.OnClick += () => expandCollapseButton.isOn = !expandCollapseButton.isOn;
header.OnPointerEnter += evt => expandCollapseButton.selectionState = SelectionState.Highlighted;
header.OnPointerLeave += evt => expandCollapseButton.selectionState = SelectionState.Normal;
header.OnPointerDown += evt => expandCollapseButton.selectionState = SelectionState.Pressed;
header.OnPointerUp += evt => expandCollapseButton.selectionState = SelectionState.Normal;
}
protected static VisualElement GetDrawerHeader(FluidToggleIconButton expandCollapseButton, FluidComponentHeader header, FluidToggleSwitch disableSwitch)
{
return
DesignUtils.row
.AddChild(expandCollapseButton)
.AddChild(header)
.AddChild(disableSwitch);
}
protected static VisualElement AnimatedContainerContent(SerializedProperty property)
{
VisualElement content =
DesignUtils.column
.AddChild(GetTriggersListView(property))
.AddSpaceBlock()
.AddChild
(
DesignUtils.row
.AddChild(GetStartDelayFluidField(property))
.AddSpaceBlock()
.AddChild(GetDurationFluidField(property))
.AddSpaceBlock()
.AddChild(GetCooldownFluidField(property))
)
.AddSpaceBlock()
.AddChild
(
DesignUtils.row
.AddChild(GetTimescaleFluidField(property))
.AddSpaceBlock()
.AddChild(GetStopAllActionsOnStartFluidField(property))
)
.AddSpaceBlock()
.AddChild(GetOnStartFluidField(property))
.AddSpaceBlock()
.AddChild(GetOnFinishPropertyField(property))
;
content.Bind(property.serializedObject);
return content;
}
protected static ModyActionStateIndicator GetActionStateIndicator(ModyAction modyAction)
{
var stateIndicator = new ModyActionStateIndicator();
stateIndicator.UpdateTriggeredState(modyAction, ModyAction.TriggeredActionState.Disabled);
modyAction.onStateChanged = state => stateIndicator.UpdateTriggeredState(modyAction, state);
return stateIndicator;
}
protected static FluidListView GetTriggersListView(SerializedProperty property)
{
SerializedProperty actionNameProperty = property.FindPropertyRelative("ActionName");
SerializedProperty arrayProperty = property.FindPropertyRelative("SignalsReceivers");
var itemsSource = new List<SerializedProperty>();
FluidListView fluidListView =
new FluidListView()
.SetListDescription($"Action Triggers")
.SetDynamicListHeight(false)
.ShowEmptyListPlaceholder(true)
.UseSmallEmptyListPlaceholder(true)
.HideFooterWhenEmpty(true);
fluidListView.emptyListPlaceholder.SetIcon(EditorSpriteSheets.EditorUI.Placeholders.EmptyListViewSmall);
fluidListView.listView.selectionType = SelectionType.None;
fluidListView.listView.itemsSource = itemsSource;
#if UNITY_2021_2_OR_NEWER
fluidListView.listView.fixedItemHeight = 70;
fluidListView.SetPreferredListHeight((int)fluidListView.listView.fixedItemHeight * 4);
#else
fluidListView.listView.itemHeight = 70;
fluidListView.SetPreferredListHeight(fluidListView.listView.itemHeight * 4);
#endif
fluidListView.listView.makeItem = () => new PropertyFluidListViewItem(fluidListView);
fluidListView.listView.bindItem = (element, i) =>
{
var item = (PropertyFluidListViewItem)element;
item.Update(i, itemsSource[i]);
item.OnRemoveButtonClick += itemProperty =>
{
int propertyIndex = 0;
for (int j = 0; j < arrayProperty.arraySize; j++)
{
if (itemProperty.propertyPath != arrayProperty.GetArrayElementAtIndex(j).propertyPath)
continue;
propertyIndex = j;
break;
}
arrayProperty.DeleteArrayElementAtIndex(propertyIndex);
arrayProperty.serializedObject.ApplyModifiedProperties();
UpdateItemsSource();
};
};
//ADD ITEM BUTTON (plus button)
fluidListView.addItemButton.SetTooltip($"Add a new Trigger for the '{actionNameProperty.stringValue}' Action");
fluidListView.AddNewItemButtonCallback += () =>
{
arrayProperty.InsertArrayElementAtIndex(0);
arrayProperty.serializedObject.ApplyModifiedProperties();
UpdateItemsSource();
};
UpdateItemsSource();
int arraySize = arrayProperty.arraySize;
fluidListView.schedule.Execute(() =>
{
if (arrayProperty.arraySize == arraySize) return;
arraySize = arrayProperty.arraySize;
UpdateItemsSource();
}).Every(100);
void UpdateItemsSource()
{
itemsSource.Clear();
for (int i = 0; i < arrayProperty.arraySize; i++)
itemsSource.Add(arrayProperty.GetArrayElementAtIndex(i));
fluidListView.Update();
}
return fluidListView;
}
protected static VisualElement GetTriggersField(SerializedProperty property)
{
SerializedProperty signalReceiversProperty = property.FindPropertyRelative("SignalsReceivers");
SerializedProperty actionNameProperty = property.FindPropertyRelative("ActionName");
var container = new VisualElement();
FluidField field = FluidField.Get();
container.Add(field);
container.RegisterCallback<AttachToPanelEvent>(evt =>
{
Undo.undoRedoPerformed -= RefreshFieldContent;
Undo.undoRedoPerformed += RefreshFieldContent;
});
container.RegisterCallback<DetachFromPanelEvent>(evt => Undo.undoRedoPerformed -= RefreshFieldContent);
RefreshFieldContent();
void RefreshFieldContent()
{
field.ClearFieldContent();
property.serializedObject.Update();
field.AddFieldContent
(
DesignUtils.row
.AddChild
(
new Label($"Action Triggers")
.ResetLayout()
.SetStyleMarginLeft(DesignUtils.k_Spacing * 2)
.SetStyleAlignSelf(Align.Center)
.SetStyleFontSize(10)
.SetStyleUnityFont(DesignUtils.fieldNameTextFont)
)
.AddChild(DesignUtils.flexibleSpace)
.AddChild
(
FluidButton.Get()
.SetIcon(EditorSpriteSheets.EditorUI.Icons.Plus)
.SetAccentColor(EditorSelectableColors.Default.Add)
.SetElementSize(ElementSize.Small)
.SetTooltip($"Add a new Trigger for the '{actionNameProperty.stringValue}' Action")
.SetOnClick(() =>
{
signalReceiversProperty.InsertArrayElementAtIndex(0);
property.serializedObject.ApplyModifiedProperties();
RefreshFieldContent();
})
)
);
for (int i = 0; i < signalReceiversProperty.arraySize; i++)
{
int index = i;
SerializedProperty arrayElementAtIndex = signalReceiversProperty.GetArrayElementAtIndex(index);
field.AddFieldContent
(
DesignUtils.row
.SetStyleMarginBottom(DesignUtils.k_Spacing / 2f)
.AddChild(DesignUtils.NewPropertyField(arrayElementAtIndex.propertyPath))
.AddChild
(
FluidButton.Get()
.SetIcon(EditorSpriteSheets.EditorUI.Icons.Minus)
.SetAccentColor(EditorSelectableColors.Default.Remove)
.SetStyleAlignSelf(Align.Center)
.SetElementSize(ElementSize.Small)
.SetTooltip($"Remove Trigger")
.SetOnClick(() =>
{
signalReceiversProperty.DeleteArrayElementAtIndex(index);
property.serializedObject.ApplyModifiedProperties();
RefreshFieldContent();
}))
);
}
field.Bind(property.serializedObject);
}
return container;
}
private static FluidField TimeField(SerializedProperty property, string targetPropertyName, Color enabledColor, string fieldLabelText, string fieldTooltip, IEnumerable<Texture2D> fieldIconTextures)
{
SerializedProperty targetProperty = property.FindPropertyRelative(targetPropertyName);
EnabledIndicator indicator = EnabledIndicator.Get().SetIcon(fieldIconTextures).SetEnabledColor(enabledColor).SetSize(18);
FloatField floatField = new FloatField().ResetLayout().BindToProperty(targetProperty).SetStyleFlexGrow(1);
floatField.RegisterValueChangedCallback(evt => indicator.Toggle(evt.newValue > 0, true));
indicator.Toggle(targetProperty.floatValue > 0, false);
return FluidField.Get()
.SetLabelText(fieldLabelText)
.SetTooltip(fieldTooltip)
.SetElementSize(ElementSize.Small)
.AddFieldContent
(
DesignUtils.row.SetStyleFlexGrow(0)
.AddChild(indicator.SetStyleMarginRight(DesignUtils.k_Spacing))
.AddChild(floatField)
);
}
protected static FluidField GetStartDelayFluidField(SerializedProperty property) =>
TimeField
(
property,
"ActionStartDelay",
startDelayColor,
"Start Delay",
"Time interval before the Action executes its task, after it started running",
EditorSpriteSheets.EditorUI.Icons.StartDelay
);
protected static FluidField GetDurationFluidField(SerializedProperty property) =>
TimeField
(
property,
"ActionDuration",
runningColor,
"Duration",
"Running time from start to finish. Does not include StartDelay. At 0 (zero) the Action's task happens instantly",
EditorSpriteSheets.EditorUI.Icons.Duration
);
protected static FluidField GetCooldownFluidField(SerializedProperty property) =>
TimeField
(
property,
"ActionCooldown",
cooldownColor,
"Cooldown",
"Cooldown time after the Action ran. During this time, the Action cannot Start running again",
EditorSpriteSheets.EditorUI.Icons.Cooldown
);
protected static FluidField GetTimescaleFluidField(SerializedProperty property)
{
SerializedProperty targetProperty = property.FindPropertyRelative("ActionTimescale");
EnumField enumField =
new EnumField()
.ResetLayout()
.BindToProperty(targetProperty)
.SetStyleMinWidth(94);
FluidField field =
FluidField.Get()
.SetLabelText("Timescale")
.SetTooltip
(
"Determine if the Action's timers will be affected by the application's timescale" +
"\n\nTimescale.Independent - (Realtime)\nNot affected by the application's timescale value" +
"\n\nTimescale.Dependent - (Application Time)\nAffected by the application's timescale value"
)
.SetElementSize(ElementSize.Small)
.SetIcon(EditorSpriteSheets.EditorUI.Icons.TimeScale)
.AddFieldContent(enumField);
return field;
}
protected static FluidField GetStopAllActionsOnStartFluidField(SerializedProperty property)
{
SerializedProperty targetProperty = property.FindPropertyRelative("ActionOnStartStopOtherActions");
FluidToggleCheckbox checkbox =
FluidToggleCheckbox.Get()
.SetLabelText("Stop Other Actions")
.BindToProperty(targetProperty.propertyPath);
FluidField field =
FluidField.Get()
.SetLabelText("OnStart")
.SetTooltip("Stop for all other Actions, on this Module (MonoBehaviour), when this Action starts running")
.SetElementSize(ElementSize.Small)
.AddFieldContent(checkbox);
return field;
}
private static FluidField GetOnStartOrOnFinishFluidField(SerializedProperty property, string targetPropertyName) =>
FluidField.Get<PropertyField>(property.FindPropertyRelative(targetPropertyName))
.SetElementSize(ElementSize.Small);
protected static FluidField GetOnStartFluidField(SerializedProperty property) =>
GetOnStartOrOnFinishFluidField(property, "OnStartEvents")
.SetLabelText("OnStart Events - triggered when this Action starts running")
.SetIcon(EditorSpriteSheets.EditorUI.Icons.EventsOnStart);
protected static FluidField GetOnFinishPropertyField(SerializedProperty property) =>
GetOnStartOrOnFinishFluidField(property, "OnFinishEvents")
.SetLabelText("OnFinish Events - triggered when this Action finished running")
.SetIcon(EditorSpriteSheets.EditorUI.Icons.EventsOnFinish);
}
}