annotate Libs/AceGUI-3.0/AceGUI-3.0.lua @ 0:169f5211fc7f

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