Index: ps/trunk/binaries/data/mods/public/gui/credits/texts/programming.json =================================================================== --- ps/trunk/binaries/data/mods/public/gui/credits/texts/programming.json (revision 27383) +++ ps/trunk/binaries/data/mods/public/gui/credits/texts/programming.json (revision 27384) @@ -1,311 +1,312 @@ { "Title": "Programming", "Content": [ { "Title": "Programming managers", "List": [ { "nick": "Acumen", "name": "Stuart Walpole" }, { "nick": "Dak Lozar", "name": "Dave Loeser" }, { "nick": "h20", "name": "Daniel Wilhelm" }, { "nick": "Janwas", "name": "Jan Wassenberg" }, { "nick": "Raj", "name": "Raj Sharma" } ] }, { "Subtitle": "Special thanks to", "List": [ { "nick": "leper", "name": "Georg Kilzer" }, { "nick": "Ykkrosh", "name": "Philip Taylor" } ] }, { "List": [ { "nick": "01d55" }, { "nick": "aBothe", "name": "Alexander Bothe" }, { "nick": "Acumen", "name": "Stuart Walpole" }, { "nick": "adrian", "name": "Adrian Boguszewszki" }, { "name": "Adrian Fatol" }, { "nick": "AI-Amsterdam" }, { "nick": "Alan", "name": "Alan Kemp" }, { "nick": "Alex", "name": "Alexander Yakobovich" }, { "nick": "alpha123", "name": "Peter P. Cannici" }, { "nick": "alre" }, { "nick": "Ampaex", "name": "Antonio Vazquez" }, { "name": "André Puel" }, { "nick": "andy5995", "name": "Andy Alt" }, { "nick": "Angen" }, { "nick": "Arfrever", "name": "Arfrever Frehtes Taifersar Arahesis" }, { "nick": "ArnH", "name": "Arno Hemelhof" }, { "nick": "Aurium", "name": "Aurélio Heckert" }, { "nick": "azayrahmad", "name": "Aziz Rahmad" }, { "nick": "baco", "name": "Dionisio E Alonso" }, { "nick": "badmadblacksad", "name": "Martin F" }, { "nick": "badosu", "name": "Amadeus Folego" }, { "nick": "bb", "name": "Bouke Jansen" }, { "nick": "Bellaz89", "name": "Andrea Bellandi" }, { "nick": "Ben", "name": "Ben Vinegar" }, { "nick": "Bird" }, { "nick": "Blue", "name": "Richard Welsh" }, { "nick": "bmwiedemann" }, { "nick": "boeseRaupe", "name": "Michael Kluge" }, { "nick": "bog_dan_ro", "name": "BogDan Vatra" }, { "nick": "Bonk", "name": "Christopher Ebbert" }, { "nick": "Boudica" }, { "nick": "Caius", "name": "Lars Kemmann" }, { "nick": "Calefaction", "name": "Matt Holmes" }, { "nick": "Calvinh", "name": "Carl-Johan Höiby" }, { "nick": "causative", "name": "Bart Parkis" }, { "name": "Cédric Houbart" }, { "nick": "Ceres" }, { "nick": "Chakakhan", "name": "Kenny Long" }, { "nick": "Clockwork-Muse", "name": "Stephen A. Imhoff" }, { "nick": "cpc", "name": "Clément Pit-Claudel" }, { "nick": "Cracker78", "name": "Chad Heim" }, { "nick": "Crynux", "name": "Stephen J. Fewer" }, { "nick": "cwprogger" }, { "nick": "cygal", "name": "Quentin Pradet" }, { "nick": "Dak Lozar", "name": "Dave Loeser" }, { "nick": "dalerank", "name": "Sergey Kushnirenko" }, { "nick": "dan", "name": "Dan Strandberg" }, { "nick": "DanCar", "name": "Daniel Cardenas" }, { "nick": "danger89", "name": "Melroy van den Berg" }, { "name": "Daniel Trevitz" }, { "nick": "Dariost", "name": "Dario Ostuni" }, { "nick": "Dave", "name": "David Protasowski" }, { "name": "David Marshall" }, { "nick": "dax", "name": "Dacian Fiordean" }, { "nick": "deebee", "name": "Deepak Anthony" }, { "nick": "Deiz" }, { "nick": "Dietger", "name": "Dietger van Antwerpen" }, { "nick": "DigitalSeraphim", "name": "Nick Owens" }, { "nick": "dp304" }, { "nick": "dpiquet", "name": "Damien Piquet" }, { "nick": "dumbo" }, { "nick": "Dunedan", "name": "Daniel Roschka" }, { "nick": "dvangennip", "name": "Doménique" }, { "nick": "DynamoFox" }, { "nick": "Echelon9", "name": "Rhys Kidd" }, { "nick": "echotangoecho" }, { "nick": "edoput", "name": "Edoardo Putti"}, { "nick": "eihrul", "name": "Lee Salzman" }, { "nick": "elexis", "name": "Alexander Heinsius" }, { "nick": "EmjeR", "name": "Matthijs de Rijk" }, { "nick": "EMontana" }, { "nick": "ericb" }, { "nick": "evanssthomas", "name": "Evans Thomas" }, { "nick": "Evulant", "name": "Alexander S." }, { "nick": "fabio", "name": "Fabio Pedretti" }, { "nick": "falsevision", "name": "Mahdi Khodadadifard" }, { "nick": "fatherbushido", "name": "Nicolas Tisserand" }, { "nick": "Fatton", "name": "Alexey Beloyarov" }, { "nick": "fcxSanya", "name": "Alexander Olkhovskiy" }, { "nick": "FeXoR", "name": "Florian Finke" }, { "nick": "Fire Giant", "name": "Malte Schwarzkopf" }, { "name": "Fork AD" }, { "nick": "fpre", "name": "Frederick Stallmeyer" }, { "nick": "Freagarach" }, { "nick": "freenity", "name": "Anton Galitch" }, { "nick": "Gallaecio", "name": "Adrián Chaves" }, { "nick": "gbish (aka Iny)", "name": "Grant Bishop" }, { "nick": "Gee", "name": "Gustav Larsson" }, { "nick": "Gentz", "name": "Hal Gentz" }, { "nick": "gerbilOFdoom" }, { "nick": "godlikeldh" }, + { "nick": "Grapjas", "name": "Richie van Coesant" }, { "nick": "greybeard", "name": "Joe Cocovich" }, { "nick": "grillaz" }, { "nick": "Grugnas", "name": "Giuseppe Tranchese" }, { "nick": "gudo" }, { "nick": "Guuts", "name": "Matthew Guttag" }, { "nick": "h20", "name": "Daniel Wilhelm" }, { "nick": "Hannibal_Barca", "name": "Clive Juhász S." }, { "nick": "Haommin" }, { "nick": "happyconcepts", "name": "Ben Bird" }, { "nick": "historic_bruno", "name": "Ben Brian" }, { "nick": "hyiltiz", "name": "Hormet Yiltiz" }, { "nick": "idanwin" }, { "nick": "Imarok", "name": "J. S." }, { "nick": "Inari" }, { "nick": "infyquest", "name": "Vijay Kiran Kamuju" }, { "nick": "irishninja", "name": "Brian Broll" }, { "nick": "IronNerd", "name": "Matthew McMullan" }, { "nick": "Itms", "name": "Nicolas Auvray" }, { "nick": "Jaison", "name": "Marco tom Suden" }, { "nick": "jammus", "name": "James Scott" }, { "nick": "Jammyjamjamman", "name": "James Sherratt" }, { "nick": "Janwas", "name": "Jan Wassenberg" }, { "nick": "javiergodas", "name": "Javier Godas Vieitez" }, { "nick": "JCWasmx86" }, { "nick": "Jgwman" }, { "nick": "JonBaer", "name": "Jon Baer" }, { "nick": "Josh", "name": "Joshua J. Bakita" }, { "nick": "joskar", "name": "Johnny Oskarsson" }, { "nick": "jP_wanN", "name": "Jonas Platte" }, { "nick": "jprahman", "name": "Jason Rahman" }, { "nick": "Jubalbarca", "name": "James Baillie" }, { "nick": "JubJub", "name": "Sebastian Vetter" }, { "nick": "jurgemaister" }, { "nick": "kabzerek", "name": "Grzegorz Kabza" }, { "nick": "Kai", "name": "Kai Chen" }, { "nick": "kalev", "name": "Kalev Lember" }, { "name": "Kareem Ergawy" }, { "nick": "karmux", "name": "Karmo Rosental" }, { "nick": "kevmo", "name": "Kevin Caffrey" }, { "nick": "kezz", "name": "Graeme Kerry" }, { "nick": "kingadami", "name": "Adam Winsor" }, { "nick": "kingbasil", "name": "Giannis Fafalios" }, { "nick": "Krinkle", "name": "Timo Tijhof" }, { "nick": "Kuba386", "name": "Jakub Kośmicki" }, { "nick": "lafferjm", "name": "Justin Lafferty" }, { "nick": "Langbart" }, { "nick": "LeanderH", "name": "Leander Hemelhof" }, { "nick": "leper", "name": "Georg Kilzer" }, { "nick": "Link Mauve", "name": "Emmanuel Gil Peyrot" }, { "nick": "LittleDev" }, { "nick": "livingaftermidnight", "name": "Will Dull" }, { "nick": "lonehawk", "name": "Vignesh Krishnan" }, { "nick": "Louhike" }, { "nick": "lsdh" }, { "nick": "Ludovic", "name": "Ludovic Rousseau" }, { "nick": "luiko", "name": "Luis Carlos Garcia Barajas" }, { "nick": "m0l0t0ph", "name": "Christoph Gielisch" }, { "nick": "madmax", "name": "Abhijit Nandy" }, { "nick": "madpilot", "name": "Guido Falsi" }, { "nick": "mammadori", "name": "Marco Amadori" }, { "nick": "marder", "name": "Stefan R. F." }, { "nick": "markcho" }, { "nick": "MarkT", "name": "Mark Thompson" }, { "nick": "Markus" }, { "nick": "Mate-86", "name": "Mate Kovacs" }, { "nick": "Matei", "name": "Matei Zaharia" }, { "nick": "MatSharrow" }, { "nick": "MattDoerksen", "name": "Matt Doerksen" }, { "nick": "mattlott", "name": "Matt Lott" }, { "nick": "maveric", "name": "Anton Protko" }, { "nick": "Micnasty", "name": "Travis Gorkin" }, { "name": "Mikołaj \"Bajter\" Korcz" }, { "nick": "mimo" }, { "nick": "mk12", "name": "Mitchell Kember" }, { "nick": "mmayfield45", "name": "Michael Mayfield" }, { "nick": "mmoanis", "name": "Mohamed Moanis" }, { "nick": "Molotov", "name": "Dario Alvarez" }, { "nick": "mpmoreti", "name": "Marcos Paulo Moreti" }, { "nick": "mreiland", "name": "Michael Reiland" }, { "nick": "myconid" }, { "nick": "n1xc0d3r", "name": "Luis Guerrero" }, { "nick": "nani", "name": "S. N." }, { "nick": "nd3c3nt", "name": "Gavin Fowler" }, { "nick": "nephele" }, { "nick": "Nescio" }, { "nick": "niektb", "name": "Niek ten Brinke" }, { "nick": "nikagra", "name": "Mikita Hradovich" }, { "nick": "njm" }, { "nick": "NoMonkey", "name": "John Mena" }, { "nick": "norsnor" }, { "nick": "notpete", "name": "Rich Cross" }, { "nick": "Nullus" }, { "nick": "nwtour" }, { "nick": "odoaker", "name": "Ágoston Sipos" }, { "nick": "Offensive ePeen", "name": "Jared Ryan Bills" }, { "nick": "Ols", "name": "Oliver Whiteman" }, { "nick": "olsner", "name": "Simon Brenner" }, { "nick": "OptimusShepard", "name": "Pirmin Stanglmeier" }, { "nick": "otero" }, { "nick": "Palaxin", "name": "David A. Freitag" }, { "name": "Paul Withers" }, { "nick": "paulobezerr", "name": "Paulo George Gomes Bezerra" }, { "nick": "pcpa", "name": "Paulo Andrade" }, { "nick": "Pendingchaos" }, { "nick": "PeteVasi", "name": "Pete Vasiliauskas" }, { "nick": "phosit" }, { "nick": "pilino1234" }, { "nick": "PingvinBetyar", "name": "Schronk Tamás" }, { "nick": "plugwash", "name": "Peter Michael Green" }, { "nick": "Polakrity" }, { "nick": "Poya", "name": "Poya Manouchehri" }, { "nick": "prefect", "name": "Nicolai Hähnle" }, { "nick": "Prodigal Son" }, { "nick": "pstumpf", "name": "Pascal Stumpf" }, { "nick": "pszemsza", "name": "Przemek Szałaj" }, { "nick": "pyrolink", "name": "Andrew Decker" }, { "nick": "quantumstate", "name": "Jonathan Waller" }, { "nick": "QuickShot", "name": "Walter Krawec" }, { "nick": "quonter" }, { "nick": "qwertz" }, { "nick": "Radagast" }, { "nick": "Raj", "name": "Raj Sharma" }, { "nick": "ramtzok1", "name": "Ram" }, { "nick": "rapidelectron", "name": "Christian Weihsbach" }, { "nick": "r-a-sattarov", "name": "Ramil Sattarov" }, { "nick": "RedFox", "name": "Jorma Rebane" }, { "nick": "RefinedCode" }, { "nick": "Riemer" }, { "name": "Rolf Sievers" }, { "nick": "s0600204", "name": "Matthew Norwood" }, { "nick": "sacha_vrand", "name": "Sacha Vrand" }, { "nick": "SafaAlfulaij" }, { "name": "Samuel Guarnieri" }, { "nick": "Samulis", "name": "Sam Gossner" }, { "nick": "Sandarac" }, { "nick": "sanderd17", "name": "Sander Deryckere" }, { "nick": "sathyam", "name": "Sathyam Vellal" }, { "nick": "sbirmi", "name": "Sharad Birmiwal" }, { "nick": "sbte", "name": "Sven Baars" }, { "nick": "scroogie", "name": "André Gemünd" }, { "nick": "scythetwirler", "name": "Casey X." }, { "nick": "sera", "name": "Ralph Sennhauser" }, { "nick": "serveurix" }, { "nick": "Shane", "name": "Shane Grant" }, { "nick": "shh" }, { "nick": "Silk", "name": "Josh Godsiff" }, { "nick": "silure" }, { "nick": "Simikolon", "name": "Yannick & Simon" }, { "nick": "smiley", "name": "M. L." }, { "nick": "Spahbod", "name": "Omid Davoodi" }, { "nick": "Stan", "name": "Stanislas Dolcini" }, { "nick": "Stefan" }, { "nick": "StefanBruens", "name": "Stefan Brüns" }, { "nick": "stilz", "name": "Sławomir Zborowski" }, { "nick": "stwf", "name": "Steven Fuchs" }, { "nick": "svott", "name": "Sven Ott" }, { "nick": "t4nk004" }, { "nick": "tau" }, { "nick": "tbm", "name": "Martin Michlmayr" }, { "nick": "Teiresias" }, { "nick": "temple" }, { "nick": "texane" }, { "nick": "thamlett", "name": "Timothy Hamlett" }, { "nick": "thedrunkyak", "name": "Dan Fuhr" }, { "nick": "Tobbi" }, { "nick": "Toonijn", "name": "Toon Baeyens" }, { "nick": "TrinityDeath", "name": "Jethro Lu" }, { "nick": "triumvir", "name": "Corin Schedler" }, { "nick": "trompetin17", "name": "Juan Guillermo" }, { "nick": "tpearson", "name": "Timothy Pearson" }, { "nick": "user1", "name": "A. C." }, { "nick": "usey11" }, { "nick": "vincent_c", "name": "Vincent Cheng" }, { "nick": "vinhig", "name": "Vincent Higginson" }, { "nick": "vladislavbelov", "name": "Vladislav Belov" }, { "nick": "voroskoi" }, { "nick": "vts", "name": "Jeroen DR" }, { "nick": "wacko", "name": "Andrew Spiering" }, { "nick": "WhiteTreePaladin", "name": "Brian Ashley" }, { "nick": "wowgetoffyourcellphone", "name": "Justus Avramenko" }, { "nick": "wraitii", "name": "Lancelot de Ferrière le Vayer" }, { "nick": "Xentelian", "name": "Mark Strawson" }, { "nick": "Xienen", "name": "Dayle Flowers" }, { "nick": "xone47", "name": "Brent Johnson" }, { "nick": "xtizer", "name": "Matt Green" }, { "nick": "yashi", "name": "Yasushi Shoji" }, { "nick": "Ykkrosh", "name": "Philip Taylor" }, { "nick": "Yves" }, { "nick": "z0rg", "name": "Sébastien Maire" }, { "nick": "Zeusthor", "name": "Jeffrey Tavares" }, { "nick": "zoot" }, { "nick": "zsol", "name": "Zsolt Dollenstein" }, { "nick": "ztamas", "name": "Tamas Zolnai" }, { "nick": "Zyi", "name": "Charles De Meulenaer" } ] } ] } Index: ps/trunk/source/gui/ObjectTypes/COList.cpp =================================================================== --- ps/trunk/source/gui/ObjectTypes/COList.cpp (revision 27383) +++ ps/trunk/source/gui/ObjectTypes/COList.cpp (revision 27384) @@ -1,440 +1,448 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #include "precompiled.h" #include "COList.h" #include "gui/CGUI.h" #include "gui/IGUIScrollBar.h" #include "gui/SettingTypes/CGUIColor.h" #include "gui/SettingTypes/CGUIList.h" #include "i18n/L10n.h" #include "ps/CLogger.h" const float SORT_SPRITE_DIM = 16.0f; const CVector2D COLUMN_SHIFT = CVector2D(0, 4); const CStr COList::EventNameSelectionColumnChange = "SelectionColumnChange"; COList::COList(CGUI& pGUI) : CList(pGUI), m_SpriteHeading(this, "sprite_heading"), m_Sortable(this, "sortable"), // The actual sorting is done in JS for more versatility m_SelectedColumn(this, "selected_column"), m_SelectedColumnOrder(this, "selected_column_order"), m_SpriteAsc(this, "sprite_asc"), // Show the order of sorting m_SpriteDesc(this, "sprite_desc"), m_SpriteNotSorted(this, "sprite_not_sorted") { } void COList::SetupText() { m_ItemsYPositions.resize(m_List->m_Items.size() + 1); // Delete all generated texts. Some could probably be saved, // but this is easier, and this function will never be called // continuously, or even often, so it'll probably be okay. m_GeneratedTexts.clear(); m_TotalAvailableColumnWidth = GetListRect().GetWidth(); // remove scrollbar if applicable if (m_ScrollBar && GetScrollBar(0).GetStyle()) m_TotalAvailableColumnWidth -= GetScrollBar(0).GetStyle()->m_Width; m_HeadingHeight = SORT_SPRITE_DIM; // At least the size of the sorting sprite for (const COListColumn& column : m_Columns) { float width = column.m_Width; if (column.m_Width > 0 && column.m_Width < 1) width *= m_TotalAvailableColumnWidth; CGUIString gui_string; gui_string.SetValue(column.m_Heading); const CGUIText& text = AddText(gui_string, m_Font, width, m_BufferZone); m_HeadingHeight = std::max(m_HeadingHeight, text.GetSize().Height + COLUMN_SHIFT.Y); } // Generate texts float buffered_y = 0.f; for (size_t i = 0; i < m_List->m_Items.size(); ++i) { m_ItemsYPositions[i] = buffered_y; float shift = 0.0f; for (const COListColumn& column : m_Columns) { float width = column.m_Width; if (column.m_Width > 0 && column.m_Width < 1) width *= m_TotalAvailableColumnWidth; CGUIText* text; if (!column.m_List->m_Items[i].GetOriginalString().empty()) text = &AddText(column.m_List->m_Items[i], m_Font, width, m_BufferZone); else { // Minimum height of a space character of the current font size CGUIString align_string; align_string.SetValue(L" "); text = &AddText(align_string, m_Font, width, m_BufferZone); } shift = std::max(shift, text->GetSize().Height); } buffered_y += shift; } m_ItemsYPositions[m_List->m_Items.size()] = buffered_y; if (m_ScrollBar) { CRect rect = GetListRect(); GetScrollBar(0).SetScrollRange(m_ItemsYPositions.back()); GetScrollBar(0).SetScrollSpace(rect.GetHeight()); GetScrollBar(0).SetX(rect.right); GetScrollBar(0).SetY(rect.top); GetScrollBar(0).SetZ(GetBufferedZ()); GetScrollBar(0).SetLength(rect.bottom - rect.top); } } CRect COList::GetListRect() const { return m_CachedActualSize + CRect(0, m_HeadingHeight, 0, 0); } void COList::HandleMessage(SGUIMessage& Message) { CList::HandleMessage(Message); switch (Message.type) { + + case GUIM_SETTINGS_UPDATED: + { + if (Message.value.find("heading_") == 0) + SetupText(); + break; + } + // If somebody clicks on the column heading case GUIM_MOUSE_PRESS_LEFT: { if (!m_Sortable) return; const CVector2D& mouse = m_pGUI.GetMousePos(); if (!m_CachedActualSize.PointInside(mouse)) return; float xpos = 0; for (const COListColumn& column : m_Columns) { if (column.m_Hidden) continue; float width = column.m_Width; // Check if it's a decimal value, and if so, assume relative positioning. if (column.m_Width < 1 && column.m_Width > 0) width *= m_TotalAvailableColumnWidth; CVector2D leftTopCorner = m_CachedActualSize.TopLeft() + CVector2D(xpos, 0); if (mouse.X >= leftTopCorner.X && mouse.X < leftTopCorner.X + width && mouse.Y < leftTopCorner.Y + m_HeadingHeight) { if (column.m_Id != static_cast(m_SelectedColumn)) { m_SelectedColumnOrder.Set(-1, true); CStr selected_column = column.m_Id; m_SelectedColumn.Set(selected_column, true); } else m_SelectedColumnOrder.Set(-m_SelectedColumnOrder, true); ScriptEvent(EventNameSelectionColumnChange); PlaySound(m_SoundSelected); return; } xpos += width; } return; } default: return; } } bool COList::HandleAdditionalChildren(const XMBData& xmb, const XMBElement& child) { #define ELMT(x) int elmt_##x = xmb.GetElementID(#x) #define ATTR(x) int attr_##x = xmb.GetAttributeID(#x) ELMT(item); ELMT(column); ELMT(translatableAttribute); ATTR(id); ATTR(context); if (child.GetNodeName() == elmt_item) { CGUIString vlist; vlist.SetValue(child.GetText().FromUTF8()); AddItem(vlist, vlist); return true; } else if (child.GetNodeName() == elmt_column) { CStr id; XERO_ITER_ATTR(child, attr) { if (attr.Name == attr_id) id = attr.Value; } COListColumn column(this, id); for (XMBAttribute attr : child.GetAttributes()) { std::string_view attr_name(xmb.GetAttributeStringView(attr.Name)); CStr attr_value(attr.Value); if (attr_name == "color") { if (!CGUI::ParseString(&m_pGUI, attr_value.FromUTF8(), column.m_TextColor)) LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name.data(), attr_value.c_str()); } else if (attr_name == "hidden") { bool hidden = false; if (!CGUI::ParseString(&m_pGUI, attr_value.FromUTF8(), hidden)) LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name.data(), attr_value.c_str()); else column.m_Hidden.Set(hidden, false); } else if (attr_name == "width") { float width; if (!CGUI::ParseString(&m_pGUI, attr_value.FromUTF8(), width)) LOGERROR("GUI: Error parsing '%s' (\"%s\")", attr_name.data(), attr_value.c_str()); else { // Check if it's a relative value, and save as decimal if so. if (attr_value.find("%") != std::string::npos) width = width / 100.f; column.m_Width = width; } } else if (attr_name == "heading") { - column.m_Heading = attr_value.FromUTF8(); + column.m_Heading.Set(attr_value.FromUTF8(), false); } } for (XMBElement grandchild : child.GetChildNodes()) { if (grandchild.GetNodeName() != elmt_translatableAttribute) continue; CStr attributeName(grandchild.GetAttributes().GetNamedItem(attr_id)); // only the heading is translatable for list column if (attributeName.empty() || attributeName != "heading") { LOGERROR("GUI: translatable attribute in olist column that isn't a heading. (object: %s)", this->GetPresentableName().c_str()); continue; } CStr value(grandchild.GetText()); if (value.empty()) continue; CStr context(grandchild.GetAttributes().GetNamedItem(attr_context)); // Read the context if any. if (!context.empty()) { CStr translatedValue(g_L10n.TranslateWithContext(context, value)); - column.m_Heading = translatedValue.FromUTF8(); + column.m_Heading.Set(translatedValue.FromUTF8(), false); } else { CStr translatedValue(g_L10n.Translate(value)); - column.m_Heading = translatedValue.FromUTF8(); + column.m_Heading.Set(translatedValue.FromUTF8(), false); } } m_Columns.emplace_back(std::move(column)); return true; } return false; } void COList::AdditionalChildrenHandled() { SetupText(); } void COList::DrawList(CCanvas2D& canvas, const int& selected, const CGUISpriteInstance& sprite, const CGUISpriteInstance& spriteOverlay, const CGUISpriteInstance& spriteSelectArea, const CGUISpriteInstance& spriteSelectAreaOverlay, const CGUIColor& textColor) { CRect rect = GetListRect(); m_pGUI.DrawSprite(sprite, canvas, rect); float scroll = 0.f; if (m_ScrollBar) scroll = GetScrollBar(0).GetPos(); bool drawSelected = false; CRect rectSel; // Draw item selection if (selected != -1) { ENSURE(selected >= 0 && selected+1 < (int)m_ItemsYPositions.size()); // Get rectangle of selection: rectSel = CRect( rect.left, rect.top + m_ItemsYPositions[selected] - scroll, rect.right, rect.top + m_ItemsYPositions[selected+1] - scroll); if (rectSel.top <= rect.bottom && rectSel.bottom >= rect.top) { if (rectSel.bottom > rect.bottom) rectSel.bottom = rect.bottom; if (rectSel.top < rect.top) rectSel.top = rect.top; if (m_ScrollBar) { // Remove any overlapping area of the scrollbar. if (rectSel.right > GetScrollBar(0).GetOuterRect().left && rectSel.right <= GetScrollBar(0).GetOuterRect().right) rectSel.right = GetScrollBar(0).GetOuterRect().left; if (rectSel.left >= GetScrollBar(0).GetOuterRect().left && rectSel.left < GetScrollBar(0).GetOuterRect().right) rectSel.left = GetScrollBar(0).GetOuterRect().right; } // Draw item selection m_pGUI.DrawSprite(spriteSelectArea, canvas, rectSel); drawSelected = true; } } // Draw line above column header CRect rect_head(m_CachedActualSize.left, m_CachedActualSize.top, m_CachedActualSize.right, m_CachedActualSize.top + m_HeadingHeight); m_pGUI.DrawSprite(m_SpriteHeading, canvas, rect_head); // Draw column headers float xpos = 0; size_t col = 0; for (const COListColumn& column : m_Columns) { if (column.m_Hidden) { ++col; continue; } // Check if it's a decimal value, and if so, assume relative positioning. float width = column.m_Width; if (column.m_Width < 1 && column.m_Width > 0) width *= m_TotalAvailableColumnWidth; CVector2D leftTopCorner = m_CachedActualSize.TopLeft() + CVector2D(xpos, 0); // Draw sort arrows in colum header if (m_Sortable) { const CGUISpriteInstance* pSprite; if (*m_SelectedColumn == column.m_Id) { if (m_SelectedColumnOrder == 0) LOGERROR("selected_column_order must not be 0"); if (m_SelectedColumnOrder != -1) pSprite = &*m_SpriteAsc; else pSprite = &*m_SpriteDesc; } else pSprite = &*m_SpriteNotSorted; m_pGUI.DrawSprite(*pSprite, canvas, CRect(leftTopCorner + CVector2D(width - SORT_SPRITE_DIM, 0), leftTopCorner + CVector2D(width, SORT_SPRITE_DIM))); } // Draw column header text DrawText(canvas, col, textColor, leftTopCorner + COLUMN_SHIFT, rect_head); xpos += width; ++col; } // Draw list items for each column const size_t objectsCount = m_Columns.size(); for (size_t i = 0; i < m_List->m_Items.size(); ++i) { if (m_ItemsYPositions[i+1] - scroll < 0 || m_ItemsYPositions[i] - scroll > rect.GetHeight()) continue; const float rowHeight = m_ItemsYPositions[i+1] - m_ItemsYPositions[i]; // Clipping area (we'll have to substract the scrollbar) CRect cliparea = GetListRect(); if (m_ScrollBar) { if (cliparea.right > GetScrollBar(0).GetOuterRect().left && cliparea.right <= GetScrollBar(0).GetOuterRect().right) cliparea.right = GetScrollBar(0).GetOuterRect().left; if (cliparea.left >= GetScrollBar(0).GetOuterRect().left && cliparea.left < GetScrollBar(0).GetOuterRect().right) cliparea.left = GetScrollBar(0).GetOuterRect().right; } // Draw all items for that column xpos = 0; for (size_t colIdx = 0; colIdx < m_Columns.size(); ++colIdx) { const COListColumn& column = m_Columns[colIdx]; if (column.m_Hidden) continue; // Determine text position and width const CVector2D textPos = rect.TopLeft() + CVector2D(xpos, -scroll + m_ItemsYPositions[i]); float width = column.m_Width; // Check if it's a decimal value, and if so, assume relative positioning. if (column.m_Width < 1 && column.m_Width > 0) width *= m_TotalAvailableColumnWidth; // Clip text to the column (to prevent drawing text into the neighboring column) CRect cliparea2 = cliparea; cliparea2.right = std::min(cliparea2.right, textPos.X + width); cliparea2.bottom = std::min(cliparea2.bottom, textPos.Y + rowHeight); // Draw list item DrawText(canvas, objectsCount * (i +/*Heading*/1) + colIdx, column.m_TextColor, textPos, cliparea2); xpos += width; } } // Draw scrollbars on top of the content if (m_ScrollBar) IGUIScrollBarOwner::Draw(canvas); // Draw the overlays last m_pGUI.DrawSprite(spriteOverlay, canvas, rect); if (drawSelected) m_pGUI.DrawSprite(spriteSelectAreaOverlay, canvas, rectSel); } Index: ps/trunk/source/gui/ObjectTypes/COList.h =================================================================== --- ps/trunk/source/gui/ObjectTypes/COList.h (revision 27383) +++ ps/trunk/source/gui/ObjectTypes/COList.h (revision 27384) @@ -1,97 +1,98 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with 0 A.D. If not, see . */ #ifndef INCLUDED_COLIST #define INCLUDED_COLIST #include "gui/ObjectTypes/CList.h" #include "gui/SettingTypes/CGUIColor.h" #include /** * Represents a column. */ class COListColumn { public: COListColumn(IGUIObject* owner, const CStr& cid) - : m_Width(0), m_Id(cid), m_List(owner, "list_" + cid), m_Hidden(owner, "hidden_" + cid, false) + : m_Id(cid), m_Width(0), m_Heading(owner, "heading_" + cid), m_List(owner, "list_" + cid), + m_Hidden(owner, "hidden_" + cid, false) {} // Avoid copying the strings. NONCOPYABLE(COListColumn); MOVABLE(COListColumn); CGUIColor m_TextColor; CStr m_Id; float m_Width; - CStrW m_Heading; // CGUIString?? + CGUISimpleSetting m_Heading; // CGUIString?? CGUISimpleSetting m_List; CGUISimpleSetting m_Hidden; }; /** * Multi-column list. One row can be selected by the user. * Individual cells are clipped if the contained text is too long. * * The list can be sorted dynamically by JS code when a * heading is clicked. * A scroll-bar will appear when needed. */ class COList : public CList { GUI_OBJECT(COList) public: COList(CGUI& pGUI); protected: void SetupText(); void HandleMessage(SGUIMessage& Message); /** * Handle the \ tag. */ virtual bool HandleAdditionalChildren(const XMBData& xmb, const XMBElement& child); virtual void AdditionalChildrenHandled(); virtual void DrawList( CCanvas2D& canvas, const int& selected, const CGUISpriteInstance& sprite, const CGUISpriteInstance& spriteOverlay, const CGUISpriteInstance& spriteSelectarea, const CGUISpriteInstance& spriteSelectAreaOverlay, const CGUIColor& textColor); virtual CRect GetListRect() const; /** * Available columns. */ std::vector m_Columns; CGUISimpleSetting m_SpriteHeading; CGUISimpleSetting m_Sortable; CGUISimpleSetting m_SelectedColumn; CGUISimpleSetting m_SelectedColumnOrder; CGUISimpleSetting m_SpriteAsc; CGUISimpleSetting m_SpriteDesc; CGUISimpleSetting m_SpriteNotSorted; private: static const CStr EventNameSelectionColumnChange; // Width of space available for columns float m_TotalAvailableColumnWidth; float m_HeadingHeight; }; #endif // INCLUDED_COLIST