From 0eb173ad7e8f77d4fff17914d7073afb6f74484a Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 20:22:39 +0400 Subject: [PATCH 01/16] feat: category type --- Core/Database.lua | 8 ++ UI/BagFrame.lua | 183 ++++++++++++++++++++++++++++++++++++++++--- UI/BankFrame.lua | 180 +++++++++++++++++++++++++++++++++++++++--- UI/SettingsPopup.lua | 54 +++++++++++++ UI/SettingsPopup.xml | 25 ++++++ 5 files changed, 425 insertions(+), 25 deletions(-) diff --git a/Core/Database.lua b/Core/Database.lua index 0bdebcf..533b830 100644 --- a/Core/Database.lua +++ b/Core/Database.lua @@ -47,12 +47,20 @@ function DB:Initialize() bgTransparency = 0.15, showTrackedItems = true, showTooltipCounts = true, + bagViewType = "single", -- single, category + bankViewType = "single", -- single, category trackedItems = {}, }, } end -- Ensure new settings exist for existing installations + if Guda_CharDB.settings.bagViewType == nil then + Guda_CharDB.settings.bagViewType = "single" + end + if Guda_CharDB.settings.bankViewType == nil then + Guda_CharDB.settings.bankViewType = "single" + end if Guda_CharDB.settings.showTooltipCounts == nil then Guda_CharDB.settings.showTooltipCounts = true end diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index 1032ade..a1606fc 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -322,7 +322,12 @@ function BagFrame:Update() end -- Display items - self:DisplayItems(bagData, isOtherChar, charName) + local viewType = addon.Modules.DB:GetSetting("bagViewType") or "single" + if viewType == "category" then + self:DisplayItemsByCategory(bagData, isOtherChar, charName) + else + self:DisplayItems(bagData, isOtherChar, charName) + end -- Update money self:UpdateMoney() @@ -330,9 +335,20 @@ function BagFrame:Update() -- Update bag slots info self:UpdateBagSlotsInfo(bagData, isOtherChar) - -- Update bagline layout (hover option) + -- Update bagline layout (hover option) self:UpdateBaglineLayout() + -- Clean up unused section headers + local i = 1 + while true do + local header = getglobal("Guda_BagFrame_SectionHeader" .. i) + if not header then break end + if not header.inUse then + header:Hide() + end + i = i + 1 + end + -- Clean up unused buttons AFTER display is complete (prevents drag/drop issues) for _, bagParent in pairs(bagParents) do if bagParent then @@ -347,6 +363,157 @@ function BagFrame:Update() end end +-- Helper to get or create section header +function BagFrame:GetSectionHeader(index) + local name = "Guda_BagFrame_SectionHeader" .. index + local header = getglobal(name) + if not header then + header = CreateFrame("Frame", name, getglobal("Guda_BagFrame_ItemContainer")) + header:SetHeight(20) + local text = header:CreateFontString(nil, "OVERLAY", "GameFontNormal") + text:SetPoint("LEFT", header, "LEFT", 5, 0) + header.text = text + end + header.inUse = true + return header +end + +-- Helper to get or create bag parent frame +function BagFrame:GetBagParent(bagID) + local itemContainer = getglobal("Guda_BagFrame_ItemContainer") + 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 + return bagParents[bagID] +end + +-- Display items by category +function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) + 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") + + -- Reset headers + local idx = 1 + while true do + local h = getglobal("Guda_BagFrame_SectionHeader" .. idx) + if not h then break end + h.inUse = false + idx = idx + 1 + end + + -- Group items by category + local categories = {} + local categoryList = { + "Equipment", "Consumable", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Miscellaneous", "Soul Bag", "Keyring" + } + for _, cat in ipairs(categoryList) do categories[cat] = {} end + + for _, bagID in ipairs(addon.Constants.BAGS) do + if not hiddenBags[bagID] then + local bag = bagData[bagID] + if bag and bag.slots then + for slotID, itemData in pairs(bag.slots) do + if itemData then + local cat = itemData.class or "Miscellaneous" + -- Special handling for equipment + if itemData.equipSlot and itemData.equipSlot ~= "" then + cat = "Equipment" + end + if not categories[cat] then categories[cat] = {} end + table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) + end + end + end + end + end + + -- Handle Keyring if visible + if showKeyring and not hiddenBags[-2] then + local bag = bagData[-2] + if bag and bag.slots then + for slotID, itemData in pairs(bag.slots) do + if itemData then + table.insert(categories["Keyring"], {bagID = -2, slotID = slotID, itemData = itemData}) + end + end + end + end + + -- Layout + local x, y = 10, -10 + local headerIdx = 1 + local totalHeight = 20 + + for _, catName in ipairs(categoryList) do + local items = categories[catName] + if items and table.getn(items) > 0 then + -- Sort items in category + table.sort(items, function(a, b) + if a.itemData.quality ~= b.itemData.quality then + return a.itemData.quality > b.itemData.quality + end + return (a.itemData.name or "") < (b.itemData.name or "") + end) + + -- Add Header + local header = self:GetSectionHeader(headerIdx) + headerIdx = headerIdx + 1 + header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", 0, y) + header:SetWidth(perRow * (buttonSize + spacing)) + header.text:SetText(catName) + header:Show() + + y = y - 20 + + local col = 0 + for _, item in ipairs(items) do + local bagID = item.bagID + local slot = item.slotID + local itemData = item.itemData + + local bagParent = self:GetBagParent(bagID) + local button = Guda_GetItemButton(bagParent) + + button:SetParent(bagParent) + button:SetWidth(buttonSize) + button:SetHeight(buttonSize) + button:ClearAllPoints() + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + (col * (buttonSize + spacing)), y) + button:Show() + + local matchesFilter = true + if searchText ~= "" then + matchesFilter = BagFrame:MatchesSearch(itemData, searchText) + end + + Guda_ItemButton_SetItem(button, bagID, slot, itemData, false, isOtherChar and charName or nil, matchesFilter, true) + button.inUse = true + + col = col + 1 + if col >= perRow then + col = 0 + y = y - (buttonSize + spacing) + end + end + + if col > 0 then + y = y - (buttonSize + spacing) + end + y = y - 5 -- Padding between sections + end + end + + -- Update container height + itemContainer:SetHeight(math.abs(y) + 20) + self:UpdateFrameSize() +end + -- Display items function BagFrame:DisplayItems(bagData, isOtherChar, charName) local x, y = 10, -10 @@ -465,17 +632,7 @@ function BagFrame:DisplayItems(bagData, isOtherChar, charName) 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 + local bagParent = self:GetBagParent(bagID) for slot = 1, numSlots do local itemData = bag and bag.slots and bag.slots[slot] or nil diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index 4fe30f3..793891a 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -177,7 +177,12 @@ function BankFrame:Update() end end - self:DisplayItems(bankData, isOtherChar, charName) + local viewType = addon.Modules.DB:GetSetting("bankViewType") or "single" + if viewType == "category" then + self:DisplayItemsByCategory(bankData, isOtherChar, charName) + else + self:DisplayItems(bankData, isOtherChar, charName) + end -- Update money self:UpdateMoney() @@ -185,6 +190,17 @@ function BankFrame:Update() -- Update bank slots info self:UpdateBankSlotsInfo(bankData, isOtherChar) + -- Clean up unused section headers + local i = 1 + while true do + local header = getglobal("Guda_BankFrame_SectionHeader" .. i) + if not header then break end + if not header.inUse then + header:Hide() + end + i = i + 1 + end + -- Clean up unused buttons AFTER display is complete (prevents drag/drop issues) for _, bankBagParent in pairs(bankBagParents) do if bankBagParent then @@ -199,6 +215,152 @@ function BankFrame:Update() end end +-- Helper to get or create section header +function BankFrame:GetSectionHeader(index) + local name = "Guda_BankFrame_SectionHeader" .. index + local header = getglobal(name) + if not header then + header = CreateFrame("Frame", name, getglobal("Guda_BankFrame_ItemContainer")) + header:SetHeight(20) + local text = header:CreateFontString(nil, "OVERLAY", "GameFontNormal") + text:SetPoint("LEFT", header, "LEFT", 5, 0) + header.text = text + end + header.inUse = true + return header +end + +-- Helper to get or create bank bag parent frame +function BankFrame:GetBagParent(bagID) + local itemContainer = getglobal("Guda_BankFrame_ItemContainer") + if not bankBagParents[bagID] then + bankBagParents[bagID] = CreateFrame("Frame", "Guda_BankFrame_BagParent"..bagID, itemContainer) + bankBagParents[bagID]:SetAllPoints(itemContainer) + if bankBagParents[bagID].SetID then + bankBagParents[bagID]:SetID(bagID) + end + end + return bankBagParents[bagID] +end + +-- Display items by category +function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) + 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("bankColumns") or 10 + local itemContainer = getglobal("Guda_BankFrame_ItemContainer") + + -- Reset headers + local idx = 1 + while true do + local h = getglobal("Guda_BankFrame_SectionHeader" .. idx) + if not h then break end + h.inUse = false + idx = idx + 1 + end + + -- Group items by category + local categories = {} + local categoryList = { + "Equipment", "Consumable", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Miscellaneous" + } + for _, cat in ipairs(categoryList) do categories[cat] = {} end + + -- Bank main slots (bagID -1) + local bankMain = bankData[-1] + if bankMain and bankMain.slots then + for slotID, itemData in pairs(bankMain.slots) do + if itemData then + local cat = itemData.class or "Miscellaneous" + if itemData.equipSlot and itemData.equipSlot ~= "" then cat = "Equipment" end + if not categories[cat] then categories[cat] = {} end + table.insert(categories[cat], {bagID = -1, slotID = slotID, itemData = itemData}) + end + end + end + + -- Bank bags + for _, bagID in ipairs(addon.Constants.BANK_BAGS) do + if not hiddenBankBags[bagID] then + local bag = bankData[bagID] + if bag and bag.slots then + for slotID, itemData in pairs(bag.slots) do + if itemData then + local cat = itemData.class or "Miscellaneous" + if itemData.equipSlot and itemData.equipSlot ~= "" then cat = "Equipment" end + if not categories[cat] then categories[cat] = {} end + table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) + end + end + end + end + end + + -- Layout + local x, y = 5, -10 + local headerIdx = 1 + + for _, catName in ipairs(categoryList) do + local items = categories[catName] + if items and table.getn(items) > 0 then + -- Sort items in category + table.sort(items, function(a, b) + if a.itemData.quality ~= b.itemData.quality then + return a.itemData.quality > b.itemData.quality + end + return (a.itemData.name or "") < (b.itemData.name or "") + end) + + -- Add Header + local header = self:GetSectionHeader(headerIdx) + headerIdx = headerIdx + 1 + header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", 0, y) + header:SetWidth(perRow * (buttonSize + spacing)) + header.text:SetText(catName) + header:Show() + + y = y - 20 + + local col = 0 + for _, item in ipairs(items) do + local bagID = item.bagID + local slot = item.slotID + local itemData = item.itemData + + local bagParent = self:GetBagParent(bagID) + local button = Guda_GetItemButton(bagParent) + + button:SetParent(bagParent) + button:SetWidth(buttonSize) + button:SetHeight(buttonSize) + button:ClearAllPoints() + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + (col * (buttonSize + spacing)), y) + button:Show() + + local matchesFilter = self:PassesSearchFilter(itemData) + + Guda_ItemButton_SetItem(button, bagID, slot, itemData, true, isOtherChar and charName or nil, matchesFilter, true) + button.inUse = true + + col = col + 1 + if col >= perRow then + col = 0 + y = y - (buttonSize + spacing) + end + end + + if col > 0 then + y = y - (buttonSize + spacing) + end + y = y - 5 -- Padding between sections + end + end + + -- Update container height + itemContainer:SetHeight(math.abs(y) + 20) + self:UpdateFrameSize() +end + -- Display items function BankFrame:DisplayItems(bankData, isOtherChar, charName) local x, y = 5, -10 @@ -304,15 +466,7 @@ function BankFrame:DisplayItems(bankData, isOtherChar, charName) local matchesFilter = self:PassesSearchFilter(itemData) -- 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 bankBagParent = self:GetBagParent(bagID) local button = Guda_GetItemButton(bankBagParent) if button.isBagSlot then break end @@ -854,14 +1008,16 @@ function BankFrame:EnsureBagButtonsInitialized() btn = CreateFrame("Button", name, toolbar, "ItemButtonTemplate") -- Anchor sequenced to the left; position similar to XML if suffix == "BankBagMain" then - btn:SetSize(24, 24) + btn:SetWidth(24) + btn:SetHeight(24) btn:SetPoint("LEFT", toolbar, "LEFT", 13, 0) else -- Determine previous button local prev if bagID == 5 then prev = getglobal("Guda_BankFrame_Toolbar_BankBagMain") else prev = getglobal("Guda_BankFrame_Toolbar_BankBag"..tostring(bagID-1)) end - btn:SetSize(24, 24) + btn:SetWidth(24) + btn:SetHeight(24) if prev then btn:SetPoint("LEFT", prev, "RIGHT", 2, 0) else diff --git a/UI/SettingsPopup.lua b/UI/SettingsPopup.lua index f49f8ba..076d209 100644 --- a/UI/SettingsPopup.lua +++ b/UI/SettingsPopup.lua @@ -107,6 +107,8 @@ function Guda_SettingsPopup_OnShow(self) hoverBagline = false end local bgTransparency = Guda.Modules.DB:GetSetting("bgTransparency") or 0.15 + local bagViewType = Guda.Modules.DB:GetSetting("bagViewType") or "single" + local bankViewType = Guda.Modules.DB:GetSetting("bankViewType") or "single" -- Update sliders and checkboxes local bagSlider = getglobal("Guda_SettingsPopup_BagColumnsSlider") @@ -124,6 +126,8 @@ function Guda_SettingsPopup_OnShow(self) local hoverBaglineCheckbox = getglobal("Guda_SettingsPopup_HoverBaglineCheckbox") local hideFooterCheckbox = getglobal("Guda_SettingsPopup_HideFooterCheckbox") local showTooltipCountsCheckbox = getglobal("Guda_SettingsPopup_ShowTooltipCountsCheckbox") + local bagViewButton = getglobal("Guda_SettingsPopup_BagViewTypeButton") + local bankViewButton = getglobal("Guda_SettingsPopup_BankViewTypeButton") local showTooltipCounts = Guda.Modules.DB:GetSetting("showTooltipCounts") if showTooltipCounts == nil then @@ -186,6 +190,22 @@ function Guda_SettingsPopup_OnShow(self) showTooltipCountsCheckbox:SetChecked(showTooltipCounts and 1 or 0) end + if bagViewButton then + if bagViewType == "single" then + bagViewButton:SetText("Bag View: Single") + else + bagViewButton:SetText("Bag View: Category") + end + end + + if bankViewButton then + if bankViewType == "single" then + bankViewButton:SetText("Bank View: Single") + else + bankViewButton:SetText("Bank View: Category") + end + end + -- Update display (might be too tall for current frame size) local frame = getglobal("Guda_SettingsPopup") @@ -919,6 +939,40 @@ function Guda_SettingsPopup_ShowTooltipCountsCheckbox_OnClick(self) end end +-- Bag View Type Button OnClick +function Guda_SettingsPopup_BagViewTypeButton_OnClick() + local current = Guda.Modules.DB:GetSetting("bagViewType") or "single" + local newValue = (current == "single") and "category" or "single" + Guda.Modules.DB:SetSetting("bagViewType", newValue) + + local btn = getglobal("Guda_SettingsPopup_BagViewTypeButton") + if btn then + btn:SetText(newValue == "single" and "Bag View: Single" or "Bag View: Category") + end + + -- Refresh bag frame if it's open + if Guda_BagFrame:IsShown() then + Guda.Modules.BagFrame:Update() + end +end + +-- Bank View Type Button OnClick +function Guda_SettingsPopup_BankViewTypeButton_OnClick() + local current = Guda.Modules.DB:GetSetting("bankViewType") or "single" + local newValue = (current == "single") and "category" or "single" + Guda.Modules.DB:SetSetting("bankViewType", newValue) + + local btn = getglobal("Guda_SettingsPopup_BankViewTypeButton") + if btn then + btn:SetText(newValue == "single" and "Bank View: Single" or "Bank View: Category") + end + + -- Refresh bank frame if it's open + if Guda_BankFrame:IsShown() then + Guda.Modules.BankFrame:Update() + end +end + -- Initialize function SettingsPopup:Initialize() Guda:Debug("Settings popup initialized") diff --git a/UI/SettingsPopup.xml b/UI/SettingsPopup.xml index d75bd29..d6c9f6f 100644 --- a/UI/SettingsPopup.xml +++ b/UI/SettingsPopup.xml @@ -394,6 +394,31 @@ + + + + + From a95167a91a1303245f405d24faf32d7dcffbef4e Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 20:35:02 +0400 Subject: [PATCH 02/16] fix: category toggle --- UI/BagFrame.lua | 44 ++++++++++++++++++-------------------------- UI/BankFrame.lua | 42 +++++++++++++++++------------------------- 2 files changed, 35 insertions(+), 51 deletions(-) diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index a1606fc..edbf7e6 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -323,6 +323,17 @@ function BagFrame:Update() -- Display items local viewType = addon.Modules.DB:GetSetting("bagViewType") or "single" + + -- Reset all section headers before displaying items + local i = 1 + while true do + local header = getglobal("Guda_BagFrame_SectionHeader" .. i) + if not header then break end + header.inUse = false + header:Hide() + i = i + 1 + end + if viewType == "category" then self:DisplayItemsByCategory(bagData, isOtherChar, charName) else @@ -335,20 +346,9 @@ function BagFrame:Update() -- Update bag slots info self:UpdateBagSlotsInfo(bagData, isOtherChar) - -- Update bagline layout (hover option) + -- Update bagline layout (hover option) self:UpdateBaglineLayout() - -- Clean up unused section headers - local i = 1 - while true do - local header = getglobal("Guda_BagFrame_SectionHeader" .. i) - if not header then break end - if not header.inUse then - header:Hide() - end - i = i + 1 - end - -- Clean up unused buttons AFTER display is complete (prevents drag/drop issues) for _, bagParent in pairs(bagParents) do if bagParent then @@ -398,15 +398,6 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) local perRow = addon.Modules.DB:GetSetting("bagColumns") or 10 local itemContainer = getglobal("Guda_BagFrame_ItemContainer") - -- Reset headers - local idx = 1 - while true do - local h = getglobal("Guda_BagFrame_SectionHeader" .. idx) - if not h then break end - h.inUse = false - idx = idx + 1 - end - -- Group items by category local categories = {} local categoryList = { @@ -510,8 +501,9 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) end -- Update container height - itemContainer:SetHeight(math.abs(y) + 20) - self:UpdateFrameSize() + local finalHeight = math.abs(y) + 20 + itemContainer:SetHeight(finalHeight) + self:ResizeFrame(nil, nil, perRow, finalHeight) end -- Display items @@ -676,12 +668,12 @@ function BagFrame:DisplayItems(bagData, isOtherChar, charName) end -- Resize frame based on number of rows and columns -function BagFrame:ResizeFrame(currentRow, currentCol, columns) +function BagFrame:ResizeFrame(currentRow, currentCol, columns, overrideHeight) 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 + local totalRows = (currentRow or 0) + 1 -- Ensure at least 1 row if totalRows < 1 then @@ -690,7 +682,7 @@ function BagFrame:ResizeFrame(currentRow, currentCol, columns) -- Calculate required dimensions based on columns local containerWidth = (columns * (buttonSize + spacing)) + 20 - local containerHeight = (totalRows * (buttonSize + spacing)) + 20 + local containerHeight = overrideHeight or ((totalRows * (buttonSize + spacing)) + 20) local frameWidth = containerWidth + 20 -- Check if search bar is visible diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index 793891a..d88a657 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -178,6 +178,17 @@ function BankFrame:Update() end local viewType = addon.Modules.DB:GetSetting("bankViewType") or "single" + + -- Reset all section headers before displaying items + local i = 1 + while true do + local header = getglobal("Guda_BankFrame_SectionHeader" .. i) + if not header then break end + header.inUse = false + header:Hide() + i = i + 1 + end + if viewType == "category" then self:DisplayItemsByCategory(bankData, isOtherChar, charName) else @@ -190,17 +201,6 @@ function BankFrame:Update() -- Update bank slots info self:UpdateBankSlotsInfo(bankData, isOtherChar) - -- Clean up unused section headers - local i = 1 - while true do - local header = getglobal("Guda_BankFrame_SectionHeader" .. i) - if not header then break end - if not header.inUse then - header:Hide() - end - i = i + 1 - end - -- Clean up unused buttons AFTER display is complete (prevents drag/drop issues) for _, bankBagParent in pairs(bankBagParents) do if bankBagParent then @@ -250,15 +250,6 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) local perRow = addon.Modules.DB:GetSetting("bankColumns") or 10 local itemContainer = getglobal("Guda_BankFrame_ItemContainer") - -- Reset headers - local idx = 1 - while true do - local h = getglobal("Guda_BankFrame_SectionHeader" .. idx) - if not h then break end - h.inUse = false - idx = idx + 1 - end - -- Group items by category local categories = {} local categoryList = { @@ -357,8 +348,9 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) end -- Update container height - itemContainer:SetHeight(math.abs(y) + 20) - self:UpdateFrameSize() + local finalHeight = math.abs(y) + 20 + itemContainer:SetHeight(finalHeight) + self:ResizeFrame(nil, nil, perRow, finalHeight) end -- Display items @@ -495,12 +487,12 @@ function BankFrame:DisplayItems(bankData, isOtherChar, charName) end -- Resize bank frame based on number of rows and columns -function BankFrame:ResizeFrame(currentRow, currentCol, columns) +function BankFrame:ResizeFrame(currentRow, currentCol, columns, overrideHeight) 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 + local totalRows = (currentRow or 0) + 1 if totalRows < 1 then totalRows = 1 end @@ -512,7 +504,7 @@ function BankFrame:ResizeFrame(currentRow, currentCol, columns) -- Calculate required dimensions local containerWidth = (columns * (buttonSize + spacing)) + 10 - local containerHeight = (totalRows * (buttonSize + spacing)) + 20 + local containerHeight = overrideHeight or ((totalRows * (buttonSize + spacing)) + 20) local frameWidth = containerWidth + 30 -- Check if search bar is visible From 079d134a60c1c0e19233a98e403731e1adc93267 Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 20:49:03 +0400 Subject: [PATCH 03/16] fix: category types added --- UI/BagFrame.lua | 37 ++++++++++++++++++++++++++++++++----- UI/BankFrame.lua | 46 ++++++++++++++++++++++++++++++++++++++-------- UI/ItemButton.lua | 44 ++++++++++++++++++++++---------------------- 3 files changed, 92 insertions(+), 35 deletions(-) diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index edbf7e6..5ae802f 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -336,8 +336,10 @@ function BagFrame:Update() if viewType == "category" then self:DisplayItemsByCategory(bagData, isOtherChar, charName) + if getglobal("Guda_BagFrame_SortButton") then getglobal("Guda_BagFrame_SortButton"):Hide() end else self:DisplayItems(bagData, isOtherChar, charName) + if getglobal("Guda_BagFrame_SortButton") then getglobal("Guda_BagFrame_SortButton"):Show() end end -- Update money @@ -401,7 +403,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) -- Group items by category local categories = {} local categoryList = { - "Equipment", "Consumable", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Miscellaneous", "Soul Bag", "Keyring" + "Weapon", "Armor", "Consumable", "Food", "Drink", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Keyring", "Miscellaneous" } for _, cat in ipairs(categoryList) do categories[cat] = {} end @@ -412,11 +414,33 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) for slotID, itemData in pairs(bag.slots) do if itemData then local cat = itemData.class or "Miscellaneous" - -- Special handling for equipment + + -- Split Equipment into Weapon and Armor if itemData.equipSlot and itemData.equipSlot ~= "" then - cat = "Equipment" + if itemData.class == "Weapon" or itemData.class == "Armor" then + cat = itemData.class + else + cat = "Armor" -- Accessories etc usually fall here if equippable + end end - if not categories[cat] then categories[cat] = {} end + + -- Detect Food and Drink + if itemData.class == "Consumable" then + local sub = itemData.subclass or "" + if sub == "Food & Drink" or string.find(sub, "Food") or string.find(sub, "Drink") then + -- Try to be more specific if possible, but "Food & Drink" is the standard subclass + -- For simplicity we can check item name or texture if we wanted to split, + -- but user asked for "Food", "Drink". + -- If we can't easily distinguish, we'll put them in "Food" or "Drink" based on subclass text + if string.find(sub, "Drink") then + cat = "Drink" + else + cat = "Food" + end + end + end + + if not categories[cat] then cat = "Miscellaneous" end table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) end end @@ -444,8 +468,11 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) for _, catName in ipairs(categoryList) do local items = categories[catName] if items and table.getn(items) > 0 then - -- Sort items in category + -- Sort items in category: Subclass > Quality > Name table.sort(items, function(a, b) + if a.itemData.subclass ~= b.itemData.subclass then + return (a.itemData.subclass or "") < (b.itemData.subclass or "") + end if a.itemData.quality ~= b.itemData.quality then return a.itemData.quality > b.itemData.quality end diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index d88a657..1329c6a 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -191,8 +191,10 @@ function BankFrame:Update() if viewType == "category" then self:DisplayItemsByCategory(bankData, isOtherChar, charName) + if getglobal("Guda_BankFrame_SortButton") then getglobal("Guda_BankFrame_SortButton"):Hide() end else self:DisplayItems(bankData, isOtherChar, charName) + if getglobal("Guda_BankFrame_SortButton") then getglobal("Guda_BankFrame_SortButton"):Show() end end -- Update money @@ -253,18 +255,45 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) -- Group items by category local categories = {} local categoryList = { - "Equipment", "Consumable", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Miscellaneous" + "Weapon", "Armor", "Consumable", "Food", "Drink", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Miscellaneous" } for _, cat in ipairs(categoryList) do categories[cat] = {} end + -- Helper to assign category + local function GetItemCategory(itemData) + local cat = itemData.class or "Miscellaneous" + + -- Split Equipment into Weapon and Armor + if itemData.equipSlot and itemData.equipSlot ~= "" then + if itemData.class == "Weapon" or itemData.class == "Armor" then + cat = itemData.class + else + cat = "Armor" + end + end + + -- Detect Food and Drink + if itemData.class == "Consumable" then + local sub = itemData.subclass or "" + if sub == "Food & Drink" or string.find(sub, "Food") or string.find(sub, "Drink") then + if string.find(sub, "Drink") then + cat = "Drink" + else + cat = "Food" + end + end + end + + if not categories[cat] then cat = "Miscellaneous" end + return cat + end + -- Bank main slots (bagID -1) local bankMain = bankData[-1] if bankMain and bankMain.slots then for slotID, itemData in pairs(bankMain.slots) do if itemData then - local cat = itemData.class or "Miscellaneous" - if itemData.equipSlot and itemData.equipSlot ~= "" then cat = "Equipment" end - if not categories[cat] then categories[cat] = {} end + local cat = GetItemCategory(itemData) table.insert(categories[cat], {bagID = -1, slotID = slotID, itemData = itemData}) end end @@ -277,9 +306,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) if bag and bag.slots then for slotID, itemData in pairs(bag.slots) do if itemData then - local cat = itemData.class or "Miscellaneous" - if itemData.equipSlot and itemData.equipSlot ~= "" then cat = "Equipment" end - if not categories[cat] then categories[cat] = {} end + local cat = GetItemCategory(itemData) table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) end end @@ -294,8 +321,11 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) for _, catName in ipairs(categoryList) do local items = categories[catName] if items and table.getn(items) > 0 then - -- Sort items in category + -- Sort items in category: Subclass > Quality > Name table.sort(items, function(a, b) + if a.itemData.subclass ~= b.itemData.subclass then + return (a.itemData.subclass or "") < (b.itemData.subclass or "") + end if a.itemData.quality ~= b.itemData.quality then return a.itemData.quality > b.itemData.quality end diff --git a/UI/ItemButton.lua b/UI/ItemButton.lua index 5d83090..51dc495 100644 --- a/UI/ItemButton.lua +++ b/UI/ItemButton.lua @@ -949,28 +949,28 @@ function Guda_ItemButton_OnEnter(self) -- Debug: print hovered item's texture path to chat -- Uses GetItemInfo on the hovered item's ID - --if self.hasItem then - -- local link = nil - -- if self.itemData and self.itemData.link then - -- link = self.itemData.link - -- else - -- -- Fallback to live bag query - -- link = GetContainerItemLink(self.bagID, self.slotID) - -- end - -- if link and addon and addon.Modules and addon.Modules.Utils and addon.Modules.Utils.ExtractItemID then - -- local itemID = addon.Modules.Utils:ExtractItemID(link) - -- if itemID and addon.Modules.Utils.GetItemInfoSafe then - -- local name, itemLink, itemRarity, itemLevel, itemCategory, itemType, itemStackCount, - -- itemSubType, itemTexture, itemEquipLoc, itemSellPrice = addon.Modules.Utils:GetItemInfoSafe(itemID) - -- if addon and addon.Print then - -- addon:Print("itemTexture: %s", tostring(itemTexture)) - -- addon:Print("itemCategory: %s", tostring(itemCategory)) - -- addon:Print("itemType: %s", tostring(itemType)) - -- addon:Print("itemType: %s", tostring(itemType)) - -- end - -- end - -- end - --end + if self.hasItem then + local link = nil + if self.itemData and self.itemData.link then + link = self.itemData.link + else + -- Fallback to live bag query + link = GetContainerItemLink(self.bagID, self.slotID) + end + if link and addon and addon.Modules and addon.Modules.Utils and addon.Modules.Utils.ExtractItemID then + local itemID = addon.Modules.Utils:ExtractItemID(link) + if itemID and addon.Modules.Utils.GetItemInfoSafe then + local name, itemLink, itemRarity, itemLevel, itemCategory, itemType, itemStackCount, + itemSubType, itemTexture, itemEquipLoc, itemSellPrice = addon.Modules.Utils:GetItemInfoSafe(itemID) + if addon and addon.Print then + addon:Print("itemTexture: %s", tostring(itemTexture)) + addon:Print("itemCategory: %s", tostring(itemCategory)) + addon:Print("itemType: %s", tostring(itemType)) + addon:Print("itemType: %s", tostring(itemType)) + end + end + end + end -- 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) From 9dcf76d80afca112bb077281b99d7fccd975c9f7 Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 21:24:07 +0400 Subject: [PATCH 04/16] feat: categorized Home, Tools, Mounts --- Sorting/SortEngine.lua | 17 ++-- UI/BagFrame.lua | 193 ++++++++++++++++++++++++++++++++++------- UI/BankFrame.lua | 189 +++++++++++++++++++++++++++++++++++----- 3 files changed, 339 insertions(+), 60 deletions(-) diff --git a/Sorting/SortEngine.lua b/Sorting/SortEngine.lua index 0127b65..376bc01 100644 --- a/Sorting/SortEngine.lua +++ b/Sorting/SortEngine.lua @@ -56,14 +56,15 @@ local CATEGORY_ORDER = { ["Projectile"] = 3, ["Weapon"] = 4, -- Non-equippable weapons ["Armor"] = 5, -- Non-equippable armor - ["Quiver"] = 6, - ["Reagent"] = 7, - ["Trade Goods"] = 8, - ["Recipe"] = 9, - ["Container"] = 10, - ["Quest"] = 11, - ["Key"] = 12, - ["Miscellaneous"] = 13, + ["Tools"] = 6, + ["Quiver"] = 7, + ["Reagent"] = 8, + ["Trade Goods"] = 9, + ["Recipe"] = 10, + ["Container"] = 11, + ["Quest"] = 12, + ["Key"] = 13, + ["Miscellaneous"] = 14, } -- Subclass ordering for grouping related items diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index 5ae802f..be199ce 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -406,6 +406,12 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) "Weapon", "Armor", "Consumable", "Food", "Drink", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Keyring", "Miscellaneous" } for _, cat in ipairs(categoryList) do categories[cat] = {} end + + local specialItems = { + Hearthstone = {}, + Mount = {}, + Tools = {} + } for _, bagID in ipairs(addon.Constants.BAGS) do if not hiddenBags[bagID] then @@ -414,34 +420,48 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) for slotID, itemData in pairs(bag.slots) do if itemData then local cat = itemData.class or "Miscellaneous" - + local itemName = itemData.name or "" + + -- Detect Hearthstone + if string.find(itemName, "Hearthstone") then + table.insert(specialItems.Hearthstone, {bagID = bagID, slotID = slotID, itemData = itemData}) + -- Detect Mounts + elseif addon.Modules.SortEngine and addon.Modules.SortEngine.IsMount and addon.Modules.SortEngine.IsMount(itemData.texture) then + table.insert(specialItems.Mount, {bagID = bagID, slotID = slotID, itemData = itemData}) + -- Detect Tools category + elseif string.find(itemName, "Runed .* Rod") or + string.find(itemName, "Fishing Pole") or + string.find(itemName, "Mining Pick") or + string.find(itemName, "Blacksmith Hammer") or + itemName == "Arclight Spanner" or + itemName == "Gyromatic Micro-Adjustor" or + itemName == "Philosopher's Stone" or + string.find(itemName, "Skinning Knife") or + itemName == "Blood Scythe" then + table.insert(specialItems.Tools, {bagID = bagID, slotID = slotID, itemData = itemData}) -- Split Equipment into Weapon and Armor - if itemData.equipSlot and itemData.equipSlot ~= "" then + elseif itemData.equipSlot and itemData.equipSlot ~= "" then if itemData.class == "Weapon" or itemData.class == "Armor" then cat = itemData.class else cat = "Armor" -- Accessories etc usually fall here if equippable end - end - + table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) -- Detect Food and Drink - if itemData.class == "Consumable" then + elseif itemData.class == "Consumable" then local sub = itemData.subclass or "" if sub == "Food & Drink" or string.find(sub, "Food") or string.find(sub, "Drink") then - -- Try to be more specific if possible, but "Food & Drink" is the standard subclass - -- For simplicity we can check item name or texture if we wanted to split, - -- but user asked for "Food", "Drink". - -- If we can't easily distinguish, we'll put them in "Food" or "Drink" based on subclass text if string.find(sub, "Drink") then cat = "Drink" else cat = "Food" end end + table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) + else + if not categories[cat] then cat = "Miscellaneous" end + table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) end - - if not categories[cat] then cat = "Miscellaneous" end - table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) end end end @@ -461,13 +481,16 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) end -- Layout - local x, y = 10, -10 + local startX, startY = 10, -10 + local currentX, currentY = 0, 0 + local rowMaxHeight = 0 local headerIdx = 1 - local totalHeight = 20 + local totalWidth = perRow * (buttonSize + spacing) for _, catName in ipairs(categoryList) do local items = categories[catName] - if items and table.getn(items) > 0 then + local numItems = items and table.getn(items) or 0 + if numItems > 0 then -- Sort items in category: Subclass > Quality > Name table.sort(items, function(a, b) if a.itemData.subclass ~= b.itemData.subclass then @@ -479,17 +502,30 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) return (a.itemData.name or "") < (b.itemData.name or "") end) + local blockCols = numItems + if blockCols > perRow then blockCols = perRow end + local blockRows = math.ceil(numItems / perRow) + local blockWidth = blockCols * (buttonSize + spacing) + local blockHeight = 20 + (blockRows * (buttonSize + spacing)) + 5 -- 20 header, 5 padding + + -- Check if it fits in current row + if currentX > 0 and currentX + blockWidth > totalWidth + 5 then + currentX = 0 + currentY = currentY + rowMaxHeight + rowMaxHeight = 0 + end + -- Add Header local header = self:GetSectionHeader(headerIdx) headerIdx = headerIdx + 1 - header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", 0, y) - header:SetWidth(perRow * (buttonSize + spacing)) + header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", startX + currentX, startY - currentY) + header:SetWidth(blockWidth) header.text:SetText(catName) header:Show() - y = y - 20 - + local itemY = currentY + 20 local col = 0 + local row = 0 for _, item in ipairs(items) do local bagID = item.bagID local slot = item.slotID @@ -502,28 +538,123 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) button:SetWidth(buttonSize) button:SetHeight(buttonSize) button:ClearAllPoints() - button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + (col * (buttonSize + spacing)), y) + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", startX + currentX + (col * (buttonSize + spacing)), startY - (itemY + (row * (buttonSize + spacing)))) button:Show() - local matchesFilter = true - if searchText ~= "" then - matchesFilter = BagFrame:MatchesSearch(itemData, searchText) - end - - Guda_ItemButton_SetItem(button, bagID, slot, itemData, false, isOtherChar and charName or nil, matchesFilter, true) + local matchesFilter = self:PassesSearchFilter(itemData) + Guda_ItemButton_SetItem(button, bagID, slot, itemData, false, isOtherChar and charName or nil, matchesFilter, isOtherChar) button.inUse = true col = col + 1 - if col >= perRow then + if col >= blockCols then col = 0 - y = y - (buttonSize + spacing) + row = row + 1 end end - if col > 0 then - y = y - (buttonSize + spacing) + if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end + currentX = currentX + blockWidth + end + end + + -- Update Y for bottom sections + local y = currentY + rowMaxHeight + + -- Special sections at bottom (Hearthstone, Mount, Tools) + local bottomSections = { + { name = "Home", items = specialItems.Hearthstone }, + { name = "Mounts", items = specialItems.Mount }, + { name = "Tools", items = specialItems.Tools } + } + + local x = startX + y = startY - y -- convert to coordinate relative to TOPLEFT + + -- Add spacing before special section + local hasAnyBottom = false + for _, sec in ipairs(bottomSections) do + if table.getn(sec.items) > 0 then + hasAnyBottom = true + break + end + end + + if hasAnyBottom then + y = y - 10 + local col = 0 + local sectionMaxHeight = 0 + + for _, sec in ipairs(bottomSections) do + local items = sec.items + local numItems = table.getn(items) + if numItems > 0 then + -- Sort Tools (Hearthstone/Mounts don't usually need it but good for consistency) + if sec.name == "Tools" then + table.sort(items, function(a, b) + if a.itemData.quality ~= b.itemData.quality then + return a.itemData.quality > b.itemData.quality + end + return (a.itemData.name or "") < (b.itemData.name or "") + end) + end + + local blockCols = numItems + if blockCols > perRow then blockCols = perRow end + local blockRows = math.ceil(numItems / perRow) + local blockWidth = blockCols * (buttonSize + spacing) + local blockHeight = 20 + (blockRows * (buttonSize + spacing)) + + -- Check if it fits in current row (Inline block for bottom sections too) + if col > 0 and (col * (buttonSize + spacing)) + blockWidth > totalWidth + 5 then + col = 0 + y = y - sectionMaxHeight - 5 + sectionMaxHeight = 0 + end + + -- Add Header + local header = self:GetSectionHeader(headerIdx) + headerIdx = headerIdx + 1 + header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + (col * (buttonSize + spacing)), y) + header:SetWidth(blockWidth) + header.text:SetText(sec.name) + header:Show() + + local itemY = y - 20 + local sCol = 0 + local sRow = 0 + for _, item in ipairs(items) do + local bagParent = self:GetBagParent(item.bagID) + local button = Guda_GetItemButton(bagParent) + button:SetParent(bagParent) + button:SetWidth(buttonSize) + button:SetHeight(buttonSize) + button:ClearAllPoints() + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + ((col + sCol) * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) + button:Show() + Guda_ItemButton_SetItem(button, item.bagID, item.slotID, item.itemData, false, isOtherChar and charName or nil, self:PassesSearchFilter(item.itemData), isOtherChar) + button.inUse = true + + sCol = sCol + 1 + if sCol >= blockCols then + sCol = 0 + sRow = sRow + 1 + end + end + + if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end + col = col + blockCols + + -- If we wrapped exactly at the end of a block + if col >= perRow then + col = 0 + y = y - sectionMaxHeight - 5 + sectionMaxHeight = 0 + end end - y = y - 5 -- Padding between sections + end + + if col > 0 then + y = y - sectionMaxHeight end end diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index 1329c6a..2754141 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -259,10 +259,44 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) } for _, cat in ipairs(categoryList) do categories[cat] = {} end + local specialItems = { + Hearthstone = {}, + Mount = {}, + Tools = {} + } + -- Helper to assign category - local function GetItemCategory(itemData) + local function CategorizeItem(itemData, bagID, slotID) + local itemName = itemData.name or "" + + -- Detect Hearthstone + if string.find(itemName, "Hearthstone") then + table.insert(specialItems.Hearthstone, {bagID = bagID, slotID = slotID, itemData = itemData}) + return + end + + -- Detect Mounts + if addon.Modules.SortEngine and addon.Modules.SortEngine.IsMount and addon.Modules.SortEngine.IsMount(itemData.texture) then + table.insert(specialItems.Mount, {bagID = bagID, slotID = slotID, itemData = itemData}) + return + end + + -- Detect Tools category + if string.find(itemName, "Runed .* Rod") or + string.find(itemName, "Fishing Pole") or + string.find(itemName, "Mining Pick") or + string.find(itemName, "Blacksmith Hammer") or + itemName == "Arclight Spanner" or + itemName == "Gyromatic Micro-Adjustor" or + itemName == "Philosopher's Stone" or + string.find(itemName, "Skinning Knife") or + itemName == "Blood Scythe" then + table.insert(specialItems.Tools, {bagID = bagID, slotID = slotID, itemData = itemData}) + return + end + local cat = itemData.class or "Miscellaneous" - + -- Split Equipment into Weapon and Armor if itemData.equipSlot and itemData.equipSlot ~= "" then if itemData.class == "Weapon" or itemData.class == "Armor" then @@ -285,7 +319,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) end if not categories[cat] then cat = "Miscellaneous" end - return cat + table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) end -- Bank main slots (bagID -1) @@ -293,8 +327,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) if bankMain and bankMain.slots then for slotID, itemData in pairs(bankMain.slots) do if itemData then - local cat = GetItemCategory(itemData) - table.insert(categories[cat], {bagID = -1, slotID = slotID, itemData = itemData}) + CategorizeItem(itemData, -1, slotID) end end end @@ -306,8 +339,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) if bag and bag.slots then for slotID, itemData in pairs(bag.slots) do if itemData then - local cat = GetItemCategory(itemData) - table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) + CategorizeItem(itemData, bagID, slotID) end end end @@ -315,12 +347,16 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) end -- Layout - local x, y = 5, -10 + local startX, startY = 5, -10 + local currentX, currentY = 0, 0 + local rowMaxHeight = 0 local headerIdx = 1 + local totalWidth = perRow * (buttonSize + spacing) for _, catName in ipairs(categoryList) do local items = categories[catName] - if items and table.getn(items) > 0 then + local numItems = items and table.getn(items) or 0 + if numItems > 0 then -- Sort items in category: Subclass > Quality > Name table.sort(items, function(a, b) if a.itemData.subclass ~= b.itemData.subclass then @@ -332,17 +368,30 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) return (a.itemData.name or "") < (b.itemData.name or "") end) + local blockCols = numItems + if blockCols > perRow then blockCols = perRow end + local blockRows = math.ceil(numItems / perRow) + local blockWidth = blockCols * (buttonSize + spacing) + local blockHeight = 20 + (blockRows * (buttonSize + spacing)) + 5 + + -- Check if it fits in current row + if currentX > 0 and currentX + blockWidth > totalWidth + 5 then + currentX = 0 + currentY = currentY + rowMaxHeight + rowMaxHeight = 0 + end + -- Add Header local header = self:GetSectionHeader(headerIdx) headerIdx = headerIdx + 1 - header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", 0, y) - header:SetWidth(perRow * (buttonSize + spacing)) + header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", startX + currentX, startY - currentY) + header:SetWidth(blockWidth) header.text:SetText(catName) header:Show() - y = y - 20 - + local itemY = currentY + 20 local col = 0 + local row = 0 for _, item in ipairs(items) do local bagID = item.bagID local slot = item.slotID @@ -355,28 +404,126 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) button:SetWidth(buttonSize) button:SetHeight(buttonSize) button:ClearAllPoints() - button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + (col * (buttonSize + spacing)), y) + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", startX + currentX + (col * (buttonSize + spacing)), startY - (itemY + (row * (buttonSize + spacing)))) button:Show() local matchesFilter = self:PassesSearchFilter(itemData) - Guda_ItemButton_SetItem(button, bagID, slot, itemData, true, isOtherChar and charName or nil, matchesFilter, true) button.inUse = true col = col + 1 - if col >= perRow then + if col >= blockCols then col = 0 - y = y - (buttonSize + spacing) + row = row + 1 end end - if col > 0 then - y = y - (buttonSize + spacing) - end - y = y - 5 -- Padding between sections + if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end + currentX = currentX + blockWidth end end + -- Update Y for bottom sections + local y = currentY + rowMaxHeight + + -- Special sections at bottom (Hearthstone, Mount, Tools) + local bottomSections = { + { name = "Home", items = specialItems.Hearthstone }, + { name = "Mounts", items = specialItems.Mount }, + { name = "Tools", items = specialItems.Tools } + } + + local x = startX + y = startY - y -- convert to coordinate relative to TOPLEFT + + -- Add spacing before special section + local hasAnyBottom = false + for _, sec in ipairs(bottomSections) do + if table.getn(sec.items) > 0 then + hasAnyBottom = true + break + end + end + + if hasAnyBottom then + y = y - 10 + local col = 0 + local sectionMaxHeight = 0 + + for _, sec in ipairs(bottomSections) do + local items = sec.items + local numItems = table.getn(items) + if numItems > 0 then + -- Sort Tools (Hearthstone/Mounts don't usually need it but good for consistency) + if sec.name == "Tools" then + table.sort(items, function(a, b) + if a.itemData.quality ~= b.itemData.quality then + return a.itemData.quality > b.itemData.quality + end + return (a.itemData.name or "") < (b.itemData.name or "") + end) + end + + local blockCols = numItems + if blockCols > perRow then blockCols = perRow end + local blockRows = math.ceil(numItems / perRow) + local blockWidth = blockCols * (buttonSize + spacing) + local blockHeight = 20 + (blockRows * (buttonSize + spacing)) + + -- Check if it fits in current row (Inline block for bottom sections too) + if col > 0 and (col * (buttonSize + spacing)) + blockWidth > totalWidth + 5 then + col = 0 + y = y - sectionMaxHeight - 5 + sectionMaxHeight = 0 + end + + -- Add Header + local header = self:GetSectionHeader(headerIdx) + headerIdx = headerIdx + 1 + header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + (col * (buttonSize + spacing)), y) + header:SetWidth(blockWidth) + header.text:SetText(sec.name) + header:Show() + + local itemY = y - 20 + local sCol = 0 + local sRow = 0 + for _, item in ipairs(items) do + local bagParent = self:GetBagParent(item.bagID) + local button = Guda_GetItemButton(bagParent) + button:SetParent(bagParent) + button:SetWidth(buttonSize) + button:SetHeight(buttonSize) + button:ClearAllPoints() + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + ((col + sCol) * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) + button:Show() + Guda_ItemButton_SetItem(button, item.bagID, item.slotID, item.itemData, true, isOtherChar and charName or nil, self:PassesSearchFilter(item.itemData), true) + button.inUse = true + + sCol = sCol + 1 + if sCol >= blockCols then + sCol = 0 + sRow = sRow + 1 + end + end + + if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end + col = col + blockCols + + -- If we wrapped exactly at the end of a block + if col >= perRow then + col = 0 + y = y - sectionMaxHeight - 5 + sectionMaxHeight = 0 + end + end + end + + if col > 0 then + y = y - sectionMaxHeight + end + end + -- Update container height local finalHeight = math.abs(y) + 20 itemContainer:SetHeight(finalHeight) From dddbbb7407ff32bb417f08d7a12de7b112e88caf Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 21:32:49 +0400 Subject: [PATCH 05/16] fix: category spacing --- Core/Utils.lua | 51 +++++++++++++++++++++++++++++++++++++----------- UI/BagFrame.lua | 16 ++++++++++----- UI/BankFrame.lua | 15 +++++++++----- 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/Core/Utils.lua b/Core/Utils.lua index b862dbc..ca78ade 100644 --- a/Core/Utils.lua +++ b/Core/Utils.lua @@ -155,6 +155,46 @@ function Utils:FormatTimeAgo(timestamp) end end +-- Create hidden tooltip for scanning (only once) +local scanTooltip = nil +local function GetScanTooltip() + if not scanTooltip then + scanTooltip = CreateFrame("GameTooltip", "GudaBagScanTooltip", nil, "GameTooltipTemplate") + scanTooltip:SetOwner(WorldFrame, "ANCHOR_NONE") + end + return scanTooltip +end + +-- Check if an item is a quest item by scanning its tooltip +function Utils:IsQuestItemTooltip(bagID, slotID) + if not bagID or not slotID then return false end + + local tooltip = GetScanTooltip() + tooltip:ClearLines() + tooltip:SetBagItem(bagID, slotID) + + -- Check all tooltip lines for quest-related text + for i = 1, tooltip:NumLines() do + local line = getglobal("GudaBagScanTooltipTextLeft" .. i) + if line then + local text = line:GetText() + if text then + -- Check for quest starter patterns + if string.find(text, "Quest Starter") or + string.find(text, "This Item Begins a Quest") or + string.find(text, "Use: Starts a Quest") then + return true + -- Check for regular quest item patterns + elseif string.find(text, "Quest Item") or + string.find(text, "Manual") then + return true + end + end + end + end + return false +end + -- Get bag slot count function Utils:GetBagSlotCount(bagID) if bagID == -1 then @@ -190,17 +230,6 @@ function Utils:TruncateText(text, maxLen) return string.sub(text, 1, maxLen - 3) .. "..." end --- Create hidden tooltip for scanning (only once) -local scanTooltip = nil -local function GetScanTooltip() - if not scanTooltip then - scanTooltip = CreateFrame("GameTooltip", "GudaBagScanTooltip", nil, "GameTooltipTemplate") - scanTooltip:SetOwner(WorldFrame, "ANCHOR_NONE") - end - return scanTooltip -end - --- Check if a bag is Quiver or Ammo Pouch function Utils:IsAmmoQuiverBag(bagID) -- Skip backpack, bank, and keyring if bagID == 0 or bagID == -1 or bagID == -2 then diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index be199ce..d3ef1d5 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -422,6 +422,12 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) local cat = itemData.class or "Miscellaneous" local itemName = itemData.name or "" + -- Force Quest category if it's a quest item (tooltip scan) + -- For other characters, we rely on the saved class, but for current we can be more accurate + if not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID) then + cat = "Quest" + end + -- Detect Hearthstone if string.find(itemName, "Hearthstone") then table.insert(specialItems.Hearthstone, {bagID = bagID, slotID = slotID, itemData = itemData}) @@ -509,7 +515,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) local blockHeight = 20 + (blockRows * (buttonSize + spacing)) + 5 -- 20 header, 5 padding -- Check if it fits in current row - if currentX > 0 and currentX + blockWidth > totalWidth + 5 then + if currentX > 0 and currentX + blockWidth + 20 > totalWidth + 5 then currentX = 0 currentY = currentY + rowMaxHeight rowMaxHeight = 0 @@ -553,7 +559,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) end if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end - currentX = currentX + blockWidth + currentX = currentX + blockWidth + 20 end end @@ -605,7 +611,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) local blockHeight = 20 + (blockRows * (buttonSize + spacing)) -- Check if it fits in current row (Inline block for bottom sections too) - if col > 0 and (col * (buttonSize + spacing)) + blockWidth > totalWidth + 5 then + if col > 0 and (col * (buttonSize + spacing)) + blockWidth + 20 > totalWidth + 5 then col = 0 y = y - sectionMaxHeight - 5 sectionMaxHeight = 0 @@ -642,10 +648,10 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) end if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end - col = col + blockCols + col = col + blockCols + math.ceil(20 / (buttonSize + spacing)) -- If we wrapped exactly at the end of a block - if col >= perRow then + if (col * (buttonSize + spacing)) >= totalWidth then col = 0 y = y - sectionMaxHeight - 5 sectionMaxHeight = 0 diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index 2754141..d638293 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -297,6 +297,11 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) local cat = itemData.class or "Miscellaneous" + -- Force Quest category if it's a quest item (tooltip scan) + if not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID) then + cat = "Quest" + end + -- Split Equipment into Weapon and Armor if itemData.equipSlot and itemData.equipSlot ~= "" then if itemData.class == "Weapon" or itemData.class == "Armor" then @@ -375,7 +380,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) local blockHeight = 20 + (blockRows * (buttonSize + spacing)) + 5 -- Check if it fits in current row - if currentX > 0 and currentX + blockWidth > totalWidth + 5 then + if currentX > 0 and currentX + blockWidth + 20 > totalWidth + 5 then currentX = 0 currentY = currentY + rowMaxHeight rowMaxHeight = 0 @@ -419,7 +424,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) end if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end - currentX = currentX + blockWidth + currentX = currentX + blockWidth + 20 end end @@ -471,7 +476,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) local blockHeight = 20 + (blockRows * (buttonSize + spacing)) -- Check if it fits in current row (Inline block for bottom sections too) - if col > 0 and (col * (buttonSize + spacing)) + blockWidth > totalWidth + 5 then + if col > 0 and (col * (buttonSize + spacing)) + blockWidth + 20 > totalWidth + 5 then col = 0 y = y - sectionMaxHeight - 5 sectionMaxHeight = 0 @@ -508,10 +513,10 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) end if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end - col = col + blockCols + col = col + blockCols + math.ceil(20 / (buttonSize + spacing)) -- If we wrapped exactly at the end of a block - if col >= perRow then + if (col * (buttonSize + spacing)) >= totalWidth then col = 0 y = y - sectionMaxHeight - 5 sectionMaxHeight = 0 From 1967f023e7ed014077fe3f88904862795297db4f Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 21:38:45 +0400 Subject: [PATCH 06/16] fix: mount section --- Sorting/SortEngine.lua | 3 ++- UI/BagFrame.lua | 20 ++++++++++---------- UI/BankFrame.lua | 28 ++++++++++++++-------------- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/Sorting/SortEngine.lua b/Sorting/SortEngine.lua index 376bc01..ce881e4 100644 --- a/Sorting/SortEngine.lua +++ b/Sorting/SortEngine.lua @@ -145,7 +145,7 @@ local function GetTexturePattern(textureName) end -- Check if an item is a mount by texture path -local function IsMount(itemTexture) +function SortEngine.IsMount(itemTexture) if not itemTexture then return false end local textureLower = string.lower(itemTexture) @@ -158,6 +158,7 @@ local function IsMount(itemTexture) return false end +local IsMount = SortEngine.IsMount -- Determine subclass order for grouping related items local function GetSubclassOrder(subclass, itemName) diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index d3ef1d5..f8d752b 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -445,14 +445,6 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) string.find(itemName, "Skinning Knife") or itemName == "Blood Scythe" then table.insert(specialItems.Tools, {bagID = bagID, slotID = slotID, itemData = itemData}) - -- Split Equipment into Weapon and Armor - elseif itemData.equipSlot and itemData.equipSlot ~= "" then - if itemData.class == "Weapon" or itemData.class == "Armor" then - cat = itemData.class - else - cat = "Armor" -- Accessories etc usually fall here if equippable - end - table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) -- Detect Food and Drink elseif itemData.class == "Consumable" then local sub = itemData.subclass or "" @@ -464,6 +456,14 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) end end table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) + -- Split Equipment into Weapon and Armor + elseif itemData.equipSlot and itemData.equipSlot ~= "" then + if itemData.class == "Weapon" or itemData.class == "Armor" then + cat = itemData.class + else + cat = "Armor" -- Accessories etc usually fall here if equippable + end + table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) else if not categories[cat] then cat = "Miscellaneous" end table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) @@ -611,7 +611,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) local blockHeight = 20 + (blockRows * (buttonSize + spacing)) -- Check if it fits in current row (Inline block for bottom sections too) - if col > 0 and (col * (buttonSize + spacing)) + blockWidth + 20 > totalWidth + 5 then + if col > 0 and (col * (buttonSize + spacing)) + blockWidth > totalWidth + 5 then col = 0 y = y - sectionMaxHeight - 5 sectionMaxHeight = 0 @@ -648,7 +648,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) end if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end - col = col + blockCols + math.ceil(20 / (buttonSize + spacing)) + col = col + blockCols + 1 -- Add 1 slot worth of spacing (around 40px) instead of 20px gap logic -- If we wrapped exactly at the end of a block if (col * (buttonSize + spacing)) >= totalWidth then diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index d638293..7be6052 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -295,6 +295,18 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) return end + -- Detect Food and Drink + if itemData.class == "Consumable" then + local sub = itemData.subclass or "" + if sub == "Food & Drink" or string.find(sub, "Food") or string.find(sub, "Drink") then + if string.find(sub, "Drink") then + cat = "Drink" + else + cat = "Food" + end + end + end + local cat = itemData.class or "Miscellaneous" -- Force Quest category if it's a quest item (tooltip scan) @@ -311,18 +323,6 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) end end - -- Detect Food and Drink - if itemData.class == "Consumable" then - local sub = itemData.subclass or "" - if sub == "Food & Drink" or string.find(sub, "Food") or string.find(sub, "Drink") then - if string.find(sub, "Drink") then - cat = "Drink" - else - cat = "Food" - end - end - end - if not categories[cat] then cat = "Miscellaneous" end table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) end @@ -476,7 +476,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) local blockHeight = 20 + (blockRows * (buttonSize + spacing)) -- Check if it fits in current row (Inline block for bottom sections too) - if col > 0 and (col * (buttonSize + spacing)) + blockWidth + 20 > totalWidth + 5 then + if col > 0 and (col * (buttonSize + spacing)) + blockWidth > totalWidth + 5 then col = 0 y = y - sectionMaxHeight - 5 sectionMaxHeight = 0 @@ -513,7 +513,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) end if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end - col = col + blockCols + math.ceil(20 / (buttonSize + spacing)) + col = col + blockCols + 1 -- Add 1 slot worth of spacing (around 40px) instead of 20px gap logic -- If we wrapped exactly at the end of a block if (col * (buttonSize + spacing)) >= totalWidth then From b89e9e1bee84d9a7701b2f2068dca4271c128c8d Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 21:48:15 +0400 Subject: [PATCH 07/16] fix: category titles --- UI/BagFrame.lua | 22 +++++++++++----------- UI/BankFrame.lua | 22 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index f8d752b..0f6afc9 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -373,7 +373,7 @@ function BagFrame:GetSectionHeader(index) header = CreateFrame("Frame", name, getglobal("Guda_BagFrame_ItemContainer")) header:SetHeight(20) local text = header:CreateFontString(nil, "OVERLAY", "GameFontNormal") - text:SetPoint("LEFT", header, "LEFT", 5, 0) + text:SetPoint("LEFT", header, "LEFT", 0, 0) header.text = text end header.inUse = true @@ -559,7 +559,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) end if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end - currentX = currentX + blockWidth + 20 + currentX = currentX + blockWidth + 20 -- 20px gap between categories end end @@ -587,7 +587,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) if hasAnyBottom then y = y - 10 - local col = 0 + local currentBottomX = 0 local sectionMaxHeight = 0 for _, sec in ipairs(bottomSections) do @@ -611,8 +611,8 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) local blockHeight = 20 + (blockRows * (buttonSize + spacing)) -- Check if it fits in current row (Inline block for bottom sections too) - if col > 0 and (col * (buttonSize + spacing)) + blockWidth > totalWidth + 5 then - col = 0 + if currentBottomX > 0 and currentBottomX + blockWidth + 20 > totalWidth + 5 then + currentBottomX = 0 y = y - sectionMaxHeight - 5 sectionMaxHeight = 0 end @@ -620,7 +620,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) -- Add Header local header = self:GetSectionHeader(headerIdx) headerIdx = headerIdx + 1 - header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + (col * (buttonSize + spacing)), y) + header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX, y) header:SetWidth(blockWidth) header.text:SetText(sec.name) header:Show() @@ -635,7 +635,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) button:SetWidth(buttonSize) button:SetHeight(buttonSize) button:ClearAllPoints() - button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + ((col + sCol) * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX + (sCol * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) button:Show() Guda_ItemButton_SetItem(button, item.bagID, item.slotID, item.itemData, false, isOtherChar and charName or nil, self:PassesSearchFilter(item.itemData), isOtherChar) button.inUse = true @@ -648,18 +648,18 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) end if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end - col = col + blockCols + 1 -- Add 1 slot worth of spacing (around 40px) instead of 20px gap logic + currentBottomX = currentBottomX + blockWidth + 20 -- Match the 20px gap used in top categories -- If we wrapped exactly at the end of a block - if (col * (buttonSize + spacing)) >= totalWidth then - col = 0 + if currentBottomX >= totalWidth then + currentBottomX = 0 y = y - sectionMaxHeight - 5 sectionMaxHeight = 0 end end end - if col > 0 then + if currentBottomX > 0 then y = y - sectionMaxHeight end end diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index 7be6052..4e34151 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -225,7 +225,7 @@ function BankFrame:GetSectionHeader(index) header = CreateFrame("Frame", name, getglobal("Guda_BankFrame_ItemContainer")) header:SetHeight(20) local text = header:CreateFontString(nil, "OVERLAY", "GameFontNormal") - text:SetPoint("LEFT", header, "LEFT", 5, 0) + text:SetPoint("LEFT", header, "LEFT", 0, 0) header.text = text end header.inUse = true @@ -424,7 +424,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) end if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end - currentX = currentX + blockWidth + 20 + currentX = currentX + blockWidth + 20 -- 20px gap between categories end end @@ -452,7 +452,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) if hasAnyBottom then y = y - 10 - local col = 0 + local currentBottomX = 0 local sectionMaxHeight = 0 for _, sec in ipairs(bottomSections) do @@ -476,8 +476,8 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) local blockHeight = 20 + (blockRows * (buttonSize + spacing)) -- Check if it fits in current row (Inline block for bottom sections too) - if col > 0 and (col * (buttonSize + spacing)) + blockWidth > totalWidth + 5 then - col = 0 + if currentBottomX > 0 and currentBottomX + blockWidth + 20 > totalWidth + 5 then + currentBottomX = 0 y = y - sectionMaxHeight - 5 sectionMaxHeight = 0 end @@ -485,7 +485,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) -- Add Header local header = self:GetSectionHeader(headerIdx) headerIdx = headerIdx + 1 - header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + (col * (buttonSize + spacing)), y) + header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX, y) header:SetWidth(blockWidth) header.text:SetText(sec.name) header:Show() @@ -500,7 +500,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) button:SetWidth(buttonSize) button:SetHeight(buttonSize) button:ClearAllPoints() - button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + ((col + sCol) * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX + (sCol * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) button:Show() Guda_ItemButton_SetItem(button, item.bagID, item.slotID, item.itemData, true, isOtherChar and charName or nil, self:PassesSearchFilter(item.itemData), true) button.inUse = true @@ -513,18 +513,18 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) end if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end - col = col + blockCols + 1 -- Add 1 slot worth of spacing (around 40px) instead of 20px gap logic + currentBottomX = currentBottomX + blockWidth + 20 -- Match the 20px gap used in top categories -- If we wrapped exactly at the end of a block - if (col * (buttonSize + spacing)) >= totalWidth then - col = 0 + if currentBottomX >= totalWidth then + currentBottomX = 0 y = y - sectionMaxHeight - 5 sectionMaxHeight = 0 end end end - if col > 0 then + if currentBottomX > 0 then y = y - sectionMaxHeight end end From 4c78ca8a6e3583932b170f714ee2d51652898db5 Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 22:00:26 +0400 Subject: [PATCH 08/16] fix: unequip item --- UI/BagFrame.lua | 73 +++++++++++++++++++++++++++++++++++++++------- UI/BankFrame.lua | 76 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 126 insertions(+), 23 deletions(-) diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index 0f6afc9..5850d3c 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -474,6 +474,27 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) end end + -- Calculate total empty slots and find first available one for drop target + local totalFreeSlots = 0 + local firstFreeBag, firstFreeSlot + for _, bagID in ipairs(addon.Constants.BAGS) do + if not hiddenBags[bagID] then + local bag = bagData[bagID] + if bag then + totalFreeSlots = totalFreeSlots + (bag.freeSlots or 0) + if not firstFreeBag and (bag.freeSlots or 0) > 0 then + for s = 1, (bag.numSlots or 0) do + if not bag.slots or not bag.slots[s] then + firstFreeBag = bagID + firstFreeSlot = s + break + end + end + end + end + end + end + -- Handle Keyring if visible if showKeyring and not hiddenBags[-2] then local bag = bagData[-2] @@ -566,11 +587,12 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) -- Update Y for bottom sections local y = currentY + rowMaxHeight - -- Special sections at bottom (Hearthstone, Mount, Tools) + -- Special sections at bottom (Hearthstone, Mount, Tools, Empty) local bottomSections = { { name = "Home", items = specialItems.Hearthstone }, { name = "Mounts", items = specialItems.Mount }, - { name = "Tools", items = specialItems.Tools } + { name = "Tools", items = specialItems.Tools }, + { name = "Empty", items = {} } } local x = startX @@ -593,6 +615,9 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) for _, sec in ipairs(bottomSections) do local items = sec.items local numItems = table.getn(items) + if sec.name == "Empty" then + numItems = (totalFreeSlots > 0) and 1 or 0 + end if numItems > 0 then -- Sort Tools (Hearthstone/Mounts don't usually need it but good for consistency) if sec.name == "Tools" then @@ -628,22 +653,48 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) local itemY = y - 20 local sCol = 0 local sRow = 0 - for _, item in ipairs(items) do - local bagParent = self:GetBagParent(item.bagID) + + if sec.name == "Empty" then + -- Display one empty slot button with count + local bagID = firstFreeBag or 0 + local slotID = firstFreeSlot or 1 + local bagParent = self:GetBagParent(bagID) local button = Guda_GetItemButton(bagParent) button:SetParent(bagParent) button:SetWidth(buttonSize) button:SetHeight(buttonSize) button:ClearAllPoints() - button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX + (sCol * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX, itemY) button:Show() - Guda_ItemButton_SetItem(button, item.bagID, item.slotID, item.itemData, false, isOtherChar and charName or nil, self:PassesSearchFilter(item.itemData), isOtherChar) - button.inUse = true - sCol = sCol + 1 - if sCol >= blockCols then - sCol = 0 - sRow = sRow + 1 + local emptyItemData = { + texture = "Interface\\PaperDoll\\UI-PaperDoll-Slot-Bag", + count = totalFreeSlots, + name = "Empty Slots" + } + -- Use fake data but real IDs for drop handling + Guda_ItemButton_SetItem(button, bagID, slotID, emptyItemData, false, isOtherChar and charName or nil, true, true) + -- Ensure it's not actually read-only for drop behavior (it should still receive clicks/drops) + button.isReadOnly = false + button.inUse = true + else + for _, item in ipairs(items) do + local bagParent = self:GetBagParent(item.bagID) + local button = Guda_GetItemButton(bagParent) + button:SetParent(bagParent) + button:SetWidth(buttonSize) + button:SetHeight(buttonSize) + button:ClearAllPoints() + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX + (sCol * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) + button:Show() + Guda_ItemButton_SetItem(button, item.bagID, item.slotID, item.itemData, false, isOtherChar and charName or nil, self:PassesSearchFilter(item.itemData), isOtherChar) + button.inUse = true + + sCol = sCol + 1 + if sCol >= blockCols then + sCol = 0 + sRow = sRow + 1 + end end end diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index 4e34151..1108a08 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -262,7 +262,8 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) local specialItems = { Hearthstone = {}, Mount = {}, - Tools = {} + Tools = {}, + Empty = {} } -- Helper to assign category @@ -351,6 +352,27 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) end end + -- Calculate total empty slots and find first available one for drop target + local totalFreeSlots = 0 + local firstFreeBag, firstFreeSlot + for _, bagID in ipairs(addon.Constants.BANK_BAGS) do + if not hiddenBankBags[bagID] then + local bag = bankData[bagID] + if bag then + totalFreeSlots = totalFreeSlots + (bag.freeSlots or 0) + if not firstFreeBag and (bag.freeSlots or 0) > 0 then + for s = 1, (bag.numSlots or 0) do + if not bag.slots or not bag.slots[s] then + firstFreeBag = bagID + firstFreeSlot = s + break + end + end + end + end + end + end + -- Layout local startX, startY = 5, -10 local currentX, currentY = 0, 0 @@ -431,11 +453,12 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) -- Update Y for bottom sections local y = currentY + rowMaxHeight - -- Special sections at bottom (Hearthstone, Mount, Tools) + -- Special sections at bottom (Hearthstone, Mount, Tools, Empty) local bottomSections = { { name = "Home", items = specialItems.Hearthstone }, { name = "Mounts", items = specialItems.Mount }, - { name = "Tools", items = specialItems.Tools } + { name = "Tools", items = specialItems.Tools }, + { name = "Empty", items = {} } } local x = startX @@ -458,6 +481,9 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) for _, sec in ipairs(bottomSections) do local items = sec.items local numItems = table.getn(items) + if sec.name == "Empty" then + numItems = (totalFreeSlots > 0) and 1 or 0 + end if numItems > 0 then -- Sort Tools (Hearthstone/Mounts don't usually need it but good for consistency) if sec.name == "Tools" then @@ -493,22 +519,48 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) local itemY = y - 20 local sCol = 0 local sRow = 0 - for _, item in ipairs(items) do - local bagParent = self:GetBagParent(item.bagID) + + if sec.name == "Empty" then + -- Display one empty slot button with count + local bagID = firstFreeBag or -1 + local slotID = firstFreeSlot or 1 + local bagParent = self:GetBagParent(bagID) local button = Guda_GetItemButton(bagParent) button:SetParent(bagParent) button:SetWidth(buttonSize) button:SetHeight(buttonSize) button:ClearAllPoints() - button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX + (sCol * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX, itemY) button:Show() - Guda_ItemButton_SetItem(button, item.bagID, item.slotID, item.itemData, true, isOtherChar and charName or nil, self:PassesSearchFilter(item.itemData), true) - button.inUse = true - sCol = sCol + 1 - if sCol >= blockCols then - sCol = 0 - sRow = sRow + 1 + local emptyItemData = { + texture = "Interface\\PaperDoll\\UI-PaperDoll-Slot-Bag", + count = totalFreeSlots, + name = "Empty Slots" + } + -- Use fake data but real IDs for drop handling + Guda_ItemButton_SetItem(button, bagID, slotID, emptyItemData, true, isOtherChar and charName or nil, true, true) + -- Ensure it's not actually read-only for drop behavior + button.isReadOnly = false + button.inUse = true + else + for _, item in ipairs(items) do + local bagParent = self:GetBagParent(item.bagID) + local button = Guda_GetItemButton(bagParent) + button:SetParent(bagParent) + button:SetWidth(buttonSize) + button:SetHeight(buttonSize) + button:ClearAllPoints() + button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX + (sCol * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) + button:Show() + Guda_ItemButton_SetItem(button, item.bagID, item.slotID, item.itemData, true, isOtherChar and charName or nil, self:PassesSearchFilter(item.itemData), true) + button.inUse = true + + sCol = sCol + 1 + if sCol >= blockCols then + sCol = 0 + sRow = sRow + 1 + end end end From fa767f8ca1ca89875a8355f3a88190ad155e3e83 Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 22:12:24 +0400 Subject: [PATCH 09/16] fix: bankview for category types --- UI/BagFrame.lua | 28 ++++++++++++++------------- UI/BankFrame.lua | 50 +++++++++++++++++------------------------------- 2 files changed, 33 insertions(+), 45 deletions(-) diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index 5850d3c..4b4cbf8 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -419,22 +419,14 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) if bag and bag.slots then for slotID, itemData in pairs(bag.slots) do if itemData then - local cat = itemData.class or "Miscellaneous" + local cat = "Miscellaneous" local itemName = itemData.name or "" - -- Force Quest category if it's a quest item (tooltip scan) - -- For other characters, we rely on the saved class, but for current we can be more accurate - if not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID) then - cat = "Quest" - end - - -- Detect Hearthstone + -- Priority 1: Special items (Hearthstone, Mounts, Tools) if string.find(itemName, "Hearthstone") then table.insert(specialItems.Hearthstone, {bagID = bagID, slotID = slotID, itemData = itemData}) - -- Detect Mounts elseif addon.Modules.SortEngine and addon.Modules.SortEngine.IsMount and addon.Modules.SortEngine.IsMount(itemData.texture) then table.insert(specialItems.Mount, {bagID = bagID, slotID = slotID, itemData = itemData}) - -- Detect Tools category elseif string.find(itemName, "Runed .* Rod") or string.find(itemName, "Fishing Pole") or string.find(itemName, "Mining Pick") or @@ -445,8 +437,14 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) string.find(itemName, "Skinning Knife") or itemName == "Blood Scythe" then table.insert(specialItems.Tools, {bagID = bagID, slotID = slotID, itemData = itemData}) - -- Detect Food and Drink + + -- Priority 2: Quest Items + elseif (not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)) or itemData.class == "Quest" then + table.insert(categories["Quest"], {bagID = bagID, slotID = slotID, itemData = itemData}) + + -- Priority 3: Food and Drink elseif itemData.class == "Consumable" then + cat = "Consumable" local sub = itemData.subclass or "" if sub == "Food & Drink" or string.find(sub, "Food") or string.find(sub, "Drink") then if string.find(sub, "Drink") then @@ -456,15 +454,19 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) end end table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) - -- Split Equipment into Weapon and Armor + + -- Priority 4: Equipment (Weapon and Armor) elseif itemData.equipSlot and itemData.equipSlot ~= "" then if itemData.class == "Weapon" or itemData.class == "Armor" then cat = itemData.class else - cat = "Armor" -- Accessories etc usually fall here if equippable + cat = "Armor" end table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) + + -- Priority 5: Other Categories else + cat = itemData.class or "Miscellaneous" if not categories[cat] then cat = "Miscellaneous" end table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) end diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index 1108a08..1b955c8 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -269,21 +269,16 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) -- Helper to assign category local function CategorizeItem(itemData, bagID, slotID) local itemName = itemData.name or "" + local cat = "Miscellaneous" - -- Detect Hearthstone + -- Priority 1: Special items (Hearthstone, Mounts, Tools) if string.find(itemName, "Hearthstone") then table.insert(specialItems.Hearthstone, {bagID = bagID, slotID = slotID, itemData = itemData}) return - end - - -- Detect Mounts - if addon.Modules.SortEngine and addon.Modules.SortEngine.IsMount and addon.Modules.SortEngine.IsMount(itemData.texture) then + elseif addon.Modules.SortEngine and addon.Modules.SortEngine.IsMount and addon.Modules.SortEngine.IsMount(itemData.texture) then table.insert(specialItems.Mount, {bagID = bagID, slotID = slotID, itemData = itemData}) return - end - - -- Detect Tools category - if string.find(itemName, "Runed .* Rod") or + elseif string.find(itemName, "Runed .* Rod") or string.find(itemName, "Fishing Pole") or string.find(itemName, "Mining Pick") or string.find(itemName, "Blacksmith Hammer") or @@ -296,8 +291,13 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) return end - -- Detect Food and Drink - if itemData.class == "Consumable" then + -- Priority 2: Quest Items + if (not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)) or itemData.class == "Quest" then + cat = "Quest" + + -- Priority 3: Food and Drink + elseif itemData.class == "Consumable" then + cat = "Consumable" local sub = itemData.subclass or "" if sub == "Food & Drink" or string.find(sub, "Food") or string.find(sub, "Drink") then if string.find(sub, "Drink") then @@ -306,39 +306,25 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) cat = "Food" end end - end - local cat = itemData.class or "Miscellaneous" - - -- Force Quest category if it's a quest item (tooltip scan) - if not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID) then - cat = "Quest" - end - - -- Split Equipment into Weapon and Armor - if itemData.equipSlot and itemData.equipSlot ~= "" then + -- Priority 4: Equipment (Weapon and Armor) + elseif itemData.equipSlot and itemData.equipSlot ~= "" then if itemData.class == "Weapon" or itemData.class == "Armor" then cat = itemData.class else cat = "Armor" end + + -- Priority 5: Other Categories + else + cat = itemData.class or "Miscellaneous" end if not categories[cat] then cat = "Miscellaneous" end table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData}) end - -- Bank main slots (bagID -1) - local bankMain = bankData[-1] - if bankMain and bankMain.slots then - for slotID, itemData in pairs(bankMain.slots) do - if itemData then - CategorizeItem(itemData, -1, slotID) - end - end - end - - -- Bank bags + -- Bank slots for _, bagID in ipairs(addon.Constants.BANK_BAGS) do if not hiddenBankBags[bagID] then local bag = bankData[bagID] From 64341eec3765e3c5963317bfe37d1aedde7caa1f Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 22:17:02 +0400 Subject: [PATCH 10/16] fix: bank view alt+click --- UI/BankFrame.lua | 4 ++-- UI/ItemButton.lua | 14 +++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index 1b955c8..f29dd8b 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -421,7 +421,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) button:Show() local matchesFilter = self:PassesSearchFilter(itemData) - Guda_ItemButton_SetItem(button, bagID, slot, itemData, true, isOtherChar and charName or nil, matchesFilter, true) + Guda_ItemButton_SetItem(button, bagID, slot, itemData, true, isOtherChar and charName or nil, matchesFilter, isOtherChar or isReadOnlyMode) button.inUse = true col = col + 1 @@ -539,7 +539,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) button:ClearAllPoints() button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX + (sCol * (buttonSize + spacing)), itemY - (sRow * (buttonSize + spacing))) button:Show() - Guda_ItemButton_SetItem(button, item.bagID, item.slotID, item.itemData, true, isOtherChar and charName or nil, self:PassesSearchFilter(item.itemData), true) + Guda_ItemButton_SetItem(button, item.bagID, item.slotID, item.itemData, true, isOtherChar and charName or nil, self:PassesSearchFilter(item.itemData), isOtherChar or isReadOnlyMode) button.inUse = true sCol = sCol + 1 diff --git a/UI/ItemButton.lua b/UI/ItemButton.lua index 51dc495..fcbd304 100644 --- a/UI/ItemButton.lua +++ b/UI/ItemButton.lua @@ -10,11 +10,19 @@ scanTooltip:SetOwner(WorldFrame, "ANCHOR_NONE") -- Check if an item is a quest item by scanning its tooltip -- Check if an item is a quest item by scanning its tooltip and determine type -local function IsQuestItem(bagID, slotID) +local function IsQuestItem(bagID, slotID, isBank) if not bagID or not slotID then return false end scanTooltip:ClearLines() - scanTooltip:SetBagItem(bagID, slotID) + if isBank and bagID == -1 then + if scanTooltip.SetInventoryItem then + scanTooltip:SetInventoryItem("player", 39 + slotID) + else + scanTooltip:SetBagItem(bagID, slotID) + end + else + scanTooltip:SetBagItem(bagID, slotID) + end local isQuestItem = false local isQuestStarter = false @@ -338,7 +346,7 @@ function Guda_ItemButton_OnLoad(self) if link and addon and addon.Modules and addon.Modules.Utils then local itemID = addon.Modules.Utils:ExtractItemID(link) if itemID then - local isQuest = IsQuestItem(this.bagID, this.slotID) + local isQuest = IsQuestItem(this.bagID, this.slotID, this.isBank) if isQuest and addon.Modules.QuestItemBar and addon.Modules.QuestItemBar.PinItem then addon.Modules.QuestItemBar:PinItem(itemID) return From d6cbfb704d2496108e08d0b6b00393da2e3a4f46 Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 22:24:38 +0400 Subject: [PATCH 11/16] fix: class items --- Sorting/SortEngine.lua | 17 +++++++++-------- UI/BagFrame.lua | 13 ++++++++++--- UI/BankFrame.lua | 15 ++++++++++++--- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/Sorting/SortEngine.lua b/Sorting/SortEngine.lua index ce881e4..516881f 100644 --- a/Sorting/SortEngine.lua +++ b/Sorting/SortEngine.lua @@ -57,14 +57,15 @@ local CATEGORY_ORDER = { ["Weapon"] = 4, -- Non-equippable weapons ["Armor"] = 5, -- Non-equippable armor ["Tools"] = 6, - ["Quiver"] = 7, - ["Reagent"] = 8, - ["Trade Goods"] = 9, - ["Recipe"] = 10, - ["Container"] = 11, - ["Quest"] = 12, - ["Key"] = 13, - ["Miscellaneous"] = 14, + ["Class Items"] = 7, + ["Quiver"] = 8, + ["Reagent"] = 9, + ["Trade Goods"] = 10, + ["Recipe"] = 11, + ["Container"] = 12, + ["Quest"] = 13, + ["Key"] = 14, + ["Miscellaneous"] = 15, } -- Subclass ordering for grouping related items diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index 4b4cbf8..25ba137 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -403,7 +403,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) -- Group items by category local categories = {} local categoryList = { - "Weapon", "Armor", "Consumable", "Food", "Drink", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Keyring", "Miscellaneous" + "Weapon", "Armor", "Consumable", "Food", "Drink", "Class Items", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Keyring", "Miscellaneous" } for _, cat in ipairs(categoryList) do categories[cat] = {} end @@ -438,11 +438,18 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) itemName == "Blood Scythe" then table.insert(specialItems.Tools, {bagID = bagID, slotID = slotID, itemData = itemData}) - -- Priority 2: Quest Items + -- Priority 2: Class Items (Soul Shards, Arrows, Bullets) + elseif addon.Modules.Utils:IsSoulShard(itemData.link) or + itemData.class == "Projectile" or + itemData.subclass == "Arrow" or + itemData.subclass == "Bullet" then + table.insert(categories["Class Items"], {bagID = bagID, slotID = slotID, itemData = itemData}) + + -- Priority 3: Quest Items elseif (not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)) or itemData.class == "Quest" then table.insert(categories["Quest"], {bagID = bagID, slotID = slotID, itemData = itemData}) - -- Priority 3: Food and Drink + -- Priority 4: Food and Drink elseif itemData.class == "Consumable" then cat = "Consumable" local sub = itemData.subclass or "" diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index f29dd8b..bd17e7d 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -255,7 +255,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) -- Group items by category local categories = {} local categoryList = { - "Weapon", "Armor", "Consumable", "Food", "Drink", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Miscellaneous" + "Weapon", "Armor", "Consumable", "Food", "Drink", "Class Items", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Miscellaneous" } for _, cat in ipairs(categoryList) do categories[cat] = {} end @@ -291,11 +291,20 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) return end - -- Priority 2: Quest Items + -- Priority 2: Class Items (Soul Shards, Arrows, Bullets) + if addon.Modules.Utils:IsSoulShard(itemData.link) or + itemData.class == "Projectile" or + itemData.subclass == "Arrow" or + itemData.subclass == "Bullet" then + table.insert(categories["Class Items"], {bagID = bagID, slotID = slotID, itemData = itemData}) + return + end + + -- Priority 3: Quest Items if (not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)) or itemData.class == "Quest" then cat = "Quest" - -- Priority 3: Food and Drink + -- Priority 4: Food and Drink elseif itemData.class == "Consumable" then cat = "Consumable" local sub = itemData.subclass or "" From a11795ff44d89dd12dab81b79c1bbb3e615f0ec8 Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 22:29:03 +0400 Subject: [PATCH 12/16] fix: category order --- Sorting/SortEngine.lua | 4 ++-- UI/BagFrame.lua | 2 +- UI/BankFrame.lua | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sorting/SortEngine.lua b/Sorting/SortEngine.lua index 516881f..2f892b2 100644 --- a/Sorting/SortEngine.lua +++ b/Sorting/SortEngine.lua @@ -57,15 +57,15 @@ local CATEGORY_ORDER = { ["Weapon"] = 4, -- Non-equippable weapons ["Armor"] = 5, -- Non-equippable armor ["Tools"] = 6, - ["Class Items"] = 7, ["Quiver"] = 8, ["Reagent"] = 9, ["Trade Goods"] = 10, ["Recipe"] = 11, ["Container"] = 12, - ["Quest"] = 13, ["Key"] = 14, ["Miscellaneous"] = 15, + ["Quest"] = 16, + ["Class Items"] = 17, } -- Subclass ordering for grouping related items diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index 25ba137..1b29895 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -403,7 +403,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName) -- Group items by category local categories = {} local categoryList = { - "Weapon", "Armor", "Consumable", "Food", "Drink", "Class Items", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Keyring", "Miscellaneous" + "Weapon", "Armor", "Consumable", "Food", "Drink", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Keyring", "Miscellaneous", "Quest", "Class Items" } for _, cat in ipairs(categoryList) do categories[cat] = {} end diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index bd17e7d..b7e6fd8 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -255,7 +255,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName) -- Group items by category local categories = {} local categoryList = { - "Weapon", "Armor", "Consumable", "Food", "Drink", "Class Items", "Quest", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Miscellaneous" + "Weapon", "Armor", "Consumable", "Food", "Drink", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Miscellaneous", "Quest", "Class Items" } for _, cat in ipairs(categoryList) do categories[cat] = {} end From 5bff2eb452bfa790372d1098f3ea1ed066d235c4 Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Tue, 23 Dec 2025 22:34:37 +0400 Subject: [PATCH 13/16] feat: category type --- UI/SettingsPopup.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UI/SettingsPopup.xml b/UI/SettingsPopup.xml index d6c9f6f..c9c5a24 100644 --- a/UI/SettingsPopup.xml +++ b/UI/SettingsPopup.xml @@ -397,10 +397,10 @@