File Details
v0.4.10-3-g70c086f
- A
- Jul 3, 2026
- 566.57 KB
- 13
- 12.0.7+3
- Classic + 3
File Name
BetterBags-v0.4.10-3-g70c086f.zip
Supported Versions
- 12.0.7
- 5.5.4
- 2.5.5
- 1.15.8
BetterBags
v0.4.10-3-g70c086f (2026-07-03)
Full Changelog Previous Releases
- fix: fix bank button lock overlay and mouse interaction issues (#993)
- Inside SetItemFromData and SetFreeSlots in frames/item.lua, ensure we safely invoke UpdateExtended() on both self.button (the secure physical button) and the themed decoration overlay button.
- At startup when the bank is closed, C_Container.GetContainerNumSlots() returns 0 for bank bags, causing both physical and decoration buttons to initialize in an extended/locked state (showing padlock overlays and disabling mouse interaction).
- By updating both self.button and decoration's extended states when loading bank items or free slots (once the bank actually opens), we correctly remove the lock overlays and re-enable mouse interaction across the entire bank layout.
- Added comprehensive unit tests in spec/frames/item_spec.lua to assert correct UpdateExtended invocation on both buttons for occupied (SetItem) and empty (SetFreeSlots) bag slots.
- Expanded WoW mocks in spec/helpers/wow_mocks.lua to support IsAccountSecured, ContainerFrame_GetContainerNumSlots, ContainerIDToInventoryID, GetInventoryItemLink, and subclass/enabled methods on ItemButton widgets, aligning the test runtime perfectly with WoW's native API contracts.
- Fix: Blank custom tabs after UI loading (First-time lazy initialization) (#992)
- Refactor: Implement persistent tab-scoped views to prevent nil slotkey crash
- Replaced the shared-view-with-destructive-wipe design with a lazy-allocated persistent view per tab (tabID-scoped View).
- Implemented robust, non-destructive changeset filtering inside GridView and BagView based on tab membership, ensuring that item and section updates are incrementally applied only to their owning tab view.
- Improved the button creation/update state machine in grid and bag views (UpdateButton / ReconcileStack) to strictly guard against nil item data and prevent mapping ghost button frames.
- Resolved the string.split crash during asynchronous or transient WoW data loads during tab switching, achieving a perfect data state with zero defensive nil-guards.
- Cleanly integrated tab view reclamation and resource recycling when custom group tabs are deleted.
- Added comprehensive integration tests in spec/views/persistent_tabs_spec.lua verifying both state-consistency safety and tab deletion recycling.
- fix(linter): remove unused 'views' variable in classic/era bag.lua
- Removed the unused import 'local views = addon:GetModule('Views')' at line 23 in frames/classic/bag.lua and frames/era/bag.lua.
- This resolves the luacheck (W211) warning regarding the unused variable 'views' left behind after the lazy tabViews refactoring.
- Confirmed luacheck passes cleanly and all 867 unit tests run successfully.
- refactor(frames): implement static cache and remove redundant ID mutations in item buttons
- Introduce a permanent 1-to-1 physical slot to button registry to completely eliminate dynamic frame pool mutations and combat lockdown taint.
- Setup pre-populated buttons for backpack and bank bag types during initialization.
- Remove redundant ID/bagID/decoration mutations from SetItemFromData, SetFreeSlots, and ClearItem in frames/item.lua and frames/era/item.lua.
- Resolve merged free slot virtual button bypass in views/gridview.lua by using physical static keys provided by slotInfo.freeSlotKeys.
- Update bag views and grid views to use the static item buttons.
- Add a new unit test suite spec/frames/item_spec.lua to validate static button caching, layout parenting, and initialization.
- Ensure all 875 tests and luacheck linting pass cleanly without warnings.
- fix: clamp SetFrameLevel calculation to valid range [0, 65535]
In 'frames/item.lua', the frame level of the theme decoration overlay behind the item button was set using 'self.button:GetFrameLevel() - 1'. Since 'self.button' can have a frame level of 0 (e.g. after being parentless or unparented), this subtraction evaluates to -1, which is outside the valid range [0, 65535] allowed by Blizzard's SetFrameLevel API and triggers a fatal Lua error.
To resolve this issue:
- Updated the mock implementation of 'SetFrameLevel' in 'spec/helpers/wow_mocks.lua' and 'spec/frames/item_spec.lua' to strictly enforce the valid [0, 65535] frame level range, aligning our test suite contract with the real WoW environment.
- Clamped the decoration frame level calculation in 'frames/item.lua' using 'math.max(0, self.button:GetFrameLevel() - 1)' to guarantee a valid non-negative integer.
- Added a new TDD test in 'spec/frames/item_spec.lua' that mocks an item button at frame level 0, triggers 'SetItem', and verifies that the calculation is safely clamped to 0 without throwing errors.
- Resolved several test mock issues including a 'Themes.GetItemButton' argument order bug and missing global WoW function stubs (e.g., 'SetItemButtonCount', 'SetItemButtonDesaturated', 'SetHasItem').
Verified all 872 tests successfully pass and 'luacheck' returns 0 warnings.
- fix: resolve GetBagID nil parent crash during combat
Summary:
- Removed virtual/dynamic fallbacks for empty slots in gridview
- Switched to native Blizzard Initialize(bag, slot) for button creation
- Expanded static pre-population to cover all possible bags and slots
- Added mocks for SetBagID, GetBagID, and Initialize
Motivation:
Fixes a nil parent crash when trying to dynamically create and initialize buttons during combat, which would otherwise trigger secure taint/action blocked errors.
Testing:
All 876 tests pass with busted, luacheck reports 0 warnings/errors.
- fix: resolve GetBagID nil parent crash during template Initialize
Summary:
- Introduced a unique, dedicated parent frame per physical item button in both Retail (`frames/item.lua`) and Classic/Era (`frames/era/item.lua`).
- Configured each parent frame with the correct bag ID using `parent:SetID(bagID)` and defined `parent.IsCombinedBagContainer = function() return false end`.
- Parented the secure `ItemButton`/`Button` to this parent frame on creation, ensuring a valid parent frame exists during Blizzard's native `Initialize(bag, slot)` call.
- Maintained compatibility with layout and scrolling engines by designating the parent frame as `item.frame` (the positioned and scrolled element).
- Updated unit test mock in `spec/helpers/wow_mocks.lua` to enforce Blizzard's native contract, and updated `spec/frames/item_spec.lua` to verify the new parent frame behaviour.
- Fixed all luacheck warnings and confirmed all 876 tests pass successfully.
- docs: document WoW UI Parent Frame and Blizzard Mixin patterns
- Initialize custom tab views with all current items on first render
We recently switched to static item buttons instead of dynamic pools to increase speed and prevent frame cycling bugs. However, when switching to a custom tab for the first time, its view is lazily created. Since the initial login/reload changeset has already been fully processed, subsequent requests to switch tabs obtain an empty delta changeset, resulting in blank custom tabs until a manual sort is triggered.
This change:
- Adds an isNew flag on views (View type) which is initialized to true when a view is created.
- In BagView and GridView, checks if ctx:GetBool('redraw') or view.isNew is true.
- If true, triggers a clean view wipe, resets isNew to false, and populates the added set with all non-empty items from slotInfo:GetCurrentItems(), successfully drawing the current items for the tab.
- Adds robust unit tests in spec/views/persistent_tabs_spec.lua to verify isNew creation behavior and full redraw on first rendering for both BagView and GridView (complying with PRIME DIRECTIVE #1: Test-First TDD).
- Updates mocks in spec/views/bagview_spec.lua and spec/views/gridview_spec.lua to support the first-render initialization logic.
Lints clean with luacheck and passes all 878 unit tests.
- Update state/architecture patterns with lazy tab view initialization details
- Refactor: Implement persistent tab-scoped views to prevent nil slotkey crash (#989)
- Refactor: Implement persistent tab-scoped views to prevent nil slotkey crash
- Replaced the shared-view-with-destructive-wipe design with a lazy-allocated persistent view per tab (tabID-scoped View).
- Implemented robust, non-destructive changeset filtering inside GridView and BagView based on tab membership, ensuring that item and section updates are incrementally applied only to their owning tab view.
- Improved the button creation/update state machine in grid and bag views (UpdateButton / ReconcileStack) to strictly guard against nil item data and prevent mapping ghost button frames.
- Resolved the string.split crash during asynchronous or transient WoW data loads during tab switching, achieving a perfect data state with zero defensive nil-guards.
- Cleanly integrated tab view reclamation and resource recycling when custom group tabs are deleted.
- Added comprehensive integration tests in spec/views/persistent_tabs_spec.lua verifying both state-consistency safety and tab deletion recycling.
- fix(linter): remove unused 'views' variable in classic/era bag.lua
- Removed the unused import 'local views = addon:GetModule('Views')' at line 23 in frames/classic/bag.lua and frames/era/bag.lua.
- This resolves the luacheck (W211) warning regarding the unused variable 'views' left behind after the lazy tabViews refactoring.
- Confirmed luacheck passes cleanly and all 867 unit tests run successfully.
- fix(views): fix empty tabs, help text parenting, bank tabs contents mismatch, and add CPU gating
- Fix Help Text parenting and visibility: parent emptyGroupFrame (backpack help text frame) to the view content container instead of parent bag frame so that hiding the view content scrollbox automatically toggles the help text visibility. Also check section visibility against view.tabID rather than activeGroup so that background-rendering other tab views doesn't pollute the main Backpack tab's visibility logic.
- Fix Empty Tab Views: Render and reconcile all instantiated persistent views during bag:Draw() so that hidden tab views do not miss transient item changesets (added/removed/changed), and hide their contents after rendering so only the active view is displayed.
- Fix Bank Tab contents mismatch under Show Bags mode: If GetShowBankTabs() is enabled in Retail, refresh and scan ALL bank bags (both Character bank and Account/Warband bank bags) into slotInfo so that persistent views can dynamically and correctly filter and show items for each tab using ItemBelongsToTab.
- Implement closed-bag CPU gating: check bag:IsShown() on Draw() and skip rendering when the bag is closed/hidden, saving slotInfo and setting drawPendingOnShow = true. When the bag opens via bag:Show(), clear the flag and perform a deferred draw with the cached slotInfo.
- Added comprehensive unit and integration tests in spec/views/persistent_tabs_spec.lua verifying each bug fix, zero linter warnings or errors.