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