119 lines
4.5 KiB
C#
119 lines
4.5 KiB
C#
|
#if UNITY_EDITOR && UNITY_INPUT_SYSTEM_UI_TK_ASSET_EDITOR
|
||
|
using System;
|
||
|
using System.Linq.Expressions;
|
||
|
using UnityEditor;
|
||
|
using UnityEditor.UIElements;
|
||
|
using UnityEngine.UIElements;
|
||
|
|
||
|
namespace UnityEngine.InputSystem.Editor
|
||
|
{
|
||
|
internal class StateContainer
|
||
|
{
|
||
|
public event Action<InputActionsEditorState> StateChanged;
|
||
|
|
||
|
private readonly VisualElement m_RootVisualElement;
|
||
|
private InputActionsEditorState m_State;
|
||
|
|
||
|
public StateContainer(VisualElement rootVisualElement, InputActionsEditorState initialState)
|
||
|
{
|
||
|
m_RootVisualElement = rootVisualElement;
|
||
|
m_State = initialState;
|
||
|
|
||
|
m_RootVisualElement.TrackSerializedObjectValue(initialState.serializedObject, so =>
|
||
|
{
|
||
|
StateChanged?.Invoke(m_State);
|
||
|
});
|
||
|
rootVisualElement.Bind(initialState.serializedObject);
|
||
|
}
|
||
|
|
||
|
public void Dispatch(Command command)
|
||
|
{
|
||
|
if (command == null)
|
||
|
throw new ArgumentNullException(nameof(command));
|
||
|
|
||
|
m_State = command(m_State);
|
||
|
|
||
|
// why not just invoke the state changed event immediately you ask? The Dispatch method might have
|
||
|
// been called from inside a UI element event handler and if we raised the event immediately, a view
|
||
|
// might try to redraw itself *during* execution of the event handler.
|
||
|
m_RootVisualElement.schedule.Execute(() =>
|
||
|
{
|
||
|
// catch exceptions here or the UIToolkit scheduled event will keep firing forever.
|
||
|
try
|
||
|
{
|
||
|
StateChanged?.Invoke(m_State);
|
||
|
}
|
||
|
catch (Exception e)
|
||
|
{
|
||
|
Debug.LogError(e.Message);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
public void Initialize()
|
||
|
{
|
||
|
StateChanged?.Invoke(m_State);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Return a copy of the state.
|
||
|
/// </summary>
|
||
|
/// <remarks>
|
||
|
/// It can sometimes be necessary to get access to the state outside of a state change event, like for example
|
||
|
/// when creating views in response to UI click events. This method is for those times.
|
||
|
/// </remarks>
|
||
|
/// <returns></returns>
|
||
|
public InputActionsEditorState GetState()
|
||
|
{
|
||
|
return m_State;
|
||
|
}
|
||
|
|
||
|
public void Bind<TValue>(Expression<Func<InputActionsEditorState, ReactiveProperty<TValue>>> expr,
|
||
|
Action<InputActionsEditorState> propertyChangedCallback)
|
||
|
{
|
||
|
WhenChanged(expr, propertyChangedCallback);
|
||
|
propertyChangedCallback(m_State);
|
||
|
}
|
||
|
|
||
|
public void Bind(Expression<Func<InputActionsEditorState, SerializedProperty>> expr,
|
||
|
Action<SerializedProperty> serializedPropertyChangedCallback)
|
||
|
{
|
||
|
var propertyGetterFunc = WhenChanged(expr, serializedPropertyChangedCallback);
|
||
|
serializedPropertyChangedCallback(propertyGetterFunc(m_State));
|
||
|
}
|
||
|
|
||
|
public Func<InputActionsEditorState, ReactiveProperty<TValue>> WhenChanged<TValue>(Expression<Func<InputActionsEditorState, ReactiveProperty<TValue>>> expr,
|
||
|
Action<InputActionsEditorState> propertyChangedCallback)
|
||
|
{
|
||
|
var func = ExpressionUtils.CreateGetter(expr);
|
||
|
if (func == null)
|
||
|
throw new ArgumentException($"Couldn't get property info from expression.");
|
||
|
|
||
|
var prop = func(m_State);
|
||
|
if (prop == null)
|
||
|
throw new InvalidOperationException($"ReactiveProperty {expr} has not been assigned.");
|
||
|
|
||
|
prop.Changed += _ => propertyChangedCallback(m_State);
|
||
|
|
||
|
return func;
|
||
|
}
|
||
|
|
||
|
public Func<InputActionsEditorState, SerializedProperty> WhenChanged(Expression<Func<InputActionsEditorState, SerializedProperty>> expr,
|
||
|
Action<SerializedProperty> serializedPropertyChangedCallback)
|
||
|
{
|
||
|
var serializedPropertyGetter = ExpressionUtils.CreateGetter(expr);
|
||
|
if (serializedPropertyGetter == null)
|
||
|
throw new ArgumentException($"Couldn't get property info from expression.");
|
||
|
|
||
|
var serializedProperty = serializedPropertyGetter(m_State);
|
||
|
if (serializedProperty == null)
|
||
|
throw new InvalidOperationException($"ReactiveProperty {expr} has not been assigned.");
|
||
|
|
||
|
m_RootVisualElement.TrackPropertyValue(serializedProperty, serializedPropertyChangedCallback);
|
||
|
return serializedPropertyGetter;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|