|
|
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; TimelineUndo.PushUndo(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; } }}
|