diff --git a/Core/Database.lua b/Core/Database.lua index 7e3c835..7bdeeb0 100644 --- a/Core/Database.lua +++ b/Core/Database.lua @@ -117,7 +117,7 @@ end -- Get current player's full name function DB:GetPlayerFullName() - return playerName + return playerName .. "-" .. playerRealm end -- Get current character data diff --git a/Core/Tooltip.lua b/Core/Tooltip.lua index 533ce93..442f0d6 100644 --- a/Core/Tooltip.lua +++ b/Core/Tooltip.lua @@ -218,6 +218,8 @@ function Tooltip:AddInventoryInfo(tooltip, link) local totalCount = totalBags + totalBank + totalEquipped if hasAnyItems then + + -- Top padding above the Inventory block (~10-12px visually) tooltip:AddLine(" ") -- Inventory label in exact bag frame title color @@ -263,8 +265,8 @@ function Tooltip:AddInventoryInfo(tooltip, link) tooltip:AddDoubleLine(displayName, countText, r, g, b, 1.0, 1.0, 1.0) end - -- Add small gap between inventory data and vendor sell price - tooltip:AddLine(" ") + -- Bottom padding below the Inventory block (~10-12px visually) + --tooltip:AddLine(" ") tooltip:Show() end @@ -275,6 +277,32 @@ function Tooltip:Initialize() -- Helper function to defer vendor money so we can insert our Inventory block above it local Orig_SetTooltipMoney = SetTooltipMoney + -- Move the tooltip money frame(s) vertically to fine-tune their position under our custom block + local function AdjustMoneyFrames(tooltip, yOffset) + if not tooltip or not tooltip.GetName then return end + local baseName = tooltip:GetName() + if not baseName then return end + + -- Collect potential money frame names used by WoW tooltips + local candidates = {} + -- Primary money frame + tinsert(candidates, baseName .. "MoneyFrame") + -- Sometimes multiple money frames are created with numeric suffixes + for i = 1, 8 do + tinsert(candidates, baseName .. "MoneyFrame" .. i) + tinsert(candidates, baseName .. "SmallMoneyFrame" .. i) + end + + for i = 1, getn(candidates) do + local f = getglobal(candidates[i]) + if f and f:IsShown() and f.GetPoint then + local point, relTo, relPoint, xOfs, yOfs = f:GetPoint(1) + if point then + f:SetPoint(point, relTo, relPoint, xOfs or 0, (yOfs or 0) + (yOffset or 0)) + end + end + end + end local function WithDeferredMoney(tooltip, buildFunc) local queue = {} -- Temporarily override global SetTooltipMoney @@ -295,30 +323,59 @@ function Tooltip:Initialize() local q = queue[i] Orig_SetTooltipMoney(q[1], q[2], q[3], q[4], q[5], q[6], q[7]) end + + -- Add ~5px more space below the Inventory block by reducing the upward nudge from 15px to 10px + --AdjustMoneyFrames(tooltip, 12) return ret end -- Hook SetBagItem local oldSetBagItem = GameTooltip.SetBagItem + local oldSetInventoryItem = GameTooltip.SetInventoryItem function GameTooltip:SetBagItem(bag, slot) return WithDeferredMoney(self, function() - local ret = oldSetBagItem(self, bag, slot) - local link = GetContainerItemLink(bag, slot) - if link then - Tooltip:AddInventoryInfo(self, link) + local bankFrame = getglobal("BankFrame") + if bag == -1 and bankFrame and bankFrame:IsVisible() then + local invSlot = BankButtonIDToInvSlotID(slot) + if invSlot then + -- Use the inventory item method for bank main bag + local ret = oldSetInventoryItem(self, "player", invSlot) + local link = GetInventoryItemLink("player", invSlot) + if link then + Tooltip:AddInventoryInfo(self, link) + end + return ret + end + return nil + else + local ret = oldSetBagItem(self, bag, slot) + local link = GetContainerItemLink(bag, slot) + if link then + Tooltip:AddInventoryInfo(self, link) + end + return ret end - return ret end) end - -- Hook SetHyperlink for chat links - local oldSetHyperlink = GameTooltip.SetHyperlink + -- Hook SetHyperlink for hyperlinks from chat and cached links + local oldSetHyperlink = GameTooltip.SetHyperlink function GameTooltip:SetHyperlink(link) return WithDeferredMoney(self, function() - local ret = oldSetHyperlink(self, link) - if link and strfind(link, "item:") then - Tooltip:AddInventoryInfo(self, link) + local _, _, inner = string.find(link or "", "|H(.+)|h") + local forwarded = link + local itemLinkForCounts = link + if inner then + forwarded = inner + if strfind(inner, "^item:") then + itemLinkForCounts = inner + end end + local ret = oldSetHyperlink(self, forwarded) + if itemLinkForCounts and strfind(itemLinkForCounts, "item:") then + Tooltip:AddInventoryInfo(self, itemLinkForCounts) + end + return ret end) end diff --git a/Data/BagScanner.lua b/Data/BagScanner.lua index 13b308a..05cc15c 100644 --- a/Data/BagScanner.lua +++ b/Data/BagScanner.lua @@ -104,17 +104,17 @@ function BagScanner:Initialize() -- Create event frame for bag updates local eventFrame = CreateFrame("Frame") self.eventFrame = eventFrame - + -- Register bag update events eventFrame:RegisterEvent("BAG_UPDATE") eventFrame:RegisterEvent("BAG_UPDATE_DELAYED") - + eventFrame:SetScript("OnEvent", function() if event == "BAG_UPDATE" or event == "BAG_UPDATE_DELAYED" then addon:Debug("Bag update detected, saving data...") self:SaveToDatabase() end end) - + addon:Debug("Bag scanner initialized with auto-save") end \ No newline at end of file diff --git a/Guda.toc b/Guda.toc index 550365c..037f9e7 100644 --- a/Guda.toc +++ b/Guda.toc @@ -2,7 +2,7 @@ ## Title: Guda ## Notes: All-in-one bag and bank addon for Turtle WoW ## Author: Vati -## Version: 1.1.2 +## Version: 1.1.3 ## SavedVariables: Guda_DB ## SavedVariablesPerCharacter: Guda_CharDB @@ -25,7 +25,6 @@ Sorting\SortEngine.lua UI\ItemButton.lua UI\BagFrame.lua UI\BankFrame.lua -UI\MoneyDisplay.lua UI\SettingsPopup.lua UI\ItemButton.xml diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index 6b94cf7..30c6d42 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -11,725 +11,785 @@ local searchText = "" local itemButtons = {} local showKeyring = false -- Toggle for keyring display local hiddenBags = {} -- Track which bags are hidden (bagID -> true/false) +local bagParents = {} -- Per-bag parent frames to carry bagID for Blizzard item button templates -- Global click catcher for clearing search focus local clickCatcher = nil -- Get player race icon path (using racial ability icons) function Guda_GetPlayerRaceIcon() - local _, race = UnitRace("player") + local _, race = UnitRace("player") - -- Use racial ability icons that exist in vanilla - local raceIcons = { - Human = "Interface\\Icons\\Spell_Magic_PolymorphPig", - Dwarf = "Interface\\Icons\\Spell_Shadow_UnholyFrenzy", - NightElf = "Interface\\Icons\\Spell_Nature_Invisibility", - Gnome = "Interface\\Icons\\Ability_Repair", - Orc = "Interface\\Icons\\Ability_Racial_BloodRage", - Undead = "Interface\\Icons\\Spell_Shadow_RaiseDead", - Tauren = "Interface\\Icons\\Ability_Thunderclap", - Troll = "Interface\\Icons\\Ability_Racial_Avatar", - } + -- Use racial ability icons that exist in vanilla + local raceIcons = { + Human = "Interface\\Icons\\Spell_Magic_PolymorphPig", + Dwarf = "Interface\\Icons\\Spell_Shadow_UnholyFrenzy", + NightElf = "Interface\\Icons\\Spell_Nature_Invisibility", + Gnome = "Interface\\Icons\\Ability_Repair", + Orc = "Interface\\Icons\\Ability_Racial_BloodRage", + Undead = "Interface\\Icons\\Spell_Shadow_RaiseDead", + Tauren = "Interface\\Icons\\Ability_Thunderclap", + Troll = "Interface\\Icons\\Ability_Racial_Avatar", + } - return raceIcons[race] or "Interface\\Icons\\INV_Misc_GroupNeedMore" + return raceIcons[race] or "Interface\\Icons\\INV_Misc_GroupNeedMore" end -- OnLoad function Guda_BagFrame_OnLoad(self) - -- Set up search box placeholder - local searchBox = getglobal(self:GetName().."_SearchBar_SearchBox") - if searchBox then - searchBox:SetText("Search, try ~equipment") - searchBox:SetTextColor(0.5, 0.5, 0.5, 1) - end +-- Set up search box placeholder + local searchBox = getglobal(self:GetName().."_SearchBar_SearchBox") + if searchBox then + searchBox:SetText("Search, try ~equipment") + searchBox:SetTextColor(0.5, 0.5, 0.5, 1) + end - -- Create invisible full-screen frame to catch clicks outside bag - if not clickCatcher then - clickCatcher = CreateFrame("Frame", "Guda_ClickCatcher", UIParent) - clickCatcher:SetFrameStrata("BACKGROUND") - clickCatcher:SetAllPoints(UIParent) - clickCatcher:EnableMouse(true) - clickCatcher:Hide() + -- Create invisible full-screen frame to catch clicks outside bag + if not clickCatcher then + clickCatcher = CreateFrame("Frame", "Guda_ClickCatcher", UIParent) + clickCatcher:SetFrameStrata("BACKGROUND") + clickCatcher:SetAllPoints(UIParent) + clickCatcher:EnableMouse(true) + clickCatcher:Hide() - clickCatcher:SetScript("OnMouseDown", function() - Guda_BagFrame_ClearSearch() - end) - end + clickCatcher:SetScript("OnMouseDown", function() + Guda_BagFrame_ClearSearch() + end) + end end -- OnShow function Guda_BagFrame_OnShow(self) - -- Save bag data when opening bags - addon.Modules.BagScanner:SaveToDatabase() - addon.Modules.MoneyTracker:Update() +-- Save bag data when opening bags + addon.Modules.BagScanner:SaveToDatabase() + addon.Modules.MoneyTracker:Update() - -- Restore saved position if it exists (only if saved as BOTTOMRIGHT) - if addon and addon.Modules and addon.Modules.DB then - local pos = addon.Modules.DB:GetSetting("bagFramePosition") - if pos and pos.point == "BOTTOMRIGHT" and pos.x and pos.y then - self:ClearAllPoints() - self:SetPoint("BOTTOMRIGHT", "UIParent", "BOTTOMRIGHT", pos.x, pos.y) - end - end + -- Restore saved position if it exists (only if saved as BOTTOMRIGHT) + if addon and addon.Modules and addon.Modules.DB then + local pos = addon.Modules.DB:GetSetting("bagFramePosition") + if pos and pos.point == "BOTTOMRIGHT" and pos.x and pos.y then + self:ClearAllPoints() + self:SetPoint("BOTTOMRIGHT", "UIParent", "BOTTOMRIGHT", pos.x, pos.y) + end + end - -- Set lock state when frame is shown (ensures all child frames are loaded) - if BagFrame.UpdateLockState then - BagFrame:UpdateLockState() - end + -- Set lock state when frame is shown (ensures all child frames are loaded) + if BagFrame.UpdateLockState then + BagFrame:UpdateLockState() + end - -- Apply border visibility setting - if BagFrame.UpdateBorderVisibility then - BagFrame:UpdateBorderVisibility() - end + -- Apply border visibility setting + if BagFrame.UpdateBorderVisibility then + BagFrame:UpdateBorderVisibility() + end - -- Apply search bar visibility setting - if BagFrame.UpdateSearchBarVisibility then - BagFrame:UpdateSearchBarVisibility() - end + -- Apply search bar visibility setting + if BagFrame.UpdateSearchBarVisibility then + BagFrame:UpdateSearchBarVisibility() + end - BagFrame:Update() + BagFrame:Update() end -- OnHide function Guda_BagFrame_OnHide(self) - -- Only release buttons that belong to this frame - local itemContainer = getglobal("Guda_BagFrame_ItemContainer") - if itemContainer then - local children = { itemContainer:GetChildren() } - for _, child in ipairs(children) do - if child.hasItem ~= nil then -- It's an item button - child:Hide() - child:ClearAllPoints() - end - end - end + -- Clean up all buttons when frame is hidden (safe since we're not displaying) + for _, bagParent in pairs(bagParents) do + if bagParent then + local buttons = { bagParent:GetChildren() } + for _, button in ipairs(buttons) do + if button.hasItem ~= nil then + button:Hide() + button:ClearAllPoints() + end + end + end + end end -- Toggle visibility function BagFrame:Toggle() - if Guda_BagFrame:IsShown() then - Guda_BagFrame:Hide() - else - Guda_BagFrame:Show() - end + if Guda_BagFrame:IsShown() then + Guda_BagFrame:Hide() + else + Guda_BagFrame:Show() + end end -- Show specific character's bags function BagFrame:ShowCharacter(fullName) - currentViewChar = fullName - self:Update() + currentViewChar = fullName + self:Update() end -- Show current character function BagFrame:ShowCurrentCharacter() - currentViewChar = nil - self:Update() + currentViewChar = nil + self:Update() +end + +-- Update lock states of existing buttons (lightweight, used during drag) +function BagFrame:UpdateLockStates() + for _, bagParent in pairs(bagParents) do + if bagParent then + local buttons = { bagParent:GetChildren() } + for _, button in ipairs(buttons) do + if button.hasItem ~= nil and button:IsShown() and button.bagID and button.slotID then + -- Get live lock state + local _, _, locked = GetContainerItemInfo(button.bagID, button.slotID) + -- Update desaturation (gray out locked items) + if not button.otherChar and not button.isReadOnly and SetItemButtonDesaturated then + SetItemButtonDesaturated(button, locked, 0.5, 0.5, 0.5) + end + end + end + end + end end -- Update display function BagFrame:Update() - if not Guda_BagFrame:IsShown() then - return - end + if not Guda_BagFrame:IsShown() then + return + end - -- Only release buttons that belong to this frame - local itemContainer = getglobal("Guda_BagFrame_ItemContainer") - if itemContainer then - local children = { itemContainer:GetChildren() } - for _, child in ipairs(children) do - if child.hasItem ~= nil then -- It's an item button - child:Hide() - child:ClearAllPoints() - end - end - end + -- If cursor is holding an item (mid-drag), only update lock states, don't rebuild UI + if CursorHasItem and CursorHasItem() then + self:UpdateLockStates() + return + end - local bagData - local isOtherChar = false - local charName = "" + -- Mark all existing buttons as not in use (we'll mark active ones during display) + for _, bagParent in pairs(bagParents) do + if bagParent then + local buttons = { bagParent:GetChildren() } + for _, button in ipairs(buttons) do + if button.hasItem ~= nil then + button.inUse = false + end + end + end + end - local titleFont = getglobal("Guda_BagFrame_Title") - local displayName + local bagData + local isOtherChar = false + local charName = "" - if currentViewChar then - -- Viewing another character - bagData = addon.Modules.DB:GetCharacterBags(currentViewChar) - isOtherChar = true - charName = currentViewChar + local titleFont = getglobal("Guda_BagFrame_Title") + local displayName - local dash = string.find(currentViewChar, "-") - if dash then - displayName = string.sub(currentViewChar, 1, dash - 1) - else - displayName = currentViewChar - end - else - -- Viewing current character - bagData = addon.Modules.BagScanner:ScanBags() - displayName = UnitName("player") or "Character" - end + if currentViewChar then + -- Viewing another character + bagData = addon.Modules.DB:GetCharacterBags(currentViewChar) + isOtherChar = true + charName = currentViewChar - if titleFont and displayName then - titleFont:SetText(string.format("%s's Bags", displayName)) - end + local dash = string.find(currentViewChar, "-") + if dash then + displayName = string.sub(currentViewChar, 1, dash - 1) + else + displayName = currentViewChar + end + else + -- Viewing current character + bagData = addon.Modules.BagScanner:ScanBags() + displayName = UnitName("player") or "Character" + end - -- Display items - self:DisplayItems(bagData, isOtherChar, charName) + if titleFont and displayName then + titleFont:SetText(string.format("%s's Bags", displayName)) + end - -- Update money - self:UpdateMoney() + -- Display items + self:DisplayItems(bagData, isOtherChar, charName) - -- Update bag slots info - self:UpdateBagSlotsInfo(bagData, isOtherChar) + -- Update money + self:UpdateMoney() + + -- Update bag slots info + self:UpdateBagSlotsInfo(bagData, isOtherChar) + + -- Clean up unused buttons AFTER display is complete (prevents drag/drop issues) + for _, bagParent in pairs(bagParents) do + if bagParent then + local buttons = { bagParent:GetChildren() } + for _, button in ipairs(buttons) do + if button.hasItem ~= nil and not button.inUse then + button:Hide() + button:ClearAllPoints() + end + end + end + end end -- Display items function BagFrame:DisplayItems(bagData, isOtherChar, charName) - local x, y = 10, -10 - local row = 0 - local col = 0 - local buttonSize = addon.Modules.DB:GetSetting("iconSize") or addon.Constants.BUTTON_SIZE - local spacing = addon.Modules.DB:GetSetting("iconSpacing") or addon.Constants.BUTTON_SPACING - local perRow = addon.Modules.DB:GetSetting("bagColumns") or 10 - local itemContainer = getglobal("Guda_BagFrame_ItemContainer") + local x, y = 10, -10 + local row = 0 + local col = 0 + local buttonSize = addon.Modules.DB:GetSetting("iconSize") or addon.Constants.BUTTON_SIZE + local spacing = addon.Modules.DB:GetSetting("iconSpacing") or addon.Constants.BUTTON_SPACING + local perRow = addon.Modules.DB:GetSetting("bagColumns") or 10 + local itemContainer = getglobal("Guda_BagFrame_ItemContainer") - -- Separate bags into regular, soul, and ammo/quiver types - local regularBags = {} - local soulBags = {} - local ammoQuiverBags = {} + -- Separate bags into regular, soul, and ammo/quiver types + local regularBags = {} + local soulBags = {} + local ammoQuiverBags = {} - for _, bagID in ipairs(addon.Constants.BAGS) do - -- Skip hidden bags - if not hiddenBags[bagID] then - local bagType - if isOtherChar then - -- For other characters, use saved bag type - local bag = bagData[bagID] - bagType = bag and bag.bagType or "regular" - else - -- For current character, detect bag type in real-time - if addon.Modules.Utils:IsSoulBag(bagID) then - bagType = "soul" - elseif addon.Modules.Utils:IsAmmoQuiverBag(bagID) then - bagType = "ammo" - else - bagType = "regular" - end - end + for _, bagID in ipairs(addon.Constants.BAGS) do + -- Skip hidden bags + if not hiddenBags[bagID] then + local bagType + if isOtherChar then + -- For other characters, use saved bag type + local bag = bagData[bagID] + bagType = bag and bag.bagType or "regular" + else + -- For current character, detect bag type in real-time + if addon.Modules.Utils:IsSoulBag(bagID) then + bagType = "soul" + elseif addon.Modules.Utils:IsAmmoQuiverBag(bagID) then + bagType = "ammo" + else + bagType = "regular" + end + end - if bagType == "soul" then - table.insert(soulBags, bagID) - elseif bagType == "ammo" then - table.insert(ammoQuiverBags, bagID) - else - table.insert(regularBags, bagID) - end - end - end + if bagType == "soul" then + table.insert(soulBags, bagID) + elseif bagType == "ammo" then + table.insert(ammoQuiverBags, bagID) + else + table.insert(regularBags, bagID) + end + end + end - -- Build display order: regular bags -> soul bags -> ammo/quiver bags -> keyring - local bagsToShow = {} - for _, bagID in ipairs(regularBags) do - table.insert(bagsToShow, {bagID = bagID, needsSpacing = false}) - end + -- Build display order: regular bags -> soul bags -> ammo/quiver bags -> keyring + local bagsToShow = {} + for _, bagID in ipairs(regularBags) do + table.insert(bagsToShow, {bagID = bagID, needsSpacing = false}) + end - -- Add soul bags with spacing marker - if table.getn(soulBags) > 0 then - for i, bagID in ipairs(soulBags) do - table.insert(bagsToShow, {bagID = bagID, needsSpacing = (i == 1)}) - end - end + -- Add soul bags with spacing marker + if table.getn(soulBags) > 0 then + for i, bagID in ipairs(soulBags) do + table.insert(bagsToShow, {bagID = bagID, needsSpacing = (i == 1)}) + end + end - -- Add ammo/quiver bags with spacing marker - if table.getn(ammoQuiverBags) > 0 then - for i, bagID in ipairs(ammoQuiverBags) do - table.insert(bagsToShow, {bagID = bagID, needsSpacing = (i == 1)}) - end - end + -- Add ammo/quiver bags with spacing marker + if table.getn(ammoQuiverBags) > 0 then + for i, bagID in ipairs(ammoQuiverBags) do + table.insert(bagsToShow, {bagID = bagID, needsSpacing = (i == 1)}) + end + end - -- Add keyring at the end if toggled on and not hidden - if showKeyring and not hiddenBags[-2] then - table.insert(bagsToShow, {bagID = -2, needsSpacing = true}) - end + -- Add keyring at the end if toggled on and not hidden + if showKeyring and not hiddenBags[-2] then + table.insert(bagsToShow, {bagID = -2, needsSpacing = true}) + end - for _, bagInfo in ipairs(bagsToShow) do - local bagID = bagInfo.bagID - local bag = bagData[bagID] + for _, bagInfo in ipairs(bagsToShow) do + local bagID = bagInfo.bagID + local bag = bagData[bagID] - -- Add spacing before soul, ammo/quiver, or keyring sections - if bagInfo.needsSpacing then - if col > 0 then - -- Move to next row if not at start of row - col = 0 - row = row + 1 - end - -- Add extra spacing (0.5 row for tighter spacing) - row = row + 0.5 - end + -- Add spacing before soul, ammo/quiver, or keyring sections + if bagInfo.needsSpacing then + if col > 0 then + -- Move to next row if not at start of row + col = 0 + row = row + 1 + end + -- Add extra spacing (0.5 row for tighter spacing) + row = row + 0.5 + end - -- Get slot count for this bag - local numSlots - if isOtherChar and bag and bag.numSlots then - -- Use stored slot count for other characters - numSlots = bag.numSlots - else - -- Use current character's bag slot count - numSlots = addon.Modules.Utils:GetBagSlotCount(bagID) - end + -- Get slot count for this bag + local numSlots + if isOtherChar and bag and bag.numSlots then + -- Use stored slot count for other characters + numSlots = bag.numSlots + else + -- Use current character's bag slot count + numSlots = addon.Modules.Utils:GetBagSlotCount(bagID) + end - -- Only show bags that have slots - if numSlots and numSlots > 0 then - -- Iterate through ALL slots (1 to numSlots) to show empty slots too - for slot = 1, numSlots do - local itemData = bag and bag.slots and bag.slots[slot] or nil + -- Only show bags that have slots + if numSlots and numSlots > 0 then + -- Iterate through ALL slots (1 to numSlots) to show empty slots too + -- Ensure a per-bag parent frame exists and carries the bag ID (Blizzard expects parent:GetID() == bagID) + local bagParent + do + if not bagParents[bagID] then + bagParents[bagID] = CreateFrame("Frame", "Guda_BagFrame_BagParent"..bagID, itemContainer) + bagParents[bagID]:SetAllPoints(itemContainer) + if bagParents[bagID].SetID then + bagParents[bagID]:SetID(bagID) + end + end + bagParent = bagParents[bagID] + end - -- Check if item matches search filter - local matchesFilter = self:PassesSearchFilter(itemData) + for slot = 1, numSlots do + local itemData = bag and bag.slots and bag.slots[slot] or nil - local button = Guda_GetItemButton(itemContainer) + -- Check if item matches search filter + local matchesFilter = self:PassesSearchFilter(itemData) - -- Position button - local xPos = x + (col * (buttonSize + spacing)) - local yPos = y - (row * (buttonSize + spacing)) + local button = Guda_GetItemButton(bagParent) + button.inUse = true -- Mark this button as actively in use - button:ClearAllPoints() - button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", xPos, yPos) + -- Position button + local xPos = x + (col * (buttonSize + spacing)) + local yPos = y - (row * (buttonSize + spacing)) - -- Set item data with filter match info - Guda_ItemButton_SetItem(button, bagID, slot, itemData, false, isOtherChar and charName or nil, matchesFilter) + button:ClearAllPoints() + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", xPos, yPos) - table.insert(itemButtons, button) + -- Set item data with filter match info + -- isReadOnly = true when viewing other characters (can't interact with their items) + Guda_ItemButton_SetItem(button, bagID, slot, itemData, false, isOtherChar and charName or nil, matchesFilter, isOtherChar) - -- Advance position - col = col + 1 - if col >= perRow then - col = 0 - row = row + 1 - end - end - end - end + table.insert(itemButtons, button) + + -- Advance position + col = col + 1 + if col >= perRow then + col = 0 + row = row + 1 + end + end + end + end -- Resize frame dynamically based on content self:ResizeFrame(row, col, perRow) + + -- Ensure cooldown visuals are current after (re)building buttons + if self.RefreshCooldowns then + self:RefreshCooldowns() + end end -- Resize frame based on number of rows and columns function BagFrame:ResizeFrame(currentRow, currentCol, columns) - local buttonSize = addon.Modules.DB:GetSetting("iconSize") or addon.Constants.BUTTON_SIZE - local spacing = addon.Modules.DB:GetSetting("iconSpacing") or addon.Constants.BUTTON_SPACING + local buttonSize = addon.Modules.DB:GetSetting("iconSize") or addon.Constants.BUTTON_SIZE + local spacing = addon.Modules.DB:GetSetting("iconSpacing") or addon.Constants.BUTTON_SPACING - -- Calculate actual number of rows used - local totalRows = currentRow + 1 + -- Calculate actual number of rows used + local totalRows = currentRow + 1 - -- Ensure at least 1 row - if totalRows < 1 then - totalRows = 1 - end + -- Ensure at least 1 row + if totalRows < 1 then + totalRows = 1 + end - -- Calculate required dimensions based on columns - local containerWidth = (columns * (buttonSize + spacing)) + 20 - local containerHeight = (totalRows * (buttonSize + spacing)) + 20 - local frameWidth = containerWidth + 20 + -- Calculate required dimensions based on columns + local containerWidth = (columns * (buttonSize + spacing)) + 20 + local containerHeight = (totalRows * (buttonSize + spacing)) + 20 + local frameWidth = containerWidth + 20 - -- Check if search bar is visible - local showSearchBar = addon.Modules.DB:GetSetting("showSearchBar") - if showSearchBar == nil then - showSearchBar = true - end + -- Check if search bar is visible + local showSearchBar = addon.Modules.DB:GetSetting("showSearchBar") + if showSearchBar == nil then + showSearchBar = true + end - -- Adjust frame height based on search bar visibility - -- Footer height varies: more space needed when search bar is visible - local titleHeight = 40 - local searchBarHeight = 30 - local footerHeight - local frameHeight - if showSearchBar then - footerHeight = 55 -- Increased footer height when search bar is visible (toolbar 40px + spacing 15px) - frameHeight = containerHeight + titleHeight + searchBarHeight + footerHeight -- 125 total - else - footerHeight = 45 -- Normal footer height (toolbar 40px + spacing 5px) - frameHeight = containerHeight + titleHeight + footerHeight -- 85 total - end + -- Adjust frame height based on search bar visibility + -- Footer height varies: more space needed when search bar is visible + local titleHeight = 40 + local searchBarHeight = 30 + local footerHeight + local frameHeight + if showSearchBar then + footerHeight = 55 -- Increased footer height when search bar is visible (toolbar 40px + spacing 15px) + frameHeight = containerHeight + titleHeight + searchBarHeight + footerHeight -- 125 total + else + footerHeight = 45 -- Normal footer height (toolbar 40px + spacing 5px) + frameHeight = containerHeight + titleHeight + footerHeight -- 85 total + end - -- Minimum sizes - if containerWidth < 200 then - containerWidth = 200 - frameWidth = 220 - end - if containerHeight < 150 then - containerHeight = 150 - end - if frameHeight < 250 then - frameHeight = 250 - end + -- Minimum sizes + if containerWidth < 200 then + containerWidth = 200 + frameWidth = 220 + end + if containerHeight < 150 then + containerHeight = 150 + end + if frameHeight < 250 then + frameHeight = 250 + end - -- Maximum sizes - if containerWidth > 800 then - containerWidth = 800 - frameWidth = 820 - end - if containerHeight > 600 then - containerHeight = 600 - end - if frameHeight > 800 then - frameHeight = 800 - end + -- Maximum sizes + if containerWidth > 800 then + containerWidth = 800 + frameWidth = 820 + end + if containerHeight > 600 then + containerHeight = 600 + end + if frameHeight > 800 then + frameHeight = 800 + end - -- Resize frames - local bagFrame = getglobal("Guda_BagFrame") - local itemContainer = getglobal("Guda_BagFrame_ItemContainer") + -- Resize frames + local bagFrame = getglobal("Guda_BagFrame") + local itemContainer = getglobal("Guda_BagFrame_ItemContainer") - if bagFrame then - bagFrame:SetWidth(frameWidth) - bagFrame:SetHeight(frameHeight) + if bagFrame then + bagFrame:SetWidth(frameWidth) + bagFrame:SetHeight(frameHeight) - -- Always use BOTTOMRIGHT anchor to make frame grow left - bagFrame:ClearAllPoints() + -- Always use BOTTOMRIGHT anchor to make frame grow left + bagFrame:ClearAllPoints() - if addon and addon.Modules and addon.Modules.DB then - local pos = addon.Modules.DB:GetSetting("bagFramePosition") - -- Only use saved position if it was saved as BOTTOMRIGHT - if pos and pos.point == "BOTTOMRIGHT" and pos.x and pos.y then - bagFrame:SetPoint("BOTTOMRIGHT", "UIParent", "BOTTOMRIGHT", pos.x, pos.y) - else - -- Default position: bottom right corner - bagFrame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -50, 100) - end - else - -- Fallback to default if DB not available - bagFrame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -50, 100) - end - end + if addon and addon.Modules and addon.Modules.DB then + local pos = addon.Modules.DB:GetSetting("bagFramePosition") + -- Only use saved position if it was saved as BOTTOMRIGHT + if pos and pos.point == "BOTTOMRIGHT" and pos.x and pos.y then + bagFrame:SetPoint("BOTTOMRIGHT", "UIParent", "BOTTOMRIGHT", pos.x, pos.y) + else + -- Default position: bottom right corner + bagFrame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -50, 100) + end + else + -- Fallback to default if DB not available + bagFrame:SetPoint("BOTTOMRIGHT", UIParent, "BOTTOMRIGHT", -50, 100) + end + end - if itemContainer then - itemContainer:SetWidth(containerWidth) - itemContainer:SetHeight(containerHeight) - end + if itemContainer then + itemContainer:SetWidth(containerWidth) + itemContainer:SetHeight(containerHeight) + end - -- Resize search bar and toolbar to match container width - local searchBar = getglobal("Guda_BagFrame_SearchBar") - if searchBar then - searchBar:SetWidth(containerWidth) - end + -- Resize search bar and toolbar to match container width + local searchBar = getglobal("Guda_BagFrame_SearchBar") + if searchBar then + searchBar:SetWidth(containerWidth) + end - local toolbar = getglobal("Guda_BagFrame_Toolbar") - if toolbar then - toolbar:SetWidth(containerWidth) - end + local toolbar = getglobal("Guda_BagFrame_Toolbar") + if toolbar then + toolbar:SetWidth(containerWidth) + end end -- Check if search is currently active function BagFrame:IsSearchActive() - return searchText and searchText ~= "" and searchText ~= "Search, try ~equipment" + return searchText and searchText ~= "" and searchText ~= "Search, try ~equipment" end -- Check if item passes search filter (pfUI style) function BagFrame:PassesSearchFilter(itemData) - -- If no search text, everything matches - if not self:IsSearchActive() then - return true - end +-- If no search text, everything matches + if not self:IsSearchActive() then + return true + end - -- Empty slots don't match when searching (pfUI style - they get dimmed) - if not itemData then - return false - end + -- Empty slots don't match when searching (pfUI style - they get dimmed) + if not itemData then + return false + end - -- Get item name from itemData.name or parse from link - local itemName = itemData.name - if not itemName and itemData.link then - -- Parse name from item link: |cffffffff|Hitem:...|h[Item Name]|h|r - local _, _, name = string.find(itemData.link, "%[(.+)%]") - itemName = name - if not self.warnedAboutParsing then - addon:Print("DEBUG: Had to parse item name from link: " .. (itemName or "FAILED")) - self.warnedAboutParsing = true - end - end + -- Get item name from itemData.name or parse from link + local itemName = itemData.name + if not itemName and itemData.link then + -- Parse name from item link: |cffffffff|Hitem:...|h[Item Name]|h|r + local _, _, name = string.find(itemData.link, "%[(.+)%]") + itemName = name + if not self.warnedAboutParsing then + addon:Print("DEBUG: Had to parse item name from link: " .. (itemName or "FAILED")) + self.warnedAboutParsing = true + end + end - if not itemName then - if not self.warnedAboutNoName then - addon:Print("DEBUG: Item has no name and no link! texture = " .. tostring(itemData.texture)) - self.warnedAboutNoName = true - end - return false - end + if not itemName then + if not self.warnedAboutNoName then + addon:Print("DEBUG: Item has no name and no link! texture = " .. tostring(itemData.texture)) + self.warnedAboutNoName = true + end + return false + end - -- Case-insensitive search in item name - itemName = string.lower(itemName) - local search = string.lower(searchText) + -- Case-insensitive search in item name + itemName = string.lower(itemName) + local search = string.lower(searchText) - -- Check if item name contains search text - local matches = string.find(itemName, search, 1, true) ~= nil + -- Check if item name contains search text + local matches = string.find(itemName, search, 1, true) ~= nil - -- Debug: print first match found - if matches and not self.foundFirstMatch then - self.foundFirstMatch = true - end + -- Debug: print first match found + if matches and not self.foundFirstMatch then + self.foundFirstMatch = true + end - return matches + return matches end function BagFrame:UpdateMoney() - local moneyFrame = getglobal("Guda_BagFrame_MoneyFrame") + local moneyFrame = getglobal("Guda_BagFrame_MoneyFrame") - if not moneyFrame then - addon:Debug("Guda_BagFrame exists: " .. tostring(getglobal("Guda_BagFrame") ~= nil)) + if not moneyFrame then + addon:Debug("Guda_BagFrame exists: " .. tostring(getglobal("Guda_BagFrame") ~= nil)) - -- Try to create it manually - self:CreateMoneyFrame() - moneyFrame = getglobal("Guda_BagFrame_MoneyFrame") - end + -- Try to create it manually + self:CreateMoneyFrame() + moneyFrame = getglobal("Guda_BagFrame_MoneyFrame") + end - if moneyFrame then - MoneyFrame_Update("Guda_BagFrame_MoneyFrame", GetMoney()) - moneyFrame:Show() + if moneyFrame then + MoneyFrame_Update("Guda_BagFrame_MoneyFrame", GetMoney()) + moneyFrame:Show() - -- Ensure tooltip overlay exists - self:EnsureMoneyTooltipOverlay() + -- Ensure tooltip overlay exists + self:EnsureMoneyTooltipOverlay() - -- Also add tooltip to toolbar empty space - self:SetupToolbarTooltip() - else - addon:Debug("Still couldn't find or create MoneyFrame!") - end + -- Also add tooltip to toolbar empty space + self:SetupToolbarTooltip() + else + addon:Debug("Still couldn't find or create MoneyFrame!") + end end -- Update bag slots info text (excluding keyring) function BagFrame:UpdateBagSlotsInfo(bagData, isOtherChar) - local infoText = getglobal("Guda_BagFrame_Toolbar_BagSlotsInfo_Text") - if not infoText then return end + local infoText = getglobal("Guda_BagFrame_Toolbar_BagSlotsInfo_Text") + if not infoText then return end - local totalSlots = 0 - local usedSlots = 0 + local totalSlots = 0 + local usedSlots = 0 - -- Count slots in regular bags only (0-4), exclude keyring (-2) - for _, bagID in ipairs(addon.Constants.BAGS) do - local bag = bagData[bagID] + -- Count slots in regular bags only (0-4), exclude keyring (-2) + for _, bagID in ipairs(addon.Constants.BAGS) do + local bag = bagData[bagID] - -- Get slot count for this bag - local numSlots - if isOtherChar and bag and bag.numSlots then - numSlots = bag.numSlots - else - numSlots = addon.Modules.Utils:GetBagSlotCount(bagID) - end + -- Get slot count for this bag + local numSlots + if isOtherChar and bag and bag.numSlots then + numSlots = bag.numSlots + else + numSlots = addon.Modules.Utils:GetBagSlotCount(bagID) + end - if numSlots and numSlots > 0 then - totalSlots = totalSlots + numSlots + if numSlots and numSlots > 0 then + totalSlots = totalSlots + numSlots - -- Count used slots - if bag and bag.slots then - for slot = 1, numSlots do - if bag.slots[slot] then - usedSlots = usedSlots + 1 - end - end - end - end - end + -- Count used slots + if bag and bag.slots then + for slot = 1, numSlots do + if bag.slots[slot] then + usedSlots = usedSlots + 1 + end + end + end + end + end - -- Format: "24 / 80" (used / total) - infoText:SetText(string.format("%d / %d", usedSlots, totalSlots)) - infoText:SetTextColor(0.7, 0.7, 0.7) + -- Format: "24 / 80" (used / total) + infoText:SetText(string.format("%d / %d", usedSlots, totalSlots)) + infoText:SetTextColor(0.7, 0.7, 0.7) end function BagFrame:CreateMoneyFrame() - local moneyFrame = CreateFrame("Frame", "Guda_BagFrame_MoneyFrame", Guda_BagFrame, "SmallMoneyFrameTemplate") - moneyFrame:SetPoint("BOTTOMRIGHT", Guda_BagFrame, "BOTTOMRIGHT", -15, 10) - moneyFrame:SetWidth(180) - moneyFrame:SetHeight(35) - addon:Debug("MoneyFrame created via CreateMoneyFrame") + local moneyFrame = CreateFrame("Frame", "Guda_BagFrame_MoneyFrame", Guda_BagFrame, "SmallMoneyFrameTemplate") + moneyFrame:SetPoint("BOTTOMRIGHT", Guda_BagFrame, "BOTTOMRIGHT", -15, 10) + moneyFrame:SetWidth(180) + moneyFrame:SetHeight(35) + addon:Debug("MoneyFrame created via CreateMoneyFrame") end -- Setup tooltip on toolbar empty space function BagFrame:SetupToolbarTooltip() - local toolbar = getglobal("Guda_BagFrame_Toolbar") - if not toolbar then return end + local toolbar = getglobal("Guda_BagFrame_Toolbar") + if not toolbar then return end - -- Only set up once - if toolbar.tooltipSetup then return end + -- Only set up once + if toolbar.tooltipSetup then return end - -- Get the existing OnEnter script (if any) - local originalOnEnter = toolbar:GetScript("OnEnter") + -- Get the existing OnEnter script (if any) + local originalOnEnter = toolbar:GetScript("OnEnter") - -- Set new OnEnter that shows money tooltip - toolbar:SetScript("OnEnter", function() - -- Call original if it exists - if originalOnEnter then - originalOnEnter() - end + -- Set new OnEnter that shows money tooltip + toolbar:SetScript("OnEnter", function() + -- Call original if it exists + if originalOnEnter then + originalOnEnter() + end - -- Show money tooltip - addon:Debug("Toolbar OnEnter - showing money tooltip") - Guda_BagFrame_MoneyOnEnter(getglobal("Guda_BagFrame_MoneyFrame")) - end) + -- Show money tooltip + addon:Debug("Toolbar OnEnter - showing money tooltip") + Guda_BagFrame_MoneyOnEnter(getglobal("Guda_BagFrame_MoneyFrame")) + end) - -- Set OnLeave to hide tooltip - toolbar:SetScript("OnLeave", function() - addon:Debug("Toolbar OnLeave - hiding tooltip") - GameTooltip:Hide() - end) + -- Set OnLeave to hide tooltip + toolbar:SetScript("OnLeave", function() + addon:Debug("Toolbar OnLeave - hiding tooltip") + GameTooltip:Hide() + end) - toolbar.tooltipSetup = true - addon:Debug("Toolbar tooltip handlers set up") + toolbar.tooltipSetup = true + addon:Debug("Toolbar tooltip handlers set up") end -- Save bag frame position (always as BOTTOMRIGHT) local function SaveBagFramePosition() - local frame = getglobal("Guda_BagFrame") - if not frame or not addon or not addon.Modules or not addon.Modules.DB then return end + local frame = getglobal("Guda_BagFrame") + if not frame or not addon or not addon.Modules or not addon.Modules.DB then return end - -- Always save as BOTTOMRIGHT coordinates - local right = frame:GetRight() - local bottom = frame:GetBottom() - local screenWidth = GetScreenWidth() + -- Always save as BOTTOMRIGHT coordinates + local right = frame:GetRight() + local bottom = frame:GetBottom() + local screenWidth = GetScreenWidth() - if right and bottom and screenWidth then - local xOffset = right - screenWidth - local yOffset = bottom + if right and bottom and screenWidth then + local xOffset = right - screenWidth + local yOffset = bottom - addon.Modules.DB:SetSetting("bagFramePosition", { - point = "BOTTOMRIGHT", - x = xOffset, - y = yOffset - }) - end + addon.Modules.DB:SetSetting("bagFramePosition", { + point = "BOTTOMRIGHT", + x = xOffset, + y = yOffset + }) + end end -- Create transparent overlay for money tooltip function BagFrame:EnsureMoneyTooltipOverlay() - local overlayName = "Guda_BagFrame_MoneyTooltipOverlay" - local overlay = getglobal(overlayName) + local overlayName = "Guda_BagFrame_MoneyTooltipOverlay" + local overlay = getglobal(overlayName) - if not overlay then - local moneyFrame = getglobal("Guda_BagFrame_MoneyFrame") - if not moneyFrame then return end + if not overlay then + local moneyFrame = getglobal("Guda_BagFrame_MoneyFrame") + if not moneyFrame then return end - -- Create transparent overlay frame - overlay = CreateFrame("Frame", overlayName, moneyFrame) - overlay:SetAllPoints(moneyFrame) - overlay:SetFrameLevel(moneyFrame:GetFrameLevel() + 1) - overlay:EnableMouse(true) + -- Create transparent overlay frame + overlay = CreateFrame("Frame", overlayName, moneyFrame) + overlay:SetAllPoints(moneyFrame) + overlay:SetFrameLevel(moneyFrame:GetFrameLevel() + 1) + overlay:EnableMouse(true) - -- Set tooltip handlers on overlay - overlay:SetScript("OnEnter", function() - addon:Debug("Money overlay OnEnter triggered") - Guda_BagFrame_MoneyOnEnter(moneyFrame) - end) + -- Set tooltip handlers on overlay + overlay:SetScript("OnEnter", function() + addon:Debug("Money overlay OnEnter triggered") + Guda_BagFrame_MoneyOnEnter(moneyFrame) + end) - overlay:SetScript("OnLeave", function() - addon:Debug("Money overlay OnLeave triggered") - GameTooltip:Hide() - end) + overlay:SetScript("OnLeave", function() + addon:Debug("Money overlay OnLeave triggered") + GameTooltip:Hide() + end) - -- Forward drag events to bag frame (if not locked) - overlay:SetScript("OnMouseDown", function() - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:ClearFocus() - end + -- Forward drag events to bag frame (if not locked) + overlay:SetScript("OnMouseDown", function() + local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") + if searchBox then + searchBox:ClearFocus() + end - local bagFrame = getglobal("Guda_BagFrame") - local isLocked = addon.Modules.DB and addon.Modules.DB:GetSetting("lockBags") + local bagFrame = getglobal("Guda_BagFrame") + local isLocked = addon.Modules.DB and addon.Modules.DB:GetSetting("lockBags") - if bagFrame and not isLocked and arg1 == "LeftButton" then - bagFrame:StartMoving() - end - end) + if bagFrame and not isLocked and arg1 == "LeftButton" then + bagFrame:StartMoving() + end + end) - overlay:SetScript("OnMouseUp", function() - local bagFrame = getglobal("Guda_BagFrame") - local isLocked = addon.Modules.DB and addon.Modules.DB:GetSetting("lockBags") + overlay:SetScript("OnMouseUp", function() + local bagFrame = getglobal("Guda_BagFrame") + local isLocked = addon.Modules.DB and addon.Modules.DB:GetSetting("lockBags") - if bagFrame and not isLocked then - bagFrame:StopMovingOrSizing() - SaveBagFramePosition() - end - end) + if bagFrame and not isLocked then + bagFrame:StopMovingOrSizing() + SaveBagFramePosition() + end + end) - addon:Debug("Money tooltip overlay created") - end + addon:Debug("Money tooltip overlay created") + end - overlay:Show() + overlay:Show() end -- Money frame OnLoad handler function Guda_BagFrame_MoneyFrame_OnLoad(self) - addon:Debug("MoneyFrame OnLoad called for: " .. self:GetName()) + addon:Debug("MoneyFrame OnLoad called for: " .. self:GetName()) - -- Set tooltip handlers on all money denomination buttons - local buttons = {"GoldButton", "SilverButton", "CopperButton"} + -- Set tooltip handlers on all money denomination buttons + local buttons = {"GoldButton", "SilverButton", "CopperButton"} - for _, buttonName in ipairs(buttons) do - local fullName = self:GetName() .. buttonName - local button = getglobal(fullName) - addon:Debug("Looking for button: " .. fullName .. " - Found: " .. tostring(button ~= nil)) - if button then - button:SetScript("OnEnter", function() - addon:Debug("Money button OnEnter triggered") - Guda_BagFrame_MoneyOnEnter(this:GetParent()) - end) - button:SetScript("OnLeave", function() - GameTooltip:Hide() - end) - end - end + for _, buttonName in ipairs(buttons) do + local fullName = self:GetName() .. buttonName + local button = getglobal(fullName) + addon:Debug("Looking for button: " .. fullName .. " - Found: " .. tostring(button ~= nil)) + if button then + button:SetScript("OnEnter", function() + addon:Debug("Money button OnEnter triggered") + Guda_BagFrame_MoneyOnEnter(this:GetParent()) + end) + button:SetScript("OnLeave", function() + GameTooltip:Hide() + end) + end + end - -- Also try setting handlers directly on the parent frame - addon:Debug("Setting handlers on parent frame as fallback") - self:EnableMouse(true) - self:SetScript("OnEnter", function() - addon:Debug("Parent frame OnEnter triggered") - Guda_BagFrame_MoneyOnEnter(this) - end) - self:SetScript("OnLeave", function() - GameTooltip:Hide() - end) + -- Also try setting handlers directly on the parent frame + addon:Debug("Setting handlers on parent frame as fallback") + self:EnableMouse(true) + self:SetScript("OnEnter", function() + addon:Debug("Parent frame OnEnter triggered") + Guda_BagFrame_MoneyOnEnter(this) + end) + self:SetScript("OnLeave", function() + GameTooltip:Hide() + end) end -- Money tooltip handler function Guda_BagFrame_MoneyOnEnter(self) - if not self then return end + if not self then return end - -- Anchor tooltip to TOPRIGHT - aligns tooltip's right edge with money container's right edge - GameTooltip:SetOwner(self, "ANCHOR_TOPRIGHT", 0, 0) - GameTooltip:ClearLines() + -- Anchor tooltip to TOPRIGHT - aligns tooltip's right edge with money container's right edge + GameTooltip:SetOwner(self, "ANCHOR_TOPRIGHT", 0, 0) + GameTooltip:ClearLines() - -- Get all characters and total (realm filtered, all factions) - local chars = addon.Modules.DB:GetAllCharacters(false, true) - local totalMoney = addon.Modules.DB:GetTotalMoney(false, true) + -- Get all characters and total (realm filtered, all factions) + local chars = addon.Modules.DB:GetAllCharacters(false, true) + local totalMoney = addon.Modules.DB:GetTotalMoney(false, true) - -- Header with current realm total - use colored money - GameTooltip:AddLine( - "Current realm gold: " .. addon.Modules.Utils:FormatMoney(totalMoney, false, true), - 1, 0.82, 0 - ) - GameTooltip:AddLine(" ") + -- Header with current realm total - use colored money + GameTooltip:AddLine( + "Current realm gold: " .. addon.Modules.Utils:FormatMoney(totalMoney, false, true), + 1, 0.82, 0 + ) + GameTooltip:AddLine(" ") - -- List each character with class-colored names - for _, char in ipairs(chars) do - -- Get class color from WoW's built-in table using English class token - local classToken = char.classToken - local classColor = classToken and (CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS)[classToken] - local colorR, colorG, colorB = 0.7, 0.7, 0.7 + -- List each character with class-colored names + for _, char in ipairs(chars) do + -- Get class color from WoW's built-in table using English class token + local classToken = char.classToken + local classColor = classToken and (CUSTOM_CLASS_COLORS or RAID_CLASS_COLORS)[classToken] + local colorR, colorG, colorB = 0.7, 0.7, 0.7 - if classColor then - colorR, colorG, colorB = classColor.r, classColor.g, classColor.b - end + if classColor then + colorR, colorG, colorB = classColor.r, classColor.g, classColor.b + end - -- Create colored name - local coloredName = addon.Modules.Utils:ColorText(char.name, colorR, colorG, colorB) + -- Create colored name + local coloredName = addon.Modules.Utils:ColorText(char.name, colorR, colorG, colorB) - GameTooltip:AddLine( - coloredName .. ": " .. addon.Modules.Utils:FormatMoney(char.money or 0, false, true) - ) - end + GameTooltip:AddLine( + coloredName .. ": " .. addon.Modules.Utils:FormatMoney(char.money or 0, false, true) + ) + end - GameTooltip:Show() + GameTooltip:Show() end -- Dropdown management @@ -738,1135 +798,1168 @@ local bankDropdown = nil -- Toggle character dropdown function Guda_BagFrame_ToggleCharacterDropdown(button) - -- Hide bank dropdown if it's shown - if bankDropdown and bankDropdown:IsShown() then - bankDropdown:Hide() - end +-- Hide bank dropdown if it's shown + if bankDropdown and bankDropdown:IsShown() then + bankDropdown:Hide() + end - if characterDropdown and characterDropdown:IsShown() then - characterDropdown:Hide() - return - end + if characterDropdown and characterDropdown:IsShown() then + characterDropdown:Hide() + return + end - if not characterDropdown then - -- Create dropdown frame - characterDropdown = CreateFrame("Frame", "Guda_CharacterDropdown", UIParent) - characterDropdown:SetFrameStrata("DIALOG") - characterDropdown:SetWidth(200) - characterDropdown:SetBackdrop({ - bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", - edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", - tile = true, - tileSize = 16, - edgeSize = 16, - insets = { left = 4, right = 4, top = 4, bottom = 4 } - }) - characterDropdown:SetBackdropColor(0, 0, 0, 0.95) - characterDropdown:EnableMouse(true) - characterDropdown:Hide() + if not characterDropdown then + -- Create dropdown frame + characterDropdown = CreateFrame("Frame", "Guda_CharacterDropdown", UIParent) + characterDropdown:SetFrameStrata("DIALOG") + characterDropdown:SetWidth(200) + characterDropdown:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", + tile = true, + tileSize = 16, + edgeSize = 16, + insets = { left = 4, right = 4, top = 4, bottom = 4 } + }) + characterDropdown:SetBackdropColor(0, 0, 0, 0.95) + characterDropdown:EnableMouse(true) + characterDropdown:Hide() - characterDropdown.buttons = {} - end + characterDropdown.buttons = {} + end - -- Position dropdown below the button - characterDropdown:ClearAllPoints() - characterDropdown:SetPoint("TOPLEFT", button, "BOTTOMLEFT", 0, -2) + -- Position dropdown below the button + characterDropdown:ClearAllPoints() + characterDropdown:SetPoint("TOPLEFT", button, "BOTTOMLEFT", 0, -2) - -- Clear existing buttons - for _, btn in ipairs(characterDropdown.buttons) do - btn:Hide() - end - characterDropdown.buttons = {} + -- Clear existing buttons + for _, btn in ipairs(characterDropdown.buttons) do + btn:Hide() + end + characterDropdown.buttons = {} - -- Get all characters on current realm - local chars = addon.Modules.DB:GetAllCharacters(false, true) + -- Get all characters on current realm + local chars = addon.Modules.DB:GetAllCharacters(false, true) - -- Add "Current Character" option at the top - local yOffset = -8 - local currentCharButton = CreateFrame("Button", nil, characterDropdown) - currentCharButton:SetWidth(188) - currentCharButton:SetHeight(20) - currentCharButton:SetPoint("TOP", characterDropdown, "TOP", 0, yOffset) + -- Add "Current Character" option at the top + local yOffset = -8 + local currentCharButton = CreateFrame("Button", nil, characterDropdown) + currentCharButton:SetWidth(188) + currentCharButton:SetHeight(20) + currentCharButton:SetPoint("TOP", characterDropdown, "TOP", 0, yOffset) - -- Button background on hover - local currentCharBg = currentCharButton:CreateTexture(nil, "BACKGROUND") - currentCharBg:SetAllPoints() - currentCharBg:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") - currentCharBg:SetBlendMode("ADD") - currentCharBg:SetAlpha(0) + -- Button background on hover + local currentCharBg = currentCharButton:CreateTexture(nil, "BACKGROUND") + currentCharBg:SetAllPoints() + currentCharBg:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + currentCharBg:SetBlendMode("ADD") + currentCharBg:SetAlpha(0) - -- Button text - local currentCharText = currentCharButton:CreateFontString(nil, "ARTWORK", "GameFontHighlight") - currentCharText:SetPoint("LEFT", currentCharButton, "LEFT", 8, 0) - currentCharText:SetText("Current Character") + -- Button text + local currentCharText = currentCharButton:CreateFontString(nil, "ARTWORK", "GameFontHighlight") + currentCharText:SetPoint("LEFT", currentCharButton, "LEFT", 8, 0) + currentCharText:SetText("Characters") - -- Button scripts - currentCharButton:SetScript("OnEnter", function() - currentCharBg:SetAlpha(0.3) - end) - currentCharButton:SetScript("OnLeave", function() - currentCharBg:SetAlpha(0) - end) - currentCharButton:SetScript("OnClick", function() - addon.Modules.BagFrame:ShowCurrentCharacter() - characterDropdown:Hide() - end) + -- Button scripts + currentCharButton:SetScript("OnEnter", function() + currentCharBg:SetAlpha(0.3) + end) + currentCharButton:SetScript("OnLeave", function() + currentCharBg:SetAlpha(0) + end) + currentCharButton:SetScript("OnClick", function() + addon.Modules.BagFrame:ShowCurrentCharacter() + characterDropdown:Hide() + end) - table.insert(characterDropdown.buttons, currentCharButton) - yOffset = yOffset - 20 + table.insert(characterDropdown.buttons, currentCharButton) + yOffset = yOffset - 20 - -- Add separator - local separator = characterDropdown:CreateTexture(nil, "ARTWORK") - separator:SetHeight(1) - separator:SetWidth(180) - separator:SetPoint("TOP", characterDropdown, "TOP", 0, yOffset) - separator:SetTexture(1, 1, 1, 0.2) - yOffset = yOffset - 4 + -- Add separator + local separator = characterDropdown:CreateTexture(nil, "ARTWORK") + separator:SetHeight(1) + separator:SetWidth(180) + separator:SetPoint("TOP", characterDropdown, "TOP", 0, yOffset) + separator:SetTexture(1, 1, 1, 0.2) + yOffset = yOffset - 4 - -- Get current player's full name for comparison - local currentPlayerFullName = addon.Modules.DB:GetPlayerFullName() + -- Get current player's full name for comparison + local currentPlayerFullName = addon.Modules.DB:GetPlayerFullName() - -- Add character buttons - for _, char in ipairs(chars) do - -- Capture variables in local scope for closure - local charFullName = char.fullName - local charName = char.name - local charMoney = char.money or 0 - local charClassToken = char.classToken - local isCurrentChar = (charFullName == currentPlayerFullName) + -- Add character buttons + for _, char in ipairs(chars) do + -- Capture variables in local scope for closure + local charFullName = char.fullName + local charName = char.name + local charMoney = char.money or 0 + local charClassToken = char.classToken + local isCurrentChar = (charFullName == currentPlayerFullName) - local charButton = CreateFrame("Button", nil, characterDropdown) - charButton:SetWidth(188) - charButton:SetHeight(20) - charButton:SetPoint("TOP", characterDropdown, "TOP", 0, yOffset) + local charButton = CreateFrame("Button", nil, characterDropdown) + charButton:SetWidth(188) + charButton:SetHeight(20) + charButton:SetPoint("TOP", characterDropdown, "TOP", 0, yOffset) - -- Button background on hover - local charBg = charButton:CreateTexture(nil, "BACKGROUND") - charBg:SetAllPoints() - charBg:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") - charBg:SetBlendMode("ADD") - charBg:SetAlpha(0) + -- Button background on hover + local charBg = charButton:CreateTexture(nil, "BACKGROUND") + charBg:SetAllPoints() + charBg:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + charBg:SetBlendMode("ADD") + charBg:SetAlpha(0) - -- Get class color - local classColor = charClassToken and RAID_CLASS_COLORS[charClassToken] - local r, g, b = 1, 1, 1 - if classColor then - r, g, b = classColor.r, classColor.g, classColor.b - end + -- Get class color + local classColor = charClassToken and RAID_CLASS_COLORS[charClassToken] + local r, g, b = 1, 1, 1 + if classColor then + r, g, b = classColor.r, classColor.g, classColor.b + end - -- Button text - local charText = charButton:CreateFontString(nil, "ARTWORK", "GameFontHighlight") - charText:SetPoint("LEFT", charButton, "LEFT", 8, 0) - charText:SetText(charName) - charText:SetTextColor(r, g, b) + -- Button text + local charText = charButton:CreateFontString(nil, "ARTWORK", "GameFontHighlight") + charText:SetPoint("LEFT", charButton, "LEFT", 8, 0) + charText:SetText(charName) + charText:SetTextColor(r, g, b) - -- Money text - local moneyText = charButton:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") - moneyText:SetPoint("RIGHT", charButton, "RIGHT", -8, 0) - moneyText:SetText(addon.Modules.Utils:FormatMoney(charMoney)) - moneyText:SetTextColor(0.7, 0.7, 0.7) + -- Money text + local moneyText = charButton:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + moneyText:SetPoint("RIGHT", charButton, "RIGHT", -8, 0) + moneyText:SetText(addon.Modules.Utils:FormatMoney(charMoney)) + moneyText:SetTextColor(0.7, 0.7, 0.7) - -- Button scripts - charButton:SetScript("OnEnter", function() - charBg:SetAlpha(0.3) - end) - charButton:SetScript("OnLeave", function() - charBg:SetAlpha(0) - end) - charButton:SetScript("OnClick", function() - if charFullName then - if isCurrentChar then - -- Clicking current character - show current live view - addon.Modules.BagFrame:ShowCurrentCharacter() - else - -- Clicking different character - show their stored bags - addon.Modules.BagFrame:ShowCharacter(charFullName) - end - characterDropdown:Hide() - else - addon:Print("Error: Character fullName is nil") - end - end) + -- Button scripts + charButton:SetScript("OnEnter", function() + charBg:SetAlpha(0.3) + end) + charButton:SetScript("OnLeave", function() + charBg:SetAlpha(0) + end) + charButton:SetScript("OnClick", function() + if charFullName then + if isCurrentChar then + -- Clicking current character - show current live view + addon.Modules.BagFrame:ShowCurrentCharacter() + else + -- Clicking different character - show their stored bags + addon.Modules.BagFrame:ShowCharacter(charFullName) + end + characterDropdown:Hide() + else + addon:Print("Error: Character fullName is nil") + end + end) - table.insert(characterDropdown.buttons, charButton) - yOffset = yOffset - 20 - end + table.insert(characterDropdown.buttons, charButton) + yOffset = yOffset - 20 + end - -- Set dropdown height based on content - characterDropdown:SetHeight(math.abs(yOffset) + 8) + -- Set dropdown height based on content + characterDropdown:SetHeight(math.abs(yOffset) + 8) - -- Show dropdown - characterDropdown:Show() + -- Show dropdown + characterDropdown:Show() end -- Hide dropdown when clicking elsewhere local function HideCharacterDropdown() - if characterDropdown then - characterDropdown:Hide() - end + if characterDropdown then + characterDropdown:Hide() + end end -- Toggle bank dropdown function Guda_BagFrame_ToggleBankDropdown(button) - -- Hide character dropdown if it's shown - if characterDropdown and characterDropdown:IsShown() then - characterDropdown:Hide() - end +-- Hide character dropdown if it's shown + if characterDropdown and characterDropdown:IsShown() then + characterDropdown:Hide() + end - if bankDropdown and bankDropdown:IsShown() then - bankDropdown:Hide() - return - end + if bankDropdown and bankDropdown:IsShown() then + bankDropdown:Hide() + return + end - if not bankDropdown then - -- Create dropdown frame - bankDropdown = CreateFrame("Frame", "Guda_BankDropdown", UIParent) - bankDropdown:SetFrameStrata("DIALOG") - bankDropdown:SetWidth(200) - bankDropdown:SetBackdrop({ - bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", - edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", - tile = true, - tileSize = 16, - edgeSize = 16, - insets = { left = 4, right = 4, top = 4, bottom = 4 } - }) - bankDropdown:SetBackdropColor(0, 0, 0, 0.95) - bankDropdown:EnableMouse(true) - bankDropdown:Hide() + if not bankDropdown then + -- Create dropdown frame + bankDropdown = CreateFrame("Frame", "Guda_BankDropdown", UIParent) + bankDropdown:SetFrameStrata("DIALOG") + bankDropdown:SetWidth(200) + bankDropdown:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", + tile = true, + tileSize = 16, + edgeSize = 16, + insets = { left = 4, right = 4, top = 4, bottom = 4 } + }) + bankDropdown:SetBackdropColor(0, 0, 0, 0.95) + bankDropdown:EnableMouse(true) + bankDropdown:Hide() - bankDropdown.buttons = {} - end + bankDropdown.buttons = {} + end - -- Position dropdown below the button - bankDropdown:ClearAllPoints() - bankDropdown:SetPoint("TOPLEFT", button, "BOTTOMLEFT", 0, -2) + -- Position dropdown below the button + bankDropdown:ClearAllPoints() + bankDropdown:SetPoint("TOPLEFT", button, "BOTTOMLEFT", 0, -2) + -- Clear existing buttons + for _, btn in ipairs(bankDropdown.buttons) do + btn:Hide() + end + bankDropdown.buttons = {} - -- Clear existing buttons - for _, btn in ipairs(bankDropdown.buttons) do - btn:Hide() - end - bankDropdown.buttons = {} + -- Get all characters on current realm + local chars = addon.Modules.DB:GetAllCharacters(false, true) - -- Get all characters on current realm - local chars = addon.Modules.DB:GetAllCharacters(false, true) + local yOffset = -8 - local yOffset = -8 + -- Add character buttons + for _, char in ipairs(chars) do + -- Capture variables in local scope for closure + local charFullName = char.fullName + local charName = char.name + local charMoney = char.money or 0 + local charClassToken = char.classToken - -- Add character buttons - for _, char in ipairs(chars) do - -- Capture variables in local scope for closure - local charFullName = char.fullName - local charName = char.name - local charMoney = char.money or 0 - local charClassToken = char.classToken + local charButton = CreateFrame("Button", nil, bankDropdown) + charButton:SetWidth(188) + charButton:SetHeight(20) + charButton:SetPoint("TOP", bankDropdown, "TOP", 0, yOffset) - local charButton = CreateFrame("Button", nil, bankDropdown) - charButton:SetWidth(188) - charButton:SetHeight(20) - charButton:SetPoint("TOP", bankDropdown, "TOP", 0, yOffset) + -- Button background on hover + local charBg = charButton:CreateTexture(nil, "BACKGROUND") + charBg:SetAllPoints() + charBg:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") + charBg:SetBlendMode("ADD") + charBg:SetAlpha(0) - -- Button background on hover - local charBg = charButton:CreateTexture(nil, "BACKGROUND") - charBg:SetAllPoints() - charBg:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") - charBg:SetBlendMode("ADD") - charBg:SetAlpha(0) + -- Get class color + local classColor = charClassToken and RAID_CLASS_COLORS[charClassToken] + local r, g, b = 1, 1, 1 + if classColor then + r, g, b = classColor.r, classColor.g, classColor.b + end - -- Get class color - local classColor = charClassToken and RAID_CLASS_COLORS[charClassToken] - local r, g, b = 1, 1, 1 - if classColor then - r, g, b = classColor.r, classColor.g, classColor.b - end + -- Button text + local charText = charButton:CreateFontString(nil, "ARTWORK", "GameFontHighlight") + charText:SetPoint("LEFT", charButton, "LEFT", 8, 0) + charText:SetText(charName) + charText:SetTextColor(r, g, b) - -- Button text - local charText = charButton:CreateFontString(nil, "ARTWORK", "GameFontHighlight") - charText:SetPoint("LEFT", charButton, "LEFT", 8, 0) - charText:SetText(charName) - charText:SetTextColor(r, g, b) + -- Money text + local moneyText = charButton:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") + moneyText:SetPoint("RIGHT", charButton, "RIGHT", -8, 0) + moneyText:SetText(addon.Modules.Utils:FormatMoney(charMoney)) + moneyText:SetTextColor(0.7, 0.7, 0.7) - -- Money text - local moneyText = charButton:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") - moneyText:SetPoint("RIGHT", charButton, "RIGHT", -8, 0) - moneyText:SetText(addon.Modules.Utils:FormatMoney(charMoney)) - moneyText:SetTextColor(0.7, 0.7, 0.7) + -- Button scripts + charButton:SetScript("OnEnter", function() + charBg:SetAlpha(0.3) + end) + charButton:SetScript("OnLeave", function() + charBg:SetAlpha(0) + end) + charButton:SetScript("OnClick", function() + if charFullName then + -- Show bank for this character + Guda_BagFrame_ShowCharacterBank(charFullName, charName) + bankDropdown:Hide() + else + addon:Print("Error: Character fullName is nil") + end + end) - -- Button scripts - charButton:SetScript("OnEnter", function() - charBg:SetAlpha(0.3) - end) - charButton:SetScript("OnLeave", function() - charBg:SetAlpha(0) - end) - charButton:SetScript("OnClick", function() - if charFullName then - -- Show bank for this character - Guda_BagFrame_ShowCharacterBank(charFullName, charName) - bankDropdown:Hide() - else - addon:Print("Error: Character fullName is nil") - end - end) + table.insert(bankDropdown.buttons, charButton) + yOffset = yOffset - 20 + end - table.insert(bankDropdown.buttons, charButton) - yOffset = yOffset - 20 - end + -- Set dropdown height based on content + bankDropdown:SetHeight(math.abs(yOffset) + 8) - -- Set dropdown height based on content - bankDropdown:SetHeight(math.abs(yOffset) + 8) - - -- Show dropdown - bankDropdown:Show() + -- Show dropdown + bankDropdown:Show() end -- Show character's bank function Guda_BagFrame_ShowCharacterBank(fullName, displayName) - -- Use the existing BankFrame module - if not addon.Modules.BankFrame then - addon:Print("Bank frame module not available") - return - end +-- Use the existing BankFrame module + if not addon.Modules.BankFrame then + addon:Print("Bank frame module not available") + return + end - -- Position bank frame at center of screen - if Guda_BankFrame then - Guda_BankFrame:ClearAllPoints() - Guda_BankFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 0) - end + -- Position bank frame at center of screen + if Guda_BankFrame then + Guda_BankFrame:ClearAllPoints() + Guda_BankFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 0) + end - -- Show the character's bank - addon.Modules.BankFrame:ShowCharacter(fullName) + -- Show the character's bank + addon.Modules.BankFrame:ShowCharacter(fullName) - -- Make sure frame is shown - if Guda_BankFrame then - Guda_BankFrame:Show() - end + -- Make sure frame is shown + if Guda_BankFrame then + Guda_BankFrame:Show() + end end -- Clear search and restore placeholder function Guda_BagFrame_ClearSearch() - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:SetText("Search, try ~equipment") - searchBox:SetTextColor(0.5, 0.5, 0.5, 1) - searchBox:ClearFocus() - end + local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") + if searchBox then + searchBox:SetText("Search, try ~equipment") + searchBox:SetTextColor(0.5, 0.5, 0.5, 1) + searchBox:ClearFocus() + end - -- Reset search state - searchText = "" - BagFrame.foundFirstMatch = false - BagFrame.warnedAboutParsing = false - BagFrame.warnedAboutNoName = false + -- Reset search state + searchText = "" + BagFrame.foundFirstMatch = false + BagFrame.warnedAboutParsing = false + BagFrame.warnedAboutNoName = false - -- Update display - BagFrame:Update() + -- Update display + BagFrame:Update() end -- Search changed handler function Guda_BagFrame_OnSearchChanged(self) - local text = self:GetText() - -- Ignore placeholder text - if text == "Search, try ~equipment" then - text = "" - end - if text ~= searchText then - searchText = text - BagFrame.foundFirstMatch = false -- Reset debug flags - BagFrame.warnedAboutParsing = false - BagFrame.warnedAboutNoName = false - BagFrame:Update() - end + local text = self:GetText() + -- Ignore placeholder text + if text == "Search, try ~equipment" then + text = "" + end + if text ~= searchText then + searchText = text + BagFrame.foundFirstMatch = false -- Reset debug flags + BagFrame.warnedAboutParsing = false + BagFrame.warnedAboutNoName = false + BagFrame:Update() + end end -- Keyring toggle handler function Guda_BagFrame_ToggleKeyring() - showKeyring = not showKeyring + showKeyring = not showKeyring - -- Update button appearance to show toggle state - local button = getglobal("Guda_BagFrame_Toolbar_KeyringButton") - if button then - local icon = getglobal(button:GetName().."_Icon") - if icon then - if showKeyring then - -- Highlighted when active - icon:SetVertexColor(1.0, 1.0, 0.5) - else - -- Normal color when inactive - icon:SetVertexColor(0.8, 0.8, 0.8) - end - end - end + -- Update button appearance to show toggle state + local button = getglobal("Guda_BagFrame_Toolbar_KeyringButton") + if button then + local icon = getglobal(button:GetName().."_Icon") + if icon then + if showKeyring then + -- Highlighted when active + icon:SetVertexColor(1.0, 1.0, 0.5) + else + -- Normal color when inactive + icon:SetVertexColor(0.8, 0.8, 0.8) + end + end + end - -- Refresh display - BagFrame:Update() + -- Refresh display + BagFrame:Update() end -- Sort button handler with auto-repeat and smart pass calculation function Guda_BagFrame_Sort() - if currentViewChar then - addon:Print("Cannot sort another character's bags!") - return - end + if currentViewChar then + addon:Print("Cannot sort another character's bags!") + return + end - -- Analyze bags to determine how many passes are needed - local analysis = addon.Modules.SortEngine:AnalyzeBags() + -- Analyze bags to determine how many passes are needed + local analysis = addon.Modules.SortEngine:AnalyzeBags() - -- Check if already sorted - if analysis.alreadySorted then - addon:Print("Bags are already sorted!") - return - end + -- Check if already sorted + if analysis.alreadySorted then + addon:Print("Bags are already sorted!") + return + end - -- Print analysis results - addon:Print("Sorting bags... (%d/%d items need sorting, estimated %d passes)", - analysis.itemsOutOfPlace, analysis.totalItems, analysis.passes) + -- Print analysis results + addon:Print("Sorting bags... (%d/%d items need sorting, estimated %d passes)", + analysis.itemsOutOfPlace, analysis.totalItems, analysis.passes) - local passCount = 0 - local maxPasses = math.max(analysis.passes, 1) -- Use estimated passes, minimum 1 - local safetyLimit = maxPasses + 3 -- Add 3 extra passes as safety margin + local passCount = 0 + local maxPasses = math.max(analysis.passes, 1) -- Use estimated passes, minimum 1 + local safetyLimit = maxPasses + 3 -- Add 3 extra passes as safety margin - local function DoSortPass() - passCount = passCount + 1 + local function DoSortPass() + passCount = passCount + 1 - -- Perform one sort pass - local moveCount = addon.Modules.SortEngine:SortBags() + -- Perform one sort pass + local moveCount = addon.Modules.SortEngine:SortBags() - -- If items were moved and we haven't hit the limit, do another pass - if moveCount > 0 and passCount < safetyLimit then - -- Wait for items to settle, then sort again - local frame = CreateFrame("Frame") - local elapsed = 0 - frame:SetScript("OnUpdate", function() - elapsed = elapsed + arg1 - if elapsed >= 0.7 then - frame:SetScript("OnUpdate", nil) - DoSortPass() -- Recursive call for next pass - end - end) - else - -- Sorting complete - if passCount >= safetyLimit then - addon:Print("Sort complete! (reached safety limit after %d passes)", passCount) - elseif passCount <= maxPasses then - addon:Print("Sort complete! (%d passes, as predicted)", passCount) - else - addon:Print("Sort complete! (%d passes, %d more than estimated)", passCount, passCount - maxPasses) - end + -- If items were moved and we haven't hit the limit, do another pass + if moveCount > 0 and passCount < safetyLimit then + -- Wait for items to settle, then sort again + local frame = CreateFrame("Frame") + local elapsed = 0 + frame:SetScript("OnUpdate", function() + elapsed = elapsed + arg1 + if elapsed >= 0.7 then + frame:SetScript("OnUpdate", nil) + DoSortPass() -- Recursive call for next pass + end + end) + else + -- Sorting complete + if passCount >= safetyLimit then + addon:Print("Sort complete! (reached safety limit after %d passes)", passCount) + elseif passCount <= maxPasses then + addon:Print("Sort complete! (%d passes, as predicted)", passCount) + else + addon:Print("Sort complete! (%d passes, %d more than estimated)", passCount, passCount - maxPasses) + end - -- Final update - local frame = CreateFrame("Frame") - local elapsed = 0 - frame:SetScript("OnUpdate", function() - elapsed = elapsed + arg1 - if elapsed >= 0.7 then - frame:SetScript("OnUpdate", nil) - BagFrame:Update() - end - end) - end - end + -- Final update + local frame = CreateFrame("Frame") + local elapsed = 0 + frame:SetScript("OnUpdate", function() + elapsed = elapsed + arg1 + if elapsed >= 0.7 then + frame:SetScript("OnUpdate", nil) + BagFrame:Update() + end + end) + end + end - -- Start the first pass - DoSortPass() + -- Start the first pass + DoSortPass() end -- Hook bag container buttons to open Guda Bag View local function HookBagContainers() - -- Hook the main bag container buttons (bags 1-4) - for i = 1, 4 do - local buttonName = "CharacterBag"..i.."Slot" - local button = getglobal(buttonName) +-- Hook the main bag container buttons (bags 1-4) + for i = 1, 4 do + local buttonName = "CharacterBag"..i.."Slot" + local button = getglobal(buttonName) - if button then - local originalOnClick = button:GetScript("OnClick") - button:SetScript("OnClick", function() - local mouseButton = arg1 or "LeftButton" -- Vanilla uses global arg1 - if mouseButton == "LeftButton" then - -- Open Guda Bag View instead of default bag - BagFrame:Toggle() - else - -- Allow right-click and other buttons to work normally - if originalOnClick then - originalOnClick() - end - end - end) - end - end + if button then + local originalOnClick = button:GetScript("OnClick") + button:SetScript("OnClick", function() + local mouseButton = arg1 or "LeftButton" -- Vanilla uses global arg1 + if mouseButton == "LeftButton" then + -- Open Guda Bag View instead of default bag + BagFrame:Toggle() + else + -- Allow right-click and other buttons to work normally + if originalOnClick then + originalOnClick() + end + end + end) + end + end - -- Also hook the backpack button - local backpackButton = getglobal("MainMenuBarBackpackButton") - if backpackButton then - local originalOnClick = backpackButton:GetScript("OnClick") - backpackButton:SetScript("OnClick", function() - local mouseButton = arg1 or "LeftButton" -- Vanilla uses global arg1 - if mouseButton == "LeftButton" then - -- Open Guda Bag View instead of default bag - BagFrame:Toggle() - else - -- Allow right-click and other buttons to work normally - if originalOnClick then - originalOnClick() - end - end - end) - end + -- Also hook the backpack button + local backpackButton = getglobal("MainMenuBarBackpackButton") + if backpackButton then + local originalOnClick = backpackButton:GetScript("OnClick") + backpackButton:SetScript("OnClick", function() + local mouseButton = arg1 or "LeftButton" -- Vanilla uses global arg1 + if mouseButton == "LeftButton" then + -- Open Guda Bag View instead of default bag + BagFrame:Toggle() + else + -- Allow right-click and other buttons to work normally + if originalOnClick then + originalOnClick() + end + end + end) + end - -- Hook keyring button if it exists - local keyringButton = getglobal("KeyRingButton") - if keyringButton then - local originalOnClick = keyringButton:GetScript("OnClick") - keyringButton:SetScript("OnClick", function() - local mouseButton = arg1 or "LeftButton" -- Vanilla uses global arg1 - if mouseButton == "LeftButton" then - -- Toggle keyring in Guda Bag View - Guda_BagFrame_ToggleKeyring() - BagFrame:Toggle() -- Also open the bag frame - else - -- Allow right-click and other buttons to work normally - if originalOnClick then - originalOnClick() - end - end - end) - end + -- Hook keyring button if it exists + local keyringButton = getglobal("KeyRingButton") + if keyringButton then + local originalOnClick = keyringButton:GetScript("OnClick") + keyringButton:SetScript("OnClick", function() + local mouseButton = arg1 or "LeftButton" -- Vanilla uses global arg1 + if mouseButton == "LeftButton" then + -- Toggle keyring in Guda Bag View + Guda_BagFrame_ToggleKeyring() + BagFrame:Toggle() -- Also open the bag frame + else + -- Allow right-click and other buttons to work normally + if originalOnClick then + originalOnClick() + end + end + end) + end end -- Alternative approach: Completely replace the bag open functions local function ReplaceBagOpenFunctions() - -- Override OpenBag (if it exists) - if OpenBag then - local originalOpenBag = OpenBag - function OpenBag(bagId) - if bagId and bagId >= 0 and bagId <= 4 then - -- For regular bags, open Guda Bag View - BagFrame:Toggle() - else - -- For other containers, use original function - if originalOpenBag then - originalOpenBag(bagId) - end - end - end - end +-- Override OpenBag (if it exists) + if OpenBag then + local originalOpenBag = OpenBag + function OpenBag(bagId) + if bagId and bagId >= 0 and bagId <= 4 then + -- For regular bags, open Guda Bag View + BagFrame:Toggle() + else + -- For other containers, use original function + if originalOpenBag then + originalOpenBag(bagId) + end + end + end + end - -- Override ToggleBag (if it exists) - if ToggleBag then - local originalToggleBag = ToggleBag - function ToggleBag(bagId) - if bagId and bagId >= 0 and bagId <= 4 then - -- For regular bags, toggle Guda Bag View - BagFrame:Toggle() - else - -- For other containers, use original function - if originalToggleBag then - originalToggleBag(bagId) - end - end - end - end + -- Override ToggleBag (if it exists) + if ToggleBag then + local originalToggleBag = ToggleBag + function ToggleBag(bagId) + if bagId and bagId >= 0 and bagId <= 4 then + -- For regular bags, toggle Guda Bag View + BagFrame:Toggle() + else + -- For other containers, use original function + if originalToggleBag then + originalToggleBag(bagId) + end + end + end + end end -- Hook to default bag opening local function HookDefaultBags() - -- Override ToggleBackpack (if it exists) - if ToggleBackpack then - local originalToggleBackpack = ToggleBackpack - function ToggleBackpack() - BagFrame:Toggle() - end - end +-- Override ToggleBackpack (if it exists) + if ToggleBackpack then + local originalToggleBackpack = ToggleBackpack + function ToggleBackpack() + BagFrame:Toggle() + end + end - -- Override OpenAllBags (if it exists) - if OpenAllBags then - local originalOpenAllBags = OpenAllBags - function OpenAllBags() - Guda_BagFrame:Show() - end - end + -- Override OpenAllBags (if it exists) + if OpenAllBags then + local originalOpenAllBags = OpenAllBags + function OpenAllBags() + Guda_BagFrame:Show() + end + end - -- Override CloseAllBags (if it exists) - if CloseAllBags then - local originalCloseAllBags = CloseAllBags - function CloseAllBags() - Guda_BagFrame:Hide() - end - end + -- Override CloseAllBags (if it exists) + if CloseAllBags then + local originalCloseAllBags = CloseAllBags + function CloseAllBags() + Guda_BagFrame:Hide() + end + end - -- Hook individual bag opening functions (for bag slot buttons) - ReplaceBagOpenFunctions() + -- Hook individual bag opening functions (for bag slot buttons) + ReplaceBagOpenFunctions() - -- Hook the bag slot button clicks directly - HookBagContainers() + -- Hook the bag slot button clicks directly + HookBagContainers() end -- Update lock state (controls whether frame is draggable) function BagFrame:UpdateLockState() - -- Safety check: ensure addon and modules exist - if not addon or not addon.Modules then return end +-- Safety check: ensure addon and modules exist + if not addon or not addon.Modules then return end - local frame = getglobal("Guda_BagFrame") - if not frame then return end + local frame = getglobal("Guda_BagFrame") + if not frame then return end - -- Check if DB module is available - if not addon.Modules.DB or not addon.Modules.DB.GetSetting then return end + -- Check if DB module is available + if not addon.Modules.DB or not addon.Modules.DB.GetSetting then return end - local success, isLocked = pcall(function() - return addon.Modules.DB:GetSetting("lockBags") - end) + local success, isLocked = pcall(function() + return addon.Modules.DB:GetSetting("lockBags") + end) - if not success then return end + if not success then return end - if isLocked == nil then - isLocked = false - end + if isLocked == nil then + isLocked = false + end - -- Get draggable areas - local toolbar = getglobal("Guda_BagFrame_Toolbar") - local moneyFrame = getglobal("Guda_BagFrame_MoneyFrame") - local itemContainer = getglobal("Guda_BagFrame_ItemContainer") + -- Get draggable areas + local toolbar = getglobal("Guda_BagFrame_Toolbar") + local moneyFrame = getglobal("Guda_BagFrame_MoneyFrame") + local itemContainer = getglobal("Guda_BagFrame_ItemContainer") - if isLocked then - -- Disable dragging on main frame - if frame.SetScript then - frame:SetScript("OnMouseDown", function() - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:ClearFocus() - end - end) - frame:SetScript("OnMouseUp", nil) - end + if isLocked then + -- Disable dragging on main frame + if frame.SetScript then + frame:SetScript("OnMouseDown", function() + local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") + if searchBox then + searchBox:ClearFocus() + end + end) + frame:SetScript("OnMouseUp", nil) + end - -- Disable dragging on toolbar - if toolbar and toolbar.SetScript then - toolbar:SetScript("OnMouseDown", function() - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:ClearFocus() - end - end) - toolbar:SetScript("OnMouseUp", nil) - end + -- Disable dragging on toolbar + if toolbar and toolbar.SetScript then + toolbar:SetScript("OnMouseDown", function() + local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") + if searchBox then + searchBox:ClearFocus() + end + end) + toolbar:SetScript("OnMouseUp", nil) + end - -- Disable dragging on money frame (preserve tooltip handlers on child buttons) - if moneyFrame and moneyFrame.SetScript then - moneyFrame:SetScript("OnMouseDown", function() - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:ClearFocus() - end - end) - moneyFrame:SetScript("OnMouseUp", nil) - end + -- Disable dragging on money frame (preserve tooltip handlers on child buttons) + if moneyFrame and moneyFrame.SetScript then + moneyFrame:SetScript("OnMouseDown", function() + local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") + if searchBox then + searchBox:ClearFocus() + end + end) + moneyFrame:SetScript("OnMouseUp", nil) + end - -- Disable dragging on item container - if itemContainer and itemContainer.SetScript then - itemContainer:SetScript("OnMouseDown", function() - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:ClearFocus() - end - end) - itemContainer:SetScript("OnMouseUp", nil) - end - else - -- Enable dragging on main frame - if frame and frame.SetScript then - frame:SetScript("OnMouseDown", function() - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:ClearFocus() - end + -- Disable dragging on item container + if itemContainer and itemContainer.SetScript then + itemContainer:SetScript("OnMouseDown", function() + local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") + if searchBox then + searchBox:ClearFocus() + end + end) + itemContainer:SetScript("OnMouseUp", nil) + end + else + -- Enable dragging on main frame + if frame and frame.SetScript then + frame:SetScript("OnMouseDown", function() + local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") + if searchBox then + searchBox:ClearFocus() + end - local bagFrame = getglobal("Guda_BagFrame") - if bagFrame and arg1 == "LeftButton" then - bagFrame:StartMoving() - end - end) - frame:SetScript("OnMouseUp", function() - local bagFrame = getglobal("Guda_BagFrame") - if bagFrame then - bagFrame:StopMovingOrSizing() - SaveBagFramePosition() - end - end) - end + local bagFrame = getglobal("Guda_BagFrame") + if bagFrame and arg1 == "LeftButton" then + bagFrame:StartMoving() + end + end) + frame:SetScript("OnMouseUp", function() + local bagFrame = getglobal("Guda_BagFrame") + if bagFrame then + bagFrame:StopMovingOrSizing() + SaveBagFramePosition() + end + end) + end - -- Enable dragging on toolbar (title area) - if toolbar and toolbar.SetScript then - toolbar:SetScript("OnMouseDown", function() - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:ClearFocus() - end + -- Enable dragging on toolbar (title area) + if toolbar and toolbar.SetScript then + toolbar:SetScript("OnMouseDown", function() + local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") + if searchBox then + searchBox:ClearFocus() + end - local bagFrame = getglobal("Guda_BagFrame") - if bagFrame and arg1 == "LeftButton" then - bagFrame:StartMoving() - end - end) - toolbar:SetScript("OnMouseUp", function() - local bagFrame = getglobal("Guda_BagFrame") - if bagFrame then - bagFrame:StopMovingOrSizing() - SaveBagFramePosition() - end - end) - end + local bagFrame = getglobal("Guda_BagFrame") + if bagFrame and arg1 == "LeftButton" then + bagFrame:StartMoving() + end + end) + toolbar:SetScript("OnMouseUp", function() + local bagFrame = getglobal("Guda_BagFrame") + if bagFrame then + bagFrame:StopMovingOrSizing() + SaveBagFramePosition() + end + end) + end - -- Enable dragging on money frame (preserve tooltip handlers on child buttons) - if moneyFrame and moneyFrame.SetScript then - moneyFrame:SetScript("OnMouseDown", function() - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:ClearFocus() - end + -- Enable dragging on money frame (preserve tooltip handlers on child buttons) + if moneyFrame and moneyFrame.SetScript then + moneyFrame:SetScript("OnMouseDown", function() + local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") + if searchBox then + searchBox:ClearFocus() + end - local bagFrame = getglobal("Guda_BagFrame") - if bagFrame and arg1 == "LeftButton" then - bagFrame:StartMoving() - end - end) - moneyFrame:SetScript("OnMouseUp", function() - local bagFrame = getglobal("Guda_BagFrame") - if bagFrame then - bagFrame:StopMovingOrSizing() - SaveBagFramePosition() - end - end) - end + local bagFrame = getglobal("Guda_BagFrame") + if bagFrame and arg1 == "LeftButton" then + bagFrame:StartMoving() + end + end) + moneyFrame:SetScript("OnMouseUp", function() + local bagFrame = getglobal("Guda_BagFrame") + if bagFrame then + bagFrame:StopMovingOrSizing() + SaveBagFramePosition() + end + end) + end - -- Enable dragging on item container - if itemContainer and itemContainer.SetScript then - itemContainer:SetScript("OnMouseDown", function() - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:ClearFocus() - end + -- Enable dragging on item container + if itemContainer and itemContainer.SetScript then + itemContainer:SetScript("OnMouseDown", function() + local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") + if searchBox then + searchBox:ClearFocus() + end - local bagFrame = getglobal("Guda_BagFrame") - if bagFrame and arg1 == "LeftButton" then - bagFrame:StartMoving() - end - end) - itemContainer:SetScript("OnMouseUp", function() - local bagFrame = getglobal("Guda_BagFrame") - if bagFrame then - bagFrame:StopMovingOrSizing() - SaveBagFramePosition() - end - end) - end - end + local bagFrame = getglobal("Guda_BagFrame") + if bagFrame and arg1 == "LeftButton" then + bagFrame:StartMoving() + end + end) + itemContainer:SetScript("OnMouseUp", function() + local bagFrame = getglobal("Guda_BagFrame") + if bagFrame then + bagFrame:StopMovingOrSizing() + SaveBagFramePosition() + end + end) + end + end end -- Update border visibility based on setting function BagFrame:UpdateBorderVisibility() - if not addon or not addon.Modules or not addon.Modules.DB then return end + if not addon or not addon.Modules or not addon.Modules.DB then return end - local frame = getglobal("Guda_BagFrame") - if not frame then return end + local frame = getglobal("Guda_BagFrame") + if not frame then return end - local hideBorders = addon.Modules.DB:GetSetting("hideBorders") - if hideBorders == nil then - hideBorders = false - end + local hideBorders = addon.Modules.DB:GetSetting("hideBorders") + if hideBorders == nil then + hideBorders = false + end - -- Use helper function with constants - if hideBorders then - addon:ApplyBackdrop(frame, "MINIMALIST_BORDER", "DEFAULT") - else - addon:ApplyBackdrop(frame, "DEFAULT_FRAME", "DEFAULT") - end + -- Use helper function with constants + if hideBorders then + addon:ApplyBackdrop(frame, "MINIMALIST_BORDER", "DEFAULT") + else + addon:ApplyBackdrop(frame, "DEFAULT_FRAME", "DEFAULT") + end end -- Update search bar visibility based on setting function BagFrame:UpdateSearchBarVisibility() - if not addon or not addon.Modules or not addon.Modules.DB then return end + if not addon or not addon.Modules or not addon.Modules.DB then return end - local searchBar = getglobal("Guda_BagFrame_SearchBar") - local itemContainer = getglobal("Guda_BagFrame_ItemContainer") - if not searchBar or not itemContainer then return end + local searchBar = getglobal("Guda_BagFrame_SearchBar") + local itemContainer = getglobal("Guda_BagFrame_ItemContainer") + if not searchBar or not itemContainer then return end - local showSearchBar = addon.Modules.DB:GetSetting("showSearchBar") - if showSearchBar == nil then - showSearchBar = true - end + local showSearchBar = addon.Modules.DB:GetSetting("showSearchBar") + if showSearchBar == nil then + showSearchBar = true + end - if showSearchBar then - searchBar:Show() - -- Anchor ItemContainer to SearchBar's bottom - itemContainer:ClearAllPoints() - itemContainer:SetPoint("TOP", searchBar, "BOTTOM", 0, -5) - else - searchBar:Hide() - -- Anchor ItemContainer directly to frame top (skip search bar space) - itemContainer:ClearAllPoints() - itemContainer:SetPoint("TOP", "Guda_BagFrame", "TOP", 0, -40) - end + if showSearchBar then + searchBar:Show() + -- Anchor ItemContainer to SearchBar's bottom + itemContainer:ClearAllPoints() + itemContainer:SetPoint("TOP", searchBar, "BOTTOM", 0, -5) + else + searchBar:Hide() + -- Anchor ItemContainer directly to frame top (skip search bar space) + itemContainer:ClearAllPoints() + itemContainer:SetPoint("TOP", "Guda_BagFrame", "TOP", 0, -40) + end end -- Bag Slot Button Handlers -- OnLoad handler for bag slot buttons function Guda_BagSlot_OnLoad(button, bagID) - -- Hide borders from ItemButtonTemplate - local buttonName = button:GetName() +-- Hide borders from ItemButtonTemplate + local buttonName = button:GetName() - -- Hide the normal texture border - local normalTexture = getglobal(buttonName .. "NormalTexture") - if normalTexture then - normalTexture:SetTexture(nil) - normalTexture:Hide() - end + -- Hide the normal texture border + local normalTexture = getglobal(buttonName .. "NormalTexture") + if normalTexture then + normalTexture:SetTexture(nil) + normalTexture:Hide() + end - -- Hide icon border - local iconBorder = getglobal(buttonName .. "IconBorder") - if iconBorder then - iconBorder:Hide() - end + -- Hide icon border + local iconBorder = getglobal(buttonName .. "IconBorder") + if iconBorder then + iconBorder:Hide() + end - -- Set up the button with proper ID - if bagID == 0 then - -- Backpack (bag 0) - button.bagID = 0 - button.hasItem = 1 - SetItemButtonTexture(button, "Interface\\Buttons\\Button-Backpack-Up") - else - -- Bags 1-4 - local invSlot = ContainerIDToInventoryID(bagID) - button:SetID(invSlot) - button.bagID = bagID + -- Set up the button with proper ID + if bagID == 0 then + -- Backpack (bag 0) + button.bagID = 0 + button.hasItem = 1 + SetItemButtonTexture(button, "Interface\\Buttons\\Button-Backpack-Up") + else + -- Bags 1-4 + local invSlot = ContainerIDToInventoryID(bagID) + button:SetID(invSlot) + button.bagID = bagID - -- REGISTER FOR DRAG - This is crucial for Classic - button:RegisterForDrag("LeftButton") - - -- Register for updates - button:RegisterEvent("BAG_UPDATE") - button:RegisterEvent("ITEM_LOCK_CHANGED") - button:RegisterEvent("CURSOR_UPDATE") - button:RegisterEvent("UNIT_INVENTORY_CHANGED") + -- REGISTER FOR DRAG - This is crucial for Classic + button:RegisterForDrag("LeftButton") - -- Accept drops (equip when a bag is dropped on this slot) - button:SetScript("OnReceiveDrag", function() - if this and this.bagID and this.bagID ~= 0 and CursorHasItem and CursorHasItem() then - local inv = ContainerIDToInventoryID(this.bagID) - if EquipCursorItem then - EquipCursorItem(inv) - elseif PutItemInBag then - PutItemInBag(inv) - end - Guda_BagSlot_Update(this, this.bagID) - if BagFrame and BagFrame.Update then BagFrame:Update() end - end - end) - end + -- Register for updates + button:RegisterEvent("BAG_UPDATE") + button:RegisterEvent("ITEM_LOCK_CHANGED") + button:RegisterEvent("CURSOR_UPDATE") + button:RegisterEvent("UNIT_INVENTORY_CHANGED") - -- Ensure we handle right-click toggling like BankFrame - if button.RegisterForClicks then - button:RegisterForClicks("LeftButtonUp", "RightButtonUp") - end + -- Accept drops (equip when a bag is dropped on this slot) + button:SetScript("OnReceiveDrag", function() + if this and this.bagID and this.bagID ~= 0 and CursorHasItem and CursorHasItem() then + local inv = ContainerIDToInventoryID(this.bagID) + if EquipCursorItem then + EquipCursorItem(inv) + elseif PutItemInBag then + PutItemInBag(inv) + end + Guda_BagSlot_Update(this, this.bagID) + if BagFrame and BagFrame.Update then BagFrame:Update() end + end + end) + end - -- Initial update - Guda_BagSlot_Update(button, bagID) + -- Ensure we handle right-click toggling like BankFrame + if button.RegisterForClicks then + button:RegisterForClicks("LeftButtonUp", "RightButtonUp") + end + + -- Initial update + Guda_BagSlot_Update(button, bagID) end function Guda_BagSlot_OnDragStart(frame, bagID) - if bagID == 0 then return end - - -- Check if we should start dragging (only if there's an item) - local invSlot = ContainerIDToInventoryID(bagID) - local texture = GetInventoryItemTexture("player", invSlot) - - if texture then - frame:SetAlpha(0.6) - -- Immediate pickup for Classic - PickupInventoryItem(invSlot) - -- Instantly reflect the change in UI (slot is now empty on cursor pickup) - Guda_BagSlot_Update(frame, bagID) - if BagFrame and BagFrame.Update then BagFrame:Update() end - end - -- If no texture (empty slot), do nothing - drag won't start + if bagID == 0 then return end + + -- Check if we should start dragging (only if there's an item) + local invSlot = ContainerIDToInventoryID(bagID) + local texture = GetInventoryItemTexture("player", invSlot) + + if texture then + frame:SetAlpha(0.6) + -- Immediate pickup for Classic + PickupInventoryItem(invSlot) + -- Instantly reflect the change in UI (slot is now empty on cursor pickup) + Guda_BagSlot_Update(frame, bagID) + if BagFrame and BagFrame.Update then BagFrame:Update() end + end +-- If no texture (empty slot), do nothing - drag won't start end function Guda_BagSlot_OnDragStop(frame, bagID) - frame:SetAlpha(1.0) + frame:SetAlpha(1.0) end -- Update bag slot button texture function Guda_BagSlot_Update(button, bagID) - local isHidden = hiddenBags[bagID] + local isHidden = hiddenBags[bagID] - if bagID == 0 then - -- Backpack always has the same texture - SetItemButtonTexture(button, "Interface\\Buttons\\Button-Backpack-Up") - -- Dim if hidden - if isHidden then - SetItemButtonTextureVertexColor(button, 0.4, 0.4, 0.4) - else - SetItemButtonTextureVertexColor(button, 1.0, 1.0, 1.0) - end - return - end + if bagID == 0 then + -- Backpack always has the same texture + SetItemButtonTexture(button, "Interface\\Buttons\\Button-Backpack-Up") + -- Dim if hidden + if isHidden then + SetItemButtonTextureVertexColor(button, 0.4, 0.4, 0.4) + else + SetItemButtonTextureVertexColor(button, 1.0, 1.0, 1.0) + end + return + end - -- Get the inventory slot ID for this bag - local invSlot = ContainerIDToInventoryID(bagID) - local texture = GetInventoryItemTexture("player", invSlot) + -- Get the inventory slot ID for this bag + local invSlot = ContainerIDToInventoryID(bagID) + local texture = GetInventoryItemTexture("player", invSlot) - if texture then - -- Bag is equipped - SetItemButtonTexture(button, texture) - -- Dim if hidden - if isHidden then - SetItemButtonTextureVertexColor(button, 0.4, 0.4, 0.4) - else - SetItemButtonTextureVertexColor(button, 1.0, 1.0, 1.0) - end - else - -- No bag in this slot - SetItemButtonTexture(button, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Bag") - SetItemButtonTextureVertexColor(button, 0.5, 0.5, 0.5) - end + if texture then + -- Bag is equipped + SetItemButtonTexture(button, texture) + -- Dim if hidden + if isHidden then + SetItemButtonTextureVertexColor(button, 0.4, 0.4, 0.4) + else + SetItemButtonTextureVertexColor(button, 1.0, 1.0, 1.0) + end + else + -- No bag in this slot + SetItemButtonTexture(button, "Interface\\PaperDoll\\UI-PaperDoll-Slot-Bag") + SetItemButtonTextureVertexColor(button, 0.5, 0.5, 0.5) + end end -- OnEvent handler function Guda_BagSlot_OnEvent(button, event, arg1) - local bagID = button.bagID - if not bagID then - return - end + local bagID = button.bagID + if not bagID then + return + end - if event == "BAG_UPDATE" then - if arg1 == bagID then - Guda_BagSlot_Update(button, bagID) - end - elseif event == "UNIT_INVENTORY_CHANGED" then - if arg1 == "player" then - Guda_BagSlot_Update(button, bagID) - end - elseif event == "ITEM_LOCK_CHANGED" or event == "CURSOR_UPDATE" then - Guda_BagSlot_Update(button, bagID) - end + if event == "BAG_UPDATE" then + if arg1 == bagID then + Guda_BagSlot_Update(button, bagID) + end + elseif event == "UNIT_INVENTORY_CHANGED" then + if arg1 == "player" then + Guda_BagSlot_Update(button, bagID) + end + elseif event == "ITEM_LOCK_CHANGED" or event == "CURSOR_UPDATE" then + Guda_BagSlot_Update(button, bagID) + end end -- OnClick handler function Guda_BagSlot_OnClick(button, bagID) - local which = arg1 -- Vanilla uses global arg1 for mouse button name + local which = arg1 -- Vanilla uses global arg1 for mouse button name - -- Right-Click: toggle visibility - if which == "RightButton" then - hiddenBags[bagID] = not hiddenBags[bagID] + -- Right-Click: toggle visibility + if which == "RightButton" then + hiddenBags[bagID] = not hiddenBags[bagID] - -- Update bag slot visual (dim/undim) - Guda_BagSlot_Update(button, bagID) + -- Update bag slot visual (dim/undim) + Guda_BagSlot_Update(button, bagID) - -- Refresh the bag display - BagFrame:Update() + -- Refresh the bag display + BagFrame:Update() - return - end + return + end - -- Left-Click: equip bag from cursor into this slot (bags 1-4 only) - if which == "LeftButton" then - if bagID ~= 0 and CursorHasItem and CursorHasItem() then - local invSlot = ContainerIDToInventoryID(bagID) - if EquipCursorItem then - EquipCursorItem(invSlot) - else - if PutItemInBag then PutItemInBag(invSlot) end - end - -- Update visuals after attempted equip - Guda_BagSlot_Update(button, bagID) - BagFrame:Update() - end - return - end + -- Left-Click: equip bag from cursor into this slot (bags 1-4 only) + if which == "LeftButton" then + if bagID ~= 0 and CursorHasItem and CursorHasItem() then + local invSlot = ContainerIDToInventoryID(bagID) + if EquipCursorItem then + EquipCursorItem(invSlot) + else + if PutItemInBag then PutItemInBag(invSlot) end + end + -- Update visuals after attempted equip + Guda_BagSlot_Update(button, bagID) + BagFrame:Update() + end + return + end end -- OnEnter handler for tooltip function Guda_BagSlot_OnEnter(button, bagID) - GameTooltip:SetOwner(button, "ANCHOR_TOP") + GameTooltip:SetOwner(button, "ANCHOR_TOP") - if bagID == 0 then - -- Backpack tooltip - GameTooltip:SetText("Backpack", 1.0, 1.0, 1.0) - local numSlots = GetContainerNumSlots(0) - GameTooltip:AddLine(string.format("%d Slots", numSlots), 0.8, 0.8, 0.8) - if hiddenBags[bagID] then - GameTooltip:AddLine("(Hidden - Right-Click to show)", 0.8, 0.5, 0.5) - else - GameTooltip:AddLine("(Right-Click to hide)", 0.5, 0.8, 0.5) - end - else - -- Bag slot tooltip - local invSlot = ContainerIDToInventoryID(bagID) - local hasItem = GetInventoryItemTexture("player", invSlot) + if bagID == 0 then + -- Backpack tooltip + GameTooltip:SetText("Backpack", 1.0, 1.0, 1.0) + local numSlots = GetContainerNumSlots(0) + GameTooltip:AddLine(string.format("%d Slots", numSlots), 0.8, 0.8, 0.8) + if hiddenBags[bagID] then + GameTooltip:AddLine("(Hidden - Right-Click to show)", 0.8, 0.5, 0.5) + else + GameTooltip:AddLine("(Right-Click to hide)", 0.5, 0.8, 0.5) + end + else + -- Bag slot tooltip + local invSlot = ContainerIDToInventoryID(bagID) + local hasItem = GetInventoryItemTexture("player", invSlot) - if hasItem then - -- Show bag item tooltip - GameTooltip:SetInventoryItem("player", invSlot) - if hiddenBags[bagID] then - GameTooltip:AddLine("(Hidden - Right-Click to show)", 0.8, 0.5, 0.5) - else - GameTooltip:AddLine("(Right-Click to hide)", 0.5, 0.8, 0.5) - end - else - -- Empty slot - GameTooltip:SetText(string.format("Bag %d", bagID), 1.0, 1.0, 1.0) - GameTooltip:AddLine("Empty", 0.5, 0.5, 0.5) - if hiddenBags[bagID] then - GameTooltip:AddLine("(Hidden - Right-Click to show)", 0.8, 0.5, 0.5) - else - GameTooltip:AddLine("(Right-Click to hide)", 0.5, 0.8, 0.5) - end - end - end + if hasItem then + -- Show bag item tooltip + GameTooltip:SetInventoryItem("player", invSlot) + if hiddenBags[bagID] then + GameTooltip:AddLine("(Hidden - Right-Click to show)", 0.8, 0.5, 0.5) + else + GameTooltip:AddLine("(Right-Click to hide)", 0.5, 0.8, 0.5) + end + else + -- Empty slot + GameTooltip:SetText(string.format("Bag %d", bagID), 1.0, 1.0, 1.0) + GameTooltip:AddLine("Empty", 0.5, 0.5, 0.5) + if hiddenBags[bagID] then + GameTooltip:AddLine("(Hidden - Right-Click to show)", 0.8, 0.5, 0.5) + else + GameTooltip:AddLine("(Right-Click to hide)", 0.5, 0.8, 0.5) + end + end + end - GameTooltip:Show() + GameTooltip:Show() end -- Highlight all item slots belonging to a specific bag by dimming others function Guda_BagFrame_HighlightBagSlots(bagID) - local itemContainer = getglobal("Guda_BagFrame_ItemContainer") - if not itemContainer then - return - end + -- Use the tracked itemButtons list because actual buttons are parented under per-bag parents + if not itemButtons or type(itemButtons) ~= "table" then return end - local highlightCount = 0 - local dimCount = 0 + local highlightCount, dimCount = 0, 0 - -- Iterate through all children (item buttons) - local children = { itemContainer:GetChildren() } - for _, button in ipairs(children) do - -- Check if this is an item button - if button.hasItem ~= nil and button:IsShown() then + for _, button in ipairs(itemButtons) do + if button and button:IsShown() and button.hasItem ~= nil and not button.isBagSlot then if button.bagID == bagID then - -- This button belongs to the hovered bag - keep it bright button:SetAlpha(1.0) highlightCount = highlightCount + 1 else - -- This button belongs to a different bag - dim it button:SetAlpha(0.25) dimCount = dimCount + 1 end end end - end -- Clear all highlighting by restoring full opacity to all slots function Guda_BagFrame_ClearHighlightedSlots() - local itemContainer = getglobal("Guda_BagFrame_ItemContainer") - if not itemContainer then return end + -- Restore alpha to whatever the search filter dictates (pfUI style). If no search, full opacity. + if not itemButtons or type(itemButtons) ~= "table" then return end - -- Iterate through all children (item buttons) - local children = { itemContainer:GetChildren() } - for _, button in ipairs(children) do - -- Check if this is an item button - if button.hasItem ~= nil and button:IsShown() then - -- Restore full opacity - button:SetAlpha(1.0) + local searchActive = BagFrame and BagFrame.IsSearchActive and BagFrame:IsSearchActive() + for _, button in ipairs(itemButtons) do + if button and button:IsShown() and button.hasItem ~= nil and not button.isBagSlot then + if searchActive and BagFrame and BagFrame.PassesSearchFilter then + local matches = BagFrame:PassesSearchFilter(button.itemData) + button:SetAlpha(matches and 1.0 or 0.25) + else + button:SetAlpha(1.0) + end end end end -- Highlight a specific bag button in the toolbar function Guda_BagFrame_HighlightBagButton(bagID) - if not bagID then return end + if not bagID then return end - local buttonName - if bagID == -2 then - -- Keyring button - buttonName = "Guda_BagFrame_Toolbar_KeyringButton" - elseif bagID >= 0 and bagID <= 4 then - -- Bag buttons 0-4 (0 is backpack) - buttonName = "Guda_BagFrame_Toolbar_BagSlot" .. bagID - else - return - end + local buttonName + if bagID == -2 then + -- Keyring button + buttonName = "Guda_BagFrame_Toolbar_KeyringButton" + elseif bagID >= 0 and bagID <= 4 then + -- Bag buttons 0-4 (0 is backpack) + buttonName = "Guda_BagFrame_Toolbar_BagSlot" .. bagID + else + return + end - local button = getglobal(buttonName) - if button then - -- Set the button's pushed texture to highlight it - button:LockHighlight() - end + local button = getglobal(buttonName) + if button then + -- Set the button's pushed texture to highlight it + button:LockHighlight() + end end -- Clear bag button highlighting function Guda_BagFrame_ClearBagButtonHighlight() - -- Clear highlight from all bag buttons (0-4) - for bagID = 0, 4 do - local buttonName = "Guda_BagFrame_Toolbar_BagSlot" .. bagID - local button = getglobal(buttonName) - if button then - button:UnlockHighlight() - end - end +-- Clear highlight from all bag buttons (0-4) + for bagID = 0, 4 do + local buttonName = "Guda_BagFrame_Toolbar_BagSlot" .. bagID + local button = getglobal(buttonName) + if button then + button:UnlockHighlight() + end + end - -- Clear keyring button highlight - local keyringButton = getglobal("Guda_BagFrame_Toolbar_KeyringButton") - if keyringButton then - keyringButton:UnlockHighlight() - end + -- Clear keyring button highlight + local keyringButton = getglobal("Guda_BagFrame_Toolbar_KeyringButton") + if keyringButton then + keyringButton:UnlockHighlight() + end end -- Initialize function BagFrame:Initialize() - -- Hook default bag functions (with slight delay to ensure UI is loaded) - local frame = CreateFrame("Frame") - frame:RegisterEvent("PLAYER_LOGIN") - frame:SetScript("OnEvent", function() - HookDefaultBags() +-- Hook default bag functions (with slight delay to ensure UI is loaded) + local frame = CreateFrame("Frame") + frame:RegisterEvent("PLAYER_LOGIN") + frame:SetScript("OnEvent", function() + HookDefaultBags() - -- Re-hook when character frame is opened (for safety) - local charFrame = getglobal("CharacterFrame") - if charFrame then - local originalShow = charFrame:GetScript("OnShow") - charFrame:SetScript("OnShow", function() - HookBagContainers() - if originalShow then - originalShow() - end - end) - end - end) + -- Re-hook when character frame is opened (for safety) + local charFrame = getglobal("CharacterFrame") + if charFrame then + local originalShow = charFrame:GetScript("OnShow") + charFrame:SetScript("OnShow", function() + HookBagContainers() + if originalShow then + originalShow() + end + end) + end + end) - -- Update on bag changes - addon.Modules.Events:OnBagUpdate(function() - if not currentViewChar then - BagFrame:Update() - end - end, "BagFrame") + -- Update on bag changes + addon.Modules.Events:OnBagUpdate(function() + if not currentViewChar then + BagFrame:Update() + end + end, "BagFrame") - -- Update on money changes - addon.Modules.Events:OnMoneyChanged(function() - BagFrame:UpdateMoney() - end, "BagFrame") + -- Update item cooldown overlays when item cooldowns change + addon.Modules.Events:Register("BAG_UPDATE_COOLDOWN", function() + if not currentViewChar then + if BagFrame.RefreshCooldowns then + BagFrame:RefreshCooldowns() + end + end + end, "BagFrame") - -- Update when items get locked/unlocked (for trading, mailing, etc.) - addon.Modules.Events:Register("ITEM_LOCK_CHANGED", function() - if not currentViewChar then - BagFrame:Update() - end - end, "BagFrame") + -- Update on money changes + addon.Modules.Events:OnMoneyChanged(function() + BagFrame:UpdateMoney() + end, "BagFrame") - -- Auto-open bag frame when mail is opened - addon.Modules.Events:Register("MAIL_SHOW", function() - Guda_BagFrame:Show() - end, "BagFrame") + -- Update when items get locked/unlocked (for trading, mailing, etc.) + addon.Modules.Events:Register("ITEM_LOCK_CHANGED", function() + if not currentViewChar then + BagFrame:Update() + end + end, "BagFrame") - -- Hide character dropdown when clicking on bag frame - local bagFrame = getglobal("Guda_BagFrame") - if bagFrame then - local originalOnMouseDown = bagFrame:GetScript("OnMouseDown") - bagFrame:SetScript("OnMouseDown", function() - HideCharacterDropdown() - if originalOnMouseDown then - originalOnMouseDown() - end - end) - end + -- Auto-open bag frame when mail is opened + addon.Modules.Events:Register("MAIL_SHOW", function() + Guda_BagFrame:Show() + end, "BagFrame") + + -- Hide character dropdown when clicking on bag frame + local bagFrame = getglobal("Guda_BagFrame") + if bagFrame then + local originalOnMouseDown = bagFrame:GetScript("OnMouseDown") + bagFrame:SetScript("OnMouseDown", function() + HideCharacterDropdown() + if originalOnMouseDown then + originalOnMouseDown() + end + end) + end addon:Debug("Bag frame initialized") +end + +-- Refresh cooldown overlays for all visible item buttons +function BagFrame:RefreshCooldowns() + local itemContainer = getglobal("Guda_BagFrame_ItemContainer") + if not itemContainer then return end + + -- Iterate through all children; our item buttons are direct children of per-bag parents inside the container, + -- so iterate all descendants by scanning children of children as well. + local function refreshChildren(parent) + local children = { parent:GetChildren() } + for _, child in ipairs(children) do + if child and child.hasItem ~= nil then + if child:IsShown() and Guda_ItemButton_UpdateCooldown then + Guda_ItemButton_UpdateCooldown(child) + end + end + -- Recurse one level to reach actual buttons under bag parents + local grandChildren = { child:GetChildren() } + if table.getn(grandChildren) > 0 then + for _, gc in ipairs(grandChildren) do + if gc and gc.hasItem ~= nil then + if gc:IsShown() and Guda_ItemButton_UpdateCooldown then + Guda_ItemButton_UpdateCooldown(gc) + end + end + end + end + end + end + + refreshChildren(itemContainer) end \ No newline at end of file diff --git a/UI/BagFrame.xml b/UI/BagFrame.xml index faa1716..1059ac6 100644 --- a/UI/BagFrame.xml +++ b/UI/BagFrame.xml @@ -193,7 +193,7 @@ GameTooltip:SetOwner(this, "ANCHOR_TOP") - GameTooltip:SetText("View Other Characters") + GameTooltip:SetText("My Characters") GameTooltip:Show() @@ -638,7 +638,8 @@ - + + @@ -654,15 +655,8 @@ - - - -- Clear search focus when clicking in item area - local searchBox = getglobal("Guda_BagFrame_SearchBar_SearchBox") - if searchBox then - searchBox:ClearFocus() - end - - + diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index 212610c..9b527ae 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -10,6 +10,9 @@ local currentViewChar = nil local searchText = "" local isReadOnlyMode = false -- Track if viewing saved bank (read-only) or live bank (interactive) local hiddenBankBags = {} -- Track which bank bags are hidden (bagID -> true/false) +local bankBagParents = {} -- Parent frames per bank bag (same approach as BagFrame) +-- Global click catcher for clearing bank search focus +local bankClickCatcher = nil -- OnLoad function Guda_BankFrame_OnLoad(self) @@ -20,6 +23,21 @@ function Guda_BankFrame_OnLoad(self) searchBox:SetTextColor(0.5, 0.5, 0.5, 1) end + -- Create invisible full-screen frame to catch clicks outside the bank frame while typing in search + if not bankClickCatcher then + bankClickCatcher = CreateFrame("Frame", "Guda_BankClickCatcher", UIParent) + bankClickCatcher:SetFrameStrata("BACKGROUND") + bankClickCatcher:SetAllPoints(UIParent) + bankClickCatcher:EnableMouse(true) + bankClickCatcher:Hide() + + bankClickCatcher:SetScript("OnMouseDown", function() + if Guda_BankFrame_ClearSearch then + Guda_BankFrame_ClearSearch() + end + end) + end + end -- OnShow @@ -47,22 +65,6 @@ function Guda_BankFrame_OnShow(self) BankFrame:Update() end --- OnHide -function Guda_BankFrame_OnHide(self) - -- Only release buttons that belong to this frame - local itemContainer = getglobal("Guda_BankFrame_ItemContainer") - if itemContainer then - -- Hide only the buttons that are children of this container - local children = { itemContainer:GetChildren() } - for _, child in ipairs(children) do - if child.hasItem ~= nil then -- It's an item button - child:Hide() - child:ClearAllPoints() - end - end - end -end - -- Toggle visibility function BankFrame:Toggle() if Guda_BankFrame:IsShown() then @@ -86,20 +88,45 @@ function BankFrame:ShowCurrentCharacter() self:Update() end +-- Update lock states of existing buttons (lightweight, used during drag) +function BankFrame:UpdateLockStates() + for _, bankBagParent in pairs(bankBagParents) do + if bankBagParent then + local buttons = { bankBagParent:GetChildren() } + for _, button in ipairs(buttons) do + if button.hasItem ~= nil and button:IsShown() and button.bagID and button.slotID then + -- Get live lock state + local _, _, locked = GetContainerItemInfo(button.bagID, button.slotID) + -- Update desaturation (gray out locked items) + if not button.otherChar and not button.isReadOnly and SetItemButtonDesaturated then + SetItemButtonDesaturated(button, locked, 0.5, 0.5, 0.5) + end + end + end + end + end +end + -- Update display function BankFrame:Update() if not Guda_BankFrame:IsShown() then return end - -- Only release buttons that belong to this frame - local itemContainer = getglobal("Guda_BankFrame_ItemContainer") - if itemContainer then - local children = { itemContainer:GetChildren() } - for _, child in ipairs(children) do - if child.hasItem ~= nil then -- It's an item button - child:Hide() - child:ClearAllPoints() + -- If cursor is holding an item (mid-drag), only update lock states, don't rebuild UI + if CursorHasItem and CursorHasItem() then + self:UpdateLockStates() + return + end + + -- Mark all existing buttons as not in use (we'll mark active ones during display) + for _, bankBagParent in pairs(bankBagParents) do + if bankBagParent then + local buttons = { bankBagParent:GetChildren() } + for _, button in ipairs(buttons) do + if button.hasItem ~= nil then + button.inUse = false + end end end end @@ -120,7 +147,7 @@ function BankFrame:Update() bankData = addon.Modules.DB:GetCharacterBank(currentViewChar) isOtherChar = true charName = currentViewChar - getglobal("Guda_BankFrame_Title"):SetText("Bank - " .. currentViewChar) + getglobal("Guda_BankFrame_Title"):SetText(currentViewChar .. "'s Bank") else -- Viewing current character's bank if bankIsOpen then @@ -128,12 +155,12 @@ function BankFrame:Update() bankData = addon.Modules.BankScanner:ScanBank() -- Use current character's name for the title local playerName = addon.Modules.DB:GetPlayerFullName() - getglobal("Guda_BankFrame_Title"):SetText("Bank - " .. playerName) + getglobal("Guda_BankFrame_Title"):SetText(playerName .. "'s Bank") else -- Bank is closed - use saved data (read-only mode) local playerName = addon.Modules.DB:GetPlayerFullName() bankData = addon.Modules.DB:GetCharacterBank(playerName) - getglobal("Guda_BankFrame_Title"):SetText("Bank - " .. playerName) + getglobal("Guda_BankFrame_Title"):SetText(playerName .. "'s Bank") end end @@ -144,6 +171,19 @@ function BankFrame:Update() -- Update bank slots info self:UpdateBankSlotsInfo(bankData, isOtherChar) + + -- Clean up unused buttons AFTER display is complete (prevents drag/drop issues) + for _, bankBagParent in pairs(bankBagParents) do + if bankBagParent then + local buttons = { bankBagParent:GetChildren() } + for _, button in ipairs(buttons) do + if button.hasItem ~= nil and not button.inUse then + button:Hide() + button:ClearAllPoints() + end + end + end + end end -- Display items @@ -180,13 +220,26 @@ function BankFrame:DisplayItems(bankData, isOtherChar, charName) -- Check if item matches search filter local matchesFilter = self:PassesSearchFilter(itemData) - local button = Guda_GetItemButton(itemContainer) + -- Ensure a per-bag parent frame exists and carries the bag ID + local bankBagParent + if not bankBagParents[bagID] then + bankBagParents[bagID] = CreateFrame("Frame", "Guda_BankFrame_BankBagParent"..bagID, itemContainer) + bankBagParents[bagID]:SetAllPoints(itemContainer) + if bankBagParents[bagID].SetID then + bankBagParents[bagID]:SetID(bagID) + end + end + bankBagParent = bankBagParents[bagID] + + local button = Guda_GetItemButton(bankBagParent) -- Ensure this is NOT a bag slot button if button.isBagSlot then break end + button.inUse = true -- Mark this button as actively in use + -- Position button local xPos = x + (col * (buttonSize + spacing)) local yPos = y - (row * (buttonSize + spacing)) @@ -522,6 +575,27 @@ function Guda_BankFrame_OnSearchChanged(self) end end +-- Clear bank search and restore placeholder +function Guda_BankFrame_ClearSearch() + local searchBox = getglobal("Guda_BankFrame_SearchBar_SearchBox") + if searchBox then + searchBox:SetText("Search bank...") + searchBox:SetTextColor(0.5, 0.5, 0.5, 1) + if searchBox.ClearFocus then searchBox:ClearFocus() end + end + + -- Reset search state + searchText = "" + + -- Update display + BankFrame:Update() + + -- Hide click catcher if present + if bankClickCatcher and bankClickCatcher.Hide then + bankClickCatcher:Hide() + end +end + -- Sort button handler with auto-repeat and smart pass calculation function Guda_BankFrame_Sort() if isReadOnlyMode or currentViewChar then @@ -861,6 +935,12 @@ function BankFrame:Initialize() -- Show current character's bank in interactive mode currentViewChar = nil + -- Hide BagFrame to prevent button overlap + local bagFrame = getglobal("Guda_BagFrame") + if bagFrame and bagFrame:IsShown() then + bagFrame:Hide() + end + -- Show and update custom bank frame local customBankFrame = getglobal("Guda_BankFrame") if customBankFrame then @@ -1170,46 +1250,49 @@ end -- Highlight all item slots belonging to a specific bank bag by dimming others function Guda_BankFrame_HighlightBagSlots(bagID) - local itemContainer = getglobal("Guda_BankFrame_ItemContainer") - if not itemContainer then - return - end + -- Buttons are parented under per-bag parents, not directly under the item container + local highlightCount, dimCount = 0, 0 - local highlightCount = 0 - local dimCount = 0 - - -- Iterate through all children (item buttons) - local children = { itemContainer:GetChildren() } - for _, button in ipairs(children) do - -- Check if this is an item button - if button.hasItem ~= nil and button:IsShown() and not button.isBagSlot then - if button.bagID == bagID then - -- This button belongs to the hovered bag - keep it bright - button:SetAlpha(1.0) - highlightCount = highlightCount + 1 - else - -- This button belongs to a different bag - dim it - button:SetAlpha(0.25) - dimCount = dimCount + 1 + for _, bankBagParent in pairs(bankBagParents) do + if bankBagParent then + local children = { bankBagParent:GetChildren() } + for _, button in ipairs(children) do + if button and button:IsShown() and button.hasItem ~= nil and not button.isBagSlot then + if button.bagID == bagID then + button:SetAlpha(1.0) + highlightCount = highlightCount + 1 + else + button:SetAlpha(0.25) + dimCount = dimCount + 1 + end + end end end end - addon:Debug(string.format("BankFrame HighlightBagSlots: Highlighted %d slots, dimmed %d slots for bagID %d", highlightCount, dimCount, bagID)) + if addon and addon.Debug then + addon:Debug(string.format("BankFrame HighlightBagSlots: Highlighted %d slots, dimmed %d slots for bagID %d", highlightCount, dimCount, bagID)) + end end -- Clear all highlighting by restoring full opacity to all slots function Guda_BankFrame_ClearHighlightedSlots() - local itemContainer = getglobal("Guda_BankFrame_ItemContainer") - if not itemContainer then return end + -- Restore alpha to search-filter state (pfUI style). If no search, full opacity. + local searchActive = BankFrame and BankFrame.IsSearchActive and BankFrame:IsSearchActive() - -- Iterate through all children (item buttons) - local children = { itemContainer:GetChildren() } - for _, button in ipairs(children) do - -- Check if this is an item button - if button.hasItem ~= nil and button:IsShown() and not button.isBagSlot then - -- Restore full opacity - button:SetAlpha(1.0) + for _, bankBagParent in pairs(bankBagParents) do + if bankBagParent then + local children = { bankBagParent:GetChildren() } + for _, button in ipairs(children) do + if button and button:IsShown() and button.hasItem ~= nil and not button.isBagSlot then + if searchActive and BankFrame and BankFrame.PassesSearchFilter then + local matches = BankFrame:PassesSearchFilter(button.itemData) + button:SetAlpha(matches and 1.0 or 0.25) + else + button:SetAlpha(1.0) + end + end + end end end end diff --git a/UI/BankFrame.xml b/UI/BankFrame.xml index 994e150..2bf3685 100644 --- a/UI/BankFrame.xml +++ b/UI/BankFrame.xml @@ -240,7 +240,7 @@ GameTooltip:SetOwner(this, "ANCHOR_TOP") - GameTooltip:SetText("View Other Characters' Bank") + GameTooltip:SetText("View Bank") GameTooltip:Show() @@ -286,8 +286,14 @@ this:SetText("") this:SetTextColor(1, 1, 1, 1) end + -- Show bank click catcher to detect clicks outside bank while typing + local cc = getglobal("Guda_BankClickCatcher") + if cc then cc:Show() end + -- Hide bank click catcher when focus is lost + local cc = getglobal("Guda_BankClickCatcher") + if cc then cc:Hide() end if this:GetText() == "" then this:SetText("Search bank...") this:SetTextColor(0.5, 0.5, 0.5, 1) @@ -302,6 +308,8 @@ this:SetTextColor(0.5, 0.5, 0.5, 1) this:ClearFocus() Guda_BankFrame_OnSearchChanged(this) + local cc = getglobal("Guda_BankClickCatcher") + if cc then cc:Hide() end @@ -595,7 +603,11 @@ Guda_BankFrame_OnShow(this) - Guda_BankFrame_OnHide(this) + Guda_BankFrame:Hide() + -- Close the actual Blizzard bank too + if BankFrame and BankFrame:IsShown() then + CloseBankFrame() + end -- Clear search box focus when clicking on bank frame diff --git a/UI/ItemButton.lua b/UI/ItemButton.lua index 50b84d6..51da5e5 100644 --- a/UI/ItemButton.lua +++ b/UI/ItemButton.lua @@ -1,8 +1,6 @@ --- Guda Item Button --- Handles item button display and interaction - +-- Local alias to the addon root table (must be defined before any usages below) local addon = Guda - + -- Item button pool local buttonPool = {} local nextButtonID = 1 @@ -11,6 +9,8 @@ local nextButtonID = 1 local scanTooltip = CreateFrame("GameTooltip", "Guda_QuestScanTooltip", nil, "GameTooltipTemplate") scanTooltip:SetOwner(WorldFrame, "ANCHOR_NONE") +-- (rollback) no custom drag-source tracking or target resolution; rely on Blizzard handlers + -- Check if an item is a quest item by scanning its tooltip local function IsQuestItem(bagID, slotID) if not bagID or not slotID then return false end @@ -108,30 +108,172 @@ function Guda_ItemButton_OnLoad(self) iconFrame:Hide() self.questIcon = iconFrame end + + -- Ensure the item button sits above its container backdrop and is mouse-enabled + local parent = self:GetParent() + if parent and parent.GetFrameLevel then + -- Place button above parent backdrop/mouse layer to reliably receive drops + local parentLevel = parent:GetFrameLevel() + if parentLevel and self:GetFrameLevel() <= parentLevel + 1 then + self:SetFrameLevel(parentLevel + 2) + end + end + + -- Enable mouse and register for drag/drop (crucial for Classic/Vanilla WoW) + if self.EnableMouse then + self:EnableMouse(true) + end + if self.RegisterForDrag then + self:RegisterForDrag("LeftButton") + end + if self.RegisterForClicks then + self:RegisterForClicks("LeftButtonUp", "RightButtonUp") + end +end + +-- Update the Blizzard cooldown overlay on this item button +function Guda_ItemButton_UpdateCooldown(self) + -- Only show cooldowns for live items of the current character + if not self or self.isReadOnly or self.otherChar then return end + + local cooldown = getglobal(self:GetName().."Cooldown") or self.cooldown + if not cooldown then return end + + if not self.hasItem or not self.bagID or not self.slotID then + cooldown:Hide() + return + end + + local start, duration, enable = GetContainerItemCooldown(self.bagID, self.slotID) + if start and duration and duration > 0 and enable == 1 then + if CooldownFrame_SetTimer then + CooldownFrame_SetTimer(cooldown, start, duration, enable) + elseif CooldownFrame_Set then + -- Some clients expose CooldownFrame_Set instead + CooldownFrame_Set(cooldown, start, duration, enable) + else + -- Fallback: show the frame if API missing + cooldown:Show() + end + else + cooldown:Hide() + end end -- Set item data function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCharName, matchesFilter, isReadOnly) self.bagID = bagID self.slotID = slotID + -- Also set the Blizzard slot ID for compatibility with ContainerFrameItemButtonTemplate behavior + if self.SetID and slotID then + self:SetID(slotID) + end self.itemData = itemData self.isBank = isBank or false self.otherChar = otherCharName self.isReadOnly = isReadOnly or false -- Track if this is read-only mode + -- Re-register for drag/drop every time (crucial for button reuse in Classic/Vanilla) + if not self.isReadOnly and not self.otherChar then + if self.RegisterForDrag then + self:RegisterForDrag("LeftButton") + end + if self.RegisterForClicks then + self:RegisterForClicks("LeftButtonUp", "RightButtonUp") + end + if self.EnableMouse then + self:EnableMouse(true) + end + else + -- Disable drag for read-only or other character items + if self.RegisterForDrag then + self:RegisterForDrag() -- Clear drag registration + end + if self.EnableMouse then + self:EnableMouse(true) -- Still enable mouse for tooltips + end + end + -- Default to true if not specified (for non-filtered displays) if matchesFilter == nil then matchesFilter = true end - local countText = getglobal(self:GetName().."_Count") + -- Use Blizzard's default count fontstring (ContainerFrameItemButtonTemplate creates $parentCount) + local countText = getglobal(self:GetName().."Count") local emptySlotBg = getglobal(self:GetName().."_EmptySlotBg") - -- Apply icon size setting - local iconSize = Guda.Modules.DB:GetSetting("iconSize") or addon.Constants.BUTTON_SIZE + -- Apply icon size setting (nil-safe) + local iconSize = 37 + if addon and addon.Modules and addon.Modules.DB and addon.Modules.DB.GetSetting then + iconSize = addon.Modules.DB:GetSetting("iconSize") or iconSize + elseif Guda and Guda.Modules and Guda.Modules.DB and Guda.Modules.DB.GetSetting then + iconSize = Guda.Modules.DB:GetSetting("iconSize") or iconSize + end + if addon and addon.Constants and addon.Constants.BUTTON_SIZE then + iconSize = iconSize or addon.Constants.BUTTON_SIZE + end self:SetWidth(iconSize) self:SetHeight(iconSize) + -- In live mode (readOnly=false), query real-time game state instead of cached DB + -- In read-only mode (readOnly=true), use cached itemData from DB + local displayTexture, displayCount + + if not self.isReadOnly then + -- LIVE MODE: Always query game state directly, never use cached itemData + local liveTexture, liveCount = GetContainerItemInfo(bagID, slotID) + if liveTexture then + displayTexture = liveTexture + displayCount = liveCount + self.hasItem = true + else + -- No item in this slot (even if itemData has cached data) + self.hasItem = false + end + else + -- READ-ONLY MODE: Use cached itemData from DB (can't query other characters) + if itemData and itemData.texture then + displayTexture = itemData.texture + displayCount = itemData.count + self.hasItem = true + else + self.hasItem = false + end + end + + -- Apply the determined texture and count + if self.hasItem then + if SetItemButtonTexture then SetItemButtonTexture(self, displayTexture) end + if SetItemButtonCount then SetItemButtonCount(self, displayCount or 1) end + if emptySlotBg then emptySlotBg:Hide() end + -- Update cooldown overlay for live items + if not self.isReadOnly and Guda_ItemButton_UpdateCooldown then + Guda_ItemButton_UpdateCooldown(self) + end + else + -- Fully clear all item button state for empty slots + if SetItemButtonTexture then SetItemButtonTexture(self, nil) end + if SetItemButtonCount then SetItemButtonCount(self, 0) end + if SetItemButtonDesaturated then SetItemButtonDesaturated(self, false) end + + -- Ensure cooldown overlay is hidden for empty slots + local cooldown = getglobal(self:GetName().."Cooldown") or self.cooldown + if cooldown and cooldown.Hide then cooldown:Hide() end + + -- Also clear the icon texture directly + local iconTexture = getglobal(self:GetName().."IconTexture") + if not iconTexture then + iconTexture = getglobal(self:GetName().."Icon") or self.icon or self.Icon + end + if iconTexture then + iconTexture:SetTexture(nil) + iconTexture:Hide() + end + + if emptySlotBg then emptySlotBg:Show() end + end + -- Resize empty slot background to match icon size (slightly larger to ensure coverage) if emptySlotBg then emptySlotBg:ClearAllPoints() @@ -190,7 +332,12 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha -- Apply icon font size setting to stack count text if countText and countText.GetFont then local font, _, flags = countText:GetFont() - local fontSize = Guda.Modules.DB:GetSetting("iconFontSize") or 12 + local fontSize = 12 + if addon and addon.Modules and addon.Modules.DB and addon.Modules.DB.GetSetting then + fontSize = addon.Modules.DB:GetSetting("iconFontSize") or fontSize + elseif Guda and Guda.Modules and Guda.Modules.DB and Guda.Modules.DB.GetSetting then + fontSize = Guda.Modules.DB:GetSetting("iconFontSize") or fontSize + end countText:SetFont(font, fontSize, flags) -- Adjust count text position based on icon size for better alignment @@ -204,16 +351,28 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha end end - if itemData then - self.hasItem = true + -- Get live metadata if in live mode (quality, link, lock status) + local itemQuality, itemLink, isLocked + if not self.isReadOnly and bagID and slotID and self.hasItem then + -- Query live game state for metadata + local _, _, locked, quality = GetContainerItemInfo(bagID, slotID) + itemLink = GetContainerItemLink(bagID, slotID) + itemQuality = quality + isLocked = locked + elseif itemData then + -- Use cached metadata from database + itemQuality = itemData.quality + itemLink = itemData.link + isLocked = itemData.locked + end - -- Set icon - SetItemButtonTexture(self, itemData.texture) + if self.hasItem then + -- Icon already set above based on mode (live vs cached) -- Gray out locked items (being traded, mailed, or auctioned) - BagShui style -- Don't desaturate items from other characters since they're read-only anyway if not self.otherChar and not self.isReadOnly then - SetItemButtonDesaturated(self, itemData.locked, 0.5, 0.5, 0.5) + SetItemButtonDesaturated(self, isLocked, 0.5, 0.5, 0.5) end -- Hide NormalTexture for filled slots (pfUI style) @@ -238,9 +397,9 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha self:SetAlpha(0.25) end - -- Set count - if itemData.count and itemData.count > 1 then - countText:SetText(itemData.count) + -- Set count (use displayCount which was determined above based on mode) + if displayCount and displayCount > 1 then + countText:SetText(displayCount) countText:Show() else countText:Hide() @@ -252,10 +411,16 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha -- Special border for keyring items (cyan/blue) self.qualityBorder:SetBackdropBorderColor(0.2, 0.8, 1.0, 1) self.qualityBorder:Show() - elseif itemData.quality and itemData.link then - -- Check settings to determine if we should show borders - local showEquipmentBorder = addon.Modules.DB:GetSetting("showQualityBorderEquipment") - local showOtherBorder = addon.Modules.DB:GetSetting("showQualityBorderOther") + elseif itemQuality and itemLink then + -- Check settings to determine if we should show borders (nil-safe) + local showEquipmentBorder, showOtherBorder + if addon and addon.Modules and addon.Modules.DB and addon.Modules.DB.GetSetting then + showEquipmentBorder = addon.Modules.DB:GetSetting("showQualityBorderEquipment") + showOtherBorder = addon.Modules.DB:GetSetting("showQualityBorderOther") + elseif Guda and Guda.Modules and Guda.Modules.DB and Guda.Modules.DB.GetSetting then + showEquipmentBorder = Guda.Modules.DB:GetSetting("showQualityBorderEquipment") + showOtherBorder = Guda.Modules.DB:GetSetting("showQualityBorderOther") + end -- Default to true if settings not found if showEquipmentBorder == nil then @@ -265,15 +430,25 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha showOtherBorder = true end - -- Check if item is equipment - local isEquipment = addon.Modules.Utils:IsEquipment(itemData.link) + -- Check if item is equipment (nil-safe) + local isEquipment = false + if addon and addon.Modules and addon.Modules.Utils and addon.Modules.Utils.IsEquipment then + isEquipment = addon.Modules.Utils:IsEquipment(itemLink) + elseif Guda and Guda.Modules and Guda.Modules.Utils and Guda.Modules.Utils.IsEquipment then + isEquipment = Guda.Modules.Utils:IsEquipment(itemLink) + end -- Determine if we should show the border based on item type and settings local shouldShowBorder = (isEquipment and showEquipmentBorder) or (not isEquipment and showOtherBorder) if shouldShowBorder then -- Show colored border for all items (Poor, Common, Uncommon, Rare, Epic, etc.) - local r, g, b = addon.Modules.Utils:GetQualityColor(itemData.quality) + local r, g, b = 1, 1, 1 + if addon and addon.Modules and addon.Modules.Utils and addon.Modules.Utils.GetQualityColor then + r, g, b = addon.Modules.Utils:GetQualityColor(itemQuality) + elseif Guda and Guda.Modules and Guda.Modules.Utils and Guda.Modules.Utils.GetQualityColor then + r, g, b = Guda.Modules.Utils:GetQualityColor(itemQuality) + end self.qualityBorder:SetBackdropBorderColor(r, g, b, 1) self.qualityBorder:Show() else @@ -417,51 +592,64 @@ end -- OnEnter handler (show tooltip) function Guda_ItemButton_OnEnter(self) - -- 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 - -- Bank item - highlight bank bag button - Guda_BankFrame_HighlightBagButton(self.bagID) - else - -- Regular bag item - highlight bag button - Guda_BagFrame_HighlightBagButton(self.bagID) - 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 + -- Bank item - highlight bank bag button + Guda_BankFrame_HighlightBagButton(self.bagID) + else + -- Regular bag item - highlight bag button + Guda_BagFrame_HighlightBagButton(self.bagID) + end + end - -- Early return for empty slots (no tooltip needed) - if not self.hasItem or not self.itemData then - return - end + -- Early return for empty slots (no tooltip needed) + if not self.hasItem or not self.itemData then + return + end - GameTooltip:SetOwner(self, "ANCHOR_RIGHT") + GameTooltip:SetOwner(UIParent, "ANCHOR_NONE") + -- important position of tooltip + GameTooltip:SetPoint("BOTTOMRIGHT", self, "TOPLEFT", 10, 0) - -- For bank items, use the item link directly since SetBagItem might not work for bank bags - if self.isBank and self.itemData.link then - -- Extract hyperlink from item link: |cFFFFFFFF|Hitem:1234:0:0:0|h[Name]|h|r -> item:1234:0:0:0 - local _, _, hyperlink = strfind(self.itemData.link, "|H(.+)|h") - if hyperlink then - GameTooltip:SetHyperlink(hyperlink) - else - -- Fallback to SetBagItem - GameTooltip:SetBagItem(self.bagID, self.slotID) - end - else - -- For regular bags, use SetBagItem as normal - GameTooltip:SetBagItem(self.bagID, self.slotID) - end - - GameTooltip:Show() - - -- Handle merchant sell cursor (same approach as BagShui) - if MerchantFrame:IsShown() and not self.isBank and not self.otherChar and self.hasItem then - ShowContainerSellCursor(self.bagID, self.slotID) - else - ResetCursor() - end + if self.otherChar or self.isReadOnly then + GameTooltip.GudaViewedCharacter = self.otherChar + if self.itemData and self.itemData.link then + GameTooltip:SetHyperlink(self.itemData.link) + else + GameTooltip:Hide() + return + end + -- Special handling for bank main bag when bank might be closed + elseif self.isBank and self.bagID == -1 then + local bankFrame = getglobal("BankFrame") + if bankFrame and bankFrame:IsVisible() then + -- Bank is open - use SetBagItem which will trigger inventory slot handling + GameTooltip:SetBagItem(self.bagID, self.slotID) + elseif self.itemData and self.itemData.link then + -- Bank is closed - use cached link + GameTooltip:SetHyperlink(self.itemData.link) + end + else + -- For live mode: use SetBagItem for all bags + GameTooltip:SetBagItem(self.bagID, self.slotID) + end + GameTooltip:Show() + -- Handle merchant sell cursor (same approach as BagShui) + if MerchantFrame:IsShown() and not self.isBank and not self.otherChar and self.hasItem then + ShowContainerSellCursor(self.bagID, self.slotID) + else + ResetCursor() + end end + -- OnLeave handler function Guda_ItemButton_OnLeave(self) + -- Clear any viewed character hint on the tooltip when leaving + if GameTooltip and GameTooltip.GudaViewedCharacter then + GameTooltip.GudaViewedCharacter = nil + end GameTooltip:Hide() ResetCursor() @@ -484,6 +672,11 @@ function Guda_ItemButton_OnDragStart(self, button) -- Only allow left button drag if button == "LeftButton" and self.hasItem then + -- Ensure the global click catcher doesn't intercept the drag/drop + local cc = getglobal and getglobal("Guda_ClickCatcher") + if cc and cc.Hide and cc:IsShown() then + cc:Hide() + end PickupContainerItem(self.bagID, self.slotID) end end @@ -497,6 +690,30 @@ function Guda_ItemButton_OnReceiveDrag(self) -- Place the item being dragged PickupContainerItem(self.bagID, self.slotID) + + -- Trigger UI update after drop completes (was skipped during drag) + if addon and addon.Modules and addon.Modules.BagFrame then + addon.Modules.BagFrame:Update() + end +end + +-- Handle mouse-up to emulate Blizzard drop behavior on 1.12 where OnReceiveDrag may not always fire +-- (rollback) no custom OnMouseUp; rely on Blizzard default + +-- OnMouseDown handler - pick up item when pressing mouse button (classic pattern) +-- (rollback) no custom OnMouseDown; rely on Blizzard default + +-- OnDragStop handler (optional cleanup) +function Guda_ItemButton_OnDragStop(self) + -- Reset cursor to default to avoid lingering special cursors + if ResetCursor then + ResetCursor() + end + + -- Trigger UI update after drag completes (was skipped during drag) + if addon and addon.Modules and addon.Modules.BagFrame then + addon.Modules.BagFrame:Update() + end end -- Stack split callback (called by StackSplitFrame) @@ -509,44 +726,43 @@ end -- OnClick handler function Guda_ItemButton_OnClick(self, button) -- Don't allow interaction with other characters' items or in read-only mode - if self.otherChar or self.isReadOnly then - return - end + if self.otherChar or self.isReadOnly then return end -- Set the split callback self.SplitStack = ItemButton_SplitStack - -- Handle modified clicks first + -- Modified clicks if IsShiftKeyDown() then if button == "LeftButton" and self.hasItem then + -- Get live count and link + local _, count = GetContainerItemInfo(self.bagID, self.slotID) + local itemLink = GetContainerItemLink(self.bagID, self.slotID) + -- Shift+Left Click on stackable item: Show split stack dialog - if self.itemData and self.itemData.count and self.itemData.count > 1 then - -- Get the actual stack count from the container - local _, count = GetContainerItemInfo(self.bagID, self.slotID) - if count and count > 1 then - -- Open the stack split frame (positioned to the left) - OpenStackSplitFrame(count, self, "BOTTOMRIGHT", "TOPRIGHT") - return - end + if count and count > 1 then + OpenStackSplitFrame(count, self, "BOTTOMRIGHT", "TOPRIGHT") + return end -- If not stackable or only 1 item, link to chat - if self.itemData and self.itemData.link and ChatFrameEditBox:IsVisible() then - ChatFrameEditBox:Insert(self.itemData.link) + if itemLink and ChatFrameEditBox:IsVisible() then + ChatFrameEditBox:Insert(itemLink) end end return elseif IsControlKeyDown() then -- Ctrl+Click: Dress up (if applicable) - if self.hasItem and self.itemData and self.itemData.link then - DressUpItemLink(self.itemData.link) + if self.hasItem then + local itemLink = GetContainerItemLink(self.bagID, self.slotID) + if itemLink then + DressUpItemLink(itemLink) + end end return end - -- Normal clicks - handle item pickup/placement if button == "LeftButton" then - -- Pick up or place item - PickupContainerItem(self.bagID, self.slotID) + -- Left clicks are handled by MouseDown/MouseUp to prevent instant self-drop. + return elseif button == "RightButton" then -- Right click: Use item (only if slot has an item) if self.hasItem then @@ -561,4 +777,4 @@ function Guda_ReleaseAllButtons() button:Hide() button:ClearAllPoints() end -end +end \ No newline at end of file diff --git a/UI/ItemButton.xml b/UI/ItemButton.xml index dccdb67..cc0affb 100644 --- a/UI/ItemButton.xml +++ b/UI/ItemButton.xml @@ -1,7 +1,7 @@ - - diff --git a/UI/MoneyDisplay.lua b/UI/MoneyDisplay.lua deleted file mode 100644 index dc764a0..0000000 --- a/UI/MoneyDisplay.lua +++ /dev/null @@ -1,57 +0,0 @@ --- Guda Money Display --- Handles money display in UI - -local addon = Guda - -local MoneyDisplay = {} -addon.Modules.MoneyDisplay = MoneyDisplay - --- Create a money display frame -function MoneyDisplay:CreateDisplay(parent, x, y) - local frame = CreateFrame("Frame", nil, parent) - frame:SetPoint("TOPLEFT", parent, "TOPLEFT", x, y) - frame:SetWidth(200) - frame:SetHeight(20) - - -- Gold text - frame.gold = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - frame.gold:SetPoint("LEFT", frame, "LEFT", 0, 0) - - -- Silver text - frame.silver = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - frame.silver:SetPoint("LEFT", frame.gold, "RIGHT", 2, 0) - - -- Copper text - frame.copper = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - frame.copper:SetPoint("LEFT", frame.silver, "RIGHT", 2, 0) - - return frame -end - --- Update money display -function MoneyDisplay:Update(frame, copper) - if not frame or not copper then - return - end - - local gold = math.floor(copper / 10000) - local silver = math.floor(mod(copper, 10000) / 100) - local bronze = mod(copper, 100) - - if gold > 0 then - frame.gold:SetText(gold .. "|cFFFFD700g|r") - frame.gold:Show() - else - frame.gold:Hide() - end - - if silver > 0 or gold > 0 then - frame.silver:SetText(silver .. "|cFFC0C0C0s|r") - frame.silver:Show() - else - frame.silver:Hide() - end - - frame.copper:SetText(bronze .. "|cFFFF6600c|r") - frame.copper:Show() -end