This Trac instance is not used for development anymore!

We migrated our development workflow to git and Gitea.
To test the future redirection, replace trac by ariadne in the page URL.

source: ps/trunk/source/gui/ObjectTypes/CList.cpp

Last change on this file was 28135, checked in by Stan, 6 months ago

Finish implementing property "textcolor_selected" for list GUI objects
Fixes #6920
Patch by: @Vantha
Differential Revision: https://code.wildfiregames.com/D5269

  • Property svn:eol-style set to native
File size: 12.2 KB
RevLine 
[28135]1/* Copyright (C) 2024 Wildfire Games.
[27965]2 * This file is part of 0 A.D.
[6830]3 *
[27965]4 * 0 A.D. is free software: you can redistribute it and/or modify
[6830]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 *
[27965]9 * 0 A.D. is distributed in the hope that it will be useful,
[6830]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
[27965]15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
[6830]16 */
17
[3802]18#include "precompiled.h"
[13521]19
[3802]20#include "CList.h"
[13521]21
[22931]22#include "gui/CGUI.h"
[22558]23#include "gui/CGUIScrollBarVertical.h"
[23028]24#include "gui/SettingTypes/CGUIColor.h"
25#include "gui/SettingTypes/CGUIList.h"
[13521]26#include "lib/external_libraries/libsdl.h"
[22863]27#include "lib/timer.h"
[3802]28
[23403]29const CStr CList::EventNameSelectionChange = "SelectionChange";
30const CStr CList::EventNameHoverChange = "HoverChange";
31const CStr CList::EventNameMouseLeftClickItem = "MouseLeftClickItem";
32const CStr CList::EventNameMouseLeftDoubleClickItem = "MouseLeftDoubleClickItem";
33
[22741]34CList::CList(CGUI& pGUI)
[23005]35 : IGUIObject(pGUI),
[23020]36 IGUITextOwner(*static_cast<IGUIObject*>(this)),
37 IGUIScrollBarOwner(*static_cast<IGUIObject*>(this)),
[23005]38 m_Modified(false),
39 m_PrevSelectedItem(-1),
40 m_LastItemClickTime(0),
[25392]41 m_BufferZone(this, "buffer_zone"),
42 m_Font(this, "font"),
43 m_ScrollBar(this, "scrollbar", false),
44 m_ScrollBarStyle(this, "scrollbar_style"),
45 m_ScrollBottom(this, "scroll_bottom", false),
46 m_SoundDisabled(this, "sound_disabled"),
47 m_SoundSelected(this, "sound_selected"),
48 m_Sprite(this, "sprite"),
[25587]49 m_SpriteOverlay(this, "sprite_overlay"),
[25392]50 // Add sprite_disabled! TODO
51 m_SpriteSelectArea(this, "sprite_selectarea"),
[25587]52 m_SpriteSelectAreaOverlay(this, "sprite_selectarea_overlay"),
[25392]53 m_TextColor(this, "textcolor"),
54 m_TextColorSelected(this, "textcolor_selected"),
55 m_Selected(this, "selected", -1), // Index selected. -1 is none.
56 m_AutoScroll(this, "auto_scroll", false),
57 m_Hovered(this, "hovered", -1),
58 // Each list item has both a name (in 'list') and an associated data string (in 'list_data')
59 m_List(this, "list"),
60 m_ListData(this, "list_data")
[3802]61{
62 // Add scroll-bar
[25693]63 auto bar = std::make_unique<CGUIScrollBarVertical>(pGUI);
[3802]64 bar->SetRightAligned(true);
[25693]65 AddScrollBar(std::move(bar));
[3802]66}
67
68CList::~CList()
69{
70}
71
72void CList::SetupText()
73{
[24306]74 SetupText(false);
75}
76
77void CList::SetupText(bool append)
78{
[12244]79 m_Modified = true;
[3802]80
[24306]81 if (!append)
82 // Delete all generated texts.
83 // TODO: try to be cleverer if we want to update items before the end.
84 m_GeneratedTexts.clear();
[3802]85
86 float width = GetListRect().GetWidth();
[24306]87
88 bool bottom = false;
[23005]89 if (m_ScrollBar && GetScrollBar(0).GetStyle())
[24306]90 {
91 if (m_ScrollBottom && GetScrollBar(0).GetPos() > GetScrollBar(0).GetMaxPos() - 1.5f)
92 bottom = true;
93
94 // remove scrollbar if applicable
[3802]95 width -= GetScrollBar(0).GetStyle()->m_Width;
[24306]96 }
[3802]97
98 // Generate texts
99 float buffered_y = 0.f;
[16931]100
[24317]101 if (append && !m_ItemsYPositions.empty())
[24312]102 buffered_y = m_ItemsYPositions.back();
[24306]103
[25392]104 m_ItemsYPositions.resize(m_List->m_Items.size() + 1);
[24312]105
[25392]106 for (size_t i = append ? m_List->m_Items.size() - 1 : 0; i < m_List->m_Items.size(); ++i)
[3802]107 {
[22679]108 CGUIText* text;
[3802]109
[25392]110 if (!m_List->m_Items[i].GetOriginalString().empty())
111 text = &AddText(m_List->m_Items[i], m_Font, width, m_BufferZone);
[19306]112 else
113 {
114 // Minimum height of a space character of the current font size
115 CGUIString align_string;
116 align_string.SetValue(L" ");
[23009]117 text = &AddText(align_string, m_Font, width, m_BufferZone);
[19306]118 }
[17276]119
[3802]120 m_ItemsYPositions[i] = buffered_y;
[25143]121 buffered_y += text->GetSize().Height;
[3802]122 }
123
[25392]124 m_ItemsYPositions[m_List->m_Items.size()] = buffered_y;
[3802]125
126 // Setup scrollbar
[23005]127 if (m_ScrollBar)
[3802]128 {
[16931]129 GetScrollBar(0).SetScrollRange(m_ItemsYPositions.back());
130 GetScrollBar(0).SetScrollSpace(GetListRect().GetHeight());
[7649]131
132 CRect rect = GetListRect();
[16931]133 GetScrollBar(0).SetX(rect.right);
134 GetScrollBar(0).SetY(rect.top);
135 GetScrollBar(0).SetZ(GetBufferedZ());
136 GetScrollBar(0).SetLength(rect.bottom - rect.top);
[24306]137
138 if (bottom)
139 GetScrollBar(0).SetPos(GetScrollBar(0).GetMaxPos());
[3802]140 }
141}
142
[23020]143void CList::ResetStates()
144{
145 IGUIObject::ResetStates();
146 IGUIScrollBarOwner::ResetStates();
147}
148
149void CList::UpdateCachedSize()
150{
151 IGUIObject::UpdateCachedSize();
152 IGUITextOwner::UpdateCachedSize();
153}
154
[16931]155void CList::HandleMessage(SGUIMessage& Message)
[3802]156{
[23020]157 IGUIObject::HandleMessage(Message);
[3802]158 IGUIScrollBarOwner::HandleMessage(Message);
159 //IGUITextOwner::HandleMessage(Message); <== placed it after the switch instead!
160
[12244]161 m_Modified = false;
[3802]162 switch (Message.type)
163 {
164 case GUIM_SETTINGS_UPDATED:
165 if (Message.value == "list")
166 SetupText();
167
168 // If selected is changed, call "SelectionChange"
169 if (Message.value == "selected")
170 {
171 // TODO: Check range
172
[23005]173 if (m_AutoScroll)
[20958]174 UpdateAutoScroll();
175
[23403]176 ScriptEvent(EventNameSelectionChange);
[3802]177 }
178
[7649]179 if (Message.value == "scrollbar")
[3802]180 SetupText();
181
182 // Update scrollbar
[7649]183 if (Message.value == "scrollbar_style")
[3802]184 {
[23005]185 GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle);
[3802]186 SetupText();
187 }
188
189 break;
190
191 case GUIM_MOUSE_PRESS_LEFT:
192 {
[23005]193 if (!m_Enabled)
[13521]194 {
[23005]195 PlaySound(m_SoundDisabled);
[7649]196 break;
[13521]197 }
[7649]198
[19588]199 int hovered = GetHoveredItem();
200 if (hovered == -1)
201 break;
[25392]202 m_Selected.Set(hovered, true);
[19588]203 UpdateAutoScroll();
[23005]204 PlaySound(m_SoundSelected);
[3802]205
[19588]206 if (timer_Time() - m_LastItemClickTime < SELECT_DBLCLICK_RATE && hovered == m_PrevSelectedItem)
[23403]207 this->SendMouseEvent(GUIM_MOUSE_DBLCLICK_LEFT_ITEM, EventNameMouseLeftDoubleClickItem);
[19841]208 else
[23403]209 this->SendMouseEvent(GUIM_MOUSE_PRESS_LEFT_ITEM, EventNameMouseLeftClickItem);
[19841]210
[19588]211 m_LastItemClickTime = timer_Time();
212 m_PrevSelectedItem = hovered;
213 break;
214 }
[13521]215
[19588]216 case GUIM_MOUSE_LEAVE:
217 {
[23005]218 if (m_Hovered == -1)
[19588]219 break;
[17274]220
[25392]221 m_Hovered.Set(-1, true);
[23403]222 ScriptEvent(EventNameHoverChange);
[14458]223 break;
224 }
[3802]225
[19588]226 case GUIM_MOUSE_OVER:
227 {
228 int hovered = GetHoveredItem();
[23005]229 if (hovered == m_Hovered)
[19588]230 break;
231
[25392]232 m_Hovered.Set(hovered, true);
[23403]233 ScriptEvent(EventNameHoverChange);
[19588]234 break;
235 }
236
[3802]237 case GUIM_LOAD:
[16931]238 {
[23005]239 GetScrollBar(0).SetScrollBarStyle(m_ScrollBarStyle);
[3802]240 break;
[16931]241 }
[3802]242
243 default:
244 break;
245 }
246
247 IGUITextOwner::HandleMessage(Message);
248}
249
[24215]250InReaction CList::ManuallyHandleKeys(const SDL_Event_* ev)
[3802]251{
[15909]252 InReaction result = IN_PASS;
[3802]253
[15909]254 if (ev->ev.type == SDL_KEYDOWN)
[3802]255 {
[15909]256 int szChar = ev->ev.key.keysym.sym;
[3802]257
[15909]258 switch (szChar)
259 {
260 case SDLK_HOME:
261 SelectFirstElement();
262 UpdateAutoScroll();
263 result = IN_HANDLED;
264 break;
[3802]265
[15909]266 case SDLK_END:
267 SelectLastElement();
268 UpdateAutoScroll();
269 result = IN_HANDLED;
270 break;
[3802]271
[15909]272 case SDLK_UP:
273 SelectPrevElement();
274 UpdateAutoScroll();
275 result = IN_HANDLED;
276 break;
[3802]277
[15909]278 case SDLK_DOWN:
279 SelectNextElement();
280 UpdateAutoScroll();
281 result = IN_HANDLED;
282 break;
[3802]283
[15909]284 case SDLK_PAGEUP:
285 GetScrollBar(0).ScrollMinusPlenty();
286 result = IN_HANDLED;
287 break;
[3802]288
[15909]289 case SDLK_PAGEDOWN:
290 GetScrollBar(0).ScrollPlusPlenty();
291 result = IN_HANDLED;
292 break;
293
294 default: // Do nothing
295 result = IN_PASS;
296 }
[3802]297 }
298
[15909]299 return result;
[3802]300}
301
[25591]302void CList::Draw(CCanvas2D& canvas)
[3802]303{
[25591]304 DrawList(canvas, m_Selected, m_Sprite, m_SpriteOverlay, m_SpriteSelectArea, m_SpriteSelectAreaOverlay, m_TextColor);
[3802]305}
306
[25591]307void CList::DrawList(CCanvas2D& canvas, const int& selected, const CGUISpriteInstance& sprite, const CGUISpriteInstance& spriteOverlay,
[25587]308 const CGUISpriteInstance& spriteSelectArea, const CGUISpriteInstance& spriteSelectAreaOverlay, const CGUIColor& textColor)
[3802]309{
[25524]310 CRect rect = GetListRect();
[3802]311
[25591]312 m_pGUI.DrawSprite(sprite, canvas, rect);
[3802]313
[25524]314 float scroll = 0.f;
315 if (m_ScrollBar)
316 scroll = GetScrollBar(0).GetPos();
[3802]317
[25587]318 bool drawSelected = false;
319 CRect rectSel;
[25524]320 if (selected >= 0 && selected+1 < (int)m_ItemsYPositions.size())
321 {
322 // Get rectangle of selection:
[25587]323 rectSel = CRect(
[25524]324 rect.left, rect.top + m_ItemsYPositions[selected] - scroll,
325 rect.right, rect.top + m_ItemsYPositions[selected+1] - scroll);
[3802]326
[25587]327 if (rectSel.top <= rect.bottom &&
328 rectSel.bottom >= rect.top)
[3802]329 {
[25587]330 if (rectSel.bottom > rect.bottom)
331 rectSel.bottom = rect.bottom;
332 if (rectSel.top < rect.top)
333 rectSel.top = rect.top;
[25524]334 if (m_ScrollBar)
[3802]335 {
[25524]336 // Remove any overlapping area of the scrollbar.
[25587]337 if (rectSel.right > GetScrollBar(0).GetOuterRect().left &&
338 rectSel.right <= GetScrollBar(0).GetOuterRect().right)
339 rectSel.right = GetScrollBar(0).GetOuterRect().left;
[3802]340
[25587]341 if (rectSel.left >= GetScrollBar(0).GetOuterRect().left &&
342 rectSel.left < GetScrollBar(0).GetOuterRect().right)
343 rectSel.left = GetScrollBar(0).GetOuterRect().right;
[25524]344 }
[3802]345
[25591]346 m_pGUI.DrawSprite(spriteSelectArea, canvas, rectSel);
[25587]347 drawSelected = true;
[3802]348 }
[25524]349 }
[3802]350
[25524]351 for (size_t i = 0; i < m_List->m_Items.size(); ++i)
352 {
353 if (m_ItemsYPositions[i+1] - scroll < 0 ||
354 m_ItemsYPositions[i] - scroll > rect.GetHeight())
355 continue;
[3802]356
[25524]357 // Clipping area (we'll have to substract the scrollbar)
358 CRect cliparea = GetListRect();
[3802]359
[25524]360 if (m_ScrollBar)
361 {
362 if (cliparea.right > GetScrollBar(0).GetOuterRect().left &&
363 cliparea.right <= GetScrollBar(0).GetOuterRect().right)
364 cliparea.right = GetScrollBar(0).GetOuterRect().left;
[3802]365
[25524]366 if (cliparea.left >= GetScrollBar(0).GetOuterRect().left &&
367 cliparea.left < GetScrollBar(0).GetOuterRect().right)
368 cliparea.left = GetScrollBar(0).GetOuterRect().right;
369 }
[3802]370
[28135]371 const CGUIColor& finalTextColor = (drawSelected && static_cast<size_t>(selected) == i && *m_TextColorSelected) ? m_TextColorSelected : textColor;
372
373 DrawText(canvas, i, finalTextColor, rect.TopLeft() - CVector2D(0.f, scroll - m_ItemsYPositions[i]), cliparea);
[3802]374 }
[25303]375
[25587]376 // Draw scrollbars on top of the content
[25303]377 if (m_ScrollBar)
[25591]378 IGUIScrollBarOwner::Draw(canvas);
[25587]379
380 // Draw the overlays last
[25591]381 m_pGUI.DrawSprite(spriteOverlay, canvas, rect);
[25587]382 if (drawSelected)
[25591]383 m_pGUI.DrawSprite(spriteSelectAreaOverlay, canvas, rectSel);
[3802]384}
385
[24306]386void CList::AddItem(const CGUIString& str, const CGUIString& data)
[3802]387{
[22693]388 // Do not send a settings-changed message
[25392]389 m_List.GetMutable().m_Items.push_back(str);
390 m_ListData.GetMutable().m_Items.push_back(data);
[22693]391
[24312]392 SetupText(true);
[3802]393}
394
[25225]395void CList::AddItem(const CGUIString& strAndData)
396{
397 AddItem(strAndData, strAndData);
398}
399
[25378]400bool CList::HandleAdditionalChildren(const XMBData& xmb, const XMBElement& child)
[3802]401{
[25378]402 int elmt_item = xmb.GetElementID("item");
[3802]403
[5017]404 if (child.GetNodeName() == elmt_item)
[3802]405 {
[24306]406 CGUIString vlist;
407 vlist.SetValue(child.GetText().FromUTF8());
408 AddItem(vlist, vlist);
[3802]409 return true;
410 }
[16931]411
412 return false;
[3802]413}
414
415void CList::SelectNextElement()
416{
[25392]417 if (m_Selected != static_cast<int>(m_List->m_Items.size()) - 1)
[3802]418 {
[25392]419 m_Selected.Set(m_Selected + 1, true);
[23005]420 PlaySound(m_SoundSelected);
[3802]421 }
422}
[16931]423
[3802]424void CList::SelectPrevElement()
425{
[23005]426 if (m_Selected > 0)
[3802]427 {
[25392]428 m_Selected.Set(m_Selected - 1, true);
[23005]429 PlaySound(m_SoundSelected);
[3802]430 }
431}
432
433void CList::SelectFirstElement()
434{
[23005]435 if (m_Selected >= 0)
[25392]436 m_Selected.Set(0, true);
[3802]437}
[16931]438
[3802]439void CList::SelectLastElement()
440{
[25392]441 const int index = static_cast<int>(m_List->m_Items.size()) - 1;
[3802]442
[23005]443 if (m_Selected != index)
[25392]444 m_Selected.Set(index, true);
[3802]445}
446
447void CList::UpdateAutoScroll()
448{
449 // No scrollbar, no scrolling (at least it's not made to work properly).
[23005]450 if (!m_ScrollBar || m_Selected < 0 || static_cast<std::size_t>(m_Selected) >= m_ItemsYPositions.size())
[3802]451 return;
452
[22693]453 float scroll = GetScrollBar(0).GetPos();
[3802]454
455 // Check upper boundary
[23005]456 if (m_ItemsYPositions[m_Selected] < scroll)
[3802]457 {
[23005]458 GetScrollBar(0).SetPos(m_ItemsYPositions[m_Selected]);
[3802]459 return; // this means, if it wants to align both up and down at the same time
460 // this will have precedence.
461 }
462
463 // Check lower boundary
[18935]464 CRect rect = GetListRect();
[23005]465 if (m_ItemsYPositions[m_Selected+1]-rect.GetHeight() > scroll)
466 GetScrollBar(0).SetPos(m_ItemsYPositions[m_Selected+1]-rect.GetHeight());
[3802]467}
[19588]468
469int CList::GetHoveredItem()
470{
[23005]471 const float scroll = m_ScrollBar ? GetScrollBar(0).GetPos() : 0.f;
[19588]472
[22641]473 const CRect& rect = GetListRect();
[25152]474 CVector2D mouse = m_pGUI.GetMousePos();
475 mouse.Y += scroll;
[19588]476
477 // Mouse is over scrollbar
[23005]478 if (m_ScrollBar && GetScrollBar(0).IsVisible() &&
[25152]479 mouse.X >= GetScrollBar(0).GetOuterRect().left &&
480 mouse.X <= GetScrollBar(0).GetOuterRect().right)
[19588]481 return -1;
482
[25392]483 for (size_t i = 0; i < m_List->m_Items.size(); ++i)
[25152]484 if (mouse.Y >= rect.top + m_ItemsYPositions[i] &&
485 mouse.Y < rect.top + m_ItemsYPositions[i + 1])
[19588]486 return i;
487
488 return -1;
489}
Note: See TracBrowser for help on using the repository browser.