From 97344bc4eb1c3289453915f1ef6cace3b1a2921b Mon Sep 17 00:00:00 2001 From: Cinecom <42314202+Cinecom@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:26:36 +0100 Subject: [PATCH] Update 1.6 - Added more items to the consumables list - UI changes to the tabs - Added an option to show/hide a 'use' - Added an option to show/hide categories - Small UI fixes --- ConsumesManager.toc | 2 +- Itemlist.lua | 5 +- Main.lua | 695 +++++++++++++++++++++++++--------------- images/resize_icon.tga | Bin 4140 -> 0 bytes images/tracker_icon.tga | Bin 0 -> 16428 bytes 5 files changed, 439 insertions(+), 263 deletions(-) delete mode 100644 images/resize_icon.tga create mode 100644 images/tracker_icon.tga diff --git a/ConsumesManager.toc b/ConsumesManager.toc index e8e867c..47f2eb3 100644 --- a/ConsumesManager.toc +++ b/ConsumesManager.toc @@ -2,7 +2,7 @@ ## Title: |cff00ff00Consumes Manager|r |cff808080[Horyoshi]|r ## Notes: Tracks and manages consumables across your inventory, bank and mail through multiple characters. Keeps track of how much consumes you have left. ## Author: Horyoshi -## Version: 1.5 +## Version: 1.6 ## SavedVariables: ConsumesManager_Settings, ConsumesManager_Data Main.lua diff --git a/Itemlist.lua b/Itemlist.lua index d749ddc..b4a106e 100644 --- a/Itemlist.lua +++ b/Itemlist.lua @@ -65,7 +65,9 @@ consumablesCategories = { { id = 13462, name = "Purification Potion", texture = "Interface\\Icons\\INV_Potion_31", description = "Attempts to remove one Curse, one Disease and one Poison from the Imbiber." }, { id = 13444, name = "Major Mana Potion", texture = "Interface\\Icons\\INV_Potion_76", description = "Restores 1350 to 2251 mana." }, { id = 13446, name = "Major Healing Potion", texture = "Interface\\Icons\\INV_Potion_54", description = "Restores 1050 to 1751 health." }, - { id = 7676, name = "Thistle Tea", texture = "Interface\\Icons\\INV_Drink_Milk_05", description = "Instantly restores 100 energy." }, + { id = 7676, name = "Thistle Tea", texture = "Interface\\Icons\\INV_Drink_Milk_05", description = "Instantly restores 100 energy." }, + { id = 20004, name = "Major Troll's Blood Potion", texture = "Interface\\Icons\\inv_potion_80", description = "Regenerate 20 health every 5 sec for 1 hour." }, + { id = 14530, name = "Heavy Runecloth Bandage", texture = "Interface\\Icons\\inv_misc_bandage_12", description = "Heals 2000 damage over 8 sec." }, }, ["Food Buffs"] = { { id = 21023, name = "Dirge's Kickin' Chimaerok Chops", texture = "Interface\\Icons\\INV_Misc_Food_65", description = "Increases Strength by 25 for 1 hour." }, @@ -91,6 +93,7 @@ consumablesCategories = { { id = 20079, name = "Spirit of Zanza", texture = "Interface\\Icons\\INV_Potion_30", description = "Increases the player's Spirit by 50 and Stamina by 50 for 2 hours." }, { id = 20081, name = "Swiftness of Zanza", texture = "Interface\\Icons\\INV_Potion_30", description = "Increases the player's run speed by 20% for 2 hours." }, { id = 8412, name = "Ground Scorpok Assay", texture = "Interface\\Icons\\inv_misc_dust_02", description = "Increases Agility by 25 for 1 hour." }, + { id = 8410, name = "R.O.I.D.S.", texture = "Interface\\Icons\\inv_stone_15", description = "Increases Strenght by 25 for 1 hour." }, }, } diff --git a/Main.lua b/Main.lua index ef9593b..2f453fc 100644 --- a/Main.lua +++ b/Main.lua @@ -1,7 +1,7 @@ -- Metadata --------------------------------------------------------------------------------------------- AddonName = "Consumes Manager" -Version = "1.5" -VState = "beta" +Version = "1.6" +VState = "release" WindowWidth = 350 -- Onload & Click Functionality ------------------------------------------------------------------------- @@ -151,7 +151,6 @@ function ConsumesManager_CreateMainWindow() titleBg:SetHeight(64) titleBg:SetPoint("TOP", ConsumesManager_MainFrame, "TOP", 0, 12) - -- Close Button local closeButton = CreateFrame("Button", nil, ConsumesManager_MainFrame, "UIPanelCloseButton") closeButton:SetWidth(32) @@ -163,7 +162,7 @@ function ConsumesManager_CreateMainWindow() -- Create a custom tooltip frame local tooltipFrame = CreateFrame("Frame", "ConsumesManagerTooltip", UIParent) - tooltipFrame:SetWidth(150) + tooltipFrame:SetWidth(100) tooltipFrame:SetHeight(40) tooltipFrame:SetFrameStrata("TOOLTIP") tooltipFrame:SetBackdrop({ @@ -236,19 +235,19 @@ function ConsumesManager_CreateMainWindow() end -- Manager Tab - local tab1 = CreateTab("ConsumesManager_MainFrameTab1", "Interface\\Icons\\INV_Potion_93", 30, "Consumables Manager", 1) + local tab1 = CreateTab("ConsumesManager_MainFrameTab1", "Interface\\AddOns\\ConsumesManager\\images\\minimap_icon", 30, "Tracker", 1) tab1:SetScript("OnClick", function() ConsumesManager_ShowTab(1) end) - -- Options Tab - local tab2 = CreateTab("ConsumesManager_MainFrameTab2", "Interface\\Icons\\INV_Misc_Gear_01", 80, "Options", 2) + -- Items Tab + local tab2 = CreateTab("ConsumesManager_MainFrameTab2", "Interface\\Icons\\Inv_misc_book_03", 80, "Items", 2) tab2:SetScript("OnClick", function() ConsumesManager_ShowTab(2) end) - -- Characters Tab - local tab3 = CreateTab("ConsumesManager_MainFrameTab3", "Interface\\Icons\\Ability_Rogue_Disguise", 130, "Characters", 3) + -- Options Tab + local tab3 = CreateTab("ConsumesManager_MainFrameTab3", "Interface\\Icons\\INV_Misc_Gear_01", 130, "Settings", 3) tab3:SetScript("OnClick", function() ConsumesManager_ShowTab(3) end) @@ -313,10 +312,11 @@ function ConsumesManager_CreateMainWindow() -- Add Custom Content for Tabs ConsumesManager_CreateManagerContent(tab1Content) - ConsumesManager_CreateOptionsContent(tab2Content) - ConsumesManager_CreateCharactersContent(tab3Content) + ConsumesManager_CreateItemsContent(tab2Content) + ConsumesManager_CreateSettingsContent(tab3Content) end + function ConsumesManager_ShowMainWindow() if not ConsumesManager_MainFrame then ConsumesManager_CreateMainWindow() @@ -335,6 +335,7 @@ function ConsumesManager_ShowMainWindow() ConsumesManager_UpdateManagerContent() end + function ConsumesManager_ShowTab(tabIndex) if not ConsumesManager_MainFrame or not ConsumesManager_MainFrame.tabs then return end for i, tabContent in pairs(ConsumesManager_MainFrame.tabs) do @@ -342,12 +343,12 @@ function ConsumesManager_ShowTab(tabIndex) tabContent:Show() -- Set the tab button to active local tabButton = getglobal("ConsumesManager_MainFrameTab" .. i) - tabButton:SetNormalTexture("Interface\\OptionsFrame\\UI-OptionsFrame-ActiveTab") + tabButton:SetNormalTexture("Interface\\ItemsFrame\\UI-ItemsFrame-ActiveTab") else tabContent:Hide() -- Set the tab button to inactive local tabButton = getglobal("ConsumesManager_MainFrameTab" .. i) - tabButton:SetNormalTexture("Interface\\OptionsFrame\\UI-OptionsFrame-InActiveTab") + tabButton:SetNormalTexture("Interface\\ItemsFrame\\UI-ItemsFrame-InActiveTab") end end end @@ -400,11 +401,11 @@ function ConsumesManager_CreateManagerContent(parentFrame) categoryLabel:SetTextColor(1, 1, 1) categoryLabel:Show() - local categoryInfo = { name = categoryName, label = categoryLabel, options = {} } + local categoryInfo = { name = categoryName, label = categoryLabel, Items = {} } index = index + 1 -- Position for the category label - local numOptionsInCategory = 0 -- Counter for options in this category + local numItemsInCategory = 0 -- Counter for Items in this category -- Sort the consumables by name table.sort(consumables, function(a, b) return a.name < b.name end) @@ -415,24 +416,36 @@ function ConsumesManager_CreateManagerContent(parentFrame) local itemName = consumable.name -- Create a frame that encompasses the button and label - local optionFrame = CreateFrame("Frame", "ConsumesManager_ManagerOptionFrame" .. index, scrollChild) - optionFrame:SetWidth(WindowWidth - 10) - optionFrame:SetHeight(lineHeight) - optionFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index) * lineHeight) - optionFrame:Show() - optionFrame:EnableMouse(true) -- Enable mouse events for OnEnter and OnLeave + local itemFrame = CreateFrame("Frame", "ConsumesManager_ManagerItemFrame" .. index, scrollChild) + itemFrame:SetWidth(WindowWidth - 10) + itemFrame:SetHeight(lineHeight) + itemFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + itemFrame:Show() + itemFrame:EnableMouse(true) -- Enable mouse events for OnEnter and OnLeave - -- Create the 'Use' button inside the optionFrame - local useButton = CreateFrame("Button", "ConsumesManager_UseButton" .. index, optionFrame, "UIPanelButtonTemplate") + -- Create the 'Use' button inside the itemFrame + local useButton = CreateFrame("Button", "ConsumesManager_UseButton" .. index, itemFrame, "UIPanelButtonTemplate") useButton:SetWidth(40) useButton:SetHeight(16) - useButton:SetPoint("LEFT", optionFrame, "LEFT", 0, 0) + useButton:SetPoint("LEFT", itemFrame, "LEFT", 0, 0) useButton:SetText("Use") + -- Initially show or hide the use button based on settings + if ConsumesManager_Settings.showUseButton then + useButton:Show() + else + useButton:Hide() + end + -- Create FontString for label - local label = optionFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - label:SetPoint("LEFT", useButton, "RIGHT", 4, 0) + local label = itemFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + if ConsumesManager_Settings.showUseButton then + label:SetPoint("LEFT", useButton, "RIGHT", 4, 0) + else + label:SetPoint("LEFT", itemFrame, "LEFT", 0, 0) + end label:SetText(itemName) + label:SetJustifyH("LEFT") -- Set up the button OnClick handler useButton:SetScript("OnClick", function() @@ -448,30 +461,30 @@ function ConsumesManager_CreateManagerContent(parentFrame) useButton:Disable() -- Mouseover Tooltip - optionFrame:SetScript("OnEnter", function() + itemFrame:SetScript("OnEnter", function() ConsumesManager_ShowConsumableTooltip(itemID) end) - optionFrame:SetScript("OnLeave", function() + itemFrame:SetScript("OnLeave", function() if ConsumesManager_CustomTooltip then ConsumesManager_CustomTooltip:Hide() end end) - -- Store option info - table.insert(categoryInfo.options, { - frame = optionFrame, + -- Store item info + table.insert(categoryInfo.Items, { + frame = itemFrame, label = label, name = itemName, itemID = itemID, button = useButton }) - index = index + 1 -- Increment index after adding option - numOptionsInCategory = numOptionsInCategory + 1 -- Increment options count + index = index + 1 -- Increment index after adding item + numItemsInCategory = numItemsInCategory + 1 -- Increment Items count end - -- Position the category label above its options - categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - numOptionsInCategory - 1) * lineHeight) + -- Position the category label above its Items + categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - numItemsInCategory - 1) * lineHeight) -- Store category info table.insert(parentFrame.categoryInfo, categoryInfo) @@ -486,7 +499,7 @@ function ConsumesManager_CreateManagerContent(parentFrame) -- Message Label (adjusted to be a child of parentFrame) local messageLabel = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |roptions|cffffffff to get started|r") + messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |rItems|cffffffff to get started|r") messageLabel:SetPoint("CENTER", parentFrame, "CENTER", 0, 0) messageLabel:Hide() -- Initially hidden parentFrame.messageLabel = messageLabel @@ -514,7 +527,8 @@ function ConsumesManager_CreateManagerContent(parentFrame) ConsumesManager_UpdateManagerScrollBar() end -function ConsumesManager_CreateOptionsContent(parentFrame) + +function ConsumesManager_CreateItemsContent(parentFrame) -- Create Search Input local searchBox = CreateFrame("EditBox", "ConsumesManager_SearchBox", parentFrame, "InputBoxTemplate") searchBox:SetWidth(WindowWidth - 50) @@ -548,36 +562,36 @@ function ConsumesManager_CreateOptionsContent(parentFrame) -- Iterate over categories for _, categoryInfo in ipairs(parentFrame.categoryInfo) do local categoryLabel = categoryInfo.label - local anyOptionVisible = false + local anyitemVisible = false - -- First, check if any options in the category match the filter - for _, optionInfo in ipairs(categoryInfo.options) do - local itemNameLower = string.lower(optionInfo.name) + -- First, check if any Items in the category match the filter + for _, itemInfo in ipairs(categoryInfo.Items) do + local itemNameLower = string.lower(itemInfo.name) if filterText == "" or string.find(itemNameLower, filterText, 1, true) then - anyOptionVisible = true + anyitemVisible = true break end end - -- If any options are visible, show the category label - if anyOptionVisible then + -- If any Items are visible, show the category label + if anyitemVisible then categoryLabel:SetPoint("TOPLEFT", parentFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) categoryLabel:Show() index = index + 1 - -- Now, position and show the matching options - for _, optionInfo in ipairs(categoryInfo.options) do - local optionFrame = optionInfo.frame - local itemNameLower = string.lower(optionInfo.name) + -- Now, position and show the matching Items + for _, itemInfo in ipairs(categoryInfo.Items) do + local itemFrame = itemInfo.frame + local itemNameLower = string.lower(itemInfo.name) if filterText == "" or string.find(itemNameLower, filterText, 1, true) then - -- Show the option - optionFrame:SetPoint("TOPLEFT", parentFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) - optionFrame:Show() + -- Show the item + itemFrame:SetPoint("TOPLEFT", parentFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + itemFrame:Show() index = index + 1 else - optionFrame:Hide() + itemFrame:Hide() end end @@ -586,9 +600,9 @@ function ConsumesManager_CreateOptionsContent(parentFrame) else categoryLabel:Hide() - -- Hide all options under this category - for _, optionInfo in ipairs(categoryInfo.options) do - optionInfo.frame:Hide() + -- Hide all Items under this category + for _, itemInfo in ipairs(categoryInfo.Items) do + itemInfo.frame:Hide() end end end @@ -598,7 +612,7 @@ function ConsumesManager_CreateOptionsContent(parentFrame) parentFrame.scrollChild:SetHeight(parentFrame.scrollChild.contentHeight) -- Update the scrollbar - ConsumesManager_UpdateOptionsScrollBar() + ConsumesManager_UpdateItemsScrollBar() end searchBox:SetScript("OnTextChanged", function() @@ -606,7 +620,7 @@ function ConsumesManager_CreateOptionsContent(parentFrame) end) -- Adjust the size of the scroll frame to make room for the search box and add extra spacing - local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_OptionsScrollFrame", parentFrame) + local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_ItemsScrollFrame", parentFrame) scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, -40) -- Start below the search box scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 60) -- Leave space for buttons scrollFrame:EnableMouseWheel(true) @@ -650,11 +664,11 @@ function ConsumesManager_CreateOptionsContent(parentFrame) categoryLabel:SetTextColor(1, 1, 1) categoryLabel:Show() - local categoryInfo = { name = categoryName, label = categoryLabel, options = {} } + local categoryInfo = { name = categoryName, label = categoryLabel, Items = {} } index = index + 1 -- Position for the category label - local numOptionsInCategory = 0 -- Counter for options in this category + local numItemsInCategory = 0 -- Counter for Items in this category -- Sort the consumables by name table.sort(consumables, function(a, b) return a.name < b.name end) @@ -665,17 +679,17 @@ function ConsumesManager_CreateOptionsContent(parentFrame) local itemName = consumable.name -- Create a frame that encompasses the checkbox and label - local optionFrame = CreateFrame("Frame", "ConsumesManager_OptionsFrame" .. index, scrollChild) - optionFrame:SetWidth(WindowWidth - 10) - optionFrame:SetHeight(lineHeight) - optionFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index) * lineHeight) - optionFrame:Show() + local itemFrame = CreateFrame("Frame", "ConsumesManager_ItemsFrame" .. index, scrollChild) + itemFrame:SetWidth(WindowWidth - 10) + itemFrame:SetHeight(lineHeight) + itemFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + itemFrame:Show() - -- Create the checkbox inside the optionFrame - local checkbox = CreateFrame("CheckButton", "ConsumesManager_OptionsCheckbox" .. index, optionFrame) + -- Create the checkbox inside the itemFrame + local checkbox = CreateFrame("CheckButton", "ConsumesManager_ItemsCheckbox" .. index, itemFrame) checkbox:SetWidth(16) checkbox:SetHeight(16) - checkbox:SetPoint("LEFT", optionFrame, "LEFT", 0, 0) + checkbox:SetPoint("LEFT", itemFrame, "LEFT", 0, 0) -- Create Textures for the checkbox checkbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") @@ -684,7 +698,7 @@ function ConsumesManager_CreateOptionsContent(parentFrame) checkbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") -- Create FontString for label - local label = optionFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + local label = itemFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") label:SetPoint("LEFT", checkbox, "RIGHT", 4, 0) label:SetText(itemName) @@ -699,31 +713,31 @@ function ConsumesManager_CreateOptionsContent(parentFrame) end parentFrame.checkboxes[currentItemID] = checkbox - -- Make the optionFrame clickable - optionFrame:EnableMouse(true) - optionFrame:SetScript("OnMouseDown", function() + -- Make the itemFrame clickable + itemFrame:EnableMouse(true) + itemFrame:SetScript("OnMouseDown", function() checkbox:Click() end) -- Mouseover Tooltip - optionFrame:SetScript("OnEnter", function() - ConsumesManager_ShowOptionsTooltip(currentItemID) + itemFrame:SetScript("OnEnter", function() + ConsumesManager_ShowItemsTooltip(currentItemID) end) - optionFrame:SetScript("OnLeave", function() - if ConsumesManager_OptionsTooltip then - ConsumesManager_OptionsTooltip:Hide() + itemFrame:SetScript("OnLeave", function() + if ConsumesManager_ItemsTooltip then + ConsumesManager_ItemsTooltip:Hide() end end) - -- Store option info - table.insert(categoryInfo.options, { frame = optionFrame, name = itemName }) + -- Store item info + table.insert(categoryInfo.Items, { frame = itemFrame, name = itemName }) - index = index + 1 -- Increment index after adding option - numOptionsInCategory = numOptionsInCategory + 1 -- Increment options count + index = index + 1 -- Increment index after adding item + numItemsInCategory = numItemsInCategory + 1 -- Increment Items count end - -- Position the category label above its options - categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - numOptionsInCategory - 1) * lineHeight) + -- Position the category label above its Items + categoryLabel:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, - (index - numItemsInCategory - 1) * lineHeight) -- Store category info table.insert(parentFrame.categoryInfo, categoryInfo) @@ -737,7 +751,7 @@ function ConsumesManager_CreateOptionsContent(parentFrame) scrollChild:SetHeight(scrollChild.contentHeight) -- Scroll Bar - local scrollBar = CreateFrame("Slider", "ConsumesManager_OptionsScrollBar", parentFrame) + local scrollBar = CreateFrame("Slider", "ConsumesManager_ItemsScrollBar", parentFrame) -- Corrected Y-offsets to prevent overlapping scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -40) -- Start below the search box scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) -- End above the buttons @@ -757,7 +771,7 @@ function ConsumesManager_CreateOptionsContent(parentFrame) parentFrame.scrollBar = scrollBar -- Update the scrollbar - ConsumesManager_UpdateOptionsScrollBar() + ConsumesManager_UpdateItemsScrollBar() -- Create Select All Button local selectAllButton = CreateFrame("Button", "ConsumesManager_SelectAllButton", parentFrame, "UIPanelButtonTemplate") @@ -788,21 +802,20 @@ function ConsumesManager_CreateOptionsContent(parentFrame) end) end -function ConsumesManager_CreateCharactersContent(parentFrame) + +function ConsumesManager_CreateSettingsContent(parentFrame) -- Scroll Frame - local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_CharactersScrollFrame", parentFrame) + local scrollFrame = CreateFrame("ScrollFrame", "ConsumesManager_SettingsScrollFrame", parentFrame) scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", 0, 0) scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -20, 0) scrollFrame:EnableMouseWheel(true) scrollFrame:SetScript("OnMouseWheel", function() local delta = arg1 - local current = scrollFrame:GetVerticalScroll() - local maxScroll = scrollFrame.maxScroll or 0 + local current = this:GetVerticalScroll() + local maxScroll = this.maxScroll or 0 local newScroll = math.max(0, math.min(current - (delta * 20), maxScroll)) - scrollFrame:SetVerticalScroll(newScroll) - if parentFrame.scrollBar then - parentFrame.scrollBar:SetValue(newScroll) - end + this:SetVerticalScroll(newScroll) + parentFrame.scrollBar:SetValue(newScroll) end) -- Scroll Child Frame @@ -810,10 +823,13 @@ function ConsumesManager_CreateCharactersContent(parentFrame) scrollChild:SetWidth(WindowWidth - 10) scrollChild:SetHeight(1) scrollFrame:SetScrollChild(scrollChild) + parentFrame.scrollChild = scrollChild + parentFrame.scrollFrame = scrollFrame -- Initialize variables parentFrame.checkboxes = {} local index = 0 -- Position index + local lineHeight = 20 -- Increased to accommodate spacing -- Ensure settings table for characters exists ConsumesManager_Settings["Characters"] = ConsumesManager_Settings["Characters"] or {} @@ -850,11 +866,11 @@ function ConsumesManager_CreateCharactersContent(parentFrame) -- Create FontString for title local title = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") title:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, 0) -- Offset from the top - title:SetText("Select Characters to Track") + title:SetText("Select Characters To Track") title:SetTextColor(1, 1, 1) -- Offset index to start below the title - local startYOffset = 20 + local startYOffset = -20 -- For each character for _, characterName in ipairs(characterList) do @@ -864,16 +880,16 @@ function ConsumesManager_CreateCharactersContent(parentFrame) local currentCharacterName = characterName -- Create a frame that encompasses the checkbox and label - local optionFrame = CreateFrame("Frame", "ConsumesManager_CharacterFrame" .. index, scrollChild) - optionFrame:SetWidth(WindowWidth - 10) - optionFrame:SetHeight(18) - optionFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -(startYOffset + (index - 1) * 20)) + local itemFrame = CreateFrame("Frame", "ConsumesManager_CharacterFrame" .. index, scrollChild) + itemFrame:SetWidth(WindowWidth - 10) + itemFrame:SetHeight(18) + itemFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, startYOffset - (index - 1) * lineHeight) - -- Create the checkbox inside the optionFrame - local checkbox = CreateFrame("CheckButton", "ConsumesManager_CharacterCheckbox" .. index, optionFrame) + -- Create the checkbox inside the itemFrame + local checkbox = CreateFrame("CheckButton", "ConsumesManager_CharacterCheckbox" .. index, itemFrame) checkbox:SetWidth(16) checkbox:SetHeight(16) - checkbox:SetPoint("LEFT", optionFrame, "LEFT", 0, 0) + checkbox:SetPoint("LEFT", itemFrame, "LEFT", 0, 0) -- Create Textures for the checkbox checkbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") @@ -882,17 +898,14 @@ function ConsumesManager_CreateCharactersContent(parentFrame) checkbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") -- Create FontString for label - local label = optionFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + local label = itemFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") label:SetPoint("LEFT", checkbox, "RIGHT", 4, 0) label:SetText(currentCharacterName) + label:SetJustifyH("LEFT") -- Set up the checkbox OnClick handler checkbox:SetScript("OnClick", function() - if checkbox:GetChecked() then - ConsumesManager_Settings["Characters"][currentCharacterName] = true - else - ConsumesManager_Settings["Characters"][currentCharacterName] = false - end + ConsumesManager_Settings["Characters"][currentCharacterName] = checkbox:GetChecked() ConsumesManager_UpdateManagerContent() end) -- Load saved setting @@ -905,19 +918,107 @@ function ConsumesManager_CreateCharactersContent(parentFrame) end parentFrame.checkboxes[currentCharacterName] = checkbox - -- Make the optionFrame clickable (so clicking on the label checks/unchecks the box) - optionFrame:EnableMouse(true) - optionFrame:SetScript("OnMouseDown", function() + -- Make the itemFrame clickable (so clicking on the label checks/unchecks the box) + itemFrame:EnableMouse(true) + itemFrame:SetScript("OnMouseDown", function() checkbox:Click() end) end + -- Add spacing of 20 below the character list + index = index + 1 + + -- Create 'General Settings' title with spacing of 20 + local generalSettingsTitle = scrollChild:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + generalSettingsTitle:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, startYOffset - index * lineHeight) + generalSettingsTitle:SetText("General Settings") + generalSettingsTitle:SetTextColor(1, 1, 1) + + -- Move index down for the checkboxes + index = index + 1 + + -- Initialize General Settings if not set + ConsumesManager_Settings.enableCategories = ConsumesManager_Settings.enableCategories == nil and true or ConsumesManager_Settings.enableCategories + ConsumesManager_Settings.showUseButton = ConsumesManager_Settings.showUseButton == nil and true or ConsumesManager_Settings.showUseButton + + -- Create 'Enable Categories' checkbox + local enableCategoriesFrame = CreateFrame("Frame", "ConsumesManager_EnableCategoriesFrame", scrollChild) + enableCategoriesFrame:SetWidth(WindowWidth - 10) + enableCategoriesFrame:SetHeight(18) + enableCategoriesFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, startYOffset - index * lineHeight) + enableCategoriesFrame:EnableMouse(true) + + local enableCategoriesCheckbox = CreateFrame("CheckButton", "ConsumesManager_EnableCategoriesCheckbox", enableCategoriesFrame) + enableCategoriesCheckbox:SetWidth(16) + enableCategoriesCheckbox:SetHeight(16) + enableCategoriesCheckbox:SetPoint("LEFT", enableCategoriesFrame, "LEFT", 0, 0) + + enableCategoriesCheckbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") + enableCategoriesCheckbox:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down") + enableCategoriesCheckbox:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") + enableCategoriesCheckbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") + + enableCategoriesCheckbox:SetChecked(ConsumesManager_Settings.enableCategories) + + enableCategoriesCheckbox:SetScript("OnClick", function() + ConsumesManager_Settings.enableCategories = enableCategoriesCheckbox:GetChecked() + ConsumesManager_UpdateManagerContent() + end) + + local enableCategoriesLabel = enableCategoriesFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + enableCategoriesLabel:SetPoint("LEFT", enableCategoriesCheckbox, "RIGHT", 4, 0) + enableCategoriesLabel:SetText("Enable Categories") + enableCategoriesLabel:SetJustifyH("LEFT") + + -- Make the frame clickable (so clicking on the label checks/unchecks the box) + enableCategoriesFrame:SetScript("OnMouseDown", function() + enableCategoriesCheckbox:Click() + end) + + index = index + 1 -- Move index down for next checkbox + + -- Create 'Show Use Button' checkbox + local showUseButtonFrame = CreateFrame("Frame", "ConsumesManager_ShowUseButtonFrame", scrollChild) + showUseButtonFrame:SetWidth(WindowWidth - 10) + showUseButtonFrame:SetHeight(18) + showUseButtonFrame:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, startYOffset - index * lineHeight) + showUseButtonFrame:EnableMouse(true) + + local showUseButtonCheckbox = CreateFrame("CheckButton", "ConsumesManager_ShowUseButtonCheckbox", showUseButtonFrame) + showUseButtonCheckbox:SetWidth(16) + showUseButtonCheckbox:SetHeight(16) + showUseButtonCheckbox:SetPoint("LEFT", showUseButtonFrame, "LEFT", 0, 0) + + showUseButtonCheckbox:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up") + showUseButtonCheckbox:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down") + showUseButtonCheckbox:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight") + showUseButtonCheckbox:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check") + + showUseButtonCheckbox:SetChecked(ConsumesManager_Settings.showUseButton) + + showUseButtonCheckbox:SetScript("OnClick", function() + ConsumesManager_Settings.showUseButton = showUseButtonCheckbox:GetChecked() + ConsumesManager_UpdateManagerContent() + end) + + local showUseButtonLabel = showUseButtonFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") + showUseButtonLabel:SetPoint("LEFT", showUseButtonCheckbox, "RIGHT", 4, 0) + showUseButtonLabel:SetText("Show Use Button") + showUseButtonLabel:SetJustifyH("LEFT") + + -- Make the frame clickable (so clicking on the label checks/unchecks the box) + showUseButtonFrame:SetScript("OnMouseDown", function() + showUseButtonCheckbox:Click() + end) + + index = index + 1 -- Move index down after adding General Settings + -- Adjust the scroll child height - scrollChild.contentHeight = startYOffset + index * 20 + scrollChild.contentHeight = math.abs(startYOffset) + index * lineHeight scrollChild:SetHeight(scrollChild.contentHeight) -- Scroll Bar - local scrollBar = CreateFrame("Slider", "ConsumesManager_CharactersScrollBar", parentFrame) + local scrollBar = CreateFrame("Slider", "ConsumesManager_SettingsScrollBar", parentFrame) scrollBar:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -2, -16) scrollBar:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -2, 16) scrollBar:SetWidth(16) @@ -934,15 +1035,15 @@ function ConsumesManager_CreateCharactersContent(parentFrame) scrollFrame:SetVerticalScroll(value) end) parentFrame.scrollBar = scrollBar - parentFrame.scrollFrame = scrollFrame - parentFrame.scrollChild = scrollChild -- Update the scrollbar - ConsumesManager_UpdateCharactersScrollBar() + ConsumesManager_UpdateSettingsScrollBar() end + + -- Update Window Tabs ----------------------------------------------------------------------------------- function ConsumesManager_UpdateManagerContent() if not ConsumesManager_MainFrame or not ConsumesManager_MainFrame.tabs or not ConsumesManager_MainFrame.tabs[1] then @@ -967,11 +1068,11 @@ function ConsumesManager_UpdateManagerContent() -- If bank and mail have not been scanned, show message and hide item labels if not bankScanned or not mailScanned then - -- Hide all option frames + -- Hide all item frames and category labels for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do categoryInfo.label:Hide() - for _, optionInfo in ipairs(categoryInfo.options) do - optionInfo.frame:Hide() + for _, itemInfo in ipairs(categoryInfo.Items) do + itemInfo.frame:Hide() end end @@ -993,87 +1094,173 @@ function ConsumesManager_UpdateManagerContent() -- Hide the message label ManagerFrame.messageLabel:Hide() - -- Iterate over categories - for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do - local anyOptionVisible = false - - -- First, check if any options in the category are enabled - for _, optionInfo in ipairs(categoryInfo.options) do - local itemID = optionInfo.itemID - if ConsumesManager_Settings[itemID] then - anyOptionVisible = true - break - end - end - - -- If any options are visible, show the category label - if anyOptionVisible then - categoryInfo.label:SetPoint("TOPLEFT", ManagerFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) - categoryInfo.label:Show() - index = index + 1 - - -- Now, position and show the enabled options - for _, optionInfo in ipairs(categoryInfo.options) do - local itemID = optionInfo.itemID - local itemName = optionInfo.name - local label = optionInfo.label - local button = optionInfo.button - local frame = optionInfo.frame + -- Check if categories are enabled + if ConsumesManager_Settings.enableCategories then + -- Iterate over categories + for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do + local anyItemVisible = false + -- First, check if any Items in the category are enabled + for _, itemInfo in ipairs(categoryInfo.Items) do + local itemID = itemInfo.itemID if ConsumesManager_Settings[itemID] then - -- Sum counts across all selected characters - local totalCount = 0 - for character, charData in pairs(data) do - if ConsumesManager_Settings["Characters"][character] == true then - local inventory = charData["inventory"] and charData["inventory"][itemID] or 0 - local bank = charData["bank"] and charData["bank"][itemID] or 0 - local mail = charData["mail"] and charData["mail"][itemID] or 0 - totalCount = totalCount + inventory + bank + mail - end - end - - -- Update label text with counts - label:SetText(itemName .. " (" .. totalCount .. ")") - - -- Adjust label color based on count - if totalCount == 0 then - label:SetTextColor(1, 0, 0) -- Red - elseif totalCount < 10 then - label:SetTextColor(1, 0.4, 0) -- Orange - elseif totalCount <= 20 then - label:SetTextColor(1, 0.85, 0) -- Yellow - else - label:SetTextColor(0, 1, 0) -- Green - end - - -- Enable or disable the 'Use' button based on whether the item is in the player's inventory - local playerInventory = data[playerName]["inventory"] or {} - local countInInventory = playerInventory[itemID] or 0 - if countInInventory > 0 then - button:Enable() - else - button:Disable() - end - - -- Show and position the option frame - frame:SetPoint("TOPLEFT", ManagerFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) - frame:Show() - index = index + 1 - hasAnyVisibleItems = true - else - frame:Hide() + anyItemVisible = true + break end end - -- Add extra spacing after the category - index = index + 1 -- Add one extra line of spacing between categories - else - categoryInfo.label:Hide() - -- Hide all options under this category - for _, optionInfo in ipairs(categoryInfo.options) do - optionInfo.frame:Hide() + -- If any Items are visible, handle category label + if anyItemVisible then + categoryInfo.label:SetPoint("TOPLEFT", ManagerFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + categoryInfo.label:Show() + index = index + 1 + + -- Now, position and show the enabled Items + for _, itemInfo in ipairs(categoryInfo.Items) do + local itemID = itemInfo.itemID + local itemName = itemInfo.name + local label = itemInfo.label + local button = itemInfo.button + local frame = itemInfo.frame + + if ConsumesManager_Settings[itemID] then + -- Sum counts across all selected characters + local totalCount = 0 + for character, charData in pairs(data) do + if ConsumesManager_Settings["Characters"][character] == true then + local inventory = charData["inventory"] and charData["inventory"][itemID] or 0 + local bank = charData["bank"] and charData["bank"][itemID] or 0 + local mail = charData["mail"] and charData["mail"][itemID] or 0 + totalCount = totalCount + inventory + bank + mail + end + end + + -- Update label text with counts + label:SetText(itemName .. " (" .. totalCount .. ")") + + -- Adjust label color based on count + if totalCount == 0 then + label:SetTextColor(1, 0, 0) -- Red + elseif totalCount < 10 then + label:SetTextColor(1, 0.4, 0) -- Orange + elseif totalCount <= 20 then + label:SetTextColor(1, 0.85, 0) -- Yellow + else + label:SetTextColor(0, 1, 0) -- Green + end + + -- Enable or disable the 'Use' button based on whether the item is in the player's inventory + local playerInventory = data[playerName]["inventory"] or {} + local countInInventory = playerInventory[itemID] or 0 + + if ConsumesManager_Settings.showUseButton then + button:Show() + if countInInventory > 0 then + button:Enable() + else + button:Disable() + end + label:SetPoint("LEFT", button, "RIGHT", 4, 0) + else + button:Hide() + label:SetPoint("LEFT", frame, "LEFT", 0, 0) + end + + -- Show and position the item frame + frame:SetPoint("TOPLEFT", ManagerFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + frame:Show() + index = index + 1 + hasAnyVisibleItems = true + else + frame:Hide() + end + end + + -- Add extra spacing after the category + index = index + 1 -- Add one extra line of spacing between categories + else + categoryInfo.label:Hide() + -- Hide all Items under this category + for _, itemInfo in ipairs(categoryInfo.Items) do + itemInfo.frame:Hide() + end end end + else + -- Categories are disabled + -- Collect all enabled items into a single list + local allItems = {} + for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do + categoryInfo.label:Hide() + for _, itemInfo in ipairs(categoryInfo.Items) do + local itemID = itemInfo.itemID + if ConsumesManager_Settings[itemID] then + table.insert(allItems, itemInfo) + else + itemInfo.frame:Hide() + end + end + end + + -- Sort allItems by item name + table.sort(allItems, function(a, b) return a.name < b.name end) + + -- Display all items + for _, itemInfo in ipairs(allItems) do + local itemID = itemInfo.itemID + local itemName = itemInfo.name + local label = itemInfo.label + local button = itemInfo.button + local frame = itemInfo.frame + + -- Sum counts across all selected characters + local totalCount = 0 + for character, charData in pairs(data) do + if ConsumesManager_Settings["Characters"][character] == true then + local inventory = charData["inventory"] and charData["inventory"][itemID] or 0 + local bank = charData["bank"] and charData["bank"][itemID] or 0 + local mail = charData["mail"] and charData["mail"][itemID] or 0 + totalCount = totalCount + inventory + bank + mail + end + end + + -- Update label text with counts + label:SetText(itemName .. " (" .. totalCount .. ")") + + -- Adjust label color based on count + if totalCount == 0 then + label:SetTextColor(1, 0, 0) -- Red + elseif totalCount < 10 then + label:SetTextColor(1, 0.4, 0) -- Orange + elseif totalCount <= 20 then + label:SetTextColor(1, 0.85, 0) -- Yellow + else + label:SetTextColor(0, 1, 0) -- Green + end + + -- Enable or disable the 'Use' button based on whether the item is in the player's inventory + local playerInventory = data[playerName]["inventory"] or {} + local countInInventory = playerInventory[itemID] or 0 + + if ConsumesManager_Settings.showUseButton then + button:Show() + if countInInventory > 0 then + button:Enable() + else + button:Disable() + end + label:SetPoint("LEFT", button, "RIGHT", 4, 0) + else + button:Hide() + label:SetPoint("LEFT", frame, "LEFT", 0, 0) + end + + -- Show and position the item frame + frame:SetPoint("TOPLEFT", ManagerFrame.scrollChild, "TOPLEFT", 0, - (index) * lineHeight) + frame:Show() + index = index + 1 + hasAnyVisibleItems = true + end end -- Adjust the scroll child height @@ -1085,7 +1272,7 @@ function ConsumesManager_UpdateManagerContent() if not hasAnyVisibleItems then -- Show message when no items are selected - ManagerFrame.messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |roptions|cffffffff to get started|r") + ManagerFrame.messageLabel:SetText("|cffff0000No consumables selected|r\n\n|cffffffffClick on |rItems|cffffffff to get started|r") ManagerFrame.messageLabel:Show() else -- Hide the message label as we have content to display @@ -1120,14 +1307,14 @@ function ConsumesManager_UpdateManagerScrollBar() end end -function ConsumesManager_UpdateOptionsScrollBar() - local optionsFrame = ConsumesManager_MainFrame.tabs[2] - local scrollBar = optionsFrame.scrollBar - local scrollFrame = optionsFrame.scrollFrame - local scrollChild = optionsFrame.scrollChild +function ConsumesManager_UpdateItemsScrollBar() + local ItemsFrame = ConsumesManager_MainFrame.tabs[2] + local scrollBar = ItemsFrame.scrollBar + local scrollFrame = ItemsFrame.scrollFrame + local scrollChild = ItemsFrame.scrollChild local totalHeight = scrollChild.contentHeight - local parentHeight = optionsFrame:GetHeight() + local parentHeight = ItemsFrame:GetHeight() local searchBoxHeight = 36 -- Adjusted height including padding local buttonsHeight = 40 -- Space reserved for Select/Deselect buttons local shownHeight = parentHeight - searchBoxHeight - buttonsHeight - 20 -- Additional padding @@ -1147,11 +1334,11 @@ function ConsumesManager_UpdateOptionsScrollBar() end -function ConsumesManager_UpdateCharactersScrollBar() - local charactersFrame = ConsumesManager_MainFrame.tabs[3] - local scrollBar = charactersFrame.scrollBar - local scrollFrame = charactersFrame.scrollFrame - local scrollChild = charactersFrame.scrollChild +function ConsumesManager_UpdateSettingsScrollBar() + local OptionsFrame = ConsumesManager_MainFrame.tabs[3] + local scrollBar = OptionsFrame.scrollBar + local scrollFrame = OptionsFrame.scrollFrame + local scrollChild = OptionsFrame.scrollChild local totalHeight = scrollChild.contentHeight local shownHeight = 320 -- Adjust based on your UI @@ -1173,6 +1360,7 @@ end -- Functions to USE an item ----------------------------------------------------------------------------- + function ConsumesManager_UpdateUseButtons() if not ConsumesManager_MainFrame or not ConsumesManager_MainFrame.tabs or not ConsumesManager_MainFrame.tabs[1] then return @@ -1193,25 +1381,41 @@ function ConsumesManager_UpdateUseButtons() local inventory = charData["inventory"] or {} - -- Iterate over the options to update the buttons + -- Iterate over the Items to update the buttons for _, categoryInfo in ipairs(ManagerFrame.categoryInfo) do - for _, optionInfo in ipairs(categoryInfo.options) do - local itemID = optionInfo.itemID - local button = optionInfo.button + for _, itemInfo in ipairs(categoryInfo.Items) do + local itemID = itemInfo.itemID + local button = itemInfo.button + local label = itemInfo.label + local frame = itemInfo.frame + if ConsumesManager_Settings[itemID] then local count = inventory[itemID] or 0 if count > 0 then - button:Enable() + if ConsumesManager_Settings.showUseButton then + button:Enable() + button:Show() + label:SetPoint("LEFT", button, "RIGHT", 4, 0) + else + button:Disable() + button:Hide() + label:SetPoint("LEFT", frame, "LEFT", 0, 0) + end else button:Disable() + button:Hide() + label:SetPoint("LEFT", frame, "LEFT", 0, 0) end else button:Disable() + button:Hide() + label:SetPoint("LEFT", frame, "LEFT", 0, 0) end end end end + function ConsumesManager_FindItemInBags(itemID) for bag = 0, 4 do local numSlots = GetContainerNumSlots(bag) @@ -1272,7 +1476,7 @@ function ConsumesManager_ShowConsumableTooltip(itemID) tooltipFrame.title = title -- Item Total - local total = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") + local total = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") total:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -20) total:SetPoint("RIGHT", tooltipFrame, "RIGHT", -10, 0) total:SetJustifyH("LEFT") @@ -1363,7 +1567,7 @@ function ConsumesManager_ShowConsumableTooltip(itemID) contentText = contentText .. "|cffff0000No items found for this consumable.|r" else - -- Sort characters alphabetically (optional) + -- Sort characters alphabetically (itemal) table.sort(characterList, function(a, b) return a.name < b.name end) -- Display data for each character @@ -1394,8 +1598,8 @@ function ConsumesManager_ShowConsumableTooltip(itemID) numLines = numLines + 2 -- Add lines for title and padding -- Adjust tooltip height based on the number of lines - local lineHeight = 10 - local totalHeight = 50 + (numLines * lineHeight) + local lineHeightTooltip = 12 + local totalHeight = 50 + (numLines * lineHeightTooltip) tooltipFrame:SetHeight(totalHeight) -- Set the width based on content @@ -1410,14 +1614,15 @@ function ConsumesManager_ShowConsumableTooltip(itemID) tooltipFrame:Show() end -function ConsumesManager_ShowOptionsTooltip(itemID) + +function ConsumesManager_ShowItemsTooltip(itemID) -- Set the maximum width for the description text - local maxDescriptionWidth = 160 + local maxDescriptionWidth = 200 -- Create or reuse the tooltip frame - if not ConsumesManager_OptionsTooltip then + if not ConsumesManager_ItemsTooltip then -- Create the frame - local tooltipFrame = CreateFrame("Frame", "ConsumesManager_OptionsTooltip", UIParent) + local tooltipFrame = CreateFrame("Frame", "ConsumesManager_ItemsTooltip", UIParent) tooltipFrame:SetFrameStrata("TOOLTIP") tooltipFrame:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", @@ -1442,71 +1647,39 @@ function ConsumesManager_ShowOptionsTooltip(itemID) -- Item description local description = tooltipFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal") - description:SetPoint("TOPLEFT", icon, "TOPRIGHT", 10, -16) + description:SetPoint("TOPLEFT", icon, "BOTTOMLEFT", 0, -10) description:SetWidth(maxDescriptionWidth) description:SetJustifyH("LEFT") description:SetTextColor(1, 1, 1) tooltipFrame.description = description - ConsumesManager_OptionsTooltip = tooltipFrame + ConsumesManager_ItemsTooltip = tooltipFrame end - local tooltipFrame = ConsumesManager_OptionsTooltip + local tooltipFrame = ConsumesManager_ItemsTooltip -- Get item info local itemName = consumablesList[itemID] or "Unknown Item" local itemTexture = consumablesTexture[itemID] or "Interface\\Icons\\INV_Misc_QuestionMark" local itemDescription = consumablesDescription[itemID] or "" - -- Function to add line breaks manually - local function WrapText(text, maxWidth, fontObject) - local wrappedText = "" - local currentLine = "" - local space = " " - local testString = tooltipFrame:CreateFontString(nil, "OVERLAY", fontObject) - testString:SetWidth(0) - testString:SetHeight(0) - testString:Hide() -- We don't need to display this - - -- Replace \n with spaces to avoid unintended line breaks - text = string.gsub(text, "\n", " ") - - -- Split the text into words - for word in string.gfind(text, "%S+") do - local testLine = currentLine == "" and word or (currentLine .. space .. word) - testString:SetText(testLine) - if testString:GetStringWidth() > maxWidth then - wrappedText = wrappedText == "" and currentLine or (wrappedText .. "\n" .. currentLine) - currentLine = word - else - currentLine = testLine - end - end - wrappedText = wrappedText == "" and currentLine or (wrappedText .. "\n" .. currentLine) - return wrappedText - end - - -- Wrap the description text - local wrappedDescription = WrapText(itemDescription, maxDescriptionWidth, "GameFontNormal") - -- Set icon, title, and description tooltipFrame.icon:SetTexture(itemTexture) tooltipFrame.title:SetText(itemName) - tooltipFrame.description:SetText(wrappedDescription) + tooltipFrame.description:SetText(itemDescription) - -- Calculate the number of lines in the wrapped description - local _, lineCount = string.gsub(wrappedDescription, "\n", "") - lineCount = lineCount + 1 -- Add 1 for the last line + -- Adjust the height of the description based on its content + tooltipFrame.description:SetWidth(maxDescriptionWidth) + tooltipFrame.description:SetText(itemDescription) + local descriptionHeight = tooltipFrame.description:GetHeight() - -- Set the heights for the description and the tooltip frame - local lineHeight = 12 - tooltipFrame.description:SetHeight(lineCount * lineHeight) - - local totalHeight = 50 + (lineCount * lineHeight) + -- Adjust tooltip height based on content + local totalHeight = 70 + descriptionHeight tooltipFrame:SetHeight(totalHeight) -- Set the width of the tooltip - local maxWidth = math.max(tooltipFrame.title:GetStringWidth() + 70, tooltipFrame.description:GetStringWidth() + 70) + local titleWidth = tooltipFrame.title:GetStringWidth() + 70 + local maxWidth = math.max(titleWidth, maxDescriptionWidth + 20) tooltipFrame:SetWidth(maxWidth) -- Position the tooltip near the cursor diff --git a/images/resize_icon.tga b/images/resize_icon.tga deleted file mode 100644 index 28c89cc1c7d3b6a7f5dfcf67cd31afce231d90c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4140 zcmZQzU}As)1qLAG`2YVu!zdm!VE~K@6o4ykP!s_iM5hshz+)K&*G8=JY< z+(2!0gzQIm11|fCRR^;V8%?R%#JB;Qy~tv;b_23qxY*Qo11@{;iI2JglK)4;VKf|& z^9iNt0og2a*_60}TszVAk?RI@b12~x>jp~fAf$KH4WyM1gu;^&`O&x-{Bc1^{F15{ tTY4bX3`*6byMa>kM)gC|IK|-r8x05v3Uv+h4EFT*(~WTR^l{Z=007BsB<%nI diff --git a/images/tracker_icon.tga b/images/tracker_icon.tga new file mode 100644 index 0000000000000000000000000000000000000000..e1b575fb066a5b32a7f3b41891aed5e91fcb7554 GIT binary patch literal 16428 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 Cb@OKc literal 0 HcmV?d00001