Compare commits
3 Commits
7b4feec0f0
...
4364f4673d
| Author | SHA1 | Date | |
|---|---|---|---|
| 4364f4673d | |||
| e7b0f64c05 | |||
| bc554a5062 |
@ -20,12 +20,6 @@ namespace UnityEditor.UI
|
|||||||
{
|
{
|
||||||
SerializedProperty m_OnClickProperty;
|
SerializedProperty m_OnClickProperty;
|
||||||
|
|
||||||
|
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
private SerializedProperty _hotKeyRefrence;
|
|
||||||
private SerializedProperty _hotkeyPressType;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private SerializedProperty hoverAudioClip;
|
private SerializedProperty hoverAudioClip;
|
||||||
private SerializedProperty clickAudioClip;
|
private SerializedProperty clickAudioClip;
|
||||||
|
|
||||||
@ -35,12 +29,6 @@ namespace UnityEditor.UI
|
|||||||
|
|
||||||
m_OnClickProperty = serializedObject.FindProperty("m_OnClick");
|
m_OnClickProperty = serializedObject.FindProperty("m_OnClick");
|
||||||
|
|
||||||
|
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
_hotKeyRefrence = serializedObject.FindProperty("_hotkeyAction");
|
|
||||||
_hotkeyPressType = serializedObject.FindProperty("_hotkeyPressType");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
hoverAudioClip = serializedObject.FindProperty("hoverAudioClip");
|
hoverAudioClip = serializedObject.FindProperty("hoverAudioClip");
|
||||||
clickAudioClip = serializedObject.FindProperty("clickAudioClip");
|
clickAudioClip = serializedObject.FindProperty("clickAudioClip");
|
||||||
|
|
||||||
@ -64,15 +52,6 @@ namespace UnityEditor.UI
|
|||||||
serializedObject.Update();
|
serializedObject.Update();
|
||||||
EditorGUILayout.PropertyField(m_OnClickProperty);
|
EditorGUILayout.PropertyField(m_OnClickProperty);
|
||||||
|
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
|
|
||||||
{
|
|
||||||
EditorGUILayout.LabelField("Hotkey Setting", EditorStyles.boldLabel);
|
|
||||||
EditorGUILayout.PropertyField(_hotKeyRefrence, new GUIContent("InputAction"));
|
|
||||||
EditorGUILayout.PropertyField(_hotkeyPressType, new GUIContent("PressType"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using AlicizaX.UI.Runtime;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
namespace UnityEditor.UI
|
|
||||||
{
|
|
||||||
[CustomEditor(typeof(HotkeyBindComponent))]
|
|
||||||
public class HotkeyBindComponentInspector : UnityEditor.Editor
|
|
||||||
{
|
|
||||||
private SerializedProperty hotButtonsProp;
|
|
||||||
private HotkeyBindComponent _target;
|
|
||||||
|
|
||||||
private void OnEnable()
|
|
||||||
{
|
|
||||||
_target = (HotkeyBindComponent)target;
|
|
||||||
hotButtonsProp = serializedObject.FindProperty("hotButtons");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnInspectorGUI()
|
|
||||||
{
|
|
||||||
serializedObject.Update();
|
|
||||||
|
|
||||||
var holder = _target.GetComponent<UIHolderObjectBase>();
|
|
||||||
if (holder == null)
|
|
||||||
{
|
|
||||||
EditorGUILayout.HelpBox(
|
|
||||||
"⚠ 当前对象缺少 UIHolderObjectBase 组件。\nHotkeyBindComponent 依赖它进行热键绑定事件。",
|
|
||||||
MessageType.Error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorGUILayout.Space();
|
|
||||||
|
|
||||||
// 灰掉显示 hotButtons 列表(不可手动编辑)
|
|
||||||
GUI.enabled = false;
|
|
||||||
DrawHotButtonListAlwaysExpanded(hotButtonsProp);
|
|
||||||
GUI.enabled = true;
|
|
||||||
|
|
||||||
EditorGUILayout.Space();
|
|
||||||
|
|
||||||
// 按钮:扫描子物体(包含隐藏)
|
|
||||||
if (GUILayout.Button("🔍 扫描所有子物体 (包含隐藏对象)"))
|
|
||||||
{
|
|
||||||
FindAllUXHotkeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
serializedObject.ApplyModifiedProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义展开显示 hotButtons 列表
|
|
||||||
/// </summary>
|
|
||||||
private void DrawHotButtonListAlwaysExpanded(SerializedProperty listProp)
|
|
||||||
{
|
|
||||||
EditorGUILayout.LabelField("Hot Buttons", EditorStyles.boldLabel);
|
|
||||||
|
|
||||||
if (listProp == null || listProp.arraySize == 0)
|
|
||||||
{
|
|
||||||
EditorGUILayout.HelpBox("当前没有绑定任何 UXHotkey。", MessageType.Info);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditorGUI.indentLevel++;
|
|
||||||
for (int i = 0; i < listProp.arraySize; i++)
|
|
||||||
{
|
|
||||||
var element = listProp.GetArrayElementAtIndex(i);
|
|
||||||
var comp = element.objectReferenceValue as Component;
|
|
||||||
string name = comp != null ? comp.name + " (" + comp.GetType().Name + ")" : "Null";
|
|
||||||
// 注意:这里用 typeof(Component)
|
|
||||||
EditorGUILayout.ObjectField($"[{i}] {name}", element.objectReferenceValue, typeof(Component), true);
|
|
||||||
}
|
|
||||||
EditorGUI.indentLevel--;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 查找所有子物体(包含隐藏)并绑定 UXHotkey
|
|
||||||
/// </summary>
|
|
||||||
private void FindAllUXHotkeys()
|
|
||||||
{
|
|
||||||
Undo.RecordObject(_target, "Scan UXHotkey");
|
|
||||||
|
|
||||||
var collectMethod = target.GetType().GetMethod("CollectUXHotkeys", BindingFlags.NonPublic | BindingFlags.Instance);
|
|
||||||
if (collectMethod != null)
|
|
||||||
{
|
|
||||||
collectMethod.Invoke(target, null);
|
|
||||||
EditorUtility.SetDirty(_target);
|
|
||||||
serializedObject.Update();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.LogWarning("未找到 CollectUXHotkeys 方法。");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: f9779e310e684e9497bdac4f4ac3983e
|
|
||||||
timeCreated: 1760340548
|
|
||||||
65
Editor/UX/Hotkey/HotkeyComponentEditor.cs
Normal file
65
Editor/UX/Hotkey/HotkeyComponentEditor.cs
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
using AlicizaX.UI.Runtime;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace UnityEditor.UI
|
||||||
|
{
|
||||||
|
[CustomEditor(typeof(HotkeyComponent), true)]
|
||||||
|
public class HotkeyComponentEditor : UnityEditor.Editor
|
||||||
|
{
|
||||||
|
private SerializedProperty _hotkeyAction;
|
||||||
|
private SerializedProperty _hotkeyPressType;
|
||||||
|
private SerializedProperty _component;
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
_component = serializedObject.FindProperty("_component");
|
||||||
|
_hotkeyAction = serializedObject.FindProperty("_hotkeyAction");
|
||||||
|
_hotkeyPressType = serializedObject.FindProperty("_hotkeyPressType");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnInspectorGUI()
|
||||||
|
{
|
||||||
|
serializedObject.Update();
|
||||||
|
|
||||||
|
HotkeyComponent hotkeyComponent = (HotkeyComponent)target;
|
||||||
|
|
||||||
|
EditorGUILayout.HelpBox(
|
||||||
|
"Hotkeys auto-register to the nearest UIHolderObjectBase at runtime.",
|
||||||
|
MessageType.Info
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hotkeyComponent.GetComponentInParent<UIHolderObjectBase>(true) == null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox(
|
||||||
|
"No UIHolderObjectBase was found in parents. This hotkey will not register at runtime.",
|
||||||
|
MessageType.Warning
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_component.objectReferenceValue == null)
|
||||||
|
{
|
||||||
|
EditorGUILayout.HelpBox("No submit target was found on this object.", MessageType.Error);
|
||||||
|
if (hotkeyComponent.TryGetComponent(typeof(ISubmitHandler), out Component submitHandler))
|
||||||
|
{
|
||||||
|
_component.objectReferenceValue = submitHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
|
||||||
|
{
|
||||||
|
EditorGUILayout.LabelField("Hotkey Setting", EditorStyles.boldLabel);
|
||||||
|
|
||||||
|
EditorGUI.BeginDisabledGroup(true);
|
||||||
|
EditorGUILayout.PropertyField(_component, new GUIContent("Component"));
|
||||||
|
EditorGUI.EndDisabledGroup();
|
||||||
|
|
||||||
|
EditorGUILayout.PropertyField(_hotkeyAction, new GUIContent("Input Action"));
|
||||||
|
EditorGUILayout.PropertyField(_hotkeyPressType, new GUIContent("Press Type"));
|
||||||
|
}
|
||||||
|
|
||||||
|
serializedObject.ApplyModifiedProperties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
Editor/UX/Hotkey/HotkeyComponentEditor.cs.meta
Normal file
3
Editor/UX/Hotkey/HotkeyComponentEditor.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 939657bb1b5f4ad5b0836fa8fae2bef3
|
||||||
|
timeCreated: 1773919464
|
||||||
@ -17,11 +17,6 @@ namespace UnityEditor.UI
|
|||||||
SerializedProperty m_GroupProperty;
|
SerializedProperty m_GroupProperty;
|
||||||
SerializedProperty m_IsOnProperty;
|
SerializedProperty m_IsOnProperty;
|
||||||
|
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
private SerializedProperty _hotKeyRefrence;
|
|
||||||
private SerializedProperty _hotkeyPressType;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private SerializedProperty hoverAudioClip;
|
private SerializedProperty hoverAudioClip;
|
||||||
private SerializedProperty clickAudioClip;
|
private SerializedProperty clickAudioClip;
|
||||||
|
|
||||||
@ -35,11 +30,6 @@ namespace UnityEditor.UI
|
|||||||
m_IsOnProperty = serializedObject.FindProperty("m_IsOn");
|
m_IsOnProperty = serializedObject.FindProperty("m_IsOn");
|
||||||
m_OnValueChangedProperty = serializedObject.FindProperty("onValueChanged");
|
m_OnValueChangedProperty = serializedObject.FindProperty("onValueChanged");
|
||||||
|
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
_hotKeyRefrence = serializedObject.FindProperty("_hotkeyAction");
|
|
||||||
_hotkeyPressType = serializedObject.FindProperty("_hotkeyPressType");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
hoverAudioClip = serializedObject.FindProperty("hoverAudioClip");
|
hoverAudioClip = serializedObject.FindProperty("hoverAudioClip");
|
||||||
clickAudioClip = serializedObject.FindProperty("clickAudioClip");
|
clickAudioClip = serializedObject.FindProperty("clickAudioClip");
|
||||||
|
|
||||||
@ -62,15 +52,6 @@ namespace UnityEditor.UI
|
|||||||
serializedObject.Update();
|
serializedObject.Update();
|
||||||
EditorGUILayout.PropertyField(m_OnValueChangedProperty);
|
EditorGUILayout.PropertyField(m_OnValueChangedProperty);
|
||||||
|
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox))
|
|
||||||
{
|
|
||||||
EditorGUILayout.LabelField("Hotkey Setting", EditorStyles.boldLabel);
|
|
||||||
EditorGUILayout.PropertyField(_hotKeyRefrence, new GUIContent("InputAction"));
|
|
||||||
EditorGUILayout.PropertyField(_hotkeyPressType, new GUIContent("PressType"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
serializedObject.ApplyModifiedProperties();
|
serializedObject.ApplyModifiedProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,48 +5,11 @@ using AlicizaX.UI;
|
|||||||
using AlicizaX.UI.Extension;
|
using AlicizaX.UI.Extension;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
|
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
using UnityEngine.InputSystem;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace UnityEngine.UI
|
namespace UnityEngine.UI
|
||||||
{
|
{
|
||||||
[AddComponentMenu("UI/UXButton", 30)]
|
[AddComponentMenu("UI/UXButton", 30)]
|
||||||
public class UXButton : UXSelectable, IPointerClickHandler, ISubmitHandler, IButton
|
public class UXButton : UXSelectable, IPointerClickHandler, ISubmitHandler, IButton
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
, IHotkeyTrigger
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
|
|
||||||
InputActionReference IHotkeyTrigger.HotkeyAction
|
|
||||||
{
|
|
||||||
get => _hotkeyAction;
|
|
||||||
set => _hotkeyAction = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
EHotkeyPressType IHotkeyTrigger.HotkeyPressType
|
|
||||||
{
|
|
||||||
get => _hotkeyPressType;
|
|
||||||
set => _hotkeyPressType = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IHotkeyTrigger.HotkeyActionTrigger()
|
|
||||||
{
|
|
||||||
if (interactable)
|
|
||||||
{
|
|
||||||
OnSubmit(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SerializeField] internal InputActionReference _hotkeyAction;
|
|
||||||
[SerializeField] internal EHotkeyPressType _hotkeyPressType;
|
|
||||||
|
|
||||||
public InputActionReference HotKeyRefrence
|
|
||||||
{
|
|
||||||
get { return _hotkeyAction; }
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
[SerializeField] private AudioClip hoverAudioClip;
|
[SerializeField] private AudioClip hoverAudioClip;
|
||||||
[SerializeField] private AudioClip clickAudioClip;
|
[SerializeField] private AudioClip clickAudioClip;
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Events;
|
using UnityEngine.Events;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
using UnityEngine.InputSystem;
|
|
||||||
#endif
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEditor;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace UnityEngine.UI
|
namespace UnityEngine.UI
|
||||||
{
|
{
|
||||||
[AddComponentMenu("UI/UXToggle", 30)]
|
[AddComponentMenu("UI/UXToggle", 30)]
|
||||||
[RequireComponent(typeof(RectTransform))]
|
[RequireComponent(typeof(RectTransform))]
|
||||||
public class UXToggle : UXSelectable, IPointerClickHandler, ISubmitHandler, ICanvasElement
|
public class UXToggle : UXSelectable, IPointerClickHandler, ISubmitHandler, ICanvasElement
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
, IHotkeyTrigger
|
|
||||||
#endif
|
|
||||||
{
|
{
|
||||||
[Serializable]
|
[Serializable]
|
||||||
public class ToggleEvent : UnityEvent<bool>
|
public class ToggleEvent : UnityEvent<bool>
|
||||||
@ -249,36 +239,6 @@ namespace UnityEngine.UI
|
|||||||
UXComponentExtensionsHelper.AudioHelper.PlayAudio(clip);
|
UXComponentExtensionsHelper.AudioHelper.PlayAudio(clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
|
|
||||||
InputActionReference IHotkeyTrigger.HotkeyAction
|
|
||||||
{
|
|
||||||
get => _hotkeyAction;
|
|
||||||
set => _hotkeyAction = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
EHotkeyPressType IHotkeyTrigger.HotkeyPressType
|
|
||||||
{
|
|
||||||
get => _hotkeyPressType;
|
|
||||||
set => _hotkeyPressType = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IHotkeyTrigger.HotkeyActionTrigger()
|
|
||||||
{
|
|
||||||
if (interactable)
|
|
||||||
{
|
|
||||||
OnSubmit(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SerializeField] internal InputActionReference _hotkeyAction;
|
|
||||||
[SerializeField] internal EHotkeyPressType _hotkeyPressType;
|
|
||||||
|
|
||||||
public InputActionReference HotKeyRefrence
|
|
||||||
{
|
|
||||||
get { return _hotkeyAction; }
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[SerializeField] private AudioClip hoverAudioClip;
|
[SerializeField] private AudioClip hoverAudioClip;
|
||||||
[SerializeField] private AudioClip clickAudioClip;
|
[SerializeField] private AudioClip clickAudioClip;
|
||||||
|
|||||||
@ -1,79 +0,0 @@
|
|||||||
#if INPUTSYSTEM_SUPPORT
|
|
||||||
using System.Linq;
|
|
||||||
using AlicizaX.UI.Runtime;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace UnityEngine.UI
|
|
||||||
{
|
|
||||||
public class HotkeyBindComponent : MonoBehaviour
|
|
||||||
{
|
|
||||||
private UIHolderObjectBase _holderObjectBase;
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
_holderObjectBase = GetComponent<UIHolderObjectBase>();
|
|
||||||
_holderObjectBase.OnWindowBeforeShowEvent += BindHotKeys;
|
|
||||||
_holderObjectBase.OnWindowBeforeClosedEvent += UnBindHotKeys;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
|
||||||
{
|
|
||||||
if (_holderObjectBase != null)
|
|
||||||
{
|
|
||||||
_holderObjectBase.OnWindowBeforeShowEvent -= BindHotKeys;
|
|
||||||
_holderObjectBase.OnWindowBeforeClosedEvent -= UnBindHotKeys;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SerializeField] private Component[] hotButtons;
|
|
||||||
|
|
||||||
internal void BindHotKeys()
|
|
||||||
{
|
|
||||||
if (hotButtons == null) return;
|
|
||||||
for (int i = 0; i < hotButtons.Length; i++)
|
|
||||||
{
|
|
||||||
if (hotButtons[i] is IHotkeyTrigger trigger)
|
|
||||||
{
|
|
||||||
trigger.BindHotKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
[ContextMenu("Bind HotKeys")]
|
|
||||||
private void CollectUXHotkeys()
|
|
||||||
{
|
|
||||||
var found = gameObject
|
|
||||||
.GetComponentsInChildren<MonoBehaviour>(true)
|
|
||||||
.OfType<IHotkeyTrigger>()
|
|
||||||
.Where(t => t.HotkeyAction != null)
|
|
||||||
.Select(t => t as Component)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
hotButtons = found;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnValidate()
|
|
||||||
{
|
|
||||||
if (_holderObjectBase == null)
|
|
||||||
{
|
|
||||||
_holderObjectBase = gameObject.GetComponent<UIHolderObjectBase>();
|
|
||||||
CollectUXHotkeys();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
internal void UnBindHotKeys()
|
|
||||||
{
|
|
||||||
if (hotButtons == null) return;
|
|
||||||
for (int i = 0; i < hotButtons.Length; i++)
|
|
||||||
{
|
|
||||||
if (hotButtons[i] is IHotkeyTrigger trigger)
|
|
||||||
{
|
|
||||||
trigger.UnBindHotKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
fileFormatVersion: 2
|
|
||||||
guid: fdcda7a93f3c4639a0cb68dd00509bb1
|
|
||||||
timeCreated: 1758683821
|
|
||||||
88
Runtime/UXComponent/Hotkey/HotkeyComponent.cs
Normal file
88
Runtime/UXComponent/Hotkey/HotkeyComponent.cs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#if INPUTSYSTEM_SUPPORT
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using UnityEngine.InputSystem;
|
||||||
|
|
||||||
|
namespace UnityEngine.UI
|
||||||
|
{
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public sealed class HotkeyComponent : MonoBehaviour, IHotkeyTrigger
|
||||||
|
{
|
||||||
|
[SerializeField] private Component _component;
|
||||||
|
[SerializeField] private InputActionReference _hotkeyAction;
|
||||||
|
[SerializeField] private EHotkeyPressType _hotkeyPressType = EHotkeyPressType.Performed;
|
||||||
|
|
||||||
|
public InputActionReference HotkeyAction
|
||||||
|
{
|
||||||
|
get => _hotkeyAction;
|
||||||
|
set => _hotkeyAction = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
EHotkeyPressType IHotkeyTrigger.HotkeyPressType
|
||||||
|
{
|
||||||
|
get => _hotkeyPressType;
|
||||||
|
set => _hotkeyPressType = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Reset()
|
||||||
|
{
|
||||||
|
AutoAssignTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEnable()
|
||||||
|
{
|
||||||
|
AutoAssignTarget();
|
||||||
|
((IHotkeyTrigger)this).BindHotKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDisable()
|
||||||
|
{
|
||||||
|
((IHotkeyTrigger)this).UnBindHotKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDestroy()
|
||||||
|
{
|
||||||
|
((IHotkeyTrigger)this).UnBindHotKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
private void OnValidate()
|
||||||
|
{
|
||||||
|
AutoAssignTarget();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void IHotkeyTrigger.HotkeyActionTrigger()
|
||||||
|
{
|
||||||
|
if (!isActiveAndEnabled || _component == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_component is ISubmitHandler)
|
||||||
|
{
|
||||||
|
ExecuteEvents.Execute(
|
||||||
|
_component.gameObject,
|
||||||
|
new BaseEventData(EventSystem.current),
|
||||||
|
ExecuteEvents.submitHandler
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.LogWarning($"{nameof(HotkeyComponent)} target must implement {nameof(ISubmitHandler)}: {_component.name}", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AutoAssignTarget()
|
||||||
|
{
|
||||||
|
if (_component != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryGetComponent(typeof(ISubmitHandler), out Component submitHandler))
|
||||||
|
{
|
||||||
|
_component = submitHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
3
Runtime/UXComponent/Hotkey/HotkeyComponent.cs.meta
Normal file
3
Runtime/UXComponent/Hotkey/HotkeyComponent.cs.meta
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ad0e473486c04dc19b468f2b20675091
|
||||||
|
timeCreated: 1773912951
|
||||||
@ -5,7 +5,7 @@ namespace UnityEngine.UI
|
|||||||
{
|
{
|
||||||
public interface IHotkeyTrigger
|
public interface IHotkeyTrigger
|
||||||
{
|
{
|
||||||
internal InputActionReference HotkeyAction { get; set; }
|
public InputActionReference HotkeyAction { get; }
|
||||||
internal EHotkeyPressType HotkeyPressType { get; set; }
|
internal EHotkeyPressType HotkeyPressType { get; set; }
|
||||||
internal void HotkeyActionTrigger();
|
internal void HotkeyActionTrigger();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
#if INPUTSYSTEM_SUPPORT
|
#if INPUTSYSTEM_SUPPORT
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using AlicizaX.UI.Runtime;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.InputSystem;
|
using UnityEngine.InputSystem;
|
||||||
using Unity.Collections;
|
|
||||||
using Unity.Collections.LowLevel.Unsafe;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
namespace UnityEngine.UI
|
namespace UnityEngine.UI
|
||||||
{
|
{
|
||||||
@ -16,395 +14,638 @@ namespace UnityEngine.UI
|
|||||||
Performed = 1
|
Performed = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
internal readonly struct HotkeyRegistration
|
||||||
internal struct HotkeyRegistration
|
|
||||||
{
|
{
|
||||||
public IHotkeyTrigger button;
|
public readonly IHotkeyTrigger Trigger;
|
||||||
public EHotkeyPressType pressType;
|
public readonly EHotkeyPressType PressType;
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public HotkeyRegistration(IHotkeyTrigger btn, EHotkeyPressType pressType)
|
public HotkeyRegistration(IHotkeyTrigger trigger, EHotkeyPressType pressType)
|
||||||
{
|
{
|
||||||
this.button = btn;
|
Trigger = trigger;
|
||||||
this.pressType = pressType;
|
PressType = pressType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
internal sealed class HotkeyScope
|
||||||
internal struct HandlerInfo
|
|
||||||
{
|
{
|
||||||
public Action<InputAction.CallbackContext> handler;
|
public HotkeyScope(UIHolderObjectBase holder)
|
||||||
public InputActionReference action;
|
|
||||||
|
|
||||||
public HandlerInfo(Action<InputAction.CallbackContext> handler, InputActionReference action)
|
|
||||||
{
|
{
|
||||||
this.handler = handler;
|
Holder = holder;
|
||||||
this.action = action;
|
HierarchyDepth = GetHierarchyDepth(holder.transform);
|
||||||
|
BlocksLowerScopes = FindParentHolder(holder) == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly UIHolderObjectBase Holder;
|
||||||
|
public readonly int HierarchyDepth;
|
||||||
|
public readonly bool BlocksLowerScopes;
|
||||||
|
public readonly Dictionary<string, List<HotkeyRegistration>> RegistrationsByAction = new(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
public bool LifecycleActive;
|
||||||
|
public ulong ActivationSerial;
|
||||||
|
|
||||||
|
public Action OnBeforeShow;
|
||||||
|
public Action OnBeforeClosed;
|
||||||
|
public Action OnDestroy;
|
||||||
|
|
||||||
|
private Canvas _canvas;
|
||||||
|
|
||||||
|
public Canvas Canvas
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_canvas == null && Holder != null)
|
||||||
|
{
|
||||||
|
_canvas = Holder.GetComponent<Canvas>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _canvas;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static int GetHierarchyDepth(Transform current)
|
||||||
|
{
|
||||||
|
int depth = 0;
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
depth++;
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static UIHolderObjectBase FindParentHolder(UIHolderObjectBase holder)
|
||||||
|
{
|
||||||
|
if (holder == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform current = holder.transform.parent;
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
if (current.TryGetComponent<UIHolderObjectBase>(out var parentHolder))
|
||||||
|
{
|
||||||
|
return parentHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class ActionRegistrationBucket
|
||||||
|
{
|
||||||
|
public InputActionReference ActionReference;
|
||||||
|
public Action<InputAction.CallbackContext> StartedHandler;
|
||||||
|
public Action<InputAction.CallbackContext> PerformedHandler;
|
||||||
|
public int StartedCount;
|
||||||
|
public int PerformedCount;
|
||||||
|
|
||||||
|
public int TotalCount => StartedCount + PerformedCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal readonly struct TriggerRegistration
|
||||||
|
{
|
||||||
|
public readonly string ActionId;
|
||||||
|
public readonly UIHolderObjectBase Holder;
|
||||||
|
public readonly EHotkeyPressType PressType;
|
||||||
|
|
||||||
|
public TriggerRegistration(string actionId, UIHolderObjectBase holder, EHotkeyPressType pressType)
|
||||||
|
{
|
||||||
|
ActionId = actionId;
|
||||||
|
Holder = holder;
|
||||||
|
PressType = pressType;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class UXHotkeyRegisterManager
|
internal static class UXHotkeyRegisterManager
|
||||||
{
|
{
|
||||||
private const int INITIAL_CAPACITY = 32;
|
private static readonly Dictionary<string, ActionRegistrationBucket> _actions = new(StringComparer.Ordinal);
|
||||||
private const int MAX_REGISTRATIONS_PER_ACTION = 16;
|
private static readonly Dictionary<IHotkeyTrigger, TriggerRegistration> _triggerMap = new();
|
||||||
|
private static readonly Dictionary<UIHolderObjectBase, HotkeyScope> _scopes = new();
|
||||||
|
private static readonly List<HotkeyScope> _leafScopes = new();
|
||||||
|
private static readonly HashSet<UIHolderObjectBase> _ancestorHolders = new();
|
||||||
|
|
||||||
private static HotkeyRegistration[][] _registrationPool;
|
private static ulong _serialCounter;
|
||||||
private static int[] _registrationCounts;
|
|
||||||
private static string[] _actionIds;
|
|
||||||
private static int _actionCount;
|
|
||||||
private static int _actionCapacity;
|
|
||||||
|
|
||||||
private static HandlerInfo[] _handlers;
|
|
||||||
private static InputActionReference[] _actionRefs;
|
|
||||||
|
|
||||||
private static IHotkeyTrigger[] _buttons;
|
|
||||||
private static int[] _buttonToActionIndex;
|
|
||||||
private static int _buttonCount;
|
|
||||||
private static int _buttonCapacity;
|
|
||||||
|
|
||||||
private static Action<InputAction.CallbackContext>[] _cachedHandlers;
|
|
||||||
private static int _cachedHandlerCount;
|
|
||||||
|
|
||||||
static UXHotkeyRegisterManager()
|
|
||||||
{
|
|
||||||
_actionCapacity = INITIAL_CAPACITY;
|
|
||||||
_registrationPool = new HotkeyRegistration[_actionCapacity][];
|
|
||||||
_registrationCounts = new int[_actionCapacity];
|
|
||||||
_actionIds = new string[_actionCapacity];
|
|
||||||
_handlers = new HandlerInfo[_actionCapacity];
|
|
||||||
_actionRefs = new InputActionReference[_actionCapacity];
|
|
||||||
|
|
||||||
_buttonCapacity = 64;
|
|
||||||
_buttons = new IHotkeyTrigger[_buttonCapacity];
|
|
||||||
_buttonToActionIndex = new int[_buttonCapacity];
|
|
||||||
|
|
||||||
_cachedHandlers = new Action<InputAction.CallbackContext>[INITIAL_CAPACITY];
|
|
||||||
|
|
||||||
for (int i = 0; i < _actionCapacity; i++)
|
|
||||||
{
|
|
||||||
_registrationPool[i] = new HotkeyRegistration[MAX_REGISTRATIONS_PER_ACTION];
|
|
||||||
_buttonToActionIndex[i] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
[UnityEditor.Callbacks.DidReloadScripts]
|
[UnityEditor.Callbacks.DidReloadScripts]
|
||||||
internal static void ClearHotkeyRegistry()
|
internal static void ClearHotkeyRegistry()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _buttonCount; i++)
|
IHotkeyTrigger[] triggers = new IHotkeyTrigger[_triggerMap.Count];
|
||||||
|
int index = 0;
|
||||||
|
foreach (var kvp in _triggerMap)
|
||||||
{
|
{
|
||||||
if (_buttons[i] != null)
|
triggers[index++] = kvp.Key;
|
||||||
{
|
|
||||||
UnregisterHotkey(_buttons[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Array.Clear(_registrationCounts, 0, _actionCount);
|
for (int i = 0; i < triggers.Length; i++)
|
||||||
Array.Clear(_actionIds, 0, _actionCount);
|
{
|
||||||
Array.Clear(_handlers, 0, _actionCount);
|
UnregisterHotkey(triggers[i]);
|
||||||
Array.Clear(_actionRefs, 0, _actionCount);
|
}
|
||||||
Array.Clear(_buttons, 0, _buttonCount);
|
|
||||||
Array.Clear(_buttonToActionIndex, 0, _buttonCount);
|
|
||||||
|
|
||||||
_actionCount = 0;
|
_actions.Clear();
|
||||||
_buttonCount = 0;
|
_triggerMap.Clear();
|
||||||
_cachedHandlerCount = 0;
|
_scopes.Clear();
|
||||||
|
_leafScopes.Clear();
|
||||||
|
_ancestorHolders.Clear();
|
||||||
|
_serialCounter = 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
internal static void RegisterHotkey(IHotkeyTrigger button, InputActionReference action, EHotkeyPressType pressType)
|
internal static void RegisterHotkey(IHotkeyTrigger trigger, UIHolderObjectBase holder, InputActionReference action, EHotkeyPressType pressType)
|
||||||
{
|
{
|
||||||
if (action == null || action.action == null || button == null)
|
if (trigger == null || holder == null || action == null || action.action == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnregisterHotkey(trigger);
|
||||||
|
|
||||||
string actionId = action.action.id.ToString();
|
string actionId = action.action.id.ToString();
|
||||||
int actionIndex = FindOrCreateActionIndex(actionId);
|
HotkeyScope scope = GetOrCreateScope(holder);
|
||||||
|
ActionRegistrationBucket bucket = GetOrCreateBucket(actionId, action);
|
||||||
|
HotkeyRegistration registration = new HotkeyRegistration(trigger, pressType);
|
||||||
|
|
||||||
ref int count = ref _registrationCounts[actionIndex];
|
AdjustBucketSubscription(bucket, pressType, true);
|
||||||
if (count >= MAX_REGISTRATIONS_PER_ACTION)
|
AddScopeRegistration(scope, actionId, registration);
|
||||||
|
|
||||||
|
if (scope.LifecycleActive)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"Max registrations reached for action: {actionId}");
|
scope.ActivationSerial = ++_serialCounter;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_registrationPool[actionIndex][count] = new HotkeyRegistration(button, pressType);
|
_triggerMap[trigger] = new TriggerRegistration(actionId, holder, pressType);
|
||||||
count++;
|
|
||||||
|
|
||||||
int buttonIndex = FindOrCreateButtonIndex(button);
|
|
||||||
_buttonToActionIndex[buttonIndex] = actionIndex;
|
|
||||||
|
|
||||||
if (count == 1)
|
|
||||||
{
|
|
||||||
Action<InputAction.CallbackContext> handler = GetOrCreateHandler(actionIndex);
|
|
||||||
_handlers[actionIndex] = new HandlerInfo(handler, action);
|
|
||||||
_actionRefs[actionIndex] = action;
|
|
||||||
|
|
||||||
switch (pressType)
|
|
||||||
{
|
|
||||||
case EHotkeyPressType.Started:
|
|
||||||
action.action.started += handler;
|
|
||||||
break;
|
|
||||||
case EHotkeyPressType.Performed:
|
|
||||||
action.action.performed += handler;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
action.action.Enable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void UnregisterHotkey(IHotkeyTrigger button)
|
internal static void UnregisterHotkey(IHotkeyTrigger trigger)
|
||||||
{
|
{
|
||||||
if (button == null)
|
if (trigger == null || !_triggerMap.TryGetValue(trigger, out var triggerRegistration))
|
||||||
return;
|
|
||||||
|
|
||||||
int buttonIndex = FindButtonIndex(button);
|
|
||||||
if (buttonIndex < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int actionIndex = _buttonToActionIndex[buttonIndex];
|
|
||||||
if (actionIndex < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ref int count = ref _registrationCounts[actionIndex];
|
|
||||||
HotkeyRegistration[] registrations = _registrationPool[actionIndex];
|
|
||||||
|
|
||||||
for (int i = count - 1; i >= 0; i--)
|
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(registrations[i].button, button))
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_actions.TryGetValue(triggerRegistration.ActionId, out var bucket))
|
||||||
|
{
|
||||||
|
RemoveActionRegistration(bucket, triggerRegistration.PressType, triggerRegistration.ActionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_scopes.TryGetValue(triggerRegistration.Holder, out var scope))
|
||||||
|
{
|
||||||
|
RemoveScopeRegistration(scope, triggerRegistration.ActionId, trigger);
|
||||||
|
ReleaseScopeIfEmpty(scope);
|
||||||
|
}
|
||||||
|
|
||||||
|
_triggerMap.Remove(trigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static ActionRegistrationBucket GetOrCreateBucket(string actionId, InputActionReference action)
|
||||||
|
{
|
||||||
|
if (_actions.TryGetValue(actionId, out var bucket))
|
||||||
|
{
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket = new ActionRegistrationBucket
|
||||||
|
{
|
||||||
|
ActionReference = action,
|
||||||
|
StartedHandler = _ => Dispatch(actionId, EHotkeyPressType.Started),
|
||||||
|
PerformedHandler = _ => Dispatch(actionId, EHotkeyPressType.Performed)
|
||||||
|
};
|
||||||
|
_actions[actionId] = bucket;
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HotkeyScope GetOrCreateScope(UIHolderObjectBase holder)
|
||||||
|
{
|
||||||
|
if (_scopes.TryGetValue(holder, out var scope))
|
||||||
|
{
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
scope = new HotkeyScope(holder)
|
||||||
|
{
|
||||||
|
LifecycleActive = IsHolderVisible(holder),
|
||||||
|
ActivationSerial = ++_serialCounter
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.OnBeforeShow = () => ActivateScope(holder);
|
||||||
|
scope.OnBeforeClosed = () => DeactivateScope(holder);
|
||||||
|
scope.OnDestroy = () => DestroyScope(holder);
|
||||||
|
|
||||||
|
holder.OnWindowBeforeShowEvent += scope.OnBeforeShow;
|
||||||
|
holder.OnWindowBeforeClosedEvent += scope.OnBeforeClosed;
|
||||||
|
holder.OnWindowDestroyEvent += scope.OnDestroy;
|
||||||
|
|
||||||
|
_scopes[holder] = scope;
|
||||||
|
return scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ActivateScope(UIHolderObjectBase holder)
|
||||||
|
{
|
||||||
|
if (_scopes.TryGetValue(holder, out var scope))
|
||||||
|
{
|
||||||
|
scope.LifecycleActive = true;
|
||||||
|
scope.ActivationSerial = ++_serialCounter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DeactivateScope(UIHolderObjectBase holder)
|
||||||
|
{
|
||||||
|
if (_scopes.TryGetValue(holder, out var scope))
|
||||||
|
{
|
||||||
|
scope.LifecycleActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void DestroyScope(UIHolderObjectBase holder)
|
||||||
|
{
|
||||||
|
if (holder == null || !_scopes.TryGetValue(holder, out var scope))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IHotkeyTrigger> triggers = null;
|
||||||
|
foreach (var pair in scope.RegistrationsByAction)
|
||||||
|
{
|
||||||
|
List<HotkeyRegistration> registrations = pair.Value;
|
||||||
|
for (int i = 0; i < registrations.Count; i++)
|
||||||
{
|
{
|
||||||
EHotkeyPressType pressType = registrations[i].pressType;
|
triggers ??= new List<IHotkeyTrigger>(registrations.Count);
|
||||||
|
triggers.Add(registrations[i].Trigger);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
registrations[i] = registrations[count - 1];
|
if (triggers != null)
|
||||||
registrations[count - 1] = default;
|
{
|
||||||
count--;
|
for (int i = 0; i < triggers.Count; i++)
|
||||||
|
{
|
||||||
|
UnregisterHotkey(triggers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (count == 0)
|
DetachScope(scope);
|
||||||
{
|
}
|
||||||
ref HandlerInfo handlerInfo = ref _handlers[actionIndex];
|
|
||||||
InputActionReference actionRef = _actionRefs[actionIndex];
|
|
||||||
|
|
||||||
if (actionRef != null && actionRef.action != null)
|
private static void DetachScope(HotkeyScope scope)
|
||||||
{
|
{
|
||||||
switch (pressType)
|
if (scope == null || scope.Holder == null)
|
||||||
{
|
{
|
||||||
case EHotkeyPressType.Started:
|
return;
|
||||||
actionRef.action.started -= handlerInfo.handler;
|
}
|
||||||
break;
|
|
||||||
case EHotkeyPressType.Performed:
|
|
||||||
actionRef.action.performed -= handlerInfo.handler;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
actionRef.action.Disable();
|
scope.Holder.OnWindowBeforeShowEvent -= scope.OnBeforeShow;
|
||||||
}
|
scope.Holder.OnWindowBeforeClosedEvent -= scope.OnBeforeClosed;
|
||||||
|
scope.Holder.OnWindowDestroyEvent -= scope.OnDestroy;
|
||||||
|
_scopes.Remove(scope.Holder);
|
||||||
|
}
|
||||||
|
|
||||||
handlerInfo = default;
|
private static void ReleaseScopeIfEmpty(HotkeyScope scope)
|
||||||
_actionRefs[actionIndex] = null;
|
{
|
||||||
_actionIds[actionIndex] = null;
|
if (scope != null && scope.RegistrationsByAction.Count == 0)
|
||||||
}
|
{
|
||||||
|
DetachScope(scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AddScopeRegistration(HotkeyScope scope, string actionId, HotkeyRegistration registration)
|
||||||
|
{
|
||||||
|
if (!scope.RegistrationsByAction.TryGetValue(actionId, out var registrations))
|
||||||
|
{
|
||||||
|
registrations = new List<HotkeyRegistration>();
|
||||||
|
scope.RegistrationsByAction[actionId] = registrations;
|
||||||
|
}
|
||||||
|
|
||||||
|
registrations.Add(registration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RemoveScopeRegistration(HotkeyScope scope, string actionId, IHotkeyTrigger trigger)
|
||||||
|
{
|
||||||
|
if (!scope.RegistrationsByAction.TryGetValue(actionId, out var registrations))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = registrations.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(registrations[i].Trigger, trigger))
|
||||||
|
{
|
||||||
|
registrations.RemoveAt(i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_buttons[buttonIndex] = null;
|
if (registrations.Count == 0)
|
||||||
_buttonToActionIndex[buttonIndex] = -1;
|
{
|
||||||
|
scope.RegistrationsByAction.Remove(actionId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
private static void RemoveActionRegistration(ActionRegistrationBucket bucket, EHotkeyPressType pressType, string actionId)
|
||||||
private static Action<InputAction.CallbackContext> GetOrCreateHandler(int actionIndex)
|
|
||||||
{
|
{
|
||||||
if (actionIndex < _cachedHandlers.Length && _cachedHandlers[actionIndex] != null)
|
AdjustBucketSubscription(bucket, pressType, false);
|
||||||
|
if (bucket.TotalCount == 0)
|
||||||
{
|
{
|
||||||
return _cachedHandlers[actionIndex];
|
_actions.Remove(actionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (actionIndex >= _cachedHandlers.Length)
|
|
||||||
{
|
|
||||||
Array.Resize(ref _cachedHandlers, actionIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int capturedIndex = actionIndex;
|
|
||||||
_cachedHandlers[actionIndex] = ctx => OnHotkeyTriggered(capturedIndex);
|
|
||||||
return _cachedHandlers[actionIndex];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
private static void AdjustBucketSubscription(ActionRegistrationBucket bucket, EHotkeyPressType pressType, bool add)
|
||||||
private static void OnHotkeyTriggered(int actionIndex)
|
|
||||||
{
|
{
|
||||||
int count = _registrationCounts[actionIndex];
|
InputAction inputAction = bucket.ActionReference != null ? bucket.ActionReference.action : null;
|
||||||
if (count > 0)
|
if (inputAction == null)
|
||||||
{
|
{
|
||||||
ref HotkeyRegistration registration = ref _registrationPool[actionIndex][count - 1];
|
return;
|
||||||
registration.button?.HotkeyActionTrigger();
|
}
|
||||||
|
|
||||||
|
switch (pressType)
|
||||||
|
{
|
||||||
|
case EHotkeyPressType.Started:
|
||||||
|
if (add)
|
||||||
|
{
|
||||||
|
if (bucket.StartedCount == 0)
|
||||||
|
{
|
||||||
|
inputAction.started += bucket.StartedHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket.StartedCount++;
|
||||||
|
}
|
||||||
|
else if (bucket.StartedCount > 0)
|
||||||
|
{
|
||||||
|
bucket.StartedCount--;
|
||||||
|
if (bucket.StartedCount == 0)
|
||||||
|
{
|
||||||
|
inputAction.started -= bucket.StartedHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case EHotkeyPressType.Performed:
|
||||||
|
if (add)
|
||||||
|
{
|
||||||
|
if (bucket.PerformedCount == 0)
|
||||||
|
{
|
||||||
|
inputAction.performed += bucket.PerformedHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket.PerformedCount++;
|
||||||
|
}
|
||||||
|
else if (bucket.PerformedCount > 0)
|
||||||
|
{
|
||||||
|
bucket.PerformedCount--;
|
||||||
|
if (bucket.PerformedCount == 0)
|
||||||
|
{
|
||||||
|
inputAction.performed -= bucket.PerformedHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bucket.TotalCount > 0)
|
||||||
|
{
|
||||||
|
inputAction.Enable();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inputAction.Disable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static int FindOrCreateActionIndex(string actionId)
|
private static void Dispatch(string actionId, EHotkeyPressType pressType)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _actionCount; i++)
|
HotkeyScope[] leafScopes = GetLeafScopes();
|
||||||
|
if (leafScopes.Length == 0)
|
||||||
{
|
{
|
||||||
if (_actionIds[i] == actionId)
|
return;
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_actionCount >= _actionCapacity)
|
TryDispatchToScopeChain(leafScopes[0], actionId, pressType);
|
||||||
{
|
|
||||||
ExpandActionCapacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = _actionCount++;
|
|
||||||
_actionIds[index] = actionId;
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
private static bool TryDispatchToScopeChain(HotkeyScope leafScope, string actionId, EHotkeyPressType pressType)
|
||||||
private static int FindOrCreateButtonIndex(IHotkeyTrigger button)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _buttonCount; i++)
|
UIHolderObjectBase currentHolder = leafScope.Holder;
|
||||||
|
while (currentHolder != null)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(_buttons[i], button))
|
if (_scopes.TryGetValue(currentHolder, out var scope)
|
||||||
return i;
|
&& TryGetLatestRegistration(scope, actionId, pressType, out var registration))
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < _buttonCapacity; i++)
|
|
||||||
{
|
|
||||||
if (_buttons[i] == null)
|
|
||||||
{
|
{
|
||||||
_buttons[i] = button;
|
registration.Trigger?.HotkeyActionTrigger();
|
||||||
if (i >= _buttonCount)
|
return true;
|
||||||
_buttonCount = i + 1;
|
}
|
||||||
return i;
|
|
||||||
|
currentHolder = FindParentHolder(currentHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryGetLatestRegistration(HotkeyScope scope, string actionId, EHotkeyPressType pressType, out HotkeyRegistration registration)
|
||||||
|
{
|
||||||
|
if (scope.RegistrationsByAction.TryGetValue(actionId, out var registrations))
|
||||||
|
{
|
||||||
|
for (int i = registrations.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
HotkeyRegistration candidate = registrations[i];
|
||||||
|
if (candidate.PressType == pressType && candidate.Trigger != null)
|
||||||
|
{
|
||||||
|
registration = candidate;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_buttonCount >= _buttonCapacity)
|
registration = default;
|
||||||
{
|
return false;
|
||||||
ExpandButtonCapacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = _buttonCount++;
|
|
||||||
_buttons[index] = button;
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
private static HotkeyScope[] GetLeafScopes()
|
||||||
private static int FindButtonIndex(IHotkeyTrigger button)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < _buttonCount; i++)
|
_leafScopes.Clear();
|
||||||
|
_ancestorHolders.Clear();
|
||||||
|
|
||||||
|
foreach (var scope in _scopes.Values)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(_buttons[i], button))
|
if (!IsScopeActive(scope))
|
||||||
return i;
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIHolderObjectBase parentHolder = FindParentHolder(scope.Holder);
|
||||||
|
while (parentHolder != null)
|
||||||
|
{
|
||||||
|
_ancestorHolders.Add(parentHolder);
|
||||||
|
parentHolder = FindParentHolder(parentHolder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
foreach (var scope in _scopes.Values)
|
||||||
|
{
|
||||||
|
if (IsScopeActive(scope) && !_ancestorHolders.Contains(scope.Holder))
|
||||||
|
{
|
||||||
|
_leafScopes.Add(scope);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_leafScopes.Sort(CompareScopePriority);
|
||||||
|
return _leafScopes.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ExpandActionCapacity()
|
private static bool IsScopeActive(HotkeyScope scope)
|
||||||
{
|
{
|
||||||
int newCapacity = _actionCapacity * 2;
|
if (scope == null || !scope.LifecycleActive)
|
||||||
Array.Resize(ref _registrationPool, newCapacity);
|
|
||||||
Array.Resize(ref _registrationCounts, newCapacity);
|
|
||||||
Array.Resize(ref _actionIds, newCapacity);
|
|
||||||
Array.Resize(ref _handlers, newCapacity);
|
|
||||||
Array.Resize(ref _actionRefs, newCapacity);
|
|
||||||
|
|
||||||
for (int i = _actionCapacity; i < newCapacity; i++)
|
|
||||||
{
|
{
|
||||||
_registrationPool[i] = new HotkeyRegistration[MAX_REGISTRATIONS_PER_ACTION];
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_actionCapacity = newCapacity;
|
UIHolderObjectBase holder = scope.Holder;
|
||||||
|
if (holder == null || !holder.IsValid())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!holder.gameObject.activeInHierarchy)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas canvas = scope.Canvas;
|
||||||
|
return canvas != null && canvas.gameObject.layer == UIComponent.UIShowLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ExpandButtonCapacity()
|
private static bool IsHolderVisible(UIHolderObjectBase holder)
|
||||||
{
|
{
|
||||||
int newCapacity = _buttonCapacity * 2;
|
if (holder == null || !holder.gameObject.activeInHierarchy)
|
||||||
Array.Resize(ref _buttons, newCapacity);
|
|
||||||
Array.Resize(ref _buttonToActionIndex, newCapacity);
|
|
||||||
|
|
||||||
for (int i = _buttonCapacity; i < newCapacity; i++)
|
|
||||||
{
|
{
|
||||||
_buttonToActionIndex[i] = -1;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_buttonCapacity = newCapacity;
|
Canvas canvas = holder.GetComponent<Canvas>();
|
||||||
|
return canvas != null && canvas.gameObject.layer == UIComponent.UIShowLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
private static int CompareScopePriority(HotkeyScope left, HotkeyScope right)
|
||||||
public static void RegisterHotkeyBatch(Span<IHotkeyTrigger> buttons, InputActionReference action, EHotkeyPressType pressType)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < buttons.Length; i++)
|
int leftDepth = left.Canvas != null ? left.Canvas.sortingOrder : int.MinValue;
|
||||||
|
int rightDepth = right.Canvas != null ? right.Canvas.sortingOrder : int.MinValue;
|
||||||
|
int depthCompare = rightDepth.CompareTo(leftDepth);
|
||||||
|
if (depthCompare != 0)
|
||||||
{
|
{
|
||||||
RegisterHotkey(buttons[i], action, pressType);
|
return depthCompare;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int hierarchyCompare = right.HierarchyDepth.CompareTo(left.HierarchyDepth);
|
||||||
|
if (hierarchyCompare != 0)
|
||||||
|
{
|
||||||
|
return hierarchyCompare;
|
||||||
|
}
|
||||||
|
|
||||||
|
return right.ActivationSerial.CompareTo(left.ActivationSerial);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
private static UIHolderObjectBase FindParentHolder(UIHolderObjectBase holder)
|
||||||
public static void UnregisterHotkeyBatch(Span<IHotkeyTrigger> buttons)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < buttons.Length; i++)
|
if (holder == null)
|
||||||
{
|
{
|
||||||
UnregisterHotkey(buttons[i]);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Transform current = holder.transform.parent;
|
||||||
|
while (current != null)
|
||||||
|
{
|
||||||
|
if (current.TryGetComponent<UIHolderObjectBase>(out var parentHolder))
|
||||||
|
{
|
||||||
|
return parentHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current.parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
public static string GetDebugInfo()
|
public static string GetDebugInfo()
|
||||||
{
|
{
|
||||||
return $"Actions: {_actionCount}/{_actionCapacity}, Buttons: {_buttonCount}/{_buttonCapacity}";
|
return $"Actions: {_actions.Count}, Triggers: {_triggerMap.Count}, Scopes: {_scopes.Count}";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class UXHotkeyHotkeyExtension
|
namespace UnityEngine.UI
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
public static class UXHotkeyHotkeyExtension
|
||||||
public static void BindHotKey(this IHotkeyTrigger button)
|
|
||||||
{
|
{
|
||||||
if (button?.HotkeyAction != null)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static void BindHotKey(this IHotkeyTrigger trigger)
|
||||||
{
|
{
|
||||||
UXHotkeyRegisterManager.RegisterHotkey(
|
if (trigger?.HotkeyAction == null)
|
||||||
button,
|
{
|
||||||
button.HotkeyAction,
|
return;
|
||||||
button.HotkeyPressType
|
}
|
||||||
);
|
|
||||||
|
if (trigger is not Component component)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UIHolderObjectBase holder = component.GetComponentInParent<UIHolderObjectBase>(true);
|
||||||
|
if (holder == null)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"{nameof(HotkeyComponent)} could not find a {nameof(UIHolderObjectBase)} owner.", component);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UXHotkeyRegisterManager.RegisterHotkey(trigger, holder, trigger.HotkeyAction, trigger.HotkeyPressType);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void UnBindHotKey(this IHotkeyTrigger button)
|
public static void UnBindHotKey(this IHotkeyTrigger trigger)
|
||||||
{
|
|
||||||
if (button?.HotkeyAction != null)
|
|
||||||
{
|
{
|
||||||
UXHotkeyRegisterManager.UnregisterHotkey(button);
|
if (trigger?.HotkeyAction != null)
|
||||||
|
{
|
||||||
|
UXHotkeyRegisterManager.UnregisterHotkey(trigger);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void BindHotKeyBatch(this IHotkeyTrigger[] buttons)
|
public static void BindHotKeyBatch(this IHotkeyTrigger[] triggers)
|
||||||
{
|
|
||||||
if (buttons == null) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < buttons.Length; i++)
|
|
||||||
{
|
{
|
||||||
buttons[i]?.BindHotKey();
|
if (triggers == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < triggers.Length; i++)
|
||||||
|
{
|
||||||
|
triggers[i]?.BindHotKey();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public static void UnBindHotKeyBatch(this IHotkeyTrigger[] buttons)
|
public static void UnBindHotKeyBatch(this IHotkeyTrigger[] triggers)
|
||||||
{
|
|
||||||
if (buttons == null) return;
|
|
||||||
|
|
||||||
for (int i = 0; i < buttons.Length; i++)
|
|
||||||
{
|
{
|
||||||
buttons[i]?.UnBindHotKey();
|
if (triggers == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < triggers.Length; i++)
|
||||||
|
{
|
||||||
|
triggers[i]?.UnBindHotKey();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user