Ticket #2405: colist_handler_lobby_sortable_game_and_player_list_with_arrows_v3.patch
File colist_handler_lobby_sortable_game_and_player_list_with_arrows_v3.patch, 12.9 KB (added by , 9 years ago) |
---|
-
binaries/data/mods/mod/gui/common/modern/sprites.xml
585 585 size="0 6 16 22" 586 586 /> 587 587 </sprite> 588 <sprite name = "ModernDropUpArrow"> 589 <image texture = "global/modern/dropup-arrow.png" 590 real_texture_placement = "0 0 16 16" 591 texture_size="0 0 16 16" 592 size="0 6 16 22" 593 /> 594 </sprite> 588 595 <sprite name = "ModernDropDownArrowHighlight"> 589 596 <image texture = "global/modern/dropdown-arrow.png" 590 597 real_texture_placement = "0 0 16 16" -
binaries/data/mods/public/gui/lobby/lobby.js
1 1 var g_ChatMessages = []; 2 2 var g_Name = "unknown"; 3 3 var g_GameList = {}; 4 var g_GameListSortBy = "name"; 5 var g_PlayerListSortBy = "name"; 6 var g_GameListOrder = 1; // 1 for ascending sort, and -1 for descending 7 var g_PlayerListOrder = 1; 4 8 var g_specialKey = Math.random(); 5 9 // This object looks like {"name":[numMessagesSinceReset, lastReset, timeBlocked]} when in use. 6 10 var g_spamMonitor = {}; … … 74 78 // Update functions 75 79 //////////////////////////////////////////////////////////////////////////////////////////////// 76 80 81 function updateGameListOrderSelection() 82 { 83 g_GameListSortBy = Engine.GetGUIObjectByName("gamesBox").selected_column; 84 g_GameListOrder = Engine.GetGUIObjectByName("gamesBox").selected_column_order; 85 86 applyFilters(); 87 } 88 89 function updatePlayerListOrderSelection() 90 { 91 g_PlayerListSortBy = Engine.GetGUIObjectByName("playersBox").selected_column; 92 g_PlayerListOrder = Engine.GetGUIObjectByName("playersBox").selected_column_order; 93 94 updatePlayerList(); 95 } 96 77 97 function resetFilters() 78 98 { 79 99 // Reset states of gui objects … … 82 102 Engine.GetGUIObjectByName("mapTypeFilter").selected = 0; 83 103 Engine.GetGUIObjectByName("showFullFilter").checked = false; 84 104 85 // Update the list of games 86 updateGameList(); 87 88 // Update info box about the game currently selected 89 updateGameSelection(); 105 applyFilters(); 90 106 } 91 107 92 108 function applyFilters() … … 149 165 } 150 166 151 167 /** 152 * Do a full update of the player listing, including ratings from C++.168 * Do a full update of the player listing, including ratings from cached C++ information. 153 169 * 154 170 * @return Array containing the player, presence, nickname, and rating listings. 155 171 */ … … 164 180 // Sort the player list, ignoring case. 165 181 cleanPlayerList.sort(function(a,b) 166 182 { 167 var aName = a.name.toLowerCase(); 168 var bName = b.name.toLowerCase(); 169 return ((aName > bName) ? 1 : (bName > aName) ? -1 : 0); 170 } ); 183 switch (g_PlayerListSortBy) 184 { 185 case 'rating': 186 if (a.rating < b.rating) 187 return -g_PlayerListOrder; 188 else if (a.rating > b.rating) 189 return g_PlayerListOrder; 190 return 0; 191 case 'status': 192 let order = ["available", "away", "playing", "gone", "offline"]; 193 let presenceA = order.indexOf(a.presence); 194 let presenceB = order.indexOf(b.presence); 195 if (presenceA < presenceB) 196 return -g_PlayerListOrder; 197 else if (presenceA > presenceB) 198 return g_PlayerListOrder; 199 return 0; 200 default: 201 var aName = a.name.toLowerCase(); 202 var bName = b.name.toLowerCase(); 203 if (aName < bName) 204 return -g_PlayerListOrder; 205 else if (aName > bName) 206 return g_PlayerListOrder; 207 return 0; 208 } 209 }); 171 210 for (var i = 0; i < cleanPlayerList.length; i++) 172 211 { 173 212 // Identify current user's rating. … … 365 404 // Sort the list of games to that games 'waiting' are displayed at the top, followed by 'init', followed by 'running'. 366 405 var gameStatuses = ['waiting', 'init', 'running']; 367 406 g_GameList.sort(function (a,b) { 368 if (gameStatuses.indexOf(a.state) < gameStatuses.indexOf(b.state)) 369 return -1; 370 else if (gameStatuses.indexOf(a.state) > gameStatuses.indexOf(b.state)) 371 return 1; 407 switch (g_GameListSortBy) 408 { 409 case 'name': 410 case 'mapSize': 411 // mapSize contains the number of tiles for random maps 412 // scenario maps always display default size 413 case 'mapType': 414 if (a[g_GameListSortBy] < b[g_GameListSortBy]) 415 return -g_GameListOrder; 416 else if (a[g_GameListSortBy] > b[g_GameListSortBy]) 417 return g_GameListOrder; 418 return 0; 419 case 'mapName': 420 if (translate(a.niceMapName) < translate(b.niceMapName)) 421 return -g_GameListOrder; 422 else if (translate(a.niceMapName) > translate(b.niceMapName)) 423 return g_GameListOrder; 424 return 0; 425 case 'nPlayers': 426 // Numerical comparison of player count ratio. 427 if (a.nbp * b.tnbp < b.nbp * a.tnbp) // ratio a = a.nbp / a.tnbp, ratio b = b.nbp / b.tnbp 428 return -g_GameListOrder; 429 else if (a.nbp * b.tnbp > b.nbp * a.tnbp) 430 return g_GameListOrder; 431 return 0; 432 default: 433 if (gameStatuses.indexOf(a.state) < gameStatuses.indexOf(b.state)) 434 return -1; 435 else if (gameStatuses.indexOf(a.state) > gameStatuses.indexOf(b.state)) 436 return 1; 372 437 373 // Alphabetical comparison of names as tiebreaker. 374 if (a.name < b.name) 375 return -1; 376 else if (a.name > b.name) 377 return 1; 378 return 0; 438 // Alphabetical comparison of names as tiebreaker. 439 if (a.name < b.name) 440 return -1; 441 else if (a.name > b.name) 442 return 1; 443 return 0; 444 } 379 445 }); 380 446 381 447 var list_name = []; -
binaries/data/mods/public/gui/lobby/lobby.xml
20 20 21 21 <!-- Left panel: Player list. --> 22 22 <object name="leftPanel" size="20 30 20% 100%-280"> 23 <object name="playersBox" style="ModernList" type="olist" size="0 0 100% 100%" font="sans-bold-stroke-13">23 <object name="playersBox" style="ModernList" sprite_asc="ModernDropDownArrow" default_column="name" sprite_desc="ModernDropUpArrow" type="olist" sortable="true" size="0 0 100% 100%" font="sans-bold-stroke-13"> 24 24 <def id="status" width="26%"> 25 25 <translatableAttribute id="heading">Status</translatableAttribute> 26 26 </def> 27 <def id="name" width=" 50%">27 <def id="name" width="48%"> 28 28 <translatableAttribute id="heading">Name</translatableAttribute> 29 29 </def> 30 <def id="rating" width="2 4%">30 <def id="rating" width="26%"> 31 31 <translatableAttribute id="heading">Rating</translatableAttribute> 32 32 </def> 33 33 <action on="SelectionChange"> 34 34 displayProfile("lobbylist"); 35 35 </action> 36 <action on="SelectionColumnChange"> 37 updatePlayerListOrderSelection(); 38 </action> 36 39 </object> 37 40 </object> 38 41 … … 169 172 170 173 <!-- Middle panel: Filters, game list, chat box. --> 171 174 <object name="middlePanel" size="20%+5 5% 100%-255 97.2%"> 172 <object name="gamesBox" style="ModernList" type="olist" size="0 25 100% 48%" font="sans-stroke-13">175 <object name="gamesBox" style="ModernList" sprite_asc="ModernDropDownArrow" default_column="name" sprite_desc="ModernDropUpArrow" type="olist" sortable="true" size="0 25 100% 48%" font="sans-stroke-13"> 173 176 <action on="SelectionChange">updateGameSelection();</action> 177 <action on="SelectionColumnChange">updateGameListOrderSelection();</action> 174 178 <def id="name" color="0 60 0" width="27%"> 175 179 <translatableAttribute id="heading">Name</translatableAttribute> 176 180 </def> -
source/gui/COList.cpp
19 19 #include "i18n/L10n.h" 20 20 21 21 #include "ps/CLogger.h" 22 #include "soundmanager/ISoundManager.h" 22 23 23 COList::COList() : CList(),m_HeadingHeight(30.f) 24 COList::COList() : CList(),m_HeadingHeight(30.f),m_SelectedDef(-1),m_SelectedColumnOrder(1) 24 25 { 25 26 AddSetting(GUIST_CGUISpriteInstance, "sprite_heading"); 27 AddSetting(GUIST_bool, "sortable"); // The actual sorting is done in JS for more versatility 28 AddSetting(GUIST_CStr, "selected_column"); 29 AddSetting(GUIST_int, "selected_column_order"); 30 AddSetting(GUIST_CStr, "default_column"); 31 AddSetting(GUIST_int, "selected_def"); 32 AddSetting(GUIST_CGUISpriteInstance, "sprite_asc"); // Show the order of sorting 33 AddSetting(GUIST_CGUISpriteInstance, "sprite_desc"); 34 35 // Nothing is selected by default. 36 GUI<CStr>::SetSetting(this, "selected_column", ""); 37 GUI<int>::SetSetting(this, "selected_column_order", 1); 38 GUI<int>::SetSetting(this, "selected_def", -1); 26 39 } 27 40 28 41 void COList::SetupText() … … 66 79 float buffer_zone=0.f; 67 80 GUI<float>::GetSetting(this, "buffer_zone", buffer_zone); 68 81 82 CStr defaultColumn; 83 GUI<CStr>::GetSetting(this, "default_column", defaultColumn); 84 defaultColumn = "list_" + defaultColumn; 69 85 70 86 for (unsigned int c=0; c<m_ObjectsDefs.size(); ++c) 71 87 { … … 74 90 gui_string.SetValue(m_ObjectsDefs[c].m_Heading); 75 91 *text = GetGUI()->GenerateText(gui_string, font, width, buffer_zone, this); 76 92 AddText(text); 93 94 if (m_SelectedDef == -1 && defaultColumn == m_ObjectsDefs[c].m_Id) 95 m_SelectedDef = c; 77 96 } 78 97 79 98 … … 119 138 void COList::HandleMessage(SGUIMessage &Message) 120 139 { 121 140 CList::HandleMessage(Message); 141 142 switch (Message.type) 143 { 144 // If somebody clicks on the column heading 145 case GUIM_MOUSE_PRESS_LEFT: 146 { 147 bool sortable; 148 GUI<bool>::GetSetting(this, "sortable", sortable); 149 if (!sortable) 150 return; 151 152 CPos mouse = GetMousePos(); 153 if (!m_CachedActualSize.PointInside(mouse)) 154 return; 155 156 float xpos = 0; 157 for (unsigned int def = 0; def < m_ObjectsDefs.size(); ++def) 158 { 159 float width = m_ObjectsDefs[def].m_Width; 160 // Check if it's a decimal value, and if so, assume relative positioning. 161 if (m_ObjectsDefs[def].m_Width < 1 && m_ObjectsDefs[def].m_Width > 0) 162 width *= m_TotalAvalibleColumnWidth; 163 CPos leftTopCorner = m_CachedActualSize.TopLeft() + CPos(xpos, 4); 164 if (mouse.x >= leftTopCorner.x && 165 mouse.x < leftTopCorner.x + width && 166 mouse.y < leftTopCorner.y + m_HeadingHeight) 167 { 168 if (static_cast<int> (def) != m_SelectedDef) 169 { 170 m_SelectedColumnOrder = 1; 171 m_SelectedDef = def; 172 } 173 else 174 m_SelectedColumnOrder = -m_SelectedColumnOrder; 175 GUI<CStr>::SetSetting(this, "selected_column", m_ObjectsDefs[def].m_Id.substr(5)); 176 GUI<int>::SetSetting(this, "selected_column_order", m_SelectedColumnOrder); 177 GUI<int>::SetSetting(this, "selected_def", def); 178 ScriptEvent("selectioncolumnchange"); 179 180 CStrW soundPath; 181 if (g_SoundManager && GUI<CStrW>::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty()) 182 g_SoundManager->PlayAsUI(soundPath.c_str(), false); 183 184 return; 185 } 186 xpos += width; 187 } 188 return; 189 } 190 default: 191 return; 192 } 122 193 } 123 194 124 195 bool COList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile) … … 307 378 CRect rect_head(m_CachedActualSize.left, m_CachedActualSize.top, m_CachedActualSize.right, 308 379 m_CachedActualSize.top + m_HeadingHeight); 309 380 GetGUI()->DrawSprite(*sprite_heading, cell_id, bz, rect_head); 381 382 CGUISpriteInstance *sprite_order; 383 if (m_SelectedColumnOrder != -1) 384 GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite_asc", sprite_order); 385 else 386 GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite_desc", sprite_order); 310 387 311 388 float xpos = 0; 312 389 for (unsigned int def=0; def< m_ObjectsDefs.size(); ++def) 313 390 { 314 DrawText(def, color, m_CachedActualSize.TopLeft() + CPos(xpos, 4), bz+0.1f, rect_head);315 391 // Check if it's a decimal value, and if so, assume relative positioning. 392 float width = m_ObjectsDefs[def].m_Width; 316 393 if (m_ObjectsDefs[def].m_Width < 1 && m_ObjectsDefs[def].m_Width > 0) 317 xpos += m_ObjectsDefs[def].m_Width * m_TotalAvalibleColumnWidth; 318 else 319 xpos += m_ObjectsDefs[def].m_Width; 394 width *= m_TotalAvalibleColumnWidth; 395 396 CPos leftTopCorner = m_CachedActualSize.TopLeft() + CPos(xpos, 0); 397 // If the list sorted by current column 398 if (m_SelectedDef == static_cast<int> (def)) 399 GetGUI()->DrawSprite(*sprite_order, cell_id, bz + 0.1f, CRect(leftTopCorner + CPos(width - 16, 0), leftTopCorner + CPos(width, 16))); 400 401 DrawText(def, color, leftTopCorner + CPos(0, 4), bz + 0.1f, rect_head); 402 xpos += width; 320 403 } 321 404 322 405 for (int i=0; i<(int)pList->m_Items.size(); ++i) -
source/gui/COList.h
1 /* Copyright (C) 201 4Wildfire Games.1 /* Copyright (C) 2015 Wildfire Games. 2 2 * This file is part of 0 A.D. 3 3 * 4 4 * 0 A.D. is free software: you can redistribute it and/or modify … … 70 70 virtual CRect GetListRect() const; 71 71 72 72 std::vector<ObjectDef> m_ObjectsDefs; 73 int m_SelectedDef; 74 int m_SelectedColumnOrder; 73 75 74 76 private: 75 77 float m_HeadingHeight;