annotate Libs/AceGUI-3.0/AceGUI-3.0.lua @ 25:ac501d71c890 tip

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