A game about forced loneliness, made by TACStudios
1#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_PROJECT_WIDE_ACTIONS 2using System; 3using System.Linq.Expressions; 4using UnityEditor; 5using UnityEditor.UIElements; 6using UnityEngine.UIElements; 7 8namespace UnityEngine.InputSystem.Editor 9{ 10 internal class StateContainer 11 { 12 public event Action<InputActionsEditorState> StateChanged; 13 14 private VisualElement m_RootVisualElement; 15 private InputActionsEditorState m_State; 16 public readonly string assetGUID; 17 18 public StateContainer(InputActionsEditorState initialState, string assetGUID) 19 { 20 m_State = initialState; 21 this.assetGUID = assetGUID; 22 } 23 24 public void Dispatch(Command command) 25 { 26 if (command == null) 27 throw new ArgumentNullException(nameof(command)); 28 29 m_State = command(m_State); 30 31 // why not just invoke the state changed event immediately you ask? The Dispatch method might have 32 // been called from inside a UI element event handler and if we raised the event immediately, a view 33 // might try to redraw itself *during* execution of the event handler. 34 m_RootVisualElement.schedule.Execute(() => 35 { 36 // catch exceptions here or the UIToolkit scheduled event will keep firing forever. 37 try 38 { 39 StateChanged?.Invoke(m_State); 40 } 41 catch (Exception e) 42 { 43 Debug.LogException(e); 44 } 45 }); 46 } 47 48 public void Initialize(VisualElement rootVisualElement) 49 { 50 // We need to use a root element for the TrackSerializedObjectValue that is destroyed with the view. 51 // Using a root element from the settings window would not enable the tracking callback to be destroyed or garbage collected. 52 53 m_RootVisualElement = rootVisualElement; 54 55 m_RootVisualElement.Unbind(); 56 m_RootVisualElement.TrackSerializedObjectValue(m_State.serializedObject, so => 57 { 58 StateChanged?.Invoke(m_State); 59 }); 60 StateChanged?.Invoke(m_State); 61 rootVisualElement.Bind(m_State.serializedObject); 62 } 63 64 /// <summary> 65 /// Return a copy of the state. 66 /// </summary> 67 /// <remarks> 68 /// It can sometimes be necessary to get access to the state outside of a state change event, like for example 69 /// when creating views in response to UI click events. This method is for those times. 70 /// </remarks> 71 /// <returns></returns> 72 public InputActionsEditorState GetState() 73 { 74 return m_State; 75 } 76 77 public void Bind<TValue>(Expression<Func<InputActionsEditorState, ReactiveProperty<TValue>>> expr, 78 Action<InputActionsEditorState> propertyChangedCallback) 79 { 80 WhenChanged(expr, propertyChangedCallback); 81 propertyChangedCallback(m_State); 82 } 83 84 public void Bind(Expression<Func<InputActionsEditorState, SerializedProperty>> expr, 85 Action<SerializedProperty> serializedPropertyChangedCallback) 86 { 87 var propertyGetterFunc = WhenChanged(expr, serializedPropertyChangedCallback); 88 serializedPropertyChangedCallback(propertyGetterFunc(m_State)); 89 } 90 91 public Func<InputActionsEditorState, ReactiveProperty<TValue>> WhenChanged<TValue>(Expression<Func<InputActionsEditorState, ReactiveProperty<TValue>>> expr, 92 Action<InputActionsEditorState> propertyChangedCallback) 93 { 94 var func = ExpressionUtils.CreateGetter(expr); 95 if (func == null) 96 throw new ArgumentException($"Couldn't get property info from expression."); 97 98 var prop = func(m_State); 99 if (prop == null) 100 throw new InvalidOperationException($"ReactiveProperty {expr} has not been assigned."); 101 102 prop.Changed += _ => propertyChangedCallback(m_State); 103 104 return func; 105 } 106 107 public Func<InputActionsEditorState, SerializedProperty> WhenChanged(Expression<Func<InputActionsEditorState, SerializedProperty>> expr, 108 Action<SerializedProperty> serializedPropertyChangedCallback) 109 { 110 var serializedPropertyGetter = ExpressionUtils.CreateGetter(expr); 111 if (serializedPropertyGetter == null) 112 throw new ArgumentException($"Couldn't get property info from expression."); 113 114 var serializedProperty = serializedPropertyGetter(m_State); 115 if (serializedProperty == null) 116 throw new InvalidOperationException($"ReactiveProperty {expr} has not been assigned."); 117 118 m_RootVisualElement.TrackPropertyValue(serializedProperty, serializedPropertyChangedCallback); 119 return serializedPropertyGetter; 120 } 121 } 122} 123 124#endif