Asa@0
|
1 --- **AceGUI-3.0** provides access to numerous widgets which can be used to create GUIs.
|
Asa@0
|
2 -- AceGUI is used by AceConfigDialog to create the option GUIs, but you can use it by itself
|
Asa@0
|
3 -- to create any custom GUI. There are more extensive examples in the test suite in the Ace3
|
Asa@0
|
4 -- stand-alone distribution.
|
Asa@0
|
5 --
|
Asa@0
|
6 -- **Note**: When using AceGUI-3.0 directly, please do not modify the frames of the widgets directly,
|
Asa@0
|
7 -- as any "unknown" change to the widgets will cause addons that get your widget out of the widget pool
|
Asa@0
|
8 -- to misbehave. If you think some part of a widget should be modifiable, please open a ticket, and we'll
|
Asa@0
|
9 -- implement a proper API to modify it.
|
Asa@0
|
10 -- @usage
|
Asa@0
|
11 -- local AceGUI = LibStub("AceGUI-3.0")
|
Asa@0
|
12 -- -- Create a container frame
|
Asa@0
|
13 -- local f = AceGUI:Create("Frame")
|
Asa@0
|
14 -- f:SetCallback("OnClose",function(widget) AceGUI:Release(widget) end)
|
Asa@0
|
15 -- f:SetTitle("AceGUI-3.0 Example")
|
Asa@0
|
16 -- f:SetStatusText("Status Bar")
|
Asa@0
|
17 -- f:SetLayout("Flow")
|
Asa@0
|
18 -- -- Create a button
|
Asa@0
|
19 -- local btn = AceGUI:Create("Button")
|
Asa@0
|
20 -- btn:SetWidth(170)
|
Asa@0
|
21 -- btn:SetText("Button !")
|
Asa@0
|
22 -- btn:SetCallback("OnClick", function() print("Click!") end)
|
Asa@0
|
23 -- -- Add the button to the container
|
Asa@0
|
24 -- f:AddChild(btn)
|
Asa@0
|
25 -- @class file
|
Asa@0
|
26 -- @name AceGUI-3.0
|
Asa@0
|
27 -- @release $Id: AceGUI-3.0.lua 896 2009-12-06 16:29:49Z nevcairiel $
|
Asa@0
|
28 local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 30
|
Asa@0
|
29 local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR)
|
Asa@0
|
30
|
Asa@0
|
31 if not AceGUI then return end -- No upgrade needed
|
Asa@0
|
32
|
Asa@0
|
33 -- Lua APIs
|
Asa@0
|
34 local tconcat, tremove, tinsert = table.concat, table.remove, table.insert
|
Asa@0
|
35 local select, pairs, next, type = select, pairs, next, type
|
Asa@0
|
36 local error, assert, loadstring = error, assert, loadstring
|
Asa@0
|
37 local setmetatable, rawget, rawset = setmetatable, rawget, rawset
|
Asa@0
|
38 local math_max = math.max
|
Asa@0
|
39
|
Asa@0
|
40 -- WoW APIs
|
Asa@0
|
41 local UIParent = UIParent
|
Asa@0
|
42
|
Asa@0
|
43 -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
Asa@0
|
44 -- List them here for Mikk's FindGlobals script
|
Asa@0
|
45 -- GLOBALS: geterrorhandler, LibStub
|
Asa@0
|
46
|
Asa@0
|
47 --local con = LibStub("AceConsole-3.0",true)
|
Asa@0
|
48
|
Asa@0
|
49 AceGUI.WidgetRegistry = AceGUI.WidgetRegistry or {}
|
Asa@0
|
50 AceGUI.LayoutRegistry = AceGUI.LayoutRegistry or {}
|
Asa@0
|
51 AceGUI.WidgetBase = AceGUI.WidgetBase or {}
|
Asa@0
|
52 AceGUI.WidgetContainerBase = AceGUI.WidgetContainerBase or {}
|
Asa@0
|
53 AceGUI.WidgetVersions = AceGUI.WidgetVersions or {}
|
Asa@0
|
54
|
Asa@0
|
55 -- local upvalues
|
Asa@0
|
56 local WidgetRegistry = AceGUI.WidgetRegistry
|
Asa@0
|
57 local LayoutRegistry = AceGUI.LayoutRegistry
|
Asa@0
|
58 local WidgetVersions = AceGUI.WidgetVersions
|
Asa@0
|
59
|
Asa@0
|
60 --[[
|
Asa@0
|
61 xpcall safecall implementation
|
Asa@0
|
62 ]]
|
Asa@0
|
63 local xpcall = xpcall
|
Asa@0
|
64
|
Asa@0
|
65 local function errorhandler(err)
|
Asa@0
|
66 return geterrorhandler()(err)
|
Asa@0
|
67 end
|
Asa@0
|
68
|
Asa@0
|
69 local function CreateDispatcher(argCount)
|
Asa@0
|
70 local code = [[
|
Asa@0
|
71 local xpcall, eh = ...
|
Asa@0
|
72 local method, ARGS
|
Asa@0
|
73 local function call() return method(ARGS) end
|
Asa@0
|
74
|
Asa@0
|
75 local function dispatch(func, ...)
|
Asa@0
|
76 method = func
|
Asa@0
|
77 if not method then return end
|
Asa@0
|
78 ARGS = ...
|
Asa@0
|
79 return xpcall(call, eh)
|
Asa@0
|
80 end
|
Asa@0
|
81
|
Asa@0
|
82 return dispatch
|
Asa@0
|
83 ]]
|
Asa@0
|
84
|
Asa@0
|
85 local ARGS = {}
|
Asa@0
|
86 for i = 1, argCount do ARGS[i] = "arg"..i end
|
Asa@0
|
87 code = code:gsub("ARGS", tconcat(ARGS, ", "))
|
Asa@0
|
88 return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler)
|
Asa@0
|
89 end
|
Asa@0
|
90
|
Asa@0
|
91 local Dispatchers = setmetatable({}, {__index=function(self, argCount)
|
Asa@0
|
92 local dispatcher = CreateDispatcher(argCount)
|
Asa@0
|
93 rawset(self, argCount, dispatcher)
|
Asa@0
|
94 return dispatcher
|
Asa@0
|
95 end})
|
Asa@0
|
96 Dispatchers[0] = function(func)
|
Asa@0
|
97 return xpcall(func, errorhandler)
|
Asa@0
|
98 end
|
Asa@0
|
99
|
Asa@0
|
100 local function safecall(func, ...)
|
Asa@0
|
101 return Dispatchers[select('#', ...)](func, ...)
|
Asa@0
|
102 end
|
Asa@0
|
103
|
Asa@0
|
104 -- Recycling functions
|
Asa@0
|
105 local newWidget, delWidget
|
Asa@0
|
106 do
|
Asa@0
|
107 -- Version Upgrade in Minor 29
|
Asa@0
|
108 -- Internal Storage of the objects changed, from an array table
|
Asa@0
|
109 -- to a hash table, and additionally we introduced versioning on
|
Asa@0
|
110 -- the widgets which would discard all widgets from a pre-29 version
|
Asa@0
|
111 -- anyway, so we just clear the storage now, and don't try to
|
Asa@0
|
112 -- convert the storage tables to the new format.
|
Asa@0
|
113 -- This should generally not cause *many* widgets to end up in trash,
|
Asa@0
|
114 -- since once dialogs are opened, all addons should be loaded already
|
Asa@0
|
115 -- and AceGUI should be on the latest version available on the users
|
Asa@0
|
116 -- setup.
|
Asa@0
|
117 -- -- nevcairiel - Nov 2nd, 2009
|
Asa@0
|
118 if oldminor and oldminor < 29 and AceGUI.objPools then
|
Asa@0
|
119 AceGUI.objPools = nil
|
Asa@0
|
120 end
|
Asa@0
|
121
|
Asa@0
|
122 AceGUI.objPools = AceGUI.objPools or {}
|
Asa@0
|
123 local objPools = AceGUI.objPools
|
Asa@0
|
124 --Returns a new instance, if none are available either returns a new table or calls the given contructor
|
Asa@0
|
125 function newWidget(type)
|
Asa@0
|
126 if not WidgetRegistry[type] then
|
Asa@0
|
127 error("Attempt to instantiate unknown widget type", 2)
|
Asa@0
|
128 end
|
Asa@0
|
129
|
Asa@0
|
130 if not objPools[type] then
|
Asa@0
|
131 objPools[type] = {}
|
Asa@0
|
132 end
|
Asa@0
|
133
|
Asa@0
|
134 local newObj = next(objPools[type])
|
Asa@0
|
135 if not newObj then
|
Asa@0
|
136 newObj = WidgetRegistry[type]()
|
Asa@0
|
137 newObj.AceGUIWidgetVersion = WidgetVersions[type]
|
Asa@0
|
138 else
|
Asa@0
|
139 objPools[type][newObj] = nil
|
Asa@0
|
140 -- if the widget is older then the latest, don't even try to reuse it
|
Asa@0
|
141 -- just forget about it, and grab a new one.
|
Asa@0
|
142 if not newObj.AceGUIWidgetVersion or newObj.AceGUIWidgetVersion < WidgetVersions[type] then
|
Asa@0
|
143 return newWidget(type)
|
Asa@0
|
144 end
|
Asa@0
|
145 end
|
Asa@0
|
146 return newObj
|
Asa@0
|
147 end
|
Asa@0
|
148 -- Releases an instance to the Pool
|
Asa@0
|
149 function delWidget(obj,type)
|
Asa@0
|
150 if not objPools[type] then
|
Asa@0
|
151 objPools[type] = {}
|
Asa@0
|
152 end
|
Asa@0
|
153 if objPools[type][obj] then
|
Asa@0
|
154 error("Attempt to Release Widget that is already released", 2)
|
Asa@0
|
155 end
|
Asa@0
|
156 objPools[type][obj] = true
|
Asa@0
|
157 end
|
Asa@0
|
158 end
|
Asa@0
|
159
|
Asa@0
|
160
|
Asa@0
|
161 -------------------
|
Asa@0
|
162 -- API Functions --
|
Asa@0
|
163 -------------------
|
Asa@0
|
164
|
Asa@0
|
165 -- Gets a widget Object
|
Asa@0
|
166
|
Asa@0
|
167 --- Create a new Widget of the given type.
|
Asa@0
|
168 -- This function will instantiate a new widget (or use one from the widget pool), and call the
|
Asa@0
|
169 -- OnAcquire function on it, before returning.
|
Asa@0
|
170 -- @param type The type of the widget.
|
Asa@0
|
171 -- @return The newly created widget.
|
Asa@0
|
172 function AceGUI:Create(type)
|
Asa@0
|
173 if WidgetRegistry[type] then
|
Asa@0
|
174 local widget = newWidget(type)
|
Asa@0
|
175
|
Asa@0
|
176 if rawget(widget,'Acquire') then
|
Asa@0
|
177 widget.OnAcquire = widget.Acquire
|
Asa@0
|
178 widget.Acquire = nil
|
Asa@0
|
179 elseif rawget(widget,'Aquire') then
|
Asa@0
|
180 widget.OnAcquire = widget.Aquire
|
Asa@0
|
181 widget.Aquire = nil
|
Asa@0
|
182 end
|
Asa@0
|
183
|
Asa@0
|
184 if rawget(widget,'Release') then
|
Asa@0
|
185 widget.OnRelease = rawget(widget,'Release')
|
Asa@0
|
186 widget.Release = nil
|
Asa@0
|
187 end
|
Asa@0
|
188
|
Asa@0
|
189 if widget.OnAcquire then
|
Asa@0
|
190 widget:OnAcquire()
|
Asa@0
|
191 else
|
Asa@0
|
192 error(("Widget type %s doesn't supply an OnAcquire Function"):format(type))
|
Asa@0
|
193 end
|
Asa@0
|
194 -- Set the default Layout ('List')
|
Asa@0
|
195 safecall(widget.SetLayout, widget, 'List')
|
Asa@0
|
196 safecall(widget.ResumeLayout, widget)
|
Asa@0
|
197 return widget
|
Asa@0
|
198 end
|
Asa@0
|
199 end
|
Asa@0
|
200
|
Asa@0
|
201 --- Releases a widget Object.
|
Asa@0
|
202 -- This function calls OnRelease on the widget and places it back in the widget pool.
|
Asa@0
|
203 -- Any data on the widget is being erased, and the widget will be hidden.\\
|
Asa@0
|
204 -- If this widget is a Container-Widget, all of its Child-Widgets will be releases as well.
|
Asa@0
|
205 -- @param widget The widget to release
|
Asa@0
|
206 function AceGUI:Release(widget)
|
Asa@0
|
207 safecall( widget.PauseLayout, widget )
|
Asa@0
|
208 widget:Fire("OnRelease")
|
Asa@0
|
209 safecall( widget.ReleaseChildren, widget )
|
Asa@0
|
210
|
Asa@0
|
211 if widget.OnRelease then
|
Asa@0
|
212 widget:OnRelease()
|
Asa@0
|
213 else
|
Asa@0
|
214 error(("Widget type %s doesn't supply an OnRelease Function"):format(type))
|
Asa@0
|
215 end
|
Asa@0
|
216 for k in pairs(widget.userdata) do
|
Asa@0
|
217 widget.userdata[k] = nil
|
Asa@0
|
218 end
|
Asa@0
|
219 for k in pairs(widget.events) do
|
Asa@0
|
220 widget.events[k] = nil
|
Asa@0
|
221 end
|
Asa@0
|
222 widget.width = nil
|
Asa@0
|
223 widget.relWidth = nil
|
Asa@0
|
224 widget.height = nil
|
Asa@0
|
225 widget.relHeight = nil
|
Asa@0
|
226 widget.noAutoHeight = nil
|
Asa@0
|
227 widget.frame:ClearAllPoints()
|
Asa@0
|
228 widget.frame:Hide()
|
Asa@0
|
229 widget.frame:SetParent(UIParent)
|
Asa@0
|
230 widget.frame.width = nil
|
Asa@0
|
231 widget.frame.height = nil
|
Asa@0
|
232 if widget.content then
|
Asa@0
|
233 widget.content.width = nil
|
Asa@0
|
234 widget.content.height = nil
|
Asa@0
|
235 end
|
Asa@0
|
236 delWidget(widget, widget.type)
|
Asa@0
|
237 end
|
Asa@0
|
238
|
Asa@0
|
239 -----------
|
Asa@0
|
240 -- Focus --
|
Asa@0
|
241 -----------
|
Asa@0
|
242
|
Asa@0
|
243
|
Asa@0
|
244 --- Called when a widget has taken focus.
|
Asa@0
|
245 -- e.g. Dropdowns opening, Editboxes gaining kb focus
|
Asa@0
|
246 -- @param widget The widget that should be focused
|
Asa@0
|
247 function AceGUI:SetFocus(widget)
|
Asa@0
|
248 if self.FocusedWidget and self.FocusedWidget ~= widget then
|
Asa@0
|
249 safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget)
|
Asa@0
|
250 end
|
Asa@0
|
251 self.FocusedWidget = widget
|
Asa@0
|
252 end
|
Asa@0
|
253
|
Asa@0
|
254
|
Asa@0
|
255 --- Called when something has happened that could cause widgets with focus to drop it
|
Asa@0
|
256 -- e.g. titlebar of a frame being clicked
|
Asa@0
|
257 function AceGUI:ClearFocus()
|
Asa@0
|
258 if self.FocusedWidget then
|
Asa@0
|
259 safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget)
|
Asa@0
|
260 self.FocusedWidget = nil
|
Asa@0
|
261 end
|
Asa@0
|
262 end
|
Asa@0
|
263
|
Asa@0
|
264 -------------
|
Asa@0
|
265 -- Widgets --
|
Asa@0
|
266 -------------
|
Asa@0
|
267 --[[
|
Asa@0
|
268 Widgets must provide the following functions
|
Asa@0
|
269 OnAcquire() - Called when the object is acquired, should set everything to a default hidden state
|
Asa@0
|
270 OnRelease() - Called when the object is Released, should remove any anchors and hide the Widget
|
Asa@0
|
271
|
Asa@0
|
272 And the following members
|
Asa@0
|
273 frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes
|
Asa@0
|
274 type - the type of the object, same as the name given to :RegisterWidget()
|
Asa@0
|
275
|
Asa@0
|
276 Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet
|
Asa@0
|
277 It will be cleared automatically when a widget is released
|
Asa@0
|
278 Placing values directly into a widget object should be avoided
|
Asa@0
|
279
|
Asa@0
|
280 If the Widget can act as a container for other Widgets the following
|
Asa@0
|
281 content - frame or derivitive that children will be anchored to
|
Asa@0
|
282
|
Asa@0
|
283 The Widget can supply the following Optional Members
|
Asa@0
|
284 :OnWidthSet(width) - Called when the width of the widget is changed
|
Asa@0
|
285 :OnHeightSet(height) - Called when the height of the widget is changed
|
Asa@0
|
286 Widgets should not use the OnSizeChanged events of thier frame or content members, use these methods instead
|
Asa@0
|
287 AceGUI already sets a handler to the event
|
Asa@0
|
288 :LayoutFinished(width, height) - called after a layout has finished, the width and height will be the width and height of the
|
Asa@0
|
289 area used for controls. These can be nil if the layout used the existing size to layout the controls.
|
Asa@0
|
290
|
Asa@0
|
291 ]]
|
Asa@0
|
292
|
Asa@0
|
293 --------------------------
|
Asa@0
|
294 -- Widget Base Template --
|
Asa@0
|
295 --------------------------
|
Asa@0
|
296 do
|
Asa@0
|
297 local function fixlevels(parent,...)
|
Asa@0
|
298 local i = 1
|
Asa@0
|
299 local child = select(i, ...)
|
Asa@0
|
300 while child do
|
Asa@0
|
301 child:SetFrameLevel(parent:GetFrameLevel()+1)
|
Asa@0
|
302 fixlevels(child, child:GetChildren())
|
Asa@0
|
303 i = i + 1
|
Asa@0
|
304 child = select(i, ...)
|
Asa@0
|
305 end
|
Asa@0
|
306 end
|
Asa@0
|
307
|
Asa@0
|
308 local WidgetBase = AceGUI.WidgetBase
|
Asa@0
|
309
|
Asa@0
|
310 WidgetBase.SetParent = function(self, parent)
|
Asa@0
|
311 local frame = self.frame
|
Asa@0
|
312 frame:SetParent(nil)
|
Asa@0
|
313 frame:SetParent(parent.content)
|
Asa@0
|
314 self.parent = parent
|
Asa@0
|
315 --fixlevels(parent.frame,parent.frame:GetChildren())
|
Asa@0
|
316 end
|
Asa@0
|
317
|
Asa@0
|
318 WidgetBase.SetCallback = function(self, name, func)
|
Asa@0
|
319 if type(func) == "function" then
|
Asa@0
|
320 self.events[name] = func
|
Asa@0
|
321 end
|
Asa@0
|
322 end
|
Asa@0
|
323
|
Asa@0
|
324 WidgetBase.Fire = function(self, name, ...)
|
Asa@0
|
325 if self.events[name] then
|
Asa@0
|
326 local success, ret = safecall(self.events[name], self, name, ...)
|
Asa@0
|
327 if success then
|
Asa@0
|
328 return ret
|
Asa@0
|
329 end
|
Asa@0
|
330 end
|
Asa@0
|
331 end
|
Asa@0
|
332
|
Asa@0
|
333 WidgetBase.SetWidth = function(self, width)
|
Asa@0
|
334 self.frame:SetWidth(width)
|
Asa@0
|
335 self.frame.width = width
|
Asa@0
|
336 if self.OnWidthSet then
|
Asa@0
|
337 self:OnWidthSet(width)
|
Asa@0
|
338 end
|
Asa@0
|
339 end
|
Asa@0
|
340
|
Asa@0
|
341 WidgetBase.SetRelativeWidth = function(self, width)
|
Asa@0
|
342 if width <= 0 or width > 1 then
|
Asa@0
|
343 error(":SetRelativeWidth(width): Invalid relative width.", 2)
|
Asa@0
|
344 end
|
Asa@0
|
345 self.relWidth = width
|
Asa@0
|
346 self.width = "relative"
|
Asa@0
|
347 end
|
Asa@0
|
348
|
Asa@0
|
349 WidgetBase.SetHeight = function(self, height)
|
Asa@0
|
350 self.frame:SetHeight(height)
|
Asa@0
|
351 self.frame.height = height
|
Asa@0
|
352 if self.OnHeightSet then
|
Asa@0
|
353 self:OnHeightSet(height)
|
Asa@0
|
354 end
|
Asa@0
|
355 end
|
Asa@0
|
356
|
Asa@0
|
357 --[[ WidgetBase.SetRelativeHeight = function(self, height)
|
Asa@0
|
358 if height <= 0 or height > 1 then
|
Asa@0
|
359 error(":SetRelativeHeight(height): Invalid relative height.", 2)
|
Asa@0
|
360 end
|
Asa@0
|
361 self.relHeight = height
|
Asa@0
|
362 self.height = "relative"
|
Asa@0
|
363 end ]]
|
Asa@0
|
364
|
Asa@0
|
365 WidgetBase.IsVisible = function(self)
|
Asa@0
|
366 return self.frame:IsVisible()
|
Asa@0
|
367 end
|
Asa@0
|
368
|
Asa@0
|
369 WidgetBase.IsShown= function(self)
|
Asa@0
|
370 return self.frame:IsShown()
|
Asa@0
|
371 end
|
Asa@0
|
372
|
Asa@0
|
373 WidgetBase.Release = function(self)
|
Asa@0
|
374 AceGUI:Release(self)
|
Asa@0
|
375 end
|
Asa@0
|
376
|
Asa@0
|
377 WidgetBase.SetPoint = function(self, ...)
|
Asa@0
|
378 return self.frame:SetPoint(...)
|
Asa@0
|
379 end
|
Asa@0
|
380
|
Asa@0
|
381 WidgetBase.ClearAllPoints = function(self)
|
Asa@0
|
382 return self.frame:ClearAllPoints()
|
Asa@0
|
383 end
|
Asa@0
|
384
|
Asa@0
|
385 WidgetBase.GetNumPoints = function(self)
|
Asa@0
|
386 return self.frame:GetNumPoints()
|
Asa@0
|
387 end
|
Asa@0
|
388
|
Asa@0
|
389 WidgetBase.GetPoint = function(self, ...)
|
Asa@0
|
390 return self.frame:GetPoint(...)
|
Asa@0
|
391 end
|
Asa@0
|
392
|
Asa@0
|
393 WidgetBase.GetUserDataTable = function(self)
|
Asa@0
|
394 return self.userdata
|
Asa@0
|
395 end
|
Asa@0
|
396
|
Asa@0
|
397 WidgetBase.SetUserData = function(self, key, value)
|
Asa@0
|
398 self.userdata[key] = value
|
Asa@0
|
399 end
|
Asa@0
|
400
|
Asa@0
|
401 WidgetBase.GetUserData = function(self, key)
|
Asa@0
|
402 return self.userdata[key]
|
Asa@0
|
403 end
|
Asa@0
|
404
|
Asa@0
|
405 WidgetBase.IsFullHeight = function(self)
|
Asa@0
|
406 return self.height == "fill"
|
Asa@0
|
407 end
|
Asa@0
|
408
|
Asa@0
|
409 WidgetBase.SetFullHeight = function(self, isFull)
|
Asa@0
|
410 if isFull then
|
Asa@0
|
411 self.height = "fill"
|
Asa@0
|
412 else
|
Asa@0
|
413 self.height = nil
|
Asa@0
|
414 end
|
Asa@0
|
415 end
|
Asa@0
|
416
|
Asa@0
|
417 WidgetBase.IsFullWidth = function(self)
|
Asa@0
|
418 return self.width == "fill"
|
Asa@0
|
419 end
|
Asa@0
|
420
|
Asa@0
|
421 WidgetBase.SetFullWidth = function(self, isFull)
|
Asa@0
|
422 if isFull then
|
Asa@0
|
423 self.width = "fill"
|
Asa@0
|
424 else
|
Asa@0
|
425 self.width = nil
|
Asa@0
|
426 end
|
Asa@0
|
427 end
|
Asa@0
|
428
|
Asa@0
|
429 -- local function LayoutOnUpdate(this)
|
Asa@0
|
430 -- this:SetScript("OnUpdate",nil)
|
Asa@0
|
431 -- this.obj:PerformLayout()
|
Asa@0
|
432 -- end
|
Asa@0
|
433
|
Asa@0
|
434 local WidgetContainerBase = AceGUI.WidgetContainerBase
|
Asa@0
|
435
|
Asa@0
|
436 WidgetContainerBase.PauseLayout = function(self)
|
Asa@0
|
437 self.LayoutPaused = true
|
Asa@0
|
438 end
|
Asa@0
|
439
|
Asa@0
|
440 WidgetContainerBase.ResumeLayout = function(self)
|
Asa@0
|
441 self.LayoutPaused = nil
|
Asa@0
|
442 end
|
Asa@0
|
443
|
Asa@0
|
444 WidgetContainerBase.PerformLayout = function(self)
|
Asa@0
|
445 if self.LayoutPaused then
|
Asa@0
|
446 return
|
Asa@0
|
447 end
|
Asa@0
|
448 safecall(self.LayoutFunc,self.content, self.children)
|
Asa@0
|
449 end
|
Asa@0
|
450
|
Asa@0
|
451 --call this function to layout, makes sure layed out objects get a frame to get sizes etc
|
Asa@0
|
452 WidgetContainerBase.DoLayout = function(self)
|
Asa@0
|
453 self:PerformLayout()
|
Asa@0
|
454 -- if not self.parent then
|
Asa@0
|
455 -- self.frame:SetScript("OnUpdate", LayoutOnUpdate)
|
Asa@0
|
456 -- end
|
Asa@0
|
457 end
|
Asa@0
|
458
|
Asa@0
|
459 WidgetContainerBase.AddChild = function(self, child, beforeWidget)
|
Asa@0
|
460 if beforeWidget then
|
Asa@0
|
461 local siblingIndex = 1
|
Asa@0
|
462 for _, widget in pairs(self.children) do
|
Asa@0
|
463 if widget == beforeWidget then
|
Asa@0
|
464 break
|
Asa@0
|
465 end
|
Asa@0
|
466 siblingIndex = siblingIndex + 1
|
Asa@0
|
467 end
|
Asa@0
|
468 tinsert(self.children, siblingIndex, child)
|
Asa@0
|
469 else
|
Asa@0
|
470 tinsert(self.children, child)
|
Asa@0
|
471 end
|
Asa@0
|
472 child:SetParent(self)
|
Asa@0
|
473 child.frame:Show()
|
Asa@0
|
474 self:DoLayout()
|
Asa@0
|
475 end
|
Asa@0
|
476
|
Asa@0
|
477 WidgetContainerBase.AddChildren = function(self, ...)
|
Asa@0
|
478 for i = 1, select("#", ...) do
|
Asa@0
|
479 local child = select(i, ...)
|
Asa@0
|
480 tinsert(self.children, child)
|
Asa@0
|
481 child:SetParent(self)
|
Asa@0
|
482 child.frame:Show()
|
Asa@0
|
483 end
|
Asa@0
|
484 self:DoLayout()
|
Asa@0
|
485 end
|
Asa@0
|
486
|
Asa@0
|
487 WidgetContainerBase.ReleaseChildren = function(self)
|
Asa@0
|
488 local children = self.children
|
Asa@0
|
489 for i = 1,#children do
|
Asa@0
|
490 AceGUI:Release(children[i])
|
Asa@0
|
491 children[i] = nil
|
Asa@0
|
492 end
|
Asa@0
|
493 end
|
Asa@0
|
494
|
Asa@0
|
495 WidgetContainerBase.SetLayout = function(self, Layout)
|
Asa@0
|
496 self.LayoutFunc = AceGUI:GetLayout(Layout)
|
Asa@0
|
497 end
|
Asa@0
|
498
|
Asa@0
|
499 WidgetContainerBase.SetAutoAdjustHeight = function(self, adjust)
|
Asa@0
|
500 if adjust then
|
Asa@0
|
501 self.noAutoHeight = nil
|
Asa@0
|
502 else
|
Asa@0
|
503 self.noAutoHeight = true
|
Asa@0
|
504 end
|
Asa@0
|
505 end
|
Asa@0
|
506
|
Asa@0
|
507 local function FrameResize(this)
|
Asa@0
|
508 local self = this.obj
|
Asa@0
|
509 if this:GetWidth() and this:GetHeight() then
|
Asa@0
|
510 if self.OnWidthSet then
|
Asa@0
|
511 self:OnWidthSet(this:GetWidth())
|
Asa@0
|
512 end
|
Asa@0
|
513 if self.OnHeightSet then
|
Asa@0
|
514 self:OnHeightSet(this:GetHeight())
|
Asa@0
|
515 end
|
Asa@0
|
516 end
|
Asa@0
|
517 end
|
Asa@0
|
518
|
Asa@0
|
519 local function ContentResize(this)
|
Asa@0
|
520 if this:GetWidth() and this:GetHeight() then
|
Asa@0
|
521 this.width = this:GetWidth()
|
Asa@0
|
522 this.height = this:GetHeight()
|
Asa@0
|
523 this.obj:DoLayout()
|
Asa@0
|
524 end
|
Asa@0
|
525 end
|
Asa@0
|
526
|
Asa@0
|
527 setmetatable(WidgetContainerBase,{__index=WidgetBase})
|
Asa@0
|
528
|
Asa@0
|
529 --One of these function should be called on each Widget Instance as part of its creation process
|
Asa@0
|
530
|
Asa@0
|
531 --- Register a widget-class as a container for newly created widgets.
|
Asa@0
|
532 -- @param widget The widget class
|
Asa@0
|
533 function AceGUI:RegisterAsContainer(widget)
|
Asa@0
|
534 widget.children = {}
|
Asa@0
|
535 widget.userdata = {}
|
Asa@0
|
536 widget.events = {}
|
Asa@0
|
537 widget.base = WidgetContainerBase
|
Asa@0
|
538 widget.content.obj = widget
|
Asa@0
|
539 widget.frame.obj = widget
|
Asa@0
|
540 widget.content:SetScript("OnSizeChanged",ContentResize)
|
Asa@0
|
541 widget.frame:SetScript("OnSizeChanged",FrameResize)
|
Asa@0
|
542 setmetatable(widget,{__index=WidgetContainerBase})
|
Asa@0
|
543 widget:SetLayout("List")
|
Asa@0
|
544 end
|
Asa@0
|
545
|
Asa@0
|
546 --- Register a widget-class as a widget.
|
Asa@0
|
547 -- @param widget The widget class
|
Asa@0
|
548 function AceGUI:RegisterAsWidget(widget)
|
Asa@0
|
549 widget.userdata = {}
|
Asa@0
|
550 widget.events = {}
|
Asa@0
|
551 widget.base = WidgetBase
|
Asa@0
|
552 widget.frame.obj = widget
|
Asa@0
|
553 widget.frame:SetScript("OnSizeChanged",FrameResize)
|
Asa@0
|
554 setmetatable(widget,{__index=WidgetBase})
|
Asa@0
|
555 end
|
Asa@0
|
556 end
|
Asa@0
|
557
|
Asa@0
|
558
|
Asa@0
|
559
|
Asa@0
|
560
|
Asa@0
|
561 ------------------
|
Asa@0
|
562 -- Widget API --
|
Asa@0
|
563 ------------------
|
Asa@0
|
564
|
Asa@0
|
565 --- Registers a widget Constructor, this function returns a new instance of the Widget
|
Asa@0
|
566 -- @param Name The name of the widget
|
Asa@0
|
567 -- @param Constructor The widget constructor function
|
Asa@0
|
568 -- @param Version The version of the widget
|
Asa@0
|
569 function AceGUI:RegisterWidgetType(Name, Constructor, Version)
|
Asa@0
|
570 assert(type(Constructor) == "function")
|
Asa@0
|
571 assert(type(Version) == "number")
|
Asa@0
|
572
|
Asa@0
|
573 local oldVersion = WidgetVersions[Name]
|
Asa@0
|
574 if oldVersion and oldVersion >= Version then return end
|
Asa@0
|
575
|
Asa@0
|
576 WidgetVersions[Name] = Version
|
Asa@0
|
577 WidgetRegistry[Name] = Constructor
|
Asa@0
|
578 end
|
Asa@0
|
579
|
Asa@0
|
580 --- Registers a Layout Function
|
Asa@0
|
581 -- @param Name The name of the layout
|
Asa@0
|
582 -- @param LayoutFunc Reference to the layout function
|
Asa@0
|
583 function AceGUI:RegisterLayout(Name, LayoutFunc)
|
Asa@0
|
584 assert(type(LayoutFunc) == "function")
|
Asa@0
|
585 if type(Name) == "string" then
|
Asa@0
|
586 Name = Name:upper()
|
Asa@0
|
587 end
|
Asa@0
|
588 LayoutRegistry[Name] = LayoutFunc
|
Asa@0
|
589 end
|
Asa@0
|
590
|
Asa@0
|
591 --- Get a Layout Function from the registry
|
Asa@0
|
592 -- @param Name The name of the layout
|
Asa@0
|
593 function AceGUI:GetLayout(Name)
|
Asa@0
|
594 if type(Name) == "string" then
|
Asa@0
|
595 Name = Name:upper()
|
Asa@0
|
596 end
|
Asa@0
|
597 return LayoutRegistry[Name]
|
Asa@0
|
598 end
|
Asa@0
|
599
|
Asa@0
|
600 AceGUI.counts = AceGUI.counts or {}
|
Asa@0
|
601
|
Asa@0
|
602 --- A type-based counter to count the number of widgets created.
|
Asa@0
|
603 -- This is used by widgets that require a named frame, e.g. when a Blizzard
|
Asa@0
|
604 -- Template requires it.
|
Asa@0
|
605 -- @param type The widget type
|
Asa@0
|
606 function AceGUI:GetNextWidgetNum(type)
|
Asa@0
|
607 if not self.counts[type] then
|
Asa@0
|
608 self.counts[type] = 0
|
Asa@0
|
609 end
|
Asa@0
|
610 self.counts[type] = self.counts[type] + 1
|
Asa@0
|
611 return self.counts[type]
|
Asa@0
|
612 end
|
Asa@0
|
613
|
Asa@0
|
614 --[[ Widget Template
|
Asa@0
|
615
|
Asa@0
|
616 --------------------------
|
Asa@0
|
617 -- Widget Name --
|
Asa@0
|
618 --------------------------
|
Asa@0
|
619 do
|
Asa@0
|
620 local Type = "Type"
|
Asa@0
|
621
|
Asa@0
|
622 local function OnAcquire(self)
|
Asa@0
|
623
|
Asa@0
|
624 end
|
Asa@0
|
625
|
Asa@0
|
626 local function OnRelease(self)
|
Asa@0
|
627 self.frame:ClearAllPoints()
|
Asa@0
|
628 self.frame:Hide()
|
Asa@0
|
629 end
|
Asa@0
|
630
|
Asa@0
|
631
|
Asa@0
|
632 local function Constructor()
|
Asa@0
|
633 local frame = CreateFrame("Frame",nil,UIParent)
|
Asa@0
|
634 local self = {}
|
Asa@0
|
635 self.type = Type
|
Asa@0
|
636
|
Asa@0
|
637 self.OnRelease = OnRelease
|
Asa@0
|
638 self.OnAcquire = OnAcquire
|
Asa@0
|
639
|
Asa@0
|
640 self.frame = frame
|
Asa@0
|
641 frame.obj = self
|
Asa@0
|
642
|
Asa@0
|
643 --Container Support
|
Asa@0
|
644 --local content = CreateFrame("Frame",nil,frame)
|
Asa@0
|
645 --self.content = content
|
Asa@0
|
646
|
Asa@0
|
647 --AceGUI:RegisterAsContainer(self)
|
Asa@0
|
648 AceGUI:RegisterAsWidget(self)
|
Asa@0
|
649 return self
|
Asa@0
|
650 end
|
Asa@0
|
651
|
Asa@0
|
652 AceGUI:RegisterWidgetType(Type,Constructor)
|
Asa@0
|
653 end
|
Asa@0
|
654
|
Asa@0
|
655
|
Asa@0
|
656 ]]
|
Asa@0
|
657
|
Asa@0
|
658 -------------
|
Asa@0
|
659 -- Layouts --
|
Asa@0
|
660 -------------
|
Asa@0
|
661
|
Asa@0
|
662 --[[
|
Asa@0
|
663 A Layout is a func that takes 2 parameters
|
Asa@0
|
664 content - the frame that widgets will be placed inside
|
Asa@0
|
665 children - a table containing the widgets to layout
|
Asa@0
|
666
|
Asa@0
|
667 ]]
|
Asa@0
|
668
|
Asa@0
|
669 -- Very simple Layout, Children are stacked on top of each other down the left side
|
Asa@0
|
670 AceGUI:RegisterLayout("List",
|
Asa@0
|
671 function(content, children)
|
Asa@0
|
672
|
Asa@0
|
673 local height = 0
|
Asa@0
|
674 local width = content.width or content:GetWidth() or 0
|
Asa@0
|
675 for i = 1, #children do
|
Asa@0
|
676 local child = children[i]
|
Asa@0
|
677
|
Asa@0
|
678 local frame = child.frame
|
Asa@0
|
679 frame:ClearAllPoints()
|
Asa@0
|
680 frame:Show()
|
Asa@0
|
681 if i == 1 then
|
Asa@0
|
682 frame:SetPoint("TOPLEFT",content,"TOPLEFT",0,0)
|
Asa@0
|
683 else
|
Asa@0
|
684 frame:SetPoint("TOPLEFT",children[i-1].frame,"BOTTOMLEFT",0,0)
|
Asa@0
|
685 end
|
Asa@0
|
686
|
Asa@0
|
687 if child.width == "fill" then
|
Asa@0
|
688 child:SetWidth(width)
|
Asa@0
|
689 frame:SetPoint("RIGHT",content,"RIGHT")
|
Asa@0
|
690 if child.OnWidthSet then
|
Asa@0
|
691 child:OnWidthSet(content.width or content:GetWidth())
|
Asa@0
|
692 end
|
Asa@0
|
693 if child.DoLayout then
|
Asa@0
|
694 child:DoLayout()
|
Asa@0
|
695 end
|
Asa@0
|
696 elseif child.width == "relative" then
|
Asa@0
|
697 child:SetWidth(width * child.relWidth)
|
Asa@0
|
698 if child.OnWidthSet then
|
Asa@0
|
699 child:OnWidthSet(content.width or content:GetWidth())
|
Asa@0
|
700 end
|
Asa@0
|
701 if child.DoLayout then
|
Asa@0
|
702 child:DoLayout()
|
Asa@0
|
703 end
|
Asa@0
|
704 end
|
Asa@0
|
705
|
Asa@0
|
706 height = height + (frame.height or frame:GetHeight() or 0)
|
Asa@0
|
707 end
|
Asa@0
|
708 safecall( content.obj.LayoutFinished, content.obj, nil, height )
|
Asa@0
|
709 end
|
Asa@0
|
710 )
|
Asa@0
|
711
|
Asa@0
|
712 -- A single control fills the whole content area
|
Asa@0
|
713 AceGUI:RegisterLayout("Fill",
|
Asa@0
|
714 function(content, children)
|
Asa@0
|
715 if children[1] then
|
Asa@0
|
716 children[1]:SetWidth(content:GetWidth() or 0)
|
Asa@0
|
717 children[1]:SetHeight(content:GetHeight() or 0)
|
Asa@0
|
718 children[1].frame:SetAllPoints(content)
|
Asa@0
|
719 children[1].frame:Show()
|
Asa@0
|
720 safecall( content.obj.LayoutFinished, content.obj, nil, children[1].frame:GetHeight() )
|
Asa@0
|
721 end
|
Asa@0
|
722 end
|
Asa@0
|
723 )
|
Asa@0
|
724
|
Asa@0
|
725 AceGUI:RegisterLayout("Flow",
|
Asa@0
|
726 function(content, children)
|
Asa@0
|
727 --used height so far
|
Asa@0
|
728 local height = 0
|
Asa@0
|
729 --width used in the current row
|
Asa@0
|
730 local usedwidth = 0
|
Asa@0
|
731 --height of the current row
|
Asa@0
|
732 local rowheight = 0
|
Asa@0
|
733 local rowoffset = 0
|
Asa@0
|
734 local lastrowoffset
|
Asa@0
|
735
|
Asa@0
|
736 local width = content.width or content:GetWidth() or 0
|
Asa@0
|
737
|
Asa@0
|
738 --control at the start of the row
|
Asa@0
|
739 local rowstart
|
Asa@0
|
740 local rowstartoffset
|
Asa@0
|
741 local lastrowstart
|
Asa@0
|
742 local isfullheight
|
Asa@0
|
743
|
Asa@0
|
744 local frameoffset
|
Asa@0
|
745 local lastframeoffset
|
Asa@0
|
746 local oversize
|
Asa@0
|
747 for i = 1, #children do
|
Asa@0
|
748 local child = children[i]
|
Asa@0
|
749 oversize = nil
|
Asa@0
|
750 local frame = child.frame
|
Asa@0
|
751 local frameheight = frame.height or frame:GetHeight() or 0
|
Asa@0
|
752 local framewidth = frame.width or frame:GetWidth() or 0
|
Asa@0
|
753 lastframeoffset = frameoffset
|
Asa@0
|
754 -- HACK: Why did we set a frameoffset of (frameheight / 2) ?
|
Asa@0
|
755 -- That was moving all widgets half the widgets size down, is that intended?
|
Asa@0
|
756 -- Actually, it seems to be neccessary for many cases, we'll leave it in for now.
|
Asa@0
|
757 -- If widgets seem to anchor weirdly with this, provide a valid alignoffset for them.
|
Asa@0
|
758 -- TODO: Investigate moar!
|
Asa@0
|
759 frameoffset = child.alignoffset or (frameheight / 2)
|
Asa@0
|
760
|
Asa@0
|
761 if child.width == "relative" then
|
Asa@0
|
762 framewidth = width * child.relWidth
|
Asa@0
|
763 end
|
Asa@0
|
764
|
Asa@0
|
765 frame:Show()
|
Asa@0
|
766 frame:ClearAllPoints()
|
Asa@0
|
767 if i == 1 then
|
Asa@0
|
768 -- anchor the first control to the top left
|
Asa@0
|
769 frame:SetPoint("TOPLEFT",content,"TOPLEFT",0,0)
|
Asa@0
|
770 rowheight = frameheight
|
Asa@0
|
771 rowoffset = frameoffset
|
Asa@0
|
772 rowstart = frame
|
Asa@0
|
773 rowstartoffset = frameoffset
|
Asa@0
|
774 usedwidth = framewidth
|
Asa@0
|
775 if usedwidth > width then
|
Asa@0
|
776 oversize = true
|
Asa@0
|
777 end
|
Asa@0
|
778 else
|
Asa@0
|
779 -- if there isn't available width for the control start a new row
|
Asa@0
|
780 -- if a control is "fill" it will be on a row of its own full width
|
Asa@0
|
781 if usedwidth == 0 or ((framewidth) + usedwidth > width) or child.width == "fill" then
|
Asa@0
|
782 if isfullheight then
|
Asa@0
|
783 -- a previous row has already filled the entire height, there's nothing we can usefully do anymore
|
Asa@0
|
784 -- (maybe error/warn about this?)
|
Asa@0
|
785 break
|
Asa@0
|
786 end
|
Asa@0
|
787 --anchor the previous row, we will now know its height and offset
|
Asa@0
|
788 rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-(height+(rowoffset-rowstartoffset)+3))
|
Asa@0
|
789 height = height + rowheight + 3
|
Asa@0
|
790 --save this as the rowstart so we can anchor it after the row is complete and we have the max height and offset of controls in it
|
Asa@0
|
791 rowstart = frame
|
Asa@0
|
792 rowstartoffset = frameoffset
|
Asa@0
|
793 rowheight = frameheight
|
Asa@0
|
794 rowoffset = frameoffset
|
Asa@0
|
795 usedwidth = framewidth
|
Asa@0
|
796 if usedwidth > width then
|
Asa@0
|
797 oversize = true
|
Asa@0
|
798 end
|
Asa@0
|
799 -- put the control on the current row, adding it to the width and checking if the height needs to be increased
|
Asa@0
|
800 else
|
Asa@0
|
801 --handles cases where the new height is higher than either control because of the offsets
|
Asa@0
|
802 --math.max(rowheight-rowoffset+frameoffset, frameheight-frameoffset+rowoffset)
|
Asa@0
|
803
|
Asa@0
|
804 --offset is always the larger of the two offsets
|
Asa@0
|
805 rowoffset = math_max(rowoffset, frameoffset)
|
Asa@0
|
806
|
Asa@0
|
807 rowheight = math_max(rowheight,rowoffset+(frameheight/2))
|
Asa@0
|
808 --print("type:", child.type, "offset:",frameoffset-lastframeoffset)
|
Asa@0
|
809 frame:SetPoint("TOPLEFT",children[i-1].frame,"TOPRIGHT",0,frameoffset-lastframeoffset)
|
Asa@0
|
810 usedwidth = framewidth + usedwidth
|
Asa@0
|
811 end
|
Asa@0
|
812 end
|
Asa@0
|
813
|
Asa@0
|
814 if child.width == "fill" then
|
Asa@0
|
815 child:SetWidth(width)
|
Asa@0
|
816 frame:SetPoint("RIGHT",content,"RIGHT",0,0)
|
Asa@0
|
817
|
Asa@0
|
818 usedwidth = 0
|
Asa@0
|
819 rowstart = frame
|
Asa@0
|
820 rowstartoffset = frameoffset
|
Asa@0
|
821
|
Asa@0
|
822 if child.OnWidthSet then
|
Asa@0
|
823 child:OnWidthSet(width)
|
Asa@0
|
824 end
|
Asa@0
|
825 if child.DoLayout then
|
Asa@0
|
826 child:DoLayout()
|
Asa@0
|
827 end
|
Asa@0
|
828 rowheight = frame.height or frame:GetHeight() or 0
|
Asa@0
|
829 rowoffset = child.alignoffset or (rowheight / 2)
|
Asa@0
|
830 rowstartoffset = rowoffset
|
Asa@0
|
831 elseif child.width == "relative" then
|
Asa@0
|
832 child:SetWidth(width * child.relWidth)
|
Asa@0
|
833
|
Asa@0
|
834 if child.OnWidthSet then
|
Asa@0
|
835 child:OnWidthSet(width)
|
Asa@0
|
836 end
|
Asa@0
|
837
|
Asa@0
|
838 if child.DoLayout then
|
Asa@0
|
839 child:DoLayout()
|
Asa@0
|
840 end
|
Asa@0
|
841 elseif oversize then
|
Asa@0
|
842 if width > 1 then
|
Asa@0
|
843 frame:SetPoint("RIGHT",content,"RIGHT",0,0)
|
Asa@0
|
844 end
|
Asa@0
|
845 end
|
Asa@0
|
846
|
Asa@0
|
847 if child.height == "fill" then
|
Asa@0
|
848 frame:SetPoint("BOTTOM",content,"BOTTOM")
|
Asa@0
|
849 isfullheight = true
|
Asa@0
|
850 end
|
Asa@0
|
851 end
|
Asa@0
|
852
|
Asa@0
|
853 --anchor the last row, if its full height needs a special case since its height has just been changed by the anchor
|
Asa@0
|
854 if isfullheight then
|
Asa@0
|
855 rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-height)
|
Asa@0
|
856 elseif rowstart then
|
Asa@0
|
857 rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-(height+(rowoffset-rowstartoffset)+3))
|
Asa@0
|
858 end
|
Asa@0
|
859
|
Asa@0
|
860 height = height + rowheight + 3
|
Asa@0
|
861 safecall( content.obj.LayoutFinished, content.obj, nil, height )
|
Asa@0
|
862 end
|
Asa@0
|
863 )
|