LCOV - code coverage report
Current view: top level - source/gui/ObjectTypes - CList.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 5 256 2.0 %
Date: 2023-01-19 00:18:29 Functions: 2 22 9.1 %

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

Generated by: LCOV version 1.13