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