Addon UI Help: Why doesn't the "image" inside of "text" animate with the text?

What follows is a very simple addon file that displays looted items and such near the center of the screen in a sleek animated fade/move-in, fade/move-out kind of way.

The problem is that when a looted item contains a graphical element (such as the quality of a crafting reagent), the graphical element DOES NOT animate along with the rest of the parent frame being animated.

What am I doing wrong? Or is this just a bug?

Here’s the code in question:

---------------------------------------------------------------------
-- Initialization
---------------------------------------------------------------------
local CasualUI, addon = ...
addon.Loot = addon.Loot or CreateFrame("Frame", "CasualUI.Loot", UIParent)
addon.Loot.Items = addon.Loot.Items or CreateFrame("Frame", nil, addon.Loot)

addon.Loot:RegisterEvent("ADDON_LOADED")
addon.Loot:RegisterEvent("CHAT_MSG_LOOT")
addon.Loot:RegisterEvent("CHAT_MSG_SKILL")
addon.Loot:RegisterEvent("CHAT_MSG_COMBAT_FACTION_CHANGE")
addon.Loot:RegisterEvent("CHAT_MSG_MONEY")

---------------------------------------------------------------------
-- Functions
---------------------------------------------------------------------
addon.Loot.GetTextFrame = addon.Loot.GetTextFrame or function()
    local items = {addon.Loot.Items:GetChildren()}

    -- Return the first available item text frame, if any
    for index, item in ipairs(items) do
        if not item.animation:IsPlaying() then
            return item, index
        end
    end

    -- Otherwise, create a new item text frame to return
    local newItem = CreateFrame("Frame", nil, addon.Loot.Items)
    newItem:SetPoint("LEFT", UIParent, "CENTER")
    newItem:SetSize(GetScreenWidth() / 2, 50)
    newItem:SetAlpha(1)
    newItem:Hide()
    newItem.text = newItem:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    newItem.text:SetPoint("LEFT", newItem, "LEFT")
    newItem.text:SetTextColor(1, 1, 1, 1)
    newItem.text:SetTextScale(1.5)
    newItem.text:SetWordWrap(false)
    newItem.animation = newItem:CreateAnimationGroup()
    newItem.animation:SetScript("OnFinished", function()
        newItem.text:SetText("")
        newItem:Hide()
    end)
    newItem.duration = 1.5

    newItem.startFaded = newItem.animation:CreateAnimation("Alpha")
    newItem.startFaded:SetFromAlpha(0)
    newItem.startFaded:SetToAlpha(0)
    newItem.startFaded:SetDuration(0)

    newItem.fadeIn = newItem.animation:CreateAnimation("Alpha")
    newItem.fadeIn:SetStartDelay(0)
    newItem.fadeIn:SetFromAlpha(0)
    newItem.fadeIn:SetToAlpha(1)
    newItem.fadeIn:SetDuration(0.5)
    newItem.fadeIn:SetSmoothing("OUT")

    newItem.fadeOut = newItem.animation:CreateAnimation("Alpha")
    newItem.fadeOut:SetStartDelay(1.5)
    newItem.fadeOut:SetFromAlpha(1)
    newItem.fadeOut:SetToAlpha(0)
    newItem.fadeOut:SetDuration(0.5)
    newItem.fadeOut:SetSmoothing("IN")

    newItem.moveIn = newItem.animation:CreateAnimation("Translation")
    newItem.moveIn:SetStartDelay(0)
    newItem.moveIn:SetOffset(50, 50)
    newItem.moveIn:SetDuration(0.5)
    newItem.moveIn:SetSmoothing("OUT")

    newItem.moveOut = newItem.animation:CreateAnimation("Translation")
    newItem.moveOut:SetStartDelay(1.5)
    newItem.moveOut:SetOffset(25, 25)
    newItem.moveOut:SetDuration(0.5)
    newItem.moveOut:SetSmoothing("IN")

    newItem.delay = 0
    newItem.UpdateDelay = function(self, newDelay)
        newItem.delay = newDelay
        newItem.fadeIn:SetStartDelay(newDelay)
        newItem.fadeOut:SetStartDelay(newDelay + newItem.duration)
        newItem.moveIn:SetStartDelay(newDelay)
        newItem.moveOut:SetStartDelay(newDelay + newItem.duration)
    end

    return newItem, addon.Loot.Items:GetNumChildren()
end

addon.Loot.Reformat = addon.Loot.Reformat or function(self, eventType, text)
    if eventType == "CHAT_MSG_LOOT" then
        local reformattedText =  text
            :gsub("You receive loot: ", "")
            :gsub("You receive currency: ", "")
            :gsub("%[", "")
            :gsub("%]", "")
            :gsub("x%d+$", " (%1)")
            :gsub("(x", "")
        return reformattedText
    end

    return text
end

addon.Loot.DisplayText = addon.Loot.DisplayText or function(self, eventType, text)
    local textFrame, textFrameIndex = addon.Loot:GetTextFrame()
    local reformattedText = addon.Loot:Reformat(eventType, text)

    textFrame.text:SetPoint("LEFT", textFrame, "LEFT", 0, ((textFrameIndex - 1) * 25))
    textFrame.text:SetText(reformattedText)
    textFrame:UpdateDelay((textFrameIndex - 1) * 0.1)
    textFrame:Show()
    textFrame.animation:Play()
end

---------------------------------------------------------------------
-- Events
---------------------------------------------------------------------
addon.Loot:SetScript("OnEvent", function(self, event, ...)
    if event == "ADDON_LOADED" then
        local addonName = ...
        if addonName == CasualUI then
            SetCVar("autoLootDefault", 1)
            SetCVar("lootUnderMouse", 1)
            SetCVar("lootPickup", 1)
            self:UnregisterEvent(event)
        end
    elseif event ~= "ADDON_LOADED" then
        self:DisplayText(event, ...)
    end
end)

Textures in fontstrings made from escape sequences don’t always like to “follow” the fontstring. They are slowly fixing it in places, like it used to be that doing a button:SetText(“\124TInterface\etc\124t Click Me”) would have the text push button the texture remain in place. (Or maybe it’s just a UIPanelButtonTemplate that fixes this, I haven’t verified)

A workaround is to attach a texture to the fontstring. For your code, possibly create a newItem.icon anchored to the left of the fontstring, and then in your replace bit have it look for \124T.-124t or \124A.-124a (named vs atlas) and strip that out and pass it as a second return to set the icon.

Thanks Gello, the idea of that approach seems to be working well.

Here’s the updated code:

---------------------------------------------------------------------
-- Initialization
---------------------------------------------------------------------
local CasualUI, addon = ...
addon.Loot = addon.Loot or CreateFrame("Frame", "CasualUI.Loot", UIParent)
addon.Loot.Items = addon.Loot.Items or CreateFrame("Frame", nil, addon.Loot)

addon.Loot:RegisterEvent("ADDON_LOADED")
addon.Loot:RegisterEvent("CHAT_MSG_LOOT")
addon.Loot:RegisterEvent("CHAT_MSG_SKILL")
addon.Loot:RegisterEvent("CHAT_MSG_COMBAT_FACTION_CHANGE")
addon.Loot:RegisterEvent("CHAT_MSG_MONEY")

addon.Loot.icons = addon.Loot.icons or {
    ["Professions-ChatIcon-Quality-Tier1"] = {
        fileID = 4723389,
        texCoords = {0.094, 0.119, 0.92, 0.97},
    },
    ["Professions-ChatIcon-Quality-Tier2"] = {
        fileID = 4723392,
        texCoords = {0.094, 0.119, 0.92, 0.97},
    },
    ["Professions-ChatIcon-Quality-Tier3"] = {
        fileID = 4723395,
        texCoords = {0.094, 0.119, 0.92, 0.97},
    },
    ["Professions-ChatIcon-Quality-Tier4"] = {
        fileID = 4723398,
        texCoords = {0.094, 0.119, 0.92, 0.97},
    },
    ["Professions-ChatIcon-Quality-Tier5"] = {
        fileID = 4723401,
        texCoords = {0.094, 0.119, 0.92, 0.97},
    },
}

---------------------------------------------------------------------
-- Functions
---------------------------------------------------------------------
addon.Loot.GetTextFrame = addon.Loot.GetTextFrame or function()
    local items = {addon.Loot.Items:GetChildren()}

    -- Return the first available item text frame, if any
    for index, item in ipairs(items) do
        if not item.animation:IsPlaying() then
            return item, index
        end
    end

    -- Otherwise, create a new item text frame to return
    local newItem = CreateFrame("Frame", nil, addon.Loot.Items)
    newItem:SetPoint("LEFT", UIParent, "CENTER")
    newItem:SetSize(GetScreenWidth() / 2, 50)
    newItem:SetAlpha(1)
    newItem:Hide()

    newItem.icon = newItem:CreateTexture(nil, "BACKGROUND") -- used for images in text
    newItem.icon:SetPoint("RIGHT", newItem, "LEFT", -5, 0)
    newItem.icon:SetSize(16, 16)
    newItem.icon:SetTexture("")

    newItem.text = newItem:CreateFontString(nil, "OVERLAY", "GameFontNormal")
    newItem.text:SetPoint("LEFT", newItem, "LEFT")
    newItem.text:SetTextColor(1, 1, 1, 1)
    newItem.text:SetTextScale(1.5)
    newItem.text:SetWordWrap(false)

    newItem.animation = newItem:CreateAnimationGroup()
    newItem.animation:SetScript("OnFinished", function()
    newItem.text:SetText("")
    newItem.icon:SetTexture("")
    newItem:Hide()
    end)

    newItem.duration = 1.5

    newItem.startFaded = newItem.animation:CreateAnimation("Alpha")
    newItem.startFaded:SetFromAlpha(0)
    newItem.startFaded:SetToAlpha(0)
    newItem.startFaded:SetDuration(0)

    newItem.fadeIn = newItem.animation:CreateAnimation("Alpha")
    newItem.fadeIn:SetStartDelay(0)
    newItem.fadeIn:SetFromAlpha(0)
    newItem.fadeIn:SetToAlpha(1)
    newItem.fadeIn:SetDuration(0.5)
    newItem.fadeIn:SetSmoothing("OUT")

    newItem.fadeOut = newItem.animation:CreateAnimation("Alpha")
    newItem.fadeOut:SetStartDelay(1.5)
    newItem.fadeOut:SetFromAlpha(1)
    newItem.fadeOut:SetToAlpha(0)
    newItem.fadeOut:SetDuration(0.5)
    newItem.fadeOut:SetSmoothing("IN")

    newItem.moveIn = newItem.animation:CreateAnimation("Translation")
    newItem.moveIn:SetStartDelay(0)
    newItem.moveIn:SetOffset(50, 0)
    newItem.moveIn:SetDuration(0.5)
    newItem.moveIn:SetSmoothing("OUT")

    newItem.moveOut = newItem.animation:CreateAnimation("Translation")
    newItem.moveOut:SetStartDelay(1.5)
    newItem.moveOut:SetOffset(25, 0)
    newItem.moveOut:SetDuration(0.5)
    newItem.moveOut:SetSmoothing("IN")

    newItem.delay = 0
    newItem.UpdateDelay = function(self, newDelay)
        newItem.delay = newDelay
        newItem.fadeIn:SetStartDelay(newDelay)
        newItem.fadeOut:SetStartDelay(newDelay + newItem.duration)
        newItem.moveIn:SetStartDelay(newDelay)
        newItem.moveOut:SetStartDelay(newDelay + newItem.duration)
    end

    return newItem, addon.Loot.Items:GetNumChildren()
end

addon.Loot.ExtractIcon = addon.Loot.ExtractIcon or function(textFrame, text)
    if text then
        local iconReference = text:match("|A:(.-):")

        if iconReference and addon.Loot.icons[iconReference] then
            textFrame.icon:SetTexture(addon.Loot.icons[iconReference].fileID)
            textFrame.icon:SetTexCoord(unpack(addon.Loot.icons[iconReference].texCoords))
        end
    end
end

addon.Loot.Reformat = addon.Loot.Reformat or function(self, eventType, text)
    if eventType == "CHAT_MSG_LOOT" then
        local reformattedText =  text
            :gsub("You receive loot: ", "")
            :gsub("You receive currency: ", "")
            :gsub("You receive item: ", "")
            :gsub("%[", "")
            :gsub("|A.-|a", "")     -- remove inline icons
            :gsub("%]", "")
            :gsub("x%d+$", " (%1)") -- change "x2" into " (2)"
            :gsub("(x", "")         -- remove excess "(x"
        return reformattedText
    end

    return text
end

addon.Loot.DisplayText = addon.Loot.DisplayText or function(self, eventType, text)
    local textFrame, textFrameIndex = addon.Loot:GetTextFrame()
    local reformattedText = addon.Loot:Reformat(eventType, text)

    addon.Loot.ExtractIcon(textFrame, text)

    textFrame.text:SetPoint("LEFT", textFrame, "LEFT", 0, ((textFrameIndex - 1) * 25))
    textFrame.text:SetText(reformattedText)
    textFrame:UpdateDelay((textFrameIndex - 1) * 0.1)
    textFrame:Show()
    textFrame.animation:Play()
end

---------------------------------------------------------------------
-- Events
---------------------------------------------------------------------
addon.Loot:SetScript("OnEvent", function(self, event, ...)
    if event == "ADDON_LOADED" then
        local addonName = ...
        if addonName == CasualUI then
            SetCVar("autoLootDefault", 1)
            SetCVar("lootUnderMouse", 1)
            SetCVar("lootPickup", 1)
            self:UnregisterEvent(event)
            addon.Loot:GetTextFrame()
        end
    elseif event ~= "ADDON_LOADED" then
        self:DisplayText(event, ...)
    end
end)