annotate Libs/AceGUI-3.0/AceGUI-3.0.lua @ 15:21cefa363c73 tip

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