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
development
Simon Jackson 2023-02-05 11:35:18 +00:00
parent 9ce7661af6
commit 33cd5a9be4
3 changed files with 203 additions and 51 deletions

View File

@ -1,6 +1,7 @@
///Credit perchik ///Credit perchik
///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/ ///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.Collections.Generic;
using System.Linq; using System.Linq;
@ -16,7 +17,6 @@ namespace UnityEngine.UI.Extensions
[AddComponentMenu("UI/Extensions/ComboBox/AutoComplete ComboBox")] [AddComponentMenu("UI/Extensions/ComboBox/AutoComplete ComboBox")]
public class AutoCompleteComboBox : MonoBehaviour public class AutoCompleteComboBox : MonoBehaviour
{ {
public Color disabledTextColor;
public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it
/// <summary> /// <summary>
@ -24,6 +24,7 @@ namespace UnityEngine.UI.Extensions
/// <see cref="RemoveItem(string)"/> and <see cref="SetAvailableOptions(List{string})"/> methods as these also execute /// <see cref="RemoveItem(string)"/> and <see cref="SetAvailableOptions(List{string})"/> methods as these also execute
/// the required methods to update to the current collection. /// the required methods to update to the current collection.
/// </summary> /// </summary>
[Header("AutoComplete Box Items")]
public List<string> AvailableOptions; public List<string> AvailableOptions;
private bool _isPanelActive = false; private bool _isPanelActive = false;
@ -51,11 +52,13 @@ namespace UnityEngine.UI.Extensions
private Dictionary<string, GameObject> panelObjects; private Dictionary<string, GameObject> panelObjects;
private GameObject itemTemplate; private GameObject itemTemplate;
private bool _initialized;
public string Text { get; private set; } public string Text { get; private set; }
[Header("Properties")]
[SerializeField] [SerializeField]
private float dropdownOffset; private bool isActive = true;
[SerializeField] [SerializeField]
private float _scrollBarWidth = 20.0f; private float _scrollBarWidth = 20.0f;
@ -81,8 +84,6 @@ namespace UnityEngine.UI.Extensions
} }
} }
public bool SelectFirstItemOnStart = false;
[SerializeField] [SerializeField]
[Tooltip("Change input text color based on matching items")] [Tooltip("Change input text color based on matching items")]
private bool _ChangeInputTextColorBasedOnMatchingItems = false; private bool _ChangeInputTextColorBasedOnMatchingItems = false;
@ -107,9 +108,19 @@ namespace UnityEngine.UI.Extensions
public AutoCompleteSearchType autocompleteSearchType = AutoCompleteSearchType.Linq; public AutoCompleteSearchType autocompleteSearchType = AutoCompleteSearchType.Linq;
[SerializeField]
private float dropdownOffset;
[SerializeField] [SerializeField]
private bool _displayPanelAbove = false; private bool _displayPanelAbove = false;
public bool SelectFirstItemOnStart = false;
[SerializeField]
private int selectItemIndexOnStart = 0;
private bool shouldSelectItemOnStart => SelectFirstItemOnStart || selectItemIndexOnStart > 0;
private bool _selectionIsValid = false; private bool _selectionIsValid = false;
[System.Serializable] [System.Serializable]
@ -121,12 +132,18 @@ namespace UnityEngine.UI.Extensions
[System.Serializable] [System.Serializable]
public class SelectionValidityChangedEvent : Events.UnityEvent<bool> { } public class SelectionValidityChangedEvent : Events.UnityEvent<bool> { }
[System.Serializable]
public class ControlDisabledEvent : Events.UnityEvent<bool> { }
// fires when input text is changed; // fires when input text is changed;
[Header("Events")]
public SelectionTextChangedEvent OnSelectionTextChanged; public SelectionTextChangedEvent OnSelectionTextChanged;
// fires when an Item gets selected / deselected (including when items are added/removed once this is possible) // fires when an Item gets selected / deselected (including when items are added/removed once this is possible)
public SelectionValidityChangedEvent OnSelectionValidityChanged; public SelectionValidityChangedEvent OnSelectionValidityChanged;
// fires in both cases // fires in both cases
public SelectionChangedEvent OnSelectionChanged; public SelectionChangedEvent OnSelectionChanged;
// fires when item is changed;
public ControlDisabledEvent OnControlDisabled;
public void Awake() public void Awake()
{ {
@ -135,16 +152,17 @@ namespace UnityEngine.UI.Extensions
public void Start() public void Start()
{ {
if (SelectFirstItemOnStart && AvailableOptions.Count > 0) if (shouldSelectItemOnStart && AvailableOptions.Count > 0)
{ {
ToggleDropdownPanel(false); SelectItemIndex(SelectFirstItemOnStart ? 0 : selectItemIndexOnStart);
OnItemClicked(AvailableOptions[0]);
} }
RedrawPanel(); RedrawPanel();
} }
private bool Initialize() private bool Initialize()
{ {
if (_initialized) return true;
bool success = true; bool success = true;
try try
{ {
@ -184,6 +202,8 @@ namespace UnityEngine.UI.Extensions
_prunedPanelItems = new List<string>(); _prunedPanelItems = new List<string>();
_panelItems = new List<string>(); _panelItems = new List<string>();
_initialized = true;
RebuildPanel(); RebuildPanel();
return success; return success;
} }
@ -218,6 +238,17 @@ namespace UnityEngine.UI.Extensions
} }
} }
/// <summary>
/// Update the drop down selection to a specific index
/// </summary>
/// <param name="index"></param>
public void SelectItemIndex(int index)
{
ToggleDropdownPanel(false);
OnItemClicked(AvailableOptions[index]);
}
/// <summary> /// <summary>
/// Sets the given items as new content for the comboBox. Previous entries will be cleared. /// Sets the given items as new content for the comboBox. Previous entries will be cleared.
/// </summary> /// </summary>
@ -263,6 +294,11 @@ namespace UnityEngine.UI.Extensions
/// </summary> /// </summary>
private void RebuildPanel() private void RebuildPanel()
{ {
if (!_initialized)
{
Start();
}
if (_isPanelActive) ToggleDropdownPanel(); if (_isPanelActive) ToggleDropdownPanel();
//panel starts with all options //panel starts with all options
@ -427,6 +463,8 @@ namespace UnityEngine.UI.Extensions
/// <param name="directClick"> whether an item was directly clicked on</param> /// <param name="directClick"> whether an item was directly clicked on</param>
public void ToggleDropdownPanel(bool directClick = false) public void ToggleDropdownPanel(bool directClick = false)
{ {
if (!isActive) return;
_isPanelActive = !_isPanelActive; _isPanelActive = !_isPanelActive;
_overlayRT.gameObject.SetActive(_isPanelActive); _overlayRT.gameObject.SetActive(_isPanelActive);
@ -440,6 +478,20 @@ namespace UnityEngine.UI.Extensions
} }
} }
/// <summary>
/// Updates the control and sets its active status, determines whether the dropdown will open ot not
/// </summary>
/// <param name="status"></param>
public void SetActive(bool status)
{
if (status != isActive)
{
OnControlDisabled?.Invoke(status);
}
isActive = status;
}
private void PruneItems(string currText) private void PruneItems(string currText)
{ {
if (autocompleteSearchType == AutoCompleteSearchType.Linq) if (autocompleteSearchType == AutoCompleteSearchType.Linq)

View File

@ -3,6 +3,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using static UnityEditor.Progress;
namespace UnityEngine.UI.Extensions namespace UnityEngine.UI.Extensions
{ {
@ -10,11 +11,15 @@ namespace UnityEngine.UI.Extensions
[AddComponentMenu("UI/Extensions/ComboBox/ComboBox")] [AddComponentMenu("UI/Extensions/ComboBox/ComboBox")]
public class ComboBox : MonoBehaviour public class ComboBox : MonoBehaviour
{ {
public Color disabledTextColor;
public DropDownListItem SelectedItem { get; private set; } public DropDownListItem SelectedItem { get; private set; }
[Header("Combo Box Items")]
public List<string> AvailableOptions; public List<string> AvailableOptions;
[Header("Properties")]
[SerializeField]
private bool isActive = true;
[SerializeField] [SerializeField]
private float _scrollBarWidth = 20.0f; private float _scrollBarWidth = 20.0f;
@ -27,22 +32,32 @@ namespace UnityEngine.UI.Extensions
[SerializeField] [SerializeField]
private bool _displayPanelAbove = false; private bool _displayPanelAbove = false;
public bool SelectFirstItemOnStart = false;
[SerializeField]
private int selectItemIndexOnStart = 0;
private bool shouldSelectItemOnStart => SelectFirstItemOnStart || selectItemIndexOnStart > 0;
[System.Serializable] [System.Serializable]
public class SelectionChangedEvent : Events.UnityEvent<string> { } public class SelectionChangedEvent : Events.UnityEvent<string> { }
[Header("Events")]
// fires when item is changed; // fires when item is changed;
public SelectionChangedEvent OnSelectionChanged; public SelectionChangedEvent OnSelectionChanged;
[System.Serializable]
public class ControlDisabledEvent : Events.UnityEvent<bool> { }
// fires when item is changed;
public ControlDisabledEvent OnControlDisabled;
//private bool isInitialized = false; //private bool isInitialized = false;
private bool _isPanelActive = false; private bool _isPanelActive = false;
private bool _hasDrawnOnce = false; private bool _hasDrawnOnce = false;
private InputField _mainInput; private InputField _mainInput;
private RectTransform _inputRT; private RectTransform _inputRT;
private RectTransform _rectTransform; private RectTransform _rectTransform;
private RectTransform _overlayRT; private RectTransform _overlayRT;
private RectTransform _scrollPanelRT; private RectTransform _scrollPanelRT;
private RectTransform _scrollBarRT; private RectTransform _scrollBarRT;
@ -51,14 +66,11 @@ namespace UnityEngine.UI.Extensions
private RectTransform _itemsPanelRT; private RectTransform _itemsPanelRT;
private Canvas _canvas; private Canvas _canvas;
private RectTransform _canvasRT; private RectTransform _canvasRT;
private ScrollRect _scrollRect; private ScrollRect _scrollRect;
private List<string> _panelItems; //items that will get shown in the drop-down private List<string> _panelItems; //items that will get shown in the drop-down
private Dictionary<string, GameObject> panelObjects; private Dictionary<string, GameObject> panelObjects;
private GameObject itemTemplate; private GameObject itemTemplate;
private bool _initialized;
public string Text { get; private set; } public string Text { get; private set; }
@ -89,11 +101,17 @@ namespace UnityEngine.UI.Extensions
public void Start() public void Start()
{ {
if (shouldSelectItemOnStart && AvailableOptions.Count > 0)
{
SelectItemIndex(SelectFirstItemOnStart ? 0 : selectItemIndexOnStart);
}
RedrawPanel(); RedrawPanel();
} }
private bool Initialize() private bool Initialize()
{ {
if (_initialized) return true;
bool success = true; bool success = true;
try try
{ {
@ -133,11 +151,22 @@ namespace UnityEngine.UI.Extensions
_panelItems = AvailableOptions.ToList(); _panelItems = AvailableOptions.ToList();
_initialized = true;
RebuildPanel(); RebuildPanel();
//RedrawPanel(); - causes an initialisation failure in U5
return success; return success;
} }
/// <summary>
/// Update the drop down selection to a specific index
/// </summary>
/// <param name="index"></param>
public void SelectItemIndex(int index)
{
ToggleDropdownPanel(false);
OnItemClicked(AvailableOptions[index]);
}
public void AddItem(string item) public void AddItem(string item)
{ {
AvailableOptions.Add(item); AvailableOptions.Add(item);
@ -187,6 +216,11 @@ namespace UnityEngine.UI.Extensions
/// </summary> /// </summary>
private void RebuildPanel() private void RebuildPanel()
{ {
if (!_initialized)
{
Start();
}
//panel starts with all options //panel starts with all options
_panelItems.Clear(); _panelItems.Clear();
foreach (string option in AvailableOptions) foreach (string option in AvailableOptions)
@ -310,6 +344,8 @@ namespace UnityEngine.UI.Extensions
/// <param name="directClick"> whether an item was directly clicked on</param> /// <param name="directClick"> whether an item was directly clicked on</param>
public void ToggleDropdownPanel(bool directClick) public void ToggleDropdownPanel(bool directClick)
{ {
if (!isActive) return;
_isPanelActive = !_isPanelActive; _isPanelActive = !_isPanelActive;
_overlayRT.gameObject.SetActive(_isPanelActive); _overlayRT.gameObject.SetActive(_isPanelActive);
@ -322,5 +358,18 @@ namespace UnityEngine.UI.Extensions
// scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y); // scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y);
} }
} }
/// <summary>
/// Updates the control and sets its active status, determines whether the dropdown will open ot not
/// </summary>
/// <param name="status"></param>
public void SetActive(bool status)
{
if (status != isActive)
{
OnControlDisabled?.Invoke(status);
}
isActive = status;
}
} }
} }

View File

@ -15,7 +15,14 @@ namespace UnityEngine.UI.Extensions
public Color disabledTextColor; public Color disabledTextColor;
public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it
public List<DropDownListItem> Items; [Header("Dropdown List Items")]
public List<DropDownListItem> Items;
[Header("Properties")]
[SerializeField]
private bool isActive = true;
public bool OverrideHighlighted = true; public bool OverrideHighlighted = true;
//private bool isInitialized = false; //private bool isInitialized = false;
@ -37,12 +44,10 @@ namespace UnityEngine.UI.Extensions
private ScrollRect _scrollRect; private ScrollRect _scrollRect;
private List<DropDownListButton> _panelItems; private List<DropDownListButton> _panelItems = new List<DropDownListButton>();
private GameObject _itemTemplate; private GameObject _itemTemplate;
private bool _initialized;
[SerializeField]
private float dropdownOffset;
[SerializeField] [SerializeField]
private float _scrollBarWidth = 20.0f; private float _scrollBarWidth = 20.0f;
@ -71,28 +76,45 @@ namespace UnityEngine.UI.Extensions
} }
} }
public bool SelectFirstItemOnStart = false; [SerializeField]
private float dropdownOffset;
[SerializeField] [SerializeField]
private bool _displayPanelAbove = false; 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<int> { } public class SelectionChangedEvent : Events.UnityEvent<int> { }
// fires when item is changed;
public SelectionChangedEvent OnSelectionChanged;
public void Start() // fires when item is changed;
{ [Header("Events")]
Initialize(); public SelectionChangedEvent OnSelectionChanged;
if (SelectFirstItemOnStart && Items.Count > 0) {
ToggleDropdownPanel (false);
OnItemClicked (0);
}
RedrawPanel();
}
private bool Initialize() [System.Serializable]
public class ControlDisabledEvent : Events.UnityEvent<bool> { }
// 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; bool success = true;
try try
{ {
@ -125,23 +147,32 @@ namespace UnityEngine.UI.Extensions
Debug.LogError("Something is setup incorrectly with the dropdownlist component causing a Null Reference Exception"); Debug.LogError("Something is setup incorrectly with the dropdownlist component causing a Null Reference Exception");
success = false; success = false;
} }
_initialized = true;
_panelItems = new List<DropDownListButton>(); RebuildPanel();
RebuildPanel();
RedrawPanel(); RedrawPanel();
return success; return success;
} }
// currently just using items in the list instead of being able to add to it. /// <summary>
/// <summary> /// Update the drop down selection to a specific index
/// Rebuilds the list from a new collection.
/// </summary> /// </summary>
/// <remarks> /// <param name="index"></param>
/// NOTE, this will clear all existing items public void SelectItemIndex(int index)
/// </remarks> {
/// <param name="list"></param> ToggleDropdownPanel(false);
public void RefreshItems(params object[] list) OnItemClicked(index);
}
// currently just using items in the list instead of being able to add to it.
/// <summary>
/// Rebuilds the list from a new collection.
/// </summary>
/// <remarks>
/// NOTE, this will clear all existing items
/// </remarks>
/// <param name="list"></param>
public void RefreshItems(params object[] list)
{ {
Items.Clear(); Items.Clear();
List<DropDownListItem> ddItems = new List<DropDownListItem>(); List<DropDownListItem> ddItems = new List<DropDownListItem>();
@ -249,6 +280,11 @@ namespace UnityEngine.UI.Extensions
{ {
if (Items.Count == 0) return; if (Items.Count == 0) return;
if (!_initialized)
{
Start();
}
int indx = _panelItems.Count; int indx = _panelItems.Count;
while (_panelItems.Count < Items.Count) while (_panelItems.Count < Items.Count)
{ {
@ -374,6 +410,8 @@ namespace UnityEngine.UI.Extensions
/// <param name="directClick"> whether an item was directly clicked on</param> /// <param name="directClick"> whether an item was directly clicked on</param>
public void ToggleDropdownPanel(bool directClick) public void ToggleDropdownPanel(bool directClick)
{ {
if (!isActive) return;
_overlayRT.transform.localScale = new Vector3(1, 1, 1); _overlayRT.transform.localScale = new Vector3(1, 1, 1);
_scrollBarRT.transform.localScale = new Vector3(1, 1, 1); _scrollBarRT.transform.localScale = new Vector3(1, 1, 1);
_isPanelActive = !_isPanelActive; _isPanelActive = !_isPanelActive;
@ -387,5 +425,18 @@ namespace UnityEngine.UI.Extensions
// scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y); // scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y);
} }
} }
}
/// <summary>
/// Updates the control and sets its active status, determines whether the dropdown will open ot not
/// </summary>
/// <param name="status"></param>
public void SetActive(bool status)
{
if (status != isActive)
{
OnControlDisabled?.Invoke(status);
}
isActive = status;
}
}
} }