From 33cd5a9be4487b83e446b4a6ceb1c949f1f562fa Mon Sep 17 00:00:00 2001 From: Simon Jackson Date: Sun, 5 Feb 2023 11:35:18 +0000 Subject: [PATCH] Several lifetime feature updates for the ComboBox controls: - Resolves startup issue that prevented the control being used (Unity changed the start order in some instances), this was causing null reference issues with comboboxes - Added the ability to set a specific item on start and not just the first - Added the ability to disable the dropdown to make a read-only dropdown Resolves: - https://github.com/Unity-UI-Extensions/com.unity.uiextensions/issues/426 - https://github.com/Unity-UI-Extensions/com.unity.uiextensions/issues/425 --- .../Controls/ComboBox/AutoCompleteComboBox.cs | 66 ++++++++-- Runtime/Scripts/Controls/ComboBox/ComboBox.cs | 69 ++++++++-- .../Scripts/Controls/ComboBox/DropDownList.cs | 119 +++++++++++++----- 3 files changed, 203 insertions(+), 51 deletions(-) diff --git a/Runtime/Scripts/Controls/ComboBox/AutoCompleteComboBox.cs b/Runtime/Scripts/Controls/ComboBox/AutoCompleteComboBox.cs index 889ea54..d5708d8 100644 --- a/Runtime/Scripts/Controls/ComboBox/AutoCompleteComboBox.cs +++ b/Runtime/Scripts/Controls/ComboBox/AutoCompleteComboBox.cs @@ -1,6 +1,7 @@ ///Credit perchik ///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/ +using System; using System.Collections.Generic; using System.Linq; @@ -16,7 +17,6 @@ namespace UnityEngine.UI.Extensions [AddComponentMenu("UI/Extensions/ComboBox/AutoComplete ComboBox")] public class AutoCompleteComboBox : MonoBehaviour { - public Color disabledTextColor; public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it /// @@ -24,6 +24,7 @@ namespace UnityEngine.UI.Extensions /// 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; @@ -51,11 +52,13 @@ namespace UnityEngine.UI.Extensions private Dictionary panelObjects; private GameObject itemTemplate; + private bool _initialized; public string Text { get; private set; } + [Header("Properties")] [SerializeField] - private float dropdownOffset; + private bool isActive = true; [SerializeField] private float _scrollBarWidth = 20.0f; @@ -81,8 +84,6 @@ namespace UnityEngine.UI.Extensions } } - public bool SelectFirstItemOnStart = false; - [SerializeField] [Tooltip("Change input text color based on matching items")] private bool _ChangeInputTextColorBasedOnMatchingItems = false; @@ -107,9 +108,19 @@ namespace UnityEngine.UI.Extensions 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] @@ -121,12 +132,18 @@ namespace UnityEngine.UI.Extensions [System.Serializable] public class SelectionValidityChangedEvent : 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 item is changed; + public ControlDisabledEvent OnControlDisabled; public void Awake() { @@ -135,16 +152,17 @@ namespace UnityEngine.UI.Extensions public void Start() { - if (SelectFirstItemOnStart && AvailableOptions.Count > 0) + if (shouldSelectItemOnStart && AvailableOptions.Count > 0) { - ToggleDropdownPanel(false); - OnItemClicked(AvailableOptions[0]); + SelectItemIndex(SelectFirstItemOnStart ? 0 : selectItemIndexOnStart); } RedrawPanel(); } private bool Initialize() { + if (_initialized) return true; + bool success = true; try { @@ -184,6 +202,8 @@ namespace UnityEngine.UI.Extensions _prunedPanelItems = new List(); _panelItems = new List(); + _initialized = true; + RebuildPanel(); return success; } @@ -218,6 +238,17 @@ namespace UnityEngine.UI.Extensions } } + + /// + /// 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. /// @@ -263,6 +294,11 @@ namespace UnityEngine.UI.Extensions /// private void RebuildPanel() { + if (!_initialized) + { + Start(); + } + if (_isPanelActive) ToggleDropdownPanel(); //panel starts with all options @@ -427,6 +463,8 @@ namespace UnityEngine.UI.Extensions /// whether an item was directly clicked on public void ToggleDropdownPanel(bool directClick = false) { + if (!isActive) return; + _isPanelActive = !_isPanelActive; _overlayRT.gameObject.SetActive(_isPanelActive); @@ -440,6 +478,20 @@ namespace UnityEngine.UI.Extensions } } + + /// + /// Updates the control and sets its active status, determines whether the dropdown will open ot not + /// + /// + public void SetActive(bool status) + { + if (status != isActive) + { + OnControlDisabled?.Invoke(status); + } + isActive = status; + } + private void PruneItems(string currText) { if (autocompleteSearchType == AutoCompleteSearchType.Linq) diff --git a/Runtime/Scripts/Controls/ComboBox/ComboBox.cs b/Runtime/Scripts/Controls/ComboBox/ComboBox.cs index 8f8a690..b790f57 100644 --- a/Runtime/Scripts/Controls/ComboBox/ComboBox.cs +++ b/Runtime/Scripts/Controls/ComboBox/ComboBox.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; +using static UnityEditor.Progress; namespace UnityEngine.UI.Extensions { @@ -10,11 +11,15 @@ namespace UnityEngine.UI.Extensions [AddComponentMenu("UI/Extensions/ComboBox/ComboBox")] public class ComboBox : MonoBehaviour { - public Color disabledTextColor; public DropDownListItem SelectedItem { get; private set; } + [Header("Combo Box Items")] public List AvailableOptions; + [Header("Properties")] + [SerializeField] + private bool isActive = true; + [SerializeField] private float _scrollBarWidth = 20.0f; @@ -27,22 +32,32 @@ namespace UnityEngine.UI.Extensions [SerializeField] private bool _displayPanelAbove = false; + public bool SelectFirstItemOnStart = false; + + [SerializeField] + private int selectItemIndexOnStart = 0; + + private bool shouldSelectItemOnStart => SelectFirstItemOnStart || selectItemIndexOnStart > 0; + [System.Serializable] public class SelectionChangedEvent : Events.UnityEvent { } + [Header("Events")] // fires when item is changed; public SelectionChangedEvent OnSelectionChanged; + [System.Serializable] + public class ControlDisabledEvent : Events.UnityEvent { } + + // fires when item is changed; + public ControlDisabledEvent OnControlDisabled; + //private bool isInitialized = false; 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; @@ -51,14 +66,11 @@ namespace UnityEngine.UI.Extensions 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 Dictionary panelObjects; - private GameObject itemTemplate; + private bool _initialized; public string Text { get; private set; } @@ -89,11 +101,17 @@ namespace UnityEngine.UI.Extensions public void Start() { + if (shouldSelectItemOnStart && AvailableOptions.Count > 0) + { + SelectItemIndex(SelectFirstItemOnStart ? 0 : selectItemIndexOnStart); + } RedrawPanel(); } private bool Initialize() { + if (_initialized) return true; + bool success = true; try { @@ -133,11 +151,22 @@ namespace UnityEngine.UI.Extensions _panelItems = AvailableOptions.ToList(); + _initialized = true; + RebuildPanel(); - //RedrawPanel(); - causes an initialisation failure in U5 return success; } + /// + /// Update the drop down selection to a specific index + /// + /// + public void SelectItemIndex(int index) + { + ToggleDropdownPanel(false); + OnItemClicked(AvailableOptions[index]); + } + public void AddItem(string item) { AvailableOptions.Add(item); @@ -187,6 +216,11 @@ namespace UnityEngine.UI.Extensions /// private void RebuildPanel() { + if (!_initialized) + { + Start(); + } + //panel starts with all options _panelItems.Clear(); foreach (string option in AvailableOptions) @@ -310,6 +344,8 @@ namespace UnityEngine.UI.Extensions /// whether an item was directly clicked on public void ToggleDropdownPanel(bool directClick) { + if (!isActive) return; + _isPanelActive = !_isPanelActive; _overlayRT.gameObject.SetActive(_isPanelActive); @@ -322,5 +358,18 @@ namespace UnityEngine.UI.Extensions // scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y); } } + + /// + /// Updates the control and sets its active status, determines whether the dropdown will open ot not + /// + /// + public void SetActive(bool status) + { + if (status != isActive) + { + OnControlDisabled?.Invoke(status); + } + isActive = status; + } } } diff --git a/Runtime/Scripts/Controls/ComboBox/DropDownList.cs b/Runtime/Scripts/Controls/ComboBox/DropDownList.cs index 612a0b4..d50f24d 100644 --- a/Runtime/Scripts/Controls/ComboBox/DropDownList.cs +++ b/Runtime/Scripts/Controls/ComboBox/DropDownList.cs @@ -15,7 +15,14 @@ namespace UnityEngine.UI.Extensions public Color disabledTextColor; public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it - public List Items; + [Header("Dropdown List Items")] + public List Items; + + [Header("Properties")] + + [SerializeField] + private bool isActive = true; + public bool OverrideHighlighted = true; //private bool isInitialized = false; @@ -37,12 +44,10 @@ namespace UnityEngine.UI.Extensions private ScrollRect _scrollRect; - private List _panelItems; + private List _panelItems = new List(); - private GameObject _itemTemplate; - - [SerializeField] - private float dropdownOffset; + private GameObject _itemTemplate; + private bool _initialized; [SerializeField] private float _scrollBarWidth = 20.0f; @@ -71,28 +76,45 @@ namespace UnityEngine.UI.Extensions } } - public bool SelectFirstItemOnStart = false; + [SerializeField] + private float dropdownOffset; - [SerializeField] - private bool _displayPanelAbove = false; + [SerializeField] + private bool _displayPanelAbove = false; - [System.Serializable] + public bool SelectFirstItemOnStart = false; + + [SerializeField] + private int selectItemIndexOnStart = 0; + private bool shouldSelectItemOnStart => SelectFirstItemOnStart || selectItemIndexOnStart > 0; + + [System.Serializable] public class SelectionChangedEvent : Events.UnityEvent { } - // fires when item is changed; - public SelectionChangedEvent OnSelectionChanged; - public void Start() - { - Initialize(); - if (SelectFirstItemOnStart && Items.Count > 0) { - ToggleDropdownPanel (false); - OnItemClicked (0); - } - RedrawPanel(); - } + // fires when item is changed; + [Header("Events")] + public SelectionChangedEvent OnSelectionChanged; - private bool Initialize() + [System.Serializable] + public class ControlDisabledEvent : Events.UnityEvent { } + + // fires when item is changed; + public ControlDisabledEvent OnControlDisabled; + + public void Start() + { + Initialize(); + if (shouldSelectItemOnStart && Items.Count > 0) + { + SelectItemIndex(SelectFirstItemOnStart ? 0 : selectItemIndexOnStart); + } + RedrawPanel(); + } + + private bool Initialize() { + if (_initialized) return true; + bool success = true; try { @@ -125,23 +147,32 @@ namespace UnityEngine.UI.Extensions Debug.LogError("Something is setup incorrectly with the dropdownlist component causing a Null Reference Exception"); success = false; } + _initialized = true; - _panelItems = new List(); - - RebuildPanel(); + RebuildPanel(); RedrawPanel(); return success; } - // currently just using items in the list instead of being able to add to it. - /// - /// Rebuilds the list from a new collection. + /// + /// Update the drop down selection to a specific index /// - /// - /// NOTE, this will clear all existing items - /// - /// - public void RefreshItems(params object[] list) + /// + public void SelectItemIndex(int index) + { + ToggleDropdownPanel(false); + OnItemClicked(index); + } + + // currently just using items in the list instead of being able to add to it. + /// + /// Rebuilds the list from a new collection. + /// + /// + /// NOTE, this will clear all existing items + /// + /// + public void RefreshItems(params object[] list) { Items.Clear(); List ddItems = new List(); @@ -249,6 +280,11 @@ namespace UnityEngine.UI.Extensions { if (Items.Count == 0) return; + if (!_initialized) + { + Start(); + } + int indx = _panelItems.Count; while (_panelItems.Count < Items.Count) { @@ -374,6 +410,8 @@ namespace UnityEngine.UI.Extensions /// whether an item was directly clicked on public void ToggleDropdownPanel(bool directClick) { + if (!isActive) return; + _overlayRT.transform.localScale = new Vector3(1, 1, 1); _scrollBarRT.transform.localScale = new Vector3(1, 1, 1); _isPanelActive = !_isPanelActive; @@ -387,5 +425,18 @@ namespace UnityEngine.UI.Extensions // scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y); } } - } + + /// + /// Updates the control and sets its active status, determines whether the dropdown will open ot not + /// + /// + public void SetActive(bool status) + { + if (status != isActive) + { + OnControlDisabled?.Invoke(status); + } + isActive = status; + } + } } \ No newline at end of file