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 Vladislav Belov, 9 years ago)
  • binaries/data/mods/mod/gui/common/modern/sprites.xml

     
    585585            size="0 6 16 22"
    586586        />
    587587    </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>
    588595    <sprite name = "ModernDropDownArrowHighlight">
    589596        <image texture = "global/modern/dropdown-arrow.png"
    590597            real_texture_placement = "0 0 16 16"
  • binaries/data/mods/public/gui/lobby/lobby.js

     
    11var g_ChatMessages = [];
    22var g_Name = "unknown";
    33var g_GameList = {};
     4var g_GameListSortBy = "name";
     5var g_PlayerListSortBy = "name";
     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 = {};
     
    7478// Update functions
    7579////////////////////////////////////////////////////////////////////////////////////////////////
    7680
     81function updateGameListOrderSelection()
     82{
     83    g_GameListSortBy = Engine.GetGUIObjectByName("gamesBox").selected_column;
     84    g_GameListOrder = Engine.GetGUIObjectByName("gamesBox").selected_column_order;
     85
     86    applyFilters();
     87}
     88
     89function updatePlayerListOrderSelection()
     90{
     91    g_PlayerListSortBy = Engine.GetGUIObjectByName("playersBox").selected_column;
     92    g_PlayerListOrder = Engine.GetGUIObjectByName("playersBox").selected_column_order;
     93
     94    updatePlayerList();
     95}
     96
    7797function resetFilters()
    7898{
    7999    // Reset states of gui objects
     
    82102    Engine.GetGUIObjectByName("mapTypeFilter").selected = 0;
    83103    Engine.GetGUIObjectByName("showFullFilter").checked = false;
    84104
    85     // Update the list of games
    86     updateGameList();
    87 
    88     // Update info box about the game currently selected
    89     updateGameSelection();
     105    applyFilters();
    90106}
    91107
    92108function applyFilters()
     
    149165}
    150166
    151167/**
    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.
    153169 *
    154170 * @return Array containing the player, presence, nickname, and rating listings.
    155171 */
     
    164180    // Sort the player list, ignoring case.
    165181    cleanPlayerList.sort(function(a,b)
    166182    {
    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    });
    171210    for (var i = 0; i < cleanPlayerList.length; i++)
    172211    {
    173212        // Identify current user's rating.
     
    365404    // Sort the list of games to that games 'waiting' are displayed at the top, followed by 'init', followed by 'running'.
    366405    var gameStatuses = ['waiting', 'init', 'running'];
    367406    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;
    372437
    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        }
    379445    });
    380446
    381447    var list_name = [];
  • binaries/data/mods/public/gui/lobby/lobby.xml

     
    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" sprite_asc="ModernDropDownArrow" default_column="name" sprite_desc="ModernDropUpArrow" 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>
    27                 <def id="name" width="50%">
     27                <def id="name" width="48%">
    2828                    <translatableAttribute id="heading">Name</translatableAttribute>
    2929                </def>
    30                 <def id="rating" width="24%">
     30                <def id="rating" width="26%">
    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
     
    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" sprite_asc="ModernDropDownArrow" default_column="name" sprite_desc="ModernDropUpArrow" 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>
  • source/gui/COList.cpp

     
    1919#include "i18n/L10n.h"
    2020
    2121#include "ps/CLogger.h"
     22#include "soundmanager/ISoundManager.h"
    2223
    23 COList::COList() : CList(),m_HeadingHeight(30.f)
     24COList::COList() : CList(),m_HeadingHeight(30.f),m_SelectedDef(-1),m_SelectedColumnOrder(1)
    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_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);
    2639}
    2740
    2841void COList::SetupText()
     
    6679    float buffer_zone=0.f;
    6780    GUI<float>::GetSetting(this, "buffer_zone", buffer_zone);
    6881
     82    CStr defaultColumn;
     83    GUI<CStr>::GetSetting(this, "default_column", defaultColumn);
     84    defaultColumn = "list_" + defaultColumn;
    6985
    7086    for (unsigned int c=0; c<m_ObjectsDefs.size(); ++c)
    7187    {
     
    7490        gui_string.SetValue(m_ObjectsDefs[c].m_Heading);
    7591        *text = GetGUI()->GenerateText(gui_string, font, width, buffer_zone, this);
    7692        AddText(text);
     93
     94        if (m_SelectedDef == -1 && defaultColumn == m_ObjectsDefs[c].m_Id)
     95            m_SelectedDef = c;
    7796    }
    7897
    7998
     
    119138void COList::HandleMessage(SGUIMessage &Message)
    120139{
    121140    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    }
    122193}
    123194
    124195bool COList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile)
     
    307378        CRect rect_head(m_CachedActualSize.left, m_CachedActualSize.top, m_CachedActualSize.right,
    308379                                        m_CachedActualSize.top + m_HeadingHeight);
    309380        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);
    310387
    311388        float xpos = 0;
    312389        for (unsigned int def=0; def< m_ObjectsDefs.size(); ++def)
    313390        {
    314             DrawText(def, color, m_CachedActualSize.TopLeft() + CPos(xpos, 4), bz+0.1f, rect_head);
    315391            // Check if it's a decimal value, and if so, assume relative positioning.
     392            float width = m_ObjectsDefs[def].m_Width;
    316393            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;
    320403        }
    321404
    322405        for (int i=0; i<(int)pList->m_Items.size(); ++i)
  • source/gui/COList.h

     
    1 /* Copyright (C) 2014 Wildfire Games.
     1/* Copyright (C) 2015 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
     
    7070    virtual CRect GetListRect() const;
    7171
    7272    std::vector<ObjectDef> m_ObjectsDefs;
     73    int m_SelectedDef;
     74    int m_SelectedColumnOrder;
    7375
    7476private:
    7577    float m_HeadingHeight;