From dc8923564b4e4c9e08499fa71abc26e47dde458a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E6=80=9D=E6=B5=B7?= <1464576565@qq.com> Date: Thu, 26 Mar 2026 16:12:50 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 非适配手柄 增加可选UX_NAVIGATION 宏 优化部分结构 --- Runtime/InputGlyph/Core/GlyphService.cs | 3 +- .../InputGlyph/Core/InputBindingManager.cs | 498 ++++++++---------- Runtime/InputGlyph/InputGlyph.cs | 12 +- Runtime/InputGlyph/TestRebindScript.cs | 490 ++++++++--------- .../Hotkey/UXHotkeyRegisterManager.cs | 8 +- Runtime/UXComponent/Navigation/UXInputMode.cs | 2 +- .../Navigation/UXInputModeService.cs | 2 +- .../Navigation/UXNavigationLayerWatcher.cs | 2 +- .../Navigation/UXNavigationRuntime.cs | 2 +- .../Navigation/UXNavigationScope.cs | 2 +- .../Navigation/UXNavigationSkip.cs | 2 +- 11 files changed, 487 insertions(+), 536 deletions(-) diff --git a/Runtime/InputGlyph/Core/GlyphService.cs b/Runtime/InputGlyph/Core/GlyphService.cs index 5028dd0..859b656 100644 --- a/Runtime/InputGlyph/Core/GlyphService.cs +++ b/Runtime/InputGlyph/Core/GlyphService.cs @@ -15,7 +15,7 @@ public static class GlyphService private static InputGlyphDatabase _database; - public static InputGlyphDatabase Database + static InputGlyphDatabase Database { get { @@ -362,5 +362,4 @@ public static class GlyphService return StartsWithDevice(path, "") || StartsWithDevice(path, "") || ContainsAny(path, OtherGamepadGroupHints); } } - } diff --git a/Runtime/InputGlyph/Core/InputBindingManager.cs b/Runtime/InputGlyph/Core/InputBindingManager.cs index a9ced19..72bcab7 100644 --- a/Runtime/InputGlyph/Core/InputBindingManager.cs +++ b/Runtime/InputGlyph/Core/InputBindingManager.cs @@ -6,9 +6,9 @@ using UnityEngine; using UnityEngine.InputSystem; using AlicizaX; -public class InputBindingManager : MonoSingleton +public sealed class InputBindingManager : MonoServiceBehaviour { - public const string NULL_BINDING = "__NULL__"; + private const string NULL_BINDING = "__NULL__"; private const string KEYBOARD_DEVICE = ""; private const string MOUSE_DELTA = "/delta"; private const string MOUSE_SCROLL = "/scroll"; @@ -22,7 +22,7 @@ public class InputBindingManager : MonoSingleton private const string FILE_NAME = "input_bindings.json"; public bool debugMode = false; - internal InputActionRebindingExtensions.RebindingOperation rebindOperation; + private InputActionRebindingExtensions.RebindingOperation rebindOperation; private bool isApplyPending = false; private string defaultBindingsJson = string.Empty; private string cachedSavePath; @@ -31,21 +31,6 @@ public class InputBindingManager : MonoSingleton private readonly Dictionary actionLookup = new(StringComparer.Ordinal); private readonly Dictionary actionLookupById = new(); private readonly HashSet ambiguousActionNames = new(StringComparer.Ordinal); - private event Action _onInputsInit; - - public event Action OnInputsInit - { - add - { - _onInputsInit += value; - // 重放行为:如果已经初始化,立即调用 - if (isInputsInitialized) - { - value?.Invoke(); - } - } - remove { _onInputsInit -= value; } - } public event Action> OnApply; public event Action OnRebindPrepare; @@ -54,12 +39,11 @@ public class InputBindingManager : MonoSingleton public event Action OnRebindConflict; public static event Action BindingsChanged; - private bool isInputsInitialized = false; public IReadOnlyDictionary ActionMaps => actionMap; public IReadOnlyCollection PreparedRebinds => preparedRebinds; - public string SavePath + private string SavePath { get { @@ -83,16 +67,9 @@ public class InputBindingManager : MonoSingleton Directory.CreateDirectory(directory); } - protected override void Awake() + + protected override void OnServiceInitialize() { - if (_instance != null && _instance != this) - { - Destroy(gameObject); - return; - } - - _instance = this; - if (actions == null) { Log.Error("InputBindingManager: InputActionAsset not assigned."); @@ -133,23 +110,14 @@ public class InputBindingManager : MonoSingleton } } - isInputsInitialized = true; - _onInputsInit?.Invoke(); actions.Enable(); } - protected override void OnDestroy() + private void OnDestroy() { - if (_instance == this) - { - _instance = null; - } - rebindOperation?.Dispose(); rebindOperation = null; - // 清除所有事件处理器 - _onInputsInit = null; OnApply = null; OnRebindPrepare = null; OnRebindStart = null; @@ -379,161 +347,6 @@ public class InputBindingManager : MonoSingleton } } - /* ---------------- Public API ---------------- */ - - /// - /// 根据操作名称获取输入操作 - /// - /// 操作名称 - /// 输入操作,未找到则返回 null - public static InputAction Action(string actionName) - { - var instance = Instance; - if (instance == null) return null; - - if (TryGetAction(actionName, out InputAction action)) - { - return action; - } - - if (instance.ambiguousActionNames.Contains(actionName)) - { - Log.Error($"[InputBindingManager] Action name '{actionName}' is ambiguous. Use 'MapName/{actionName}' instead."); - return null; - } - - Log.Error($"[InputBindingManager] Could not find action '{actionName}'"); - return null; - } - - public static bool TryGetAction(string actionName, out InputAction action) - { - var instance = Instance; - if (instance == null || string.IsNullOrWhiteSpace(actionName)) - { - action = null; - return false; - } - - if (instance.actionLookup.TryGetValue(actionName, out var result)) - { - action = result.action.action; - return true; - } - - action = null; - return false; - } - - /// - /// 开始重新绑定指定的输入操作 - /// - /// 操作名称 - /// 复合部分名称(可选) - public static void StartRebind(string actionName, string compositePartName = null) - { - var action = Action(actionName); - if (action == null) return; - - // 自动决定 bindingIndex 和 deviceMatch - int bindingIndex = Instance.FindBestBindingIndexForKeyboard(action, compositePartName); - if (bindingIndex < 0) - { - Log.Error($"[InputBindingManager] No suitable binding found for action '{actionName}' (part={compositePartName ?? ""})"); - return; - } - - Instance.actions.Disable(); - Instance.PerformInteractiveRebinding(action, bindingIndex, KEYBOARD_DEVICE, true); - Instance.OnRebindStart?.Invoke(); - if (Instance.debugMode) - { - Log.Info("[InputBindingManager] Rebind started"); - } - } - - /// - /// 取消当前的重新绑定操作 - /// - public static void CancelRebind() => Instance.rebindOperation?.Cancel(); - - /// - /// 确认并应用准备好的重新绑定 - /// - /// 是否清除冲突 - /// 是否成功应用 - public static async Task ConfirmApply(bool clearConflicts = true) - { - if (!Instance.isApplyPending) return false; - - try - { - // 在清除之前创建准备好的重绑定的副本 - HashSet appliedContexts = Instance.OnApply != null - ? new HashSet(Instance.preparedRebinds) - : null; - - foreach (var ctx in Instance.preparedRebinds) - { - if (!string.IsNullOrEmpty(ctx.overridePath)) - { - if (ctx.overridePath == NULL_BINDING) - { - ctx.action.RemoveBindingOverride(ctx.bindingIndex); - } - else - { - ctx.action.ApplyBindingOverride(ctx.bindingIndex, ctx.overridePath); - } - } - - var bp = GetBindingPath(ctx.action, ctx.bindingIndex); - if (bp != null) - { - bp.EffectivePath = (ctx.overridePath == NULL_BINDING) ? string.Empty : ctx.overridePath; - } - } - - Instance.preparedRebinds.Clear(); - await Instance.WriteOverridesToDiskAsync(); - BindingsChanged?.Invoke(); - Instance.OnApply?.Invoke(true, appliedContexts); - Instance.isApplyPending = false; - if (Instance.debugMode) - { - Log.Info("[InputBindingManager] Apply confirmed and saved."); - } - - return true; - } - catch (Exception ex) - { - Log.Error("[InputBindingManager] Failed to apply binds: " + ex); - Instance.OnApply?.Invoke(false, null); - return false; - } - } - - /// - /// 丢弃准备好的重新绑定 - /// - public static void DiscardPrepared() - { - if (!Instance.isApplyPending) return; - - // 在清除之前创建准备好的重绑定的副本(用于事件通知) - HashSet discardedContexts = Instance.OnApply != null - ? new HashSet(Instance.preparedRebinds) - : null; - - Instance.preparedRebinds.Clear(); - Instance.isApplyPending = false; - Instance.OnApply?.Invoke(false, discardedContexts); - if (Instance.debugMode) - { - Log.Info("[InputBindingManager] Prepared rebinds discarded."); - } - } private void PerformInteractiveRebinding(InputAction action, int bindingIndex, string deviceMatchPath = null, bool excludeMouseMovementAndScroll = true) { @@ -692,79 +505,6 @@ public class InputBindingManager : MonoSingleton } } - /// - /// 重置所有绑定到默认值 - /// - public async Task ResetToDefaultAsync() - { - try - { - if (!string.IsNullOrEmpty(defaultBindingsJson)) - { - actions.LoadBindingOverridesFromJson(defaultBindingsJson); - } - else - { - foreach (var map in actionMap.Values) - { - foreach (var a in map.actions.Values) - { - for (int b = 0; b < a.action.bindings.Count; b++) - { - a.action.RemoveBindingOverride(b); - } - } - } - } - - RefreshBindingPathsFromActions(); - await WriteOverridesToDiskAsync(); - BindingsChanged?.Invoke(); - if (debugMode) - { - Log.Info("Reset to default and saved."); - } - } - catch (Exception ex) - { - Log.Error("Failed to reset defaults: " + ex); - } - } - - /// - /// 获取指定操作的绑定路径 - /// - /// 操作名称 - /// 绑定索引 - /// 绑定路径,未找到则返回 null - public static BindingPath GetBindingPath(string actionName, int bindingIndex = 0) - { - var instance = Instance; - if (instance == null) return null; - - if (instance.TryGetActionRecord(actionName, out var result) - && result.action.bindings.TryGetValue(bindingIndex, out var binding)) - { - return binding.bindingPath; - } - - return null; - } - - public static BindingPath GetBindingPath(InputAction action, int bindingIndex = 0) - { - var instance = Instance; - if (instance == null || action == null) return null; - - if (instance.TryGetActionRecord(action, out var result) - && result.action.bindings.TryGetValue(bindingIndex, out var binding)) - { - return binding.bindingPath; - } - - return null; - } - private bool TryGetActionRecord(string actionName, out (ActionMap map, ActionMap.Action action) result) { return actionLookup.TryGetValue(actionName, out result); @@ -781,6 +521,7 @@ public class InputBindingManager : MonoSingleton return false; } + #region Public API // 为键盘选择最佳绑定索引;如果 compositePartName != null 则查找部分 /// @@ -827,18 +568,225 @@ public class InputBindingManager : MonoSingleton return fallbackNonComposite >= 0 ? fallbackNonComposite : fallbackPart; } - public static InputBindingManager Instance + /// + /// 根据操作名称获取输入操作 + /// + /// 操作名称 + /// 输入操作,未找到则返回 null + public static InputAction Action(string actionName) { - get + var instance= AppServices.Require(); + if (instance.TryGetAction(actionName, out InputAction action)) { - if (_instance == null) - { - _instance = FindObjectOfType(); - } + return action; + } - return _instance; + if (instance.ambiguousActionNames.Contains(actionName)) + { + Log.Error($"[InputBindingManager] Action name '{actionName}' is ambiguous. Use 'MapName/{actionName}' instead."); + return null; + } + + Log.Error($"[InputBindingManager] Could not find action '{actionName}'"); + return null; + } + + public bool TryGetAction(string actionName, out InputAction action) + { + if (string.IsNullOrWhiteSpace(actionName)) + { + action = null; + return false; + } + + if (actionLookup.TryGetValue(actionName, out var result)) + { + action = result.action.action; + return true; + } + + action = null; + return false; + } + + /// + /// 开始重新绑定指定的输入操作 + /// + /// 操作名称 + /// 复合部分名称(可选) + public void StartRebind(string actionName, string compositePartName = null) + { + var action = Action(actionName); + if (action == null) return; + + // 自动决定 bindingIndex 和 deviceMatch + int bindingIndex = FindBestBindingIndexForKeyboard(action, compositePartName); + if (bindingIndex < 0) + { + Log.Error($"[InputBindingManager] No suitable binding found for action '{actionName}' (part={compositePartName ?? ""})"); + return; + } + + actions.Disable(); + PerformInteractiveRebinding(action, bindingIndex, KEYBOARD_DEVICE, true); + OnRebindStart?.Invoke(); + if (debugMode) + { + Log.Info("[InputBindingManager] Rebind started"); } } - private static InputBindingManager _instance; + /// + /// 取消当前的重新绑定操作 + /// + public void CancelRebind() => rebindOperation?.Cancel(); + + /// + /// 确认并应用准备好的重新绑定 + /// + /// 是否清除冲突 + /// 是否成功应用 + public async Task ConfirmApply(bool clearConflicts = true) + { + if (!isApplyPending) return false; + + try + { + // 在清除之前创建准备好的重绑定的副本 + HashSet appliedContexts = OnApply != null + ? new HashSet(preparedRebinds) + : null; + + foreach (var ctx in preparedRebinds) + { + if (!string.IsNullOrEmpty(ctx.overridePath)) + { + if (ctx.overridePath == NULL_BINDING) + { + ctx.action.RemoveBindingOverride(ctx.bindingIndex); + } + else + { + ctx.action.ApplyBindingOverride(ctx.bindingIndex, ctx.overridePath); + } + } + + var bp = GetBindingPath(ctx.action, ctx.bindingIndex); + if (bp != null) + { + bp.EffectivePath = (ctx.overridePath == NULL_BINDING) ? string.Empty : ctx.overridePath; + } + } + + preparedRebinds.Clear(); + await WriteOverridesToDiskAsync(); + BindingsChanged?.Invoke(); + OnApply?.Invoke(true, appliedContexts); + isApplyPending = false; + if (debugMode) + { + Log.Info("[InputBindingManager] Apply confirmed and saved."); + } + + return true; + } + catch (Exception ex) + { + Log.Error("[InputBindingManager] Failed to apply binds: " + ex); + OnApply?.Invoke(false, null); + return false; + } + } + + /// + /// 丢弃准备好的重新绑定 + /// + public void DiscardPrepared() + { + if (!isApplyPending) return; + + // 在清除之前创建准备好的重绑定的副本(用于事件通知) + HashSet discardedContexts = OnApply != null + ? new HashSet(preparedRebinds) + : null; + + preparedRebinds.Clear(); + isApplyPending = false; + OnApply?.Invoke(false, discardedContexts); + if (debugMode) + { + Log.Info("[InputBindingManager] Prepared rebinds discarded."); + } + } + + /// + /// 重置所有绑定到默认值 + /// + public async Task ResetToDefaultAsync() + { + try + { + if (!string.IsNullOrEmpty(defaultBindingsJson)) + { + actions.LoadBindingOverridesFromJson(defaultBindingsJson); + } + else + { + foreach (var map in actionMap.Values) + { + foreach (var a in map.actions.Values) + { + for (int b = 0; b < a.action.bindings.Count; b++) + { + a.action.RemoveBindingOverride(b); + } + } + } + } + + RefreshBindingPathsFromActions(); + await WriteOverridesToDiskAsync(); + BindingsChanged?.Invoke(); + if (debugMode) + { + Log.Info("Reset to default and saved."); + } + } + catch (Exception ex) + { + Log.Error("Failed to reset defaults: " + ex); + } + } + + /// + /// 获取指定操作的绑定路径 + /// + /// 操作名称 + /// 绑定索引 + /// 绑定路径,未找到则返回 null + public BindingPath GetBindingPath(string actionName, int bindingIndex = 0) + { + if (TryGetActionRecord(actionName, out var result) + && result.action.bindings.TryGetValue(bindingIndex, out var binding)) + { + return binding.bindingPath; + } + + return null; + } + + public BindingPath GetBindingPath(InputAction action, int bindingIndex = 0) + { + if (action == null) return null; + + if (TryGetActionRecord(action, out var result) + && result.action.bindings.TryGetValue(bindingIndex, out var binding)) + { + return binding.bindingPath; + } + + return null; + } + + #endregion } diff --git a/Runtime/InputGlyph/InputGlyph.cs b/Runtime/InputGlyph/InputGlyph.cs index 188e26a..3d2d64a 100644 --- a/Runtime/InputGlyph/InputGlyph.cs +++ b/Runtime/InputGlyph/InputGlyph.cs @@ -31,20 +31,18 @@ public sealed class InputGlyph : InputGlyphBehaviourBase public UnityEvent onNotMatched; } - [Header("Source")] - [SerializeField] private ActionSourceMode actionSourceMode = ActionSourceMode.ActionReference; + [Header("Source")] [SerializeField] private ActionSourceMode actionSourceMode = ActionSourceMode.ActionReference; [SerializeField] private InputActionReference actionReference; [SerializeField] private Component hotkeyTrigger; [SerializeField] private string actionName; [SerializeField] private string compositePartName; - [Header("Output")] - [SerializeField] private OutputMode outputMode = OutputMode.Image; + [Header("Output")] [SerializeField] private OutputMode outputMode = OutputMode.Image; [SerializeField] private Image targetImage; [SerializeField] private TMP_Text targetText; - [Header("Platform Events")] - [SerializeField] private List categoryEvents = new(); + [Header("Platform Events")] [SerializeField] + private List categoryEvents = new(); private Sprite _cachedSprite; private string _templateText; @@ -177,7 +175,7 @@ public sealed class InputGlyph : InputGlyphBehaviourBase case ActionSourceMode.HotkeyTrigger: return ResolveHotkeyAction(); case ActionSourceMode.ActionName: - return InputBindingManager.TryGetAction(actionName, out InputAction action) ? action : null; + return InputBindingManager.Action(actionName); default: return null; } diff --git a/Runtime/InputGlyph/TestRebindScript.cs b/Runtime/InputGlyph/TestRebindScript.cs index f8cc3bb..1fc0922 100644 --- a/Runtime/InputGlyph/TestRebindScript.cs +++ b/Runtime/InputGlyph/TestRebindScript.cs @@ -1,245 +1,245 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using TMPro; -using UnityEngine; -using UnityEngine.InputSystem; -using UnityEngine.UI; - -public class TestRebindScript : MonoBehaviour -{ - [Header("UI")] public UXButton btn; - public TextMeshProUGUI bindKeyText; - public Image targetImage; - - [Tooltip("如果不使用 actionReference,则用 name 在全局 manager 查找")] - public string actionName = "movement"; - - [Header("Optional composite part (WASD style)")] [Tooltip("如果需要绑定 composite 的某一部分(例如 Up/Down/Left/Right),填这个;留空表示绑定非 composite 或整体 binding")] - public string compositePartName = ""; - - [Header("Behavior")] [Tooltip("如果 true,在 Prepare 后自动调用 ConfirmApply() 并保存;否则等待手动 ConfirmPrepared()/CancelPrepared()")] - public bool autoConfirm = false; - - /// - /// 启动时初始化并订阅事件 - /// - private void Start() - { - if (btn != null) btn.onClick.AddListener(OnBtnClicked); - InputDeviceWatcher.OnDeviceChanged += OnDeviceChanged; - InputBindingManager.BindingsChanged += OnBindingsChanged; - UpdateBindingText(); - - if (InputBindingManager.Instance != null) - { - // 订阅事件 - InputBindingManager.Instance.OnRebindPrepare += OnRebindPrepareHandler; - InputBindingManager.Instance.OnApply += OnApplyHandler; - InputBindingManager.Instance.OnRebindEnd += OnRebindEndHandler; - InputBindingManager.Instance.OnRebindConflict += OnRebindConflictHandler; - } - } - - /// - /// 禁用时取消订阅事件 - /// - private void OnDisable() - { - if (btn != null) btn.onClick.RemoveListener(OnBtnClicked); - InputDeviceWatcher.OnDeviceChanged -= OnDeviceChanged; - InputBindingManager.BindingsChanged -= OnBindingsChanged; - - if (InputBindingManager.Instance != null) - { - InputBindingManager.Instance.OnRebindPrepare -= OnRebindPrepareHandler; - InputBindingManager.Instance.OnApply -= OnApplyHandler; - InputBindingManager.Instance.OnRebindEnd -= OnRebindEndHandler; - InputBindingManager.Instance.OnRebindConflict -= OnRebindConflictHandler; - } - } - - /// - /// 重新绑定准备完成的处理器 - /// - private void OnRebindPrepareHandler(InputBindingManager.RebindContext ctx) - { - if (IsTargetContext(ctx)) - { - var disp = ctx.overridePath == InputBindingManager.NULL_BINDING ? "" : ctx.overridePath; - bindKeyText.text = disp; - if (autoConfirm) _ = ConfirmPreparedAsync(); - } - } - - /// - /// 应用重新绑定的处理器 - /// - private void OnApplyHandler(bool success, HashSet appliedContexts) - { - if (appliedContexts != null) - { - // 仅当任何应用/丢弃的上下文与此实例匹配时才更新 - foreach (var ctx in appliedContexts) - { - if (IsTargetContext(ctx)) - { - UpdateBindingText(); - break; - } - } - } - } - - /// - /// 重新绑定结束的处理器 - /// - private void OnRebindEndHandler(bool success, InputBindingManager.RebindContext context) - { - if (IsTargetContext(context)) - { - UpdateBindingText(); - } - } - - /// - /// 重新绑定冲突的处理器 - /// - private void OnRebindConflictHandler(InputBindingManager.RebindContext prepared, InputBindingManager.RebindContext conflict) - { - // 如果准备的或冲突的上下文匹配此实例,则更新 - if (IsTargetContext(prepared) || IsTargetContext(conflict)) - { - UpdateBindingText(); - } - } - - /// - /// 设备变更的回调 - /// - private void OnDeviceChanged(InputDeviceWatcher.InputDeviceCategory _) - { - UpdateBindingText(); - } - - private void OnBindingsChanged() - { - UpdateBindingText(); - } - - /// - /// 获取当前的输入操作 - /// - private InputAction GetAction() - { - return InputBindingManager.Action(actionName); - } - - /// - /// 判断上下文是否为目标上下文 - /// - private bool IsTargetContext(InputBindingManager.RebindContext ctx) - { - if (ctx == null || ctx.action == null) return false; - var action = GetAction(); - if (action == null) return false; - - // 必须匹配操作 - if (ctx.action != action) return false; - - // 如果指定了复合部分,需要匹配绑定索引 - if (!string.IsNullOrEmpty(compositePartName)) - { - // 获取上下文索引处的绑定 - if (ctx.bindingIndex < 0 || ctx.bindingIndex >= action.bindings.Count) - return false; - - var binding = action.bindings[ctx.bindingIndex]; - - // 检查绑定的名称是否与我们的复合部分匹配 - return string.Equals(binding.name, compositePartName, StringComparison.OrdinalIgnoreCase); - } - - // 如果未指定复合部分,仅匹配操作就足够了 - return true; - } - - /// - /// 按钮点击的回调 - /// - private void OnBtnClicked() - { - // 使用管理器 API(我们传递部分名称,以便管理器可以在需要时选择适当的绑定) - InputBindingManager.StartRebind(actionName, string.IsNullOrEmpty(compositePartName) ? null : compositePartName); - } - - /// - /// 确认准备好的重新绑定(公共方法) - /// - public async void ConfirmPrepared() - { - bool ok = await ConfirmPreparedAsync(); - if (!ok) Debug.LogError("ConfirmPrepared: apply failed."); - } - - /// - /// 确认准备好的重新绑定(异步) - /// - private async Task ConfirmPreparedAsync() - { - try - { - var task = InputBindingManager.ConfirmApply(); - return await task; - } - catch (Exception ex) - { - Debug.LogError(ex); - return false; - } - } - - /// - /// 取消准备好的重新绑定 - /// - public void CancelPrepared() - { - InputBindingManager.DiscardPrepared(); - // UpdateBindingText 将通过 OnApply 事件自动调用 - } - - /// - /// 更新绑定文本和图标显示 - /// - private void UpdateBindingText() - { - var action = GetAction(); - var deviceCat = InputDeviceWatcher.CurrentCategory; - if (action == null) - { - bindKeyText.text = ""; - if (targetImage != null) targetImage.sprite = null; - return; - } - - - bindKeyText.text = GlyphService.GetDisplayNameFromInputAction(action, compositePartName, deviceCat); - - - try - { - if (GlyphService.TryGetUISpriteForActionPath(action, compositePartName, deviceCat, out Sprite sprite)) - { - if (targetImage != null) targetImage.sprite = sprite; - } - else - { - if (targetImage != null) targetImage.sprite = null; - } - } - catch - { - if (targetImage != null) targetImage.sprite = null; - } - } -} +// using System; +// using System.Collections.Generic; +// using System.Threading.Tasks; +// using TMPro; +// using UnityEngine; +// using UnityEngine.InputSystem; +// using UnityEngine.UI; +// +// public class TestRebindScript : MonoBehaviour +// { +// [Header("UI")] public UXButton btn; +// public TextMeshProUGUI bindKeyText; +// public Image targetImage; +// +// [Tooltip("如果不使用 actionReference,则用 name 在全局 manager 查找")] +// public string actionName = "movement"; +// +// [Header("Optional composite part (WASD style)")] [Tooltip("如果需要绑定 composite 的某一部分(例如 Up/Down/Left/Right),填这个;留空表示绑定非 composite 或整体 binding")] +// public string compositePartName = ""; +// +// [Header("Behavior")] [Tooltip("如果 true,在 Prepare 后自动调用 ConfirmApply() 并保存;否则等待手动 ConfirmPrepared()/CancelPrepared()")] +// public bool autoConfirm = false; +// +// /// +// /// 启动时初始化并订阅事件 +// /// +// private void Start() +// { +// if (btn != null) btn.onClick.AddListener(OnBtnClicked); +// InputDeviceWatcher.OnDeviceChanged += OnDeviceChanged; +// InputBindingManager.BindingsChanged += OnBindingsChanged; +// UpdateBindingText(); +// +// if (InputBindingManager.Instance != null) +// { +// // 订阅事件 +// InputBindingManager.Instance.OnRebindPrepare += OnRebindPrepareHandler; +// InputBindingManager.Instance.OnApply += OnApplyHandler; +// InputBindingManager.Instance.OnRebindEnd += OnRebindEndHandler; +// InputBindingManager.Instance.OnRebindConflict += OnRebindConflictHandler; +// } +// } +// +// /// +// /// 禁用时取消订阅事件 +// /// +// private void OnDisable() +// { +// if (btn != null) btn.onClick.RemoveListener(OnBtnClicked); +// InputDeviceWatcher.OnDeviceChanged -= OnDeviceChanged; +// InputBindingManager.BindingsChanged -= OnBindingsChanged; +// +// if (InputBindingManager.Instance != null) +// { +// InputBindingManager.Instance.OnRebindPrepare -= OnRebindPrepareHandler; +// InputBindingManager.Instance.OnApply -= OnApplyHandler; +// InputBindingManager.Instance.OnRebindEnd -= OnRebindEndHandler; +// InputBindingManager.Instance.OnRebindConflict -= OnRebindConflictHandler; +// } +// } +// +// /// +// /// 重新绑定准备完成的处理器 +// /// +// private void OnRebindPrepareHandler(InputBindingManager.RebindContext ctx) +// { +// if (IsTargetContext(ctx)) +// { +// var disp = ctx.overridePath == InputBindingManager.NULL_BINDING ? "" : ctx.overridePath; +// bindKeyText.text = disp; +// if (autoConfirm) _ = ConfirmPreparedAsync(); +// } +// } +// +// /// +// /// 应用重新绑定的处理器 +// /// +// private void OnApplyHandler(bool success, HashSet appliedContexts) +// { +// if (appliedContexts != null) +// { +// // 仅当任何应用/丢弃的上下文与此实例匹配时才更新 +// foreach (var ctx in appliedContexts) +// { +// if (IsTargetContext(ctx)) +// { +// UpdateBindingText(); +// break; +// } +// } +// } +// } +// +// /// +// /// 重新绑定结束的处理器 +// /// +// private void OnRebindEndHandler(bool success, InputBindingManager.RebindContext context) +// { +// if (IsTargetContext(context)) +// { +// UpdateBindingText(); +// } +// } +// +// /// +// /// 重新绑定冲突的处理器 +// /// +// private void OnRebindConflictHandler(InputBindingManager.RebindContext prepared, InputBindingManager.RebindContext conflict) +// { +// // 如果准备的或冲突的上下文匹配此实例,则更新 +// if (IsTargetContext(prepared) || IsTargetContext(conflict)) +// { +// UpdateBindingText(); +// } +// } +// +// /// +// /// 设备变更的回调 +// /// +// private void OnDeviceChanged(InputDeviceWatcher.InputDeviceCategory _) +// { +// UpdateBindingText(); +// } +// +// private void OnBindingsChanged() +// { +// UpdateBindingText(); +// } +// +// /// +// /// 获取当前的输入操作 +// /// +// private InputAction GetAction() +// { +// return InputBindingManager.Action(actionName); +// } +// +// /// +// /// 判断上下文是否为目标上下文 +// /// +// private bool IsTargetContext(InputBindingManager.RebindContext ctx) +// { +// if (ctx == null || ctx.action == null) return false; +// var action = GetAction(); +// if (action == null) return false; +// +// // 必须匹配操作 +// if (ctx.action != action) return false; +// +// // 如果指定了复合部分,需要匹配绑定索引 +// if (!string.IsNullOrEmpty(compositePartName)) +// { +// // 获取上下文索引处的绑定 +// if (ctx.bindingIndex < 0 || ctx.bindingIndex >= action.bindings.Count) +// return false; +// +// var binding = action.bindings[ctx.bindingIndex]; +// +// // 检查绑定的名称是否与我们的复合部分匹配 +// return string.Equals(binding.name, compositePartName, StringComparison.OrdinalIgnoreCase); +// } +// +// // 如果未指定复合部分,仅匹配操作就足够了 +// return true; +// } +// +// /// +// /// 按钮点击的回调 +// /// +// private void OnBtnClicked() +// { +// // 使用管理器 API(我们传递部分名称,以便管理器可以在需要时选择适当的绑定) +// InputBindingManager.StartRebind(actionName, string.IsNullOrEmpty(compositePartName) ? null : compositePartName); +// } +// +// /// +// /// 确认准备好的重新绑定(公共方法) +// /// +// public async void ConfirmPrepared() +// { +// bool ok = await ConfirmPreparedAsync(); +// if (!ok) Debug.LogError("ConfirmPrepared: apply failed."); +// } +// +// /// +// /// 确认准备好的重新绑定(异步) +// /// +// private async Task ConfirmPreparedAsync() +// { +// try +// { +// var task = InputBindingManager.ConfirmApply(); +// return await task; +// } +// catch (Exception ex) +// { +// Debug.LogError(ex); +// return false; +// } +// } +// +// /// +// /// 取消准备好的重新绑定 +// /// +// public void CancelPrepared() +// { +// InputBindingManager.DiscardPrepared(); +// // UpdateBindingText 将通过 OnApply 事件自动调用 +// } +// +// /// +// /// 更新绑定文本和图标显示 +// /// +// private void UpdateBindingText() +// { +// var action = GetAction(); +// var deviceCat = InputDeviceWatcher.CurrentCategory; +// if (action == null) +// { +// bindKeyText.text = ""; +// if (targetImage != null) targetImage.sprite = null; +// return; +// } +// +// +// bindKeyText.text = GlyphService.GetDisplayNameFromInputAction(action, compositePartName, deviceCat); +// +// +// try +// { +// if (GlyphService.TryGetUISpriteForActionPath(action, compositePartName, deviceCat, out Sprite sprite)) +// { +// if (targetImage != null) targetImage.sprite = sprite; +// } +// else +// { +// if (targetImage != null) targetImage.sprite = null; +// } +// } +// catch +// { +// if (targetImage != null) targetImage.sprite = null; +// } +// } +// } diff --git a/Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs b/Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs index b2a8e45..40dbf69 100644 --- a/Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs +++ b/Runtime/UXComponent/Hotkey/UXHotkeyRegisterManager.cs @@ -477,7 +477,11 @@ namespace UnityEngine.UI foreach (var scope in _scopes.Values) { - if (!IsScopeActive(scope) || !UXNavigationRuntime.IsHolderWithinTopScope(scope.Holder)) + if (!IsScopeActive(scope) +#if UX_NAVIGATION + || !UXNavigationRuntime.IsHolderWithinTopScope(scope.Holder) +#endif + ) { continue; } @@ -493,7 +497,9 @@ namespace UnityEngine.UI foreach (var scope in _scopes.Values) { if (IsScopeActive(scope) +#if UX_NAVIGATION && UXNavigationRuntime.IsHolderWithinTopScope(scope.Holder) +#endif && !_ancestorHolders.Contains(scope.Holder)) { _leafScopes.Add(scope); diff --git a/Runtime/UXComponent/Navigation/UXInputMode.cs b/Runtime/UXComponent/Navigation/UXInputMode.cs index f3d8674..3007441 100644 --- a/Runtime/UXComponent/Navigation/UXInputMode.cs +++ b/Runtime/UXComponent/Navigation/UXInputMode.cs @@ -1,4 +1,4 @@ -#if INPUTSYSTEM_SUPPORT +#if INPUTSYSTEM_SUPPORT && UX_NAVIGATION namespace UnityEngine.UI { public enum UXInputMode : byte diff --git a/Runtime/UXComponent/Navigation/UXInputModeService.cs b/Runtime/UXComponent/Navigation/UXInputModeService.cs index 4c87e48..f75bb5e 100644 --- a/Runtime/UXComponent/Navigation/UXInputModeService.cs +++ b/Runtime/UXComponent/Navigation/UXInputModeService.cs @@ -1,4 +1,4 @@ -#if INPUTSYSTEM_SUPPORT +#if INPUTSYSTEM_SUPPORT && UX_NAVIGATION using System; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; diff --git a/Runtime/UXComponent/Navigation/UXNavigationLayerWatcher.cs b/Runtime/UXComponent/Navigation/UXNavigationLayerWatcher.cs index fb7d8ff..c86c411 100644 --- a/Runtime/UXComponent/Navigation/UXNavigationLayerWatcher.cs +++ b/Runtime/UXComponent/Navigation/UXNavigationLayerWatcher.cs @@ -1,4 +1,4 @@ -#if INPUTSYSTEM_SUPPORT +#if INPUTSYSTEM_SUPPORT && UX_NAVIGATION namespace UnityEngine.UI { [DisallowMultipleComponent] diff --git a/Runtime/UXComponent/Navigation/UXNavigationRuntime.cs b/Runtime/UXComponent/Navigation/UXNavigationRuntime.cs index 782045e..59989a3 100644 --- a/Runtime/UXComponent/Navigation/UXNavigationRuntime.cs +++ b/Runtime/UXComponent/Navigation/UXNavigationRuntime.cs @@ -1,4 +1,4 @@ -#if INPUTSYSTEM_SUPPORT +#if INPUTSYSTEM_SUPPORT && UX_NAVIGATION using System.Collections.Generic; using AlicizaX; using AlicizaX.UI.Runtime; diff --git a/Runtime/UXComponent/Navigation/UXNavigationScope.cs b/Runtime/UXComponent/Navigation/UXNavigationScope.cs index 3d91d17..993102a 100644 --- a/Runtime/UXComponent/Navigation/UXNavigationScope.cs +++ b/Runtime/UXComponent/Navigation/UXNavigationScope.cs @@ -1,4 +1,4 @@ -#if INPUTSYSTEM_SUPPORT +#if INPUTSYSTEM_SUPPORT && UX_NAVIGATION using System.Collections.Generic; using AlicizaX.UI.Runtime; diff --git a/Runtime/UXComponent/Navigation/UXNavigationSkip.cs b/Runtime/UXComponent/Navigation/UXNavigationSkip.cs index f13dfe9..f8d043f 100644 --- a/Runtime/UXComponent/Navigation/UXNavigationSkip.cs +++ b/Runtime/UXComponent/Navigation/UXNavigationSkip.cs @@ -1,4 +1,4 @@ -#if INPUTSYSTEM_SUPPORT +#if INPUTSYSTEM_SUPPORT && UX_NAVIGATION namespace UnityEngine.UI { [DisallowMultipleComponent]