diff --git a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs index 25d15d4e71..fe00a21a12 100644 --- a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs @@ -48,13 +48,17 @@ namespace OpenRA.Mods.Common.Widgets [Translate] public readonly string ReadyText = ""; [Translate] public readonly string HoldText = ""; - public int IconCount { get; private set; } + public int DisplayedIconCount { get; private set; } + public int TotalIconCount { get; private set; } public event Action OnIconCountChanged = (a, b) => { }; public ProductionIcon TooltipIcon { get; private set; } public readonly World World; readonly OrderManager orderManager; + public int IconRowOffset = 0; + public int MaxIconRowOffset = int.MaxValue; + Lazy tooltipContainer; ProductionQueue currentQueue; @@ -86,8 +90,53 @@ namespace OpenRA.Mods.Common.Widgets clock = new Animation(world, "clock"); } + public void ScrollDown() + { + if (CanScrollDown) + IconRowOffset++; + } + + public bool CanScrollDown + { + get + { + var totalRows = (TotalIconCount + Columns - 1) / Columns; + + return IconRowOffset < totalRows - MaxIconRowOffset; + } + } + + public void ScrollUp() + { + if (CanScrollUp) + IconRowOffset--; + } + + public bool CanScrollUp + { + get { return IconRowOffset > 0; } + } + + public void ScrollToTop() + { + IconRowOffset = 0; + } + + public IEnumerable AllBuildables + { + get + { + if (CurrentQueue == null) + return Enumerable.Empty(); + + return CurrentQueue.AllItems().OrderBy(a => a.Traits.Get().BuildPaletteOrder); + } + } + public override void Tick() { + TotalIconCount = AllBuildables.Count(); + if (CurrentQueue != null && !CurrentQueue.Actor.IsInWorld) CurrentQueue = null; @@ -202,26 +251,25 @@ namespace OpenRA.Mods.Common.Widgets icons = new Dictionary(); if (CurrentQueue == null) { - if (IconCount != 0) + if (DisplayedIconCount != 0) { - OnIconCountChanged(IconCount, 0); - IconCount = 0; + OnIconCountChanged(DisplayedIconCount, 0); + DisplayedIconCount = 0; } return; } - var allBuildables = CurrentQueue.AllItems().OrderBy(a => a.Traits.Get().BuildPaletteOrder); - - var oldIconCount = IconCount; - IconCount = 0; + var oldIconCount = DisplayedIconCount; + DisplayedIconCount = 0; var ks = Game.Settings.Keys; var rb = RenderBounds; - foreach (var item in allBuildables) + + foreach (var item in AllBuildables.Skip(IconRowOffset * Columns).Take(MaxIconRowOffset * Columns)) { - var x = IconCount % Columns; - var y = IconCount / Columns; + var x = DisplayedIconCount % Columns; + var y = DisplayedIconCount / Columns; var rect = new Rectangle(rb.X + x * (IconSize.X + IconMargin.X), rb.Y + y * (IconSize.Y + IconMargin.Y), IconSize.X, IconSize.Y); var icon = new Animation(World, RenderSimple.GetImage(item)); icon.Play(item.Traits.Get().Icon); @@ -230,20 +278,20 @@ namespace OpenRA.Mods.Common.Widgets { Actor = item, Name = item.Name, - Hotkey = ks.GetProductionHotkey(IconCount), + Hotkey = ks.GetProductionHotkey(DisplayedIconCount), Sprite = icon.Image, Pos = new float2(rect.Location), Queued = CurrentQueue.AllQueued().Where(a => a.Item == item.Name).ToList(), }; icons.Add(rect, pi); - IconCount++; + DisplayedIconCount++; } eventBounds = icons.Any() ? icons.Keys.Aggregate(Rectangle.Union) : Rectangle.Empty; - if (oldIconCount != IconCount) - OnIconCountChanged(oldIconCount, IconCount); + if (oldIconCount != DisplayedIconCount) + OnIconCountChanged(oldIconCount, DisplayedIconCount); } public override void Draw() diff --git a/OpenRA.Mods.RA/Widgets/Logic/ClassicProductionLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/ClassicProductionLogic.cs index 2721d1e231..e4e2e6b086 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/ClassicProductionLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/ClassicProductionLogic.cs @@ -36,6 +36,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic Action selectTab = reverse => { palette.CurrentQueue = queues.FirstOrDefault(q => q.Enabled); + + // When a tab is selected, scroll to the top because the current row position may be invalid for the new tab + palette.ScrollToTop(); }; Func getKey = _ => Hotkey.Invalid; @@ -134,7 +137,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic var ticker = widget.Get("PRODUCTION_TICKER"); ticker.OnTick = () => { - if (palette.CurrentQueue == null || palette.IconCount == 0) + if (palette.CurrentQueue == null || palette.DisplayedIconCount == 0) { // Select the first active tab foreach (var b in typesContainer.Children) @@ -148,6 +151,45 @@ namespace OpenRA.Mods.RA.Widgets.Logic } } }; + + // Hook up scroll up and down buttons on the palette + var scrollDown = widget.GetOrNull("SCROLL_DOWN_BUTTON"); + + if (scrollDown != null) + { + scrollDown.OnClick = palette.ScrollDown; + scrollDown.IsVisible = () => palette.TotalIconCount > (palette.MaxIconRowOffset * palette.Columns); + scrollDown.IsDisabled = () => !palette.CanScrollDown; + } + + var scrollUp = widget.GetOrNull("SCROLL_UP_BUTTON"); + + if (scrollUp != null) + { + scrollUp.OnClick = palette.ScrollUp; + scrollUp.IsVisible = () => palette.TotalIconCount > (palette.MaxIconRowOffset * palette.Columns); + scrollUp.IsDisabled = () => !palette.CanScrollUp; + } + + SetMaximumVisibleRows(palette); + } + + static void SetMaximumVisibleRows(ProductionPaletteWidget productionPalette) + { + var screenHeight = Game.Renderer.Resolution.Height; + + // Get height of currently displayed icons + var containerWidget = Ui.Root.GetOrNull("SIDEBAR_PRODUCTION"); + + if (containerWidget == null) + return; + + var sidebarProductionHeight = containerWidget.Bounds.Y; + + // Check if icon heights exceed y resolution + var maxItemsHeight = screenHeight - sidebarProductionHeight; + + productionPalette.MaxIconRowOffset = (maxItemsHeight / productionPalette.IconSize.Y) - 1; } } } diff --git a/mods/ra/chrome.yaml b/mods/ra/chrome.yaml index a09da40123..d8a2278501 100644 --- a/mods/ra/chrome.yaml +++ b/mods/ra/chrome.yaml @@ -5,21 +5,85 @@ sidebar-allies: chrome.png background-supportoverlay: 184,118,64,48 sidebar-button-allies: chrome.png - background: 56,28,28,28 + background: 59,31,22,22 + border-r: 81,31,3,22 + border-l: 56,31,3,22 + border-b: 59,53,22,3 + border-t: 59,28,22,3 + corner-tl: 56,28,3,3 + corner-tr: 81,28,3,3 + corner-bl: 56,53,3,3 + corner-br: 81,53,3,3 sidebar-button-allies-hover: chrome.png - background: 56,0,28,28 + background: 59,3,22,22 + border-r: 81,3,3,22 + border-l: 56,3,3,22 + border-b: 59,25,22,3 + border-t: 59,0,22,3 + corner-tl: 56,0,3,3 + corner-tr: 81,0,3,3 + corner-bl: 56,25,3,3 + corner-br: 81,25,3,3 sidebar-button-allies-pressed: chrome.png - background: 56,28,28,28 + background: 59,31,22,22 + border-r: 81,31,3,22 + border-l: 56,31,3,22 + border-b: 59,53,22,3 + border-t: 59,28,22,3 + corner-tl: 56,28,3,3 + corner-tr: 81,28,3,3 + corner-bl: 56,53,3,3 + corner-br: 81,53,3,3 sidebar-button-allies-highlighted: chrome.png - background: 84,28,28,28 + background: 87,31,22,22 + border-r: 109,31,3,22 + border-l: 84,31,3,22 + border-b: 87,53,22,3 + border-t: 87,28,22,3 + corner-tl: 84,28,3,3 + corner-tr: 109,28,3,3 + corner-bl: 84,53,3,3 + corner-br: 109,53,3,3 sidebar-button-allies-highlighted-hover: chrome.png - background: 84,0,28,28 + background: 87,3,22,22 + border-r: 109,3,3,22 + border-l: 84,3,3,22 + border-b: 87,25,22,3 + border-t: 87,0,22,3 + corner-tl: 84,0,3,3 + corner-tr: 109,0,3,3 + corner-bl: 84,25,3,3 + corner-br: 109,25,3,3 sidebar-button-allies-highlighted-pressed: chrome.png - background: 84,28,28,28 + background: 87,31,22,22 + border-r: 109,31,3,22 + border-l: 84,31,3,22 + border-b: 87,53,22,3 + border-t: 87,28,22,3 + corner-tl: 84,28,3,3 + corner-tr: 109,28,3,3 + corner-bl: 84,53,3,3 + corner-br: 109,53,3,3 sidebar-button-allies-disabled: chrome.png - background: 168,0,28,28 + background: 171,3,22,22 + border-r: 193,3,3,22 + border-l: 168,3,3,22 + border-b: 171,25,22,3 + border-t: 171,0,22,3 + corner-tl: 168,0,3,3 + corner-tr: 193,0,3,3 + corner-bl: 168,25,3,3 + corner-br: 193,25,3,3 sidebar-button-allies-highlighted-disabled: chrome.png - background: 168,0,28,28 + background: 171,3,22,22 + border-r: 193,3,3,22 + border-l: 168,3,3,22 + border-b: 171,25,22,3 + border-t: 171,0,22,3 + corner-tl: 168,0,3,3 + corner-tr: 193,0,3,3 + corner-bl: 168,25,3,3 + corner-br: 193,25,3,3 sidebar-soviet: chrome.png background-top: 274,167,238,290 @@ -28,21 +92,85 @@ sidebar-soviet: chrome.png background-supportoverlay: 249,118,64,48 sidebar-button-soviet: chrome.png - background: 0,28,28,28 + background: 3,31,22,22 + border-r: 25,31,3,22 + border-l: 0,31,3,22 + border-b: 3,53,22,3 + border-t: 3,28,22,3 + corner-tl: 0,28,3,3 + corner-tr: 25,28,3,3 + corner-bl: 0,53,3,3 + corner-br: 25,53,3,3 sidebar-button-soviet-hover: chrome.png - background: 0,0,28,28 + background: 3,3,22,22 + border-r: 25,3,3,22 + border-l: 0,3,3,22 + border-b: 3,25,22,3 + border-t: 3,0,22,3 + corner-tl: 0,0,3,3 + corner-tr: 25,0,3,3 + corner-bl: 0,25,3,3 + corner-br: 25,25,3,3 sidebar-button-soviet-pressed: chrome.png - background: 0,28,28,28 + background: 3,31,22,22 + border-r: 25,31,3,22 + border-l: 0,31,3,22 + border-b: 3,53,22,3 + border-t: 3,28,22,3 + corner-tl: 0,28,3,3 + corner-tr: 25,28,3,3 + corner-bl: 0,53,3,3 + corner-br: 25,53,3,3 sidebar-button-soviet-highlighted: chrome.png - background: 28,28,28,28 + background: 31,31,22,22 + border-r: 53,31,3,22 + border-l: 28,31,3,22 + border-b: 31,53,22,3 + border-t: 31,28,22,3 + corner-tl: 28,28,3,3 + corner-tr: 53,28,3,3 + corner-bl: 28,53,3,3 + corner-br: 53,53,3,3 sidebar-button-soviet-highlighted-hover: chrome.png - background: 28,0,28,28 + background: 31,3,22,22 + border-r: 53,3,3,22 + border-l: 28,3,3,22 + border-b: 31,25,22,3 + border-t: 31,0,22,3 + corner-tl: 25,0,3,3 + corner-tr: 53,0,3,3 + corner-bl: 25,25,3,3 + corner-br: 53,25,3,3 sidebar-button-soviet-highlighted-pressed: chrome.png - background: 28,28,28,28 + background: 31,31,22,22 + border-r: 53,31,3,22 + border-l: 28,31,3,22 + border-b: 31,53,22,3 + border-t: 31,28,22,3 + corner-tl: 25,28,3,3 + corner-tr: 53,28,3,3 + corner-bl: 25,53,3,3 + corner-br: 53,53,3,3 sidebar-button-soviet-disabled: chrome.png - background: 168,0,28,28 + background: 115,3,22,22 + border-r: 137,3,3,22 + border-l: 112,3,3,22 + border-b: 115,25,22,3 + border-t: 115,0,22,3 + corner-tl: 112,0,3,3 + corner-tr: 137,0,3,3 + corner-bl: 112,25,3,3 + corner-br: 137,25,3,3 sidebar-button-soviet-highlighted-disabled: chrome.png - background: 168,0,28,28 + background: 115,3,22,22 + border-r: 137,3,3,22 + border-l: 112,3,3,22 + border-b: 115,25,22,3 + border-t: 115,0,22,3 + corner-tl: 112,0,3,3 + corner-tr: 137,0,3,3 + corner-bl: 112,25,3,3 + corner-br: 137,25,3,3 sidebar-bits: chrome.png production-tooltip-time: 416, 80, 16, 16 diff --git a/mods/ra/chrome/ingame-player.yaml b/mods/ra/chrome/ingame-player.yaml index 552f28c6eb..fe3b6173a8 100644 --- a/mods/ra/chrome/ingame-player.yaml +++ b/mods/ra/chrome/ingame-player.yaml @@ -341,3 +341,33 @@ Container@PLAYER_WIDGETS: X: 6 Y: 6 ImageCollection: production-icons + Button@SCROLL_UP_BUTTON: + Logic: AddRaceSuffixLogic + Y: 186 + Width: 28 + Height: 22 + VisualHeight: 0 + Background: sidebar-button + TooltipText: Scroll up + TooltipContainer: TOOLTIP_CONTAINER + Children: + Image@ICON: + X: 6 + Y: 3 + ImageCollection: scrollbar + ImageName: up_arrow + Button@SCROLL_DOWN_BUTTON: + Logic: AddRaceSuffixLogic + Y: 211 + Width: 28 + Height: 22 + VisualHeight: 0 + Background: sidebar-button + TooltipText: Scroll down + TooltipContainer: TOOLTIP_CONTAINER + Children: + Image@ICON: + X: 6 + Y: 3 + ImageCollection: scrollbar + ImageName: down_arrow \ No newline at end of file