feat: mailbox bug

This commit is contained in:
Salikh Gurgenidze
2025-12-27 01:59:14 +04:00
parent c14a3dbffd
commit 61bb1d9d8e
12 changed files with 951 additions and 5 deletions
+21
View File
@@ -131,6 +131,7 @@ function DB:Initialize()
money = 0,
bags = {},
bank = {},
mailbox = {}, -- Add mailbox storage
equipped = {}, -- Add equipped items storage
character = {}, -- Add character info storage
lastUpdate = time(),
@@ -153,6 +154,10 @@ function DB:Initialize()
char.character = {}
addon:Debug("Added character field to existing character")
end
if not char.mailbox then
char.mailbox = {}
addon:Debug("Added mailbox field to existing character")
end
end
addon:Debug("Database initialized for %s", fullName)
@@ -215,6 +220,16 @@ function DB:SaveMoney(copper)
end
end
-- Save mailbox data
function DB:SaveMailbox(mailboxData)
local char = self:GetCurrentCharacter()
if char then
char.mailbox = mailboxData
char.lastUpdate = time()
addon:Debug("Saved mailbox data")
end
end
-- Get all characters (optionally filter by faction and/or realm)
function DB:GetAllCharacters(sameFactionOnly, currentRealmOnly)
local chars = {}
@@ -256,6 +271,12 @@ function DB:GetCharacterBank(fullName)
return char and char.bank or {}
end
-- Get character's mailbox
function DB:GetCharacterMailbox(fullName)
local char = Guda_DB.characters[fullName]
return char and char.mailbox or {}
end
-- Get character's equipped items
function DB:GetCharacterEquipped(fullName)
local char = Guda_DB.characters[fullName]
+8
View File
@@ -79,3 +79,11 @@ end
function Events:OnPlayerLogout(callback, owner)
self:Register("PLAYER_LOGOUT", callback, owner)
end
function Events:OnMailShow(callback, owner)
self:Register("MAIL_SHOW", callback, owner)
end
function Events:OnMailClosed(callback, owner)
self:Register("MAIL_CLOSED", callback, owner)
end
+2
View File
@@ -102,11 +102,13 @@ addon.Modules = {
Tooltip = {},
BagScanner = {},
BankScanner = {},
MailboxScanner = {},
MoneyTracker = {},
EquipmentScanner = {},
SortEngine = {},
BagFrame = {},
BankFrame = {},
MailboxFrame = {},
QuestItemBar = {},
TrackedItemBar = {},
SettingsPopup = {},
+7
View File
@@ -18,6 +18,7 @@ function Main:Initialize()
-- Initialize scanners
addon.Modules.BagScanner:Initialize()
addon.Modules.BankScanner:Initialize()
addon.Modules.MailboxScanner:Initialize()
addon.Modules.MoneyTracker:Initialize()
addon.Modules.EquipmentScanner:Initialize()
@@ -25,6 +26,7 @@ function Main:Initialize()
addon:Print("Initializing UI...")
addon.Modules.BagFrame:Initialize()
addon.Modules.BankFrame:Initialize()
addon.Modules.MailboxFrame:Initialize()
addon:Debug("Checking QuestItemBar module...")
if addon.Modules.QuestItemBar and addon.Modules.QuestItemBar.isLoaded then
@@ -103,6 +105,10 @@ function Main:SetupSlashCommands()
-- Toggle bank
addon.Modules.BankFrame:Toggle()
elseif msg == "mail" or msg == "mailbox" then
-- Toggle mailbox
addon.Modules.MailboxFrame:Toggle()
elseif msg == "sort" then
-- Sort bags
addon.Modules.SortEngine:SortBags()
@@ -143,6 +149,7 @@ function Main:SetupSlashCommands()
addon:Print("Commands:")
addon:Print("/guda - Toggle bags")
addon:Print("/guda bank - Toggle bank")
addon:Print("/guda mail - Toggle mailbox")
addon:Print("/guda sort - Sort bags")
addon:Print("/guda sortbank - Sort bank")
addon:Print("/guda track - Toggle item tracking")
+126
View File
@@ -0,0 +1,126 @@
-- Guda Mailbox Scanner
-- Scans and stores mailbox contents
local addon = Guda
local MailboxScanner = {}
addon.Modules.MailboxScanner = MailboxScanner
local mailboxOpen = false
-- Scan all mailbox items and return data
function MailboxScanner:ScanMailbox()
if not mailboxOpen then
addon:Debug("Cannot scan mailbox - not open")
return {}
end
local mailboxData = {}
local numItems = GetInboxNumItems()
for i = 1, numItems do
mailboxData[i] = self:ScanMailItem(i)
end
return mailboxData
end
-- Scan a single mail item
function MailboxScanner:ScanMailItem(index)
-- GetInboxItem(index) returns: packageIcon, stationeryIcon, sender, subject, money, CODAmount, daysLeft, hasItem, wasRead, wasReturned, textCreated, canReply, isGM
local packageIcon, stationeryIcon, sender, subject, money, CODAmount, daysLeft, hasItem, wasRead, wasReturned, textCreated, canReply, isGM = GetInboxItem(index)
local itemData = nil
if hasItem then
addon:Debug("Scanning mail item %d", index)
-- In 1.12.1, GetInboxItemLink(index) returns the link
local itemLink = GetInboxItemLink(index)
-- GetInboxItemInfo(index) returns: name, texture, count, quality, canUse
local infoName, infoTexture, infoCount, infoQuality, infoCanUse = GetInboxItemInfo(index)
addon:Debug("Mail item %d: infoName=%s, infoTexture=%s", index, tostring(infoName), tostring(infoTexture))
-- Even if link is missing (not cached), we can store what we have from GetInboxItemInfo
itemData = {
link = itemLink,
texture = infoTexture or packageIcon or "Interface\\Icons\\INV_Misc_Bag_08",
count = infoCount or 1,
quality = infoQuality or 0,
name = infoName or subject or "Unknown Item",
}
-- If we have a link, try to get more detailed info
if itemLink then
local itemName, link, itemQuality, iLevel, itemCategory, itemType, itemStackCount, itemSubType, itemTexture, itemEquipLoc, itemSellPrice = addon.Modules.Utils:GetItemInfo(itemLink)
if itemName then
itemData.name = itemName
itemData.quality = itemQuality or itemData.quality
itemData.iLevel = iLevel
itemData.type = itemType
itemData.class = itemCategory
itemData.subclass = itemSubType
itemData.equipSlot = itemEquipLoc
if itemTexture then itemData.texture = itemTexture end
end
end
end
return {
sender = sender,
subject = subject,
money = money,
CODAmount = CODAmount,
daysLeft = daysLeft,
hasItem = hasItem,
item = itemData,
wasRead = wasRead,
packageIcon = packageIcon,
}
end
-- Save current mailbox to database
function MailboxScanner:SaveToDatabase()
if not mailboxOpen then
return
end
local mailboxData = self:ScanMailbox()
addon.Modules.DB:SaveMailbox(mailboxData)
addon:Debug("Mailbox data saved")
end
-- Initialize mailbox scanner
function MailboxScanner:Initialize()
-- Mailbox opened
addon.Modules.Events:OnMailShow(function()
mailboxOpen = true
addon:Debug("Mailbox opened")
-- Delay scan slightly to ensure item info is available
local frame = CreateFrame("Frame")
local elapsed = 0
frame:SetScript("OnUpdate", function()
elapsed = elapsed + arg1
if elapsed >= 0.5 then
frame:SetScript("OnUpdate", nil)
if mailboxOpen then
MailboxScanner:SaveToDatabase()
end
end
end)
end, "MailboxScanner")
-- Mailbox closed
addon.Modules.Events:OnMailClosed(function()
-- Final save on close
self:SaveToDatabase()
mailboxOpen = false
addon:Debug("Mailbox closed")
end, "MailboxScanner")
end
-- Check if mailbox is currently open
function MailboxScanner:IsMailboxOpen()
return mailboxOpen
end
+3
View File
@@ -16,6 +16,7 @@ Core\Tooltip.lua
Data\BagScanner.lua
Data\BankScanner.lua
Data\MailboxScanner.lua
Data\MoneyTracker.lua
Data\EquipmentScanner.lua
@@ -25,12 +26,14 @@ Sorting\SortEngine.lua
UI\ItemButton.lua
UI\BagFrame.lua
UI\BankFrame.lua
UI\MailboxFrame.lua
UI\QuestItemBar.lua
UI\TrackedItemBar.lua
UI\SettingsPopup.lua
UI\ItemButton.xml
UI\BagFrame.xml
UI\BankFrame.xml
UI\MailboxFrame.xml
UI\SettingsPopup.xml
Core\Main.lua
+113
View File
@@ -1578,6 +1578,119 @@ local function HideCharacterDropdown()
end
end
-- Toggle mail dropdown
function Guda_BagFrame_ToggleMailDropdown(button)
-- Hide character dropdown if it's shown
if characterDropdown and characterDropdown:IsShown() then
characterDropdown:Hide()
end
if bankDropdown and bankDropdown:IsShown() then
bankDropdown:Hide()
end
if mailDropdown and mailDropdown:IsShown() then
mailDropdown:Hide()
return
end
if not mailDropdown then
-- Create dropdown frame
mailDropdown = CreateFrame("Frame", "Guda_MailDropdown", UIParent)
mailDropdown:SetFrameStrata("DIALOG")
mailDropdown:SetWidth(200)
mailDropdown:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
tile = true,
tileSize = 16,
edgeSize = 16,
insets = { left = 4, right = 4, top = 4, bottom = 4 }
})
mailDropdown:SetBackdropColor(0, 0, 0, 0.95)
mailDropdown:EnableMouse(true)
mailDropdown:Hide()
mailDropdown.buttons = {}
end
-- Position dropdown below the button
mailDropdown:ClearAllPoints()
mailDropdown:SetPoint("TOPLEFT", button, "BOTTOMLEFT", 0, -2)
-- Clear existing buttons
for _, btn in ipairs(mailDropdown.buttons) do
btn:Hide()
end
mailDropdown.buttons = {}
-- Get all characters on current realm
local chars = addon.Modules.DB:GetAllCharacters(false, true)
local yOffset = -8
-- Add character buttons
for _, char in ipairs(chars) do
-- Capture variables in local scope for closure
local charFullName = char.fullName
local charName = char.name
local charClassToken = char.classToken
local charButton = CreateFrame("Button", nil, mailDropdown)
charButton:SetWidth(188)
charButton:SetHeight(20)
charButton:SetPoint("TOP", mailDropdown, "TOP", 0, yOffset)
-- Button background on hover
local charBg = charButton:CreateTexture(nil, "BACKGROUND")
charBg:SetAllPoints()
charBg:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
charBg:SetBlendMode("ADD")
charBg:SetAlpha(0)
-- Get class color
local classColor = charClassToken and RAID_CLASS_COLORS[charClassToken]
local r, g, b = 1, 1, 1
if classColor then
r, g, b = classColor.r, classColor.g, classColor.b
end
-- Button text
local charText = charButton:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
charText:SetPoint("LEFT", charButton, "LEFT", 8, 0)
charText:SetText(charName)
charText:SetTextColor(r, g, b)
-- Button scripts
charButton:SetScript("OnEnter", function()
charBg:SetAlpha(0.3)
end)
charButton:SetScript("OnLeave", function()
charBg:SetAlpha(0)
end)
charButton:SetScript("OnClick", function()
if charFullName then
-- Show mailbox for this character
addon.Modules.MailboxFrame:ShowCharacter(charFullName)
if not Guda_MailboxFrame:IsShown() then
Guda_MailboxFrame:Show()
end
mailDropdown:Hide()
else
addon:Print("Error: Character fullName is nil")
end
end)
table.insert(mailDropdown.buttons, charButton)
yOffset = yOffset - 20
end
-- Set dropdown height based on content
mailDropdown:SetHeight(math.abs(yOffset) + 8)
-- Show dropdown
mailDropdown:Show()
end
-- Toggle bank dropdown
function Guda_BagFrame_ToggleBankDropdown(button)
-- Hide character dropdown if it's shown
+46
View File
@@ -248,6 +248,52 @@
</Scripts>
</Button>
<!-- Mail Button (header left, next to bank) -->
<Button name="$parent_MailButton">
<Size>
<AbsDimension x="24" y="24"/>
</Size>
<Anchors>
<Anchor point="LEFT" relativePoint="RIGHT" relativeTo="$parent_BankButton">
<Offset>
<AbsDimension x="2" y="0"/>
</Offset>
</Anchor>
</Anchors>
<Layers>
<Layer level="ARTWORK">
<Texture name="$parent_Icon">
<Size>
<AbsDimension x="20" y="20"/>
</Size>
<Anchors>
<Anchor point="CENTER"/>
</Anchors>
<Color r="0.8" g="0.8" b="0.8"/>
</Texture>
</Layer>
</Layers>
<Scripts>
<OnLoad>
local icon = getglobal(this:GetName().."_Icon")
if icon then
icon:SetTexture("Interface\\Icons\\INV_Letter_15")
end
</OnLoad>
<OnClick>
Guda_BagFrame_ToggleMailDropdown(this)
</OnClick>
<OnEnter>
GameTooltip:SetOwner(this, "ANCHOR_TOP")
GameTooltip:SetText("View Mailbox")
GameTooltip:Show()
</OnEnter>
<OnLeave>
GameTooltip:Hide()
</OnLeave>
</Scripts>
</Button>
<!-- Toolbar Container (for money tooltip and bag slots) -->
<Frame name="$parent_Toolbar" enableMouse="true">
<Size>
+46
View File
@@ -107,6 +107,52 @@
</Scripts>
</Button>
<!-- Mail Button (header right, next to sort) -->
<Button name="$parent_MailButton">
<Size>
<AbsDimension x="24" y="24"/>
</Size>
<Anchors>
<Anchor point="RIGHT" relativePoint="LEFT" relativeTo="$parent_SortButton">
<Offset>
<AbsDimension x="-2" y="0"/>
</Offset>
</Anchor>
</Anchors>
<Layers>
<Layer level="ARTWORK">
<Texture name="$parent_Icon">
<Size>
<AbsDimension x="20" y="20"/>
</Size>
<Anchors>
<Anchor point="CENTER"/>
</Anchors>
<Color r="0.8" g="0.8" b="0.8"/>
</Texture>
</Layer>
</Layers>
<Scripts>
<OnLoad>
local icon = getglobal(this:GetName().."_Icon")
if icon then
icon:SetTexture("Interface\\Icons\\INV_Letter_15")
end
</OnLoad>
<OnClick>
Guda_MailboxFrame:Toggle()
</OnClick>
<OnEnter>
GameTooltip:SetOwner(this, "ANCHOR_TOP")
GameTooltip:SetText("View Mailbox")
GameTooltip:Show()
</OnEnter>
<OnLeave>
GameTooltip:Hide()
</OnLeave>
</Scripts>
</Button>
<!-- Settings Button (header right, next to close) -->
<Button name="$parent_SettingsButton">
<Size>
+33 -5
View File
@@ -447,13 +447,18 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
self.bagID = bagID
self.slotID = slotID
-- Also set the Blizzard slot ID for compatibility with ContainerFrameItemButtonTemplate behavior
if self.SetID and slotID then
self:SetID(slotID)
-- ALWAYS set ID to something (0 if nil) to avoid leaking old IDs when button is reused
if self.SetID then
self:SetID(slotID or 0)
end
-- Explicitly set bag index to avoid Blizzard's ContainerFrameItemButton_OnEnter logic
-- from picking up this button as part of a real bag.
self.bagIndex = bagID or -100 -- Use an invalid bag index for non-bag buttons
self.itemData = itemData
self.isBank = isBank or false
self.otherChar = otherCharName
self.isReadOnly = isReadOnly or false -- Track if this is read-only mode
self.isMail = false -- Clear mailbox flag by default
-- Re-register for drag/drop every time (crucial for button reuse in Classic/Vanilla)
if not self.isReadOnly and not self.otherChar then
@@ -538,7 +543,17 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
-- Apply the determined texture and count
if self.hasItem then
if SetItemButtonTexture then SetItemButtonTexture(self, displayTexture) end
if SetItemButtonTexture then
SetItemButtonTexture(self, displayTexture)
end
-- Explicitly set and show icon texture as SetItemButtonTexture can be unreliable for custom paths in 1.12
local iconTexture = getglobal(self:GetName().."IconTexture") or getglobal(self:GetName().."Icon") or self.icon or self.Icon
if iconTexture and displayTexture then
iconTexture:SetTexture(displayTexture)
iconTexture:Show()
end
if SetItemButtonCount then SetItemButtonCount(self, displayCount or 1) end
if emptySlotBg then emptySlotBg:Hide() end
-- Update cooldown overlay for live items
@@ -625,6 +640,9 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
-- For 1.12.1 compatibility, access textures using both methods and proper naming
-- We'll set the NormalTexture later based on whether slot is empty or filled
-- ICON TEXTURE: Ensure we use the correct name ($parentIconTexture is standard)
local iconTexture = getglobal(self:GetName().."IconTexture") or getglobal(self:GetName().."Icon") or self.icon or self.Icon
-- Pushed texture follows the button size/position
local pushedTexture = getglobal(self:GetName().."PushedTexture")
if not pushedTexture and self.GetPushedTexture then
@@ -920,8 +938,8 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
self.questIcon:ClearAllPoints()
self.questIcon:SetPoint("TOPRIGHT", self, "TOPRIGHT", 1, 0)
end
else
-- Hide icon for empty slots
elseif not self.isMail then
-- Hide icon for empty slots, but keep it for mailbox custom icons
iconTexture:Hide()
end
end
@@ -929,6 +947,16 @@ end
-- OnEnter handler (show tooltip)
function Guda_ItemButton_OnEnter(self)
-- DETACH from Blizzard's internal tooltip logic for mailbox items
if self.isMail or this.isMail or (this:GetParent() and this:GetParent():GetName() == "Guda_MailboxFrame_ItemContainer") then
return
end
-- Prevent Blizzard's ContainerFrameItemButton_OnEnter from running if it somehow got through
if not self.bagID or self.bagID == -100 then
return
end
-- Highlight the corresponding bag button in the footer (works for empty and filled slots)
if not self.otherChar and self.bagID then
if self.isBank then
+302
View File
@@ -0,0 +1,302 @@
-- Mailbox Frame
-- Mailbox viewing UI
local addon = Guda
local MailboxFrame = {}
addon.Modules.MailboxFrame = MailboxFrame
local currentViewChar = nil
local searchText = ""
local isReadOnlyMode = true -- Mailbox is always read-only in this addon (viewing offline data)
local itemButtons = {}
local mailboxClickCatcher = nil
-- OnLoad
function Guda_MailboxFrame_OnLoad(self)
-- Set up initial backdrop
addon:ApplyBackdrop(self, "DEFAULT_FRAME")
-- Set up search box placeholder
local searchBox = getglobal(self:GetName().."_SearchBar_SearchBox")
if searchBox then
searchBox:SetText("Search mailbox...")
searchBox:SetTextColor(0.5, 0.5, 0.5, 1)
end
-- Create invisible full-screen frame to catch clicks outside the mailbox frame while typing in search
if not mailboxClickCatcher then
mailboxClickCatcher = CreateFrame("Frame", "Guda_MailboxClickCatcher", UIParent)
mailboxClickCatcher:SetFrameStrata("BACKGROUND")
mailboxClickCatcher:SetAllPoints(UIParent)
mailboxClickCatcher:EnableMouse(true)
mailboxClickCatcher:Hide()
mailboxClickCatcher:SetScript("OnMouseDown", function()
if Guda_MailboxFrame_ClearSearch then
Guda_MailboxFrame_ClearSearch()
end
end)
end
end
-- Clear search focus
function Guda_MailboxFrame_ClearSearch()
local searchBox = getglobal("Guda_MailboxFrame_SearchBar_SearchBox")
if searchBox then
searchBox:ClearFocus()
end
end
-- OnShow
function Guda_MailboxFrame_OnShow(self)
-- Apply frame transparency
if Guda_ApplyBackgroundTransparency then
Guda_ApplyBackgroundTransparency()
end
MailboxFrame:Update()
end
-- Toggle visibility
function MailboxFrame:Toggle()
if Guda_MailboxFrame:IsShown() then
Guda_MailboxFrame:Hide()
else
Guda_MailboxFrame:Show()
end
end
-- Show specific character's mailbox
function MailboxFrame:ShowCharacter(fullName)
currentViewChar = fullName
self:Update()
end
-- Initialize module
function MailboxFrame:Initialize()
-- Register events if needed
end
-- Update the mailbox frame
function MailboxFrame:Update()
if not Guda_MailboxFrame:IsShown() then return end
-- Determine which character to show
local charFullName = currentViewChar or addon.Modules.DB:GetPlayerFullName()
local mailboxData = addon.Modules.DB:GetCharacterMailbox(charFullName)
-- Extract character name from fullName
local charName = charFullName
local dashPos = string.find(charFullName, "-")
if dashPos then
charName = string.sub(charFullName, 1, dashPos - 1)
end
getglobal("Guda_MailboxFrame_Title"):SetText(charName .. "'s Mailbox")
-- Filter items based on search text
local filteredItems = {}
for i, mail in ipairs(mailboxData) do
local matchesSearch = true
if searchText ~= "" then
matchesSearch = false
if mail.sender and string.find(string.lower(mail.sender), searchText) then
matchesSearch = true
elseif mail.subject and string.find(string.lower(mail.subject), searchText) then
matchesSearch = true
elseif mail.item and mail.item.name and string.find(string.lower(mail.item.name), searchText) then
matchesSearch = true
end
end
if matchesSearch then
table.insert(filteredItems, mail)
end
end
-- Display items
self:DisplayItems(filteredItems, charFullName)
-- Update footer info
local totalItems = table.getn(mailboxData)
local displayedItems = table.getn(filteredItems)
local footerText = string.format("Items: %d", totalItems)
if searchText ~= "" then
footerText = string.format("Filtered: %d / %d", displayedItems, totalItems)
end
getglobal("Guda_MailboxFrame_Footer_Text"):SetText(footerText)
-- Update money (total money in mailbox)
local totalMoney = 0
for _, mail in ipairs(mailboxData) do
totalMoney = totalMoney + (mail.money or 0)
end
MoneyFrame_Update("Guda_MailboxFrame_MoneyFrame", totalMoney)
end
-- Display mailbox items in a grid
function MailboxFrame:DisplayItems(items, charFullName)
local container = getglobal("Guda_MailboxFrame_ItemContainer")
-- Explicitly set container ID to -100 to avoid being picked up as a bag
if container.SetID then container:SetID(-100) end
local columns = 10
local buttonSize = 34
local spacing = 2
-- Hide all existing buttons first
for _, button in pairs(itemButtons) do
button:Hide()
button.inUse = false
end
local row = 0
local col = 0
for i, mail in ipairs(items) do
local button = itemButtons[i]
if not button then
button = Guda_GetItemButton(container)
itemButtons[i] = button
end
-- Consistently set button size for the mailbox grid
button:SetWidth(buttonSize)
button:SetHeight(buttonSize)
button:ClearAllPoints()
button:SetPoint("TOPLEFT", container, "TOPLEFT", col * (buttonSize + spacing), -row * (buttonSize + spacing))
-- Set button data
button.isBank = false
button.otherChar = charFullName
button.isMail = true -- Custom flag for mailbox items
button.inUse = true
if mail.item and (mail.item.texture or mail.item.link) then
Guda_ItemButton_SetItem(button, nil, nil, mail.item, false, charFullName, true, true)
-- Re-enforce size because SetItem overrides it with global settings
button:SetWidth(buttonSize)
button:SetHeight(buttonSize)
button.isMail = true -- Re-apply after SetItem clears it
else
-- Use SetItem with nil itemData to clear the button properly first
Guda_ItemButton_SetItem(button, nil, nil, nil, false, charFullName, true, true)
button:SetWidth(buttonSize)
button:SetHeight(buttonSize)
button.isMail = true -- Re-apply after SetItem clears it
-- Then show our custom icon for money/mail
button.itemData = nil
local icon = getglobal(button:GetName().."IconTexture") or getglobal(button:GetName().."Icon")
if icon then
if (mail.money or 0) > 0 then
icon:SetTexture("Interface\\Icons\\INV_Misc_Coin_01")
elseif mail.packageIcon then
icon:SetTexture(mail.packageIcon)
else
icon:SetTexture("Interface\\Icons\\INV_Letter_15")
end
icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
icon:Show()
end
if button.qualityBorder then button.qualityBorder:Hide() end
end
-- Custom Tooltip for mail items
button.mailData = mail -- Store data on button to avoid closure issues in Lua 5.0
button.isMail = true -- Redundant set to be safe
button:SetScript("OnEnter", function()
-- Force detachment from Blizzard logic inside the closure as well
if this.SetID then this:SetID(0) end
this.isMail = true
local mailData = this.mailData
if not mailData then return end
-- Explicitly use GameTooltip:ClearLines() and SetOwner to ensure a clean tooltip
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:ClearLines()
if mailData.item and mailData.item.link then
GameTooltip:SetHyperlink(mailData.item.link)
-- Add a gap if there's also sender/subject info to show
GameTooltip:AddLine(" ")
elseif mailData.item and mailData.item.name then
-- Fallback if we have item data but no link (e.g. not in cache yet)
GameTooltip:AddLine(mailData.item.name, 1, 1, 1)
GameTooltip:AddLine(" ")
end
GameTooltip:AddLine("From: " .. (mailData.sender or "Unknown"), 1, 1, 1)
GameTooltip:AddLine("Subject: " .. (mailData.subject or "No Subject"), 1, 1, 0.8)
if (mailData.money or 0) > 0 then
GameTooltip:AddLine("Money: " .. addon.Modules.Utils:FormatMoney(mailData.money), 1, 1, 1)
end
if (mailData.CODAmount or 0) > 0 then
GameTooltip:AddLine("COD: " .. addon.Modules.Utils:FormatMoney(mailData.CODAmount), 1, 0, 0)
end
if mailData.daysLeft then
GameTooltip:AddLine("Days left: " .. math.floor(mailData.daysLeft), 0.5, 0.5, 0.5)
end
GameTooltip:Show()
end)
button:SetScript("OnLeave", function()
GameTooltip:Hide()
end)
-- Mailbox is read-only, so no clicking/dragging
button:RegisterForClicks()
button:SetScript("OnClick", nil)
button:SetScript("OnDragStart", nil)
button:SetScript("OnReceiveDrag", nil)
button:Show()
col = col + 1
if col >= columns then
col = 0
row = row + 1
end
end
-- Adjust container height
local totalRows = row + (col > 0 and 1 or 0)
container:SetHeight(math.max(420, totalRows * (buttonSize + spacing)))
end
-- Search text changed
function Guda_MailboxFrame_OnSearchTextChanged()
local searchBox = getglobal("Guda_MailboxFrame_SearchBar_SearchBox")
local text = searchBox:GetText()
if text == "Search mailbox..." then
searchText = ""
else
searchText = string.lower(text)
end
MailboxFrame:Update()
end
-- Show character selection menu
function Guda_MailboxFrame_ShowCharacterMenu()
local characters = addon.Modules.DB:GetAllCharacters(true, true)
local menu = {}
for i, char in ipairs(characters) do
local charFullName = char.fullName
table.insert(menu, {
text = char.name,
func = function() MailboxFrame:ShowCharacter(charFullName) end,
checked = (currentViewChar == char.fullName or (not currentViewChar and char.fullName == addon.Modules.DB:GetPlayerFullName()))
})
end
-- EasyMenu is available in 1.12.1
local menuFrame = CreateFrame("Frame", "Guda_MailboxCharacterMenu", UIParent, "UIDropDownMenuTemplate")
EasyMenu(menu, menuFrame, "cursor", 0, 0, "MENU")
end
+244
View File
@@ -0,0 +1,244 @@
<Ui xmlns="http://www.blizzard.com/wow/ui/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/">
<!-- Mailbox Frame -->
<Frame name="Guda_MailboxFrame" toplevel="true" movable="true" enableMouse="true" hidden="true" parent="UIParent">
<Size>
<AbsDimension x="420" y="540"/>
</Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER" relativeTo="UIParent">
<Offset>
<AbsDimension x="0" y="0"/>
</Offset>
</Anchor>
</Anchors>
<Backdrop bgFile="Interface\ChatFrame\ChatFrameBackground" edgeFile="Interface\DialogFrame\UI-DialogBox-Border" tile="true">
<BackgroundInsets>
<AbsInset left="11" right="12" top="12" bottom="11"/>
</BackgroundInsets>
<TileSize>
<AbsValue val="16"/>
</TileSize>
<EdgeSize>
<AbsValue val="32"/>
</EdgeSize>
</Backdrop>
<Layers>
<!-- Title at top -->
<Layer level="ARTWORK">
<FontString name="$parent_Title" inherits="GameFontNormalLarge">
<Anchors>
<Anchor point="TOP">
<Offset>
<AbsDimension x="0" y="-12"/>
</Offset>
</Anchor>
</Anchors>
</FontString>
</Layer>
</Layers>
<Frames>
<!-- Close Button -->
<Button name="$parent_CloseButton" inherits="UIPanelCloseButton">
<Anchors>
<Anchor point="TOPRIGHT">
<Offset>
<AbsDimension x="-16" y="-10"/>
</Offset>
</Anchor>
</Anchors>
<Scripts>
<OnClick>
Guda_MailboxFrame:Hide()
</OnClick>
</Scripts>
</Button>
<!-- Search Bar -->
<Frame name="$parent_SearchBar" inherits="OptionFrameBoxTemplate">
<Size>
<AbsDimension x="250" y="32"/>
</Size>
<Anchors>
<Anchor point="TOPLEFT">
<Offset>
<AbsDimension x="20" y="-35"/>
</Offset>
</Anchor>
</Anchors>
<Frames>
<EditBox name="$parent_SearchBox" autoFocus="false" inherits="InputBoxTemplate">
<Size>
<AbsDimension x="240" y="20"/>
</Size>
<Anchors>
<Anchor point="LEFT">
<Offset>
<AbsDimension x="5" y="0"/>
</Offset>
</Anchor>
</Anchors>
<Scripts>
<OnTextChanged>
Guda_MailboxFrame_OnSearchTextChanged()
</OnTextChanged>
<OnEscapePressed>
this:ClearFocus()
</OnEscapePressed>
<OnEnterPressed>
this:ClearFocus()
</OnEnterPressed>
<OnEditFocusGained>
if this:GetText() == "Search mailbox..." then
this:SetText("")
this:SetTextColor(1, 1, 1, 1)
end
if Guda_MailboxClickCatcher then
Guda_MailboxClickCatcher:Show()
end
</OnEditFocusGained>
<OnEditFocusLost>
if this:GetText() == "" then
this:SetText("Search mailbox...")
this:SetTextColor(0.5, 0.5, 0.5, 1)
end
if Guda_MailboxClickCatcher then
Guda_MailboxClickCatcher:Hide()
end
</OnEditFocusLost>
</Scripts>
</EditBox>
</Frames>
<Scripts>
<OnLoad>
this:SetBackdropBorderColor(0.4, 0.4, 0.4)
this:SetBackdropColor(0.15, 0.15, 0.15, 0.5)
</OnLoad>
</Scripts>
</Frame>
<!-- Character Selection Button -->
<Button name="$parent_CharacterButton">
<Size>
<AbsDimension x="24" y="24"/>
</Size>
<Anchors>
<Anchor point="TOPRIGHT">
<Offset>
<AbsDimension x="-20" y="-40"/>
</Offset>
</Anchor>
</Anchors>
<Layers>
<Layer level="ARTWORK">
<Texture name="$parent_Icon">
<Size>
<AbsDimension x="20" y="20"/>
</Size>
<Anchors>
<Anchor point="CENTER"/>
</Anchors>
<Color r="0.8" g="0.8" b="0.8"/>
</Texture>
</Layer>
</Layers>
<Scripts>
<OnLoad>
local icon = getglobal(this:GetName().."_Icon")
if icon then
icon:SetTexture("Interface\\AddOns\\Guda\\Assets\\All_Characters")
end
</OnLoad>
<OnClick>
Guda_MailboxFrame_ShowCharacterMenu()
</OnClick>
<OnEnter>
GameTooltip:SetOwner(this, "ANCHOR_TOP")
GameTooltip:SetText("Switch Character")
GameTooltip:Show()
</OnEnter>
<OnLeave>
GameTooltip:Hide()
</OnLeave>
</Scripts>
</Button>
<!-- Scroll Frame for Items -->
<ScrollFrame name="$parent_ScrollFrame" inherits="UIPanelScrollFrameTemplate">
<Size>
<AbsDimension x="360" y="420"/>
</Size>
<Anchors>
<Anchor point="TOPLEFT">
<Offset>
<AbsDimension x="20" y="-75"/>
</Offset>
</Anchor>
</Anchors>
<ScrollChild>
<Frame name="Guda_MailboxFrame_ItemContainer">
<Size>
<AbsDimension x="360" y="420"/>
</Size>
<Anchors>
<Anchor point="TOPLEFT"/>
</Anchors>
</Frame>
</ScrollChild>
</ScrollFrame>
<!-- Footer for stats/info -->
<Frame name="$parent_Footer">
<Size>
<AbsDimension x="380" y="20"/>
</Size>
<Anchors>
<Anchor point="BOTTOMLEFT">
<Offset>
<AbsDimension x="20" y="15"/>
</Offset>
</Anchor>
</Anchors>
<Layers>
<Layer level="ARTWORK">
<FontString name="$parent_Text" inherits="GameFontHighlightSmall" justifyH="LEFT">
<Anchors>
<Anchor point="LEFT"/>
</Anchors>
</FontString>
</Layer>
</Layers>
</Frame>
<Frame name="$parent_MoneyFrame" inherits="SmallMoneyFrameTemplate">
<Anchors>
<Anchor point="BOTTOMRIGHT">
<Offset>
<AbsDimension x="-15" y="10"/>
</Offset>
</Anchor>
</Anchors>
</Frame>
</Frames>
<Scripts>
<OnLoad>
Guda_MailboxFrame_OnLoad(this)
</OnLoad>
<OnShow>
Guda_MailboxFrame_OnShow(this)
</OnShow>
<OnMouseDown>
this:StartMoving()
</OnMouseDown>
<OnMouseUp>
this:StopMovingOrSizing()
</OnMouseUp>
</Scripts>
</Frame>
</Ui>