Update Text in a font string?

I have an options menu (invoked via minimap icon) with a fontstring that needs periodic updating. Specifically, this particular string shows an inventory bag’s count of free slots (Returned from C_Container.GetContainerNumFreeSlots()).

Here is the code that creates and initializes the fontstring that will later need updating.

bag.Caption = f:CreateFontString(nil, ARTWORK, GameFontNormal)
bag.Caption:SetJustifyH(LEFT)
bag.Caption:SetPoint(LEFT, bag.xPos + (BUTTON_WIDTH + 30), bag.yPos )
bag.Caption:SetFormattedText(%s: %d %s, bag.BagName, bag.NumFreeSlots, L[AVAILABLE_SLOTS] )

Now, when the bag’s count of free slots changes, I want to modify the fontstring to reflect an updated count of the bag’s free slots. The code that updates the fontstring resides in the event handlers for “BAG_UPDATE” and “BAG_UPDATE_DELAYED” are shown below. However, it doesn’t work because I cannot figure out how to erase/clear the previous text. I’ve tried variations of the code below, including just recreating the fontstring.

		if bag.NumFreeSlots ~= newFreeSlotCount then
			bag.NumFreeSlots = newFreeSlotCount
			bag.Caption:SetFormattedText( "%s: %d %s", bag.BagName, bag.NumFreeSlots, L["AVAILABLE_SLOTS"] )
		end

So, my question is this: how do I clear the previous text so that it doesn’t show through from under the new, updated text?

Thanks in advance,

My guess is that as it stands, you’re creating a new FontString for each display.

Create the FontString only once (before your updating code) and after that use bag.Caption:SetText(...) to change the contents.

I’ve done that. The font string is initialized once in BagCreateIcon() then is updated by the update code in the two BAG_UPDATE handlers. The problem is that the update code,

			if bag.NumFreeSlots ~= freeSlots then
				bag.NumFreeSlots = freeSlots
				bag.Caption:SetFormattedText( "%s: %d %s", bag.BagName, freeSlots, L["AVAILABLE_SLOTS"] )
			end

overwrites, but doesn’t clear or erase, the previous string. Thus, both string values show up - the previous string and the updated string. I’ve tried erasing/clearing the older string before executing the new, updated string as in the snippet below (and as you’ve suggested),

		if bag.NumFreeSlots ~= freeSlots then
			bag.NumFreeSlots = freeSlots
			bag.Caption:SetFormattedText( "%s", "")
			bag.Caption:SetFormattedText( "%s: %d %s", bag.BagName, bag.NumFreeSlots, L["AVAILABLE_SLOTS"] )
		end

But no joy.

Where/how is that being called?

SetText/SetFormattedText replaces the text in a fontstring, it doesn’t write on top of previous text hence the (only) likelyhood of multiple FontStrings being created on top of each other.

CreateFontString is only called from within createBagIcon().

local function createBagIcon( f, bagSlot )

    local bagFrame = CreateFrame("Button","CHACHING_BagButton", f,"TooltipBackdropTemplate")
    ...
    bagFrame.Caption = f:CreateFontString(nil, "ARTWORK", "GameFontNormal") -- @@
    bagFrame.Caption:SetJustifyH("LEFT")
    bagFrame.Caption:SetPoint("LEFT", bagFrame.xPos + (BUTTON_WIDTH + 30), bagFrame.yPos )
    bagFrame.Caption:SetFormattedText("%s: %d %s", bagFrame.BagName, bagFrame.NumFreeSlots, L["AVAILABLE_SLOTS"] )
    ...
       return bagFrame
end

Here is how createBagIcon is called from createOptionsPanel(), once for each bag installed in an inventory slot (from 0 to 4).

local function createOptionsPanel()
	local frame = CreateFrame("Frame", L["OPTIONS_MENU_TITLE"], UIParent, BackdropTemplateMixin and "BackdropTemplate")
    ...
	frame.Bags = {}	
	for i = 0, 4 do
		local bagName = C_Container.GetBagName( i )
		if bagName ~= nil then
			local freeSlots = C_Container.GetContainerNumFreeSlots( i )
			local bagFrame = createBagIcon( frame, i )
			frame.Bags[i + 1] = bagFrame
		end
	end
    ...
end

And here is the updateOptionsPanel() which is only called from the BAG_UPDATE and BAG_UPDATE_DELAY event handlers. Note that the addon only called from the handler code.

local function updateOptionsPanel()	
	for i = 0, 4 do
		local bagName = C_Container.GetBagName( i )
		if bagName ~= nil then
			-- if the number of free slots has changed, then update the caption
			local bag = optionsPanel.Bags[i+1]
			local freeSlots = C_Container.GetContainerNumFreeSlots(i)

			if bag.NumFreeSlots ~= freeSlots then
				bag.NumFreeSlots = freeSlots
				bag.Caption:SetFormattedText( "%s: %d %s", bag.BagName, freeSlots, L["AVAILABLE_SLOTS"] )
			end
			inventorySlot[i+1] = OCCUPIED		-- probably redundant
		end 
	end
end

i.e.,

    if event == "BAG_UPDATE" or event == "BAG_UPDATE_DELAYED" then
		updateOptionsPanel()
	end

Here’s what the text look like after buying 3 items.

https://photos.google.com/photo/AF1QipPfFRnQzQu1NeIKsVDzNDCgffh3F8kFlzo8k0Zd

And where is createOptionsPanel being called? Is it only run once ie. not each time you show the options panel/click a button/enter a slash command?

Probably the quickest test would be to change

    bagFrame.Caption = f:CreateFontString(nil, "ARTWORK", "GameFontNormal") -- @@
    bagFrame.Caption:SetJustifyH("LEFT")

to:

    bagFrame.Caption = f:CreateFontString(nil, "ARTWORK", "GameFontNormal") -- @@
    print("FontString created for:", f:GetName())
    bagFrame.Caption:SetJustifyH("LEFT")

The print should only run once for each bag.

Also assumes you don’t have another process also creating FontStrings.

Thanks for the suggestion. I found that even though I had 4 bags installed (5 including the backpack) the addon was calling CreateFontString() 7 times during initialization. What I discovered was that in the for-loop (for i = 0, 4) the that last 3 bags were had not yet been loaded. The next time the code updated the bags, it ran correctly and the bug was fixed.

Very grateful. The addon now passes all of my tests.

1 Like