diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs index ccb7692111..0bc0e5181c 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs @@ -173,6 +173,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs() { { "initialMap", Map.Uid }, + { "initialTab", MapClassification.System }, { "onExit", DoNothing }, { "onSelect", Game.IsHost ? onSelect : null }, { "filter", MapVisibility.Lobby }, diff --git a/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs index b58a92f4ef..ac513a01fb 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MainMenuLogic.cs @@ -170,11 +170,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic var loadMapButton = widget.Get("LOAD_MAP_BUTTON"); loadMapButton.OnClick = () => { - var initialMap = Game.ModData.MapCache.FirstOrDefault(); menuType = MenuType.None; Game.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs() { - { "initialMap", initialMap != null ? initialMap.Uid : null }, + { "initialMap", null }, + { "initialTab", MapClassification.User }, { "onExit", () => menuType = MenuType.MapEditor }, { "onSelect", onSelect }, { "filter", MapVisibility.Lobby | MapVisibility.Shellmap | MapVisibility.MissionSelector }, diff --git a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs index 26106fda5b..a2c831984c 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/MapChooserLogic.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA; using OpenRA.Primitives; using OpenRA.Widgets; @@ -18,20 +19,27 @@ namespace OpenRA.Mods.Common.Widgets.Logic { public class MapChooserLogic { - string selectedUid; + readonly Widget widget; + readonly DropDownButtonWidget gameModeDropdown; - // May be a subset of available maps if a mode filter is active + MapClassification currentTab; + + Dictionary scrollpanels = new Dictionary(); + + Dictionary tabMaps = new Dictionary(); string[] visibleMaps; - ScrollPanelWidget scrollpanel; - ScrollItemWidget itemTemplate; - string mapFilter; + string selectedUid; + Action onSelect; + string gameMode; + string mapFilter; [ObjectCreator.UseCtor] - internal MapChooserLogic(Widget widget, string initialMap, Action onExit, Action onSelect, MapVisibility filter) + internal MapChooserLogic(Widget widget, string initialMap, MapClassification initialTab, Action onExit, Action onSelect, MapVisibility filter) { - selectedUid = WidgetUtils.ChooseInitialMap(initialMap); + this.widget = widget; + this.onSelect = onSelect; var approving = new Action(() => { Ui.CloseWindow(); onSelect(selectedUid); }); var canceling = new Action(() => { Ui.CloseWindow(); onExit(); }); @@ -41,59 +49,33 @@ namespace OpenRA.Mods.Common.Widgets.Logic okButton.OnClick = approving; widget.Get("BUTTON_CANCEL").OnClick = canceling; - scrollpanel = widget.Get("MAP_LIST"); - scrollpanel.Layout = new GridLayout(scrollpanel); + gameModeDropdown = widget.GetOrNull("GAMEMODE_FILTER"); - itemTemplate = scrollpanel.Get("MAP_TEMPLATE"); + var itemTemplate = widget.Get("MAP_TEMPLATE"); + widget.RemoveChild(itemTemplate); - var gameModeDropdown = widget.GetOrNull("GAMEMODE_FILTER"); - if (gameModeDropdown != null) + var mapFilterInput = widget.GetOrNull("MAPFILTER_INPUT"); + if (mapFilterInput != null) { - var selectableMaps = Game.ModData.MapCache.Where(m => m.Status == MapStatus.Available && (m.Map.Visibility & filter) != 0); - var gameModes = selectableMaps - .GroupBy(m => m.Type) - .Select(g => Pair.New(g.Key, g.Count())).ToList(); - - // 'all game types' extra item - gameModes.Insert(0, Pair.New(null as string, selectableMaps.Count())); - - Func, string> showItem = - x => "{0} ({1})".F(x.First ?? "All Game Types", x.Second); - - Func, ScrollItemWidget, ScrollItemWidget> setupItem = (ii, template) => + mapFilterInput.TakeKeyboardFocus(); + mapFilterInput.OnEscKey = () => { - var item = ScrollItemWidget.Setup(template, - () => gameMode == ii.First, - () => { gameMode = ii.First; EnumerateMaps(onSelect, filter); }); - item.Get("LABEL").GetText = () => showItem(ii); - return item; - }; - - gameModeDropdown.OnClick = () => - gameModeDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, gameModes, setupItem); - - gameModeDropdown.GetText = () => showItem(gameModes.First(m => m.First == gameMode)); - } - - var mapfilterInput = widget.GetOrNull("MAPFILTER_INPUT"); - if (mapfilterInput != null) - { - mapfilterInput.TakeKeyboardFocus(); - mapfilterInput.OnEscKey = () => - { - if (mapfilterInput.Text.Length == 0) + if (mapFilterInput.Text.Length == 0) canceling(); else { - mapFilter = mapfilterInput.Text = null; - EnumerateMaps(onSelect, filter); + mapFilter = mapFilterInput.Text = null; + EnumerateMaps(currentTab, itemTemplate); } return true; }; - mapfilterInput.OnEnterKey = () => { approving(); return true; }; - mapfilterInput.OnTextEdited = () => - { mapFilter = mapfilterInput.Text; EnumerateMaps(onSelect, filter); }; + mapFilterInput.OnEnterKey = () => { approving(); return true; }; + mapFilterInput.OnTextEdited = () => + { + mapFilter = mapFilterInput.Text; + EnumerateMaps(currentTab, itemTemplate); + }; } var randomMapButton = widget.GetOrNull("RANDOMMAP_BUTTON"); @@ -103,18 +85,95 @@ namespace OpenRA.Mods.Common.Widgets.Logic { var uid = visibleMaps.Random(Game.CosmeticRandom); selectedUid = uid; - scrollpanel.ScrollToItem(uid, smooth: true); + scrollpanels[currentTab].ScrollToItem(uid, smooth: true); }; randomMapButton.IsDisabled = () => visibleMaps == null || visibleMaps.Length == 0; } - EnumerateMaps(onSelect, filter); + SetupMapTab(MapClassification.User, filter, "USER_MAPS_TAB_BUTTON", "USER_MAPS_TAB", itemTemplate); + SetupMapTab(MapClassification.System, filter, "SYSTEM_MAPS_TAB_BUTTON", "SYSTEM_MAPS_TAB", itemTemplate); + + if (initialMap == null && tabMaps.Keys.Contains(initialTab) && tabMaps[initialTab].Any()) + { + selectedUid = WidgetUtils.ChooseInitialMap(tabMaps[initialTab].Select(mp => mp.Uid).First()); + currentTab = initialTab; + } + else + { + selectedUid = WidgetUtils.ChooseInitialMap(initialMap); + currentTab = tabMaps.Keys.FirstOrDefault(k => tabMaps[k].Select(mp => mp.Uid).Contains(selectedUid)); + } + + SwitchTab(currentTab, itemTemplate); } - void EnumerateMaps(Action onSelect, MapVisibility filter) + void SwitchTab(MapClassification tab, ScrollItemWidget itemTemplate) { - var maps = Game.ModData.MapCache - .Where(m => m.Status == MapStatus.Available && (m.Map.Visibility & filter) != 0) + currentTab = tab; + EnumerateMaps(tab, itemTemplate); + } + + void RefreshMaps(MapClassification tab, MapVisibility filter) + { + tabMaps[tab] = Game.ModData.MapCache.Where(m => m.Status == MapStatus.Available && + m.Class == tab && (m.Map.Visibility & filter) != 0).ToArray(); + } + + void SetupMapTab(MapClassification tab, MapVisibility filter, string tabButtonName, string tabContainerName, ScrollItemWidget itemTemplate) + { + var tabContainer = widget.Get(tabContainerName); + tabContainer.IsVisible = () => currentTab == tab; + var tabScrollpanel = tabContainer.Get("MAP_LIST"); + tabScrollpanel.Layout = new GridLayout(tabScrollpanel); + scrollpanels.Add(tab, tabScrollpanel); + + var tabButton = widget.Get(tabButtonName); + tabButton.IsHighlighted = () => currentTab == tab; + tabButton.IsVisible = () => tabMaps[tab].Any(); + tabButton.OnClick = () => SwitchTab(tab, itemTemplate); + + RefreshMaps(tab, filter); + } + + void SetupGameModeDropdown(MapClassification tab, DropDownButtonWidget gameModeDropdown, ScrollItemWidget itemTemplate) + { + if (gameModeDropdown != null) + { + var gameModes = tabMaps[tab] + .GroupBy(m => m.Type) + .Select(g => Pair.New(g.Key, g.Count())).ToList(); + + // 'all game types' extra item + gameModes.Insert(0, Pair.New(null as string, tabMaps[tab].Count())); + + Func, string> showItem = x => "{0} ({1})".F(x.First ?? "All Game Types", x.Second); + + Func, ScrollItemWidget, ScrollItemWidget> setupItem = (ii, template) => + { + var item = ScrollItemWidget.Setup(template, + () => gameMode == ii.First, + () => { gameMode = ii.First; EnumerateMaps(tab, itemTemplate); }); + item.Get("LABEL").GetText = () => showItem(ii); + return item; + }; + + gameModeDropdown.OnClick = () => + gameModeDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, gameModes, setupItem); + + gameModeDropdown.GetText = () => + { + var item = gameModes.FirstOrDefault(m => m.First == gameMode); + if (item == default(Pair)) + item.First = "No matches"; + + return showItem(item); + }; + } + } + + void EnumerateMaps(MapClassification tab, ScrollItemWidget template) + { + var maps = tabMaps[tab] .Where(m => gameMode == null || m.Type == gameMode) .Where(m => mapFilter == null || m.Title.IndexOf(mapFilter, StringComparison.OrdinalIgnoreCase) >= 0 || @@ -122,7 +181,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic .OrderBy(m => m.PlayerCount) .ThenBy(m => m.Title); - scrollpanel.RemoveChildren(); + scrollpanels[tab].RemoveChildren(); foreach (var loop in maps) { var preview = loop; @@ -139,9 +198,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic } }; - var item = ScrollItemWidget.Setup(preview.Uid, itemTemplate, () => selectedUid == preview.Uid, + var item = ScrollItemWidget.Setup(preview.Uid, template, () => selectedUid == preview.Uid, () => selectedUid = preview.Uid, dblClick); - item.IsVisible = () => item.RenderBounds.IntersectsWith(scrollpanel.RenderBounds); + item.IsVisible = () => item.RenderBounds.IntersectsWith(scrollpanels[tab].RenderBounds); var titleLabel = item.Get("TITLE"); titleLabel.GetText = () => preview.Title; @@ -169,12 +228,17 @@ namespace OpenRA.Mods.Common.Widgets.Logic sizeWidget.GetText = () => size; } - scrollpanel.AddChild(item); + scrollpanels[tab].AddChild(item); + } + + if (tab == currentTab) + { + visibleMaps = maps.Select(m => m.Uid).ToArray(); + SetupGameModeDropdown(currentTab, gameModeDropdown, template); } - visibleMaps = maps.Select(m => m.Uid).ToArray(); if (visibleMaps.Contains(selectedUid)) - scrollpanel.ScrollToItem(selectedUid); + scrollpanels[tab].ScrollToItem(selectedUid); } } } diff --git a/mods/cnc/chrome/mapchooser.yaml b/mods/cnc/chrome/mapchooser.yaml index 6aae63c738..b456366ac6 100644 --- a/mods/cnc/chrome/mapchooser.yaml +++ b/mods/cnc/chrome/mapchooser.yaml @@ -43,49 +43,75 @@ Container@MAPCHOOSER_PANEL: Y: 10 Width: 200 Height: 25 - ScrollPanel@MAP_LIST: + Button@SYSTEM_MAPS_TAB_BUTTON: + X: 15 + Y: 10 + Height: 35 + Width: 140 + Text: Official Maps + Button@USER_MAPS_TAB_BUTTON: + X: 15 + Y: 10 + Height: 35 + Width: 140 + Text: Custom Maps + Container@SYSTEM_MAPS_TAB: X: 15 Y: 45 Width: PARENT_RIGHT - 30 Height: 440 Children: - ScrollItem@MAP_TEMPLATE: - Width: 183 - Height: 232 - X: 2 - Y: 0 - Visible: false + ScrollPanel@MAP_LIST: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM Children: - MapPreview@PREVIEW: - X: (PARENT_RIGHT - WIDTH)/2 - Y: 4 - Width: 173 - Height: 173 - IgnoreMouseOver: true - IgnoreMouseInput: true - Label@TITLE: - X: 2 - Y: PARENT_BOTTOM-48 - Width: PARENT_RIGHT-4 - Align: Center - Label@DETAILS: - Width: PARENT_RIGHT-4 - X: 2 - Y: PARENT_BOTTOM-34 - Align: Center - Font: Tiny - Label@AUTHOR: - Width: PARENT_RIGHT-4 - X: 2 - Y: PARENT_BOTTOM-22 - Align: Center - Font: Tiny - Label@SIZE: - Width: PARENT_RIGHT-4 - X: 2 - Y: PARENT_BOTTOM-10 - Align: Center - Font: Tiny + Container@USER_MAPS_TAB: + X: 15 + Y: 45 + Width: PARENT_RIGHT - 30 + Height: 440 + Children: + ScrollPanel@MAP_LIST: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + Children: + ScrollItem@MAP_TEMPLATE: + Width: 183 + Height: 232 + X: 2 + Y: 0 + Visible: false + Children: + MapPreview@PREVIEW: + X: (PARENT_RIGHT - WIDTH)/2 + Y: 4 + Width: 173 + Height: 173 + IgnoreMouseOver: true + IgnoreMouseInput: true + Label@TITLE: + X: 2 + Y: PARENT_BOTTOM-48 + Width: PARENT_RIGHT-4 + Align: Center + Label@DETAILS: + Width: PARENT_RIGHT-4 + X: 2 + Y: PARENT_BOTTOM-34 + Align: Center + Font: Tiny + Label@AUTHOR: + Width: PARENT_RIGHT-4 + X: 2 + Y: PARENT_BOTTOM-22 + Align: Center + Font: Tiny + Label@SIZE: + Width: PARENT_RIGHT-4 + X: 2 + Y: PARENT_BOTTOM-10 + Align: Center + Font: Tiny Button@BUTTON_CANCEL: Key: escape X: 0 diff --git a/mods/ra/chrome/map-chooser.yaml b/mods/ra/chrome/map-chooser.yaml index b4eaab9c5e..c83f79efe8 100644 --- a/mods/ra/chrome/map-chooser.yaml +++ b/mods/ra/chrome/map-chooser.yaml @@ -13,49 +13,77 @@ Background@MAPCHOOSER_PANEL: Height: 20 Text: Choose Map Font: Bold - ScrollPanel@MAP_LIST: + Button@SYSTEM_MAPS_TAB_BUTTON: + X: 20 + Y: 43 + Height: 25 + Width: 140 + Text: Official Maps + Font: Bold + Button@USER_MAPS_TAB_BUTTON: + X: 160 + Y: 43 + Height: 25 + Width: 140 + Text: Custom Maps + Font: Bold + Container@SYSTEM_MAPS_TAB: X: 20 Y: 67 Width: PARENT_RIGHT - 40 Height: 481 Children: - ScrollItem@MAP_TEMPLATE: - Width: 194 - Height: 243 - X: 4 - Y: 0 - Visible: false + ScrollPanel@MAP_LIST: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM Children: - MapPreview@PREVIEW: - X: (PARENT_RIGHT - WIDTH)/2 - Y: 4 - Width: 184 - Height: 184 - IgnoreMouseOver: true - IgnoreMouseInput: true - Label@TITLE: - X: 2 - Y: PARENT_BOTTOM-48 - Width: PARENT_RIGHT-4 - Align: Center - Label@DETAILS: - Width: PARENT_RIGHT-4 - X: 2 - Y: PARENT_BOTTOM-34 - Align: Center - Font: Tiny - Label@AUTHOR: - Width: PARENT_RIGHT-4 - X: 2 - Y: PARENT_BOTTOM-22 - Align: Center - Font: Tiny - Label@SIZE: - Width: PARENT_RIGHT-4 - X: 2 - Y: PARENT_BOTTOM-10 - Align: Center - Font: Tiny + Container@USER_MAPS_TAB: + X: 20 + Y: 67 + Width: PARENT_RIGHT - 40 + Height: 481 + Children: + ScrollPanel@MAP_LIST: + Width: PARENT_RIGHT + Height: PARENT_BOTTOM + Children: + ScrollItem@MAP_TEMPLATE: + Width: 194 + Height: 243 + X: 4 + Y: 0 + Visible: false + Children: + MapPreview@PREVIEW: + X: (PARENT_RIGHT - WIDTH)/2 + Y: 4 + Width: 184 + Height: 184 + IgnoreMouseOver: true + IgnoreMouseInput: true + Label@TITLE: + X: 2 + Y: PARENT_BOTTOM-48 + Width: PARENT_RIGHT-4 + Align: Center + Label@DETAILS: + Width: PARENT_RIGHT-4 + X: 2 + Y: PARENT_BOTTOM-34 + Align: Center + Font: Tiny + Label@AUTHOR: + Width: PARENT_RIGHT-4 + X: 2 + Y: PARENT_BOTTOM-22 + Align: Center + Font: Tiny + Label@SIZE: + Width: PARENT_RIGHT-4 + X: 2 + Y: PARENT_BOTTOM-10 + Align: Center + Font: Tiny Label@FILTER_DESC: X: PARENT_RIGHT - 467 Y: 40