Thread Tools Display Modes
01-21-12, 08:35 AM   #1
Animor
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Mar 2011
Posts: 136
Help creating scroll frame

Hello,

After spending lots of hours, I'm still stuck with creating a scrolling message frame. I've solved several issues, but I still got two majors ones:

1. When the scrolling frame is loaded and some messages are added to it, I get "0" for GetNumLinesDisplayed(). I need a correct value in order to set correctly the slider SetMinMaxValues(). If I print the GetNumLinesDisplayed() on some events, I then get correct value. What should I do in order to get the correct value when constructing the scrolling message frame? Note that GetNumMessages() does return the correct value.

2. I can't make the frame hide when hitting "escape". I've tried to use tinsert(UISpecialFrames, "historyFrame"), but it juse hide my frame and I can't get to show it.

I will appreciate any help, I'm really stuck here.
This is my code (can be copied and run as standalone code):

Code:
local historyFrame 	= CreateFrame("Frame", "historyFrame", UIParent)
	historyFrame.width 	= 500
	historyFrame.height	= 250
	historyFrame:SetFrameStrata("FULLSCREEN_DIALOG")	
	
	historyFrame:SetSize(historyFrame.width, historyFrame.height)	
	historyFrame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
	historyFrame:SetBackdrop({
		bgFile 		= "Interface\\DialogFrame\\UI-DialogBox-Background",
		edgeFile	= "Interface\\DialogFrame\\UI-DialogBox-Border",
		tile 		= true,
		tileSize 	= 32,
		edgeSize 	= 32,
		insets 		= { left = 8, right = 8, top = 8, bottom = 8 }
	}) 
				  
	historyFrame:SetBackdropColor(0, 0, 0, 1)
	historyFrame:EnableMouse(true)
	historyFrame:EnableMouseWheel(true)	
	
	-- Make movable/resizable	
	historyFrame:SetMovable(true)
	historyFrame:SetResizable(enable)
	frame:SetMinResize(100, 100)
	historyFrame:RegisterForDrag("LeftButton")
	historyFrame:SetScript("OnDragStart", frame.StartMoving)
	historyFrame:SetScript("OnDragStop", frame.StopMovingOrSizing)
	historyFrame:SetScript("OnLoad", function(self) print(self:GetName()) end)
	--tinsert(UISpecialFrames, historyFrame)
	
	-- Close button
	local closebutton = CreateFrame("Button", nil, historyFrame, "UIPanelButtonTemplate")
	closebutton:SetScript("OnClick", function(self) HideParentPanel(self) end)
	closebutton:SetPoint("BOTTOM", 0, 10)
	closebutton:SetHeight(25)
	closebutton:SetWidth(70)
	closebutton:SetText(CLOSE)
	
	-- ScrollingMessageFrame
	local historyScrollingMessage = CreateFrame("ScrollingMessageFrame", historyScrollingMessage, historyFrame)
	historyScrollingMessage:SetPoint("CENTER", 15, 20)	
	historyScrollingMessage:SetSize(historyFrame.width, historyFrame.height - 50)
	historyScrollingMessage:SetFontObject(GameFontNormal)
	historyScrollingMessage:SetTextColor(1, 1, 1, 1) -- default color	
	historyScrollingMessage:SetJustifyH("LEFT")
	historyScrollingMessage:SetHyperlinksEnabled(true)
	historyScrollingMessage:SetFading(false)
	historyScrollingMessage:SetMaxLines(300)
			
	historyScrollingMessage:Clear()
	historyScrollingMessage:AddMessage("1 Here is a message!")	
	historyScrollingMessage:AddMessage("2 Here is a message!")
	historyScrollingMessage:AddMessage("3 Here is a message!")
	historyScrollingMessage:AddMessage("4 Here is a message!")
	historyScrollingMessage:AddMessage("5 Here is a message!")
	historyScrollingMessage:AddMessage("6 Here is a message!")
	historyScrollingMessage:AddMessage("7 Here is a message!")
	historyScrollingMessage:AddMessage("8 Here is a message!")
	historyScrollingMessage:AddMessage("9 Here is a message!")
	historyScrollingMessage:AddMessage("10 Here is a message!")
	historyScrollingMessage:AddMessage("11 Here is a message!")
	historyScrollingMessage:AddMessage("12 Here is a message!")
	historyScrollingMessage:AddMessage("13 Here is a message!")
	historyScrollingMessage:AddMessage("14 Here is a message!")	
	historyScrollingMessage:AddMessage("15 Here is a message!")
	historyScrollingMessage:AddMessage("16 Here is a message!")
	historyScrollingMessage:AddMessage("17 Here is a message!")
	historyScrollingMessage:AddMessage("18 Here is a message!")
	historyScrollingMessage:AddMessage("19 Here is a message!")
	historyScrollingMessage:AddMessage("20 Here is a message!")	
	historyScrollingMessage:AddMessage("21 Here is a message!")
	historyScrollingMessage:AddMessage("22 Here is a message!")
	historyScrollingMessage:AddMessage("23 Here is a message!")
	historyScrollingMessage:AddMessage("24 Here is a message!")
	historyScrollingMessage:AddMessage("25 Here is a message!")	
	--historyScrollingMessage:ScrollToBottom()
	--historyScrollingMessage:ScrollDown()	
	print (historyScrollingMessage:GetNumMessages(), historyScrollingMessage:GetNumLinesDisplayed())
	-------------------------------------------------------------------------------
	-- Scroll bar
	-------------------------------------------------------------------------------
	local ScrollBar = CreateFrame("Slider", "ScrollBar", historyFrame, "UIPanelScrollBarTemplate")
	
	ScrollBar:ClearAllPoints()	
	ScrollBar:SetPoint("RIGHT", historyFrame, "RIGHT", -10, 10)	
	ScrollBar:SetSize(30, historyFrame.height - 90)	
	ScrollBar:SetMinMaxValues(0, 9)
	ScrollBar:SetValueStep(1)	
	ScrollBar.scrollStep = 1 
	ScrollBar:SetScript("OnValueChanged", function(self, value)		
		historyScrollingMessage:SetScrollOffset(select(2, ScrollBar:GetMinMaxValues()) - value)
	 end)
		
	ScrollBar:SetValue(select(2, ScrollBar:GetMinMaxValues()))
	
	historyFrame:SetScript("OnMouseWheel", function(self, delta)
		
		print (historyScrollingMessage:GetNumMessages(), historyScrollingMessage:GetNumLinesDisplayed())
		
		local cur_val = ScrollBar:GetValue()
		local min_val, max_val = ScrollBar:GetMinMaxValues()

		if delta < 0 and cur_val < max_val then
			cur_val = math.min(max_val, cur_val + 1)
			ScrollBar:SetValue(cur_val)			
		elseif delta > 0 and cur_val > min_val then
			cur_val = math.max(min_val, cur_val - 1)
			ScrollBar:SetValue(cur_val)		
		end	
	 end)
  Reply With Quote
01-22-12, 03:31 AM   #2
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
When cross-posting on WoWInterface and WowAce, you should usually link to the other thread. Many people read and post on both forums, so linking will prevent people from spending 20 minutes reading your post and writing code for you when someone already did the same in the other thread.

See also:
http://forums.wowace.com/showthread.php?t=19881

Originally Posted by Animor View Post
1. When the scrolling frame is loaded and some messages are added to it, I get "0" for GetNumLinesDisplayed(). I need a correct value in order to set correctly the slider SetMinMaxValues(). If I print the GetNumLinesDisplayed() on some events, I then get correct value. What should I do in order to get the correct value when constructing the scrolling message frame? Note that GetNumMessages() does return the correct value.
Which events? Perhaps you could store the value in a variable, update it when a relevant event occurs, and use the variable in your scrollbar code, instead of calling the function to look up the value again every time the scrollbar is moved.

Originally Posted by Animor View Post
2. I can't make the frame hide when hitting "escape". I've tried to use tinsert(UISpecialFrames, "historyFrame"), but it juse hide my frame and I can't get to show it.
In the code you posted, you inserted historyFrame the object into UISpecialFrames, instead of "historyFrame" the string. tinsert(UISpecialFrames, "historyFrame") should be correct, and then you should just call historyFrame:Show() and historyFrame:Hide() to show or hide the frame, in addition to being able to hide it with the Esc key.

Some other general comments about your code, in no particular order:

(1) I'd advise against using "historyFrame" as the global name for your frame. Give it a name that makes it immediately obvious which addon controls it, like AnimorHistoryFrame. Also, names beginning with a lowercase letter are conventionally used for local variables, while object names begin with an uppercase letter. It doesn't really matter, but it helps other programmers reading your code if you stick to common conventions.

(2) You should definitely not give your scrollbar a global name of "ScrollBar". Not only is this horrible for debugging and error reporting, but it's also extremely likely to conflict with other addons that are poorly written and/or inadvertently leaking variables into the global namespace.

(3) Variable names in Lua are case-sensitive, so you cannot write "scrollBar" in one place and "ScrollBar" in another place. The only reason this works is because you assigned "ScrollBar" as the global name of the object you assigned to the local variable "scrollBar", so both names happened to refer to the same thing.

(4) You have an error on line 24 of your code. You wrote frame:SetMinResize(100, 100), but frame is not defined; this looks like it was meant to be a reference to historyFrame instead.

(5) OnLoad scripts don't work for frames created in Lua. Just write any code you want to execute immediately in the main chunk after you create the frame.

(6) Your panel is not a true "UI panel" with all of the relevant attributes set, so you should not call HideParentPanel(self) to close it; just call self:GetParent():Hide() instead.

(7) You should provide some way to programmatically access your buttons and widgets. Either pass a string as the second parameter to CreateFrame to give them a global name (slow and sloppy way) or add them as members of your main frame's table (fast and clean way):

Code:
local closebutton = CreateFrame("Button", nil, historyFrame, "UIPanelButtonTemplate")
...
historyFrame.closeButton = closebutton
I made a few changes to your code; this seems to work fine, and I added a slash command to toggle the frame ("/ahf"):
Code:
local frame  = CreateFrame("Frame", "AnimorHistoryFrame", UIParent)
frame.width  = 500
frame.height = 250
frame:SetFrameStrata("FULLSCREEN_DIALOG")
frame:SetSize(frame.width, frame.height)
frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
frame:SetBackdrop({
	bgFile   = "Interface\\DialogFrame\\UI-DialogBox-Background",
	edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
	tile     = true,
	tileSize = 32,
	edgeSize = 32,
	insets   = { left = 8, right = 8, top = 8, bottom = 8 }
})
frame:SetBackdropColor(0, 0, 0, 1)
frame:EnableMouse(true)
frame:EnableMouseWheel(true)

-- Make movable/resizable
frame:SetMovable(true)
frame:SetResizable(enable)
frame:SetMinResize(100, 100)
frame:RegisterForDrag("LeftButton")
frame:SetScript("OnDragStart", frame.StartMoving)
frame:SetScript("OnDragStop", frame.StopMovingOrSizing)

tinsert(UISpecialFrames, "AnimorHistoryFrame")

-- Close button
local closeButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
closeButton:SetPoint("BOTTOM", 0, 10)
closeButton:SetHeight(25)
closeButton:SetWidth(70)
closeButton:SetText(CLOSE)
closeButton:SetScript("OnClick", function(self)
	HideParentPanel(self)
end)
frame.closeButton = closeButton

-- ScrollingMessageFrame
local messageFrame = CreateFrame("ScrollingMessageFrame", nil, frame)
messageFrame:SetPoint("CENTER", 15, 20)
messageFrame:SetSize(frame.width, frame.height - 50)
messageFrame:SetFontObject(GameFontNormal)
messageFrame:SetTextColor(1, 1, 1, 1) -- default color
messageFrame:SetJustifyH("LEFT")
messageFrame:SetHyperlinksEnabled(true)
messageFrame:SetFading(false)
messageFrame:SetMaxLines(300)
frame.messageFrame = messageFrame

for i = 1, 25 do
	messageFrame:AddMessage(i .. ". Here is a message!")
end
--messageFrame:ScrollToBottom()
--messageFrame:ScrollDown()
print(messageFrame:GetNumMessages(), messageFrame:GetNumLinesDisplayed())

-------------------------------------------------------------------------------
-- Scroll bar
-------------------------------------------------------------------------------
local scrollBar = CreateFrame("Slider", nil, frame, "UIPanelScrollBarTemplate")
scrollBar:SetPoint("RIGHT", frame, "RIGHT", -10, 10)
scrollBar:SetSize(30, frame.height - 90)
scrollBar:SetMinMaxValues(0, 9)
scrollBar:SetValueStep(1)
scrollBar.scrollStep = 1
frame.scrollBar = scrollBar

scrollBar:SetScript("OnValueChanged", function(self, value)
	messageFrame:SetScrollOffset(select(2, scrollBar:GetMinMaxValues()) - value)
end)

scrollBar:SetValue(select(2, scrollBar:GetMinMaxValues()))

frame:SetScript("OnMouseWheel", function(self, delta)
	print(messageFrame:GetNumMessages(), messageFrame:GetNumLinesDisplayed())

	local cur_val = scrollBar:GetValue()
	local min_val, max_val = scrollBar:GetMinMaxValues()

	if delta < 0 and cur_val < max_val then
		cur_val = math.min(max_val, cur_val + 1)
		scrollBar:SetValue(cur_val)
	elseif delta > 0 and cur_val > min_val then
		cur_val = math.max(min_val, cur_val - 1)
		scrollBar:SetValue(cur_val)
	end
end)

SLASH_AHF1 = "/ahf"
SlashCmdList.AHF = function()
	if AnimorHistoryFrame:IsShown() then
		AnimorHistoryFrame:Hide()
	else
		AnimorHistoryFrame:Show()
	end
end
  Reply With Quote
01-22-12, 07:08 AM   #3
Animor
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Mar 2011
Posts: 136
Thank you so much for this. I learned a lot from your comments about good practice in programming, you should write a guide with these conventions!

About GetNumLinesDisplayed - it looks like it's giving the correct result if I use it after Show(). However, when I originally used this function right after the frame construction, it gave "0". I don't really understand it.

Two questions, if I may:

1. Because of tinsert(UISpecialFrames, "AnimorHistoryFrame"), the frame was hidden by default when I reload. However, when I've added AnimorHistoryFrame:Show() to the bottom of the code, it didn't work - nothing happened. But when I used the slash command you coded for me, with the same function, the frame did show. Why is that?

2. What is the difference between HideParentPanel(self) and self:GetParent():Hide(), and why I should not use the first?

Thanks again!

Last edited by Animor : 01-22-12 at 08:40 AM.
  Reply With Quote
01-23-12, 05:02 PM   #4
Phanx
Cat.
 
Phanx's Avatar
AddOn Author - Click to view addons
Join Date: Mar 2006
Posts: 5,617
Originally Posted by Animor View Post
1. Because of tinsert(UISpecialFrames, "AnimorHistoryFrame"), the frame was hidden by default when I reload. However, when I've added AnimorHistoryFrame:Show() to the bottom of the code, it didn't work - nothing happened. But when I used the slash command you coded for me, with the same function, the frame did show. Why is that?
Most likely, at some point during the login process, the game makes sure that all of the frames in UISpecialFrames are hidden. If you want the frame to be visible right away, you will probably need to register for an event like PLAYER_LOGIN, and call frame:Show() then.

Originally Posted by Animor View Post
2. What is the difference between HideParentPanel(self) and self:GetParent():Hide(), and why I should not use the first?
HideParentPanel expects that your frame has an entry in the UIPanelWindows table, and that it has a number of attributes set through frame:SetAttribute(name, value) that tell the game how to position and move your window relative to other open frames. This is the system that arranges the character window and the social window if you open them at the same time, for example.

HideParentPanel will still hide the frame if none of those conditions are met, but it will add unnecessary overhead. It's more efficient to just hide the frame directly, since after all of the extra checks that aren't relevant to your frame, that's all HideParentPanel will do anyway.
  Reply With Quote
01-24-12, 05:41 AM   #5
Animor
A Flamescale Wyrmkin
AddOn Author - Click to view addons
Join Date: Mar 2011
Posts: 136
Thank you!
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Help creating scroll frame


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off