Question about custom AddOn Interfaces

Full disclosure, extremely new to AddOn development, and LUA / XML in general.
I’ve got a rough idea for what I want this to project to look like, but I’ve got no idea how to get the end result of this GUI.

A kind of rough idea of what I’m hoping to do with this would be this:
i.imgur
.com/1rq0YMJ.jpg
(Had to put separate because of ruling)

Effectively, what I’d probably call the main window, with four children, I suppose I’d be calling them?
The title box on top, and three tab boxes on the bottom to switch between views in the main window.
Then for one of the tabs, I’d also like to have a left side sectioned off to have some sorting options for what content is displayed. (So I guess, almost like tabs, for the one window?)

A general test case to get my hands dirty was to basically just do something along the lines of, have a main window with 3 tabs.
In each tab, the window will have a text field that says something like “/dance”
Once you do that, it will change to “Complete!” or something, just for kind of proof of concept I guess?

Unfortunately, I don’t even know where to begin for the most part.
Would it be smarter to use exclusively premade XML elements from the BlizzardInterfaceCode folder?

Is there perhaps a good guide I can try to shoot for?
I’ve not had much luck in the way of finding resources other than a couple older books. But I have a bit of trouble sitting down to read large amounts of information at once, so I figured I’d see about other resources as well.

Any and all recommendations are appreciated, either here, or through whispers on BNet via TheGnomeKing#1998
Thank you.

Full disclosure, this is really basic, only uses in-game assets and is just a hint of what you can do (time being what it is).

With the exception of secure templates being required to create certain “controlled substances” (Action Buttons, Unit Frames etc.) XML is not otherwise required so it is personal preference if you use it to create your frames or not.

There are a whole lot of templates created using XML but these can also be used in lua created frames, this example uses one of them for the buttons.

You can paste the following into the website addon.bool.no to create a simple addon consisting of a frame with a game standard border and two standard template based buttons. (The addon will have a basic .toc file required for all addons which must have the same name as the folder the addon is in.)

local f = CreateFrame("Frame", "ScrabbleTestFame", UIParent)
f:SetSize(200, 200)
f:SetPoint("CENTER")
local backdrop = {
	bgFile = "Interface/BUTTONS/WHITE8X8",
	edgeFile = "Interface/DialogFrame/UI-DialogBox-Border",
	tile = true,
	edgeSize = 32,
	tileSize = 16,
	insets = {
		left = 11,
		right = 11,
		top = 12,
		bottom = 10,
	},
}
f:SetBackdrop(backdrop)
f:SetBackdropColor(0, 0, 0)

f.Tab1 = CreateFrame("Button", "$parentTab1", f, "UIPanelButtonTemplate")
f.Tab1:SetSize(44, 22)
f.Tab1:SetText("Tab 1")
f.Tab1:SetPoint("BOTTOMLEFT", 10, 10)
f.Tab1:SetScript("OnClick", function(self)
	print("Tab 1 Clicked!")
end)

f.Tab2 = CreateFrame("Button", "$parentTab2", f, "UIPanelButtonTemplate")
f.Tab2:SetSize(44, 22)
f.Tab2:SetText("Tab 2")
f.Tab2:SetPoint("LEFT", f.Tab1, "RIGHT")
f.Tab2:SetScript("OnClick", function(self)
	print("Tab 2 Clicked!")
end)

f.Title = CreateFrame("Frame", "$parentTitle", f)
f.Title:SetSize(150, 50)
f.Title:SetBackdrop(f:GetBackdrop())
f.Title:SetBackdropColor(0, 0, 0)
f.Title:SetPoint("BOTTOM", f, "TOP", 0, -8)
f.Title.Text = f.Title:CreateFontString("$parentText")
f.Title.Text:SetFont("Fonts\\FRIZQT__.TTF", 11, "OUTLINE, MONOCHROME")
f.Title.Text:SetPoint("CENTER")
f.Title.Text:SetText("Scrabble's Addon")

In this code I’ve used the buttons OnClick scripts to just print some text to the chat window not exciting but gives a very small idea of how you might create other objects (frames, buttons, fontstrings etc.) and arrange them to build up your addon.

The addon sreates a frame as a child of UIParent (the frame that hides everything when you Alt-Z). Sets its size and location (CENTER and because no relative frame passed in the SetPoint function, the parent is used ie. center of screen).

It uses a backdrop to display the border and background with game supplied images but you could also create one or more child Texture regions with custom images instead)

f.Tab1 and f.Tab2 are two default game style buttons created using the UIPanelButtonTemplate. The template supplies the button images (normal, highlight, pressed) and fontstring used to display the text.

The SetPoints for each button “Anchor” them to the parent frame with f.Tab1 to the bottom left of the frame itself and f.Tab2 anchors it’s left side to f.Tab1’s right side.

The books are still a good starting source but are large for a reason, there’s a lot to this, but once you start to get a handle how the bits work together it becomes much easier.

That is a lot more help than I expected! Thank you tremendously.
I’ll definitely try to piece this apart and see what all I can do with it and start making some progress towards what I had in mind.

I honestly hadn’t really considered doing it without custom frames, but at the very least making something with LUA to give me a basis to work with for a while seems, well, smart enough to me at least.
Who knows I may even end up preferring it. (For laziness sake, or not)

This definitely kind of gives me some room to think and play and at least get a feel for things.

Would you have a particular Book recommendation? I was looking over World of Warcraft Programming Second Addition, James Whitehead II & Rick Roe
But I honestly have not gotten very deep into any resources other than testing Events & general API stuff in some print/message functions.

Getting your hands dirty will be better than any book (I started with the two WoW Programming books). It really is a matter of piecing the bits together and asking questions. wow.gamepedia.com is a good resource for pasting function names, widget types, scripts, events etc. to get information of the specifics.

Extract the Blizzard game addons for code examples https://wow.gamepedia.com/Viewing_Blizzard%27s_interface_code

Same addon with a couple of small tweeks for a very different look (paste into the same or create a seperate addon).

local x = CreateFrame("Frame", "ScrabbleTestFame2", UIParent)
x:SetSize(200, 200)
x:SetPoint("CENTER", 220, 0)
local backdrop = {
	bgFile = "Interface/DialogFrame/UI-DialogBox-Background",
	edgeFile =  "",
	tile = true,
	edgeSize = 32,
	tileSize = 32,
	insets = {
		left = 0,
		right = 0,
		top = 0,
		bottom = 0,
	},
}
x:SetBackdrop(backdrop)
x:SetBackdropColor(0, 0, 0)

x.Tab1 = CreateFrame("Button", "$parentTab1", x, "ConfigCategoryButtonTemplate")
x.Tab1:SetSize(40, 16)
x.Tab1:SetText("Tab 1")
x.Tab1:SetPoint("BOTTOMLEFT", 10, 10)
x.Tab1:SetScript("OnClick", function(self)
	print("Tab 1 Clicked!")
end)

x.Tab2 = CreateFrame("Button", "$parentTab2", x, "ConfigCategoryButtonTemplate")
x.Tab2:SetSize(40, 16)
x.Tab2:SetText("Tab 2")
x.Tab2:SetPoint("LEFT", x.Tab1, "RIGHT")
x.Tab2:SetScript("OnClick", function(self)
	print("Tab 2 Clicked!")
end)

x.Title = CreateFrame("Frame", "$parentTitle", X)
x.Title:SetPoint("BOTTOMLEFT", x, "TOPLEFT")
x.Title:SetPoint("BOTTOMRIGHT", x, "TOPRIGHT")
x.Title:SetHeight(20)
local backdrop = {
	bgFile = "Interface/DialogFrame/UI-DialogBox-Background-Dark",
	edgeFile = "",
	tile = true,
	edgeSize = 32,
	tileSize = 32,
	insets = {
		left = 0,
		right = 0,
		top = 0,
		bottom = 0,
	},
}

x.Title:SetBackdrop(backdrop)
x.Title:SetBackdropColor(0, 0, 0)
x.Title.Text = x.Title:CreateFontString("$parentText")
x.Title.Text:SetFont("Fonts\\FRIZQT__.TTF", 8, "OUTLINE, MONOCHROME")
x.Title.Text:SetPoint("CENTER")
x.Title.Text:SetText("Scrabble's Addon")

I’ve had the folders extracted for a minute, just haven’t really dove head first yet. A little overwhelmed by just the amount of information in them.
Although it’s a fair point, if I want to see definitively working code it is definitely not a bad place.

You are really not wrong about it being an entirely different look though.
Primary changes seemingly being bgFile, with no edgeFile now, and then the template you use for the buttons, so far as I can tell? So it basically has no border, and you’ve changed the style of the backdrop and buttons so they fit in together?

This might be a silly question, but, is attaching the buttons to the bottom of the frame an option with just LUA? With a tab feature?

I see there’s a couple Templates for Tabs, would they be implemented the same as a button, simply with different positioning and functions?
I see in say, Blizzard’s AchievementUI.lua, something along the lines of

function AchievementFrame_SetTabs()
	AchievementFrameTab2:Show();
	AchievementFrameTab3:SetPoint("LEFT", AchievementFrameTab2, "RIGHT", -5, 0);
end

Trying to figure out what they’re doing, because I then have trouble finding their AchievementFrameTab2 and 3, maybe I’m just not looking in the right places to try to put it together.
(Or maybe jumping in a little too far looking at a piece of code like that, with uh, 3900 lines when I want one function out of it)

In my head, let’s say there’s 3 tables available.
The AddOn when loaded would load all 3 tabs, however only the one would be visible. And the tabs when clicked, similar to buttons have an event of course.
However depending on the tab clicked hides the open tab, and opens the one based on the tab clicked.
But maybe I’m oversimplifying this as well.

Again, I tremendously appreciate your help. I also really enjoy the aesthetics of the second copy of that code, despite simply it’s just nice to play with a bit.

Most examples use the steel border/red buttons for examples so it’s always nice to see you can do something different without a Phd. when starting out :wink:.

Widgets can be anchored to other widgets or to UIParent so you can anchor the top to the bottom of something else or left to right etc.

Creating tab “sheets” that have been added to a table of the parent frame (once again very basic for something to play with)

local function SwitchCheet(self, sheet)
	local parent = self:GetParent()
	if parent.last then
		parent.last:Hide()
	end
	parent.tabsheets[sheet]:Show()
	parent.last = parent.tabsheets[sheet]
end

local x = CreateFrame("Frame", "ScrabbleTestFame", UIParent)
x:SetSize(200, 200)
x:SetPoint("CENTER", 220, 0)
local backdrop = {
	bgFile = "Interface/DialogFrame/UI-DialogBox-Background",
	edgeFile =  "",
	tile = true,
	edgeSize = 32,
	tileSize = 32,
	insets = {
		left = 0,
		right = 0,
		top = 0,
		bottom = 0,
	},
}
x:SetBackdrop(backdrop)
x:SetBackdropColor(0, 0, 0)

x.Tab1 = CreateFrame("Button", "$parentTab1", x, "ConfigCategoryButtonTemplate")
x.Tab1.Id = 1
x.Tab1:SetSize(40, 16)
x.Tab1:SetText("Tab 1")
x.Tab1:SetPoint("BOTTOMLEFT", 10, 10)
x.Tab1:SetScript("OnClick", function(self)
	SwitchCheet(self, self.Id)
end)

x.Tab2 = CreateFrame("Button", "$parentTab2", x, "ConfigCategoryButtonTemplate")
x.Tab2.Id = 2
x.Tab2:SetSize(40, 16)
x.Tab2:SetText("Tab 2")
x.Tab2:SetPoint("LEFT", x.Tab1, "RIGHT")
x.Tab2:SetScript("OnClick", function(self)
	SwitchCheet(self, self.Id)
end)

x.Tab3 = CreateFrame("Button", "$parentTab3", x, "ConfigCategoryButtonTemplate")
x.Tab3.Id = 3
x.Tab3:SetSize(40, 16)
x.Tab3:SetText("Tab 2")
x.Tab3:SetPoint("LEFT", x.Tab2, "RIGHT")
x.Tab3:SetScript("OnClick", function(self)
	SwitchCheet(self, self.Id)
end)
x.tabsheets = {}
for i=1, 3 do
	x.tabsheets[i] = CreateFrame("Frame", "$parentSheet"..i, x)	
	x.tabsheets[i]:SetSize(50, 50)
	x.tabsheets[i]:SetPoint("CENTER")
	x.tabsheets[i].Background = x.tabsheets[i]:CreateTexture()
	x.tabsheets[i].Background:SetAllPoints()
	x.tabsheets[i].Background:SetTexture("Interface/BUTTONS/WHITE8X8")
	x.tabsheets[i].Background:SetVertexColor(random(0, 1), random(0, 1), random(0, 1))
	x.tabsheets[i].Text = x.tabsheets[i]:CreateFontString("$parentText")
	x.tabsheets[i].Text:SetFont("Fonts\\FRIZQT__.TTF", 12, "OUTLINE, MONOCHROME")
	x.tabsheets[i].Text:SetPoint("CENTER")
	x.tabsheets[i].Text:SetText("Sheet "..i)
	x.tabsheets[i]:Hide()
end
x.last = x.tabsheets[1]
x.tabsheets[1]:Show()
x.Title = CreateFrame("Frame", "$parentTitle", X)
x.Title:SetPoint("BOTTOMLEFT", x, "TOPLEFT")
x.Title:SetPoint("BOTTOMRIGHT", x, "TOPRIGHT")
x.Title:SetHeight(20)
local backdrop = {
	bgFile = "Interface/DialogFrame/UI-DialogBox-Background-Dark",
	edgeFile = "",
	tile = true,
	edgeSize = 32,
	tileSize = 32,
	insets = {
		left = 0,
		right = 0,
		top = 0,
		bottom = 0,
	},
}

x.Title:SetBackdrop(backdrop)
x.Title:SetBackdropColor(0, 0, 0)
x.Title.Text = x.Title:CreateFontString("$parentText")
x.Title.Text:SetFont("Fonts\\FRIZQT__.TTF", 8, "OUTLINE, MONOCHROME")
x.Title.Text:SetPoint("CENTER")
x.Title.Text:SetText("Scrabble's Addon")

Any child widget of a sheet (created with the sheet as it’s parent like the fontstrings here) will show/hide with the sheet.

What exactly does this bit accomplish?
if parent.last then
parent.last:Hide()
end
I see the x.last = x.tabsheets[1]
Which is then overwritten by the last line in the function (barring end)
This is just intended to always hide the active sheet, just to ensure it never actively has two sheets showing?

I wanted to clarify just in case I missed something.

Then your ID’s are used to dictate which is which, i=1, 3 because we have 3 sheets.
The for loop will run on startup, making each one then hiding it.
So if I wanted 3 completely different mock-ups in each one I could do
for i=1, 3 do
if (i == 1) then
– code
end
if (i == 2) then
– code
end
if (i == 3) then
– code
end
end
Or run the for loop for repetitive properties like CreateFrame, Size, Point, Background, etc. Then do the specifics in an if

And effectively, you’re creating them all as soon as you /reload or log in, but you’re simply hiding a majority of your AddOn, until it is needed though.

Alright, I think I might know enough to try the foundation for this now thanks to all the help you’ve given me.
I might end up with a couple other questions because I’m going to try to use a ScrollFrame with some other shenanigans, but, I will happily come back and try to make a follow-up on what, if anything, I accomplished with testing.

You are correct, it’s just storing the last 'shown" sheet to know which to hide before showing a newly selected sheet.

This is one simplistic possibility to show it can be done. How when you create the children of each sheet (or if you use “sheet” parent controls at all) is up to you. You might add widgets to the sheets inside the the for loop at startup or as a user chooses something in a list or some other mechanism.

A little slow from some class work and rest, it’s not much, but it’s something!

So, my Tab One was intended to be a ScrolledFrame containing multiple Frames featured in Tab Two.
However, for the life of me I could not figure out how to make it scroll so I abandoned ship on that one.

The “working” product thus far, while a little fishy, it seems to work alright.
I still need to learn how SavedVariables function to do something like store the progress on these two examples, but otherwise it’s working about like I wanted.

Although my biggest question is, how in the world do I make a ScrolledFrame work?
I seemed to be able to shove stuff in there kind of alright, although I was tossing some single-50x50 frames in there with a background for tests and not layering several things up.
But I never did get the ScrollBar to move, either by clicking or mousewheel.
(And unfortunately I just kind of dropped the code since I have no idea which part was technically failing.)

Is there a good resource to look at for that?
Because trying to piece apart examples did not quite do what I hoped, but I feel I just didn’t understand what it’s actually doing.

The World of Warcraft Programming book I’ve got access to has a section on it but primarily mentioning in XML, presumably it’s fine to use code from both though?
Figured I’d ask before I mess around with it and get frustrated with it not moving again, haha

Oh and one more thing I wanted to ask!
Would I need to do some sort of layering to ensure the opacity and all? The Text seemed to fade with the same settings when used on the Frame inside of my sheet.

About as basic as a scrollframe gets (using the build in FauxScrollFrame template and associated functions.

It has:

A table with some rows of text to display
A scrollframe
Some buttons to display the text (OnClick would process the selection etc.)
A function to update the scrolling.

The buttons could have images, other buttons etc. on them.
Code:

local listItems = {} -- Some items to display
for i=1, 30 do
	tinsert(listItems, "Line "..i)
end

local NumberOfButtons = 11 -- how many rows
local ButtonHeight = 0 -- will be set when we know some more things

local function UpdateScrollFrame(self) -- what it says
    FauxScrollFrame_Update(self, #listItems, NumberOfButtons, ButtonHeight)
    for index = 1, NumberOfButtons do
        local offset = index + FauxScrollFrame_GetOffset(TestScrollFrame)
        local button = TestScrollFrame.buttons[index]
        if index > #listItems then
            button:SetText('')
            button.index = nil
        else
            button.index = offset
            button:SetText(listItems[offset])
        end
    end
end

local ScrollFrameBackground = CreateFrame('Frame', 'FizzleScrollTest', UIParent, 'TooltipBorderedFrameTemplate') -- background
ScrollFrameBackground:SetSize(180, 140)
ScrollFrameBackground:SetPoint('LEFT', 30, 0)
 
-- scroll frame
local TestScrollFrame = CreateFrame('ScrollFrame', '$parentScrollFrame', ScrollFrameBackground, 'FauxScrollFrameTemplate')
TestScrollFrame:SetPoint('TOPLEFT', 0, -8)
TestScrollFrame:SetPoint('BOTTOMRIGHT', -30, 8)
TestScrollFrame:SetScript("OnVerticalScroll", function(self, offset)
        FauxScrollFrame_OnVerticalScroll(self, offset, ButtonHeight, UpdateScrollFrame)
end)
 
-- scroll buttons
TestScrollFrame.buttons = {}

ButtonHeight = TestScrollFrame:GetHeight()/NumberOfButtons -- we know more things
for index = 1, NumberOfButtons do -- add buttons to display/select (could use CheckButtons)
	local button = CreateFrame('Button', nil, ScrollFrameBackground)
	TestScrollFrame.buttons[index] = button
	button:SetSize(TestScrollFrame:GetWidth()-25, ButtonHeight)
	button:SetNormalFontObject('GameFontHighlightLeft')
	if index == 1 then
		button:SetPoint('TOPLEFT', TestScrollFrame, 10, 0)
	else
		button:SetPoint('TOP', TestScrollFrame.buttons[index-1], "BOTTOM")
	end
	button.currentIndex = index
end

UpdateScrollFrame(TestScrollFrame) -- initialise

Frames have Stratas and Levels:
https://wow.gamepedia.com/API_Frame_SetFrameStrata
https://wow.gamepedia.com/API_Frame_SetFrameLevel

Regions (Textures, FontStrings etc.) have DrawLayers and sub-levels:
https://wow.gamepedia.com/API_LayeredRegion_SetDrawLayer

Proper layering will help make things look right.

For my own sake, I’m going to just kind of type this out here because, trying to dissect this and ensure I actually know what’s happening.

I just wanted to say again, I appreciate the patience and help you’ve provided tremendously with all of this. I honestly have no idea how long this would’ve taken me to motivate myself to do this otherwise.

So from top to bottom:

Making your Line 1-30, to store, simple enough.

Defining how many lines show up on screen at a time
Initializing your buttonHeight

Now here’s where I’m… Interested? (Didn’t take long huh?)

So, updateScrollFrame
FauxScrollFrame_Update – Is a Blizzard made method, which takes how many items it’s going to hold, the number of actively seen elements, and the height of each.

Then for each button (going by index of your list), you give it an offset.
The offset, uses another premade Blizzard Method, which takes your ScrollFrame and does some magic?
I have no idea how it creates the offset, and this was the biggest thing confusing me before.
Ignoring that for now and moving on…
Button is just storing the currently indexed button in the set for ease of use
If to determine if you need to update a nil line and finish it off, or continue updating.

Then creating your parent background, and actual scrollframe.
OnVerticalScroll allows you to both use mousewheel, or click it?
And it takes the offset argument you made earlier, along with the buttonHeight and your update method, to effectively spam update as you scroll?

Then you have your table for buttons.
Now, for the button height, this is a stupid question but I wanted to be sure.
Say I wanted Five elements(buttons) viewable at a time. I would simply adjust NumberOfButtons to 5, and it would basically auto adjust, due to GetHeight/NumerOfButtons (giving it a smart way of saying “You can only use (x) much space per button”), presumably being what your “we know more things” means?

The size is determined by the width of the ScrollFrame, -25 to compensate for the ScrollBar presumably, as well as the buttonheight, since, well, your button needs to be in the height.

And finally, putting your first button in the topleft of your ScrollFrame, then attaching each subsequent one to the bottom of the frame above it until the end.

With a required updateScrollFrame in order to initialize it (Is there a particular reason, or just a sort of required thing?)

And of course if I wanted I could SetScript on each button, if I wanted to do something with the related index’d buttons.

You pretty much have it correct.

The FauxScrollFrame calulates the location of the thumb (up/down) in the scrollbar and along with the total items in the list, number of rows to display and height of each row calculates the beginning offset for the update function to start at in the table for the next “number of rows to display” when the thumb is scrolled by wheel, click or drag.

Nothing saying you can’t do all this for yourself and get more complicated but it’s a place to start with making a simple scrolling list.

The Update at the end of the code fills the list with the initial set of lines (the thumb starts off at offset 1).

The buttons could just be frames with other buttons on top to do different things or texts, images etc. etc. It could be a checkbutton to remember the “clicked state” before scrolling off.

It all depends on what you want to display and how you want it to act.

Well, I do think there is something saying I couldn’t do it myself… me!
While it may be a bit elementary, to me it’s technically getting the job done. (And about in line with how much I can realistically do at the moment without a LOT of hand-holding… Not to say you haven’t been, to much appreciation already)
I think with just all of this I can technically do the project I have in mind.
So, with that being said, I’m going to try.
I’ve got some designs to mock up and plan out, and try to implement with this ScrollFrame stuff in mind.

It may take me a bit to get up and running like I’d like, but hopefully sooner than later I’ll have something else to show for it!
Next time with less fish, probably.

I still need to look into the Stratas and Levels, as well as DrawLayers and sub-levels you linked, but I think I’m going to try to get a couple things drawn out before they leave my mind, making some more working examples, then expand into what I’m thinking about at the moment.

I may not make the prettiest things (yet), but I’ll at least try to make them.

Hopefully I come back with good news!
All of your help has been wildly useful. I appreciate it immensely.
Thank you!

1 Like

@Scrabble

When posting things that you want to look like code, you can put three grave marks (the backwards apostrophes that share a key with the tilde on a U. S. Standard PC Keyboard) in on the line before and after and it’ll bypass all other forum edits and present your text as written.

```
your code
```

That makes it much more readable and also preserves your indentations.

In addition to the advice Fizzie gave you (my go-to source for “How do I do ____?” questions is generally Fizzie), there’s a repository of the entirety of the exposed client code here:

https://www.townlong-yak.com/framexml/live

I got that to appear using the same trick as with code but for a one-line entry. For those you just use a single grave mark and you do it in-line, like this:

`https://www.townlong-yak.com/framexml/live`

You can get special characters to appear by prefixing them with a backslash like this:

\`https://www.townlong-yak.com/framexml/live\`

You’re not prohibited from posting link text by your trust level. You’re only prohibited from posting working links and frankly I think Blizzard ought to alter their forum code to account for that. For trust levels that aren’t allowed working links, Blizzard ought to simply suppress creation of a live link at post time (or possibly at read time - making prior links active upon having gained a sufficient trust level), but that’s a bit off-topic.

That code repository is particularly useful when you want to see how Blizzard uses their own functions or if you want to see how Blizzard implements a function in order to either hook it or clone it.

You’re going about this the right way, for sure. Dive in with a small, discrete project, and work your skills out from there.

Feel free to ask here as much as you like. You’ll find that there are some great resources here to help you.

Sometimes I think I only sub to WoW to give myself a coding playground and to enjoy the Socratic learning experience that it provides.