1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "GUI/BsGUIListBox.h"
4#include "GUI/BsGUIWidget.h"
5#include "GUI/BsGUIDimensions.h"
6#include "GUI/BsGUIMouseEvent.h"
7#include "GUI/BsGUIManager.h"
8#include "GUI/BsGUIDropDownBoxManager.h"
9
10namespace bs
11{
12 const String& GUIListBox::getGUITypeName()
13 {
14 static String name = "ListBox";
15 return name;
16 }
17
18 GUIListBox::GUIListBox(const String& styleName, const Vector<HString>& elements, bool isMultiselect, const GUIDimensions& dimensions)
19 :GUIButtonBase(styleName, GUIContent(HString("")), dimensions), mElements(elements), mIsMultiselect(isMultiselect)
20 {
21 mElementStates.resize(elements.size(), false);
22 if (!mIsMultiselect && mElementStates.size() > 0)
23 mElementStates[0] = true;
24
25 updateContents();
26 }
27
28 GUIListBox::~GUIListBox()
29 {
30 closeListBox();
31 }
32
33 GUIListBox* GUIListBox::create(const Vector<HString>& elements, bool isMultiselect, const String& styleName)
34 {
35 return new (bs_alloc<GUIListBox>()) GUIListBox(getStyleName<GUIListBox>(styleName), elements, isMultiselect, GUIDimensions::create());
36 }
37
38 GUIListBox* GUIListBox::create(const Vector<HString>& elements, bool isMultiselect, const GUIOptions& options, const String& styleName)
39 {
40 return new (bs_alloc<GUIListBox>()) GUIListBox(getStyleName<GUIListBox>(styleName), elements, isMultiselect, GUIDimensions::create(options));
41 }
42
43 GUIListBox* GUIListBox::create(const Vector<HString>& elements, const GUIOptions& options, const String& styleName)
44 {
45 return new (bs_alloc<GUIListBox>()) GUIListBox(getStyleName<GUIListBox>(styleName), elements, false, GUIDimensions::create(options));
46 }
47
48 void GUIListBox::setElements(const Vector<HString>& elements)
49 {
50 bool wasOpen = mDropDownBox != nullptr;
51
52 if(wasOpen)
53 closeListBox();
54
55 mElements = elements;
56
57 mElementStates.clear();
58 mElementStates.resize(mElements.size(), false);
59 if (!mIsMultiselect && mElementStates.size() > 0)
60 mElementStates[0] = true;
61
62 updateContents();
63
64 if(wasOpen)
65 openListBox();
66 }
67
68 void GUIListBox::selectElement(UINT32 idx)
69 {
70 if (idx >= (UINT32)mElements.size())
71 return;
72
73 if (mElementStates[idx] != true)
74 elementSelected(idx);
75 }
76
77 void GUIListBox::deselectElement(UINT32 idx)
78 {
79 if (!mIsMultiselect || idx >= (UINT32)mElements.size())
80 return;
81
82 if (mElementStates[idx] != false)
83 elementSelected(idx);
84 }
85
86 void GUIListBox::setElementStates(const Vector<bool>& states)
87 {
88 UINT32 numElements = (UINT32)mElementStates.size();
89 UINT32 min = std::min(numElements, (UINT32)states.size());
90
91 bool anythingModified = min != numElements;
92 if (!anythingModified)
93 {
94 for (UINT32 i = 0; i < numElements; i++)
95 {
96 if (mElementStates[i] != states[i])
97 {
98 anythingModified = true;
99 break;
100 }
101 }
102 }
103
104 if (!anythingModified)
105 return;
106
107 bool wasOpen = mDropDownBox != nullptr;
108
109 if (wasOpen)
110 closeListBox();
111
112 for (UINT32 i = 0; i < min; i++)
113 {
114 mElementStates[i] = states[i];
115
116 if (mElementStates[i] && !mIsMultiselect)
117 {
118 for (UINT32 j = i + 1; j < numElements; j++)
119 mElementStates[j] = false;
120
121 break;
122 }
123 }
124
125 updateContents();
126
127 if (wasOpen)
128 openListBox();
129 }
130
131 bool GUIListBox::_mouseEvent(const GUIMouseEvent& ev)
132 {
133 bool processed = GUIButtonBase::_mouseEvent(ev);
134
135 if(ev.getType() == GUIMouseEventType::MouseDown)
136 {
137 if (!_isDisabled())
138 {
139 if (mDropDownBox == nullptr)
140 openListBox();
141 else
142 closeListBox();
143 }
144
145 processed = true;
146 }
147
148 return processed;
149 }
150
151 bool GUIListBox::_commandEvent(const GUICommandEvent& ev)
152 {
153 const bool processed = GUIButtonBase::_commandEvent(ev);
154
155 if(ev.getType() == GUICommandEventType::Confirm)
156 {
157 if(!_isDisabled())
158 {
159 if (mDropDownBox == nullptr)
160 openListBox();
161 else
162 closeListBox();
163 }
164
165 return true;
166 }
167
168 return processed;
169 }
170
171 void GUIListBox::elementSelected(UINT32 idx)
172 {
173 if (idx >= (UINT32)mElements.size())
174 return;
175
176 if (mIsMultiselect)
177 {
178 bool selected = mElementStates[idx];
179 mElementStates[idx] = !selected;
180
181 if (!onSelectionToggled.empty())
182 onSelectionToggled(idx, !selected);
183 }
184 else
185 {
186 for (UINT32 i = 0; i < (UINT32)mElementStates.size(); i++)
187 mElementStates[i] = false;
188
189 mElementStates[idx] = true;
190
191 if (!onSelectionToggled.empty())
192 onSelectionToggled(idx, true);
193
194 closeListBox();
195 }
196
197 updateContents();
198 }
199
200 void GUIListBox::openListBox()
201 {
202 closeListBox();
203
204 DROP_DOWN_BOX_DESC desc;
205
206 UINT32 i = 0;
207 for(auto& elem : mElements)
208 {
209 String identifier = toString(i);
210 desc.dropDownData.entries.push_back(GUIDropDownDataEntry::button(identifier, std::bind(&GUIListBox::elementSelected, this, i)));
211 desc.dropDownData.localizedNames[identifier] = elem;
212 i++;
213 }
214
215 GUIWidget* widget = _getParentWidget();
216
217 desc.camera = widget->getCamera();
218 desc.skin = widget->getSkinResource();
219 desc.placement = DropDownAreaPlacement::aroundBoundsHorz(mClippedBounds);
220 desc.dropDownData.states = mElementStates;
221
222 GUIDropDownType type;
223 if (mIsMultiselect)
224 type = GUIDropDownType::MultiListBox;
225 else
226 type = GUIDropDownType::ListBox;
227
228 mDropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(
229 desc, type, std::bind(&GUIListBox::onListBoxClosed, this));
230
231 _setOn(true);
232 }
233
234 void GUIListBox::closeListBox()
235 {
236 if (mDropDownBox != nullptr)
237 {
238 GUIDropDownBoxManager::instance().closeDropDownBox();
239
240 _setOn(false);
241 mDropDownBox = nullptr;
242 }
243 }
244
245 void GUIListBox::updateContents()
246 {
247 UINT32 selectedIdx = 0;
248 UINT32 numSelected = 0;
249 for (UINT32 i = 0; i < (UINT32)mElementStates.size(); i++)
250 {
251 if (mElementStates[i])
252 {
253 selectedIdx = i;
254 numSelected++;
255 }
256 }
257
258 if (mIsMultiselect)
259 {
260 if (numSelected == 1)
261 setContent(GUIContent(mElements[selectedIdx]));
262 else if (numSelected == 0)
263 setContent(GUIContent(HEString("None")));
264 else
265 setContent(GUIContent(HEString("Multiple")));
266 }
267 else
268 {
269 if(!mElements.empty())
270 setContent(GUIContent(mElements[selectedIdx]));
271 else
272 setContent(GUIContent(HEString("None")));
273 }
274 }
275
276 void GUIListBox::onListBoxClosed()
277 {
278 _setOn(false);
279 mDropDownBox = nullptr;
280 }
281}