diff --git a/AUTHORS b/AUTHORS index 919dccdcac..50e15cc518 100644 --- a/AUTHORS +++ b/AUTHORS @@ -37,6 +37,7 @@ Also thanks to: * Arik Lirette (Angusm3) * Barnaby Smith (mvi) * Bellator + * Bernd Stellwag (burned42) * Biofreak * Braxton Williams (Buddytex) * Brendan Gluth (Mechanical_Man) @@ -46,6 +47,7 @@ Also thanks to: * Chris Cameron (Vesuvian) * Chris Grant (Unit158) * Christer Ulfsparre (Holloweye) + * Christoph Lahner (chlah) * clem * Cody Brittain (Generalcamo) * Constantin Helmig (CH4Code) @@ -143,6 +145,7 @@ Also thanks to: * Stuart McHattie (SDJMcHattie) * Taryn Hill (Phrohdoh) * Teemu Nieminen (Temeez) + * Thomas Christlieb (ThomasChr) * Tim Mylemans (gecko) * Tirili * Tomas Einarsson (Mesacer) diff --git a/OpenRA.Game/Map/MapPreview.cs b/OpenRA.Game/Map/MapPreview.cs index 22f7197a14..bae14db614 100644 --- a/OpenRA.Game/Map/MapPreview.cs +++ b/OpenRA.Game/Map/MapPreview.cs @@ -80,6 +80,7 @@ namespace OpenRA public MapStatus Status; public MapClassification Class; public MapVisibility Visibility; + public DateTime ModifiedDate; public MiniYaml RuleDefinitions; public MiniYaml WeaponDefinitions; @@ -187,6 +188,7 @@ namespace OpenRA public ActorInfo WorldActorInfo => innerData.WorldActorInfo; public ActorInfo PlayerActorInfo => innerData.PlayerActorInfo; + public DateTime ModifiedDate => innerData.ModifiedDate; public long DownloadBytes { get; private set; } public int DownloadPercentage { get; private set; } @@ -395,6 +397,8 @@ namespace OpenRA using (var dataStream = p.GetStream("map.png")) newData.Preview = new Png(dataStream); + newData.ModifiedDate = File.GetLastWriteTime(p.Name); + // Assign the new data atomically innerData = newData; } diff --git a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs index 5a6afb7f9f..fe3ca6cf5a 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs @@ -64,6 +64,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic [TranslationReference] static readonly string DeleteAllMapsAccept = "delete-all-maps-accept"; + [TranslationReference] + static readonly string OrderMapsByPlayers = "order-maps-players"; + + [TranslationReference] + static readonly string OrderMapsByDate = "order-maps-date"; + readonly Widget widget; readonly DropDownButtonWidget gameModeDropdown; readonly ModData modData; @@ -81,6 +87,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic string category; string mapFilter; + Func orderByFunc; + readonly string orderByPlayer; + readonly string orderByDate; + [ObjectCreator.UseCtor] internal MapChooserLogic(Widget widget, ModData modData, string initialMap, MapClassification initialTab, Action onExit, Action onSelect, MapVisibility filter) @@ -90,6 +100,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic this.onSelect = onSelect; allMaps = modData.Translation.GetString(AllMaps); + orderByPlayer = modData.Translation.GetString(OrderMapsByPlayers); + orderByDate = modData.Translation.GetString(OrderMapsByDate); var approving = new Action(() => { @@ -109,6 +121,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic var itemTemplate = widget.Get("MAP_TEMPLATE"); widget.RemoveChild(itemTemplate); + SetupOrderByDropdown(itemTemplate); + var mapFilterInput = widget.GetOrNull("MAPFILTER_INPUT"); if (mapFilterInput != null) { @@ -265,6 +279,38 @@ namespace OpenRA.Mods.Common.Widgets.Logic } } + void SetupOrderByDropdown(ScrollItemWidget itemTemplate) + { + var orderByDropdown = widget.GetOrNull("ORDERBY"); + if (orderByDropdown == null) + return; + + var orderByDict = new Dictionary>() + { + { orderByPlayer, m => m.PlayerCount }, + { orderByDate, m => -m.ModifiedDate.Ticks } + }; + + if (orderByFunc == null) + orderByFunc = orderByDict[orderByPlayer]; + + Func setupItem = (o, template) => + { + var item = ScrollItemWidget.Setup(template, + () => orderByFunc == orderByDict[o], + () => { orderByFunc = orderByDict[o]; EnumerateMaps(currentTab, itemTemplate); }); + item.Get("LABEL").GetText = () => o; + + return item; + }; + + orderByDropdown.OnClick = () => + orderByDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, orderByDict.Keys, setupItem); + + orderByDropdown.GetText = () => + orderByDict.FirstOrDefault(m => m.Value == orderByFunc).Key; + } + void EnumerateMaps(MapClassification tab, ScrollItemWidget template) { if (!int.TryParse(mapFilter, out var playerCountFilter)) @@ -276,7 +322,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic (m.Title != null && m.Title.IndexOf(mapFilter, StringComparison.OrdinalIgnoreCase) >= 0) || (m.Author != null && m.Author.IndexOf(mapFilter, StringComparison.OrdinalIgnoreCase) >= 0) || m.PlayerCount == playerCountFilter) - .OrderBy(m => m.PlayerCount) + .OrderBy(orderByFunc) .ThenBy(m => m.Title); scrollpanels[tab].RemoveChildren(); diff --git a/mods/cnc/chrome/mapchooser.yaml b/mods/cnc/chrome/mapchooser.yaml index 1e5a59367d..55152df792 100644 --- a/mods/cnc/chrome/mapchooser.yaml +++ b/mods/cnc/chrome/mapchooser.yaml @@ -14,35 +14,9 @@ Container@MAPCHOOSER_PANEL: Text: Select Map Background@bg: Width: PARENT_RIGHT - Height: 540 + Height: PARENT_BOTTOM Background: panel-black Children: - Label@FILTER_DESC: - X: PARENT_RIGHT - WIDTH - 401 - Y: 16 - Width: 60 - Height: 25 - Font: Bold - Align: Right - Text: Filter: - TextField@MAPFILTER_INPUT: - X: PARENT_RIGHT - WIDTH - 245 - Y: 15 - Width: 150 - Height: 25 - Label@FILTER_DESC_JOINER: - X: PARENT_RIGHT - WIDTH - 215 - Y: 16 - Width: 30 - Height: 25 - Font: Bold - Align: Center - Text: in - DropDownButton@GAMEMODE_FILTER: - X: PARENT_RIGHT - WIDTH - 15 - Y: 15 - Width: 200 - Height: 25 Button@SYSTEM_MAPS_TAB_BUTTON: X: 15 Y: 15 @@ -55,26 +29,28 @@ Container@MAPCHOOSER_PANEL: Height: 31 Width: 135 Text: Custom Maps - Container@SYSTEM_MAPS_TAB: + Container@MAP_TAB_PANES: + Width: PARENT_RIGHT - 30 + Height: PARENT_BOTTOM - 90 X: 15 Y: 45 - Width: PARENT_RIGHT - 30 - Height: PARENT_BOTTOM - 60 Children: - ScrollPanel@MAP_LIST: + Container@SYSTEM_MAPS_TAB: Width: PARENT_RIGHT Height: PARENT_BOTTOM - ItemSpacing: 1 - Container@USER_MAPS_TAB: - X: 15 - Y: 45 - Width: PARENT_RIGHT - 30 - Height: PARENT_BOTTOM - 60 - Children: - ScrollPanel@MAP_LIST: + Children: + ScrollPanel@MAP_LIST: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + ItemSpacing: 1 + Container@USER_MAPS_TAB: Width: PARENT_RIGHT Height: PARENT_BOTTOM - ItemSpacing: 1 + Children: + ScrollPanel@MAP_LIST: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + ItemSpacing: 1 ScrollItem@MAP_TEMPLATE: Width: 210 Height: 262 @@ -120,35 +96,73 @@ Container@MAPCHOOSER_PANEL: Y: PARENT_BOTTOM - HEIGHT - 2 Align: Center Font: Tiny + Container@FILTER_ORDER_CONTROLS: + X: 15 + Y: PARENT_BOTTOM - 40 + Width: PARENT_RIGHT - 30 + Height: PARENT_BOTTOM + Children: + Label@FILTER_DESC: + Width: 40 + Height: 24 + Font: Bold + Align: Right + Text: Filter: + TextField@MAPFILTER_INPUT: + X: 45 + Width: 150 + Height: 25 + Label@FILTER_DESC_JOINER: + X: 195 + Width: 30 + Height: 24 + Font: Bold + Align: Center + Text: in + DropDownButton@GAMEMODE_FILTER: + X: 225 + Width: 200 + Height: 25 + Label@ORDERBY_LABEL: + X: PARENT_RIGHT - WIDTH - 200 - 5 + Width: 100 + Height: 24 + Font: Bold + Align: Right + Text: Order by: + DropDownButton@ORDERBY: + X: PARENT_RIGHT - WIDTH + Width: 200 + Height: 25 Button@BUTTON_CANCEL: Key: escape - Y: 539 + Y: PARENT_BOTTOM - 1 Width: 140 Height: 35 Text: Back Button@RANDOMMAP_BUTTON: Key: space X: PARENT_RIGHT - 150 - WIDTH - Y: 539 + Y: PARENT_BOTTOM - 1 Width: 140 Height: 35 Text: Random Button@DELETE_MAP_BUTTON: X: PARENT_RIGHT - 300 - WIDTH - Y: 539 + Y: PARENT_BOTTOM - 1 Width: 140 Height: 35 Text: Delete Map Button@DELETE_ALL_MAPS_BUTTON: X: PARENT_RIGHT - 450 - WIDTH - Y: 539 + Y: PARENT_BOTTOM - 1 Width: 140 Height: 35 Text: Delete All Maps Button@BUTTON_OK: Key: return X: PARENT_RIGHT - WIDTH - Y: 539 + Y: PARENT_BOTTOM - 1 Width: 140 Height: 35 Text: Ok diff --git a/mods/common/chrome/map-chooser.yaml b/mods/common/chrome/map-chooser.yaml index 44e709d4f1..6b864e198b 100644 --- a/mods/common/chrome/map-chooser.yaml +++ b/mods/common/chrome/map-chooser.yaml @@ -26,26 +26,26 @@ Background@MAPCHOOSER_PANEL: Width: 140 Text: Custom Maps Font: Bold - Container@SYSTEM_MAPS_TAB: + Container@MAP_TAB_PANES: + Width: PARENT_RIGHT - 40 + Height: 438 X: 20 Y: 77 - Width: PARENT_RIGHT - 40 - Height: 471 Children: - ScrollPanel@MAP_LIST: + Container@SYSTEM_MAPS_TAB: Width: PARENT_RIGHT Height: PARENT_BOTTOM Children: - Container@USER_MAPS_TAB: - X: 20 - Y: 77 - Width: PARENT_RIGHT - 40 - Height: 471 - Children: - ScrollPanel@MAP_LIST: + ScrollPanel@MAP_LIST: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + Container@USER_MAPS_TAB: Width: PARENT_RIGHT Height: PARENT_BOTTOM Children: + ScrollPanel@MAP_LIST: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM ScrollItem@MAP_TEMPLATE: Width: 208 Height: 266 @@ -91,32 +91,44 @@ Background@MAPCHOOSER_PANEL: Y: PARENT_BOTTOM - HEIGHT - 6 Align: Center Font: Tiny - Label@FILTER_DESC: - X: PARENT_RIGHT - 467 - Y: 50 - Width: 60 - Height: 24 - Font: Bold - Align: Right - Text: Filter: - TextField@MAPFILTER_INPUT: - X: PARENT_RIGHT - 401 - Y: 48 - Width: 150 - Height: 25 - Label@FILTER_DESC_JOINER: - X: PARENT_RIGHT - 250 - Y: 48 - Width: 30 - Height: 24 - Font: Bold - Align: Center - Text: in - DropDownButton@GAMEMODE_FILTER: - X: PARENT_RIGHT - 220 - Y: 48 - Width: 200 - Height: 25 + Container@FILTER_ORDER_CONTROLS: + X: 20 + Y: PARENT_BOTTOM - 80 + Width: PARENT_RIGHT - 40 + Height: PARENT_BOTTOM + Children: + Label@FILTER_DESC: + Width: 40 + Height: 24 + Font: Bold + Align: Right + Text: Filter: + TextField@MAPFILTER_INPUT: + X: 45 + Width: 150 + Height: 25 + Label@FILTER_DESC_JOINER: + X: 195 + Width: 30 + Height: 24 + Font: Bold + Align: Center + Text: in + DropDownButton@GAMEMODE_FILTER: + X: 225 + Width: 200 + Height: 25 + Label@ORDERBY_LABEL: + X: PARENT_RIGHT - WIDTH - 200 - 5 + Width: 100 + Height: 24 + Font: Bold + Align: Right + Text: Order by: + DropDownButton@ORDERBY: + X: PARENT_RIGHT - WIDTH + Width: 200 + Height: 25 Button@RANDOMMAP_BUTTON: X: 20 Y: PARENT_BOTTOM - 45 diff --git a/mods/common/languages/en.ftl b/mods/common/languages/en.ftl index 8762560a2e..0c592c5d27 100644 --- a/mods/common/languages/en.ftl +++ b/mods/common/languages/en.ftl @@ -449,6 +449,9 @@ delete-all-maps-title = Delete maps delete-all-maps-prompt = Delete all maps on this page? delete-all-maps-accept = Delete +order-maps-players = Players +order-maps-date = Map Date + ## MissionBrowserLogic no-video-title = Video not installed no-video-prompt = The game videos can be installed from the