Unity-jump-proj

This commit is contained in:
2024-09-09 11:07:16 +03:00
parent 2c29906bbf
commit fd96a5627d
13707 changed files with 866380 additions and 0 deletions

View File

@ -0,0 +1,28 @@
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline.Signals
{
[CustomEditor(typeof(SignalAsset))]
class SignalAssetInspector : Editor
{
[MenuItem("Assets/Create/Signal", false, 451)]
[UsedImplicitly]
public static void CreateNewSignal()
{
var icon = EditorGUIUtility.IconContent("SignalAsset Icon").image as Texture2D;
ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, CreateInstance<DoCreateSignalAsset>(), "New Signal.signal", icon, null);
}
class DoCreateSignalAsset : ProjectWindowCallback.EndNameEditAction
{
public override void Action(int instanceId, string pathName, string resourceFile)
{
var signalAsset = CreateInstance<SignalAsset>();
AssetDatabase.CreateAsset(signalAsset, pathName);
ProjectWindowUtil.ShowCreatedAsset(signalAsset);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 58004290eb3aab44e9823d1f25c4ed73
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,23 @@
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline.Signals
{
[CustomTimelineEditor(typeof(SignalEmitter))]
class SignalEmitterEditor : MarkerEditor
{
static readonly string MissingAssetError = L10n.Tr("No signal assigned");
public override MarkerDrawOptions GetMarkerOptions(IMarker marker)
{
var options = base.GetMarkerOptions(marker);
SignalEmitter emitter = (SignalEmitter)marker;
if (emitter.asset != null)
options.tooltip = emitter.asset.name;
else
options.errorText = MissingAssetError;
return options;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 51a7878f6c989394782db73339e90e46
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,377 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
using UnityObject = UnityEngine.Object;
namespace UnityEditor.Timeline.Signals
{
[CustomEditor(typeof(SignalEmitter), true)]
[CanEditMultipleObjects]
class SignalEmitterInspector : MarkerInspector, ISignalAssetProvider
{
SerializedProperty m_RetroactiveProperty;
SerializedProperty m_EmitOnceProperty;
SignalEmitter m_Signal;
GameObject m_BoundGameObject;
PlayableDirector m_AssociatedDirector;
bool m_TargetsHaveTheSameBinding;
readonly Dictionary<Component, Editor> m_Editors = new Dictionary<Component, Editor>();
readonly Dictionary<Component, bool> m_Foldouts = new Dictionary<Component, bool>();
List<Component> m_Receivers = new List<Component>();
static GUIStyle s_FoldoutStyle;
internal static GUIStyle foldoutStyle
{
get
{
if (s_FoldoutStyle == null)
{
s_FoldoutStyle = new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold };
}
return s_FoldoutStyle;
}
}
public SignalAsset signalAsset
{
get
{
var emitter = target as SignalEmitter;
return signalAssetSameValue ? emitter.asset : null;
}
set
{
AssignSignalAsset(value);
}
}
bool signalAssetSameValue
{
get
{
var emitters = targets.Cast<SignalEmitter>().ToList();
return emitters.Select(x => x.asset).Distinct().Count() == 1;
}
}
void OnEnable()
{
Undo.undoRedoPerformed += OnUndoRedo; // subscribe to the event
m_Signal = target as SignalEmitter;
m_RetroactiveProperty = serializedObject.FindProperty("m_Retroactive");
m_EmitOnceProperty = serializedObject.FindProperty("m_EmitOnce");
// In a vast majority of the cases, when this becomes enabled,
// the timeline window will be focused on the correct timeline
// in which case TimelineEditor.inspectedDirector is safe to use
m_AssociatedDirector = TimelineEditor.inspectedDirector;
UpdateState();
}
internal override bool IsEnabled()
{
return TimelineUtility.IsCurrentSequenceValid() && !IsCurrentSequenceReadOnly() && base.IsEnabled();
}
public override void OnInspectorGUI()
{
serializedObject.Update();
using (var changeScope = new EditorGUI.ChangeCheckScope())
{
var property = serializedObject.GetIterator();
var expanded = true;
while (property.NextVisible(expanded))
{
expanded = false;
if (SkipField(property.propertyPath))
continue;
EditorGUILayout.PropertyField(property, true);
}
DrawSignalFlags();
UpdateState();
DrawNameSelectorAndSignalList();
if (changeScope.changed)
{
serializedObject.ApplyModifiedProperties();
TimelineEditor.Refresh(RefreshReason.ContentsModified | RefreshReason.WindowNeedsRedraw);
}
}
}
internal override void OnHeaderIconGUI(Rect iconRect)
{
using (new EditorGUI.DisabledScope(!TimelineUtility.IsCurrentSequenceValid() || IsCurrentSequenceReadOnly()))
{
GUI.Label(iconRect, Styles.SignalEmitterIcon);
}
}
internal override Rect DrawHeaderHelpAndSettingsGUI(Rect r)
{
using (new EditorGUI.DisabledScope(!TimelineUtility.IsCurrentSequenceValid() || IsCurrentSequenceReadOnly()))
{
var helpSize = EditorStyles.iconButton.CalcSize(EditorGUI.GUIContents.helpIcon);
const int kTopMargin = 5;
return EditorGUIUtility.DrawEditorHeaderItems(new Rect(r.xMax - helpSize.x, r.y + kTopMargin, helpSize.x, helpSize.y), targets);
}
}
IEnumerable<SignalAsset> ISignalAssetProvider.AvailableSignalAssets()
{
return SignalManager.assets;
}
void ISignalAssetProvider.CreateNewSignalAsset(string path)
{
var newSignalAsset = SignalManager.CreateSignalAssetInstance(path);
AssignSignalAsset(newSignalAsset);
var receivers = m_Receivers.OfType<SignalReceiver>().ToList();
if (signalAsset != null && receivers.Count == 1 && !receivers.Any(r => r.IsSignalAssetHandled(newSignalAsset))) // Only when one receiver is present
{
receivers[0].AddNewReaction(newSignalAsset); // Add reaction on the first receiver from the list
ApplyChangesAndRefreshReceivers();
}
}
void UpdateState()
{
m_BoundGameObject = GetBoundGameObject(m_Signal.parent, m_AssociatedDirector);
m_Receivers = m_BoundGameObject == null || m_BoundGameObject.Equals(null)
? new List<Component>()
: m_BoundGameObject.GetComponents<Component>().Where(t => t is INotificationReceiver).ToList();
m_TargetsHaveTheSameBinding = targets.Cast<SignalEmitter>()
.Select(x => GetBoundGameObject(x.parent, m_AssociatedDirector))
.Distinct().Count() == 1;
}
Editor GetOrCreateReceiverEditor(Component c)
{
Editor ret;
if (m_Editors.TryGetValue(c, out ret))
{
return ret;
}
ret = CreateEditorWithContext(new Object[] { c }, target);
m_Editors[c] = ret;
if (!m_Foldouts.ContainsKey(c))
{
m_Foldouts[c] = true;
}
return ret;
}
void OnDisable()
{
Undo.undoRedoPerformed -= OnUndoRedo;
}
void OnDestroy()
{
foreach (var editor in m_Editors)
{
DestroyImmediate(editor.Value);
}
m_Editors.Clear();
}
void OnUndoRedo()
{
ApplyChangesAndRefreshReceivers();
}
void ApplyChangesAndRefreshReceivers()
{
foreach (var receiverInspector in m_Editors.Values.OfType<SignalReceiverInspector>())
{
receiverInspector.SetAssetContext(signalAsset);
}
}
void DrawNameSelectorAndSignalList()
{
using (var change = new EditorGUI.ChangeCheckScope())
{
DrawSignal();
DrawReceivers();
if (change.changed)
{
ApplyChangesAndRefreshReceivers();
}
}
}
void DrawReceivers()
{
if (!m_TargetsHaveTheSameBinding)
{
EditorGUILayout.HelpBox(Styles.MultiEditNotSupportedOnDifferentBindings, MessageType.None);
return;
}
if (targets.OfType<SignalEmitter>().Select(x => x.asset).Distinct().Count() > 1)
{
EditorGUILayout.HelpBox(Styles.MultiEditNotSupportedOnDifferentSignals, MessageType.None);
return;
}
//do not display the receiver if the current timeline is not the same as the emitter's timeline
//can happen if the inspector is locked
if (m_Signal.parent != null && m_Signal.parent.timelineAsset != TimelineEditor.inspectedAsset)
return;
if (m_BoundGameObject != null)
{
if (!m_Receivers.Any(x => x is SignalReceiver))
{
EditorGUILayout.Separator();
var message = string.Format(Styles.NoSignalReceiverComponent, m_BoundGameObject.name);
SignalUtility.DrawCenteredMessage(message);
if (SignalUtility.DrawCenteredButton(Styles.AddSignalReceiverComponent))
AddReceiverComponent();
}
foreach (var receiver in m_Receivers)
{
var editor = GetOrCreateReceiverEditor(receiver);
if (DrawReceiverHeader(receiver))
{
editor.OnInspectorGUI();
}
}
}
else if (m_AssociatedDirector != null) //not in asset mode
{
EditorGUILayout.HelpBox(Styles.NoBoundGO, MessageType.None);
}
}
void DrawSignalFlags()
{
EditorGUILayout.PropertyField(m_RetroactiveProperty, Styles.RetroactiveLabel);
EditorGUILayout.PropertyField(m_EmitOnceProperty, Styles.EmitOnceLabel);
}
void DrawSignal()
{
//should show button to create new signal if there are no signals asset in the project
if (!SignalManager.assets.Any())
{
using (new EditorGUI.DisabledScope(true))
{
DrawNameSelector();
}
EditorGUILayout.Separator();
SignalUtility.DrawCenteredMessage(Styles.ProjectHasNoSignalAsset);
if (SignalUtility.DrawCenteredButton(Styles.CreateNewSignal))
CreateNewSignalAsset(SignalUtility.GetNewSignalPath());
EditorGUILayout.Separator();
}
else
{
DrawNameSelector();
}
}
internal void CreateNewSignalAsset(string path)
{
if (!string.IsNullOrEmpty(path))
((ISignalAssetProvider)this).CreateNewSignalAsset(path);
GUIUtility.ExitGUI();
}
void AssignSignalAsset(SignalAsset newAsset)
{
foreach (var o in targets)
{
var signalEmitter = (SignalEmitter)o;
UndoExtensions.RegisterMarker(signalEmitter, Styles.UndoCreateSignalAsset);
signalEmitter.asset = newAsset;
}
}
void DrawNameSelector()
{
SignalUtility.DrawSignalNames(this, EditorGUILayout.GetControlRect(), Styles.EmitSignalLabel, !signalAssetSameValue);
}
bool DrawReceiverHeader(Component receiver)
{
EditorGUILayout.Space();
var lineRect = GUILayoutUtility.GetRect(10, 4, EditorStyles.inspectorTitlebar);
DrawSplitLine(lineRect.y);
var style = EditorGUIUtility.TrTextContentWithIcon(
ObjectNames.NicifyVariableName(receiver.GetType().Name),
AssetPreview.GetMiniThumbnail(receiver));
m_Foldouts[receiver] =
EditorGUILayout.Foldout(m_Foldouts[receiver], style, true, foldoutStyle);
if (m_Foldouts[receiver])
{
DrawReceiverObjectField();
}
return m_Foldouts[receiver];
}
void DrawReceiverObjectField()
{
EditorGUI.BeginDisabledGroup(true);
EditorGUILayout.ObjectField(Styles.ObjectLabel, m_BoundGameObject, typeof(GameObject), false);
EditorGUI.EndDisabledGroup();
}
void AddReceiverComponent()
{
var receiver = Undo.AddComponent<SignalReceiver>(m_BoundGameObject);
receiver.AddNewReaction(signalAsset);
}
static bool SkipField(string fieldName)
{
return fieldName == "m_Script" || fieldName == "m_Asset" || fieldName == "m_Retroactive" || fieldName == "m_EmitOnce";
}
static void DrawSplitLine(float y)
{
if (Event.current.type != EventType.Repaint) return;
var width = EditorGUIUtility.currentViewWidth;
var position = new Rect(0, y, width + 1, 1);
if (EditorStyles.inspectorTitlebar != null)
EditorStyles.inspectorTitlebar.Draw(position, false, false, false, false);
}
static GameObject GetBoundGameObject(TrackAsset track, PlayableDirector associatedDirector)
{
if (associatedDirector == null || track == null) //if in asset mode, no bound object for you
return null;
var boundObj = TimelineUtility.GetSceneGameObject(associatedDirector, track);
//if the signal is on the timeline marker track and user did not set a binding, assume it's bound to PlayableDirector
if (boundObj == null && track.timelineAsset.markerTrack == track)
boundObj = associatedDirector.gameObject;
return boundObj;
}
static bool IsCurrentSequenceReadOnly()
{
return TimelineWindow.instance.state.editSequence.isReadOnly;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 970e7735a0864fd40842a36d053d08fe
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,39 @@
using System;
using JetBrains.Annotations;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline.Signals
{
[CustomPropertyDrawer(typeof(CustomSignalEventDrawer))]
[UsedImplicitly]
class SignalEventDrawer : UnityEventDrawer
{
static GameObject FindBoundObject(SerializedProperty property)
{
var component = property.serializedObject.targetObject as Component;
return component != null ? component.gameObject : null;
}
protected override void OnAddEvent(ReorderableList list)
{
base.OnAddEvent(list);
var listProperty = list.serializedProperty;
if (listProperty.arraySize > 0)
{
var lastCall = list.serializedProperty.GetArrayElementAtIndex(listProperty.arraySize - 1);
var targetProperty = lastCall.FindPropertyRelative(kInstancePath);
targetProperty.objectReferenceValue = FindBoundObject(listProperty);
}
}
protected override void DrawEventHeader(Rect headerRect) { }
protected override void SetupReorderableList(ReorderableList list)
{
base.SetupReorderableList(list);
list.headerHeight = 4;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6ba7805325c426c43b8e85b5be4eae36
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline.Signals
{
class SignalManager : IDisposable
{
static SignalManager m_Instance;
readonly List<SignalAsset> m_assets = new List<SignalAsset>();
internal static SignalManager instance
{
get
{
if (m_Instance == null)
{
m_Instance = new SignalManager();
m_Instance.Refresh();
}
return m_Instance;
}
set { m_Instance = value; }
}
internal SignalManager()
{
SignalAsset.OnEnableCallback += Register;
}
public static IEnumerable<SignalAsset> assets
{
get
{
foreach (var asset in instance.m_assets)
{
if (asset != null)
yield return asset;
}
}
}
public static SignalAsset CreateSignalAssetInstance(string path)
{
var newSignal = ScriptableObject.CreateInstance<SignalAsset>();
newSignal.name = Path.GetFileNameWithoutExtension(path);
var asset = AssetDatabase.LoadMainAssetAtPath(path) as SignalAsset;
if (asset != null)
{
TimelineUndo.PushUndo(asset, Styles.UndoCreateSignalAsset);
EditorUtility.CopySerialized(newSignal, asset);
Object.DestroyImmediate(newSignal);
return asset;
}
AssetDatabase.CreateAsset(newSignal, path);
return newSignal;
}
public void Dispose()
{
SignalAsset.OnEnableCallback -= Register;
}
void Register(SignalAsset a)
{
m_assets.Add(a);
}
void Refresh()
{
var guids = AssetDatabase.FindAssets("t:SignalAsset");
foreach (var g in guids)
{
var path = AssetDatabase.GUIDToAssetPath(g);
var asset = AssetDatabase.LoadAssetAtPath<SignalAsset>(path);
m_assets.Add(asset);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 255b0c6d400fd964dab3029c8abc53f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,15 @@
using UnityEditor.IMGUI.Controls;
using UnityEngine;
namespace UnityEditor.Timeline.Signals
{
class SignalReceiverHeader : MultiColumnHeader
{
public SignalReceiverHeader(MultiColumnHeaderState state) : base(state) { }
protected override void AddColumnHeaderContextMenuItems(GenericMenu menu)
{
menu.AddItem(L10n.TextContent("Resize to Fit"), false, ResizeToFit);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 423cd382a7804414d9bfdb2e7fb7bb62
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,116 @@
using UnityEngine;
using UnityObject = UnityEngine.Object;
using UnityEditor.IMGUI.Controls;
using UnityEngine.Events;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline.Signals
{
[CustomEditor(typeof(SignalReceiver))]
class SignalReceiverInspector : Editor
{
SignalReceiver m_Target;
[SerializeField] TreeViewState m_TreeState;
[SerializeField] MultiColumnHeaderState m_MultiColumnHeaderState;
internal SignalReceiverTreeView m_TreeView;
SignalEmitter signalEmitterContext
{
get { return m_Context as SignalEmitter; }
}
void OnEnable()
{
m_Target = target as SignalReceiver;
InitTreeView(serializedObject);
Undo.undoRedoPerformed += OnUndoRedo;
}
void OnDisable()
{
Undo.undoRedoPerformed -= OnUndoRedo;
}
void OnUndoRedo()
{
m_TreeView.dirty = true;
}
public override void OnInspectorGUI()
{
serializedObject.Update();
using (var changeCheck = new EditorGUI.ChangeCheckScope())
{
m_TreeView.RefreshIfDirty();
DrawEmitterControls(); // Draws buttons coming from the Context (SignalEmitter)
EditorGUILayout.Space();
m_TreeView.Draw();
if (signalEmitterContext == null)
DrawAddRemoveButtons();
if (changeCheck.changed)
{
serializedObject.ApplyModifiedProperties();
m_TreeView.dirty = true;
}
}
}
void DrawEmitterControls()
{
var context = signalEmitterContext;
if (context != null)
{
var currentSignal = context.asset;
if (currentSignal != null && !m_Target.IsSignalAssetHandled(currentSignal))
{
EditorGUILayout.Separator();
var message = string.Format(Styles.NoReaction, currentSignal.name);
SignalUtility.DrawCenteredMessage(message);
if (SignalUtility.DrawCenteredButton(Styles.AddReactionButton))
m_Target.AddNewReaction(currentSignal); // Add reaction on the first
EditorGUILayout.Separator();
}
}
}
internal void SetAssetContext(SignalAsset asset)
{
m_TreeView.SetSignalContext(asset);
}
void DrawAddRemoveButtons()
{
using (new GUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (GUILayout.Button(Styles.AddReactionButton))
{
Undo.RecordObject(m_Target, Styles.UndoAddReaction);
m_Target.AddEmptyReaction(new UnityEvent());
PrefabUtility.RecordPrefabInstancePropertyModifications(m_Target);
}
GUILayout.Space(18.0f);
}
}
void InitTreeView(SerializedObject so)
{
m_TreeState = SignalListFactory.CreateViewState();
m_MultiColumnHeaderState = SignalListFactory.CreateHeaderState();
var header = SignalListFactory.CreateHeader(m_MultiColumnHeaderState, SignalReceiverUtility.headerHeight);
var context = signalEmitterContext;
m_TreeView = SignalListFactory.CreateSignalInspectorList(m_TreeState, header, m_Target, so);
m_TreeView.readonlySignals = context != null;
if (context != null)
m_TreeView.SetSignalContext(context.asset);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 70b265ed18dc14041bedc0263d4578ef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline.Signals
{
interface ISignalAssetProvider
{
SignalAsset signalAsset { get; set; }
IEnumerable<SignalAsset> AvailableSignalAssets();
void CreateNewSignalAsset(string path);
}
static class SignalUtility
{
const string k_SignalExtension = "signal";
public static void DrawSignalNames(ISignalAssetProvider assetProvider, Rect position, GUIContent label, bool multipleValues)
{
var assets = assetProvider.AvailableSignalAssets().ToList();
var index = assets.IndexOf(assetProvider.signalAsset);
var availableNames = new List<string>();
using (new GUIMixedValueScope(multipleValues))
{
availableNames.Add(Styles.EmptySignalList.text);
availableNames.AddRange(assets.Select(x => x.name));
availableNames.Add(Styles.CreateNewSignal.text);
var curValue = index + 1;
var selected = EditorGUI.Popup(position, label, curValue, availableNames.ToArray());
if (selected != curValue)
{
var noneEntryIdx = 0;
if (selected == noneEntryIdx) // None
assetProvider.signalAsset = null;
else if (selected == availableNames.Count - 1) // "Create New Asset"
{
var path = GetNewSignalPath();
if (!string.IsNullOrEmpty(path))
assetProvider.CreateNewSignalAsset(path);
GUIUtility.ExitGUI();
}
else
assetProvider.signalAsset = assets[selected - 1];
}
}
}
public static string GetNewSignalPath()
{
return EditorUtility.SaveFilePanelInProject(
Styles.NewSignalWindowTitle.text,
Styles.NewSignalDefaultName.text,
k_SignalExtension,
Styles.NewSignalWindowMessage.text);
}
public static bool IsSignalAssetHandled(this SignalReceiver receiver, SignalAsset asset)
{
return receiver != null && asset != null && receiver.GetRegisteredSignals().Contains(asset);
}
public static void AddNewReaction(this SignalReceiver receiver, SignalAsset signalAsset)
{
if (signalAsset != null && receiver != null)
{
Undo.RecordObject(receiver, Styles.UndoAddReaction);
var newEvent = new UnityEvent();
newEvent.AddPersistentListener();
var evtIndex = newEvent.GetPersistentEventCount() - 1;
newEvent.RegisterVoidPersistentListenerWithoutValidation(evtIndex, receiver.gameObject, string.Empty);
receiver.AddReaction(signalAsset, newEvent);
PrefabUtility.RecordPrefabInstancePropertyModifications(receiver);
}
}
public static void DrawCenteredMessage(string message)
{
using (new GUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
GUILayout.Label(message);
GUILayout.FlexibleSpace();
}
}
public static bool DrawCenteredButton(GUIContent buttonLabel)
{
bool buttonClicked;
using (new GUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
buttonClicked = GUILayout.Button(buttonLabel);
GUILayout.FlexibleSpace();
}
return buttonClicked;
}
}
static class SignalReceiverUtility
{
const int k_DefaultTreeviewHeaderHeight = 20;
public static int headerHeight
{
get { return k_DefaultTreeviewHeaderHeight; }
}
public static SerializedProperty FindSignalsProperty(SerializedObject obj)
{
return obj.FindProperty("m_Events.m_Signals");
}
public static SerializedProperty FindEventsProperty(SerializedObject obj)
{
return obj.FindProperty("m_Events.m_Events");
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b33687803b08daf418e5315de17658b8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,39 @@
using UnityEngine;
namespace UnityEditor.Timeline.Signals
{
static class Styles
{
public static readonly GUIContent RetroactiveLabel = L10n.TextContent("Retroactive", "Use retroactive to emit this signal even if playback starts afterwards.");
public static readonly GUIContent EmitOnceLabel = L10n.TextContent("Emit Once", "Emit the signal once during loops.");
public static readonly GUIContent EmitSignalLabel = L10n.TextContent("Emit Signal", "Select which Signal Asset to emit.");
public static readonly GUIContent ObjectLabel = L10n.TextContent("Receiver Component on", "The Signal Receiver Component on the bound GameObject.");
public static readonly GUIContent CreateNewSignal = L10n.TextContent("Create Signal...");
public static readonly GUIContent AddSignalReceiverComponent = L10n.TextContent("Add Signal Receiver", "Creates a Signal Receiver component on the track binding and the reaction for the current signal.");
public static readonly GUIContent EmptySignalList = L10n.TextContent("None");
public static readonly GUIContent AddReactionButton = L10n.TextContent("Add Reaction");
public static readonly GUIContent NewSignalWindowTitle = L10n.TextContent("Create Signal Key");
public static readonly GUIContent NewSignalDefaultName = L10n.TextContent("New Signal");
public static readonly GUIContent NewSignalWindowMessage = L10n.TextContent("Create Signal Key");
public static readonly string SignalListDuplicateOption = L10n.Tr("Duplicate");
public static readonly string SignalListDeleteOption = L10n.Tr("Delete");
public static readonly string NoBoundGO = L10n.Tr("Track has no bound GameObject.");
public static readonly string MultiEditNotSupportedOnDifferentBindings = L10n.Tr("Multi-edit not supported for SignalReceivers on tracks bound to different GameObjects.");
public static readonly string MultiEditNotSupportedOnDifferentSignals = L10n.Tr("Multi-edit not supported for SignalReceivers when SignalEmitters use different Signals.");
public static readonly string UndoCreateSignalAsset = L10n.Tr("Create New Signal Asset");
public static readonly string UndoDuplicateRow = L10n.Tr("Duplicate Row");
public static readonly string UndoDeleteRow = L10n.Tr("Delete Row");
public static readonly string UndoAddReaction = L10n.Tr("Add Signal Receiver Reaction");
public static readonly string NoReaction = L10n.Tr("No reaction for {0} has been defined in this receiver");
public static readonly string NoSignalReceiverComponent = L10n.Tr("There is no Signal Receiver component on {0}");
public static readonly string ProjectHasNoSignalAsset = L10n.Tr("Your project contains no Signal assets");
//Icons
public static readonly GUIStyle OptionsStyle = DirectorStyles.GetGUIStyle("Icon.Options");
public static readonly GUIContent SignalEmitterIcon = EditorGUIUtility.IconContent("SignalEmitter Icon");
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 685f63932bebd0c4db02ee14845191e2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 5ba130fc1db953547a50bcf5c162a3e8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,31 @@
using System;
using UnityEditor.IMGUI.Controls;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline.Signals
{
static class SignalListFactory
{
public static SignalReceiverTreeView CreateSignalInspectorList(TreeViewState state, SignalReceiverHeader header, SignalReceiver target, SerializedObject so)
{
return new SignalReceiverTreeView(state, header, target, so);
}
public static SignalReceiverHeader CreateHeader(MultiColumnHeaderState state, int columnHeight)
{
var header = new SignalReceiverHeader(state) { height = columnHeight };
header.ResizeToFit();
return header;
}
public static MultiColumnHeaderState CreateHeaderState()
{
return new MultiColumnHeaderState(SignalReceiverTreeView.GetColumns());
}
public static TreeViewState CreateViewState()
{
return new TreeViewState();
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 01cd96d8687272f4898cfd1562079dd7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Timeline;
using Object = UnityEngine.Object;
namespace UnityEditor.Timeline.Signals
{
class SignalReceiverItem : TreeViewItem, ISignalAssetProvider
{
static readonly SignalEventDrawer k_EvtDrawer = new SignalEventDrawer();
readonly SerializedProperty m_Asset;
readonly SerializedProperty m_Evt;
readonly SignalReceiverTreeView m_TreeView;
int m_CurrentRowIdx;
SignalReceiver m_CurrentReceiver;
internal readonly bool enabled;
internal readonly bool readonlySignal;
internal const string SignalName = "SignalName";
internal const string SignalNameReadOnly = "SignalNameReadOnly";
internal const string SignalOptions = "SignalOptions";
public SignalReceiverItem(SerializedProperty signalAsset, SerializedProperty eventListEntry, int id, bool readonlySignal, bool enabled, SignalReceiverTreeView treeView)
: base(id, 0)
{
m_Asset = signalAsset;
m_Evt = eventListEntry;
this.enabled = enabled;
this.readonlySignal = readonlySignal;
m_TreeView = treeView;
}
public SignalAsset signalAsset
{
get { return m_CurrentReceiver.GetSignalAssetAtIndex(m_CurrentRowIdx); }
set
{
Undo.RecordObject(m_CurrentReceiver, Styles.UndoCreateSignalAsset);
m_CurrentReceiver.ChangeSignalAtIndex(m_CurrentRowIdx, value);
PrefabUtility.RecordPrefabInstancePropertyModifications(m_CurrentReceiver);
}
}
public float GetHeight()
{
return k_EvtDrawer.GetPropertyHeight(m_Evt, GUIContent.none);
}
public void Draw(Rect rect, int colIdx, int rowIdx, float padding, SignalReceiver target)
{
switch (colIdx)
{
case 0:
DrawSignalNameColumn(rect, padding, target, rowIdx);
break;
case 1:
DrawReactionColumn(rect, rowIdx);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
void DrawSignalNameColumn(Rect rect, float padding, SignalReceiver target, int rowIdx)
{
using (new EditorGUI.DisabledScope(!enabled))
{
if (!readonlySignal)
{
m_CurrentRowIdx = rowIdx;
m_CurrentReceiver = target;
rect.x += padding;
rect.width -= padding;
rect.height = EditorGUIUtility.singleLineHeight;
GUI.SetNextControlName(SignalName);
SignalUtility.DrawSignalNames(this, rect, GUIContent.none, false);
}
else
{
GUI.SetNextControlName(SignalNameReadOnly);
var signalAsset = m_Asset.objectReferenceValue;
GUI.Label(rect,
signalAsset != null
? EditorGUIUtility.TempContent(signalAsset.name)
: Styles.EmptySignalList);
}
}
}
void DrawReactionColumn(Rect rect, int rowIdx)
{
if (!readonlySignal)
{
var optionButtonSize = GetOptionButtonSize();
rect.width -= optionButtonSize.x;
var optionButtonRect = new Rect
{
x = rect.xMax,
y = rect.y,
width = optionButtonSize.x,
height = optionButtonSize.y
};
DrawOptionsButton(optionButtonRect, rowIdx, m_CurrentReceiver);
}
using (new EditorGUI.DisabledScope(!enabled))
{
var nameAsString = m_Asset.objectReferenceValue == null ? "Null" : m_Asset.objectReferenceValue.name;
using (var change = new EditorGUI.ChangeCheckScope())
{
EditorGUI.PropertyField(rect, m_Evt, EditorGUIUtility.TempContent(nameAsString));
if (change.changed)
m_TreeView.dirty = true;
}
}
}
static Vector2 GetOptionButtonSize()
{
EditorGUIUtility.SetIconSize(Vector2.zero);
return EditorStyles.iconButton.CalcSize(EditorGUI.GUIContents.titleSettingsIcon);
}
void DrawOptionsButton(Rect rect, int rowIdx, SignalReceiver target)
{
GUI.SetNextControlName(SignalOptions);
if (EditorGUI.DropdownButton(rect, EditorGUI.GUIContents.titleSettingsIcon, FocusType.Passive, EditorStyles.iconButton))
{
var menu = new GenericMenu();
menu.AddItem(new GUIContent(Styles.SignalListDuplicateOption), false, () =>
{
Undo.RecordObject(target, Styles.UndoDuplicateRow);
var evtCloner = ScriptableObject.CreateInstance<UnityEventCloner>();
evtCloner.evt = target.GetReactionAtIndex(rowIdx);
var clone = Object.Instantiate(evtCloner);
target.AddEmptyReaction(clone.evt);
m_TreeView.dirty = true;
PrefabUtility.RecordPrefabInstancePropertyModifications(target);
});
menu.AddItem(new GUIContent(Styles.SignalListDeleteOption), false, () =>
{
Undo.RecordObject(target, Styles.UndoDeleteRow);
target.RemoveAtIndex(rowIdx);
m_TreeView.dirty = true;
PrefabUtility.RecordPrefabInstancePropertyModifications(target);
});
menu.ShowAsContext();
}
}
IEnumerable<SignalAsset> ISignalAssetProvider.AvailableSignalAssets()
{
var ret = SignalManager.assets.Except(m_CurrentReceiver.GetRegisteredSignals());
return signalAsset == null ? ret : ret.Union(new List<SignalAsset> { signalAsset }).ToList();
}
void ISignalAssetProvider.CreateNewSignalAsset(string path)
{
var newSignalAsset = SignalManager.CreateSignalAssetInstance(path);
Undo.RecordObject(m_CurrentReceiver, Styles.UndoCreateSignalAsset);
// case 1241170 - overwriting an existing signal with throw an exception. Instead, clear the old key
int index = m_CurrentReceiver.GetRegisteredSignals().ToList().IndexOf(newSignalAsset);
if (index != -1)
m_CurrentReceiver.ChangeSignalAtIndex(index, null);
m_CurrentReceiver.ChangeSignalAtIndex(m_CurrentRowIdx, newSignalAsset);
PrefabUtility.RecordPrefabInstancePropertyModifications(m_CurrentReceiver);
}
class UnityEventCloner : ScriptableObject
{
public UnityEvent evt;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9bc2b69915879416f8df18971dc98e2e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.Timeline;
namespace UnityEditor.Timeline.Signals
{
class SignalReceiverTreeView : TreeView
{
public bool dirty { private get; set; }
SerializedProperty signals { get; set; }
SerializedProperty events { get; set; }
readonly SignalReceiver m_Target;
const float k_VerticalPadding = 5;
const float k_HorizontalPadding = 5;
public SignalReceiverTreeView(TreeViewState state, MultiColumnHeader multiColumnHeader, SignalReceiver receiver, SerializedObject serializedObject)
: base(state, multiColumnHeader)
{
m_Target = receiver;
useScrollView = true;
SetSerializedProperties(serializedObject);
getNewSelectionOverride = (item, selection, shift) => new List<int>(); // Disable Selection
}
SignalAsset signalAssetContext { get; set; }
public bool readonlySignals { get; set; }
public void SetSignalContext(SignalAsset assetContext = null)
{
signalAssetContext = assetContext;
dirty = true;
}
void SetSerializedProperties(SerializedObject serializedObject)
{
signals = SignalReceiverUtility.FindSignalsProperty(serializedObject);
events = SignalReceiverUtility.FindEventsProperty(serializedObject);
Reload();
}
public void Draw()
{
var rect = EditorGUILayout.GetControlRect(true, GetTotalHeight());
OnGUI(rect);
}
public void RefreshIfDirty()
{
var signalsListSizeHasChanged = signals.arraySize != GetRows().Count;
if (dirty || signalsListSizeHasChanged)
Reload();
dirty = false;
}
public static MultiColumnHeaderState.Column[] GetColumns()
{
return new[]
{
new MultiColumnHeaderState.Column
{
headerContent = L10n.TextContent("Signal"),
contextMenuText = "",
headerTextAlignment = TextAlignment.Center,
width = 50, minWidth = 50,
autoResize = true,
allowToggleVisibility = false,
canSort = false
},
new MultiColumnHeaderState.Column
{
headerContent = L10n.TextContent("Reaction"),
contextMenuText = "",
headerTextAlignment = TextAlignment.Center,
width = 120, minWidth = 120,
autoResize = true,
allowToggleVisibility = false,
canSort = false
}
};
}
protected override TreeViewItem BuildRoot()
{
var root = new TreeViewItem(-1, -1) { children = new List<TreeViewItem>() };
var matchingId = signalAssetContext != null && readonlySignals ? FindIdForSignal(signals, signalAssetContext) : -1;
if (matchingId >= 0)
AddItem(root, matchingId);
for (var i = 0; i < signals.arraySize; ++i)
{
if (i == matchingId) continue;
AddItem(root, i, !readonlySignals);
}
return root;
}
protected override void RowGUI(RowGUIArgs args)
{
var item = (SignalReceiverItem)args.item;
for (var i = 0; i < args.GetNumVisibleColumns(); ++i)
{
var rect = args.GetCellRect(i);
rect.y += k_VerticalPadding;
item.Draw(rect, args.GetColumn(i), args.row, k_HorizontalPadding, m_Target);
}
}
protected override float GetCustomRowHeight(int row, TreeViewItem treeItem)
{
var item = treeItem as SignalReceiverItem;
return item.GetHeight() + k_VerticalPadding;
}
void AddItem(TreeViewItem root, int id, bool enabled = true)
{
var signal = signals.GetArrayElementAtIndex(id);
var evt = events.GetArrayElementAtIndex(id);
root.children.Add(new SignalReceiverItem(signal, evt, id, readonlySignals, enabled, this));
}
float GetTotalHeight()
{
var height = 0.0f;
foreach (var item in GetRows())
{
var signalListItem = item as SignalReceiverItem;
height += signalListItem.GetHeight() + k_VerticalPadding;
}
var scrollbarPadding = showingHorizontalScrollBar ? GUI.skin.horizontalScrollbar.fixedHeight : k_VerticalPadding;
return height + multiColumnHeader.height + scrollbarPadding;
}
static int FindIdForSignal(SerializedProperty signals, SignalAsset signalToFind)
{
for (var i = 0; i < signals.arraySize; ++i)
{
//signal in the receiver that matches the current signal asset will be displayed first
var serializedProperty = signals.GetArrayElementAtIndex(i);
var signalReferenceValue = serializedProperty.objectReferenceValue;
var signalToFindRefValue = signalToFind;
if (signalReferenceValue != null && signalReferenceValue == signalToFindRefValue)
return i;
}
return -1;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 698b660e9477f4f16abad03ec00ce38c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: