Ticket #2405: colist_handler_lobby_sortable_game_and_player_list.patch

File colist_handler_lobby_sortable_game_and_player_list.patch, 10.3 KB (added by elexis, 9 years ago)
  • binaries/data/mods/public/gui/lobby/lobby.js

     
    11var g_ChatMessages = [];
    22var g_Name = "unknown";
    33var g_GameList = {};
     4var g_GameListSortBy = "default";
     5var g_PlayerListSortBy = "default";
     6var g_GameListOrder = 1; // 1 for ascending sort, and -1 for descending
     7var g_PlayerListOrder = 1;
    48var g_specialKey = Math.random();
    59// This object looks like {"name":[numMessagesSinceReset, lastReset, timeBlocked]} when in use.
    610var g_spamMonitor = {};
    711var g_timestamp = Engine.ConfigDB_GetValue("user", "lobby.chattimestamp") == "true";
    812var g_mapSizes = {};
     
    7175
    7276////////////////////////////////////////////////////////////////////////////////////////////////
    7377// Update functions
    7478////////////////////////////////////////////////////////////////////////////////////////////////
    7579
     80function updateGameListOrderSelection()
     81{
     82    var orderBy = Engine.GetGUIObjectByName("gamesBox").selected_column;
     83
     84    // If selected column equal to previous then just reverse order
     85    if (orderBy == g_GameListSortBy)
     86        g_GameListOrder = -g_GameListOrder;
     87    else
     88    {
     89        g_GameListSortBy = orderBy;
     90        g_GameListOrder = 1;
     91    }
     92
     93    applyFilters();
     94}
     95
     96function updatePlayerListOrderSelection() {
     97    var orderBy = Engine.GetGUIObjectByName("playersBox").selected_column;
     98
     99    // If selected column equal to previous then just reverse order
     100    if (orderBy == g_PlayerListSortBy)
     101        g_PlayerListOrder = -g_PlayerListOrder;
     102    else
     103    {
     104        g_PlayerListSortBy = orderBy;
     105        g_PlayerListOrder = 1;
     106    }
     107    updatePlayerList();
     108}
     109
    76110function resetFilters()
    77111{
    78112    // Reset states of gui objects
    79113    Engine.GetGUIObjectByName("mapSizeFilter").selected = 0
    80114    Engine.GetGUIObjectByName("playersNumberFilter").selected = 0;
    81115    Engine.GetGUIObjectByName("mapTypeFilter").selected = 0;
    82116    Engine.GetGUIObjectByName("showFullFilter").checked = false;
    83117
    84     // Update the list of games
    85     updateGameList();
    86 
    87     // Update info box about the game currently selected
    88     updateGameSelection();
     118    applyFilters();
    89119}
    90120
    91121function applyFilters()
    92122{
    93123    // Update the list of games
     
    146176        logo.size = "50%-110 40 50%+110 140";
    147177    }
    148178}
    149179
    150180/**
    151  * Do a full update of the player listing, including ratings from C++.
     181 * Do a full update of the player listing, including ratings from cached C++ information.
    152182 *
    153183 * @return Array containing the player, presence, nickname, and rating listings.
    154184 */
    155185function updatePlayerList()
    156186{
     
    161191    var ratingList = [];
    162192    var cleanPlayerList = Engine.GetPlayerList();
    163193    // Sort the player list, ignoring case.
    164194    cleanPlayerList.sort(function(a,b)
    165195    {
    166         var aName = a.name.toLowerCase();
    167         var bName = b.name.toLowerCase();
    168         return ((aName > bName) ? 1 : (bName > aName) ? -1 : 0);
    169     } );
     196        switch (g_PlayerListSortBy)
     197        {
     198        case 'rating':
     199            if (a.rating < b.rating)
     200                return -g_PlayerListOrder;
     201            else if (a.rating > b.rating)
     202                return g_PlayerListOrder;
     203            return 0;
     204        case 'status':
     205            let order = ["available", "away", "playing", "gone", "offline"];
     206            let presenceA = order.indexOf(a.presence);
     207            let presenceB = order.indexOf(b.presence);
     208            if (presenceA < presenceB)
     209                return -g_PlayerListOrder;
     210            else if (presenceA > presenceB)
     211                return g_PlayerListOrder;
     212            return 0;
     213        default:
     214            var aName = a.name.toLowerCase();
     215            var bName = b.name.toLowerCase();
     216            if (aName < bName)
     217                return -g_PlayerListOrder;
     218            else if (aName > bName)
     219                return g_PlayerListOrder;
     220            return 0;
     221        }
     222    });
    170223    for (var i = 0; i < cleanPlayerList.length; i++)
    171224    {
    172225        // Identify current user's rating.
    173226        if (cleanPlayerList[i].name == g_Name && cleanPlayerList[i].rating)
    174227            g_userRating = cleanPlayerList[i].rating;
     
    362415    g_GameList = gameList;
    363416
    364417    // Sort the list of games to that games 'waiting' are displayed at the top, followed by 'init', followed by 'running'.
    365418    var gameStatuses = ['waiting', 'init', 'running'];
    366419    g_GameList.sort(function (a,b) {
    367         if (gameStatuses.indexOf(a.state) < gameStatuses.indexOf(b.state))
    368             return -1;
    369         else if (gameStatuses.indexOf(a.state) > gameStatuses.indexOf(b.state))
    370             return 1;
    371 
    372         // Alphabetical comparison of names as tiebreaker.
    373         if (a.name < b.name)
    374             return -1;
    375         else if (a.name > b.name)
    376             return 1;
    377         return 0;
     420        switch (g_GameListSortBy)
     421        {
     422        case 'name':
     423            if (a.name < b.name)
     424                return -g_GameListOrder;
     425            else if (a.name > b.name)
     426                return g_GameListOrder;
     427            return 0;
     428        case 'mapName':
     429            if (translate(a.niceMapName) < translate(b.niceMapName))
     430                return -g_GameListOrder;
     431            else if (translate(a.niceMapName) > translate(b.niceMapName))
     432                return g_GameListOrder;
     433            return 0;
     434        case 'mapSize':
     435            // mapSize contains the number of tiles for random maps
     436            // scenario maps always display default size
     437            if (a.mapSize < b.mapSize)
     438                return -g_GameListOrder;
     439            else if (a.mapSize > b.mapSize)
     440                return g_GameListOrder;
     441            return 0;
     442        case 'mapType':
     443            if (a.mapType < b.mapType)
     444                return -g_GameListOrder;
     445            else if (a.mapType > b.mapType)
     446                return g_GameListOrder;
     447            return 0;
     448        case 'nPlayers':
     449            // Numerical comparison of player count ratio.
     450            if (a.nbp * b.tnbp < b.nbp * a.tnbp) // ratio a = a.nbp / a.tnbp, ratio b = b.nbp / b.tnbp
     451                return -g_GameListOrder;
     452            else if (a.nbp * b.tnbp > b.nbp * a.tnbp)
     453                return g_GameListOrder;
     454            return 0;
     455        default:
     456            if (gameStatuses.indexOf(a.state) < gameStatuses.indexOf(b.state))
     457                return -1;
     458            else if (gameStatuses.indexOf(a.state) > gameStatuses.indexOf(b.state))
     459                return 1;
     460
     461            // Alphabetical comparison of names as tiebreaker.
     462            if (a.name < b.name)
     463                return -1;
     464            else if (a.name > b.name)
     465                return 1;
     466            return 0;
     467        }
    378468    });
    379469
    380470    var list_name = [];
    381471    var list_ip = [];
    382472    var list_mapName = [];
  • binaries/data/mods/public/gui/lobby/lobby.xml

     
    1818            onTick();
    1919        </action>
    2020
    2121        <!-- Left panel: Player list. -->
    2222        <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" type="olist" sortable="true" size="0 0 100% 100%" font="sans-bold-stroke-13">
    2424                <def id="status" width="26%">
    2525                    <translatableAttribute id="heading">Status</translatableAttribute>
    2626                </def>
    2727                <def id="name" width="50%">
    2828                    <translatableAttribute id="heading">Name</translatableAttribute>
     
    3131                    <translatableAttribute id="heading">Rating</translatableAttribute>
    3232                </def>
    3333                <action on="SelectionChange">
    3434                    displayProfile("lobbylist");
    3535                </action>
     36                <action on="SelectionColumnChange">
     37                    updatePlayerListOrderSelection();
     38                </action>
    3639            </object>
    3740        </object>
    3841
    3942        <object name="profilePanel" size="20 100%-275 20% 100%-80">
    4043            <object name="profileBox" type="image" sprite="ModernDarkBoxGold" size="0 0 100% 100%">
     
    167170            </object>
    168171        </object>
    169172
    170173        <!-- Middle panel: Filters, game list, chat box. -->
    171174        <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" type="olist" sortable="true" size="0 25 100% 48%" font="sans-stroke-13">
    173176                <action on="SelectionChange">updateGameSelection();</action>
     177                <action on="SelectionColumnChange">updateGameListOrderSelection();</action>
    174178                <def id="name" color="0 60 0" width="27%">
    175179                    <translatableAttribute id="heading">Name</translatableAttribute>
    176180                </def>
    177181                <!--<def id="ip" heading="IP" color="0 128 128" width="170"/>-->
    178182                <def id="mapName" color="128 128 128" width="25%">
  • source/gui/COList.cpp

     
    1717#include "precompiled.h"
    1818#include "COList.h"
    1919#include "i18n/L10n.h"
    2020
    2121#include "ps/CLogger.h"
     22#include "soundmanager/ISoundManager.h"
    2223
    2324COList::COList() : CList(),m_HeadingHeight(30.f)
    2425{
    2526    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_def");
     30
     31    // Nothing is selected by default.
     32    GUI<CStr>::SetSetting(this, "selected_column", "");
     33    GUI<int>::SetSetting(this, "selected_def", -1);
    2634}
    2735
    2836void COList::SetupText()
    2937{
    3038    if (!GetGUI())
     
    117125}
    118126
    119127void COList::HandleMessage(SGUIMessage &Message)
    120128{
    121129    CList::HandleMessage(Message);
     130
     131    switch (Message.type)
     132    {
     133    // If somebody clicks on the column heading
     134    case GUIM_MOUSE_PRESS_LEFT:
     135    {
     136        bool sortable;
     137        GUI<bool>::GetSetting(this, "sortable", sortable);
     138        if (!sortable)
     139            return;
     140
     141        CPos mouse = GetMousePos();
     142        if (!m_CachedActualSize.PointInside(mouse))
     143            return;
     144       
     145        float xpos = 0;
     146        for (unsigned int def = 0; def < m_ObjectsDefs.size(); ++def)
     147        {
     148            float width = m_ObjectsDefs[def].m_Width;
     149            // Check if it's a decimal value, and if so, assume relative positioning.
     150            if (m_ObjectsDefs[def].m_Width < 1 && m_ObjectsDefs[def].m_Width > 0)
     151                width *= m_TotalAvalibleColumnWidth;
     152            CPos leftTopCorner = m_CachedActualSize.TopLeft() + CPos(xpos, 4);
     153            if (mouse.x >= leftTopCorner.x &&
     154                mouse.x < leftTopCorner.x + width &&
     155                mouse.y < leftTopCorner.y + m_HeadingHeight)
     156            {
     157                GUI<CStr>::SetSetting(this, "selected_column", m_ObjectsDefs[def].m_Id.substr(5));
     158                GUI<int>::SetSetting(this, "selected_def", def);
     159                ScriptEvent("selectioncolumnchange");
     160
     161                CStrW soundPath;
     162                if (g_SoundManager && GUI<CStrW>::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty())
     163                    g_SoundManager->PlayAsUI(soundPath.c_str(), false);
     164
     165                return;
     166            }
     167            xpos += width;
     168        }
     169        return;
     170    }
     171    default:
     172        return;
     173    }
    122174}
    123175
    124176bool COList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile)
    125177{
    126178    #define ELMT(x) int elmt_##x = pFile->GetElementID(#x)