Empty category for bank category view

This commit is contained in:
Vati
2026-02-28 05:22:08 +04:00
parent 02001fb1ce
commit c25d43ad70
4 changed files with 426 additions and 2 deletions
+2
View File
@@ -765,6 +765,7 @@
end
end
end
Guda_ClearCursorItem()
</OnReceiveDrag>
</Scripts>
</Frame>
@@ -809,6 +810,7 @@
end
end
end
Guda_ClearCursorItem()
</OnReceiveDrag>
</Scripts>
</Frame>
+46 -2
View File
@@ -845,9 +845,53 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
for _, catName in ipairs(categoryList) do
local catDef = addon.Modules.CategoryManager and addon.Modules.CategoryManager:GetCategory(catName) or nil
-- Skip Empty category in bank (bank doesn't need empty slot indicator in category loop)
-- Handle Empty category: show a single slot with free slot count
if catDef and catDef.isEmptyCategory then
-- Skip empty categories in bank view
if totalFreeSlots > 0 and catDef.enabled then
local blockWidth = 1 * (buttonSize + spacing)
local blockHeight = 20 + (1 * (buttonSize + spacing)) + 5
if currentX > 0 and currentX + blockWidth + 20 > totalWidth + 5 then
currentX = 0
currentY = currentY + rowMaxHeight
rowMaxHeight = 0
end
local header = self:GetSectionHeader(headerIdx)
headerIdx = headerIdx + 1
header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", startX + currentX, startY - currentY)
header:SetWidth(blockWidth)
local displayName = (catDef and catDef.name) or catName
header.fullName = displayName
header.isShortened = false
header.text:SetText(displayName)
if header.countText then header.countText:Hide() end
header:Show()
local itemY = currentY + 20
local bagID = firstFreeBag or -1
local slotID = firstFreeSlot or 1
local bagParent = self:GetBagParent(bagID)
local button = Guda_GetItemButton(bagParent)
button:SetParent(bagParent)
button:SetWidth(buttonSize)
button:SetHeight(buttonSize)
button:ClearAllPoints()
button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", startX + currentX, startY - itemY)
button:Show()
local emptyItemData = {
texture = "Interface\\PaperDoll\\UI-PaperDoll-Slot-Bag",
count = totalFreeSlots,
name = "Empty Slots"
}
Guda_ItemButton_SetItem(button, bagID, slotID, emptyItemData, true, isOtherChar and charName or nil, true, true)
button.isReadOnly = false
button.inUse = true
if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end
currentX = currentX + blockWidth + 20
end
else
local items = categories[catName]
+48
View File
@@ -574,6 +574,30 @@
searchBox:ClearFocus()
end
</OnMouseDown>
<OnReceiveDrag>
-- Drop item into first available bank slot
if CursorHasItem and CursorHasItem() then
-- Try main bank slots (bag -1)
for slot = 1, GetContainerNumSlots(-1) do
if not GetContainerItemLink(-1, slot) then
PickupContainerItem(-1, slot)
break
end
end
-- Try bank bags 5-10
for bag = 5, 10 do
if CursorHasItem() then
for slot = 1, GetContainerNumSlots(bag) do
if not GetContainerItemLink(bag, slot) then
PickupContainerItem(bag, slot)
break
end
end
end
end
end
Guda_ClearCursorItem()
</OnReceiveDrag>
</Scripts>
</Frame>
</Frames>
@@ -602,6 +626,30 @@
<OnMouseUp>
this:StopMovingOrSizing()
</OnMouseUp>
<OnReceiveDrag>
-- Drop item into first available bank slot
if CursorHasItem and CursorHasItem() then
-- Try main bank slots (bag -1)
for slot = 1, GetContainerNumSlots(-1) do
if not GetContainerItemLink(-1, slot) then
PickupContainerItem(-1, slot)
break
end
end
-- Try bank bags 5-10
for bag = 5, 10 do
if CursorHasItem() then
for slot = 1, GetContainerNumSlots(bag) do
if not GetContainerItemLink(bag, slot) then
PickupContainerItem(bag, slot)
break
end
end
end
end
end
Guda_ClearCursorItem()
</OnReceiveDrag>
</Scripts>
</Frame>
+330
View File
@@ -6,6 +6,10 @@ local buttonPool = {}
local nextButtonID = 1
local BUTTON_POOL_MAX = 500 -- Maximum buttons to create (bags ~80 + bank ~200 + keyring ~96 + buffer for both frames open)
-- Category drag-drop: track cursor item for category reassignment
local cursorItemInfo = nil -- { bagID, slotID, itemID, link }
local activeCategoryDropIndicator = nil -- the currently shown "+" indicator button
-- Get button pool statistics (for /guda perf command)
function Guda_GetButtonPoolStats()
local total = 0
@@ -67,6 +71,222 @@ function Guda_ReleaseAllButtons()
end
end
-- Category drop indicator: a single shared frame (parented to UIParent) with plus icon
local HideCategoryDropIndicator -- forward declaration
local categoryDropIndicator = nil -- the single shared indicator frame
local dropIndicatorCategoryId = nil -- category the indicator is currently showing for
local dropCooldownTime = 0 -- GetTime() when cooldown expires
-- Helper: get category of cursor item using tracked info
local function GetCursorItemCategory()
local info = Guda_GetCursorItemInfo()
addon:Debug("GetCursorItemCat: info=%s", tostring(info ~= nil))
if not info or not info.itemID or not addon.Modules.CategoryManager then
addon:Debug("GetCursorItemCat: BAIL - info=%s itemID=%s catMgr=%s", tostring(info ~= nil), tostring(info and info.itemID), tostring(addon.Modules.CategoryManager ~= nil))
return nil
end
local itemName, itemLink, itemQuality, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture
if info.link then
itemName, itemLink, itemQuality, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture = GetItemInfo(info.link)
end
addon:Debug("GetCursorItemCat: itemName=%s itemType=%s", tostring(itemName), tostring(itemType))
if not itemName then return nil end
local itemData = {
link = info.link,
itemID = info.itemID,
name = itemName,
quality = itemQuality or 0,
class = itemType or "",
subClass = itemSubType or "",
equipLoc = itemEquipLoc or "",
stackCount = itemStackCount or 1,
level = itemLevel or 0,
minLevel = itemMinLevel or 0,
texture = itemTexture,
}
return addon.Modules.CategoryManager:CategorizeItem(itemData, info.bagID, info.slotID)
end
-- Create the single shared indicator frame (once)
local function GetOrCreateIndicator()
if categoryDropIndicator then return categoryDropIndicator end
local f = CreateFrame("Frame", "Guda_CategoryDropIndicator", UIParent)
f:SetWidth(36)
f:SetHeight(36)
f:SetFrameStrata("FULLSCREEN_DIALOG")
f:SetFrameLevel(500)
-- Slot background (same texture as empty bag slot, with green tint like GudaBags)
local slotBg = f:CreateTexture(nil, "BACKGROUND")
slotBg:SetPoint("TOPLEFT", f, "TOPLEFT", -9, 9)
slotBg:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 9, -9)
slotBg:SetTexture("Interface\\Buttons\\UI-EmptySlot")
slotBg:SetVertexColor(0.4, 0.8, 0.4, 0.9)
f.slotBg = slotBg
-- Plus icon centered
local plus = f:CreateTexture(nil, "OVERLAY")
plus:SetWidth(20)
plus:SetHeight(20)
plus:SetPoint("CENTER", f, "CENTER", 0, 0)
plus:SetTexture("Interface\\AddOns\\Guda\\Assets\\plus")
f.plus = plus
-- Mouse-enabled for drops
f:EnableMouse(true)
-- Handle drop on the indicator itself
local function DoIndicatorDrop()
if not activeCategoryDropIndicator then return end
local parentBtn = activeCategoryDropIndicator
if parentBtn and parentBtn:GetScript("OnReceiveDrag") then
local savedThis = getfenv(0)["this"]
getfenv(0)["this"] = parentBtn
parentBtn:GetScript("OnReceiveDrag")()
getfenv(0)["this"] = savedThis
end
end
f:SetScript("OnReceiveDrag", DoIndicatorDrop)
f:SetScript("OnMouseUp", function()
if CursorHasItem and CursorHasItem() then
DoIndicatorDrop()
end
end)
-- Tooltip on hover
f:SetScript("OnEnter", function()
if dropIndicatorCategoryId then
GameTooltip:SetOwner(this, "ANCHOR_TOP")
GameTooltip:SetText("Add item to this category", 1, 1, 1)
GameTooltip:AddLine("Drop here to permanently assign", 0.7, 0.7, 0.7)
GameTooltip:AddLine("this item to \"" .. tostring(dropIndicatorCategoryId) .. "\"", 0.5, 1, 0.5)
GameTooltip:Show()
end
end)
f:SetScript("OnLeave", function()
GameTooltip:Hide()
-- Delay hide: use an OnUpdate check instead of C_Timer
local elapsed = 0
this:SetScript("OnUpdate", function()
elapsed = elapsed + arg1
if elapsed >= 0.05 then
this:SetScript("OnUpdate", nil)
-- Check if mouse is still over the indicator or the parent button
if activeCategoryDropIndicator and MouseIsOver(activeCategoryDropIndicator) then
return
end
if this:IsMouseOver() then
return
end
HideCategoryDropIndicator()
end
end)
end)
f:Hide()
categoryDropIndicator = f
return f
end
local function ShowCategoryDropIndicator(button)
if activeCategoryDropIndicator == button then return end
-- Don't show during drop cooldown
if GetTime() < dropCooldownTime then return end
HideCategoryDropIndicator()
local ind = GetOrCreateIndicator()
local size = button:GetWidth()
-- Size to match icon
ind:SetWidth(size)
ind:SetHeight(size)
-- Update plus icon size (60% of icon, min 16)
local plusSize = math.max(16, math.floor(size * 0.6))
ind.plus:SetWidth(plusSize)
ind.plus:SetHeight(plusSize)
-- Position below the hovered button using screen coordinates
local buttonLeft = button:GetLeft()
local buttonBottom = button:GetBottom()
ind:ClearAllPoints()
if buttonLeft and buttonBottom then
ind:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", buttonLeft, buttonBottom - 2)
end
-- Track state
dropIndicatorCategoryId = button.categoryId or nil
activeCategoryDropIndicator = button
ind:Show()
end
HideCategoryDropIndicator = function()
if categoryDropIndicator then
categoryDropIndicator:Hide()
categoryDropIndicator:SetScript("OnUpdate", nil)
GameTooltip:Hide()
end
activeCategoryDropIndicator = nil
dropIndicatorCategoryId = nil
end
-- Track what item the cursor is holding (called before pickup)
function Guda_TrackCursorItem(bagID, slotID)
local link = GetContainerItemLink(bagID, slotID)
if link then
local itemID = addon.Modules.Utils:ExtractItemID(link)
cursorItemInfo = { bagID = bagID, slotID = slotID, itemID = itemID, link = link }
addon:Debug("TrackCursor: bag=%d slot=%d itemID=%s", bagID, slotID, tostring(itemID))
else
cursorItemInfo = nil
addon:Debug("TrackCursor: no link at bag=%d slot=%d", bagID, slotID)
end
end
function Guda_ClearCursorItem()
cursorItemInfo = nil
end
function Guda_GetCursorItemInfo()
return cursorItemInfo
end
-- Check if we're in category view for bags or bank
local function IsInCategoryView(isBank)
if not addon.Modules.DB then return false end
local key = isBank and "bankViewType" or "bagViewType"
return (addon.Modules.DB:GetSetting(key) or "single") == "category"
end
-- Auto-clear cursor tracking when cursor is truly empty
-- Uses a short delay to avoid clearing during the pickup transition
-- (CURSOR_UPDATE fires before the item is fully on the cursor)
local cursorWatcher = CreateFrame("Frame")
cursorWatcher:RegisterEvent("CURSOR_UPDATE")
cursorWatcher:SetScript("OnEvent", function()
-- Don't clear immediately — wait a frame to let the pickup finish
if not cursorItemInfo then
HideCategoryDropIndicator()
return
end
-- Schedule a check next frame
this.pendingCheck = true
end)
cursorWatcher:SetScript("OnUpdate", function()
if not this.pendingCheck then return end
this.pendingCheck = nil
-- Now check if cursor actually has an item
if cursorItemInfo and (not CursorHasItem or not CursorHasItem()) then
cursorItemInfo = nil
HideCategoryDropIndicator()
end
end)
-- Use shared tooltip from Utils module (retrieved on-demand to ensure Utils is loaded)
-- Helper function to check if an item is a quest item
@@ -535,6 +755,14 @@ function Guda_ItemButton_OnLoad(self)
HideJunkIcon(this)
end)
-- Track cursor item on drag start for category drag-drop
self:SetScript("OnDragStart", function()
if this.hasItem and this.bagID and this.slotID and not this.otherChar and not this.isReadOnly then
Guda_TrackCursorItem(this.bagID, this.slotID)
PickupContainerItem(this.bagID, this.slotID)
end
end)
self:SetScript("OnClick", function()
if IsAltKeyDown() and arg1 == "LeftButton" and this.hasItem and not this.otherChar and not this.isReadOnly then
local link = GetContainerItemLink(this.bagID, this.slotID)
@@ -585,7 +813,62 @@ function Guda_ItemButton_OnLoad(self)
return
end
-- Track cursor item before pickup for category drag-drop
if this.hasItem and this.bagID and this.slotID and not CursorHasItem() then
Guda_TrackCursorItem(this.bagID, this.slotID)
end
ContainerFrameItemButton_OnClick(arg1)
-- Clear cursor tracking if item was placed (cursor no longer has item)
if not CursorHasItem() then
Guda_ClearCursorItem()
end
end
end)
-- OnReceiveDrag: category reassignment in category view
self:SetScript("OnReceiveDrag", function()
local info = Guda_GetCursorItemInfo()
if not info then return end
local inCatView = IsInCategoryView(this.isBank)
if not inCatView then
-- Single view: let default swap happen
if ContainerFrameItemButton_OnClick then
ContainerFrameItemButton_OnClick("LeftButton")
end
return
end
-- Category view: reassign dragged item to target item's category
if this.hasItem and this.itemData and addon.Modules.CategoryManager then
local targetCategory = addon.Modules.CategoryManager:CategorizeItem(this.itemData, this.bagID, this.slotID, this.otherChar)
if targetCategory and info.itemID then
addon.Modules.CategoryManager:AssignItemToCategory(info.itemID, targetCategory)
addon:Debug("Reassigned item %d to category: %s", info.itemID, targetCategory)
end
end
-- Put dragged item back in its original slot
if CursorHasItem() then
PickupContainerItem(info.bagID, info.slotID)
end
-- Set drop cooldown to prevent immediate re-show
dropCooldownTime = GetTime() + 0.3
HideCategoryDropIndicator()
Guda_ClearCursorItem()
-- Refresh frames
if addon.Modules.BagFrame and addon.Modules.BagFrame.Update then
addon.Modules.BagFrame:Update()
end
if addon.Modules.BankFrame and addon.Modules.BankFrame.Update then
local bankFrame = getglobal("Guda_BankFrame")
if bankFrame and bankFrame:IsShown() then
addon.Modules.BankFrame:Update()
end
end
end)
end
@@ -1420,6 +1703,30 @@ end
-- OnEnter handler (show tooltip)
function Guda_ItemButton_OnEnter(self)
addon:Debug("OnEnter FIRED: button=%s CursorHasItem=%s", tostring(self:GetName()), tostring(CursorHasItem and CursorHasItem()))
-- Category drag-drop: show "+" indicator when hovering with cursor item in category view
local hasCursor = CursorHasItem and CursorHasItem()
if hasCursor then
addon:Debug("DropInd OnEnter: hasItem=%s otherChar=%s isBank=%s", tostring(self.hasItem), tostring(self.otherChar), tostring(self.isBank))
end
if hasCursor and self.hasItem and not self.otherChar then
local inCatView = IsInCategoryView(self.isBank)
addon:Debug("DropInd: inCatView=%s hasItemData=%s hasCatMgr=%s", tostring(inCatView), tostring(self.itemData ~= nil), tostring(addon.Modules.CategoryManager ~= nil))
if inCatView and self.itemData and addon.Modules.CategoryManager then
-- Don't show indicator if dragged item is already in the same category
local targetCategory = addon.Modules.CategoryManager:CategorizeItem(self.itemData, self.bagID, self.slotID, self.otherChar)
local cursorCategory = GetCursorItemCategory()
addon:Debug("DropInd: targetCat=%s cursorCat=%s cooldown=%s", tostring(targetCategory), tostring(cursorCategory), tostring(GetTime() < dropCooldownTime))
if targetCategory and cursorCategory and targetCategory ~= cursorCategory then
self.categoryId = targetCategory
addon:Debug("DropInd: SHOWING indicator for %s", tostring(targetCategory))
ShowCategoryDropIndicator(self)
else
addon:Debug("DropInd: NOT showing - same=%s targetNil=%s cursorNil=%s", tostring(targetCategory == cursorCategory), tostring(targetCategory == nil), tostring(cursorCategory == nil))
end
end
end
-- Highlight the corresponding bag button in the footer (works for empty and filled slots)
if not self.otherChar and self.bagID then
if self.isBank then
@@ -1588,6 +1895,29 @@ end
-- OnLeave handler
function Guda_ItemButton_OnLeave(self)
-- Delay hide of drop indicator to allow mouse to move to indicator frame
if activeCategoryDropIndicator == self and categoryDropIndicator and categoryDropIndicator:IsShown() then
-- Use OnUpdate delay to check if mouse moved to the indicator
local elapsed = 0
local checkFrame = CreateFrame("Frame")
checkFrame:SetScript("OnUpdate", function()
elapsed = elapsed + arg1
if elapsed >= 0.05 then
this:SetScript("OnUpdate", nil)
-- If mouse is over the indicator, don't hide
if categoryDropIndicator and categoryDropIndicator:IsMouseOver() then
return
end
-- If mouse is over the parent button, don't hide
if activeCategoryDropIndicator and MouseIsOver(activeCategoryDropIndicator) then
return
end
HideCategoryDropIndicator()
end
end)
else
HideCategoryDropIndicator()
end
self._debugPrinted = nil
-- Clear any viewed character hint on the tooltip when leaving
if GameTooltip then