1 /* Copyright (C) 2017 Wildfire Games.
2 * This file is part of 0 A.D.
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.
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.
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/>.
18 #include "precompiled.h"
22 #include "CGUIScrollBarVertical.h"
24 #include "lib/external_libraries/libsdl.h"
25 #include "ps/CLogger.h"
26 #include "ps/Profile.h"
27 #include "soundmanager/ISoundManager.h"
31 : m_Modified(false), m_PrevSelectedItem(-1), m_LastItemClickTime(0)
33 // Add sprite_disabled! TODO
35 AddSetting(GUIST_float
, "buffer_zone");
36 AddSetting(GUIST_CStrW
, "font");
37 AddSetting(GUIST_bool
, "scrollbar");
38 AddSetting(GUIST_CStr
, "scrollbar_style");
39 AddSetting(GUIST_CStrW
, "sound_disabled");
40 AddSetting(GUIST_CStrW
, "sound_selected");
41 AddSetting(GUIST_CGUISpriteInstance
, "sprite");
42 AddSetting(GUIST_CGUISpriteInstance
, "sprite_selectarea");
43 AddSetting(GUIST_int
, "cell_id");
44 AddSetting(GUIST_EAlign
, "text_align");
45 AddSetting(GUIST_CColor
, "textcolor");
46 AddSetting(GUIST_CColor
, "textcolor_selected");
47 AddSetting(GUIST_int
, "selected"); // Index selected. -1 is none.
48 AddSetting(GUIST_int
, "hovered");
49 AddSetting(GUIST_CStrW
, "tooltip");
50 AddSetting(GUIST_CStr
, "tooltip_style");
52 // Each list item has both a name (in 'list') and an associated data string (in 'list_data')
53 AddSetting(GUIST_CGUIList
, "list");
54 AddSetting(GUIST_CGUIList
, "list_data"); // TODO: this should be a list of raw strings, not of CGUIStrings
56 GUI
<bool>::SetSetting(this, "scrollbar", false);
57 GUI
<int>::SetSetting(this, "selected", -1);
58 GUI
<int>::SetSetting(this, "hovered", -1);
61 CGUIScrollBarVertical
* bar
= new CGUIScrollBarVertical();
62 bar
->SetRightAligned(true);
70 void CList::SetupText()
77 GUI
<CGUIList
>::GetSettingPointer(this, "list", pList
);
79 //ENSURE(m_GeneratedTexts.size()>=1);
81 m_ItemsYPositions
.resize(pList
->m_Items
.size()+1);
83 // Delete all generated texts. Some could probably be saved,
84 // but this is easier, and this function will never be called
85 // continuously, or even often, so it'll probably be okay.
86 for (SGUIText
* const& t
: m_GeneratedTexts
)
88 m_GeneratedTexts
.clear();
91 if (GUI
<CStrW
>::GetSetting(this, "font", font
) != PSRETURN_OK
|| font
.empty())
92 // Use the default if none is specified
93 // TODO Gee: (2004-08-14) Don't define standard like this. Do it with the default style.
97 GUI
<bool>::GetSetting(this, "scrollbar", scrollbar
);
99 float width
= GetListRect().GetWidth();
100 // remove scrollbar if applicable
101 if (scrollbar
&& GetScrollBar(0).GetStyle())
102 width
-= GetScrollBar(0).GetStyle()->m_Width
;
104 float buffer_zone
= 0.f
;
105 GUI
<float>::GetSetting(this, "buffer_zone", buffer_zone
);
108 float buffered_y
= 0.f
;
110 for (size_t i
= 0; i
< pList
->m_Items
.size(); ++i
)
112 // Create a new SGUIText. Later on, input it using AddText()
113 SGUIText
* text
= new SGUIText();
115 if (!pList
->m_Items
[i
].GetOriginalString().empty())
116 *text
= GetGUI()->GenerateText(pList
->m_Items
[i
], font
, width
, buffer_zone
, this);
119 // Minimum height of a space character of the current font size
120 CGUIString align_string
;
121 align_string
.SetValue(L
" ");
122 *text
= GetGUI()->GenerateText(align_string
, font
, width
, buffer_zone
, this);
125 m_ItemsYPositions
[i
] = buffered_y
;
126 buffered_y
+= text
->m_Size
.cy
;
131 m_ItemsYPositions
[pList
->m_Items
.size()] = buffered_y
;
136 GetScrollBar(0).SetScrollRange(m_ItemsYPositions
.back());
137 GetScrollBar(0).SetScrollSpace(GetListRect().GetHeight());
139 CRect rect
= GetListRect();
140 GetScrollBar(0).SetX(rect
.right
);
141 GetScrollBar(0).SetY(rect
.top
);
142 GetScrollBar(0).SetZ(GetBufferedZ());
143 GetScrollBar(0).SetLength(rect
.bottom
- rect
.top
);
147 void CList::HandleMessage(SGUIMessage
& Message
)
149 IGUIScrollBarOwner::HandleMessage(Message
);
150 //IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead!
153 switch (Message
.type
)
155 case GUIM_SETTINGS_UPDATED
:
156 if (Message
.value
== "list")
159 // If selected is changed, call "SelectionChange"
160 if (Message
.value
== "selected")
166 // TODO only works if lower-case, shouldn't it be made case sensitive instead?
167 ScriptEvent("selectionchange");
170 if (Message
.value
== "scrollbar")
174 if (Message
.value
== "scrollbar_style")
176 CStr scrollbar_style
;
177 GUI
<CStr
>::GetSetting(this, Message
.value
, scrollbar_style
);
179 GetScrollBar(0).SetScrollBarStyle(scrollbar_style
);
186 case GUIM_MOUSE_PRESS_LEFT
:
189 GUI
<bool>::GetSetting(this, "enabled", enabled
);
193 if (g_SoundManager
&& GUI
<CStrW
>::GetSetting(this, "sound_disabled", soundPath
) == PSRETURN_OK
&& !soundPath
.empty())
194 g_SoundManager
->PlayAsUI(soundPath
.c_str(), false);
198 int hovered
= GetHoveredItem();
201 GUI
<int>::SetSetting(this, "selected", hovered
);
205 if (g_SoundManager
&& GUI
<CStrW
>::GetSetting(this, "sound_selected", soundPath
) == PSRETURN_OK
&& !soundPath
.empty())
206 g_SoundManager
->PlayAsUI(soundPath
.c_str(), false);
208 if (timer_Time() - m_LastItemClickTime
< SELECT_DBLCLICK_RATE
&& hovered
== m_PrevSelectedItem
)
209 this->SendEvent(GUIM_MOUSE_DBLCLICK_LEFT_ITEM
, "mouseleftdoubleclickitem");
211 this->SendEvent(GUIM_MOUSE_PRESS_LEFT_ITEM
, "mouseleftclickitem");
213 m_LastItemClickTime
= timer_Time();
214 m_PrevSelectedItem
= hovered
;
218 case GUIM_MOUSE_LEAVE
:
220 int hoveredSetting
= -1;
221 GUI
<int>::GetSetting(this, "hovered", hoveredSetting
);
222 if (hoveredSetting
== -1)
225 GUI
<int>::SetSetting(this, "hovered", -1);
226 ScriptEvent("hoverchange");
230 case GUIM_MOUSE_OVER
:
232 int hoveredSetting
= -1;
233 GUI
<int>::GetSetting(this, "hovered", hoveredSetting
);
235 int hovered
= GetHoveredItem();
236 if (hovered
== hoveredSetting
)
239 GUI
<int>::SetSetting(this, "hovered", hovered
);
240 ScriptEvent("hoverchange");
246 CStr scrollbar_style
;
247 GUI
<CStr
>::GetSetting(this, "scrollbar_style", scrollbar_style
);
248 GetScrollBar(0).SetScrollBarStyle(scrollbar_style
);
256 IGUITextOwner::HandleMessage(Message
);
259 InReaction
CList::ManuallyHandleEvent(const SDL_Event_
* ev
)
261 InReaction result
= IN_PASS
;
263 if (ev
->ev
.type
== SDL_KEYDOWN
)
265 int szChar
= ev
->ev
.key
.keysym
.sym
;
270 SelectFirstElement();
294 GetScrollBar(0).ScrollMinusPlenty();
299 GetScrollBar(0).ScrollPlusPlenty();
303 default: // Do nothing
314 GUI
<int>::GetSetting(this, "selected", selected
);
316 DrawList(selected
, "sprite", "sprite_selectarea", "textcolor");
319 void CList::DrawList(const int& selected
, const CStr
& _sprite
, const CStr
& _sprite_selected
, const CStr
& _textcolor
)
321 float bz
= GetBufferedZ();
323 // First call draw on ScrollBarOwner
325 GUI
<bool>::GetSetting(this, "scrollbar", scrollbar
);
328 IGUIScrollBarOwner::Draw();
332 CRect rect
= GetListRect();
334 CGUISpriteInstance
* sprite
= NULL
;
335 CGUISpriteInstance
* sprite_selectarea
= NULL
;
337 GUI
<CGUISpriteInstance
>::GetSettingPointer(this, _sprite
, sprite
);
338 GUI
<CGUISpriteInstance
>::GetSettingPointer(this, _sprite_selected
, sprite_selectarea
);
339 GUI
<int>::GetSetting(this, "cell_id", cell_id
);
342 GUI
<CGUIList
>::GetSettingPointer(this, "list", pList
);
344 GetGUI()->DrawSprite(*sprite
, cell_id
, bz
, rect
);
348 scroll
= GetScrollBar(0).GetPos();
350 if (selected
>= 0 && selected
+1 < (int)m_ItemsYPositions
.size())
352 // Get rectangle of selection:
353 CRect
rect_sel(rect
.left
, rect
.top
+ m_ItemsYPositions
[selected
] - scroll
,
354 rect
.right
, rect
.top
+ m_ItemsYPositions
[selected
+1] - scroll
);
356 if (rect_sel
.top
<= rect
.bottom
&&
357 rect_sel
.bottom
>= rect
.top
)
359 if (rect_sel
.bottom
> rect
.bottom
)
360 rect_sel
.bottom
= rect
.bottom
;
361 if (rect_sel
.top
< rect
.top
)
362 rect_sel
.top
= rect
.top
;
366 // Remove any overlapping area of the scrollbar.
367 if (rect_sel
.right
> GetScrollBar(0).GetOuterRect().left
&&
368 rect_sel
.right
<= GetScrollBar(0).GetOuterRect().right
)
369 rect_sel
.right
= GetScrollBar(0).GetOuterRect().left
;
371 if (rect_sel
.left
>= GetScrollBar(0).GetOuterRect().left
&&
372 rect_sel
.left
< GetScrollBar(0).GetOuterRect().right
)
373 rect_sel
.left
= GetScrollBar(0).GetOuterRect().right
;
376 GetGUI()->DrawSprite(*sprite_selectarea
, cell_id
, bz
+0.05f
, rect_sel
);
381 GUI
<CColor
>::GetSetting(this, _textcolor
, color
);
383 for (size_t i
= 0; i
< pList
->m_Items
.size(); ++i
)
385 if (m_ItemsYPositions
[i
+1] - scroll
< 0 ||
386 m_ItemsYPositions
[i
] - scroll
> rect
.GetHeight())
389 // Clipping area (we'll have to substract the scrollbar)
390 CRect cliparea
= GetListRect();
394 if (cliparea
.right
> GetScrollBar(0).GetOuterRect().left
&&
395 cliparea
.right
<= GetScrollBar(0).GetOuterRect().right
)
396 cliparea
.right
= GetScrollBar(0).GetOuterRect().left
;
398 if (cliparea
.left
>= GetScrollBar(0).GetOuterRect().left
&&
399 cliparea
.left
< GetScrollBar(0).GetOuterRect().right
)
400 cliparea
.left
= GetScrollBar(0).GetOuterRect().right
;
403 DrawText(i
, color
, rect
.TopLeft() - CPos(0.f
, scroll
- m_ItemsYPositions
[i
]), bz
+0.1f
, cliparea
);
408 void CList::AddItem(const CStrW
& str
, const CStrW
& data
)
412 GUI
<CGUIList
>::GetSettingPointer(this, "list", pList
);
413 GUI
<CGUIList
>::GetSettingPointer(this, "list_data", pListData
);
415 CGUIString gui_string
;
416 gui_string
.SetValue(str
);
417 pList
->m_Items
.push_back(gui_string
);
419 CGUIString data_string
;
420 data_string
.SetValue(data
);
421 pListData
->m_Items
.push_back(data_string
);
427 bool CList::HandleAdditionalChildren(const XMBElement
& child
, CXeromyces
* pFile
)
429 int elmt_item
= pFile
->GetElementID("item");
431 if (child
.GetNodeName() == elmt_item
)
433 AddItem(child
.GetText().FromUTF8(), child
.GetText().FromUTF8());
440 void CList::SelectNextElement()
443 GUI
<int>::GetSetting(this, "selected", selected
);
446 GUI
<CGUIList
>::GetSettingPointer(this, "list", pList
);
448 if (selected
!= (int)pList
->m_Items
.size()-1)
451 GUI
<int>::SetSetting(this, "selected", selected
);
454 if (g_SoundManager
&& GUI
<CStrW
>::GetSetting(this, "sound_selected", soundPath
) == PSRETURN_OK
&& !soundPath
.empty())
455 g_SoundManager
->PlayAsUI(soundPath
.c_str(), false);
459 void CList::SelectPrevElement()
462 GUI
<int>::GetSetting(this, "selected", selected
);
467 GUI
<int>::SetSetting(this, "selected", selected
);
470 if (g_SoundManager
&& GUI
<CStrW
>::GetSetting(this, "sound_selected", soundPath
) == PSRETURN_OK
&& !soundPath
.empty())
471 g_SoundManager
->PlayAsUI(soundPath
.c_str(), false);
475 void CList::SelectFirstElement()
478 GUI
<int>::GetSetting(this, "selected", selected
);
481 GUI
<int>::SetSetting(this, "selected", 0);
484 void CList::SelectLastElement()
487 GUI
<int>::GetSetting(this, "selected", selected
);
490 GUI
<CGUIList
>::GetSettingPointer(this, "list", pList
);
492 if (selected
!= (int)pList
->m_Items
.size()-1)
493 GUI
<int>::SetSetting(this, "selected", (int)pList
->m_Items
.size()-1);
496 void CList::UpdateAutoScroll()
501 GUI
<int>::GetSetting(this, "selected", selected
);
502 GUI
<bool>::GetSetting(this, "scrollbar", scrollbar
);
504 // No scrollbar, no scrolling (at least it's not made to work properly).
505 if (!scrollbar
|| selected
< 0 || (std::size_t) selected
>= m_ItemsYPositions
.size())
508 scroll
= GetScrollBar(0).GetPos();
510 // Check upper boundary
511 if (m_ItemsYPositions
[selected
] < scroll
)
513 GetScrollBar(0).SetPos(m_ItemsYPositions
[selected
]);
514 return; // this means, if it wants to align both up and down at the same time
515 // this will have precedence.
518 // Check lower boundary
519 CRect rect
= GetListRect();
520 if (m_ItemsYPositions
[selected
+1]-rect
.GetHeight() > scroll
)
521 GetScrollBar(0).SetPos(m_ItemsYPositions
[selected
+1]-rect
.GetHeight());
524 int CList::GetHoveredItem()
528 GUI
<bool>::GetSetting(this, "scrollbar", scrollbar
);
529 GUI
<CGUIList
>::GetSettingPointer(this, "list", pList
);
532 scroll
= GetScrollBar(0).GetPos();
534 CRect rect
= GetListRect();
535 CPos mouse
= GetMousePos();
538 // Mouse is over scrollbar
539 if (scrollbar
&& GetScrollBar(0).IsVisible() &&
540 mouse
.x
>= GetScrollBar(0).GetOuterRect().left
&&
541 mouse
.x
<= GetScrollBar(0).GetOuterRect().right
)
544 for (size_t i
= 0; i
< pList
->m_Items
.size(); ++i
)
545 if (mouse
.y
>= rect
.top
+ m_ItemsYPositions
[i
] &&
546 mouse
.y
< rect
.top
+ m_ItemsYPositions
[i
+ 1])