1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
2 // This file is part of the "Irrlicht Engine".
3 // For conditions of distribution and use, see copyright notice in irrlicht.h
4
5 #include "CGUIComboBox.h"
6 #ifdef _IRR_COMPILE_WITH_GUI_
7
8 #include "IGUIEnvironment.h"
9 #include "IVideoDriver.h"
10 #include "IGUISkin.h"
11 #include "IGUIEnvironment.h"
12 #include "IGUIFont.h"
13 #include "IGUIButton.h"
14 #include "CGUIListBox.h"
15 #include "os.h"
16
17 namespace irr
18 {
19 namespace gui
20 {
21
22 //! constructor
CGUIComboBox(IGUIEnvironment * environment,IGUIElement * parent,s32 id,core::rect<s32> rectangle)23 CGUIComboBox::CGUIComboBox(IGUIEnvironment* environment, IGUIElement* parent,
24 s32 id, core::rect<s32> rectangle)
25 : IGUIComboBox(environment, parent, id, rectangle),
26 ListButton(0), SelectedText(0), ListBox(0), LastFocus(0),
27 Selected(-1), HAlign(EGUIA_UPPERLEFT), VAlign(EGUIA_CENTER), MaxSelectionRows(5), HasFocus(false)
28 {
29 #ifdef _DEBUG
30 setDebugName("CGUIComboBox");
31 #endif
32
33 IGUISkin* skin = Environment->getSkin();
34
35 s32 width = 15;
36 if (skin)
37 width = skin->getSize(EGDS_WINDOW_BUTTON_WIDTH);
38
39 core::rect<s32> r;
40 r.UpperLeftCorner.X = rectangle.getWidth() - width - 2;
41 r.LowerRightCorner.X = rectangle.getWidth() - 2;
42
43 r.UpperLeftCorner.Y = 2;
44 r.LowerRightCorner.Y = rectangle.getHeight() - 2;
45
46 ListButton = Environment->addButton(r, this, -1, L"");
47 if (skin && skin->getSpriteBank())
48 {
49 ListButton->setSpriteBank(skin->getSpriteBank());
50 ListButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_DOWN), skin->getColor(EGDC_WINDOW_SYMBOL));
51 ListButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_DOWN), skin->getColor(EGDC_WINDOW_SYMBOL));
52 }
53 ListButton->setAlignment(EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
54 ListButton->setSubElement(true);
55 ListButton->setTabStop(false);
56
57 r.UpperLeftCorner.X = 2;
58 r.UpperLeftCorner.Y = 2;
59 r.LowerRightCorner.X = RelativeRect.getWidth() - (ListButton->getAbsolutePosition().getWidth() + 2);
60 r.LowerRightCorner.Y = RelativeRect.getHeight() - 2;
61
62 SelectedText = Environment->addStaticText(L"", r, false, false, this, -1, false);
63 SelectedText->setSubElement(true);
64 SelectedText->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT);
65 SelectedText->setTextAlignment(EGUIA_UPPERLEFT, EGUIA_CENTER);
66 if (skin)
67 SelectedText->setOverrideColor(skin->getColor(EGDC_BUTTON_TEXT));
68 SelectedText->enableOverrideColor(true);
69
70 // this element can be tabbed to
71 setTabStop(true);
72 setTabOrder(-1);
73 }
74
75
setTextAlignment(EGUI_ALIGNMENT horizontal,EGUI_ALIGNMENT vertical)76 void CGUIComboBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
77 {
78 HAlign = horizontal;
79 VAlign = vertical;
80 SelectedText->setTextAlignment(horizontal, vertical);
81 }
82
83
84 //! Set the maximal number of rows for the selection listbox
setMaxSelectionRows(u32 max)85 void CGUIComboBox::setMaxSelectionRows(u32 max)
86 {
87 MaxSelectionRows = max;
88
89 // force recalculation of open listbox
90 if (ListBox)
91 {
92 openCloseMenu();
93 openCloseMenu();
94 }
95 }
96
97 //! Get the maximimal number of rows for the selection listbox
getMaxSelectionRows() const98 u32 CGUIComboBox::getMaxSelectionRows() const
99 {
100 return MaxSelectionRows;
101 }
102
103
104 //! Returns amount of items in box
getItemCount() const105 u32 CGUIComboBox::getItemCount() const
106 {
107 return Items.size();
108 }
109
110
111 //! returns string of an item. the idx may be a value from 0 to itemCount-1
getItem(u32 idx) const112 const wchar_t* CGUIComboBox::getItem(u32 idx) const
113 {
114 if (idx >= Items.size())
115 return 0;
116
117 return Items[idx].Name.c_str();
118 }
119
120 //! returns string of an item. the idx may be a value from 0 to itemCount-1
getItemData(u32 idx) const121 u32 CGUIComboBox::getItemData(u32 idx) const
122 {
123 if (idx >= Items.size())
124 return 0;
125
126 return Items[idx].Data;
127 }
128
129 //! Returns index based on item data
getIndexForItemData(u32 data) const130 s32 CGUIComboBox::getIndexForItemData(u32 data ) const
131 {
132 for ( u32 i = 0; i < Items.size (); ++i )
133 {
134 if ( Items[i].Data == data )
135 return i;
136 }
137 return -1;
138 }
139
140
141 //! Removes an item from the combo box.
removeItem(u32 idx)142 void CGUIComboBox::removeItem(u32 idx)
143 {
144 if (idx >= Items.size())
145 return;
146
147 if (Selected == (s32)idx)
148 setSelected(-1);
149
150 Items.erase(idx);
151 }
152
153
154 //! Returns caption of this element.
getText() const155 const wchar_t* CGUIComboBox::getText() const
156 {
157 return getItem(Selected);
158 }
159
160
161 //! adds an item and returns the index of it
addItem(const wchar_t * text,u32 data)162 u32 CGUIComboBox::addItem(const wchar_t* text, u32 data)
163 {
164 Items.push_back( SComboData ( text, data ) );
165
166 if (Selected == -1)
167 setSelected(0);
168
169 return Items.size() - 1;
170 }
171
172
173 //! deletes all items in the combo box
clear()174 void CGUIComboBox::clear()
175 {
176 Items.clear();
177 setSelected(-1);
178 }
179
180
181 //! returns id of selected item. returns -1 if no item is selected.
getSelected() const182 s32 CGUIComboBox::getSelected() const
183 {
184 return Selected;
185 }
186
187
188 //! sets the selected item. Set this to -1 if no item should be selected
setSelected(s32 idx)189 void CGUIComboBox::setSelected(s32 idx)
190 {
191 if (idx < -1 || idx >= (s32)Items.size())
192 return;
193
194 Selected = idx;
195 if (Selected == -1)
196 SelectedText->setText(L"");
197 else
198 SelectedText->setText(Items[Selected].Name.c_str());
199 }
200
201
202 //! called if an event happened.
OnEvent(const SEvent & event)203 bool CGUIComboBox::OnEvent(const SEvent& event)
204 {
205 if (isEnabled())
206 {
207 switch(event.EventType)
208 {
209
210 case EET_KEY_INPUT_EVENT:
211 if (ListBox && event.KeyInput.PressedDown && event.KeyInput.Key == KEY_ESCAPE)
212 {
213 // hide list box
214 openCloseMenu();
215 return true;
216 }
217 else
218 if (event.KeyInput.Key == KEY_RETURN || event.KeyInput.Key == KEY_SPACE)
219 {
220 if (!event.KeyInput.PressedDown)
221 {
222 openCloseMenu();
223 }
224
225 ListButton->setPressed(ListBox == 0);
226
227 return true;
228 }
229 else
230 if (event.KeyInput.PressedDown)
231 {
232 s32 oldSelected = Selected;
233 bool absorb = true;
234 switch (event.KeyInput.Key)
235 {
236 case KEY_DOWN:
237 setSelected(Selected+1);
238 break;
239 case KEY_UP:
240 setSelected(Selected-1);
241 break;
242 case KEY_HOME:
243 case KEY_PRIOR:
244 setSelected(0);
245 break;
246 case KEY_END:
247 case KEY_NEXT:
248 setSelected((s32)Items.size()-1);
249 break;
250 default:
251 absorb = false;
252 }
253
254 if (Selected <0)
255 setSelected(0);
256
257 if (Selected >= (s32)Items.size())
258 setSelected((s32)Items.size() -1);
259
260 if (Selected != oldSelected)
261 {
262 sendSelectionChangedEvent();
263 return true;
264 }
265
266 if (absorb)
267 return true;
268 }
269 break;
270
271 case EET_GUI_EVENT:
272
273 switch(event.GUIEvent.EventType)
274 {
275 case EGET_ELEMENT_FOCUS_LOST:
276 if (ListBox &&
277 (Environment->hasFocus(ListBox) || ListBox->isMyChild(event.GUIEvent.Caller) ) &&
278 event.GUIEvent.Element != this &&
279 !isMyChild(event.GUIEvent.Element) &&
280 !ListBox->isMyChild(event.GUIEvent.Element))
281 {
282 openCloseMenu();
283 }
284 break;
285 case EGET_BUTTON_CLICKED:
286 if (event.GUIEvent.Caller == ListButton)
287 {
288 openCloseMenu();
289 return true;
290 }
291 break;
292 case EGET_LISTBOX_SELECTED_AGAIN:
293 case EGET_LISTBOX_CHANGED:
294 if (event.GUIEvent.Caller == ListBox)
295 {
296 setSelected(ListBox->getSelected());
297 if (Selected <0 || Selected >= (s32)Items.size())
298 setSelected(-1);
299 openCloseMenu();
300
301 sendSelectionChangedEvent();
302 }
303 return true;
304 default:
305 break;
306 }
307 break;
308 case EET_MOUSE_INPUT_EVENT:
309
310 switch(event.MouseInput.Event)
311 {
312 case EMIE_LMOUSE_PRESSED_DOWN:
313 {
314 core::position2d<s32> p(event.MouseInput.X, event.MouseInput.Y);
315
316 // send to list box
317 if (ListBox && ListBox->isPointInside(p) && ListBox->OnEvent(event))
318 return true;
319
320 return true;
321 }
322 case EMIE_LMOUSE_LEFT_UP:
323 {
324 core::position2d<s32> p(event.MouseInput.X, event.MouseInput.Y);
325
326 // send to list box
327 if (!(ListBox &&
328 ListBox->getAbsolutePosition().isPointInside(p) &&
329 ListBox->OnEvent(event)))
330 {
331 openCloseMenu();
332 }
333 return true;
334 }
335 case EMIE_MOUSE_WHEEL:
336 {
337 s32 oldSelected = Selected;
338 setSelected( Selected + ((event.MouseInput.Wheel < 0) ? 1 : -1));
339
340 if (Selected <0)
341 setSelected(0);
342
343 if (Selected >= (s32)Items.size())
344 setSelected((s32)Items.size() -1);
345
346 if (Selected != oldSelected)
347 {
348 sendSelectionChangedEvent();
349 return true;
350 }
351 }
352 default:
353 break;
354 }
355 break;
356 default:
357 break;
358 }
359 }
360
361 return IGUIElement::OnEvent(event);
362 }
363
364
sendSelectionChangedEvent()365 void CGUIComboBox::sendSelectionChangedEvent()
366 {
367 if (Parent)
368 {
369 SEvent event;
370
371 event.EventType = EET_GUI_EVENT;
372 event.GUIEvent.Caller = this;
373 event.GUIEvent.Element = 0;
374 event.GUIEvent.EventType = EGET_COMBO_BOX_CHANGED;
375 Parent->OnEvent(event);
376 }
377 }
378
379
380 //! draws the element and its children
draw()381 void CGUIComboBox::draw()
382 {
383 if (!IsVisible)
384 return;
385
386 IGUISkin* skin = Environment->getSkin();
387 IGUIElement *currentFocus = Environment->getFocus();
388 if (currentFocus != LastFocus)
389 {
390 HasFocus = currentFocus == this || isMyChild(currentFocus);
391 LastFocus = currentFocus;
392 }
393
394 // set colors each time as skin-colors can be changed
395 SelectedText->setBackgroundColor(skin->getColor(EGDC_HIGH_LIGHT));
396 if(isEnabled())
397 {
398 SelectedText->setDrawBackground(HasFocus);
399 SelectedText->setOverrideColor(skin->getColor(HasFocus ? EGDC_HIGH_LIGHT_TEXT : EGDC_BUTTON_TEXT));
400 }
401 else
402 {
403 SelectedText->setDrawBackground(false);
404 SelectedText->setOverrideColor(skin->getColor(EGDC_GRAY_TEXT));
405 }
406 ListButton->setSprite(EGBS_BUTTON_UP, skin->getIcon(EGDI_CURSOR_DOWN), skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL));
407 ListButton->setSprite(EGBS_BUTTON_DOWN, skin->getIcon(EGDI_CURSOR_DOWN), skin->getColor(isEnabled() ? EGDC_WINDOW_SYMBOL : EGDC_GRAY_WINDOW_SYMBOL));
408
409
410 core::rect<s32> frameRect(AbsoluteRect);
411
412 // draw the border
413
414 skin->draw3DSunkenPane(this, skin->getColor(EGDC_3D_HIGH_LIGHT),
415 true, true, frameRect, &AbsoluteClippingRect);
416
417 // draw children
418 IGUIElement::draw();
419 }
420
421
openCloseMenu()422 void CGUIComboBox::openCloseMenu()
423 {
424 if (ListBox)
425 {
426 // close list box
427 Environment->setFocus(this);
428 ListBox->remove();
429 ListBox = 0;
430 }
431 else
432 {
433 if (Parent)
434 Parent->bringToFront(this);
435
436 IGUISkin* skin = Environment->getSkin();
437 u32 h = Items.size();
438
439 if (h > getMaxSelectionRows())
440 h = getMaxSelectionRows();
441 if (h == 0)
442 h = 1;
443
444 IGUIFont* font = skin->getFont();
445 if (font)
446 h *= (font->getDimension(L"A").Height + 4);
447
448 // open list box
449 core::rect<s32> r(0, AbsoluteRect.getHeight(),
450 AbsoluteRect.getWidth(), AbsoluteRect.getHeight() + h);
451
452 ListBox = new CGUIListBox(Environment, this, -1, r, false, true, true);
453 ListBox->setSubElement(true);
454 ListBox->setNotClipped(true);
455 ListBox->drop();
456
457 // ensure that list box is always completely visible
458 if (ListBox->getAbsolutePosition().LowerRightCorner.Y > Environment->getRootGUIElement()->getAbsolutePosition().getHeight())
459 ListBox->setRelativePosition( core::rect<s32>(0, -ListBox->getAbsolutePosition().getHeight(), AbsoluteRect.getWidth(), 0) );
460
461 for (s32 i=0; i<(s32)Items.size(); ++i)
462 ListBox->addItem(Items[i].Name.c_str());
463
464 ListBox->setSelected(Selected);
465
466 // set focus
467 Environment->setFocus(ListBox);
468 }
469 }
470
471
472 //! Writes attributes of the element.
serializeAttributes(io::IAttributes * out,io::SAttributeReadWriteOptions * options=0) const473 void CGUIComboBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
474 {
475 IGUIComboBox::serializeAttributes(out,options);
476
477 out->addEnum ("HTextAlign", HAlign, GUIAlignmentNames);
478 out->addEnum ("VTextAlign", VAlign, GUIAlignmentNames);
479 out->addInt("MaxSelectionRows", (s32)MaxSelectionRows );
480
481 out->addInt ("Selected", Selected );
482 out->addInt ("ItemCount", Items.size());
483 for (u32 i=0; i < Items.size(); ++i)
484 {
485 core::stringc s = "Item";
486 s += i;
487 s += "Text";
488 out->addString(s.c_str(), Items[i].Name.c_str());
489 }
490 }
491
492
493 //! Reads attributes of the element
deserializeAttributes(io::IAttributes * in,io::SAttributeReadWriteOptions * options=0)494 void CGUIComboBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
495 {
496 IGUIComboBox::deserializeAttributes(in,options);
497
498 setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
499 (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
500 setMaxSelectionRows( (u32)(in->getAttributeAsInt("MaxSelectionRows")) );
501
502 // clear the list
503 clear();
504 // get item count
505 u32 c = in->getAttributeAsInt("ItemCount");
506 // add items
507 for (u32 i=0; i < c; ++i)
508 {
509 core::stringc s = "Item";
510 s += i;
511 s += "Text";
512 addItem(in->getAttributeAsStringW(s.c_str()).c_str(), 0);
513 }
514
515 setSelected(in->getAttributeAsInt("Selected"));
516 }
517
518 } // end namespace gui
519 } // end namespace irr
520
521
522 #endif // _IRR_COMPILE_WITH_GUI_
523
524