using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Collections.Generic; using Lean.Common; namespace Lean.Gui { /// This component allows you to force selection of a selectable, or select a previously selected object. [HelpURL(LeanGui.HelpUrlPrefix + "LeanSelectionManager")] [AddComponentMenu(LeanGui.ComponentMenuPrefix + "Selection Manager")] public class LeanSelectionManager : MonoBehaviour { /// If the scene contains at least one selectable, and there is currently no selected object, should it be selected? /// NOTE: If there are multiple selectables in the scene, then one will be chosen based on the LeanSelectionPriority component setting. public bool ForceSelection { set { forceSelection = value; } get { return forceSelection; } } [SerializeField] private bool forceSelection; /// This stores a list of all selectables in the order they were selected. This allows selection to revert to a previous one if the current selectable becomes unselectable. public List SelectionHistory { get { if (selectionHistory == null) selectionHistory = new List(); return selectionHistory; } } [SerializeField] private List selectionHistory; private static List tempSelectables = new List(); #if UNITY_2019_1_OR_NEWER private static Selectable[] tempSelectablesArray = new Selectable[1024]; #endif private bool CanSelect(Selectable selectable) { return selectable != null && selectable.IsInteractable() == true && selectable.isActiveAndEnabled == true && selectable.navigation.mode != Navigation.Mode.None; } [ContextMenu("Select First Selectable")] public void SelectFirstSelectable() { tempSelectables.Clear(); #if UNITY_2019_1_OR_NEWER #if UNITY_2019_1_0 || UNITY_2019_1_1 || UNITY_2019_1_2 || UNITY_2019_1_3 || UNITY_2019_1_4 var count = Selectable.AllSelectablesNoAlloc(ref tempSelectablesArray); #else var count = Selectable.AllSelectablesNoAlloc(tempSelectablesArray); #endif for (var i = 0; i < count; i++) { tempSelectables.Add(tempSelectablesArray[i]); } #else tempSelectables.AddRange(Selectable.allSelectables); #endif // Select from history? for (var i = SelectionHistory.Count - 1; i >= 0; i--) // NOTE: Property { var oldSelection = selectionHistory[i]; if (oldSelection != null) { if (CanSelect(oldSelection) == true && tempSelectables.Contains(oldSelection) == true) { selectionHistory.RemoveRange(i, selectionHistory.Count - i); oldSelection.Select(); return; } } else { selectionHistory.RemoveAt(i); } } var bestSelectable = default(Selectable); var bestPriority = -1.0f; // Select from selectables? for (var i = 0; i < tempSelectables.Count; i++) { var selectable = tempSelectables[i]; if (CanSelect(selectable) == true) { var selectionPriority = selectable.GetComponent(); var priority = 0.0f; if (selectionPriority != null) { priority = selectionPriority.Priority; } if (priority > bestPriority) { bestSelectable = selectable; bestPriority = priority; } } } if (bestSelectable != null) { bestSelectable.Select(); } } protected virtual void LateUpdate() { var eventSystem = EventSystem.current; if (eventSystem != null) { var selected = eventSystem.currentSelectedGameObject; // Deselect if the selection is not selectable? if (selected != null) { var selectable = selected.GetComponent(); if (selectable != null) { for (var i = SelectionHistory.Count - 1; i >= 0; i--) // NOTE: Property { var oldSelection = selectionHistory[i]; if (oldSelection == null || oldSelection == selectable) { selectionHistory.RemoveAt(i); } } selectionHistory.Add(selectable); } if (CanSelect(selectable) == false) { selected = null; eventSystem.SetSelectedGameObject(null); } } if (LeanGui.IsDragging == true) { selected = null; eventSystem.SetSelectedGameObject(null); } else { // Auto select? if (selected == null && ForceSelection == true) { SelectFirstSelectable(); selected = eventSystem.currentSelectedGameObject; } } } } } } #if UNITY_EDITOR namespace Lean.Gui.Editor { using TARGET = LeanSelectionManager; [UnityEditor.CanEditMultipleObjects] [UnityEditor.CustomEditor(typeof(TARGET))] public class LeanSelectionManager_Editor : LeanEditor { protected override void OnInspector() { TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts); Draw("forceSelection", "If the scene contains at least one selectable, and there is currently no selected object, should it be selected?\n\nNOTE: If there are multiple selectables in the scene, then one will be chosen based on the LeanSelectionPriority component setting."); Separator(); BeginDisabled(true); UnityEditor.EditorGUILayout.ObjectField("Current Selectable", EventSystem.current != null ? EventSystem.current.currentSelectedGameObject : null, typeof(GameObject), false); Separator(); Draw("selectionHistory", "Enable this if you want ExitTransitions + OnExit to be invoked once for each mouse/finger that exits this element."); EndDisabled(); } } } #endif