From ad67853a769ff1bc868808a07a246316363a4281 Mon Sep 17 00:00:00 2001 From: Andrea Bergonzo Date: Mon, 7 Sep 2026 03:43:49 +0100 Subject: [PATCH 1/4] fix(bags): separate regular and reagent slot counts --- EllesmereUIBags/EllesmereUIBags.lua | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/EllesmereUIBags/EllesmereUIBags.lua b/EllesmereUIBags/EllesmereUIBags.lua index 8b1829141..d62922b5d 100644 --- a/EllesmereUIBags/EllesmereUIBags.lua +++ b/EllesmereUIBags/EllesmereUIBags.lua @@ -975,6 +975,7 @@ local function CreateHeader() SetBagFont(header.itemCount, 11) header.itemCount:SetPoint("LEFT", header.title, "RIGHT", 8, 0) header.itemCount:SetTextColor(0.6, 0.6, 0.6) + header.itemCount:SetJustifyH("LEFT") local search = CreateFrame("EditBox", "EUI_BagSearchBox", header) search:SetSize(160, 22) @@ -5528,12 +5529,24 @@ function EUI_Bags:RefreshInventory() ReleaseAllSlotTables() local tempItems = {} local emptySlots = {} + local regularUsed, regularTotal = 0, 0 + local reagentUsed, reagentTotal = 0, 0 for bag = 0, 5 do local numSlots = C_Container.GetContainerNumSlots(bag) + if bag == 5 then + reagentTotal = numSlots + else + regularTotal = regularTotal + numSlots + end for slot = 1, numSlots do local info = C_Container.GetContainerItemInfo(bag, slot) if info then + if bag == 5 then + reagentUsed = reagentUsed + 1 + else + regularUsed = regularUsed + 1 + end local itemLink = C_Container.GetContainerItemLink(bag, slot) local d = AcquireSlotTable() d.bag = bag; d.slot = slot; d.info = info; d.itemLink = itemLink @@ -7082,12 +7095,12 @@ function EUI_Bags:RefreshInventory() end if EUI_Bags.Header and EUI_Bags.Header.itemCount then - if selectedCategoryIndex == 0 or selectedCategoryIndex == -1 or selectedCategoryIndex == -2 then - local totalSlots = totalCount + #emptySlots - EUI_Bags.Header.itemCount:SetText(EllesmereUI.Lf("%d / %d Items", totalCount, totalSlots)) - else - EUI_Bags.Header.itemCount:SetText(EllesmereUI.Lf("%d Items", totalCount)) + -- Capacity describes physical slots, independent of categories and merged stacks. + local countText = EllesmereUI.Lf("%d / %d Bag slots", regularUsed, regularTotal) + if reagentTotal > 0 then + countText = countText .. "\n" .. EllesmereUI.Lf("%d / %d Reagent slots", reagentUsed, reagentTotal) end + EUI_Bags.Header.itemCount:SetText(countText) end -- Dice button: OneBag only (unless hidden by setting), parented to the scroll child and anchored to the first category header. From 7b9caf1298ad3adca1d24d9197fa0998ef545dbf Mon Sep 17 00:00:00 2001 From: Andrea Bergonzo Date: Sun, 13 Sep 2026 05:18:09 +0100 Subject: [PATCH 2/4] feat(bags): add slot count format and reagent split options --- EllesmereUIBags/EllesmereUIBags.lua | 21 ++++++++++++++++++--- EllesmereUIBags/EllesmereUIBags_DB.lua | 2 ++ EllesmereUIOptions/EUI_Bags_Options.lua | 20 ++++++++++++++++++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/EllesmereUIBags/EllesmereUIBags.lua b/EllesmereUIBags/EllesmereUIBags.lua index d62922b5d..1941d8811 100644 --- a/EllesmereUIBags/EllesmereUIBags.lua +++ b/EllesmereUIBags/EllesmereUIBags.lua @@ -7096,9 +7096,24 @@ function EUI_Bags:RefreshInventory() if EUI_Bags.Header and EUI_Bags.Header.itemCount then -- Capacity describes physical slots, independent of categories and merged stacks. - local countText = EllesmereUI.Lf("%d / %d Bag slots", regularUsed, regularTotal) - if reagentTotal > 0 then - countText = countText .. "\n" .. EllesmereUI.Lf("%d / %d Reagent slots", reagentUsed, reagentTotal) + local format = BP().bagSlotCountFormat + local separate = BP().bagSeparateReagentSlots ~= false + local function FormatSlots(used, total, reagent) + if format == "free" then + return EllesmereUI.Lf(reagent and "%d Reagent slots free" or "%d Bag slots free", total - used) + elseif format == "free_total" then + return EllesmereUI.Lf(reagent and "%d / %d Reagent slots free" or "%d / %d Bag slots free", total - used, total) + end + return EllesmereUI.Lf(reagent and "%d / %d Reagent slots" or "%d / %d Bag slots", used, total) + end + local countText + if separate then + countText = FormatSlots(regularUsed, regularTotal, false) + if reagentTotal > 0 then + countText = countText .. "\n" .. FormatSlots(reagentUsed, reagentTotal, true) + end + else + countText = FormatSlots(regularUsed + reagentUsed, regularTotal + reagentTotal, false) end EUI_Bags.Header.itemCount:SetText(countText) end diff --git a/EllesmereUIBags/EllesmereUIBags_DB.lua b/EllesmereUIBags/EllesmereUIBags_DB.lua index a9a191c1f..fc1d6ba50 100644 --- a/EllesmereUIBags/EllesmereUIBags_DB.lua +++ b/EllesmereUIBags/EllesmereUIBags_DB.lua @@ -15,6 +15,8 @@ local BAGS_DEFAULTS = { bagAutoSize = false, bagCatTitleSize = 11, bagCountFontSize = 11, + bagSlotCountFormat = "used_total", + bagSeparateReagentSlots = true, itemlevelFontSize = 12, showItemlevelInBags = true, showUpgradeIndicator = true, diff --git a/EllesmereUIOptions/EUI_Bags_Options.lua b/EllesmereUIOptions/EUI_Bags_Options.lua index a177115df..eb549554a 100644 --- a/EllesmereUIOptions/EUI_Bags_Options.lua +++ b/EllesmereUIOptions/EUI_Bags_Options.lua @@ -647,6 +647,26 @@ initFrame:SetScript("OnEvent", function(self) end end + -- Slot count format | Separate reagent slots + _, h = W:DualRow(parent, y, + { type="dropdown", text="Slot count format", + tooltip="Choose how the inventory header shows occupied or free bag slots. Each item stack occupies one slot.", + values={ used_total="Used/total", free_total="Free/total", free="Free only" }, + order={ "used_total", "free_total", "free" }, + getValue=function() return db.profile.bagSlotCountFormat or "used_total" end, + setValue=function(v) + db.profile.bagSlotCountFormat = v + if _G.EUI_Bags and _G.EUI_Bags.RefreshInventory then _G.EUI_Bags:RefreshInventory() end + end }, + { type="toggle", text="Separate reagent slots", + tooltip="Show regular bag and reagent bag capacity separately in the inventory header. Turn this off to combine them; reagent slots still only accept reagents.", + getValue=function() return db.profile.bagSeparateReagentSlots ~= false end, + setValue=function(v) + db.profile.bagSeparateReagentSlots = v + if _G.EUI_Bags and _G.EUI_Bags.RefreshInventory then _G.EUI_Bags:RefreshInventory() end + end } + ); y = y - h + -- Item Count Text Size | Item Level Text Size _, h = W:DualRow(parent, y, { type="slider", text="Item Count Text Size", min=8, max=16, step=1, From c0b66bdbd9e5b4820af6cc46b94dd08bd0c13be5 Mon Sep 17 00:00:00 2001 From: Andrea Bergonzo Date: Sun, 13 Sep 2026 06:50:02 +0100 Subject: [PATCH 3/4] fix(locales): refresh keys after bag count changes --- EllesmereUILocales/_keys.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EllesmereUILocales/_keys.txt b/EllesmereUILocales/_keys.txt index e4e971518..e1fb1a3fb 100644 --- a/EllesmereUILocales/_keys.txt +++ b/EllesmereUILocales/_keys.txt @@ -1,6 +1,6 @@ # Auto-generated by .tools/extract-locale-keys.sh -- do not edit by hand. # Canonical list of translatable English keys passed as string literals -# (789 unique). Regenerate after wrapping new strings. Keys passed as +# (788 unique). Regenerate after wrapping new strings. Keys passed as # variables are not listed here -- use the in-game /euiloc harvester for # the complete runtime set. (Raid) @@ -24,7 +24,6 @@ %1$s's %2$s Breakdown %1$s's Death Recap %d / %d Items -%d Items %d junk item(s) could not be sold. %s is also assigned to: (Applies on Window Close) From 338ea542b33680b3d25579318d06a0e5a2184d8e Mon Sep 17 00:00:00 2001 From: Andrea Bergonzo Date: Tue, 15 Sep 2026 04:42:19 +0100 Subject: [PATCH 4/4] fix(locales): expose bag capacity format keys --- EllesmereUIBags/EllesmereUIBags.lua | 9 ++++++--- EllesmereUILocales/_keys.txt | 8 +++++++- tests/run_bag_count_tests.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 tests/run_bag_count_tests.py diff --git a/EllesmereUIBags/EllesmereUIBags.lua b/EllesmereUIBags/EllesmereUIBags.lua index 3e1f1ffdf..8f8d11e8f 100644 --- a/EllesmereUIBags/EllesmereUIBags.lua +++ b/EllesmereUIBags/EllesmereUIBags.lua @@ -7120,11 +7120,14 @@ function EUI_Bags:RefreshInventory() local separate = BP().bagSeparateReagentSlots ~= false local function FormatSlots(used, total, reagent) if format == "free" then - return EllesmereUI.Lf(reagent and "%d Reagent slots free" or "%d Bag slots free", total - used) + if reagent then return EllesmereUI.Lf("%d Reagent slots free", total - used) end + return EllesmereUI.Lf("%d Bag slots free", total - used) elseif format == "free_total" then - return EllesmereUI.Lf(reagent and "%d / %d Reagent slots free" or "%d / %d Bag slots free", total - used, total) + if reagent then return EllesmereUI.Lf("%d / %d Reagent slots free", total - used, total) end + return EllesmereUI.Lf("%d / %d Bag slots free", total - used, total) end - return EllesmereUI.Lf(reagent and "%d / %d Reagent slots" or "%d / %d Bag slots", used, total) + if reagent then return EllesmereUI.Lf("%d / %d Reagent slots", used, total) end + return EllesmereUI.Lf("%d / %d Bag slots", used, total) end local countText if separate then diff --git a/EllesmereUILocales/_keys.txt b/EllesmereUILocales/_keys.txt index e1fb1a3fb..56c2abe1c 100644 --- a/EllesmereUILocales/_keys.txt +++ b/EllesmereUILocales/_keys.txt @@ -1,6 +1,6 @@ # Auto-generated by .tools/extract-locale-keys.sh -- do not edit by hand. # Canonical list of translatable English keys passed as string literals -# (788 unique). Regenerate after wrapping new strings. Keys passed as +# (794 unique). Regenerate after wrapping new strings. Keys passed as # variables are not listed here -- use the in-game /euiloc harvester for # the complete runtime set. (Raid) @@ -23,7 +23,13 @@ %1$s is bound to \ %1$s's %2$s Breakdown %1$s's Death Recap +%d / %d Bag slots +%d / %d Bag slots free %d / %d Items +%d / %d Reagent slots +%d / %d Reagent slots free +%d Bag slots free +%d Reagent slots free %d junk item(s) could not be sold. %s is also assigned to: (Applies on Window Close) diff --git a/tests/run_bag_count_tests.py b/tests/run_bag_count_tests.py new file mode 100644 index 000000000..0eb4f942b --- /dev/null +++ b/tests/run_bag_count_tests.py @@ -0,0 +1,29 @@ +"""Focused production-source regression tests. Requires lupa (Lua 5.1).""" +from pathlib import Path +import sys +import re +from lupa.lua51 import LuaRuntime +ROOT = Path(sys.argv[1]) if len(sys.argv) > 1 else Path(__file__).resolve().parents[1] +def source(pr, file): + return (ROOT / file).read_text(encoding="utf-8-sig") +def between(text, start, end): + return text[text.index(start):text.index(end, text.index(start))] +def runtime(): + return LuaRuntime(unpack_returned_tuples=True) + +bag = source(2074, 'EllesmereUIBags/EllesmereUIBags.lua') +fragment = between(bag, ' local function FormatSlots(', ' local countText') +keys=[] +for mode in ('used_total','free_total','free'): + lua=runtime() + lua.globals().mode=mode + lua.execute('EllesmereUI={Lf=function(k,...) return string.format(k,...) end}') + lua.execute('local format=mode\n'+fragment+'\nFormat=FormatSlots') + for reagent in (False,True): + text=lua.globals().Format(10,40,reagent) + assert ('Reagent' in text)==reagent + assert text.startswith('10 / 40' if mode=='used_total' else '30 / 40' if mode=='free_total' else '30 ') +import re +keys=re.findall(r'EllesmereUI\.Lf\("([^"]+)"',fragment) +assert len(set(keys))==6, 'all six strings must be statically extractable' +print('PASS: six slot count formats and static locale extraction')