///Credit perchik ///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/ using System.Collections.Generic; using System.Linq; namespace UnityEngine.UI.Extensions { public enum AutoCompleteSearchType { ArraySort, Linq } [RequireComponent(typeof(RectTransform))] [AddComponentMenu("UI/Extensions/ComboBox/AutoComplete ComboBox")] public class AutoCompleteComboBox : MonoBehaviour { public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it /// /// Contains the included items. To add and remove items to/from this list, use the , /// and methods as these also execute /// the required methods to update to the current collection. /// [Header("AutoComplete Box Items")] public List AvailableOptions; private bool _isPanelActive = false; private bool _hasDrawnOnce = false; private InputField _mainInput; private RectTransform _inputRT; private RectTransform _rectTransform; private RectTransform _overlayRT; private RectTransform _scrollPanelRT; private RectTransform _scrollBarRT; private RectTransform _slidingAreaRT; private RectTransform _scrollHandleRT; private RectTransform _itemsPanelRT; private Canvas _canvas; private RectTransform _canvasRT; private ScrollRect _scrollRect; private List _panelItems; //items that will get shown in the drop-down private List _prunedPanelItems; //items that used to show in the drop-down private Dictionary panelObjects; private GameObject itemTemplate; private bool _initialized; public string Text { get; private set; } [Header("Properties")] [SerializeField] private bool isActive = true; [SerializeField] private float _scrollBarWidth = 20.0f; public float ScrollBarWidth { get { return _scrollBarWidth; } set { _scrollBarWidth = value; RedrawPanel(); } } [SerializeField] private int _itemsToDisplay; public int ItemsToDisplay { get { return _itemsToDisplay; } set { _itemsToDisplay = value; RedrawPanel(); } } [SerializeField] [Tooltip("Change input text color based on matching items")] private bool _ChangeInputTextColorBasedOnMatchingItems = false; public bool InputColorMatching { get { return _ChangeInputTextColorBasedOnMatchingItems; } set { _ChangeInputTextColorBasedOnMatchingItems = value; if (_ChangeInputTextColorBasedOnMatchingItems) { SetInputTextColor(); } } } public float DropdownOffset = 10f; public Color ValidSelectionTextColor = Color.green; public Color MatchingItemsRemainingTextColor = Color.black; public Color NoItemsRemainingTextColor = Color.red; public AutoCompleteSearchType autocompleteSearchType = AutoCompleteSearchType.Linq; [SerializeField] private float dropdownOffset; [SerializeField] private bool _displayPanelAbove = false; public bool SelectFirstItemOnStart = false; [SerializeField] private int selectItemIndexOnStart = 0; private bool shouldSelectItemOnStart => SelectFirstItemOnStart || selectItemIndexOnStart > 0; private bool _selectionIsValid = false; [System.Serializable] public class SelectionChangedEvent : Events.UnityEvent { } [System.Serializable] public class SelectionTextChangedEvent : Events.UnityEvent { } [System.Serializable] public class SelectionValidityChangedEvent : Events.UnityEvent { } [System.Serializable] public class ItemSelectedEvent : Events.UnityEvent { } [System.Serializable] public class ControlDisabledEvent : Events.UnityEvent { } // fires when input text is changed; [Header("Events")] public SelectionTextChangedEvent OnSelectionTextChanged; // fires when an Item gets selected / deselected (including when items are added/removed once this is possible) public SelectionValidityChangedEvent OnSelectionValidityChanged; // fires in both cases public SelectionChangedEvent OnSelectionChanged; // fires when an item is clicked public ItemSelectedEvent OnItemSelected; // fires when item is changed; public ControlDisabledEvent OnControlDisabled; public void Awake() { Initialize(); } public void Start() { if (shouldSelectItemOnStart && AvailableOptions.Count > 0) { SelectItemIndex(SelectFirstItemOnStart ? 0 : selectItemIndexOnStart); } RedrawPanel(); } private bool Initialize() { if (_initialized) return true; bool success = true; try { _rectTransform = GetComponent(); _inputRT = _rectTransform.Find("InputField").GetComponent(); _mainInput = _inputRT.GetComponent(); _overlayRT = _rectTransform.Find("Overlay").GetComponent(); _overlayRT.gameObject.SetActive(false); _scrollPanelRT = _overlayRT.Find("ScrollPanel").GetComponent(); _scrollBarRT = _scrollPanelRT.Find("Scrollbar").GetComponent(); _slidingAreaRT = _scrollBarRT.Find("SlidingArea").GetComponent(); _scrollHandleRT = _slidingAreaRT.Find("Handle").GetComponent(); _itemsPanelRT = _scrollPanelRT.Find("Items").GetComponent(); _canvas = GetComponentInParent(); _canvasRT = _canvas.GetComponent(); _scrollRect = _scrollPanelRT.GetComponent(); _scrollRect.scrollSensitivity = _rectTransform.sizeDelta.y / 2; _scrollRect.movementType = ScrollRect.MovementType.Clamped; _scrollRect.content = _itemsPanelRT; itemTemplate = _rectTransform.Find("ItemTemplate").gameObject; itemTemplate.SetActive(false); } catch (System.NullReferenceException ex) { Debug.LogException(ex); Debug.LogError("Something is setup incorrectly with the dropdownlist component causing a Null Reference Exception"); success = false; } panelObjects = new Dictionary(); _prunedPanelItems = new List(); _panelItems = new List(); _initialized = true; RebuildPanel(); return success; } /// /// Adds the item to if it is not a duplicate and rebuilds the panel. /// /// Item to add. public void AddItem(string item) { if (!this.AvailableOptions.Contains(item)) { this.AvailableOptions.Add(item); this.RebuildPanel(); } else { Debug.LogWarning($"{nameof(AutoCompleteComboBox)}.{nameof(AddItem)}: items may only exists once. '{item}' can not be added."); } } /// /// Removes the item from and rebuilds the panel. /// /// Item to remove. public void RemoveItem(string item) { if (this.AvailableOptions.Contains(item)) { this.AvailableOptions.Remove(item); this.RebuildPanel(); } } /// /// Update the drop down selection to a specific index /// /// public void SelectItemIndex(int index) { ToggleDropdownPanel(false); OnItemClicked(AvailableOptions[index]); } /// /// Sets the given items as new content for the comboBox. Previous entries will be cleared. /// /// New entries. public void SetAvailableOptions(List newOptions) { var uniqueOptions = newOptions.Distinct().ToArray(); SetAvailableOptions(uniqueOptions); } /// /// Sets the given items as new content for the comboBox. Previous entries will be cleared. /// /// New entries. public void SetAvailableOptions(string[] newOptions) { var uniqueOptions = newOptions.Distinct().ToList(); if (newOptions.Length != uniqueOptions.Count) { Debug.LogWarning($"{nameof(AutoCompleteComboBox)}.{nameof(SetAvailableOptions)}: items may only exists once. {newOptions.Length - uniqueOptions.Count} duplicates."); } this.AvailableOptions.Clear(); for (int i = 0; i < newOptions.Length; i++) { this.AvailableOptions.Add(newOptions[i]); } this.RebuildPanel(); this.RedrawPanel(); } public void ResetItems() { AvailableOptions.Clear(); RebuildPanel(); RedrawPanel(); } /// /// Rebuilds the contents of the panel in response to items being added. /// private void RebuildPanel() { if (!_initialized) { Start(); } if (_isPanelActive) ToggleDropdownPanel(); //panel starts with all options _panelItems.Clear(); _prunedPanelItems.Clear(); panelObjects.Clear(); //clear Autocomplete children in scene foreach (Transform child in _itemsPanelRT.transform) { Destroy(child.gameObject); } foreach (string option in AvailableOptions) { _panelItems.Add(option.ToLower()); } List itemObjs = new List(panelObjects.Values); int indx = 0; while (itemObjs.Count < AvailableOptions.Count) { GameObject newItem = Instantiate(itemTemplate) as GameObject; newItem.name = "Item " + indx; newItem.transform.SetParent(_itemsPanelRT, false); itemObjs.Add(newItem); indx++; } for (int i = 0; i < itemObjs.Count; i++) { itemObjs[i].SetActive(i <= AvailableOptions.Count); if (i < AvailableOptions.Count) { itemObjs[i].name = "Item " + i + " " + _panelItems[i]; #if UNITY_2022_1_OR_NEWER itemObjs[i].transform.Find("Text").GetComponent().text = AvailableOptions[i]; //set the text value #else itemObjs[i].transform.Find("Text").GetComponent().text = AvailableOptions[i]; //set the text value #endif Button itemBtn = itemObjs[i].GetComponent