Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
CDropDown.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /*
19 CDropDown
20 */
21 
22 #include "precompiled.h"
23 
24 #include "CDropDown.h"
25 
27 #include "lib/ogl.h"
28 #include "lib/timer.h"
30 
31 
32 //-------------------------------------------------------------------
33 // Constructor / Destructor
34 //-------------------------------------------------------------------
35 CDropDown::CDropDown() : m_Open(false), m_HideScrollBar(false), m_ElementHighlight(-1)
36 {
37  AddSetting(GUIST_float, "button_width");
38  AddSetting(GUIST_float, "dropdown_size");
39  AddSetting(GUIST_float, "dropdown_buffer");
40 // AddSetting(GUIST_CStrW, "font");
41  AddSetting(GUIST_CStrW, "sound_closed");
42  AddSetting(GUIST_CStrW, "sound_disabled");
43  AddSetting(GUIST_CStrW, "sound_enter");
44  AddSetting(GUIST_CStrW, "sound_leave");
45  AddSetting(GUIST_CStrW, "sound_opened");
46 // AddSetting(GUIST_CGUISpriteInstance, "sprite"); // Background that sits around the size
47  AddSetting(GUIST_CGUISpriteInstance, "sprite_list"); // Background of the drop down list
48  AddSetting(GUIST_CGUISpriteInstance, "sprite2"); // Button that sits to the right
49  AddSetting(GUIST_CGUISpriteInstance, "sprite2_over");
50  AddSetting(GUIST_CGUISpriteInstance, "sprite2_pressed");
51  AddSetting(GUIST_CGUISpriteInstance, "sprite2_disabled");
52  AddSetting(GUIST_EVAlign, "text_valign");
53 
54  // Add these in CList! And implement TODO
55  //AddSetting(GUIST_CColor, "textcolor_over");
56  //AddSetting(GUIST_CColor, "textcolor_pressed");
57  //AddSetting(GUIST_CColor, "textcolor_disabled");
58 
59  // Scrollbar is forced to be true.
60  GUI<bool>::SetSetting(this, "scrollbar", true);
61 }
62 
64 {
65 }
66 
68 {
69  SetupListRect();
70 
72 }
73 
75 {
76  // Important
77 
78  switch (Message.type)
79  {
81  {
82  // Update cached list rect
83  if (Message.value == "size" ||
84  Message.value == "absolute" ||
85  Message.value == "dropdown_size" ||
86  Message.value == "dropdown_buffer" ||
87  Message.value == "scrollbar_style" ||
88  Message.value == "button_width")
89  {
90  SetupListRect();
91  }
92 
93  break;
94  }
95 
96  case GUIM_MOUSE_MOTION:
97  {
98  if (m_Open)
99  {
100  CPos mouse = GetMousePos();
101 
102  if (GetListRect().PointInside(mouse))
103  {
104  bool scrollbar;
105  CGUIList *pList;
106  GUI<bool>::GetSetting(this, "scrollbar", scrollbar);
107  GUI<CGUIList>::GetSettingPointer(this, "list", pList);
108  float scroll=0.f;
109  if (scrollbar)
110  {
111  scroll = GetScrollBar(0).GetPos();
112  }
113 
114  CRect rect = GetListRect();
115  mouse.y += scroll;
116  int set=-1;
117  for (int i=0; i<(int)pList->m_Items.size(); ++i)
118  {
119  if (mouse.y >= rect.top + m_ItemsYPositions[i] &&
120  mouse.y < rect.top + m_ItemsYPositions[i+1] &&
121  // mouse is not over scroll-bar
122  !(mouse.x >= GetScrollBar(0).GetOuterRect().left &&
123  mouse.x <= GetScrollBar(0).GetOuterRect().right))
124  {
125  set = i;
126  }
127  }
128 
129  if (set != -1)
130  {
131  //GUI<int>::SetSetting(this, "selected", set);
132  m_ElementHighlight = set;
133  //UpdateAutoScroll();
134  }
135  }
136  }
137 
138  break;
139  }
140 
141  case GUIM_MOUSE_ENTER:
142  {
143  bool enabled;
144  GUI<bool>::GetSetting(this, "enabled", enabled);
145  if (enabled)
146  {
147  CStrW soundPath;
148  if (g_SoundManager && GUI<CStrW>::GetSetting(this, "sound_enter", soundPath) == PSRETURN_OK && !soundPath.empty())
149  g_SoundManager->PlayAsUI(soundPath.c_str(), false);
150  }
151  break;
152  }
153 
154  case GUIM_MOUSE_LEAVE:
155  {
156  GUI<int>::GetSetting(this, "selected", m_ElementHighlight);
157 
158  bool enabled;
159  GUI<bool>::GetSetting(this, "enabled", enabled);
160  if (enabled)
161  {
162  CStrW soundPath;
163  if (g_SoundManager && GUI<CStrW>::GetSetting(this, "sound_leave", soundPath) == PSRETURN_OK && !soundPath.empty())
164  g_SoundManager->PlayAsUI(soundPath.c_str(), false);
165  }
166  break;
167  }
168 
169  // We can't inherent this routine from CList, because we need to include
170  // a mouse click to open the dropdown, also the coordinates are changed.
172  {
173  bool enabled;
174  GUI<bool>::GetSetting(this, "enabled", enabled);
175  if (!enabled)
176  {
177  CStrW soundPath;
178  if (g_SoundManager && GUI<CStrW>::GetSetting(this, "sound_disabled", soundPath) == PSRETURN_OK && !soundPath.empty())
179  g_SoundManager->PlayAsUI(soundPath.c_str(), false);
180  break;
181  }
182 
183  if (!m_Open)
184  {
185  CGUIList *pList;
186  GUI<CGUIList>::GetSettingPointer(this, "list", pList);
187  if (pList->m_Items.empty())
188  return;
189 
190  m_Open = true;
192  GUI<int>::GetSetting(this, "selected", m_ElementHighlight);
193 
194  // Start at the position of the selected item, if possible.
196 
197  CStrW soundPath;
198  if (g_SoundManager && GUI<CStrW>::GetSetting(this, "sound_opened", soundPath) == PSRETURN_OK && !soundPath.empty())
199  g_SoundManager->PlayAsUI(soundPath.c_str(), false);
200 
201  return; // overshadow
202  }
203  else
204  {
205  CPos mouse = GetMousePos();
206 
207  // If the regular area is pressed, then abort, and close.
208  if (m_CachedActualSize.PointInside(mouse))
209  {
210  m_Open = false;
212 
213  CStrW soundPath;
214  if (g_SoundManager && GUI<CStrW>::GetSetting(this, "sound_closed", soundPath) == PSRETURN_OK && !soundPath.empty())
215  g_SoundManager->PlayAsUI(soundPath.c_str(), false);
216 
217  return; // overshadow
218  }
219 
220  if (!(mouse.x >= GetScrollBar(0).GetOuterRect().left &&
221  mouse.x <= GetScrollBar(0).GetOuterRect().right) &&
222  mouse.y >= GetListRect().top)
223  {
224  m_Open = false;
226  }
227  }
228  break;
229  }
230 
231  case GUIM_LOST_FOCUS:
232  {
233  if (m_Open)
234  {
235  CStrW soundPath;
236  if (g_SoundManager && GUI<CStrW>::GetSetting(this, "sound_closed", soundPath) == PSRETURN_OK && !soundPath.empty())
237  g_SoundManager->PlayAsUI(soundPath.c_str(), false);
238  }
239  m_Open = false;
240  break;
241  }
242 
243  case GUIM_LOAD:
244  SetupListRect();
245  break;
246 
247  default:
248  break;
249  }
250 
251  // Important that this is after, so that overshadowed implementations aren't processed
252  CList::HandleMessage(Message);
253 
254  // As HandleMessage functions return void, we need to manually verify
255  // whether the child list's items were modified.
256  if (CList::GetModified())
257  SetupText();
258 }
259 
261 {
262  int szChar = ev->ev.key.keysym.sym;
263  bool update_highlight = false;
264 
265  switch (szChar)
266  {
267  case '\r':
268  m_Open=false;
269  break;
270 
271  case SDLK_HOME:
272  case SDLK_END:
273  case SDLK_UP:
274  case SDLK_DOWN:
275  case SDLK_PAGEUP:
276  case SDLK_PAGEDOWN:
277  if (!m_Open)
278  return IN_PASS;
279  // Set current selected item to highlighted, before
280  // then really processing these in CList::ManuallyHandleEvent()
281  GUI<int>::SetSetting(this, "selected", m_ElementHighlight);
282  update_highlight = true;
283  break;
284 
285  default:
286  // If we have imputed a character try to get the closest element to it.
287  // TODO: not too nice and doesn't deal with dashes.
288  if (m_Open && ((szChar >= SDLK_a && szChar <= SDLK_z) || szChar == SDLK_SPACE
289  || (szChar >= SDLK_0 && szChar <= SDLK_9)
290 #if !SDL_VERSION_ATLEAST(2,0,0)
291  || (szChar >= SDLK_KP0 && szChar <= SDLK_KP9)))
292 #else // SDL2
293  || (szChar >= SDLK_KP_0 && szChar <= SDLK_KP_9)))
294 #endif
295  {
296  // arbitrary 1 second limit to add to string or start fresh.
297  // maximal amount of characters is 100, which imo is far more than enough.
298  if (timer_Time() - m_TimeOfLastInput > 1.0 || m_InputBuffer.length() >= 100)
299  m_InputBuffer = szChar;
300  else
301  m_InputBuffer += szChar;
302 
304 
305  CGUIList *pList;
306  GUI<CGUIList>::GetSettingPointer(this, "list", pList);
307  // let's look for the closest element
308  // basically it's alphabetic order and "as many letters as we can get".
309  int closest = -1;
310  int bestIndex = -1;
311  int difference = 1250;
312  for (int i=0; i<(int)pList->m_Items.size(); ++i)
313  {
314  int indexOfDifference = 0;
315  int diff = 0;
316  for (size_t j=0; j < m_InputBuffer.length(); ++j)
317  {
318  diff = abs(pList->m_Items[i].GetOriginalString().LowerCase()[j] - (int)m_InputBuffer[j]);
319  if (diff == 0)
320  indexOfDifference = j+1;
321  else
322  break;
323  }
324  if (indexOfDifference > bestIndex || (indexOfDifference >= bestIndex && diff < difference))
325  {
326  bestIndex = indexOfDifference;
327  closest = i;
328  difference = diff;
329  }
330  }
331  // let's select the closest element. There should basically always be one.
332  if (closest != -1)
333  {
334  GUI<int>::SetSetting(this, "selected", closest);
335  update_highlight = true;
336  GetScrollBar(0).SetPos(m_ItemsYPositions[closest] - 60);
337  }
338  }
339  break;
340  }
341 
343 
344  if (update_highlight)
345  GUI<int>::GetSetting(this, "selected", m_ElementHighlight);
346 
347  return IN_HANDLED;
348 }
349 
351 {
352  float size, buffer, button_width;
353  GUI<float>::GetSetting(this, "dropdown_size", size);
354  GUI<float>::GetSetting(this, "dropdown_buffer", buffer);
355  GUI<float>::GetSetting(this, "button_width", button_width);
356 
357  if (m_ItemsYPositions.empty() || m_ItemsYPositions.back() >= size)
358  {
361 
362  m_HideScrollBar = false;
363  }
364  else
365  {
367  m_CachedActualSize.right - GetScrollBar(0).GetStyle()->m_Width, m_CachedActualSize.bottom+buffer + m_ItemsYPositions.back());
368 
369  // We also need to hide the scrollbar
370  m_HideScrollBar = true;
371  }
372 }
373 
375 {
376  return m_CachedListRect;
377 }
378 
380 {
381  if(!GetGUI())
383 
384  if (m_Open)
385  {
388 
389 
390  return rect.PointInside(GetMousePos());
391  }
392  else
394 }
395 
397 {
398  if (!GetGUI())
399  return;
400 
401  float bz = GetBufferedZ();
402 
403  float dropdown_size, button_width;
404  GUI<float>::GetSetting(this, "dropdown_size", dropdown_size);
405  GUI<float>::GetSetting(this, "button_width", button_width);
406 
407  CGUISpriteInstance *sprite, *sprite2, *sprite2_second;
408  int cell_id, selected=0;
409  CColor color;
410 
411  GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite", sprite);
412  GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite2", sprite2);
413  GUI<int>::GetSetting(this, "cell_id", cell_id);
414  GUI<int>::GetSetting(this, "selected", selected);
415  GUI<CColor>::GetSetting(this, "textcolor", color);
416 
417 
418  bool enabled;
419  GUI<bool>::GetSetting(this, "enabled", enabled);
420 
421  GetGUI()->DrawSprite(*sprite, cell_id, bz, m_CachedActualSize);
422 
423  if (button_width > 0.f)
424  {
427 
428  if (!enabled)
429  {
430  GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite2_disabled", sprite2_second);
431  GetGUI()->DrawSprite(GUI<>::FallBackSprite(*sprite2_second, *sprite2), cell_id, bz+0.05f, rect);
432  }
433  else
434  if (m_Open)
435  {
436  GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite2_pressed", sprite2_second);
437  GetGUI()->DrawSprite(GUI<>::FallBackSprite(*sprite2_second, *sprite2), cell_id, bz+0.05f, rect);
438  }
439  else
440  if (m_MouseHovering)
441  {
442  GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite2_over", sprite2_second);
443  GetGUI()->DrawSprite(GUI<>::FallBackSprite(*sprite2_second, *sprite2), cell_id, bz+0.05f, rect);
444  }
445  else
446  GetGUI()->DrawSprite(*sprite2, cell_id, bz+0.05f, rect);
447  }
448 
449  if (selected != -1) // TODO: Maybe check validity completely?
450  {
451  // figure out clipping rectangle
454 
456  DrawText(selected, color, pos, bz+0.1f, cliparea);
457  }
458 
459  bool *scrollbar=NULL, old;
460  GUI<bool>::GetSettingPointer(this, "scrollbar", scrollbar);
461 
462  old = *scrollbar;
463 
464  if (m_Open)
465  {
466  if (m_HideScrollBar)
467  *scrollbar = false;
468 
469  DrawList(m_ElementHighlight, "sprite_list", "sprite_selectarea", "textcolor");
470 
471  if (m_HideScrollBar)
472  *scrollbar = old;
473  }
474 }
475 
476 // When a dropdown list is opened, it needs to be visible above all the other
477 // controls on the page. The only way I can think of to do this is to increase
478 // its z value when opened, so that it's probably on top.
480 {
481  float bz = CList::GetBufferedZ();
482  if (m_Open)
483  return std::min(bz + 500.f, 1000.f); // TODO - don't use magic number for max z value
484  else
485  return bz;
486 }
virtual void HandleMessage(SGUIMessage &Message)
Definition: CDropDown.cpp:74
void DrawSprite(const CGUISpriteInstance &Sprite, int CellID, const float &Z, const CRect &Rect, const CRect &Clipping=CRect())
Draw GUI Sprite.
Definition: CGUI.cpp:467
CStr value
Optional data.
Definition: GUIbase.h:127
virtual InReaction ManuallyHandleEvent(const SDL_Event_ *ev)
Handle events manually to catch keyboard inputting.
Definition: CList.cpp:278
SDL_KeyboardEvent key
Definition: wsdl.h:305
void DrawList(const int &selected, const CStr &_sprite, const CStr &_sprite_selected, const CStr &_textcolor)
Definition: CList.cpp:327
const PSRETURN PSRETURN_OK
Definition: Errors.h:103
float top
Definition: Overlay.h:159
void SetZ(float z)
Set Z Position.
ISoundManager * g_SoundManager
Definition: Overlay.h:34
virtual IGUIScrollBar & GetScrollBar(const int &index)
Get Scroll Bar reference (it should be transparent it&#39;s actually pointers).
virtual CRect GetListRect() const
Definition: CDropDown.cpp:374
float left
Returning CPos representing each corner.
Definition: Overlay.h:159
virtual void SetupListRect()
Definition: CDropDown.cpp:350
static PSRETURN GetSetting(const IGUIObject *pObject, const CStr &Setting, T &Value)
Retrieves a setting by name from object pointer.
Definition: GUIutil.cpp:344
virtual void PlayAsUI(const VfsPath &itemPath, bool looping)=0
#define SDL_VERSION_ATLEAST(X, Y, Z)
Definition: wsdl.h:341
bool m_HideScrollBar
Definition: CDropDown.h:123
void SetupText()
Sets up text, should be called every time changes has been made that can change the visual...
Definition: CList.cpp:77
bool m_Open
Definition: CDropDown.h:116
virtual void HandleMessage(SGUIMessage &Message)
Definition: CList.cpp:155
virtual float GetBufferedZ() const
Returns not the Z value, but the actual buffered Z value, i.e.
Definition: IGUIObject.cpp:406
void SetupText()
Sets up text, should be called every time changes has been made that can change the visual...
Definition: CDropDown.cpp:67
virtual bool GetModified() const
Definition: CList.h:133
SDL_Event ev
Definition: libsdl.h:56
Includes static functions that needs one template argument.
Definition: GUIutil.h:103
virtual bool MouseOver()
Checks if mouse is hovering this object.
Definition: CDropDown.cpp:379
InReaction
Definition: input.h:34
Definition: input.h:40
std::vector< float > m_ItemsYPositions
List of each element&#39;s relative y position.
Definition: CList.h:144
virtual void Draw()
Draws the Button.
Definition: CDropDown.cpp:396
std::string m_InputBuffer
Definition: CDropDown.h:133
std::vector< CGUIString > m_Items
List of items (as text), the post-processed result is stored in the IGUITextOwner structure of this c...
Definition: CGUIList.h:33
void AddSetting(const EGUISettingType &Type, const CStr &Name)
Add a setting to m_Settings.
Definition: IGUIObject.cpp:172
virtual ~CDropDown()
Definition: CDropDown.cpp:63
double timer_Time()
Definition: timer.cpp:98
CRect m_CachedActualSize
Cached size, real size m_Size is actually dependent on resolution and can have different real outcome...
Definition: IGUIObject.h:434
Made to represent screen positions and delta values.
Definition: Overlay.h:167
int m_ElementHighlight
Definition: CDropDown.h:129
float GetPos() const
Get scroll-position.
float right
Definition: Overlay.h:159
virtual float GetBufferedZ() const
Returns not the Z value, but the actual buffered Z value, i.e.
Definition: CDropDown.cpp:479
CPos GetMousePos() const
Get Mouse from CGUI.
Definition: IGUIObject.cpp:207
virtual void DrawText(int index, const CColor &color, const CPos &pos, float z, const CRect &clipping=CRect())
Draws the Text.
CGUI * GetGUI()
Definition: IGUIObject.h:388
static PSRETURN SetSetting(IGUIObject *pObject, const CStr &Setting, const T &Value, const bool &SkipMessage=false)
Sets a value by name using a real datatype as input.
Definition: GUIutil.cpp:366
float y
Definition: Overlay.h:195
virtual void SetPos(float f)
Set scroll-position by hand.
SDL_keysym keysym
Definition: wsdl.h:196
virtual InReaction ManuallyHandleEvent(const SDL_Event_ *ev)
Handle events manually to catch keyboard inputting.
Definition: CDropDown.cpp:260
bool m_MouseHovering
This is an array of true or false, each element is associated with a string representing a setting...
Definition: IGUIObject.h:539
CRect m_CachedListRect
Definition: CDropDown.h:120
Message send to IGUIObject::HandleMessage() in order to give life to Objects manually with a derived ...
Definition: GUIbase.h:106
EGUIMessageType type
Describes what the message regards.
Definition: GUIbase.h:122
float bottom
Definition: Overlay.h:159
SDLKey sym
Definition: wsdl.h:188
float x
Position.
Definition: Overlay.h:195
bool PointInside(const CPos &point) const
Evalutates if point is within the rectangle.
Definition: Overlay.cpp:272
double m_TimeOfLastInput
Definition: CDropDown.h:136
static PSRETURN GetSettingPointer(const IGUIObject *pObject, const CStr &Setting, T *&Value)
Definition: GUIutil.cpp:317
Rectangle class used for screen rectangles.
Definition: Overlay.h:71