| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390 |
- ///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;
- namespace UnityEngine.UI.Extensions
- {
- /// <summary>
- /// Extension to the UI class which creates a dropdown list
- /// </summary>
- [RequireComponent(typeof(RectTransform))]
- [AddComponentMenu("UI/Extensions/Dropdown List")]
- public class DropDownList : MonoBehaviour
- {
- public Color disabledTextColor;
- public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it
- [NonReorderable]
- public List<DropDownListItem> Items;
- public bool OverrideHighlighted = true;
- //private bool isInitialized = false;
- private bool _isPanelActive = false;
- private bool _hasDrawnOnce = false;
- private DropDownListButton _mainButton;
- 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<DropDownListButton> _panelItems;
- private GameObject _itemTemplate;
- [SerializeField]
- private float _scrollBarWidth = 20.0f;
- public float ScrollBarWidth
- {
- get { return _scrollBarWidth; }
- set
- {
- _scrollBarWidth = value;
- RedrawPanel();
- }
- }
- // private int scrollOffset; //offset of the selected item
- private int _selectedIndex = -1;
- [SerializeField]
- private int _itemsToDisplay;
- public int ItemsToDisplay
- {
- get { return _itemsToDisplay; }
- set
- {
- _itemsToDisplay = value;
- RedrawPanel();
- }
- }
- public bool SelectFirstItemOnStart = false;
- [SerializeField]
- private bool _displayPanelAbove = false;
- [System.Serializable]
- public class SelectionChangedEvent : UnityEngine.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();
- }
- private bool Initialize()
- {
- bool success = true;
- try
- {
- _rectTransform = GetComponent<RectTransform>();
- _mainButton = new DropDownListButton(_rectTransform.Find("MainButton").gameObject);
- _overlayRT = _rectTransform.Find("Overlay").GetComponent<RectTransform>();
- _overlayRT.gameObject.SetActive(false);
- _scrollPanelRT = _overlayRT.Find("ScrollPanel").GetComponent<RectTransform>();
- _scrollBarRT = _scrollPanelRT.Find("Scrollbar").GetComponent<RectTransform>();
- _slidingAreaRT = _scrollBarRT.Find("SlidingArea").GetComponent<RectTransform>();
- _scrollHandleRT = _slidingAreaRT.Find("Handle").GetComponent<RectTransform>();
- _itemsPanelRT = _scrollPanelRT.Find("Items").GetComponent<RectTransform>();
- //itemPanelLayout = itemsPanelRT.gameObject.GetComponent<LayoutGroup>();
- _canvas = GetComponentInParent<Canvas>();
- _canvasRT = _canvas.GetComponent<RectTransform>();
- _scrollRect = _scrollPanelRT.GetComponent<ScrollRect>();
- _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;
- }
- _panelItems = new List<DropDownListButton>();
- 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>
- /// <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>();
- foreach (var obj in list)
- {
- if (obj is DropDownListItem)
- {
- ddItems.Add((DropDownListItem)obj);
- }
- else if (obj is string)
- {
- ddItems.Add(new DropDownListItem(caption: (string)obj));
- }
- else if (obj is Sprite)
- {
- ddItems.Add(new DropDownListItem(image: (Sprite)obj));
- }
- else
- {
- throw new System.Exception("Only ComboBoxItems, Strings, and Sprite types are allowed");
- }
- }
- Items.AddRange(ddItems);
- RebuildPanel();
- }
- /// <summary>
- /// Adds an additional item to the drop down list (recommended)
- /// </summary>
- /// <param name="item">Item of type DropDownListItem</param>
- public void AddItem(DropDownListItem item)
- {
- Items.Add(item);
- RebuildPanel();
- }
- /// <summary>
- /// Adds an additional drop down list item using a string name
- /// </summary>
- /// <param name="item">Item of type String</param>
- public void AddItem(string item)
- {
- Items.Add(new DropDownListItem(caption: (string)item));
- RebuildPanel();
- }
- /// <summary>
- /// Adds an additional drop down list item using a sprite image
- /// </summary>
- /// <param name="item">Item of type UI Sprite</param>
- public void AddItem(Sprite item)
- {
- Items.Add(new DropDownListItem(image: (Sprite)item));
- RebuildPanel();
- }
- /// <summary>
- /// Removes an item from the drop down list (recommended)
- /// </summary>
- /// <param name="item">Item of type DropDownListItem</param>
- public void RemoveItem(DropDownListItem item)
- {
- Items.Remove(item);
- RebuildPanel();
- }
- /// <summary>
- /// Removes an item from the drop down list item using a string name
- /// </summary>
- /// <param name="item">Item of type String</param>
- public void RemoveItem(string item)
- {
- Items.Remove(new DropDownListItem(caption: (string)item));
- RebuildPanel();
- }
- /// <summary>
- /// Removes an item from the drop down list item using a sprite image
- /// </summary>
- /// <param name="item">Item of type UI Sprite</param>
- public void RemoveItem(Sprite item)
- {
- Items.Remove(new DropDownListItem(image: (Sprite)item));
- RebuildPanel();
- }
- public void ResetItems()
- {
- Items.Clear();
- RebuildPanel();
- }
- /// <summary>
- /// Rebuilds the contents of the panel in response to items being added.
- /// </summary>
- private void RebuildPanel()
- {
- if (Items.Count == 0) return;
- int indx = _panelItems.Count;
- while (_panelItems.Count < Items.Count)
- {
- GameObject newItem = Instantiate(_itemTemplate) as GameObject;
- newItem.name = "Item " + indx;
- newItem.transform.SetParent(_itemsPanelRT, false);
- _panelItems.Add(new DropDownListButton(newItem));
- indx++;
- }
- for (int i = 0; i < _panelItems.Count; i++)
- {
- if (i < Items.Count)
- {
- DropDownListItem item = Items[i];
- _panelItems[i].txt.text = item.Caption;
- if (item.IsDisabled) _panelItems[i].txt.color = disabledTextColor;
- if (_panelItems[i].btnImg != null) _panelItems[i].btnImg.sprite = null;//hide the button image
- _panelItems[i].img.sprite = item.Image;
- _panelItems[i].img.color = (item.Image == null) ? new Color(1, 1, 1, 0)
- : item.IsDisabled ? new Color(1, 1, 1, .5f)
- : Color.white;
- int ii = i; //have to copy the variable for use in anonymous function
- _panelItems[i].btn.onClick.RemoveAllListeners();
- _panelItems[i].btn.onClick.AddListener(() =>
- {
- OnItemClicked(ii);
- if (item.OnSelect != null) item.OnSelect();
- });
- }
- _panelItems[i].gameobject.SetActive(i < Items.Count);// if we have more thanks in the panel than Items in the list hide them
- }
- }
- private void OnItemClicked(int indx)
- {
- //Debug.Log("item " + indx + " clicked");
- if (indx != _selectedIndex && OnSelectionChanged != null) OnSelectionChanged.Invoke(indx);
- _selectedIndex = indx;
- ToggleDropdownPanel(true);
- UpdateSelected();
- }
- private void UpdateSelected()
- {
- SelectedItem = (_selectedIndex > -1 && _selectedIndex < Items.Count) ? Items[_selectedIndex] : null;
- if (SelectedItem == null) return;
- bool hasImage = SelectedItem.Image != null;
- if (hasImage)
- {
- _mainButton.img.sprite = SelectedItem.Image;
- _mainButton.img.color = Color.white;
- //if (Interactable) mainButton.img.color = Color.white;
- //else mainButton.img.color = new Color(1, 1, 1, .5f);
- }
- else
- {
- _mainButton.img.sprite = null;
- }
- _mainButton.txt.text = SelectedItem.Caption;
- //update selected index color
- if (OverrideHighlighted)
- {
- for (int i = 0; i < _itemsPanelRT.childCount; i++)
- {
- _panelItems[i].btnImg.color = (_selectedIndex == i) ? _mainButton.btn.colors.highlightedColor : new Color(0, 0, 0, 0);
- }
- }
- }
- private void RedrawPanel()
- {
- float scrollbarWidth = Items.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items
- if (!_hasDrawnOnce || _rectTransform.sizeDelta != _mainButton.rectTransform.sizeDelta)
- {
- _hasDrawnOnce = true;
- _mainButton.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
- _mainButton.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y);
- _mainButton.txt.rectTransform.offsetMax = new Vector2(4, 0);
- _scrollPanelRT.SetParent(transform, true);//break the scroll panel from the overlay
- _scrollPanelRT.anchoredPosition = _displayPanelAbove ?
- new Vector2(0, _rectTransform.sizeDelta.y * ItemsToDisplay - 1) :
- new Vector2(0, -_rectTransform.sizeDelta.y);
- //make the overlay fill the screen
- _overlayRT.SetParent(_canvas.transform, false); //attach it to top level object
- _overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x);
- _overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y);
- _overlayRT.SetParent(transform, true);//reattach to this object
- _scrollPanelRT.SetParent(_overlayRT, true); //reattach the scrollpanel to the overlay
- }
- if (Items.Count < 1) return;
- float dropdownHeight = _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, Items.Count);
- _scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
- _scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
- _itemsPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _scrollPanelRT.sizeDelta.x - scrollbarWidth - 5);
- _itemsPanelRT.anchoredPosition = new Vector2(5, 0);
- _scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, scrollbarWidth);
- _scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
- if (scrollbarWidth == 0) _scrollHandleRT.gameObject.SetActive(false); else _scrollHandleRT.gameObject.SetActive(true);
- _slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 0);
- _slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight - _scrollBarRT.sizeDelta.x);
- }
- /// <summary>
- /// Toggle the drop down list
- /// </summary>
- /// <param name="directClick"> whether an item was directly clicked on</param>
- public void ToggleDropdownPanel(bool directClick)
- {
- _overlayRT.transform.localScale = new Vector3(1, 1, 1);
- _scrollBarRT.transform.localScale = new Vector3(1, 1, 1);
- _isPanelActive = !_isPanelActive;
- _overlayRT.gameObject.SetActive(_isPanelActive);
- if (_isPanelActive)
- {
- transform.SetAsLastSibling();
- }
- else if (directClick)
- {
- // scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y);
- }
- }
- }
- }
|