Ticket #2405: colist_handler_lobby_sortable_game_and_player_list_with_arrows_v4.1.patch

File colist_handler_lobby_sortable_game_and_player_list_with_arrows_v4.1.patch, 18.4 KB (added by Vladislav Belov, 9 years ago)
  • binaries/data/mods/mod/art/textures/ui/global/modern/arrow-down.png

    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
  • binaries/data/mods/mod/art/textures/ui/global/modern/arrow-up.png

    Property changes on: binaries/data/mods/mod/art/textures/ui/global/modern/arrow-down.png
    ___________________________________________________________________
    Added: svn:mime-type
    ## -0,0 +1 ##
    +application/octet-stream
    \ No newline at end of property
    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
  • binaries/data/mods/mod/art/textures/ui/global/modern/arrows-up-down.png

    Property changes on: binaries/data/mods/mod/art/textures/ui/global/modern/arrow-up.png
    ___________________________________________________________________
    Added: svn:mime-type
    ## -0,0 +1 ##
    +application/octet-stream
    \ No newline at end of property
    Cannot display: file marked as a binary type.
    svn:mime-type = application/octet-stream
  • binaries/data/mods/mod/gui/common/modern/sprites.xml

    Property changes on: binaries/data/mods/mod/art/textures/ui/global/modern/arrows-up-down.png
    ___________________________________________________________________
    Added: svn:mime-type
    ## -0,0 +1 ##
    +application/octet-stream
    \ No newline at end of property
     
    585585            size="0 6 16 22"
    586586        />
    587587    </sprite>
     588    <sprite name = "ModernNotSorted">
     589        <image texture = "global/modern/arrows-up-down.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>
     595    <sprite name = "ModernArrowUp">
     596        <image texture = "global/modern/arrow-up.png"
     597            real_texture_placement = "0 0 16 16"
     598            texture_size="0 0 16 16"
     599            size="0 6 16 22"
     600        />
     601    </sprite>
     602    <sprite name = "ModernArrowDown">
     603        <image texture = "global/modern/arrow-down.png"
     604            real_texture_placement = "0 0 16 16"
     605            texture_size="0 0 16 16"
     606            size="0 6 16 22"
     607        />
     608    </sprite>
    588609    <sprite name = "ModernDropDownArrowHighlight">
    589610        <image texture = "global/modern/dropdown-arrow.png"
    590611            real_texture_placement = "0 0 16 16"
  • binaries/data/mods/public/gui/gui.rnc

     
    5252  attribute button_width { xsd:decimal }?&
    5353  attribute checked { bool }?&
    5454  attribute clip { bool }?&
     55  attribute default_column { text }?&
    5556  attribute dropdown_size { xsd:decimal }?&
    5657  attribute dropdown_buffer { xsd:decimal }?&
    5758  attribute enabled { bool }?&
     
    7071  attribute scrollbar { bool }?&
    7172  attribute scrollbar_style { text }?&
    7273  attribute scroll_bottom { bool }?&
     74  attribute sortable { bool }?&
    7375  attribute sound_closed { text }?&
    7476  attribute sound_disabled { text }?&
    7577  attribute sound_enter { text }?&
     
    7981  attribute sound_selected { text }?&
    8082  attribute sprite { text }?&
    8183  attribute sprite2 { text }?&
     84  attribute sprite_asc { text }?&
    8285  attribute sprite_heading { text }?&
    8386  attribute sprite_bar { text }?&
    8487  attribute sprite_background { text }?&
     88  attribute sprite_desc { text }?&
    8589  attribute sprite_disabled { text }?&
    8690  attribute sprite_list { text }?&
    8791  attribute sprite2_disabled { text }?&
     92  attribute sprite_not_sorted { text }?&
    8893  attribute sprite_over { text }?&
    8994  attribute sprite2_over { text }?&
    9095  attribute sprite_pressed { text }?&
  • binaries/data/mods/public/gui/gui.rng

     
    200200        </attribute>
    201201      </optional>
    202202      <optional>
     203        <attribute name="default_column"/>
     204      </optional>
     205      <optional>
    203206        <attribute name="dropdown_size">
    204207          <data type="decimal"/>
    205208        </attribute>
     
    287290        </attribute>
    288291      </optional>
    289292      <optional>
     293        <attribute name="sortable"/>
     294      </optional>
     295      <optional>
    290296        <attribute name="sound_closed"/>
    291297      </optional>
    292298      <optional>
     
    347353        <attribute name="sprite_selectarea"/>
    348354      </optional>
    349355      <optional>
     356        <attribute name="sprite_asc"/>
     357      </optional>
     358      <optional>
     359        <attribute name="sprite_desc"/>
     360      </optional>
     361      <optional>
     362        <attribute name="sprite_not_sorted"/>
     363      </optional>
     364      <optional>
    350365        <attribute name="square_side">
    351366          <data type="decimal"/>
    352367        </attribute>
  • 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="ModernArrowDown" default_column="name" sprite_desc="ModernArrowUp" sprite_not_sorted="ModernNotSorted" 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="ModernArrowDown" default_column="name" sprite_desc="ModernArrowUp" sprite_not_sorted="ModernNotSorted" 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    AddSetting(GUIST_CGUISpriteInstance,    "sprite_not_sorted");
     35
     36    // Nothing is selected by default.
     37    GUI<CStr>::SetSetting(this, "selected_column", "");
     38    GUI<int>::SetSetting(this, "selected_column_order", 1);
     39    GUI<int>::SetSetting(this, "selected_def", -1);
    2640}
    2741
    2842void COList::SetupText()
     
    6680    float buffer_zone=0.f;
    6781    GUI<float>::GetSetting(this, "buffer_zone", buffer_zone);
    6882
     83    CStr defaultColumn;
     84    GUI<CStr>::GetSetting(this, "default_column", defaultColumn);
     85    defaultColumn = "list_" + defaultColumn;
    6986
    7087    for (unsigned int c=0; c<m_ObjectsDefs.size(); ++c)
    7188    {
     
    7491        gui_string.SetValue(m_ObjectsDefs[c].m_Heading);
    7592        *text = GetGUI()->GenerateText(gui_string, font, width, buffer_zone, this);
    7693        AddText(text);
     94
     95        if (m_SelectedDef == -1 && defaultColumn == m_ObjectsDefs[c].m_Id)
     96            m_SelectedDef = c;
    7797    }
    7898
    7999
     
    119139void COList::HandleMessage(SGUIMessage &Message)
    120140{
    121141    CList::HandleMessage(Message);
     142
     143    switch (Message.type)
     144    {
     145    // If somebody clicks on the column heading
     146    case GUIM_MOUSE_PRESS_LEFT:
     147    {
     148        bool sortable;
     149        GUI<bool>::GetSetting(this, "sortable", sortable);
     150        if (!sortable)
     151            return;
     152
     153        CPos mouse = GetMousePos();
     154        if (!m_CachedActualSize.PointInside(mouse))
     155            return;
     156       
     157        float xpos = 0;
     158        for (unsigned int def = 0; def < m_ObjectsDefs.size(); ++def)
     159        {
     160            float width = m_ObjectsDefs[def].m_Width;
     161            // Check if it's a decimal value, and if so, assume relative positioning.
     162            if (m_ObjectsDefs[def].m_Width < 1 && m_ObjectsDefs[def].m_Width > 0)
     163                width *= m_TotalAvalibleColumnWidth;
     164            CPos leftTopCorner = m_CachedActualSize.TopLeft() + CPos(xpos, 4);
     165            if (mouse.x >= leftTopCorner.x &&
     166                mouse.x < leftTopCorner.x + width &&
     167                mouse.y < leftTopCorner.y + m_HeadingHeight)
     168            {
     169                if (static_cast<int> (def) != m_SelectedDef)
     170                {
     171                    m_SelectedColumnOrder = 1;
     172                    m_SelectedDef = def;
     173                }
     174                else
     175                    m_SelectedColumnOrder = -m_SelectedColumnOrder;
     176                GUI<CStr>::SetSetting(this, "selected_column", m_ObjectsDefs[def].m_Id.substr(5));
     177                GUI<int>::SetSetting(this, "selected_column_order", m_SelectedColumnOrder);
     178                GUI<int>::SetSetting(this, "selected_def", def);
     179                ScriptEvent("selectioncolumnchange");
     180
     181                CStrW soundPath;
     182                if (g_SoundManager && GUI<CStrW>::GetSetting(this, "sound_selected", soundPath) == PSRETURN_OK && !soundPath.empty())
     183                    g_SoundManager->PlayAsUI(soundPath.c_str(), false);
     184
     185                return;
     186            }
     187            xpos += width;
     188        }
     189        return;
     190    }
     191    default:
     192        return;
     193    }
    122194}
    123195
    124196bool COList::HandleAdditionalChildren(const XMBElement& child, CXeromyces* pFile)
     
    307379        CRect rect_head(m_CachedActualSize.left, m_CachedActualSize.top, m_CachedActualSize.right,
    308380                                        m_CachedActualSize.top + m_HeadingHeight);
    309381        GetGUI()->DrawSprite(*sprite_heading, cell_id, bz, rect_head);
     382       
     383        CGUISpriteInstance *sprite_order, *sprite_not_sorted;
     384        if (m_SelectedColumnOrder != -1)
     385            GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite_asc", sprite_order);
     386        else
     387            GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite_desc", sprite_order);
     388        GUI<CGUISpriteInstance>::GetSettingPointer(this, "sprite_not_sorted", sprite_not_sorted);
    310389
    311390        float xpos = 0;
    312391        for (unsigned int def=0; def< m_ObjectsDefs.size(); ++def)
    313392        {
    314             DrawText(def, color, m_CachedActualSize.TopLeft() + CPos(xpos, 4), bz+0.1f, rect_head);
    315393            // Check if it's a decimal value, and if so, assume relative positioning.
     394            float width = m_ObjectsDefs[def].m_Width;
    316395            if (m_ObjectsDefs[def].m_Width < 1 && m_ObjectsDefs[def].m_Width > 0)
    317                 xpos += m_ObjectsDefs[def].m_Width * m_TotalAvalibleColumnWidth;
     396                width *= m_TotalAvalibleColumnWidth;
     397
     398            CPos leftTopCorner = m_CachedActualSize.TopLeft() + CPos(xpos, 0);
     399            CGUISpriteInstance *sprite;
     400            // If the list sorted by current column
     401            if (m_SelectedDef == static_cast<int> (def))
     402                sprite = sprite_order;
    318403            else
    319                 xpos += m_ObjectsDefs[def].m_Width;
     404                sprite = sprite_not_sorted;
     405            GetGUI()->DrawSprite(*sprite, cell_id, bz + 0.1f, CRect(leftTopCorner + CPos(width - 16, 0), leftTopCorner + CPos(width, 16)));
     406
     407            DrawText(def, color, leftTopCorner + CPos(0, 4), bz + 0.1f, rect_head);
     408            xpos += width;
    320409        }
    321410
    322411        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;