ComboBox.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. ///Credit perchik
  2. ///Sourced from - http://forum.unity3d.com/threads/receive-onclick-event-and-pass-it-on-to-lower-ui-elements.293642/
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. namespace UnityEngine.UI.Extensions
  6. {
  7. [RequireComponent(typeof(RectTransform))]
  8. [AddComponentMenu("UI/Extensions/ComboBox")]
  9. public class ComboBox : MonoBehaviour
  10. {
  11. public Color disabledTextColor;
  12. public DropDownListItem SelectedItem { get; private set; } //outside world gets to get this, not set it
  13. public List<string> AvailableOptions;
  14. [SerializeField]
  15. private float _scrollBarWidth = 20.0f;
  16. [SerializeField]
  17. private int _itemsToDisplay;
  18. //Sorting disabled as it causes issues.
  19. //[SerializeField]
  20. //private bool _sortItems = true;
  21. [SerializeField]
  22. private bool _displayPanelAbove = false;
  23. [System.Serializable]
  24. public class SelectionChangedEvent : UnityEngine.Events.UnityEvent<string>
  25. {
  26. }
  27. // fires when item is changed;
  28. public SelectionChangedEvent OnSelectionChanged;
  29. //private bool isInitialized = false;
  30. private bool _isPanelActive = false;
  31. private bool _hasDrawnOnce = false;
  32. private InputField _mainInput;
  33. private RectTransform _inputRT;
  34. private RectTransform _rectTransform;
  35. private RectTransform _overlayRT;
  36. private RectTransform _scrollPanelRT;
  37. private RectTransform _scrollBarRT;
  38. private RectTransform _slidingAreaRT;
  39. private RectTransform _scrollHandleRT;
  40. private RectTransform _itemsPanelRT;
  41. private Canvas _canvas;
  42. private RectTransform _canvasRT;
  43. private ScrollRect _scrollRect;
  44. private List<string> _panelItems; //items that will get shown in the drop-down
  45. private Dictionary<string, GameObject> panelObjects;
  46. private GameObject itemTemplate;
  47. public string Text { get; private set; }
  48. public float ScrollBarWidth
  49. {
  50. get { return _scrollBarWidth; }
  51. set
  52. {
  53. _scrollBarWidth = value;
  54. RedrawPanel();
  55. }
  56. }
  57. // private int scrollOffset; //offset of the selected item
  58. // private int _selectedIndex = 0;
  59. public int ItemsToDisplay
  60. {
  61. get { return _itemsToDisplay; }
  62. set
  63. {
  64. _itemsToDisplay = value;
  65. RedrawPanel();
  66. }
  67. }
  68. public void Awake()
  69. {
  70. Initialize();
  71. }
  72. public void Start()
  73. {
  74. RedrawPanel();
  75. }
  76. private bool Initialize()
  77. {
  78. bool success = true;
  79. try
  80. {
  81. _rectTransform = GetComponent<RectTransform>();
  82. _inputRT = _rectTransform.Find("InputField").GetComponent<RectTransform>();
  83. _mainInput = _inputRT.GetComponent<InputField>();
  84. _overlayRT = _rectTransform.Find("Overlay").GetComponent<RectTransform>();
  85. _overlayRT.gameObject.SetActive(false);
  86. _scrollPanelRT = _overlayRT.Find("ScrollPanel").GetComponent<RectTransform>();
  87. _scrollBarRT = _scrollPanelRT.Find("Scrollbar").GetComponent<RectTransform>();
  88. _slidingAreaRT = _scrollBarRT.Find("SlidingArea").GetComponent<RectTransform>();
  89. _scrollHandleRT = _slidingAreaRT.Find("Handle").GetComponent<RectTransform>();
  90. _itemsPanelRT = _scrollPanelRT.Find("Items").GetComponent<RectTransform>();
  91. //itemPanelLayout = itemsPanelRT.gameObject.GetComponent<LayoutGroup>();
  92. _canvas = GetComponentInParent<Canvas>();
  93. _canvasRT = _canvas.GetComponent<RectTransform>();
  94. _scrollRect = _scrollPanelRT.GetComponent<ScrollRect>();
  95. _scrollRect.scrollSensitivity = _rectTransform.sizeDelta.y / 2;
  96. _scrollRect.movementType = ScrollRect.MovementType.Clamped;
  97. _scrollRect.content = _itemsPanelRT;
  98. itemTemplate = _rectTransform.Find("ItemTemplate").gameObject;
  99. itemTemplate.SetActive(false);
  100. }
  101. catch (System.NullReferenceException ex)
  102. {
  103. Debug.LogException(ex);
  104. Debug.LogError("Something is setup incorrectly with the dropdownlist component causing a Null Reference Exception");
  105. success = false;
  106. }
  107. panelObjects = new Dictionary<string, GameObject>();
  108. _panelItems = AvailableOptions.ToList();
  109. RebuildPanel();
  110. //RedrawPanel(); - causes an initialisation failure in U5
  111. return success;
  112. }
  113. public void AddItem(string item)
  114. {
  115. AvailableOptions.Add(item);
  116. RebuildPanel();
  117. }
  118. public void RemoveItem(string item)
  119. {
  120. AvailableOptions.Remove(item);
  121. RebuildPanel();
  122. }
  123. public void SetAvailableOptions(List<string> newOptions)
  124. {
  125. AvailableOptions.Clear();
  126. AvailableOptions = newOptions;
  127. RebuildPanel();
  128. }
  129. public void SetAvailableOptions(string[] newOptions)
  130. {
  131. AvailableOptions.Clear();
  132. for (int i = 0; i < newOptions.Length; i++)
  133. {
  134. AvailableOptions.Add(newOptions[i]);
  135. }
  136. RebuildPanel();
  137. }
  138. public void ResetItems()
  139. {
  140. AvailableOptions.Clear();
  141. RebuildPanel();
  142. }
  143. /// <summary>
  144. /// Rebuilds the contents of the panel in response to items being added.
  145. /// </summary>
  146. private void RebuildPanel()
  147. {
  148. //panel starts with all options
  149. _panelItems.Clear();
  150. foreach (string option in AvailableOptions)
  151. {
  152. _panelItems.Add(option.ToLower());
  153. }
  154. //if(_sortItems) _panelItems.Sort();
  155. List<GameObject> itemObjs = new List<GameObject>(panelObjects.Values);
  156. panelObjects.Clear();
  157. int indx = 0;
  158. while (itemObjs.Count < AvailableOptions.Count)
  159. {
  160. GameObject newItem = Instantiate(itemTemplate) as GameObject;
  161. newItem.name = "Item " + indx;
  162. newItem.transform.SetParent(_itemsPanelRT, false);
  163. itemObjs.Add(newItem);
  164. indx++;
  165. }
  166. for (int i = 0; i < itemObjs.Count; i++)
  167. {
  168. itemObjs[i].SetActive(i <= AvailableOptions.Count);
  169. if (i < AvailableOptions.Count)
  170. {
  171. itemObjs[i].name = "Item " + i + " " + _panelItems[i];
  172. itemObjs[i].transform.Find("Text").GetComponent<Text>().text = AvailableOptions[i]; //set the text value
  173. Button itemBtn = itemObjs[i].GetComponent<Button>();
  174. itemBtn.onClick.RemoveAllListeners();
  175. string textOfItem = _panelItems[i]; //has to be copied for anonymous function or it gets garbage collected away
  176. itemBtn.onClick.AddListener(() =>
  177. {
  178. OnItemClicked(textOfItem);
  179. });
  180. panelObjects[_panelItems[i]] = itemObjs[i];
  181. }
  182. }
  183. }
  184. /// <summary>
  185. /// what happens when an item in the list is selected
  186. /// </summary>
  187. /// <param name="item"></param>
  188. private void OnItemClicked(string item)
  189. {
  190. //Debug.Log("item " + item + " clicked");
  191. Text = item;
  192. _mainInput.text = Text;
  193. ToggleDropdownPanel(true);
  194. }
  195. //private void UpdateSelected()
  196. //{
  197. // SelectedItem = (_selectedIndex > -1 && _selectedIndex < Items.Count) ? Items[_selectedIndex] : null;
  198. // if (SelectedItem == null) return;
  199. // bool hasImage = SelectedItem.Image != null;
  200. // if (hasImage)
  201. // {
  202. // mainButton.img.sprite = SelectedItem.Image;
  203. // mainButton.img.color = Color.white;
  204. // //if (Interactable) mainButton.img.color = Color.white;
  205. // //else mainButton.img.color = new Color(1, 1, 1, .5f);
  206. // }
  207. // else
  208. // {
  209. // mainButton.img.sprite = null;
  210. // }
  211. // mainButton.txt.text = SelectedItem.Caption;
  212. // //update selected index color
  213. // for (int i = 0; i < itemsPanelRT.childCount; i++)
  214. // {
  215. // panelItems[i].btnImg.color = (_selectedIndex == i) ? mainButton.btn.colors.highlightedColor : new Color(0, 0, 0, 0);
  216. // }
  217. //}
  218. private void RedrawPanel()
  219. {
  220. float scrollbarWidth = _panelItems.Count > ItemsToDisplay ? _scrollBarWidth : 0f;//hide the scrollbar if there's not enough items
  221. _scrollBarRT.gameObject.SetActive(_panelItems.Count > ItemsToDisplay);
  222. if (!_hasDrawnOnce || _rectTransform.sizeDelta != _inputRT.sizeDelta)
  223. {
  224. _hasDrawnOnce = true;
  225. _inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
  226. _inputRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _rectTransform.sizeDelta.y);
  227. _scrollPanelRT.SetParent(transform, true);//break the scroll panel from the overlay
  228. _scrollPanelRT.anchoredPosition = _displayPanelAbove ?
  229. new Vector2(0, _rectTransform.sizeDelta.y * ItemsToDisplay - 1) :
  230. new Vector2(0, -_rectTransform.sizeDelta.y);
  231. //make the overlay fill the screen
  232. _overlayRT.SetParent(_canvas.transform, false); //attach it to top level object
  233. _overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _canvasRT.sizeDelta.x);
  234. _overlayRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, _canvasRT.sizeDelta.y);
  235. _overlayRT.SetParent(transform, true);//reattach to this object
  236. _scrollPanelRT.SetParent(_overlayRT, true); //reattach the scrollpanel to the overlay
  237. }
  238. if (_panelItems.Count < 1) return;
  239. float dropdownHeight = _rectTransform.sizeDelta.y * Mathf.Min(_itemsToDisplay, _panelItems.Count);
  240. _scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
  241. _scrollPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _rectTransform.sizeDelta.x);
  242. _itemsPanelRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, _scrollPanelRT.sizeDelta.x - scrollbarWidth - 5);
  243. _itemsPanelRT.anchoredPosition = new Vector2(5, 0);
  244. _scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, scrollbarWidth);
  245. _scrollBarRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight);
  246. if (scrollbarWidth == 0) _scrollHandleRT.gameObject.SetActive(false); else _scrollHandleRT.gameObject.SetActive(true);
  247. _slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 0);
  248. _slidingAreaRT.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, dropdownHeight - _scrollBarRT.sizeDelta.x);
  249. }
  250. public void OnValueChanged(string currText)
  251. {
  252. Text = currText;
  253. RedrawPanel();
  254. //Debug.Log("value changed to: " + currText);
  255. if (_panelItems.Count == 0)
  256. {
  257. _isPanelActive = true;//this makes it get turned off
  258. ToggleDropdownPanel(false);
  259. }
  260. else if (!_isPanelActive)
  261. {
  262. ToggleDropdownPanel(false);
  263. }
  264. OnSelectionChanged.Invoke(Text);
  265. }
  266. /// <summary>
  267. /// Toggle the drop down list
  268. /// </summary>
  269. /// <param name="directClick"> whether an item was directly clicked on</param>
  270. public void ToggleDropdownPanel(bool directClick)
  271. {
  272. _isPanelActive = !_isPanelActive;
  273. _overlayRT.gameObject.SetActive(_isPanelActive);
  274. if (_isPanelActive)
  275. {
  276. transform.SetAsLastSibling();
  277. }
  278. else if (directClick)
  279. {
  280. // scrollOffset = Mathf.RoundToInt(itemsPanelRT.anchoredPosition.y / _rectTransform.sizeDelta.y);
  281. }
  282. }
  283. }
  284. }