annotate Libs/AceGUI-3.0/AceGUI-3.0.lua @ 0:c6ff7ba0e8f6

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