1
This commit is contained in:
parent
8bbfa59524
commit
d8fd91d32c
3
Editor/UX/Hotkey.meta
Normal file
3
Editor/UX/Hotkey.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be711d9c48d440c4afccffac091ccc7d
|
||||
timeCreated: 1760340542
|
||||
111
Editor/UX/Hotkey/HotkeyBindComponentInspector.cs
Normal file
111
Editor/UX/Hotkey/HotkeyBindComponentInspector.cs
Normal file
@ -0,0 +1,111 @@
|
||||
#if INPUTSYSTEM_SUPPORT
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using AlicizaX.Editor;
|
||||
using AlicizaX.UI.Extension;
|
||||
using AlicizaX.UI.Extension.UXComponent.Hotkey;
|
||||
using AlicizaX.UI.Runtime;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEngine.UI.Hotkey
|
||||
{
|
||||
[CustomEditor(typeof(HotkeyBindComponent))]
|
||||
public class HotkeyBindComponentInspector : GameFrameworkInspector
|
||||
{
|
||||
private SerializedProperty hotButtonsProp;
|
||||
private HotkeyBindComponent _target;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_target = (HotkeyBindComponent)target;
|
||||
hotButtonsProp = serializedObject.FindProperty("hotButtons");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
// 🔸 检查是否缺少 UIHolderObjectBase
|
||||
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.arraySize == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("当前没有绑定任何 UXHotkey。", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel++;
|
||||
for (int i = 0; i < listProp.arraySize; i++)
|
||||
{
|
||||
var element = listProp.GetArrayElementAtIndex(i);
|
||||
var uxHotkey = element.objectReferenceValue as UXHotkey;
|
||||
string name = uxHotkey != null ? uxHotkey.name : "Null";
|
||||
EditorGUILayout.ObjectField($"[{i}] {name}", element.objectReferenceValue, typeof(UXHotkey), true);
|
||||
}
|
||||
|
||||
EditorGUI.indentLevel--;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找所有子物体(包含隐藏)并绑定 UXHotkey
|
||||
/// </summary>
|
||||
private void FindAllUXHotkeys()
|
||||
{
|
||||
Undo.RecordObject(_target, "Scan UXHotkey");
|
||||
|
||||
var uxHotkeys = _target.GetComponentsInChildren<UXHotkey>(true);
|
||||
List<UXHotkey> valiedHotkeys = new List<UXHotkey>();
|
||||
foreach (var item in uxHotkeys)
|
||||
{
|
||||
var field = item.GetType()
|
||||
.GetField("_hotKeyRefrence", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var hotRefrenceObject = field.GetValue(item);
|
||||
if (hotRefrenceObject != null) valiedHotkeys.Add(item);
|
||||
}
|
||||
|
||||
_target.GetType()
|
||||
.GetField("hotButtons", BindingFlags.NonPublic | BindingFlags.Instance)
|
||||
?.SetValue(_target, valiedHotkeys.ToArray());
|
||||
|
||||
EditorUtility.SetDirty(_target);
|
||||
serializedObject.Update();
|
||||
|
||||
Debug.Log($"[HotkeyBindComponent] 已找到 {uxHotkeys.Length} 个 UXHotkey 组件并绑定。");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
3
Editor/UX/Hotkey/HotkeyBindComponentInspector.cs.meta
Normal file
3
Editor/UX/Hotkey/HotkeyBindComponentInspector.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9779e310e684e9497bdac4f4ac3983e
|
||||
timeCreated: 1760340548
|
||||
45
Editor/UX/Hotkey/HotkeyInspector.cs
Normal file
45
Editor/UX/Hotkey/HotkeyInspector.cs
Normal file
@ -0,0 +1,45 @@
|
||||
#if INPUTSYSTEM_SUPPORT
|
||||
using AlicizaX.Editor;
|
||||
using AlicizaX.UI.Extension;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEngine.UI.Hotkey
|
||||
{
|
||||
[CustomEditor(typeof(UXHotkey))]
|
||||
public class HotkeyInspector : GameFrameworkInspector
|
||||
{
|
||||
private UXHotkey _target;
|
||||
private SerializedProperty _button;
|
||||
private SerializedProperty _hotKeyRefrence;
|
||||
private SerializedProperty _hotkeyPressType;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_target = (UXHotkey)target;
|
||||
_button = serializedObject.FindProperty("_button");
|
||||
_hotKeyRefrence = serializedObject.FindProperty("_hotKeyRefrence");
|
||||
_hotkeyPressType = serializedObject.FindProperty("_hotkeyPressType");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
var holder = _target.GetComponent<UXButton>();
|
||||
if (holder == null)
|
||||
{
|
||||
EditorGUILayout.HelpBox(
|
||||
"⚠ 当前对象缺少 UXButton 组件",
|
||||
MessageType.Error
|
||||
);
|
||||
}
|
||||
|
||||
EditorGUILayout.PropertyField(_button);
|
||||
EditorGUILayout.PropertyField(_hotKeyRefrence);
|
||||
EditorGUILayout.PropertyField(_hotkeyPressType);
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
3
Editor/UX/Hotkey/HotkeyInspector.cs.meta
Normal file
3
Editor/UX/Hotkey/HotkeyInspector.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 87a10dc3a1544ec586ff27809213a127
|
||||
timeCreated: 1760341800
|
||||
@ -42,15 +42,11 @@ internal class UXButtonEditor : Editor
|
||||
|
||||
private SerializedProperty hoverAudioClip;
|
||||
private SerializedProperty clickAudioClip;
|
||||
|
||||
#if INPUTSYSTEM_SUPPORT
|
||||
private SerializedProperty _hotKeyRefrence;
|
||||
private SerializedProperty _hotkeyPressType;
|
||||
#endif
|
||||
|
||||
private UXButton mTarget;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
mTarget = (UXButton)target;
|
||||
customSkin = AssetDatabase.LoadAssetAtPath<GUISkin>("Packages/com.alicizax.unity.ui.extension/Editor/Res/GUISkin/UIExtensionGUISkin.guiskin");
|
||||
m_Interactable = serializedObject.FindProperty("m_Interactable");
|
||||
m_UXGroup = serializedObject.FindProperty("m_UXGroup");
|
||||
@ -67,10 +63,6 @@ internal class UXButtonEditor : Editor
|
||||
hoverAudioClip = serializedObject.FindProperty("hoverAudioClip");
|
||||
clickAudioClip = serializedObject.FindProperty("clickAudioClip");
|
||||
|
||||
#if INPUTSYSTEM_SUPPORT
|
||||
_hotKeyRefrence = serializedObject.FindProperty("_hotKeyRefrence");
|
||||
_hotkeyPressType = serializedObject.FindProperty("_hotkeyPressType");
|
||||
#endif
|
||||
CreateChildTransitionList();
|
||||
}
|
||||
|
||||
@ -199,6 +191,11 @@ internal class UXButtonEditor : Editor
|
||||
EditorGUI.EndDisabledGroup();
|
||||
|
||||
var interactable = GUILayoutHelper.DrawToggle(m_Interactable.boolValue, customSkin, "Interactable");
|
||||
if (interactable != m_Interactable.boolValue)
|
||||
{
|
||||
mTarget.Interactable = interactable;
|
||||
m_SelectionState.enumValueIndex = interactable ? 0 : 4;
|
||||
}
|
||||
m_Interactable.boolValue = interactable;
|
||||
|
||||
GUILayout.Space(1);
|
||||
@ -225,10 +222,6 @@ internal class UXButtonEditor : Editor
|
||||
}
|
||||
|
||||
EditorGUILayout.Separator();
|
||||
#if INPUTSYSTEM_SUPPORT
|
||||
EditorGUILayout.PropertyField(_hotkeyPressType);
|
||||
EditorGUILayout.PropertyField(_hotKeyRefrence);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void DrawAudioTab()
|
||||
@ -242,9 +235,9 @@ internal class UXButtonEditor : Editor
|
||||
});
|
||||
GUILayoutHelper.DrawProperty(clickAudioClip, customSkin, "Click Sound", "Play", () =>
|
||||
{
|
||||
if (hoverAudioClip.objectReferenceValue != null)
|
||||
if (clickAudioClip.objectReferenceValue != null)
|
||||
{
|
||||
PlayAudio((AudioClip)hoverAudioClip.objectReferenceValue);
|
||||
PlayAudio((AudioClip)clickAudioClip.objectReferenceValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
50
Editor/UX/UXGroupInspector.cs
Normal file
50
Editor/UX/UXGroupInspector.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using AlicizaX.Editor;
|
||||
using UnityEditor;
|
||||
|
||||
namespace UnityEngine.UI
|
||||
{
|
||||
[CustomEditor(typeof(UXGroup))]
|
||||
public class UXGroupInspector : GameFrameworkInspector
|
||||
{
|
||||
private SerializedProperty m_Buttons;
|
||||
private UXGroup _target;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
_target = (UXGroup)target;
|
||||
m_Buttons = serializedObject.FindProperty("m_Buttons");
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
serializedObject.Update();
|
||||
|
||||
GUI.enabled = false;
|
||||
DrawHotButtonListAlwaysExpanded(m_Buttons);
|
||||
GUI.enabled = true;
|
||||
|
||||
EditorGUILayout.Space();
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
private void DrawHotButtonListAlwaysExpanded(SerializedProperty listProp)
|
||||
{
|
||||
EditorGUILayout.LabelField("Hot Buttons", EditorStyles.boldLabel);
|
||||
|
||||
if (listProp.arraySize == 0)
|
||||
{
|
||||
EditorGUILayout.HelpBox("当前没有绑定任何 UXButton", MessageType.Info);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < listProp.arraySize; i++)
|
||||
{
|
||||
var element = listProp.GetArrayElementAtIndex(i);
|
||||
var uxButton = element.objectReferenceValue as UXButton;
|
||||
string name = uxButton != null ? uxButton.name : "Null";
|
||||
EditorGUILayout.ObjectField($"[{i}] {name}", element.objectReferenceValue, typeof(UXButton), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Editor/UX/UXGroupInspector.cs.meta
Normal file
3
Editor/UX/UXGroupInspector.cs.meta
Normal file
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 292ca921cd4242d4be9f76bef9bfc08f
|
||||
timeCreated: 1760343702
|
||||
@ -1,14 +1,13 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using AlicizaX;
|
||||
using AlicizaX.UI.Extension;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
|
||||
|
||||
[Serializable]
|
||||
public enum ButtonModeType
|
||||
{
|
||||
@ -16,12 +15,12 @@ public enum ButtonModeType
|
||||
Toggle
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
[Serializable]
|
||||
public class TransitionData
|
||||
{
|
||||
public Graphic targetGraphic;
|
||||
public Selectable.Transition transition = Selectable.Transition.ColorTint;
|
||||
public ColorBlock colors;
|
||||
public ColorBlock colors = ColorBlock.defaultColorBlock;
|
||||
public SpriteState spriteState;
|
||||
public AnimationTriggers animationTriggers = new();
|
||||
}
|
||||
@ -51,69 +50,55 @@ public class UXButton : UIBehaviour, IButton,
|
||||
[SerializeField] private UXGroup m_UXGroup;
|
||||
[SerializeField] private AudioClip hoverAudioClip;
|
||||
[SerializeField] private AudioClip clickAudioClip;
|
||||
#if INPUTSYSTEM_SUPPORT
|
||||
[SerializeField] internal UnityEngine.InputSystem.InputActionReference _hotKeyRefrence;
|
||||
[SerializeField] internal EHotkeyPressType _hotkeyPressType;
|
||||
|
||||
internal void OnHotkeyTriggered()
|
||||
{
|
||||
if (m_Interactable)
|
||||
{
|
||||
OnSubmit(null);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool HasHotKeyRefrenced()
|
||||
{
|
||||
return _hotKeyRefrence != null;
|
||||
}
|
||||
|
||||
#endif
|
||||
[SerializeField] private UnityEvent<bool> m_OnValueChanged = new();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Variables
|
||||
#region Private Fields
|
||||
|
||||
[SerializeField] private SelectionState m_SelectionState = SelectionState.Normal;
|
||||
private bool m_DownAndExistUI;
|
||||
private bool m_IsDown;
|
||||
private bool m_IsTogSelected;
|
||||
private bool m_HasExitedWhileDown;
|
||||
private bool _mTogSelected;
|
||||
private Animator _animator;
|
||||
private WaitForSeconds _waitTimeFadeDuration;
|
||||
private Coroutine _resetRoutine;
|
||||
private WaitForSeconds _waitFadeDuration;
|
||||
|
||||
private readonly Dictionary<string, int> _animTriggerIDs = new();
|
||||
private readonly Dictionary<string, int> _animResetTriggerIDs = new();
|
||||
private static readonly Dictionary<string, int> _animTriggerCache = new()
|
||||
{
|
||||
{ "Normal", Animator.StringToHash("Normal") },
|
||||
{ "Highlighted", Animator.StringToHash("Highlighted") },
|
||||
{ "Pressed", Animator.StringToHash("Pressed") },
|
||||
{ "Selected", Animator.StringToHash("Selected") },
|
||||
{ "Disabled", Animator.StringToHash("Disabled") },
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
internal bool Interactable
|
||||
private Animator Animator => _animator ? _animator : _animator = GetComponent<Animator>();
|
||||
|
||||
public bool Interactable
|
||||
{
|
||||
get => m_Interactable;
|
||||
}
|
||||
|
||||
internal Animator animator
|
||||
{
|
||||
get
|
||||
set
|
||||
{
|
||||
if (!_animator)
|
||||
_animator = GetComponent<Animator>();
|
||||
return _animator;
|
||||
if (m_Interactable == value) return;
|
||||
m_Interactable = value;
|
||||
SetState(m_Interactable ? SelectionState.Normal : SelectionState.Disabled);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSelected
|
||||
public bool Selected
|
||||
{
|
||||
get => m_IsTogSelected;
|
||||
get => _mTogSelected;
|
||||
internal set
|
||||
{
|
||||
if (m_IsTogSelected == value) return;
|
||||
m_IsTogSelected = value;
|
||||
onValueChanged?.Invoke(m_IsTogSelected);
|
||||
m_SelectionState = value ? SelectionState.Selected : SelectionState.Normal;
|
||||
UpdateVisualState(m_SelectionState, false);
|
||||
if (_mTogSelected == value) return;
|
||||
_mTogSelected = value;
|
||||
m_OnValueChanged?.Invoke(value);
|
||||
SetState(value ? SelectionState.Selected : SelectionState.Normal);
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,8 +108,6 @@ public class UXButton : UIBehaviour, IButton,
|
||||
set => m_OnClick = value;
|
||||
}
|
||||
|
||||
[SerializeField] private UnityEvent<bool> m_OnValueChanged = new();
|
||||
|
||||
public UnityEvent<bool> onValueChanged
|
||||
{
|
||||
get => m_OnValueChanged;
|
||||
@ -138,166 +121,96 @@ public class UXButton : UIBehaviour, IButton,
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
Initlize();
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
protected override void OnValidate()
|
||||
{
|
||||
base.OnValidate();
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Initlize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // if UNITY_EDITOR
|
||||
|
||||
protected void Initlize()
|
||||
{
|
||||
_waitTimeFadeDuration = new WaitForSeconds(
|
||||
Mathf.Max(0.01f, m_TransitionData.colors.fadeDuration));
|
||||
_animTriggerIDs.Clear();
|
||||
_animResetTriggerIDs.Clear();
|
||||
var triggers = m_TransitionData.animationTriggers;
|
||||
AddTriggerID(triggers.normalTrigger);
|
||||
AddTriggerID(triggers.highlightedTrigger);
|
||||
AddTriggerID(triggers.pressedTrigger);
|
||||
AddTriggerID(triggers.selectedTrigger);
|
||||
AddTriggerID(triggers.disabledTrigger);
|
||||
|
||||
|
||||
UpdateVisualState(m_SelectionState, true);
|
||||
_waitFadeDuration = new WaitForSeconds(Mathf.Max(0.01f, m_TransitionData.colors.fadeDuration));
|
||||
ApplyVisualState(m_SelectionState, true);
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
if (_resetRoutine != null)
|
||||
{
|
||||
StopCoroutine(_resetRoutine);
|
||||
_resetRoutine = null;
|
||||
}
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
#region Pointer Handlers
|
||||
|
||||
void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
|
||||
public void OnPointerDown(PointerEventData eventData)
|
||||
{
|
||||
if (!ShouldProcessEvent(eventData)) return;
|
||||
|
||||
if (!CanProcess(eventData)) return;
|
||||
m_IsDown = true;
|
||||
m_SelectionState = SelectionState.Pressed;
|
||||
UpdateVisualState(m_SelectionState, false);
|
||||
SetState(SelectionState.Pressed);
|
||||
}
|
||||
|
||||
void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
|
||||
public void OnPointerUp(PointerEventData eventData)
|
||||
{
|
||||
if (!m_Interactable || eventData.button != PointerEventData.InputButton.Left)
|
||||
return;
|
||||
|
||||
m_IsDown = false;
|
||||
if (m_IsTogSelected)
|
||||
{
|
||||
m_SelectionState = SelectionState.Selected;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_SelectionState = m_DownAndExistUI ? SelectionState.Normal : SelectionState.Highlighted;
|
||||
}
|
||||
var newState = _mTogSelected
|
||||
? SelectionState.Selected
|
||||
: m_HasExitedWhileDown
|
||||
? SelectionState.Normal
|
||||
: SelectionState.Highlighted;
|
||||
|
||||
UpdateVisualState(m_SelectionState, false);
|
||||
SetState(newState);
|
||||
}
|
||||
|
||||
void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)
|
||||
public void OnPointerEnter(PointerEventData eventData)
|
||||
{
|
||||
if (!ShouldProcessEvent(eventData)) return;
|
||||
|
||||
m_DownAndExistUI = false;
|
||||
if (!CanProcess(eventData)) return;
|
||||
m_HasExitedWhileDown = false;
|
||||
if (m_IsDown) return;
|
||||
|
||||
m_SelectionState = SelectionState.Highlighted;
|
||||
UpdateVisualState(m_SelectionState, false);
|
||||
PlayButtonSound(hoverAudioClip);
|
||||
SetState(SelectionState.Highlighted);
|
||||
PlayAudio(hoverAudioClip);
|
||||
}
|
||||
|
||||
void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
|
||||
public void OnPointerExit(PointerEventData eventData)
|
||||
{
|
||||
if (!m_Interactable) return;
|
||||
if (m_IsDown)
|
||||
{
|
||||
m_DownAndExistUI = true;
|
||||
m_HasExitedWhileDown = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_SelectionState = IsSelected ? SelectionState.Selected : SelectionState.Normal;
|
||||
UpdateVisualState(m_SelectionState, false);
|
||||
SetState(_mTogSelected ? SelectionState.Selected : SelectionState.Normal);
|
||||
}
|
||||
|
||||
void IPointerClickHandler.OnPointerClick(PointerEventData eventData)
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.button != PointerEventData.InputButton.Left || !m_Interactable)
|
||||
return;
|
||||
|
||||
PlayButtonSound(clickAudioClip);
|
||||
ProcessClick();
|
||||
PlayAudio(clickAudioClip);
|
||||
HandleClick();
|
||||
}
|
||||
|
||||
|
||||
public void OnSubmit(BaseEventData eventData)
|
||||
{
|
||||
UpdateVisualState(SelectionState.Pressed, false);
|
||||
ProcessClick();
|
||||
SetState(SelectionState.Pressed);
|
||||
HandleClick();
|
||||
|
||||
if (_resetRoutine != null)
|
||||
StopCoroutine(OnFinishSubmit());
|
||||
|
||||
_resetRoutine = StartCoroutine(OnFinishSubmit());
|
||||
}
|
||||
|
||||
public bool Disable
|
||||
{
|
||||
get => m_Interactable;
|
||||
set
|
||||
{
|
||||
if (m_Interactable == value) return;
|
||||
m_Interactable = value;
|
||||
m_SelectionState = !m_Interactable ? SelectionState.Disabled : SelectionState.Normal;
|
||||
UpdateVisualState(m_SelectionState, false);
|
||||
}
|
||||
StopCoroutine(_resetRoutine);
|
||||
_resetRoutine = StartCoroutine(ResetAfterSubmit());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
#region Logic
|
||||
|
||||
public bool Selected
|
||||
{
|
||||
get => IsSelected;
|
||||
set
|
||||
{
|
||||
if (m_Mode == ButtonModeType.Toggle)
|
||||
{
|
||||
ProcessClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private bool ShouldProcessEvent(PointerEventData eventData)
|
||||
private bool CanProcess(PointerEventData eventData)
|
||||
{
|
||||
return m_Interactable &&
|
||||
eventData.button == PointerEventData.InputButton.Left &&
|
||||
!(m_Mode == ButtonModeType.Toggle && IsSelected);
|
||||
!(m_Mode == ButtonModeType.Toggle && Selected);
|
||||
}
|
||||
|
||||
private void ProcessClick()
|
||||
private void HandleClick()
|
||||
{
|
||||
if (m_Mode == ButtonModeType.Normal)
|
||||
{
|
||||
@ -310,142 +223,87 @@ public class UXButton : UIBehaviour, IButton,
|
||||
}
|
||||
else
|
||||
{
|
||||
IsSelected = !IsSelected;
|
||||
Selected = !Selected;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVisualState(SelectionState state, bool instant)
|
||||
private void SetState(SelectionState state)
|
||||
{
|
||||
ProcessTransitionData(m_TransitionData, state, instant);
|
||||
foreach (var transition in m_ChildTransitions)
|
||||
{
|
||||
ProcessTransitionData(transition, state, instant);
|
||||
}
|
||||
if (m_SelectionState == state) return;
|
||||
m_SelectionState = state;
|
||||
ApplyVisualState(state, false);
|
||||
}
|
||||
|
||||
private void ProcessTransitionData(TransitionData transition, SelectionState state, bool instant)
|
||||
private void ApplyVisualState(SelectionState state, bool instant)
|
||||
{
|
||||
if ((transition.transition == Selectable.Transition.ColorTint || transition.transition == Selectable.Transition.SpriteSwap) && transition.targetGraphic == null) return;
|
||||
|
||||
Color tintColor;
|
||||
Sprite transitionSprite;
|
||||
string triggerName;
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case SelectionState.Normal:
|
||||
tintColor = transition.colors.normalColor;
|
||||
transitionSprite = transition.spriteState.highlightedSprite;
|
||||
triggerName = transition.animationTriggers.normalTrigger;
|
||||
break;
|
||||
case SelectionState.Highlighted:
|
||||
tintColor = transition.colors.highlightedColor;
|
||||
transitionSprite = transition.spriteState.highlightedSprite;
|
||||
triggerName = transition.animationTriggers.highlightedTrigger;
|
||||
break;
|
||||
case SelectionState.Pressed:
|
||||
tintColor = transition.colors.pressedColor;
|
||||
transitionSprite = transition.spriteState.pressedSprite;
|
||||
triggerName = transition.animationTriggers.pressedTrigger;
|
||||
break;
|
||||
case SelectionState.Selected:
|
||||
tintColor = transition.colors.selectedColor;
|
||||
transitionSprite = transition.spriteState.selectedSprite;
|
||||
triggerName = transition.animationTriggers.selectedTrigger;
|
||||
break;
|
||||
case SelectionState.Disabled:
|
||||
tintColor = transition.colors.disabledColor;
|
||||
transitionSprite = transition.spriteState.disabledSprite;
|
||||
triggerName = transition.animationTriggers.disabledTrigger;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
switch (transition.transition)
|
||||
{
|
||||
case Selectable.Transition.ColorTint:
|
||||
StartColorTween(transition, tintColor * transition.colors.colorMultiplier, instant);
|
||||
break;
|
||||
case Selectable.Transition.SpriteSwap:
|
||||
DoSpriteSwap(transition, transitionSprite);
|
||||
break;
|
||||
case Selectable.Transition.Animation:
|
||||
TriggerAnimation(triggerName);
|
||||
break;
|
||||
}
|
||||
ApplyTransition(m_TransitionData, state, instant);
|
||||
for (int i = 0; i < m_ChildTransitions.Count; i++)
|
||||
ApplyTransition(m_ChildTransitions[i], state, instant);
|
||||
}
|
||||
|
||||
private void StartColorTween(TransitionData data, Color targetColor, bool instant)
|
||||
private void ApplyTransition(TransitionData data, SelectionState state, bool instant)
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
data.targetGraphic.CrossFadeColor(
|
||||
targetColor,
|
||||
instant ? 0f : data.colors.fadeDuration,
|
||||
true,
|
||||
true
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
data.targetGraphic.canvasRenderer.SetColor(targetColor);
|
||||
}
|
||||
}
|
||||
|
||||
private void DoSpriteSwap(TransitionData data, Sprite newSprite)
|
||||
{
|
||||
if (data.targetGraphic is Image image)
|
||||
{
|
||||
image.overrideSprite = newSprite;
|
||||
}
|
||||
}
|
||||
|
||||
private void TriggerAnimation(string trigger)
|
||||
{
|
||||
if (animator == null ||
|
||||
!animator.isActiveAndEnabled ||
|
||||
!animator.hasBoundPlayables ||
|
||||
string.IsNullOrEmpty(trigger))
|
||||
if (data.targetGraphic == null && data.transition != Selectable.Transition.Animation)
|
||||
return;
|
||||
|
||||
foreach (var resetTrigger in _animResetTriggerIDs.Keys)
|
||||
(Color color, Sprite sprite, string trigger) = state switch
|
||||
{
|
||||
animator.ResetTrigger(_animTriggerIDs[resetTrigger]);
|
||||
}
|
||||
SelectionState.Normal => (data.colors.normalColor, data.spriteState.highlightedSprite, data.animationTriggers.normalTrigger),
|
||||
SelectionState.Highlighted => (data.colors.highlightedColor, data.spriteState.highlightedSprite, data.animationTriggers.highlightedTrigger),
|
||||
SelectionState.Pressed => (data.colors.pressedColor, data.spriteState.pressedSprite, data.animationTriggers.pressedTrigger),
|
||||
SelectionState.Selected => (data.colors.selectedColor, data.spriteState.selectedSprite, data.animationTriggers.selectedTrigger),
|
||||
SelectionState.Disabled => (data.colors.disabledColor, data.spriteState.disabledSprite, data.animationTriggers.disabledTrigger),
|
||||
_ => (Color.white, null, null)
|
||||
};
|
||||
|
||||
if (_animTriggerIDs.TryGetValue(trigger, out int id))
|
||||
switch (data.transition)
|
||||
{
|
||||
animator.SetTrigger(id);
|
||||
case Selectable.Transition.ColorTint:
|
||||
TweenColor(data, color * data.colors.colorMultiplier, instant);
|
||||
break;
|
||||
case Selectable.Transition.SpriteSwap:
|
||||
SwapSprite(data, sprite);
|
||||
break;
|
||||
case Selectable.Transition.Animation:
|
||||
PlayAnimation(trigger);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddTriggerID(string triggerName)
|
||||
private void TweenColor(TransitionData data, Color color, bool instant)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(triggerName))
|
||||
{
|
||||
int id = Animator.StringToHash(triggerName);
|
||||
if (!_animTriggerIDs.ContainsKey(triggerName))
|
||||
{
|
||||
_animTriggerIDs.Add(triggerName, id);
|
||||
_animResetTriggerIDs.Add(triggerName, id);
|
||||
}
|
||||
}
|
||||
data.targetGraphic.CrossFadeColor(color, instant ? 0f : data.colors.fadeDuration, true, true);
|
||||
}
|
||||
|
||||
private void PlayButtonSound(AudioClip clip)
|
||||
private static void SwapSprite(TransitionData data, Sprite sprite)
|
||||
{
|
||||
if (clip == null || UXComponentExtensionsHelper.AudioHelper == null) return;
|
||||
UXComponentExtensionsHelper.AudioHelper.PlayAudio(clip);
|
||||
if (data.targetGraphic is Image img)
|
||||
img.overrideSprite = sprite;
|
||||
}
|
||||
|
||||
private IEnumerator OnFinishSubmit()
|
||||
private void PlayAnimation(string trigger)
|
||||
{
|
||||
yield return _waitTimeFadeDuration;
|
||||
UpdateVisualState(
|
||||
m_IsTogSelected ? SelectionState.Selected : SelectionState.Normal,
|
||||
false
|
||||
);
|
||||
if (!Animator || !Animator.isActiveAndEnabled || string.IsNullOrEmpty(trigger))
|
||||
return;
|
||||
|
||||
foreach (int id in _animTriggerCache.Values)
|
||||
Animator.ResetTrigger(id);
|
||||
|
||||
if (_animTriggerCache.TryGetValue(trigger, out int hash))
|
||||
Animator.SetTrigger(hash);
|
||||
}
|
||||
|
||||
|
||||
private void PlayAudio(AudioClip clip)
|
||||
{
|
||||
if (clip && UXComponentExtensionsHelper.AudioHelper != null)
|
||||
UXComponentExtensionsHelper.AudioHelper.PlayAudio(clip);
|
||||
}
|
||||
|
||||
private IEnumerator ResetAfterSubmit()
|
||||
{
|
||||
yield return _waitFadeDuration;
|
||||
SetState(_mTogSelected ? SelectionState.Selected : SelectionState.Normal);
|
||||
_resetRoutine = null;
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
using System.Collections.Generic;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
|
||||
[DisallowMultipleComponent]
|
||||
public class UXGroup : UIBehaviour
|
||||
{
|
||||
[SerializeField] private bool m_AllowSwitchOff;
|
||||
[ReadOnly, SerializeField] private List<UXButton> m_Buttons = new();
|
||||
[SerializeField] private List<UXButton> m_Buttons = new();
|
||||
|
||||
private UXButton _current;
|
||||
private readonly HashSet<UXButton> _registeredButtons = new();
|
||||
private readonly HashSet<UXButton> _registered = new();
|
||||
|
||||
public UnityEvent<UXButton> onSelectedChanged = new();
|
||||
|
||||
@ -21,169 +21,118 @@ public class UXGroup : UIBehaviour
|
||||
set
|
||||
{
|
||||
m_AllowSwitchOff = value;
|
||||
ValidateGroupState();
|
||||
ValidateGroup();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Awake() => ValidateGroup();
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
foreach (var button in _registeredButtons)
|
||||
{
|
||||
if (button) button.IsSelected = false;
|
||||
}
|
||||
|
||||
foreach (var btn in _registered)
|
||||
if (btn)
|
||||
btn.Selected = false;
|
||||
_registered.Clear();
|
||||
m_Buttons.Clear();
|
||||
_registeredButtons.Clear();
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
protected override void Awake() => ValidateGroupState();
|
||||
|
||||
public void RegisterButton(UXButton button)
|
||||
{
|
||||
if (!button || _registeredButtons.Contains(button)) return;
|
||||
if (!button || !_registered.Add(button)) return;
|
||||
if (!m_Buttons.Contains(button)) m_Buttons.Add(button);
|
||||
|
||||
m_Buttons.Add(button);
|
||||
_registeredButtons.Add(button);
|
||||
|
||||
if (button.IsSelected)
|
||||
if (button.Selected)
|
||||
{
|
||||
if (_current && _current != button)
|
||||
_current.IsSelected = false;
|
||||
_current.Selected = false;
|
||||
_current = button;
|
||||
}
|
||||
|
||||
ValidateGroupState();
|
||||
ValidateGroup();
|
||||
}
|
||||
|
||||
public void UnregisterButton(UXButton button)
|
||||
{
|
||||
if (!button || !_registeredButtons.Contains(button)) return;
|
||||
|
||||
if (!button || !_registered.Remove(button)) return;
|
||||
m_Buttons.Remove(button);
|
||||
_registeredButtons.Remove(button);
|
||||
button.IsSelected = false;
|
||||
|
||||
if (_current == button)
|
||||
_current = null;
|
||||
button.Selected = false;
|
||||
}
|
||||
|
||||
internal void NotifyButtonClicked(UXButton clickedButton)
|
||||
internal void NotifyButtonClicked(UXButton button)
|
||||
{
|
||||
if (clickedButton.IsSelected)
|
||||
if (!button) return;
|
||||
|
||||
if (button.Selected)
|
||||
{
|
||||
if (m_AllowSwitchOff)
|
||||
SetSelectedButton(null);
|
||||
if (m_AllowSwitchOff) SetSelected(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSelectedButton(clickedButton);
|
||||
SetSelected(button);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSelectedButton(UXButton target)
|
||||
private void SetSelected(UXButton target)
|
||||
{
|
||||
var previous = _current;
|
||||
_current = null;
|
||||
|
||||
foreach (var button in m_Buttons)
|
||||
foreach (var btn in m_Buttons)
|
||||
{
|
||||
bool shouldSelect = (button == target);
|
||||
if (button.IsSelected != shouldSelect)
|
||||
button.IsSelected = shouldSelect;
|
||||
|
||||
if (shouldSelect)
|
||||
_current = button;
|
||||
bool select = (btn == target);
|
||||
if (btn.Selected != select)
|
||||
btn.Selected = select;
|
||||
if (select) _current = btn;
|
||||
}
|
||||
|
||||
if (previous != _current)
|
||||
onSelectedChanged?.Invoke(_current);
|
||||
}
|
||||
|
||||
private void ValidateGroupState()
|
||||
private void ValidateGroup()
|
||||
{
|
||||
bool hasSelected = _current != null && _current.IsSelected;
|
||||
if (!hasSelected && m_Buttons.Count > 0 && !m_AllowSwitchOff)
|
||||
SetSelectedButton(m_Buttons[0]);
|
||||
if (_current != null && _current.Selected) return;
|
||||
|
||||
if (!m_AllowSwitchOff && m_Buttons.Count > 0)
|
||||
SetSelected(m_Buttons[0]);
|
||||
}
|
||||
|
||||
public bool AnyOtherSelected(UXButton exclusion)
|
||||
public bool AnyOtherSelected(UXButton exclude)
|
||||
{
|
||||
foreach (var button in m_Buttons)
|
||||
{
|
||||
if (button != exclusion && button.IsSelected)
|
||||
foreach (var btn in m_Buttons)
|
||||
if (btn != exclude && btn.Selected)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SelectNext() => SelectRelative(1);
|
||||
public void SelectPrevious() => SelectRelative(-1);
|
||||
|
||||
public void SelectPrevious()
|
||||
private void SelectRelative(int dir)
|
||||
{
|
||||
if (m_Buttons.Count == 0) return;
|
||||
|
||||
int startIndex = _current ? m_Buttons.IndexOf(_current) : -1;
|
||||
int newIndex = FindSelectableIndex(startIndex, -1);
|
||||
|
||||
if (newIndex != -1)
|
||||
{
|
||||
SetSelectedButton(m_Buttons[newIndex]);
|
||||
}
|
||||
int start = _current ? m_Buttons.IndexOf(_current) : -1;
|
||||
int next = FindNextSelectable(start, dir);
|
||||
if (next >= 0) SetSelected(m_Buttons[next]);
|
||||
}
|
||||
|
||||
|
||||
public void SelectNext()
|
||||
{
|
||||
if (m_Buttons.Count == 0) return;
|
||||
|
||||
int startIndex = _current ? m_Buttons.IndexOf(_current) : -1;
|
||||
int newIndex = FindSelectableIndex(startIndex, 1);
|
||||
|
||||
if (newIndex != -1)
|
||||
{
|
||||
SetSelectedButton(m_Buttons[newIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int FindSelectableIndex(int startIndex, int direction)
|
||||
private int FindNextSelectable(int startIndex, int dir)
|
||||
{
|
||||
if (m_Buttons.Count == 0) return -1;
|
||||
int buttonCount = m_Buttons.Count - 1;
|
||||
int valueIndex = startIndex == -1 ? 0 : startIndex + direction;
|
||||
int fallBackIndex = -1;
|
||||
int count = m_Buttons.Count;
|
||||
int index = (startIndex + dir + count) % count;
|
||||
|
||||
valueIndex = valueIndex > buttonCount ? 0 : (valueIndex < 0 ? buttonCount : valueIndex);
|
||||
if (valueIndex > buttonCount)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
valueIndex = 0;
|
||||
}
|
||||
else if (valueIndex < 0)
|
||||
{
|
||||
valueIndex = buttonCount;
|
||||
var btn = m_Buttons[index];
|
||||
if (btn && btn.isActiveAndEnabled && btn.Interactable)
|
||||
return index;
|
||||
index = (index + dir + count) % count;
|
||||
}
|
||||
|
||||
while (valueIndex != startIndex)
|
||||
{
|
||||
UXButton btn = m_Buttons[valueIndex];
|
||||
if (btn.isActiveAndEnabled && btn.Interactable)
|
||||
{
|
||||
fallBackIndex = valueIndex;
|
||||
break;
|
||||
}
|
||||
|
||||
valueIndex += direction;
|
||||
if (valueIndex > buttonCount)
|
||||
{
|
||||
valueIndex = 0;
|
||||
}
|
||||
else if (valueIndex < 0)
|
||||
{
|
||||
valueIndex = buttonCount;
|
||||
}
|
||||
}
|
||||
|
||||
return fallBackIndex;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if INPUTSYSTEM_SUPPORT
|
||||
using AlicizaX.UI.Runtime;
|
||||
using Sirenix.OdinInspector;
|
||||
using UnityEngine;
|
||||
|
||||
namespace AlicizaX.UI.Extension.UXComponent.Hotkey
|
||||
@ -22,30 +20,8 @@ namespace AlicizaX.UI.Extension.UXComponent.Hotkey
|
||||
_holderObjectBase.OnWindowShowEvent -= BindHotKeys;
|
||||
_holderObjectBase.OnWindowClosedEvent -= UnBindHotKeys;
|
||||
}
|
||||
#if UNITY_EDITOR
|
||||
[InlineButton("SetHotKeyButtons")]
|
||||
#endif
|
||||
[SerializeField]
|
||||
[HideLabel]
|
||||
private UXButton[] hotButtons;
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void SetHotKeyButtons()
|
||||
{
|
||||
var btns = transform.GetComponentsInChildren<UXButton>(true);
|
||||
var hotBtnList = new List<UXButton>();
|
||||
for (int i = 0; i < btns.Length; i++)
|
||||
{
|
||||
if (btns[i].HasHotKeyRefrenced())
|
||||
{
|
||||
hotBtnList.Add(btns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
hotButtons = hotBtnList.ToArray();
|
||||
}
|
||||
#endif
|
||||
private UXHotkey[] hotButtons;
|
||||
|
||||
internal void BindHotKeys()
|
||||
{
|
||||
@ -64,3 +40,5 @@ namespace AlicizaX.UI.Extension.UXComponent.Hotkey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,174 +1,28 @@
|
||||
#if INPUTSYSTEM_SUPPORT
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
|
||||
internal enum EHotkeyPressType
|
||||
namespace AlicizaX.UI.Extension
|
||||
{
|
||||
Started,
|
||||
Performed
|
||||
}
|
||||
|
||||
internal static class UXHotkeyComponent
|
||||
{
|
||||
private readonly struct HotkeyRegistration
|
||||
public class UXHotkey : MonoBehaviour
|
||||
{
|
||||
public readonly EHotkeyPressType pressType;
|
||||
public readonly UXButton button;
|
||||
[SerializeField] private UXButton _button;
|
||||
|
||||
public HotkeyRegistration(UXButton btn, EHotkeyPressType pressType)
|
||||
[SerializeField] internal UnityEngine.InputSystem.InputActionReference _hotKeyRefrence;
|
||||
[SerializeField] internal EHotkeyPressType _hotkeyPressType;
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
button = btn;
|
||||
this.pressType = pressType;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, List<HotkeyRegistration>> _hotkeyRegistry =
|
||||
new Dictionary<string, List<HotkeyRegistration>>(32);
|
||||
|
||||
|
||||
private static readonly Dictionary<string, (System.Action<InputAction.CallbackContext> handler, InputActionReference action)> _sharedHandlers =
|
||||
new Dictionary<string, (System.Action<InputAction.CallbackContext>, InputActionReference)>(32);
|
||||
|
||||
|
||||
private static readonly Dictionary<UXButton, string> _buttonRegistrations =
|
||||
new Dictionary<UXButton, string>(64);
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.Callbacks.DidReloadScripts]
|
||||
internal static void ClearHotkeyRegistry()
|
||||
{
|
||||
foreach (var key in _buttonRegistrations.Keys)
|
||||
{
|
||||
UnregisterHotkey(key);
|
||||
_button = GetComponent<UXButton>();
|
||||
}
|
||||
|
||||
_sharedHandlers.Clear();
|
||||
_hotkeyRegistry.Clear();
|
||||
_buttonRegistrations.Clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static void RegisterHotkey(UXButton button, InputActionReference action, EHotkeyPressType pressType)
|
||||
{
|
||||
if (action == null || action.action == null || button == null)
|
||||
return;
|
||||
|
||||
string actionId = action.action.id.ToString();
|
||||
|
||||
|
||||
HotkeyRegistration registration = new HotkeyRegistration(button, pressType);
|
||||
|
||||
|
||||
if (!_hotkeyRegistry.TryGetValue(actionId, out var registrations))
|
||||
internal void HotkeyActionTrigger()
|
||||
{
|
||||
registrations = new List<HotkeyRegistration>(4);
|
||||
_hotkeyRegistry[actionId] = registrations;
|
||||
}
|
||||
|
||||
registrations.Add(registration);
|
||||
|
||||
|
||||
_buttonRegistrations[button] = actionId;
|
||||
|
||||
if (!_sharedHandlers.ContainsKey(actionId))
|
||||
{
|
||||
System.Action<InputAction.CallbackContext> handler = ctx => OnHotkeyTriggered(actionId);
|
||||
_sharedHandlers[actionId] = (handler, action);
|
||||
|
||||
switch (pressType)
|
||||
if (_button.Interactable)
|
||||
{
|
||||
case EHotkeyPressType.Started:
|
||||
action.action.started += handler;
|
||||
break;
|
||||
case EHotkeyPressType.Performed:
|
||||
action.action.performed += handler;
|
||||
break;
|
||||
}
|
||||
|
||||
action.action.Enable();
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnregisterHotkey(UXButton button)
|
||||
{
|
||||
if (button == null || !_buttonRegistrations.TryGetValue(button, out var actionId))
|
||||
return;
|
||||
|
||||
|
||||
if (_hotkeyRegistry.TryGetValue(actionId, out var registrations))
|
||||
{
|
||||
HotkeyRegistration hotkeyInfo;
|
||||
for (int i = registrations.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (registrations[i].button == button)
|
||||
{
|
||||
hotkeyInfo = registrations[i];
|
||||
registrations.RemoveAt(i);
|
||||
|
||||
if (registrations.Count == 0 && _sharedHandlers.TryGetValue(actionId, out var handlerInfo))
|
||||
{
|
||||
var (handler, actionRef) = handlerInfo;
|
||||
if (actionRef != null && actionRef.action != null)
|
||||
{
|
||||
actionRef.action.Disable();
|
||||
_sharedHandlers.Remove(actionId);
|
||||
_hotkeyRegistry.Remove(actionId);
|
||||
|
||||
switch (hotkeyInfo.pressType)
|
||||
{
|
||||
case EHotkeyPressType.Started:
|
||||
actionRef.action.started -= handler;
|
||||
break;
|
||||
case EHotkeyPressType.Performed:
|
||||
actionRef.action.performed -= handler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
_button.OnSubmit(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_buttonRegistrations.Remove(button);
|
||||
}
|
||||
|
||||
private static void OnHotkeyTriggered(string actionId)
|
||||
{
|
||||
if (_hotkeyRegistry.TryGetValue(actionId, out var registrations) && registrations.Count > 0)
|
||||
{
|
||||
var registration = registrations[registrations.Count - 1];
|
||||
registration.button.OnHotkeyTriggered();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class UXButtonHotkeyExtension
|
||||
{
|
||||
public static void BindHotKey(this UXButton button)
|
||||
{
|
||||
if (button == null) return;
|
||||
|
||||
|
||||
if (button._hotKeyRefrence != null)
|
||||
{
|
||||
UXHotkeyComponent.RegisterHotkey(
|
||||
button,
|
||||
button._hotKeyRefrence,
|
||||
button._hotkeyPressType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnBindHotKey(this UXButton button)
|
||||
{
|
||||
if (button == null || button._hotKeyRefrence == null) return;
|
||||
UXHotkeyComponent.UnregisterHotkey(button);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0aa77908962c48199d63710fa15b8c37
|
||||
timeCreated: 1754555268
|
||||
guid: 9aa1d0e145a6435c94190809bbc99235
|
||||
timeCreated: 1760340422
|
||||
174
Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs
Normal file
174
Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs
Normal file
@ -0,0 +1,174 @@
|
||||
#if INPUTSYSTEM_SUPPORT
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections;
|
||||
using AlicizaX.UI.Extension;
|
||||
internal enum EHotkeyPressType
|
||||
{
|
||||
Started,
|
||||
Performed
|
||||
}
|
||||
|
||||
internal static class UXHotkeyRegisterManager
|
||||
{
|
||||
private readonly struct HotkeyRegistration
|
||||
{
|
||||
public readonly EHotkeyPressType pressType;
|
||||
public readonly UXHotkey button;
|
||||
|
||||
public HotkeyRegistration(UXHotkey btn, EHotkeyPressType pressType)
|
||||
{
|
||||
button = btn;
|
||||
this.pressType = pressType;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Dictionary<string, List<HotkeyRegistration>> _hotkeyRegistry =
|
||||
new Dictionary<string, List<HotkeyRegistration>>(32);
|
||||
|
||||
|
||||
private static readonly Dictionary<string, (System.Action<InputAction.CallbackContext> handler, InputActionReference action)> _sharedHandlers =
|
||||
new Dictionary<string, (System.Action<InputAction.CallbackContext>, InputActionReference)>(32);
|
||||
|
||||
|
||||
private static readonly Dictionary<UXHotkey, string> _buttonRegistrations =
|
||||
new Dictionary<UXHotkey, string>(64);
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.Callbacks.DidReloadScripts]
|
||||
internal static void ClearHotkeyRegistry()
|
||||
{
|
||||
foreach (var key in _buttonRegistrations.Keys)
|
||||
{
|
||||
UnregisterHotkey(key);
|
||||
}
|
||||
|
||||
_sharedHandlers.Clear();
|
||||
_hotkeyRegistry.Clear();
|
||||
_buttonRegistrations.Clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
internal static void RegisterHotkey(UXHotkey button, InputActionReference action, EHotkeyPressType pressType)
|
||||
{
|
||||
if (action == null || action.action == null || button == null)
|
||||
return;
|
||||
|
||||
string actionId = action.action.id.ToString();
|
||||
|
||||
|
||||
HotkeyRegistration registration = new HotkeyRegistration(button, pressType);
|
||||
|
||||
|
||||
if (!_hotkeyRegistry.TryGetValue(actionId, out var registrations))
|
||||
{
|
||||
registrations = new List<HotkeyRegistration>(4);
|
||||
_hotkeyRegistry[actionId] = registrations;
|
||||
}
|
||||
|
||||
registrations.Add(registration);
|
||||
|
||||
|
||||
_buttonRegistrations[button] = actionId;
|
||||
|
||||
if (!_sharedHandlers.ContainsKey(actionId))
|
||||
{
|
||||
System.Action<InputAction.CallbackContext> handler = ctx => OnHotkeyTriggered(actionId);
|
||||
_sharedHandlers[actionId] = (handler, action);
|
||||
|
||||
switch (pressType)
|
||||
{
|
||||
case EHotkeyPressType.Started:
|
||||
action.action.started += handler;
|
||||
break;
|
||||
case EHotkeyPressType.Performed:
|
||||
action.action.performed += handler;
|
||||
break;
|
||||
}
|
||||
|
||||
action.action.Enable();
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnregisterHotkey(UXHotkey button)
|
||||
{
|
||||
if (button == null || !_buttonRegistrations.TryGetValue(button, out var actionId))
|
||||
return;
|
||||
|
||||
|
||||
if (_hotkeyRegistry.TryGetValue(actionId, out var registrations))
|
||||
{
|
||||
HotkeyRegistration hotkeyInfo;
|
||||
for (int i = registrations.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (registrations[i].button == button)
|
||||
{
|
||||
hotkeyInfo = registrations[i];
|
||||
registrations.RemoveAt(i);
|
||||
|
||||
if (registrations.Count == 0 && _sharedHandlers.TryGetValue(actionId, out var handlerInfo))
|
||||
{
|
||||
var (handler, actionRef) = handlerInfo;
|
||||
if (actionRef != null && actionRef.action != null)
|
||||
{
|
||||
actionRef.action.Disable();
|
||||
_sharedHandlers.Remove(actionId);
|
||||
_hotkeyRegistry.Remove(actionId);
|
||||
|
||||
switch (hotkeyInfo.pressType)
|
||||
{
|
||||
case EHotkeyPressType.Started:
|
||||
actionRef.action.started -= handler;
|
||||
break;
|
||||
case EHotkeyPressType.Performed:
|
||||
actionRef.action.performed -= handler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_buttonRegistrations.Remove(button);
|
||||
}
|
||||
|
||||
private static void OnHotkeyTriggered(string actionId)
|
||||
{
|
||||
if (_hotkeyRegistry.TryGetValue(actionId, out var registrations) && registrations.Count > 0)
|
||||
{
|
||||
var registration = registrations[^1];
|
||||
registration.button.HotkeyActionTrigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class UXHotkeyHotkeyExtension
|
||||
{
|
||||
public static void BindHotKey(this UXHotkey button)
|
||||
{
|
||||
if (button == null) return;
|
||||
|
||||
|
||||
if (button._hotKeyRefrence != null)
|
||||
{
|
||||
UXHotkeyRegisterManager.RegisterHotkey(
|
||||
button,
|
||||
button._hotKeyRefrence,
|
||||
button._hotkeyPressType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UnBindHotKey(this UXHotkey button)
|
||||
{
|
||||
if (button == null || button._hotKeyRefrence == null) return;
|
||||
UXHotkeyRegisterManager.UnregisterHotkey(button);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0aa77908962c48199d63710fa15b8c37
|
||||
timeCreated: 1754555268
|
||||
Loading…
Reference in New Issue
Block a user