using System; using JetBrains.Annotations; using JohnsonUtils.Utilities; using JohnsonUtils.Utilities.Icons; using UnityEngine; using UnityEngine.InputSystem; namespace JohnsonUtils.Canvases { public class RebindActionUI : MonoBehaviour { public event Action UpdateBindingUIEvent; [SerializeField] private TextUIComponent actionLabel; [SerializeField] private RebindReferences mainBindingReferences; [SerializeField] private RebindReferences altBindingReferences; private TextUIComponent _rebindOverlay; private InputAction _action; private InputActionRebindingExtensions.RebindingOperation _rebindOperation; [PublicAPI] public void ResetToDefault() { _action.actionMap.Disable(); _action.RemoveBindingOverride(mainBindingReferences.Index); _action.RemoveBindingOverride(altBindingReferences.Index); UpdateBindingDisplay(); _action.actionMap.Enable(); } private void StartInteractiveRebind(int index) { _action.actionMap.Disable(); PerformInteractiveRebind(index); _action.actionMap.Enable(); } private void PerformInteractiveRebind(int bindingIndex) { _rebindOperation?.Cancel(); // Will null out rebindOperation. void CleanUp(InputActionRebindingExtensions.RebindingOperation rebindingOperation) { if (_rebindOverlay != null) { _rebindOverlay.SetActive(false); } UpdateBindingDisplay(); _rebindOperation?.Dispose(); _rebindOperation = null; } // Configure the rebind. _rebindOperation = _action.PerformInteractiveRebinding(bindingIndex) .OnCancel(CleanUp) .OnComplete(CleanUp); // If it's a part binding, show the name of the part in the UI. string partName = string.Empty; if (_action.bindings[bindingIndex].isPartOfComposite) partName = $"Binding '{_action.bindings[bindingIndex].name}'. "; // Bring up rebind overlay, if we have one. if (_rebindOverlay != null) { _rebindOverlay.Show(); string text = !string.IsNullOrEmpty(_rebindOperation.expectedControlType) ? $"{partName}Waiting for {_rebindOperation.expectedControlType} input..." : $"{partName}Waiting for input..."; _rebindOverlay.Text =text; } // If we have no rebind overlay and no callback but we have a binding text label, // temporarily set the binding text label to "". if (_rebindOverlay == null && mainBindingReferences.Text != null) mainBindingReferences.Text.Text = ""; _rebindOperation.Start(); } protected void OnEnable() { mainBindingReferences.Button.OnClick += () => StartInteractiveRebind(mainBindingReferences.Index); altBindingReferences.Button.OnClick += () => StartInteractiveRebind(altBindingReferences.Index); } protected void OnDisable() { _rebindOperation?.Dispose(); _rebindOperation = null; } private void UpdateActionLabel() { if (actionLabel == null) return; string actionLabelText = _action.bindings[mainBindingReferences.Index].isPartOfComposite ? _action.bindings[mainBindingReferences.Index].name.Capitalize() : _action.name; if (actionLabelText.Equals("Dash")) actionLabelText = "Tackle"; actionLabel.Text = actionLabelText; } public void UpdateBindingDisplay(bool shouldCallEvent = true) { string mainDisplayString = string.Empty; string altDisplayString = string.Empty; string deviceLayoutName = string.Empty; string mainControlPath = string.Empty; string altControlPath = string.Empty; if (_action != null) { if (mainBindingReferences.Index != -1) mainDisplayString = _action.GetBindingDisplayString(mainBindingReferences.Index, out deviceLayoutName, out mainControlPath); if (altBindingReferences.Index != -1) altDisplayString = _action.GetBindingDisplayString(altBindingReferences.Index, out _, out altControlPath); } UpdateDuplicateText(mainBindingReferences, mainDisplayString); UpdateDuplicateText(altBindingReferences, altDisplayString); Sprite mainIcon = BindingsIconsUtil.GetSprite(deviceLayoutName, mainControlPath); Sprite altIcon = BindingsIconsUtil.GetSprite(deviceLayoutName, altControlPath); DisplayIcon(mainBindingReferences, mainIcon); DisplayIcon(altBindingReferences, altIcon); if (shouldCallEvent) { UpdateBindingUIEvent?.Invoke(deviceLayoutName, mainControlPath); } } private void DisplayIcon(RebindReferences reference, Sprite mainIcon) { if (mainIcon != null) { reference.Text.Hide(); reference.Image.Sprite = mainIcon; reference.Image.Show(); } else { reference.Text.Show(); reference.Image.Hide(); } } private void UpdateDuplicateText(RebindReferences reference, string mainDisplayString) { if (reference.Text == null) return; reference.Text.Text = mainDisplayString == "Delta" ? "Mouse" : mainDisplayString; bool mainDuplicate = CheckDuplicateBindings(reference.Index); reference.Button.Color = mainDuplicate ? Color.red : Color.white; } private bool CheckDuplicateBindings(int bindingIndex) { if (_action == null) return false; InputBinding newBinding = _action.bindings[bindingIndex]; if (newBinding.effectivePath.IsNullOrEmpty()) return false; foreach (InputBinding binding in _action.actionMap.bindings) { if (binding.action == newBinding.action) continue; if (binding.effectivePath == newBinding.effectivePath) return true; } if (!_action.bindings[0].isComposite) return false; for (int i = 1; i < _action.bindings.Count; i++) { if (i == bindingIndex) continue; if (_action.bindings[i].effectivePath == newBinding.effectivePath) { return true; } } return false; } public void Initialize(int bindingIndex, InputAction inputAction, TextUIComponent overlay, Action onUpdateBindingUIEvent) { mainBindingReferences.Index = bindingIndex; altBindingReferences.Index = bindingIndex + 1; _action = inputAction; _rebindOverlay = overlay; UpdateBindingUIEvent += onUpdateBindingUIEvent; UpdateActionLabel(); UpdateBindingDisplay(); } } }