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
pull/413/head
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
///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
/// <summary>
@ -24,6 +24,7 @@ namespace UnityEngine.UI.Extensions
/// <see cref="RemoveItem(string)"/> and <see cref="SetAvailableOptions(List{string})"/> methods as these also execute
/// the required methods to update to the current collection.
/// </summary>
[Header("AutoComplete Box Items")]
public List<string> AvailableOptions;
private bool _isPanelActive = false;
@ -51,11 +52,13 @@ namespace UnityEngine.UI.Extensions
private Dictionary<string, GameObject> 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<bool> { }
[System.Serializable]
public class ControlDisabledEvent : Events.UnityEvent<bool> { }
// 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<string>();
_panelItems = new List<string>();
_initialized = true;
RebuildPanel();
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>
/// Sets the given items as new content for the comboBox. Previous entries will be cleared.
/// </summary>
@ -263,6 +294,11 @@ namespace UnityEngine.UI.Extensions
/// </summary>
private void RebuildPanel()
{
if (!_initialized)
{
Start();
}
if (_isPanelActive) ToggleDropdownPanel();
//panel starts with all options
@ -427,6 +463,8 @@ namespace UnityEngine.UI.Extensions
/// <param name="directClick"> whether an item was directly clicked on</param>
public void ToggleDropdownPanel(bool directClick = false)
{
if (!isActive) return;
_isPanelActive = !_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)
{
if (autocompleteSearchType == AutoCompleteSearchType.Linq)

View File

@ -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<string> 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<string> { }
[Header("Events")]
// fires when item is changed;
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 _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<string> _panelItems; //items that will get shown in the drop-down
private Dictionary<string, GameObject> 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;
}
/// <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)
{
AvailableOptions.Add(item);
@ -187,6 +216,11 @@ namespace UnityEngine.UI.Extensions
/// </summary>
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
/// <param name="directClick"> whether an item was directly clicked on</param>
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);
}
}
/// <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 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;
//private bool isInitialized = false;
@ -37,12 +44,10 @@ namespace UnityEngine.UI.Extensions
private ScrollRect _scrollRect;
private List<DropDownListButton> _panelItems;
private List<DropDownListButton> _panelItems = new List<DropDownListButton>();
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<int> { }
// 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<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;
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<DropDownListButton>();
RebuildPanel();
RebuildPanel();
RedrawPanel();
return success;
}
// currently just using items in the list instead of being able to add to it.
/// <summary>
/// Rebuilds the list from a new collection.
/// <summary>
/// Update the drop down selection to a specific index
/// </summary>
/// <remarks>
/// NOTE, this will clear all existing items
/// </remarks>
/// <param name="list"></param>
public void RefreshItems(params object[] list)
/// <param name="index"></param>
public void SelectItemIndex(int index)
{
ToggleDropdownPanel(false);
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();
List<DropDownListItem> ddItems = new List<DropDownListItem>();
@ -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
/// <param name="directClick"> whether an item was directly clicked on</param>
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);
}
}
}
/// <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;
}
}
}