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