using System; using System.Collections; using System.Linq; using System.Collections.Generic; using UnityEditor; using UnityEngine; using UnityEngine.UIElements; using UnityEditor.ShaderGraph.Drawing; using GraphDataStore = UnityEditor.ShaderGraph.DataStore; namespace UnityEditor.ShaderGraph { class DummyChangeAction : IGraphDataAction { void OnDummyChangeAction(GraphData m_GraphData) { } public Action modifyGraphDataAction => OnDummyChangeAction; } struct SGControllerChangedEvent { public ISGControlledElement target; public SGController controller; public IGraphDataAction change; private bool m_PropagationStopped; void StopPropagation() { m_PropagationStopped = true; } public bool isPropagationStopped => m_PropagationStopped; } class SGControllerEvent { ISGControlledElement target = null; SGControllerEvent(ISGControlledElement controlledTarget) { target = controlledTarget; } } abstract class SGController { public bool m_DisableCalled = false; protected IGraphDataAction DummyChange = new DummyChangeAction(); public virtual void OnDisable() { if (m_DisableCalled) Debug.LogError(GetType().Name + ".Disable called twice"); m_DisableCalled = true; foreach (var element in allChildren) { UnityEngine.Profiling.Profiler.BeginSample(element.GetType().Name + ".OnDisable"); element.OnDisable(); UnityEngine.Profiling.Profiler.EndSample(); } } internal void RegisterHandler(ISGControlledElement handler) { //Debug.Log("RegisterHandler of " + handler.GetType().Name + " on " + GetType().Name ); if (m_EventHandlers.Contains(handler)) Debug.LogError("Handler registered twice"); else { m_EventHandlers.Add(handler); NotifyEventHandler(handler, DummyChange); } } internal void UnregisterHandler(ISGControlledElement handler) { m_EventHandlers.Remove(handler); } protected void NotifyChange(IGraphDataAction changeAction) { var eventHandlers = m_EventHandlers.ToArray(); // Some notification may trigger Register/Unregister so duplicate the collection. foreach (var eventHandler in eventHandlers) { UnityEngine.Profiling.Profiler.BeginSample("NotifyChange:" + eventHandler.GetType().Name); NotifyEventHandler(eventHandler, changeAction); UnityEngine.Profiling.Profiler.EndSample(); } } void NotifyEventHandler(ISGControlledElement eventHandler, IGraphDataAction changeAction) { SGControllerChangedEvent e = new SGControllerChangedEvent(); e.controller = this; e.target = eventHandler; e.change = changeAction; eventHandler.OnControllerChanged(ref e); if (e.isPropagationStopped) return; if (eventHandler is VisualElement) { var element = eventHandler as VisualElement; eventHandler = element.GetFirstOfType(); while (eventHandler != null) { eventHandler.OnControllerChanged(ref e); if (e.isPropagationStopped) break; eventHandler = (eventHandler as VisualElement).GetFirstAncestorOfType(); } } } public void SendEvent(SGControllerEvent e) { var eventHandlers = m_EventHandlers.ToArray(); // Some notification may trigger Register/Unregister so duplicate the collection. foreach (var eventHandler in eventHandlers) { eventHandler.OnControllerEvent(e); } } public abstract void ApplyChanges(); public virtual IEnumerable allChildren { get { return Enumerable.Empty(); } } protected List m_EventHandlers = new List(); } abstract class SGController : SGController { GraphDataStore m_DataStore; protected GraphDataStore DataStore => m_DataStore; protected SGController(T model, GraphDataStore dataStore) { m_Model = model; m_DataStore = dataStore; DataStore.Subscribe += ModelChanged; } protected abstract void RequestModelChange(IGraphDataAction changeAction); protected abstract void ModelChanged(GraphData graphData, IGraphDataAction changeAction); T m_Model; public T Model => m_Model; // Cleanup delegate association before destruction public void Cleanup() { if (m_DataStore == null) return; DataStore.Subscribe -= ModelChanged; m_Model = default; m_DataStore = null; } } abstract class SGViewController : SGController { protected SGViewController(ModelType model, ViewModelType viewModel, GraphDataStore graphDataStore) : base(model, graphDataStore) { m_ViewModel = viewModel; try { // Need ViewModel to be initialized before we call ModelChanged() [as view model might need to update] ModelChanged(DataStore.State, DummyChange); } catch (Exception e) { Debug.Log("Failed to initialize View Controller of type: " + this.GetType() + " due to exception: " + e); } } // Holds data specific to the views this controller is responsible for ViewModelType m_ViewModel; public ViewModelType ViewModel => m_ViewModel; public override void ApplyChanges() { foreach (var controller in allChildren) { controller.ApplyChanges(); } } public virtual void Dispose() { m_EventHandlers.Clear(); m_ViewModel = default; } } }