From 45836b25236bbfa85c6f60b1008c03d346d23e29 Mon Sep 17 00:00:00 2001 From: Rodolfo Date: Wed, 4 Mar 2026 12:39:51 -0300 Subject: [PATCH 1/6] Improve controller navigation and responsive layout in menu UI - Integrate filter fields and location dropdown into snappy gamepad navigation. - Add A-button activation for focused components to improve gamepad interaction. - Rebuild clickable component registration so filters/dropdown participate in focus traversal. - Fix inventory neighbor mapping to avoid null reference during menu construction. - Honor one-column chest layout in narrow/split viewports and adjust neighbor links. - Add compact mode for small viewports by reducing bottom panel spacing and control sizes. --- AllChestsMenu/AllChestsMenu.cs | 251 +++++++++++++++++++++++++-------- 1 file changed, 196 insertions(+), 55 deletions(-) diff --git a/AllChestsMenu/AllChestsMenu.cs b/AllChestsMenu/AllChestsMenu.cs index 420bb1ba..5683e594 100644 --- a/AllChestsMenu/AllChestsMenu.cs +++ b/AllChestsMenu/AllChestsMenu.cs @@ -56,7 +56,13 @@ public enum Sort private void CalculateResponsiveLayout() { - int availableWidth = Game1.uiViewport.Width - borderWidth * 4; + int viewportWidth = Game1.graphics?.GraphicsDevice?.Viewport.Width ?? Game1.uiViewport.Width; + int viewportHeight = Game1.graphics?.GraphicsDevice?.Viewport.Height ?? Game1.uiViewport.Height; + effectiveViewportWidth = Math.Min(Game1.uiViewport.Width, viewportWidth); + effectiveViewportHeight = Math.Min(Game1.uiViewport.Height, viewportHeight); + compactLayout = effectiveViewportWidth < 1250 || effectiveViewportHeight < 820; + + int availableWidth = effectiveViewportWidth - borderWidth * 4; int minChestWidth = 64 * 12 + 32; // Largura mínima para um baú // Determinar número de colunas baseado no espaço disponível @@ -64,8 +70,8 @@ private void CalculateResponsiveLayout() // Recalcular dimensões (Adiciona 64px de largura extra para as bordas laterais) int chestAreaWidth = numberOfChestColumns * (64 * 12 + 64) + borderWidth * 2 + 64; - width = Math.Min(chestAreaWidth, Game1.uiViewport.Width - borderWidth * 2); - xPositionOnScreen = (Game1.uiViewport.Width - width) / 2; + width = Math.Min(chestAreaWidth, effectiveViewportWidth - borderWidth * 2); + xPositionOnScreen = (effectiveViewportWidth - width) / 2; } public List allChestDataList = new(); @@ -151,12 +157,17 @@ private void CalculateResponsiveLayout() private bool gamepadShift = false; - public int clickpos; + public int clickpos; public bool draggingScrollbar; public int scrollbarWidth; private bool scrolling; private int totalHeight; public int bottomAreaHeight; // To store for drawing + private int locationOptionBaseId => 6 * ccMagnitude; + private int effectiveViewportWidth; + private int effectiveViewportHeight; + private bool compactLayout; + private float trashLidScale = 4f; public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game1.uiViewport.Height + borderWidth * 2 + 64, false) { @@ -164,22 +175,26 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game CalculateResponsiveLayout(); currentSort = ModEntry.Config.CurrentSort; + int actionButtonSize = compactLayout ? 48 : 64; + int actionButtonGap = compactLayout ? 6 : 8; + int actionButtonScale = compactLayout ? 3 : 4; + trashLidScale = compactLayout ? 3f : 4f; // Layout claro: área de baús (topo) e área de inventário/filtros (fundo) inventoryHeight = 64 * 3; // Altura do inventário // Filters stack height: 4 filtros + labels + espaçamento - int filterStackHeight = 380; // Aumentado para acomodar labels e espaçamento - // Added 64px gap for breathing room at the top divider (increased from 32px to raise the border higher) - bottomAreaHeight = Math.Max(inventoryHeight + 64, filterStackHeight) + borderWidth + 64; - cutoff = Game1.uiViewport.Height - bottomAreaHeight; // Y onde a área de baús termina + int filterStackHeight = compactLayout ? 320 : 380; + int verticalPadding = compactLayout ? 40 : 64; + bottomAreaHeight = Math.Max(inventoryHeight + verticalPadding, filterStackHeight) + borderWidth + verticalPadding; + cutoff = effectiveViewportHeight - bottomAreaHeight; // Y onde a área de baús termina // Centraliza o conjunto (inventário + botões) empurrado pra direita se necessário - int smallFilterWidth = 120; + int smallFilterWidth = compactLayout ? 104 : 120; int filterXStart = xPositionOnScreen + borderWidth + 24; int filtersTotalWidth = filterXStart + smallFilterWidth + 16; - int availableVerticalSpace = Game1.uiViewport.Height - cutoff; + int availableVerticalSpace = effectiveViewportHeight - cutoff; int inventoryY = cutoff + (availableVerticalSpace - inventoryHeight) / 2; widgetText = new string[]{ ModEntry.SHelper.Translation.Get("open"), @@ -199,8 +214,8 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game int capacity = rows * columns; // Centraliza o conjunto (inventário + botões) - int totalBottomWidth = 64 * columns + 32 + (64 * 6); // inventario + espaco + botoes e lixeira - int startX = (Game1.uiViewport.Width - totalBottomWidth) / 2; + int totalBottomWidth = 64 * columns + 32 + (actionButtonSize * 5) + (actionButtonSize + 16); // inventario + espaco + botoes e lixeira + int startX = (effectiveViewportWidth - totalBottomWidth) / 2; startX += 40; // Deslocar para a direita para balancear com os filtros na esqueda if (startX < filtersTotalWidth) startX = filtersTotalWidth; @@ -213,14 +228,14 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game capacity, rows); SetPlayerInventoryNeighbours(); - organizeButton = new ClickableTextureComponent("", new Rectangle(playerInventoryMenu.xPositionOnScreen + playerInventoryMenu.width + 32, playerInventoryMenu.yPositionOnScreen - 16, 64, 64), "", Game1.content.LoadString("Strings\\UI:ItemGrab_Organize"), Game1.mouseCursors, new Rectangle(162, 440, 16, 16), 4f, false) + organizeButton = new ClickableTextureComponent("", new Rectangle(playerInventoryMenu.xPositionOnScreen + playerInventoryMenu.width + 32, playerInventoryMenu.yPositionOnScreen - (compactLayout ? 8 : 16), actionButtonSize, actionButtonSize), "", Game1.content.LoadString("Strings\\UI:ItemGrab_Organize"), Game1.mouseCursors, new Rectangle(162, 440, 16, 16), actionButtonScale, false) { myID = 4 * ccMagnitude, downNeighborID = 4 * ccMagnitude + 1, leftNeighborID = 11, rightNeighborID = 4 * ccMagnitude + 1 }; - storeAlikeButton = new ClickableTextureComponent("", new Rectangle(organizeButton.bounds.X + 64 + 8, playerInventoryMenu.yPositionOnScreen - 16, 64, 64), "", Game1.content.LoadString("Strings\\UI:ItemGrab_FillStacks"), Game1.mouseCursors, new Rectangle(103, 469, 16, 16), 4f, false) + storeAlikeButton = new ClickableTextureComponent("", new Rectangle(organizeButton.bounds.X + actionButtonSize + actionButtonGap, organizeButton.bounds.Y, actionButtonSize, actionButtonSize), "", Game1.content.LoadString("Strings\\UI:ItemGrab_FillStacks"), Game1.mouseCursors, new Rectangle(103, 469, 16, 16), actionButtonScale, false) { myID = 4 * ccMagnitude + 1, downNeighborID = 4 * ccMagnitude + 2, @@ -232,7 +247,7 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game // Consolidate button - merges duplicate items across chests string consolidateText = ModEntry.SHelper.Translation.Get("consolidate"); string consolidateTooltip = ModEntry.SHelper.Translation.Get("consolidate-tooltip"); - consolidateButton = new ClickableTextureComponent("", new Rectangle(storeAlikeButton.bounds.X + 64 + 8, playerInventoryMenu.yPositionOnScreen - 16, 64, 64), "", consolidateTooltip, Game1.mouseCursors, new Rectangle(257, 284, 16, 16), 4f, false) + consolidateButton = new ClickableTextureComponent("", new Rectangle(storeAlikeButton.bounds.X + actionButtonSize + actionButtonGap, organizeButton.bounds.Y, actionButtonSize, actionButtonSize), "", consolidateTooltip, Game1.mouseCursors, new Rectangle(257, 284, 16, 16), actionButtonScale, false) { myID = 4 * ccMagnitude + 2, downNeighborID = 4 * ccMagnitude + 3, @@ -244,7 +259,7 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game // Sort all button - sorts all items in all chests string sortAllText = ModEntry.SHelper.Translation.Get("sort-all"); string sortAllTooltip = ModEntry.SHelper.Translation.Get("sort-all-tooltip"); - sortAllButton = new ClickableTextureComponent("", new Rectangle(consolidateButton.bounds.X + 64 + 8, playerInventoryMenu.yPositionOnScreen - 16, 64, 64), "", sortAllTooltip, Game1.mouseCursors, new Rectangle(162, 440, 16, 16), 4f, false) + sortAllButton = new ClickableTextureComponent("", new Rectangle(consolidateButton.bounds.X + actionButtonSize + actionButtonGap, organizeButton.bounds.Y, actionButtonSize, actionButtonSize), "", sortAllTooltip, Game1.mouseCursors, new Rectangle(162, 440, 16, 16), actionButtonScale, false) { myID = 4 * ccMagnitude + 3, downNeighborID = 4 * ccMagnitude + 4, @@ -254,7 +269,9 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game }; // Update trashCan position - moved right of the buttons, aligned vertically - trashCan = new ClickableTextureComponent(new Rectangle(sortAllButton.bounds.X + 64 + 16, playerInventoryMenu.yPositionOnScreen + 28, 64, 104), Game1.mouseCursors, new Rectangle(564 + Game1.player.trashCanLevel * 18, 102, 18, 26), 4f, false) + int trashWidth = compactLayout ? 48 : 64; + int trashHeight = compactLayout ? 78 : 104; + trashCan = new ClickableTextureComponent(new Rectangle(sortAllButton.bounds.X + actionButtonSize + 16, playerInventoryMenu.yPositionOnScreen + (compactLayout ? 18 : 28), trashWidth, trashHeight), Game1.mouseCursors, new Rectangle(564 + Game1.player.trashCanLevel * 18, 102, 18, 26), actionButtonScale, false) { myID = 4 * ccMagnitude + 4, leftNeighborID = 4 * ccMagnitude + 3, @@ -288,10 +305,13 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game int row = i % 2; string name = s[i]; int idx = 5 * ccMagnitude; + int sortXStep = compactLayout ? 48 : 64; + int sortYStart = compactLayout ? 84 : 104; + int sortYStep = compactLayout ? 32 : 40; + int sortSize = compactLayout ? 26 : 32; sortNames[name] = ModEntry.SHelper.Translation.Get("sort-" + name); - // Moved down to Y + 104 instead of 92 (and back to 64 spacing for centering relative to columns) - sortCCList.Add(new ClickableComponent(new Rectangle(organizeButton.bounds.X + (i / 2) * 64, organizeButton.bounds.Y + 104 + row * 40, 32, 32), name, name) + sortCCList.Add(new ClickableComponent(new Rectangle(organizeButton.bounds.X + (i / 2) * sortXStep, organizeButton.bounds.Y + sortYStart + row * sortYStep, sortSize, sortSize), name, name) { myID = idx + i, leftNeighborID = i > 2 ? idx + i - 2: 4 * ccMagnitude + 1, @@ -302,8 +322,8 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game } // Initialize new filter fields - layout vertical na esquerda - int filterYStart = Game1.uiViewport.Height - bottomAreaHeight + borderWidth + 64 + 16; - int filterSpacing = 84; // Espaço vertical entre cada filtro (aumentado para acomodar labels) + int filterYStart = effectiveViewportHeight - bottomAreaHeight + borderWidth + (compactLayout ? 52 : 64) + 16; + int filterSpacing = compactLayout ? 72 : 84; // Chest Label filter chestLabelText = new TextBox(Game1.content.Load("LooseSprites\\textBox"), null, Game1.smallFont, Game1.textColor) @@ -394,12 +414,12 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game int clearBtnX = filterXStart + smallFilterWidth + 20; clearFiltersButton = new ClickableTextureComponent( "", - new Rectangle(clearBtnX, clearBtnY, 48, 48), + new Rectangle(clearBtnX, clearBtnY, compactLayout ? 40 : 48, compactLayout ? 40 : 48), "", ModEntry.SHelper.Translation.Get("clear-filters"), Game1.mouseCursors, new Rectangle(337, 494, 12, 12), // X icon - 4f, + compactLayout ? 3.3f : 4f, false ) { @@ -428,8 +448,13 @@ private void PopulateMenus(bool resetAllChestDataList = false) { string loc = uniqueLocations[i].locationDisplayName; string displayLoc = loc; - locationOptionsCCList.Add(new ClickableComponent(new Rectangle(locationDropdownCC.bounds.X, locationDropdownCC.bounds.Bottom + i * 36, Math.Max(locationDropdownCC.bounds.Width, 300), 36), loc, displayLoc)); + locationOptionsCCList.Add(new ClickableComponent(new Rectangle(locationDropdownCC.bounds.X, locationDropdownCC.bounds.Bottom + i * 36, Math.Max(locationDropdownCC.bounds.Width, 300), 36), loc, displayLoc) + { + myID = locationOptionBaseId + i + }); } + UpdateLocationOptionBounds(); + RebuildControllerNavigation(); ResetChestList(); } @@ -649,7 +674,7 @@ private void ResetChestList() // Location grouping check if (lastLocation != null && chestData.locationDisplayName != lastLocation) { - if (even) // Currently next chest would be on the right. We want to start on the left. + if (numberOfChestColumns > 1 && even) // In 2-column mode, finish the partial row before starting a new location group { var prevChest = chestDataList[chestDataList.Count - 1]; rowsAlready += Math.Max(!prevChest.collapsed ? Math.Max(prevChest.menu.rows, 3) : 0, oddRows); @@ -669,23 +694,32 @@ private void ResetChestList() int columns = 12; int rows = (int)Math.Ceiling(chestData.chest.GetActualCapacity() / (float)columns); - chestData.menu = new ChestMenu(xPositionOnScreen + borderWidth + 64 + (even ? (64 * 13) : 0), yPositionOnScreen - scrolled * scrollInterval + borderWidth + 64 + 64 * rowsAlready + 96 * (1 + menusAlready) + 48 * locationGaps, false, chestData.chest.Items, null, chestData.chest.GetActualCapacity(), rows); + int columnIndex = (numberOfChestColumns > 1 && even) ? 1 : 0; + chestData.menu = new ChestMenu(xPositionOnScreen + borderWidth + 64 + columnIndex * (64 * 13), yPositionOnScreen - scrolled * scrollInterval + borderWidth + 64 + 64 * rowsAlready + 96 * (1 + menusAlready) + 48 * locationGaps, false, chestData.chest.Items, null, chestData.chest.GetActualCapacity(), rows); if (chestData.chest is ShippingBinChest && !ModEntry.Config.UnrestrictedShippingBin) { chestData.menu.highlightMethod = (Item i) => { return i == Game1.getFarm().lastItemShipped; }; } - if (!even) + if (numberOfChestColumns == 1) { - oddRows = !chestData.collapsed ? Math.Max(chestData.menu.rows, 3) : 0; + rowsAlready += !chestData.collapsed ? Math.Max(chestData.menu.rows, 3) : 0; + menusAlready++; } else { - rowsAlready += Math.Max(!chestData.collapsed ? Math.Max(chestData.menu.rows, 3) : 0, oddRows); - menusAlready++; + if (!even) + { + oddRows = !chestData.collapsed ? Math.Max(chestData.menu.rows, 3) : 0; + } + else + { + rowsAlready += Math.Max(!chestData.collapsed ? Math.Max(chestData.menu.rows, 3) : 0, oddRows); + menusAlready++; + } + even = !even; } - even = !even; if (chestDataList.Count >= 1000) { ModEntry.SMonitor.Log("More than 1000 chests. Giving up while we're ahead.", LogLevel.Warn); @@ -723,7 +757,7 @@ private void ResetChestList() ChestData chestData = chestDataList[i]; int count = chestData.menu.inventory.Count; int lastCount = i > 0 ? chestDataList[i - 1].menu.inventory.Count : 0; - // int nextCount = i < chestDataList.Count - 1 ? chestDataList[i + 1].menu.inventory.Count : 0; + int nextCount = i < chestDataList.Count - 1 ? chestDataList[i + 1].menu.inventory.Count : 0; int lastLastCount = i > 1 ? chestDataList[i - 2].menu.inventory.Count : 0; int nextNextCount = i < chestDataList.Count - 2 ? chestDataList[i + 2].menu.inventory.Count : 0; int index = ccMagnitude + i * ccMagnitude / 1000; @@ -732,13 +766,14 @@ private void ResetChestList() int lastLastIndex = ccMagnitude + (i - 2) * ccMagnitude / 1000; int nextNextIndex = ccMagnitude + (i + 2) * ccMagnitude / 1000; - ClickableComponent headerCC = new ClickableComponent(new Rectangle(chestData.menu.xPositionOnScreen, chestData.menu.yPositionOnScreen - 48, (width - borderWidth * 2 - 128) / 2, 48), chestData.index.ToString(), chestData.label) + int headerWidth = numberOfChestColumns == 1 ? width - borderWidth * 2 - 128 : (width - borderWidth * 2 - 128) / 2; + ClickableComponent headerCC = new ClickableComponent(new Rectangle(chestData.menu.xPositionOnScreen, chestData.menu.yPositionOnScreen - 48, headerWidth, 48), chestData.index.ToString(), chestData.label) { myID = index + 5000, downNeighborID = index + 0, - rightNeighborID = (i % 2 == 0 && i + 1 < chestDataList.Count) ? ccMagnitude + (i + 1) * ccMagnitude / 1000 + 5000 : -1, - leftNeighborID = (i % 2 == 1) ? ccMagnitude + (i - 1) * ccMagnitude / 1000 + 5000 : -1, - upNeighborID = (i > 1) ? ccMagnitude + (i - 2) * ccMagnitude / 1000 + 5000 : -1 + rightNeighborID = numberOfChestColumns == 1 ? -1 : ((i % 2 == 0 && i + 1 < chestDataList.Count) ? ccMagnitude + (i + 1) * ccMagnitude / 1000 + 5000 : -1), + leftNeighborID = numberOfChestColumns == 1 ? -1 : ((i % 2 == 1) ? ccMagnitude + (i - 1) * ccMagnitude / 1000 + 5000 : -1), + upNeighborID = numberOfChestColumns == 1 ? (i > 0 ? ccMagnitude + (i - 1) * ccMagnitude / 1000 + 5000 : -1) : ((i > 1) ? ccMagnitude + (i - 2) * ccMagnitude / 1000 + 5000 : -1) }; chestHeaders.Add(headerCC); @@ -813,7 +848,11 @@ private void ResetChestList() } if (j >= count - columns) { - if (i < chestDataList.Count - 2) + if (numberOfChestColumns == 1) + { + chestData.menu.inventory[j].downNeighborID = i < chestDataList.Count - 1 ? nextIndex + 5000 : -1; + } + else if (i < chestDataList.Count - 2) { chestData.menu.inventory[j].downNeighborID = nextNextIndex + 5000; } @@ -848,10 +887,10 @@ private void ResetChestList() ClickableTextureComponent cc = new("", GetWidgetRectangle(chestData, j), "", widgetText[k], Game1.mouseCursors, widgetSources[k], 32f / widgetSources[k].Width, false) { myID = index + count + k, - downNeighborID = k < widgetTextLength - 1 ? index + count + k + 5 : (i < chestDataList.Count - 2 ? nextNextIndex + nextNextCount : -1), + downNeighborID = k < widgetTextLength - 1 ? index + count + k + 5 : (numberOfChestColumns == 1 ? (i < chestDataList.Count - 1 ? nextIndex + nextCount : -1) : (i < chestDataList.Count - 2 ? nextNextIndex + nextNextCount : -1)), leftNeighborID = index + 11 + LeftRowIndex * columns - ((LeftRowIndex == (int)Math.Ceiling((double)count / columns) - 1) ? columns - (count % columns) : 0), - rightNeighborID = i < chestDataList.Count - 1 ? nextIndex + RightRowIndex * columns : -1, - upNeighborID = k > 0 ? index + count + k - 5: (i > 1 ? lastLastIndex + lastLastCount + widgetTextLength - 1: -1) + rightNeighborID = numberOfChestColumns == 1 ? -1 : (i < chestDataList.Count - 1 ? nextIndex + RightRowIndex * columns : -1), + upNeighborID = k > 0 ? index + count + k - 5: (numberOfChestColumns == 1 ? (i > 0 ? lastIndex + lastCount + widgetTextLength - 1 : -1) : (i > 1 ? lastLastIndex + lastLastCount + widgetTextLength - 1: -1)) }; chestData.inventoryButtons.Add(cc); @@ -867,10 +906,10 @@ private void ResetChestList() ClickableTextureComponent cc = new("", GetWidgetRectangle(chestData, j), "", widgetText[j], Game1.mouseCursors, widgetSources[j], 32f / widgetSources[j].Width, false) { myID = index + count + j, - downNeighborID = j < widgetText.Length - 1 ? index + count + j + 1 : (i < chestDataList.Count - 2 ? nextNextIndex + nextNextCount : -1), + downNeighborID = j < widgetText.Length - 1 ? index + count + j + 1 : (numberOfChestColumns == 1 ? (i < chestDataList.Count - 1 ? nextIndex + nextCount : -1) : (i < chestDataList.Count - 2 ? nextNextIndex + nextNextCount : -1)), leftNeighborID = index + 11 + LeftRowIndex * columns - ((LeftRowIndex == (int)Math.Ceiling((double)count / columns) - 1) ? columns - (count % columns) : 0), - rightNeighborID = i < chestDataList.Count - 1 ? nextIndex + RightRowIndex * columns : -1, - upNeighborID = j > 0 ? index + count + j - 1: (i > 1 ? lastLastIndex + lastLastCount + widgetText.Length - 1: -1) + rightNeighborID = numberOfChestColumns == 1 ? -1 : (i < chestDataList.Count - 1 ? nextIndex + RightRowIndex * columns : -1), + upNeighborID = j > 0 ? index + count + j - 1: (numberOfChestColumns == 1 ? (i > 0 ? lastIndex + lastCount + widgetText.Length - 1 : -1) : (i > 1 ? lastLastIndex + lastLastCount + widgetText.Length - 1: -1)) }; chestData.inventoryButtons.Add(cc); @@ -1059,7 +1098,9 @@ public override void draw(SpriteBatch b) consolidateButton.draw(b); sortAllButton.draw(b); clearFiltersButton.draw(b); - b.Draw(Game1.mouseCursors, new Vector2(trashCan.bounds.X + 60, trashCan.bounds.Y + 40), new Rectangle?(new Rectangle(564 + Game1.player.trashCanLevel * 18, 129, 18, 10)), Color.White, trashCanLidRotation, new Vector2(16f, 10f), 4f, SpriteEffects.None, 0.86f); + float lidOffsetX = compactLayout ? 44f : 60f; + float lidOffsetY = compactLayout ? 30f : 40f; + b.Draw(Game1.mouseCursors, new Vector2(trashCan.bounds.X + lidOffsetX, trashCan.bounds.Y + lidOffsetY), new Rectangle?(new Rectangle(564 + Game1.player.trashCanLevel * 18, 129, 18, 10)), Color.White, trashCanLidRotation, new Vector2(16f, 10f), trashLidScale, SpriteEffects.None, 0.86f); Game1.spriteBatch.Draw(Game1.menuTexture, new Rectangle(xPositionOnScreen + 16, -4, 24, 16), new Rectangle(16, 16, 24, 16), Color.White); Game1.spriteBatch.Draw(Game1.menuTexture, new Rectangle(xPositionOnScreen + width - 32, -4, 16, 16), new Rectangle(225, 16, 16, 16), Color.White); Game1.spriteBatch.Draw(Game1.menuTexture, new Rectangle(xPositionOnScreen + 40, -4, width - 72, 16), new Rectangle(40, 16, 1, 16), Color.White); @@ -1090,18 +1131,13 @@ public override void draw(SpriteBatch b) int listHeight = locationOptionsCCList.Count * 36; if (listHeight > 0) { - int startY = locationDropdownCC.bounds.Bottom; - if (startY + listHeight > Game1.uiViewport.Height) - { - startY = locationDropdownCC.bounds.Y - listHeight; - } + UpdateLocationOptionBounds(); + int startY = locationOptionsCCList[0].bounds.Y - 8; int dropdownWidth = Math.Max(locationDropdownCC.bounds.Width, 300); IClickableMenu.drawTextureBox(b, Game1.menuTexture, new Rectangle(0, 256, 60, 60), locationDropdownCC.bounds.X, startY, dropdownWidth, listHeight + 16, Color.White * 0.95f, 1f, false); for (int i = 0; i < locationOptionsCCList.Count; i++) { var cc = locationOptionsCCList[i]; - // Update bounds real Y for clicking - cc.bounds.Y = startY + 8 + i * 36; if (cc.containsPoint(Game1.getMouseX(), Game1.getMouseY())) { b.Draw(Game1.staminaRect, cc.bounds, Color.Wheat); @@ -1147,7 +1183,7 @@ public override void releaseLeftClick(int x, int y) base.releaseLeftClick(x, y); scrolling = false; } - public override void receiveLeftClick(int x, int y, bool playSound = true) + public override void receiveLeftClick(int x, int y, bool playSound = true) { if (locationDropdownOpen) { @@ -1163,10 +1199,14 @@ public override void receiveLeftClick(int x, int y, bool playSound = true) ModEntry.SHelper.WriteConfig(ModEntry.Config); Game1.playSound("drumkit6"); PopulateMenus(false); + RebuildControllerNavigation(); + populateClickableComponentList(); return; } } locationDropdownOpen = false; + RebuildControllerNavigation(); + populateClickableComponentList(); if (locationDropdownCC.bounds.Contains(x, y)) { Game1.playSound("drumkit6"); @@ -1177,6 +1217,14 @@ public override void receiveLeftClick(int x, int y, bool playSound = true) if (locationDropdownCC.bounds.Contains(x, y)) { locationDropdownOpen = true; + UpdateLocationOptionBounds(); + RebuildControllerNavigation(); + populateClickableComponentList(); + if (Game1.options.snappyMenus && Game1.options.gamepadControls && locationOptionsCCList.Count > 0) + { + currentlySnappedComponent = locationOptionsCCList[0]; + snapCursorToCurrentSnappedComponent(); + } Game1.playSound("shwip"); return; } @@ -1504,9 +1552,46 @@ public override void receiveGamePadButton(Buttons b) gamepadShift = false; return; } + + if (b == Buttons.A && currentlySnappedComponent is not null) + { + receiveLeftClick(currentlySnappedComponent.bounds.Center.X, currentlySnappedComponent.bounds.Center.Y); + return; + } base.receiveGamePadButton(b); } + public override void populateClickableComponentList() + { + base.populateClickableComponentList(); + allClickableComponents = new List(); + + allClickableComponents.AddRange(inventoryCells); + allClickableComponents.AddRange(inventoryButtons); + allClickableComponents.AddRange(chestHeaders); + allClickableComponents.AddRange(playerInventoryMenu.inventory); + allClickableComponents.AddRange(sortCCList); + allClickableComponents.Add(organizeButton); + allClickableComponents.Add(storeAlikeButton); + allClickableComponents.Add(consolidateButton); + allClickableComponents.Add(sortAllButton); + allClickableComponents.Add(trashCan); + allClickableComponents.Add(clearFiltersButton); + allClickableComponents.Add(chestLabelCC); + allClickableComponents.Add(itemNameCC); + allClickableComponents.Add(itemDescCC); + allClickableComponents.Add(locationDropdownCC); + + if (locationDropdownOpen) + allClickableComponents.AddRange(locationOptionsCCList); + + if (renamingChest is not null) + { + allClickableComponents.Add(renameBoxCC); + allClickableComponents.Add(okButton); + } + } + public override void setUpForGamePadMode() { base.setUpForGamePadMode(); @@ -2266,23 +2351,79 @@ static int CompareLabels(string labelA, string labelB) private void SetPlayerInventoryNeighbours() { + int leftTop = chestLabelCC?.myID ?? 2 * ccMagnitude; + int leftMiddle = itemNameCC?.myID ?? 2 * ccMagnitude; + int leftBottom = itemDescCC?.myID ?? 2 * ccMagnitude; + if (playerInventoryMenu.inventory.Count >= 12) { - playerInventoryMenu.inventory[0].leftNeighborID = 2 * ccMagnitude; + playerInventoryMenu.inventory[0].leftNeighborID = leftTop; playerInventoryMenu.inventory[11].rightNeighborID = 4 * ccMagnitude; if (playerInventoryMenu.inventory.Count >= 24) { - playerInventoryMenu.inventory[12].leftNeighborID = 2 * ccMagnitude; + playerInventoryMenu.inventory[12].leftNeighborID = leftMiddle; playerInventoryMenu.inventory[23].rightNeighborID = 4 * ccMagnitude + 1; if (playerInventoryMenu.inventory.Count >= 36) { - playerInventoryMenu.inventory[24].leftNeighborID = 2 * ccMagnitude; + playerInventoryMenu.inventory[24].leftNeighborID = leftBottom; playerInventoryMenu.inventory[35].rightNeighborID = 4 * ccMagnitude + 1; } } } } + private void RebuildControllerNavigation() + { + SetPlayerInventoryNeighbours(); + + chestLabelCC.upNeighborID = -1; + chestLabelCC.downNeighborID = itemNameCC.myID; + chestLabelCC.rightNeighborID = playerInventoryMenu.inventory.Count > 0 ? playerInventoryMenu.inventory[0].myID : -1; + + itemNameCC.upNeighborID = chestLabelCC.myID; + itemNameCC.downNeighborID = itemDescCC.myID; + itemNameCC.rightNeighborID = playerInventoryMenu.inventory.Count > 12 ? playerInventoryMenu.inventory[12].myID : chestLabelCC.rightNeighborID; + + itemDescCC.upNeighborID = itemNameCC.myID; + itemDescCC.downNeighborID = locationDropdownCC.myID; + itemDescCC.rightNeighborID = playerInventoryMenu.inventory.Count > 24 ? playerInventoryMenu.inventory[24].myID : chestLabelCC.rightNeighborID; + + locationDropdownCC.upNeighborID = itemDescCC.myID; + locationDropdownCC.downNeighborID = (locationDropdownOpen && locationOptionsCCList.Count > 0) ? locationOptionsCCList[0].myID : clearFiltersButton.myID; + locationDropdownCC.rightNeighborID = itemDescCC.rightNeighborID; + + clearFiltersButton.leftNeighborID = locationDropdownCC.myID; + clearFiltersButton.rightNeighborID = chestLabelCC.rightNeighborID; + clearFiltersButton.upNeighborID = locationDropdownCC.myID; + + for (int i = 0; i < locationOptionsCCList.Count; i++) + { + ClickableComponent cc = locationOptionsCCList[i]; + cc.upNeighborID = i == 0 ? locationDropdownCC.myID : locationOptionsCCList[i - 1].myID; + cc.downNeighborID = i == locationOptionsCCList.Count - 1 ? clearFiltersButton.myID : locationOptionsCCList[i + 1].myID; + cc.leftNeighborID = locationDropdownCC.myID; + cc.rightNeighborID = locationDropdownCC.rightNeighborID; + } + } + + private void UpdateLocationOptionBounds() + { + int listHeight = locationOptionsCCList.Count * 36; + if (listHeight <= 0) + return; + + int startY = locationDropdownCC.bounds.Bottom; + if (startY + listHeight > Game1.uiViewport.Height) + { + startY = locationDropdownCC.bounds.Y - listHeight; + } + + for (int i = 0; i < locationOptionsCCList.Count; i++) + { + locationOptionsCCList[i].bounds.Y = startY + 8 + i * 36; + } + } + public virtual void DropHeldItem() { if (heldItem is not null) From 7e8ecb04095b5db3ca4cfefac15d4dcf900a3818 Mon Sep 17 00:00:00 2001 From: Rodolfo Date: Wed, 4 Mar 2026 12:40:18 -0300 Subject: [PATCH 2/6] Split keyboard and controller open-menu settings - Add independent keyboard/controller toggles for requiring modifiers when opening the menu. - Add dedicated keyboard and controller modifier bindings used only for opening. - Keep transfer modifier key separate from open-menu modifiers. - Update Input.ButtonPressed logic to evaluate keyboard and controller open conditions independently. - Improve legacy config migration from old MenuKey/ModToOpen fields and persist migrated values. --- AllChestsMenu/ModConfig.cs | 8 ++- AllChestsMenu/ModEntry.cs | 137 ++++++++++++++++++++++++++++++++----- 2 files changed, 127 insertions(+), 18 deletions(-) diff --git a/AllChestsMenu/ModConfig.cs b/AllChestsMenu/ModConfig.cs index fdfd0321..b5c24031 100644 --- a/AllChestsMenu/ModConfig.cs +++ b/AllChestsMenu/ModConfig.cs @@ -26,9 +26,15 @@ public class ModConfig public bool IncludeAutoGrabbers { get; set; } = true; public AllChestsMenu.Sort CurrentSort { get; set; } = AllChestsMenu.Sort.NA; public string SecondarySortingPriority { get; set; } = "Y"; + public bool KeyboardRequireModifierToOpen { get; set; } = false; + public bool ControllerRequireModifierToOpen { get; set; } = false; + public SButton KeyboardOpenModifierKey { get; set; } = SButton.LeftShift; + public SButton ControllerOpenModifierButton { get; set; } = SButton.LeftTrigger; public SButton ModKey { get; set; } = SButton.LeftShift; public SButton ModKey2 { get; set; } = SButton.LeftControl; public SButton SwitchButton { get; set; } = SButton.ControllerBack; - public SButton MenuKey { get; set; } = SButton.F2; + public SButton KeyboardMenuKey { get; set; } = SButton.F2; + public SButton ControllerMenuButton { get; set; } = SButton.None; + public SButton MenuKey { get; set; } = SButton.None; } } diff --git a/AllChestsMenu/ModEntry.cs b/AllChestsMenu/ModEntry.cs index 406b4a0d..f164ed69 100644 --- a/AllChestsMenu/ModEntry.cs +++ b/AllChestsMenu/ModEntry.cs @@ -20,6 +20,7 @@ public partial class ModEntry : Mod public override void Entry(IModHelper helper) { Config = Helper.ReadConfig(); + MigrateLegacyConfig(); context = this; SMonitor = Monitor; @@ -53,12 +54,70 @@ public void Input_ButtonPressed(object sender, ButtonPressedEventArgs e) SHelper.Input.Suppress(e.Button); } } - if (e.Button == Config.MenuKey && (Config.ModKey == SButton.None || !Config.ModToOpen || Helper.Input.IsDown(Config.ModKey))) + bool keyboardOpen = e.Button == Config.KeyboardMenuKey + && (!Config.KeyboardRequireModifierToOpen || Config.KeyboardOpenModifierKey == SButton.None || Helper.Input.IsDown(Config.KeyboardOpenModifierKey)); + bool controllerOpen = e.Button == Config.ControllerMenuButton + && (!Config.ControllerRequireModifierToOpen || Config.ControllerOpenModifierButton == SButton.None || Helper.Input.IsDown(Config.ControllerOpenModifierButton)); + if (keyboardOpen || controllerOpen) { OpenMenu(); } } + private void MigrateLegacyConfig() + { + bool changed = false; + + if (Config.MenuKey != SButton.None) + { + if (Config.MenuKey.ToString().StartsWith("Controller")) + { + if (Config.ControllerMenuButton == SButton.None) + { + Config.ControllerMenuButton = Config.MenuKey; + changed = true; + } + } + else + { + if (Config.KeyboardMenuKey == SButton.None || Config.KeyboardMenuKey == SButton.F2) + { + Config.KeyboardMenuKey = Config.MenuKey; + changed = true; + } + } + } + + if (Config.ModToOpen) + { + if (!Config.KeyboardRequireModifierToOpen) + { + Config.KeyboardRequireModifierToOpen = true; + changed = true; + } + if (!Config.ControllerRequireModifierToOpen) + { + Config.ControllerRequireModifierToOpen = true; + changed = true; + } + } + + if (Config.KeyboardOpenModifierKey == SButton.LeftShift && Config.ModKey != SButton.LeftShift) + { + Config.KeyboardOpenModifierKey = Config.ModKey; + changed = true; + } + + if (Config.MenuKey != SButton.None) + { + Config.MenuKey = SButton.None; + changed = true; + } + + if (changed) + Helper.WriteConfig(Config); + } + public void GameLoop_GameLaunched(object sender, GameLaunchedEventArgs e) { // Get Mobile Phone's API @@ -195,34 +254,40 @@ public void GameLoop_GameLaunched(object sender, GameLaunchedEventArgs e) tooltip: () => SHelper.Translation.Get("GMCM.Section.Controls.Desc") ); - gmcm.AddKeybind( + gmcm.AddSectionTitle( mod: ModManifest, - name: () => SHelper.Translation.Get("GMCM.MenuKey.Name"), - tooltip: () => SHelper.Translation.Get("GMCM.MenuKey.Tooltip"), - getValue: () => Config.MenuKey, - setValue: value => Config.MenuKey = value + text: () => SHelper.Translation.Get("GMCM.Section.KeyboardControls.Name"), + tooltip: () => SHelper.Translation.Get("GMCM.Section.KeyboardControls.Desc") ); gmcm.AddBoolOption( mod: ModManifest, - name: () => SHelper.Translation.Get("GMCM.ModToOpen.Name"), - tooltip: () => SHelper.Translation.Get("GMCM.ModToOpen.Tooltip"), - getValue: () => Config.ModToOpen, - setValue: value => Config.ModToOpen = value + name: () => SHelper.Translation.Get("GMCM.KeyboardRequireModifierToOpen.Name"), + tooltip: () => SHelper.Translation.Get("GMCM.KeyboardRequireModifierToOpen.Tooltip"), + getValue: () => Config.KeyboardRequireModifierToOpen, + setValue: value => Config.KeyboardRequireModifierToOpen = value ); - gmcm.AddBoolOption( + gmcm.AddKeybind( mod: ModManifest, - name: () => "Enable Controller Keyboard", - tooltip: () => "Opens an on-screen keyboard when selecting a text input using a controller.", - getValue: () => Config.EnableControllerKeyboard, - setValue: value => Config.EnableControllerKeyboard = value + name: () => SHelper.Translation.Get("GMCM.KeyboardOpenModifierKey.Name"), + tooltip: () => SHelper.Translation.Get("GMCM.KeyboardOpenModifierKey.Tooltip"), + getValue: () => Config.KeyboardOpenModifierKey, + setValue: value => Config.KeyboardOpenModifierKey = value + ); + + gmcm.AddKeybind( + mod: ModManifest, + name: () => SHelper.Translation.Get("GMCM.KeyboardMenuKey.Name"), + tooltip: () => SHelper.Translation.Get("GMCM.KeyboardMenuKey.Tooltip"), + getValue: () => Config.KeyboardMenuKey, + setValue: value => Config.KeyboardMenuKey = value ); gmcm.AddKeybind( mod: ModManifest, - name: () => SHelper.Translation.Get("GMCM.ModKey.Name"), - tooltip: () => SHelper.Translation.Get("GMCM.ModKey.Tooltip"), + name: () => SHelper.Translation.Get("GMCM.TransferModifierKey.Name"), + tooltip: () => SHelper.Translation.Get("GMCM.TransferModifierKey.Tooltip"), getValue: () => Config.ModKey, setValue: value => Config.ModKey = value ); @@ -235,6 +300,36 @@ public void GameLoop_GameLaunched(object sender, GameLaunchedEventArgs e) setValue: value => Config.ModKey2 = value ); + gmcm.AddSectionTitle( + mod: ModManifest, + text: () => SHelper.Translation.Get("GMCM.Section.ControllerControls.Name"), + tooltip: () => SHelper.Translation.Get("GMCM.Section.ControllerControls.Desc") + ); + + gmcm.AddBoolOption( + mod: ModManifest, + name: () => SHelper.Translation.Get("GMCM.ControllerRequireModifierToOpen.Name"), + tooltip: () => SHelper.Translation.Get("GMCM.ControllerRequireModifierToOpen.Tooltip"), + getValue: () => Config.ControllerRequireModifierToOpen, + setValue: value => Config.ControllerRequireModifierToOpen = value + ); + + gmcm.AddKeybind( + mod: ModManifest, + name: () => SHelper.Translation.Get("GMCM.ControllerOpenModifierButton.Name"), + tooltip: () => SHelper.Translation.Get("GMCM.ControllerOpenModifierButton.Tooltip"), + getValue: () => Config.ControllerOpenModifierButton, + setValue: value => Config.ControllerOpenModifierButton = value + ); + + gmcm.AddKeybind( + mod: ModManifest, + name: () => SHelper.Translation.Get("GMCM.ControllerMenuButton.Name"), + tooltip: () => SHelper.Translation.Get("GMCM.ControllerMenuButton.Tooltip"), + getValue: () => Config.ControllerMenuButton, + setValue: value => Config.ControllerMenuButton = value + ); + gmcm.AddKeybind( mod: ModManifest, name: () => SHelper.Translation.Get("GMCM.SwitchButton.Name"), @@ -242,6 +337,14 @@ public void GameLoop_GameLaunched(object sender, GameLaunchedEventArgs e) getValue: () => Config.SwitchButton, setValue: value => Config.SwitchButton = value ); + + gmcm.AddBoolOption( + mod: ModManifest, + name: () => SHelper.Translation.Get("GMCM.EnableControllerKeyboard.Name"), + tooltip: () => SHelper.Translation.Get("GMCM.EnableControllerKeyboard.Tooltip"), + getValue: () => Config.EnableControllerKeyboard, + setValue: value => Config.EnableControllerKeyboard = value + ); } } } From fa4aef2c29219491cc1f777303e609924715c6bc Mon Sep 17 00:00:00 2001 From: Rodolfo Date: Wed, 4 Mar 2026 12:40:54 -0300 Subject: [PATCH 3/6] Revise GMCM labels and localization for new control model - Add localization strings for separate Keyboard and Controller sections in GMCM. - Add labels/tooltips for independent modifier requirements and modifier bindings per device. - Rename transfer-related key label for clarity. - Replace hardcoded controller keyboard text with localized entries. - Shorten PT-BR modifier requirement labels to avoid checkbox/text overlap in GMCM. --- AllChestsMenu/i18n/default.json | 28 +++++++++++++++++++++------- AllChestsMenu/i18n/fr.json | 8 +++++--- AllChestsMenu/i18n/pt-BR.json | 30 ++++++++++++++++++++++-------- AllChestsMenu/i18n/ru.json | 8 +++++--- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/AllChestsMenu/i18n/default.json b/AllChestsMenu/i18n/default.json index fbcc8f45..681bf6d2 100644 --- a/AllChestsMenu/i18n/default.json +++ b/AllChestsMenu/i18n/default.json @@ -53,6 +53,10 @@ "GMCM.Section.Sorting.Desc": "Change how chests are sorted in the list", "GMCM.Section.Controls.Name": "Controls", "GMCM.Section.Controls.Desc": "Keyboard and controller bindings", + "GMCM.Section.KeyboardControls.Name": "Keyboard", + "GMCM.Section.KeyboardControls.Desc": "Keyboard settings for opening the menu and item transfer shortcuts", + "GMCM.Section.ControllerControls.Name": "Controller", + "GMCM.Section.ControllerControls.Desc": "Controller settings for opening the menu and navigation", // General "GMCM.ModEnabled.Name": "Enable Mod", @@ -91,14 +95,24 @@ "GMCM.SecondarySortingPriority.Y": "Y (Top to Bottom)", // Controls - "GMCM.MenuKey.Name": "Open Menu Key", - "GMCM.MenuKey.Tooltip": "Press this key to open the All Chests Menu", - "GMCM.ModToOpen.Name": "Require Modifier Key", - "GMCM.ModToOpen.Tooltip": "If enabled, you must hold the Modifier Key while pressing Open Menu Key to open the menu", - "GMCM.ModKey.Name": "Modifier Key", - "GMCM.ModKey.Tooltip": "Hold this key while clicking to transfer items to your inventory instead of picking them up. Also used to open the menu if 'Require Modifier Key' is enabled.", + "GMCM.KeyboardMenuKey.Name": "Open Menu Key (Keyboard)", + "GMCM.KeyboardMenuKey.Tooltip": "Press this keyboard key to open the All Chests Menu", + "GMCM.KeyboardRequireModifierToOpen.Name": "Require Modifier (Keyboard)", + "GMCM.KeyboardRequireModifierToOpen.Tooltip": "If enabled, opening with keyboard requires the keyboard modifier key below", + "GMCM.KeyboardOpenModifierKey.Name": "Open Modifier Key (Keyboard)", + "GMCM.KeyboardOpenModifierKey.Tooltip": "Keyboard key used as modifier when 'Require Modifier To Open (Keyboard)' is enabled", + "GMCM.ControllerMenuButton.Name": "Open Menu Button (Controller)", + "GMCM.ControllerMenuButton.Tooltip": "Press this controller button to open the All Chests Menu", + "GMCM.ControllerRequireModifierToOpen.Name": "Require Modifier (Controller)", + "GMCM.ControllerRequireModifierToOpen.Tooltip": "If enabled, opening with controller requires the controller modifier button below", + "GMCM.ControllerOpenModifierButton.Name": "Open Modifier Button (Controller)", + "GMCM.ControllerOpenModifierButton.Tooltip": "Controller button used as modifier when 'Require Modifier To Open (Controller)' is enabled", + "GMCM.TransferModifierKey.Name": "Transfer Modifier Key", + "GMCM.TransferModifierKey.Tooltip": "Hold this key while clicking to transfer items to your inventory instead of picking them up.", "GMCM.ModKey2.Name": "Same-Item Transfer Key", "GMCM.ModKey2.Tooltip": "Hold this key while using the 'Put All' or 'Take All' buttons to only transfer items that match what's already in the target inventory.", "GMCM.SwitchButton.Name": "Controller Switch Button", - "GMCM.SwitchButton.Tooltip": "Button for gamepad users to switch focus between the chest list (top) and player inventory (bottom)" + "GMCM.SwitchButton.Tooltip": "Button for gamepad users to switch focus between the chest list (top) and player inventory (bottom)", + "GMCM.EnableControllerKeyboard.Name": "Enable Controller Keyboard", + "GMCM.EnableControllerKeyboard.Tooltip": "Opens an on-screen keyboard when selecting a text input using a controller." } diff --git a/AllChestsMenu/i18n/fr.json b/AllChestsMenu/i18n/fr.json index 8e3f983d..497928f2 100644 --- a/AllChestsMenu/i18n/fr.json +++ b/AllChestsMenu/i18n/fr.json @@ -91,10 +91,12 @@ "GMCM.SecondarySortingPriority.Y": "Y (Haut en Bas)", // Controls - "GMCM.MenuKey.Name": "Touche pour ouvrir le menu", - "GMCM.MenuKey.Tooltip": "Appuyez sur cette touche pour ouvrir le menu All Chests Menu", + "GMCM.KeyboardMenuKey.Name": "Touche d'ouverture du menu (Clavier)", + "GMCM.KeyboardMenuKey.Tooltip": "Appuyez sur cette touche du clavier pour ouvrir le menu All Chests Menu", + "GMCM.ControllerMenuButton.Name": "Bouton d'ouverture du menu (Manette)", + "GMCM.ControllerMenuButton.Tooltip": "Appuyez sur ce bouton de la manette pour ouvrir le menu All Chests Menu", "GMCM.ModToOpen.Name": "Exiger la touche modificatrice", - "GMCM.ModToOpen.Tooltip": "Si activé, vous devez maintenir la touche modificatrice tout en appuyant sur la touche d'ouverture du menu pour ouvrir le menu", + "GMCM.ModToOpen.Tooltip": "Si activé, vous devez maintenir la touche modificatrice tout en appuyant sur la touche/bouton d'ouverture du menu", "GMCM.ModKey.Name": "Touche modificatrice", "GMCM.ModKey.Tooltip": "Maintenez cette touche en cliquant pour transférer des objets vers votre inventaire au lieu de les ramasser. Également utilisée pour ouvrir le menu si 'Exiger la touche modificatrice' est activé.", "GMCM.ModKey2.Name": "Touche de transfert d'objets identiques", diff --git a/AllChestsMenu/i18n/pt-BR.json b/AllChestsMenu/i18n/pt-BR.json index e96db3e4..698d2bf5 100644 --- a/AllChestsMenu/i18n/pt-BR.json +++ b/AllChestsMenu/i18n/pt-BR.json @@ -52,6 +52,10 @@ "GMCM.Section.Sorting.Desc": "Alterar como os baús são ordenados na lista", "GMCM.Section.Controls.Name": "Controles", "GMCM.Section.Controls.Desc": "Binds de teclado e controle", + "GMCM.Section.KeyboardControls.Name": "Teclado", + "GMCM.Section.KeyboardControls.Desc": "Configurações de teclado para abrir o menu e atalhos de transferência", + "GMCM.Section.ControllerControls.Name": "Controle", + "GMCM.Section.ControllerControls.Desc": "Configurações de controle para abrir o menu e navegação", // General "GMCM.ModEnabled.Name": "Ativar Mod", "GMCM.ModEnabled.Tooltip": "Ligar ou desligar o mod All Chests Menu", @@ -85,14 +89,24 @@ "GMCM.SecondarySortingPriority.X": "X (Esquerda para Direita)", "GMCM.SecondarySortingPriority.Y": "Y (Cima para Baixo)", // Controls - "GMCM.MenuKey.Name": "Tecla de Menu", - "GMCM.MenuKey.Tooltip": "Pressione esta tecla para abrir o All Chests Menu", - "GMCM.ModToOpen.Name": "Requerer Tecla Modificadora", - "GMCM.ModToOpen.Tooltip": "Se ativado, você deve segurar a Tecla Modificadora enquanto pressiona a Tecla de Menu para abrir o menu", - "GMCM.ModKey.Name": "Tecla Modificadora", - "GMCM.ModKey.Tooltip": "Segure esta tecla ao clicar para transferir itens para o seu inventário em vez de pegá-los. Também usada para abrir o menu se 'Requerer Tecla Modificadora' estiver ativado.", + "GMCM.KeyboardRequireModifierToOpen.Name": "Exigir Mod. (Teclado)", + "GMCM.KeyboardRequireModifierToOpen.Tooltip": "Se ativado, abrir com teclado exige a tecla modificadora do teclado abaixo", + "GMCM.KeyboardOpenModifierKey.Name": "Tecla Modificadora para Abrir (Teclado)", + "GMCM.KeyboardOpenModifierKey.Tooltip": "Tecla do teclado usada como modificador quando 'Exigir Modificador para Abrir (Teclado)' estiver ativo", + "GMCM.KeyboardMenuKey.Name": "Tecla de Abrir Menu (Teclado)", + "GMCM.KeyboardMenuKey.Tooltip": "Pressione esta tecla do teclado para abrir o All Chests Menu", + "GMCM.TransferModifierKey.Name": "Tecla Modificadora de Transferência", + "GMCM.TransferModifierKey.Tooltip": "Segure esta tecla ao clicar para transferir itens para o seu inventário em vez de pegá-los.", "GMCM.ModKey2.Name": "Tecla de Transferência de Mesmos Itens", "GMCM.ModKey2.Tooltip": "Segure esta tecla ao usar os botões 'Colocar Tudo' ou 'Pegar Tudo' para transferir apenas itens que correspondem ao que já está no inventário alvo.", + "GMCM.ControllerRequireModifierToOpen.Name": "Exigir Mod. (Controle)", + "GMCM.ControllerRequireModifierToOpen.Tooltip": "Se ativado, abrir com controle exige o botão modificador do controle abaixo", + "GMCM.ControllerOpenModifierButton.Name": "Botão Modificador para Abrir (Controle)", + "GMCM.ControllerOpenModifierButton.Tooltip": "Botão do controle usado como modificador quando 'Exigir Modificador para Abrir (Controle)' estiver ativo", + "GMCM.ControllerMenuButton.Name": "Botão de Abrir Menu (Controle)", + "GMCM.ControllerMenuButton.Tooltip": "Pressione este botão do controle para abrir o All Chests Menu", "GMCM.SwitchButton.Name": "Botão de Troca do Controle", - "GMCM.SwitchButton.Tooltip": "Botão para usuários de controle trocarem o foco entre a lista de baús (cima) e o inventário do jogador (baixo)" -} \ No newline at end of file + "GMCM.SwitchButton.Tooltip": "Botão para usuários de controle trocarem o foco entre a lista de baús (cima) e o inventário do jogador (baixo)", + "GMCM.EnableControllerKeyboard.Name": "Ativar Teclado no Controle", + "GMCM.EnableControllerKeyboard.Tooltip": "Abre um teclado virtual ao selecionar um campo de texto usando controle." +} diff --git a/AllChestsMenu/i18n/ru.json b/AllChestsMenu/i18n/ru.json index 6eeb0441..ff3a9fff 100644 --- a/AllChestsMenu/i18n/ru.json +++ b/AllChestsMenu/i18n/ru.json @@ -91,10 +91,12 @@ "GMCM.SecondarySortingPriority.Y": "Y (Сверху вниз)", // Controls - "GMCM.MenuKey.Name": "Клавиша открытия меню", - "GMCM.MenuKey.Tooltip": "Нажмите эту клавишу, чтобы открыть меню All Chests Menu", + "GMCM.KeyboardMenuKey.Name": "Клавиша открытия меню (Клавиатура)", + "GMCM.KeyboardMenuKey.Tooltip": "Нажмите эту клавишу на клавиатуре, чтобы открыть меню All Chests Menu", + "GMCM.ControllerMenuButton.Name": "Кнопка открытия меню (Контроллер)", + "GMCM.ControllerMenuButton.Tooltip": "Нажмите эту кнопку на контроллере, чтобы открыть меню All Chests Menu", "GMCM.ModToOpen.Name": "Требовать модификатор", - "GMCM.ModToOpen.Tooltip": "Если включено, вы должны удерживать модификатор при нажатии клавиши открытия меню", + "GMCM.ModToOpen.Tooltip": "Если включено, нужно удерживать модификатор при нажатии клавиши/кнопки открытия меню", "GMCM.ModKey.Name": "Модификатор", "GMCM.ModKey.Tooltip": "Удерживайте при клике для передачи предметов в инвентарь вместо поднятия. Также используется для открытия меню, если включено 'Требовать модификатор'.", "GMCM.ModKey2.Name": "Клавиша передачи одинаковых", From fce6d6505b27c910856644fd23cbfdddcc6dc170 Mon Sep 17 00:00:00 2001 From: Rodolfo Date: Wed, 4 Mar 2026 14:36:25 -0300 Subject: [PATCH 4/6] Shorten PT-BR controller open-modifier label to avoid GMCM overlap - Renames controller open modifier label to a shorter PT-BR variant. - Prevents text overlap with keybind field in Generic Mod Config Menu on narrower layouts. - No behavior changes; localization-only adjustment. --- AllChestsMenu/i18n/pt-BR.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AllChestsMenu/i18n/pt-BR.json b/AllChestsMenu/i18n/pt-BR.json index 698d2bf5..3fd3d132 100644 --- a/AllChestsMenu/i18n/pt-BR.json +++ b/AllChestsMenu/i18n/pt-BR.json @@ -101,7 +101,7 @@ "GMCM.ModKey2.Tooltip": "Segure esta tecla ao usar os botões 'Colocar Tudo' ou 'Pegar Tudo' para transferir apenas itens que correspondem ao que já está no inventário alvo.", "GMCM.ControllerRequireModifierToOpen.Name": "Exigir Mod. (Controle)", "GMCM.ControllerRequireModifierToOpen.Tooltip": "Se ativado, abrir com controle exige o botão modificador do controle abaixo", - "GMCM.ControllerOpenModifierButton.Name": "Botão Modificador para Abrir (Controle)", + "GMCM.ControllerOpenModifierButton.Name": "Botão Mod. de Abertura (Controle)", "GMCM.ControllerOpenModifierButton.Tooltip": "Botão do controle usado como modificador quando 'Exigir Modificador para Abrir (Controle)' estiver ativo", "GMCM.ControllerMenuButton.Name": "Botão de Abrir Menu (Controle)", "GMCM.ControllerMenuButton.Tooltip": "Pressione este botão do controle para abrir o All Chests Menu", From 1f83dabea143a94f3b2a06b0c3ebf531d1b469ba Mon Sep 17 00:00:00 2001 From: Rodolfo Date: Sat, 7 Mar 2026 01:08:27 -0300 Subject: [PATCH 5/6] Adjust single-column responsive layout --- AllChestsMenu/AllChestsMenu.cs | 247 ++++++++++++++++++++++++--------- AllChestsMenu/NuGet.config | 7 + 2 files changed, 189 insertions(+), 65 deletions(-) create mode 100644 AllChestsMenu/NuGet.config diff --git a/AllChestsMenu/AllChestsMenu.cs b/AllChestsMenu/AllChestsMenu.cs index 5683e594..dbaab809 100644 --- a/AllChestsMenu/AllChestsMenu.cs +++ b/AllChestsMenu/AllChestsMenu.cs @@ -167,35 +167,77 @@ private void CalculateResponsiveLayout() private int effectiveViewportWidth; private int effectiveViewportHeight; private bool compactLayout; + private bool stackedBottomControls; + private int playerInventoryColumns = 12; + private int playerInventoryRows = 3; private float trashLidScale = 4f; public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game1.uiViewport.Height + borderWidth * 2 + 64, false) { // Calcular layout responsivo CalculateResponsiveLayout(); + stackedBottomControls = numberOfChestColumns == 1; currentSort = ModEntry.Config.CurrentSort; - int actionButtonSize = compactLayout ? 48 : 64; - int actionButtonGap = compactLayout ? 6 : 8; - int actionButtonScale = compactLayout ? 3 : 4; - trashLidScale = compactLayout ? 3f : 4f; + int actionButtonSize = stackedBottomControls ? (compactLayout ? 40 : 52) : (compactLayout ? 48 : 64); + int actionButtonGap = stackedBottomControls ? (compactLayout ? 4 : 6) : (compactLayout ? 6 : 8); + float actionButtonScale = stackedBottomControls ? (compactLayout ? 2.5f : 3.3f) : (compactLayout ? 3f : 4f); + float compactFilterScale = stackedBottomControls ? 0.8f : 1f; + int stackedControlGap = compactLayout ? 8 : 10; + int sortOptionCount = Enum.GetNames(typeof(Sort)).Length - 1; + int sortLabelPixelWidth = stackedBottomControls ? (compactLayout ? 54 : 66) : 0; + int sortInlineGap = stackedBottomControls ? 6 : 0; + int sortXStep = stackedBottomControls ? (compactLayout ? 22 : 26) : (compactLayout ? 48 : 64); + int sortYStep = stackedBottomControls ? 0 : (compactLayout ? 32 : 40); + int sortSize = stackedBottomControls ? (compactLayout ? 18 : 22) : (compactLayout ? 26 : 32); + int sortRowWidth = stackedBottomControls ? sortLabelPixelWidth + sortInlineGap + ((sortOptionCount - 1) * sortXStep) + sortSize : 0; + int trashWidth = stackedBottomControls ? (compactLayout ? 30 : 40) : (compactLayout ? 48 : 64); + int trashHeight = stackedBottomControls ? (compactLayout ? 46 : 60) : (compactLayout ? 78 : 104); + int topControlsHeight = Math.Max(actionButtonSize, Math.Max(trashHeight, sortSize)); + int topControlsOffset = stackedBottomControls ? actionButtonSize / 2 : 0; + int sortYStart = stackedBottomControls ? (topControlsHeight - sortSize) / 2 : (compactLayout ? 84 : 104); + int sortBlockBottom = stackedBottomControls ? topControlsOffset + topControlsHeight : sortYStart + sortYStep + sortSize; + int rightControlBlockHeight = stackedBottomControls ? sortBlockBottom : Math.Max(sortBlockBottom, actionButtonSize + stackedControlGap + trashHeight); + trashLidScale = actionButtonScale; + int totalPlayerSlots = Math.Max(12, Game1.player.Items.Count); + int smallFilterWidth = (int)Math.Round((compactLayout ? 104 : 120) * compactFilterScale); + int filterXStart = xPositionOnScreen + borderWidth + (stackedBottomControls ? 12 : 24); + int filtersTotalWidth = filterXStart + smallFilterWidth + 16; + if (stackedBottomControls) + { + int availableInventoryWidth = width - borderWidth * 2 - 12 - smallFilterWidth - 24 - 12; + playerInventoryColumns = Math.Max(6, Math.Min(12, availableInventoryWidth / 64)); + playerInventoryRows = Math.Max(3, (int)Math.Ceiling(totalPlayerSlots / (float)playerInventoryColumns)); + } + else + { + playerInventoryColumns = 12; + playerInventoryRows = Math.Min(3, (int)Math.Ceiling(totalPlayerSlots / 12f)); + } + + inventoryHeight = 64 * playerInventoryRows; // Layout claro: área de baús (topo) e área de inventário/filtros (fundo) inventoryHeight = 64 * 3; // Altura do inventário // Filters stack height: 4 filtros + labels + espaçamento + inventoryHeight = 64 * playerInventoryRows; int filterStackHeight = compactLayout ? 320 : 380; + if (stackedBottomControls) + { + filterStackHeight = Math.Max(filterStackHeight, rightControlBlockHeight + stackedControlGap + inventoryHeight); + } int verticalPadding = compactLayout ? 40 : 64; bottomAreaHeight = Math.Max(inventoryHeight + verticalPadding, filterStackHeight) + borderWidth + verticalPadding; cutoff = effectiveViewportHeight - bottomAreaHeight; // Y onde a área de baús termina // Centraliza o conjunto (inventário + botões) empurrado pra direita se necessário - int smallFilterWidth = compactLayout ? 104 : 120; - int filterXStart = xPositionOnScreen + borderWidth + 24; - int filtersTotalWidth = filterXStart + smallFilterWidth + 16; + int availableVerticalSpace = effectiveViewportHeight - cutoff; - int inventoryY = cutoff + (availableVerticalSpace - inventoryHeight) / 2; + int inventoryY = stackedBottomControls + ? cutoff + borderWidth + (compactLayout ? 14 : 20) + rightControlBlockHeight + stackedControlGap + : cutoff + (availableVerticalSpace - inventoryHeight) / 2; widgetText = new string[]{ ModEntry.SHelper.Translation.Get("open"), Game1.content.LoadString("Strings\\UI:ItemGrab_Organize"), @@ -209,15 +251,31 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game fridgeString = ModEntry.SHelper.Translation.Get("fridge"); sortString = ModEntry.SHelper.Translation.Get("sort"); - int columns = 12; - int rows = Math.Min(3, (int)Math.Ceiling((double)Game1.player.Items.Count / columns)); + int columns = playerInventoryColumns; + int rows = playerInventoryRows; int capacity = rows * columns; - // Centraliza o conjunto (inventário + botões) - int totalBottomWidth = 64 * columns + 32 + (actionButtonSize * 5) + (actionButtonSize + 16); // inventario + espaco + botoes e lixeira - int startX = (effectiveViewportWidth - totalBottomWidth) / 2; - startX += 40; // Deslocar para a direita para balancear com os filtros na esqueda - if (startX < filtersTotalWidth) startX = filtersTotalWidth; + // Centraliza o inventário e evita sobreposição com os filtros no layout de 1 coluna + int inventoryPixelWidth = 64 * columns; + int startX; + if (stackedBottomControls) + { + int centeredInventoryX = xPositionOnScreen + (width - inventoryPixelWidth) / 2; + int maxInventoryX = xPositionOnScreen + width - borderWidth - inventoryPixelWidth - 12; + int maxFilterWidth = maxInventoryX - filterXStart - 24; + int minFilterWidth = compactLayout ? 44 : 56; + smallFilterWidth = Math.Max(minFilterWidth, Math.Min(smallFilterWidth, maxFilterWidth)); + int minInventoryX = filterXStart + smallFilterWidth + 24; + startX = Math.Max(minInventoryX, centeredInventoryX); + startX = Math.Min(startX, maxInventoryX); + } + else + { + int totalBottomWidth = inventoryPixelWidth + 32 + (actionButtonSize * 5) + (actionButtonSize + 16); // inventario + espaco + botoes e lixeira + startX = (effectiveViewportWidth - totalBottomWidth) / 2; + startX += 40; // Deslocar para a direita para balancear com os filtros na esqueda + if (startX < filtersTotalWidth) startX = filtersTotalWidth; + } playerInventoryMenu = new InventoryMenu( startX, @@ -228,11 +286,23 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game capacity, rows); SetPlayerInventoryNeighbours(); - organizeButton = new ClickableTextureComponent("", new Rectangle(playerInventoryMenu.xPositionOnScreen + playerInventoryMenu.width + 32, playerInventoryMenu.yPositionOnScreen - (compactLayout ? 8 : 16), actionButtonSize, actionButtonSize), "", Game1.content.LoadString("Strings\\UI:ItemGrab_Organize"), Game1.mouseCursors, new Rectangle(162, 440, 16, 16), actionButtonScale, false) + int playerTopRightSlot = GetPlayerInventoryRowEndIndex(0); + int topButtonBlockWidth = actionButtonSize * 4 + actionButtonGap * 4 + trashWidth; + int sortLabelX = stackedBottomControls + ? playerInventoryMenu.xPositionOnScreen + playerInventoryMenu.width - (sortRowWidth + actionButtonGap + topButtonBlockWidth) + : 0; + int sortOptionsX = stackedBottomControls ? sortLabelX + sortLabelPixelWidth + sortInlineGap : 0; + int actionButtonsX = stackedBottomControls + ? sortOptionsX + ((sortOptionCount - 1) * sortXStep) + sortSize + actionButtonGap + : playerInventoryMenu.xPositionOnScreen + playerInventoryMenu.width + 32; + int actionButtonsY = stackedBottomControls + ? cutoff + borderWidth + (compactLayout ? 14 : 20) + topControlsOffset + : playerInventoryMenu.yPositionOnScreen - (compactLayout ? 8 : 16); + organizeButton = new ClickableTextureComponent("", new Rectangle(actionButtonsX, actionButtonsY, actionButtonSize, actionButtonSize), "", Game1.content.LoadString("Strings\\UI:ItemGrab_Organize"), Game1.mouseCursors, new Rectangle(162, 440, 16, 16), actionButtonScale, false) { myID = 4 * ccMagnitude, downNeighborID = 4 * ccMagnitude + 1, - leftNeighborID = 11, + leftNeighborID = playerTopRightSlot, rightNeighborID = 4 * ccMagnitude + 1 }; storeAlikeButton = new ClickableTextureComponent("", new Rectangle(organizeButton.bounds.X + actionButtonSize + actionButtonGap, organizeButton.bounds.Y, actionButtonSize, actionButtonSize), "", Game1.content.LoadString("Strings\\UI:ItemGrab_FillStacks"), Game1.mouseCursors, new Rectangle(103, 469, 16, 16), actionButtonScale, false) @@ -268,10 +338,14 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game upNeighborID = 4 * ccMagnitude }; - // Update trashCan position - moved right of the buttons, aligned vertically - int trashWidth = compactLayout ? 48 : 64; - int trashHeight = compactLayout ? 78 : 104; - trashCan = new ClickableTextureComponent(new Rectangle(sortAllButton.bounds.X + actionButtonSize + 16, playerInventoryMenu.yPositionOnScreen + (compactLayout ? 18 : 28), trashWidth, trashHeight), Game1.mouseCursors, new Rectangle(564 + Game1.player.trashCanLevel * 18, 102, 18, 26), actionButtonScale, false) + // Update trashCan position + int trashX = stackedBottomControls + ? sortAllButton.bounds.Right + actionButtonGap + : sortAllButton.bounds.X + actionButtonSize + 16; + int trashY = stackedBottomControls + ? organizeButton.bounds.Y + (actionButtonSize - trashHeight) / 2 + : playerInventoryMenu.yPositionOnScreen + (compactLayout ? 18 : 28); + trashCan = new ClickableTextureComponent(new Rectangle(trashX, trashY, trashWidth, trashHeight), Game1.mouseCursors, new Rectangle(564 + Game1.player.trashCanLevel * 18, 102, 18, 26), actionButtonScale, false) { myID = 4 * ccMagnitude + 4, leftNeighborID = 4 * ccMagnitude + 3, @@ -302,28 +376,27 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game for (int i = 0; i < s.Length - 1; i++) { - int row = i % 2; + int row = stackedBottomControls ? 0 : i % 2; string name = s[i]; int idx = 5 * ccMagnitude; - int sortXStep = compactLayout ? 48 : 64; - int sortYStart = compactLayout ? 84 : 104; - int sortYStep = compactLayout ? 32 : 40; - int sortSize = compactLayout ? 26 : 32; sortNames[name] = ModEntry.SHelper.Translation.Get("sort-" + name); - sortCCList.Add(new ClickableComponent(new Rectangle(organizeButton.bounds.X + (i / 2) * sortXStep, organizeButton.bounds.Y + sortYStart + row * sortYStep, sortSize, sortSize), name, name) + int sortGridX = stackedBottomControls ? sortOptionsX : organizeButton.bounds.X; + int sortColumn = stackedBottomControls ? i : (i / 2); + sortCCList.Add(new ClickableComponent(new Rectangle(sortGridX + sortColumn * sortXStep, organizeButton.bounds.Y + sortYStart + row * sortYStep, sortSize, sortSize), name, name) { myID = idx + i, - leftNeighborID = i > 2 ? idx + i - 2: 4 * ccMagnitude + 1, - rightNeighborID = i < s.Length - 2 ? idx + i + 2 : -1, - downNeighborID = row == 0 ? idx + i + 1 : -1, - upNeighborID = row == 1 ? idx + i - 1 : -1 + leftNeighborID = stackedBottomControls ? (i > 0 ? idx + i - 1 : 4 * ccMagnitude + 1) : (i > 2 ? idx + i - 2 : 4 * ccMagnitude + 1), + rightNeighborID = stackedBottomControls ? (i < s.Length - 2 ? idx + i + 1 : -1) : (i < s.Length - 2 ? idx + i + 2 : -1), + downNeighborID = stackedBottomControls ? -1 : (row == 0 ? idx + i + 1 : -1), + upNeighborID = stackedBottomControls ? -1 : (row == 1 ? idx + i - 1 : -1) }); } // Initialize new filter fields - layout vertical na esquerda int filterYStart = effectiveViewportHeight - bottomAreaHeight + borderWidth + (compactLayout ? 52 : 64) + 16; int filterSpacing = compactLayout ? 72 : 84; + int locationDropdownHeight = stackedBottomControls ? 32 : 40; // Chest Label filter chestLabelText = new TextBox(Game1.content.Load("LooseSprites\\textBox"), null, Game1.smallFont, Game1.textColor) @@ -352,7 +425,7 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game myID = 2 * ccMagnitude + 4, upNeighborID = 2 * ccMagnitude + 3, downNeighborID = 2 * ccMagnitude + 6, - rightNeighborID = playerInventoryMenu.inventory.Count > 12 ? playerInventoryMenu.inventory[12].myID : playerInventoryMenu.inventory[0].myID + rightNeighborID = GetPlayerInventoryRowStartIndex(1) >= 0 ? playerInventoryMenu.inventory[GetPlayerInventoryRowStartIndex(1)].myID : playerInventoryMenu.inventory[0].myID }; // Item Description filter @@ -367,16 +440,16 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game myID = 2 * ccMagnitude + 6, upNeighborID = 2 * ccMagnitude + 4, downNeighborID = 2 * ccMagnitude + 7, - rightNeighborID = playerInventoryMenu.inventory.Count > 24 ? playerInventoryMenu.inventory[24].myID : playerInventoryMenu.inventory[0].myID + rightNeighborID = GetPlayerInventoryRowStartIndex(2) >= 0 ? playerInventoryMenu.inventory[GetPlayerInventoryRowStartIndex(2)].myID : playerInventoryMenu.inventory[0].myID }; // Location dropdown (vertical na esquerda) - locationDropdownCC = new ClickableComponent(new Rectangle(filterXStart, filterYStart + filterSpacing * 3, smallFilterWidth, 40), "location", "Location") + locationDropdownCC = new ClickableComponent(new Rectangle(filterXStart, filterYStart + filterSpacing * 3, smallFilterWidth, locationDropdownHeight), "location", "Location") { myID = 2 * ccMagnitude + 7, upNeighborID = 2 * ccMagnitude + 6, downNeighborID = 5 * ccMagnitude, - rightNeighborID = playerInventoryMenu.inventory.Count > 24 ? playerInventoryMenu.inventory[24].myID : playerInventoryMenu.inventory[0].myID + rightNeighborID = GetPlayerInventoryRowStartIndex(3) >= 0 ? playerInventoryMenu.inventory[GetPlayerInventoryRowStartIndex(3)].myID : playerInventoryMenu.inventory[GetPlayerInventoryRowStartIndex(2)].myID }; // Keep original locationText for backward compatibility @@ -398,7 +471,7 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game // Filter checkboxes with controller navigation int checkboxX = xPositionOnScreen + borderWidth; - int checkboxSize = 24; + int checkboxSize = stackedBottomControls ? 19 : 24; filterCheckboxCCList.Add(new ClickableComponent( new Rectangle(chestLabelText.X - checkboxSize - 4, chestLabelText.Y + 4, checkboxSize, checkboxSize), "chestLabelChk")); @@ -410,16 +483,18 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game new Rectangle(locationDropdownCC.bounds.X - checkboxSize - 4, locationDropdownCC.bounds.Y + 8, checkboxSize, checkboxSize), "locationChk")); // Clear filters button - 20px to the right of the top filter, vertically centered with text box - int clearBtnY = filterYStart - 10; - int clearBtnX = filterXStart + smallFilterWidth + 20; + int clearButtonSize = stackedBottomControls ? (compactLayout ? 32 : 38) : (compactLayout ? 40 : 48); + float clearButtonScale = stackedBottomControls ? (compactLayout ? 2.6f : 3.1f) : (compactLayout ? 3.3f : 4f); + int clearBtnY = filterYStart - (stackedBottomControls ? 4 : 10); + int clearBtnX = filterXStart + smallFilterWidth + (stackedBottomControls ? 14 : 20); clearFiltersButton = new ClickableTextureComponent( "", - new Rectangle(clearBtnX, clearBtnY, compactLayout ? 40 : 48, compactLayout ? 40 : 48), + new Rectangle(clearBtnX, clearBtnY, clearButtonSize, clearButtonSize), "", ModEntry.SHelper.Translation.Get("clear-filters"), Game1.mouseCursors, new Rectangle(337, 494, 12, 12), // X icon - compactLayout ? 3.3f : 4f, + clearButtonScale, false ) { @@ -1057,16 +1132,17 @@ public override void draw(SpriteBatch b) Game1.drawDialogueBox(xPositionOnScreen, cutoff - (borderWidth / 2), width, bottomAreaHeight + borderWidth + (borderWidth / 2), false, true, null, false, true); playerInventoryMenu.draw(b); - b.DrawString(Game1.smallFont, filterString, new Vector2(locationText.X, locationText.Y - 29), Game1.textColor); + float filterLabelScale = stackedBottomControls ? 0.8f : 1f; + b.DrawString(Game1.smallFont, filterString, new Vector2(locationText.X, locationText.Y - 29), Game1.textColor, 0f, Vector2.Zero, filterLabelScale, SpriteEffects.None, 0.88f); locationText.Draw(b); // Draw additional filter fields // Item Name filter - b.DrawString(Game1.smallFont, ModEntry.SHelper.Translation.Get("filter-item-name"), new Vector2(itemNameText.X, itemNameText.Y - 29), Game1.textColor); + b.DrawString(Game1.smallFont, ModEntry.SHelper.Translation.Get("filter-item-name"), new Vector2(itemNameText.X, itemNameText.Y - 29), Game1.textColor, 0f, Vector2.Zero, filterLabelScale, SpriteEffects.None, 0.88f); itemNameText.Draw(b); // Item Description filter - b.DrawString(Game1.smallFont, ModEntry.SHelper.Translation.Get("filter-item-description"), new Vector2(itemDescriptionText.X, itemDescriptionText.Y - 29), Game1.textColor); + b.DrawString(Game1.smallFont, ModEntry.SHelper.Translation.Get("filter-item-description"), new Vector2(itemDescriptionText.X, itemDescriptionText.Y - 29), Game1.textColor, 0f, Vector2.Zero, filterLabelScale, SpriteEffects.None, 0.88f); itemDescriptionText.Draw(b); // Location dropdown @@ -1074,8 +1150,8 @@ public override void draw(SpriteBatch b) ? $"{ModEntry.Config.SelectedLocations.Count} {ModEntry.SHelper.Translation.Get("filter-location-select")}" : ModEntry.SHelper.Translation.Get("category-all"); Game1.spriteBatch.Draw(Game1.menuTexture, locationDropdownCC.bounds, Game1.getSourceRectForStandardTileSheet(Game1.menuTexture, 10), Color.White); - b.DrawString(Game1.smallFont, "Loc", new Vector2(locationDropdownCC.bounds.X, locationDropdownCC.bounds.Y - 29), Game1.textColor); - b.DrawString(Game1.smallFont, locationLabelText, new Vector2(locationDropdownCC.bounds.X + 8, locationDropdownCC.bounds.Y + 8), Game1.textColor); + b.DrawString(Game1.smallFont, "Loc", new Vector2(locationDropdownCC.bounds.X, locationDropdownCC.bounds.Y - 29), Game1.textColor, 0f, Vector2.Zero, filterLabelScale, SpriteEffects.None, 0.88f); + b.DrawString(Game1.smallFont, locationLabelText, new Vector2(locationDropdownCC.bounds.X + 8, locationDropdownCC.bounds.Y + 8), Game1.textColor, 0f, Vector2.Zero, filterLabelScale, SpriteEffects.None, 0.88f); // Dropdown arrow b.Draw(Game1.mouseCursors, new Vector2(locationDropdownCC.bounds.Right - 20, locationDropdownCC.bounds.Y + locationDropdownCC.bounds.Height / 2 - 4), new Rectangle(345, 494, 10, 6), Color.White, 0f, Vector2.Zero, 2f, SpriteEffects.None, 0.87f); @@ -1086,11 +1162,20 @@ public override void draw(SpriteBatch b) renameBox.Draw(b); okButton.draw(b); } - SpriteText.drawString(b, sortString, organizeButton.bounds.X, organizeButton.bounds.Y + 62); + int sortLabelX = stackedBottomControls && sortCCList.Count > 0 ? sortCCList[0].bounds.X - (compactLayout ? 60 : 72) : organizeButton.bounds.X; + int sortLabelY = stackedBottomControls + ? organizeButton.bounds.Y + (compactLayout ? 10 : 14) + : organizeButton.bounds.Y + 62; + if (stackedBottomControls) + b.DrawString(Game1.smallFont, sortString, new Vector2(sortLabelX, sortLabelY), Game1.textColor, 0f, Vector2.Zero, 0.9f, SpriteEffects.None, 0.88f); + else + SpriteText.drawString(b, sortString, sortLabelX, sortLabelY); foreach (ClickableComponent cc in sortCCList) { - b.DrawString(Game1.smallFont, cc.label, cc.bounds.Location.ToVector2() + new Vector2(-1, 1), currentSort.ToString() == cc.label ? Color.Green : Color.Black); - b.DrawString(Game1.smallFont, cc.label, cc.bounds.Location.ToVector2(), currentSort.ToString() == cc.label ? Color.LightGreen : Color.White); + Vector2 sortLabelPosition = cc.bounds.Location.ToVector2(); + float sortScale = stackedBottomControls ? 0.9f : 1f; + b.DrawString(Game1.smallFont, cc.label, sortLabelPosition + new Vector2(-1, 1), currentSort.ToString() == cc.label ? Color.Green : Color.Black, 0f, Vector2.Zero, sortScale, SpriteEffects.None, 0.88f); + b.DrawString(Game1.smallFont, cc.label, sortLabelPosition, currentSort.ToString() == cc.label ? Color.LightGreen : Color.White, 0f, Vector2.Zero, sortScale, SpriteEffects.None, 0.89f); } trashCan.draw(b); organizeButton.draw(b); @@ -1098,8 +1183,8 @@ public override void draw(SpriteBatch b) consolidateButton.draw(b); sortAllButton.draw(b); clearFiltersButton.draw(b); - float lidOffsetX = compactLayout ? 44f : 60f; - float lidOffsetY = compactLayout ? 30f : 40f; + float lidOffsetX = stackedBottomControls ? (compactLayout ? 24f : 34f) : (compactLayout ? 44f : 60f); + float lidOffsetY = stackedBottomControls ? (compactLayout ? 16f : 24f) : (compactLayout ? 30f : 40f); b.Draw(Game1.mouseCursors, new Vector2(trashCan.bounds.X + lidOffsetX, trashCan.bounds.Y + lidOffsetY), new Rectangle?(new Rectangle(564 + Game1.player.trashCanLevel * 18, 129, 18, 10)), Color.White, trashCanLidRotation, new Vector2(16f, 10f), trashLidScale, SpriteEffects.None, 0.86f); Game1.spriteBatch.Draw(Game1.menuTexture, new Rectangle(xPositionOnScreen + 16, -4, 24, 16), new Rectangle(16, 16, 24, 16), Color.White); Game1.spriteBatch.Draw(Game1.menuTexture, new Rectangle(xPositionOnScreen + width - 32, -4, 16, 16), new Rectangle(225, 16, 16, 16), Color.White); @@ -2349,26 +2434,58 @@ static int CompareLabels(string labelA, string labelB) }); } + private int GetPlayerInventoryRowStartIndex(int row) + { + if (playerInventoryMenu?.inventory == null || playerInventoryMenu.inventory.Count == 0) + return -1; + + int clampedRow = Math.Min(row, Math.Max(0, playerInventoryRows - 1)); + int index = clampedRow * playerInventoryColumns; + return Math.Min(index, playerInventoryMenu.inventory.Count - 1); + } + + private int GetPlayerInventoryRowEndIndex(int row) + { + int start = GetPlayerInventoryRowStartIndex(row); + if (start < 0) + return -1; + + int nextStart = GetPlayerInventoryRowStartIndex(row + 1); + if (nextStart > start) + return nextStart - 1; + + return playerInventoryMenu.inventory.Count - 1; + } + private void SetPlayerInventoryNeighbours() { + if (playerInventoryMenu?.inventory == null || playerInventoryMenu.inventory.Count == 0) + return; + int leftTop = chestLabelCC?.myID ?? 2 * ccMagnitude; int leftMiddle = itemNameCC?.myID ?? 2 * ccMagnitude; int leftBottom = itemDescCC?.myID ?? 2 * ccMagnitude; - if (playerInventoryMenu.inventory.Count >= 12) + int topStart = GetPlayerInventoryRowStartIndex(0); + int middleStart = GetPlayerInventoryRowStartIndex(1); + int bottomStart = GetPlayerInventoryRowStartIndex(2); + int topEnd = GetPlayerInventoryRowEndIndex(0); + int middleEnd = GetPlayerInventoryRowEndIndex(1); + int bottomEnd = GetPlayerInventoryRowEndIndex(2); + + playerInventoryMenu.inventory[topStart].leftNeighborID = leftTop; + playerInventoryMenu.inventory[topEnd].rightNeighborID = 4 * ccMagnitude; + + if (middleStart >= 0) { - playerInventoryMenu.inventory[0].leftNeighborID = leftTop; - playerInventoryMenu.inventory[11].rightNeighborID = 4 * ccMagnitude; - if (playerInventoryMenu.inventory.Count >= 24) - { - playerInventoryMenu.inventory[12].leftNeighborID = leftMiddle; - playerInventoryMenu.inventory[23].rightNeighborID = 4 * ccMagnitude + 1; - if (playerInventoryMenu.inventory.Count >= 36) - { - playerInventoryMenu.inventory[24].leftNeighborID = leftBottom; - playerInventoryMenu.inventory[35].rightNeighborID = 4 * ccMagnitude + 1; - } - } + playerInventoryMenu.inventory[middleStart].leftNeighborID = leftMiddle; + playerInventoryMenu.inventory[middleEnd].rightNeighborID = 4 * ccMagnitude + 1; + } + + if (bottomStart >= 0) + { + playerInventoryMenu.inventory[bottomStart].leftNeighborID = leftBottom; + playerInventoryMenu.inventory[bottomEnd].rightNeighborID = 4 * ccMagnitude + 1; } } @@ -2382,11 +2499,11 @@ private void RebuildControllerNavigation() itemNameCC.upNeighborID = chestLabelCC.myID; itemNameCC.downNeighborID = itemDescCC.myID; - itemNameCC.rightNeighborID = playerInventoryMenu.inventory.Count > 12 ? playerInventoryMenu.inventory[12].myID : chestLabelCC.rightNeighborID; + itemNameCC.rightNeighborID = GetPlayerInventoryRowStartIndex(1) >= 0 ? playerInventoryMenu.inventory[GetPlayerInventoryRowStartIndex(1)].myID : chestLabelCC.rightNeighborID; itemDescCC.upNeighborID = itemNameCC.myID; itemDescCC.downNeighborID = locationDropdownCC.myID; - itemDescCC.rightNeighborID = playerInventoryMenu.inventory.Count > 24 ? playerInventoryMenu.inventory[24].myID : chestLabelCC.rightNeighborID; + itemDescCC.rightNeighborID = GetPlayerInventoryRowStartIndex(2) >= 0 ? playerInventoryMenu.inventory[GetPlayerInventoryRowStartIndex(2)].myID : chestLabelCC.rightNeighborID; locationDropdownCC.upNeighborID = itemDescCC.myID; locationDropdownCC.downNeighborID = (locationDropdownOpen && locationOptionsCCList.Count > 0) ? locationOptionsCCList[0].myID : clearFiltersButton.myID; diff --git a/AllChestsMenu/NuGet.config b/AllChestsMenu/NuGet.config new file mode 100644 index 00000000..765346e5 --- /dev/null +++ b/AllChestsMenu/NuGet.config @@ -0,0 +1,7 @@ + + + + + + + From d71364212f730804bf475b3d3a424fc3aa892354 Mon Sep 17 00:00:00 2001 From: Rodolfo Date: Sat, 7 Mar 2026 12:45:31 -0300 Subject: [PATCH 6/6] Tweak sort option spacing --- AllChestsMenu/AllChestsMenu.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AllChestsMenu/AllChestsMenu.cs b/AllChestsMenu/AllChestsMenu.cs index dbaab809..2afb4b4a 100644 --- a/AllChestsMenu/AllChestsMenu.cs +++ b/AllChestsMenu/AllChestsMenu.cs @@ -187,7 +187,7 @@ public AllChestsMenu() : base(0, -borderWidth - 64, Game1.uiViewport.Width, Game int sortOptionCount = Enum.GetNames(typeof(Sort)).Length - 1; int sortLabelPixelWidth = stackedBottomControls ? (compactLayout ? 54 : 66) : 0; int sortInlineGap = stackedBottomControls ? 6 : 0; - int sortXStep = stackedBottomControls ? (compactLayout ? 22 : 26) : (compactLayout ? 48 : 64); + int sortXStep = stackedBottomControls ? (compactLayout ? 25 : 30) : (compactLayout ? 48 : 64); int sortYStep = stackedBottomControls ? 0 : (compactLayout ? 32 : 40); int sortSize = stackedBottomControls ? (compactLayout ? 18 : 22) : (compactLayout ? 26 : 32); int sortRowWidth = stackedBottomControls ? sortLabelPixelWidth + sortInlineGap + ((sortOptionCount - 1) * sortXStep) + sortSize : 0; @@ -1173,7 +1173,7 @@ public override void draw(SpriteBatch b) foreach (ClickableComponent cc in sortCCList) { Vector2 sortLabelPosition = cc.bounds.Location.ToVector2(); - float sortScale = stackedBottomControls ? 0.9f : 1f; + float sortScale = stackedBottomControls ? 0.82f : 1f; b.DrawString(Game1.smallFont, cc.label, sortLabelPosition + new Vector2(-1, 1), currentSort.ToString() == cc.label ? Color.Green : Color.Black, 0f, Vector2.Zero, sortScale, SpriteEffects.None, 0.88f); b.DrawString(Game1.smallFont, cc.label, sortLabelPosition, currentSort.ToString() == cc.label ? Color.LightGreen : Color.White, 0f, Vector2.Zero, sortScale, SpriteEffects.None, 0.89f); }