comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:169f5211fc7f
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 896 2009-12-06 16:29:49Z nevcairiel $
28 local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 30
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(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 OnRelease() - Called when the object is Released, should remove any anchors and hide the Widget
271
272 And the following members
273 frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes
274 type - the type of the object, same as the name given to :RegisterWidget()
275
276 Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet
277 It will be cleared automatically when a widget is released
278 Placing values directly into a widget object should be avoided
279
280 If the Widget can act as a container for other Widgets the following
281 content - frame or derivitive that children will be anchored to
282
283 The Widget can supply the following Optional Members
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 function fixlevels(parent,...)
298 local i = 1
299 local child = select(i, ...)
300 while child do
301 child:SetFrameLevel(parent:GetFrameLevel()+1)
302 fixlevels(child, child:GetChildren())
303 i = i + 1
304 child = select(i, ...)
305 end
306 end
307
308 local WidgetBase = AceGUI.WidgetBase
309
310 WidgetBase.SetParent = function(self, parent)
311 local frame = self.frame
312 frame:SetParent(nil)
313 frame:SetParent(parent.content)
314 self.parent = parent
315 --fixlevels(parent.frame,parent.frame:GetChildren())
316 end
317
318 WidgetBase.SetCallback = function(self, name, func)
319 if type(func) == "function" then
320 self.events[name] = func
321 end
322 end
323
324 WidgetBase.Fire = function(self, name, ...)
325 if self.events[name] then
326 local success, ret = safecall(self.events[name], self, name, ...)
327 if success then
328 return ret
329 end
330 end
331 end
332
333 WidgetBase.SetWidth = function(self, width)
334 self.frame:SetWidth(width)
335 self.frame.width = width
336 if self.OnWidthSet then
337 self:OnWidthSet(width)
338 end
339 end
340
341 WidgetBase.SetRelativeWidth = function(self, width)
342 if width <= 0 or width > 1 then
343 error(":SetRelativeWidth(width): Invalid relative width.", 2)
344 end
345 self.relWidth = width
346 self.width = "relative"
347 end
348
349 WidgetBase.SetHeight = function(self, height)
350 self.frame:SetHeight(height)
351 self.frame.height = height
352 if self.OnHeightSet then
353 self:OnHeightSet(height)
354 end
355 end
356
357 --[[ WidgetBase.SetRelativeHeight = function(self, height)
358 if height <= 0 or height > 1 then
359 error(":SetRelativeHeight(height): Invalid relative height.", 2)
360 end
361 self.relHeight = height
362 self.height = "relative"
363 end ]]
364
365 WidgetBase.IsVisible = function(self)
366 return self.frame:IsVisible()
367 end
368
369 WidgetBase.IsShown= function(self)
370 return self.frame:IsShown()
371 end
372
373 WidgetBase.Release = function(self)
374 AceGUI:Release(self)
375 end
376
377 WidgetBase.SetPoint = function(self, ...)
378 return self.frame:SetPoint(...)
379 end
380
381 WidgetBase.ClearAllPoints = function(self)
382 return self.frame:ClearAllPoints()
383 end
384
385 WidgetBase.GetNumPoints = function(self)
386 return self.frame:GetNumPoints()
387 end
388
389 WidgetBase.GetPoint = function(self, ...)
390 return self.frame:GetPoint(...)
391 end
392
393 WidgetBase.GetUserDataTable = function(self)
394 return self.userdata
395 end
396
397 WidgetBase.SetUserData = function(self, key, value)
398 self.userdata[key] = value
399 end
400
401 WidgetBase.GetUserData = function(self, key)
402 return self.userdata[key]
403 end
404
405 WidgetBase.IsFullHeight = function(self)
406 return self.height == "fill"
407 end
408
409 WidgetBase.SetFullHeight = function(self, isFull)
410 if isFull then
411 self.height = "fill"
412 else
413 self.height = nil
414 end
415 end
416
417 WidgetBase.IsFullWidth = function(self)
418 return self.width == "fill"
419 end
420
421 WidgetBase.SetFullWidth = function(self, isFull)
422 if isFull then
423 self.width = "fill"
424 else
425 self.width = nil
426 end
427 end
428
429 -- local function LayoutOnUpdate(this)
430 -- this:SetScript("OnUpdate",nil)
431 -- this.obj:PerformLayout()
432 -- end
433
434 local WidgetContainerBase = AceGUI.WidgetContainerBase
435
436 WidgetContainerBase.PauseLayout = function(self)
437 self.LayoutPaused = true
438 end
439
440 WidgetContainerBase.ResumeLayout = function(self)
441 self.LayoutPaused = nil
442 end
443
444 WidgetContainerBase.PerformLayout = function(self)
445 if self.LayoutPaused then
446 return
447 end
448 safecall(self.LayoutFunc,self.content, self.children)
449 end
450
451 --call this function to layout, makes sure layed out objects get a frame to get sizes etc
452 WidgetContainerBase.DoLayout = function(self)
453 self:PerformLayout()
454 -- if not self.parent then
455 -- self.frame:SetScript("OnUpdate", LayoutOnUpdate)
456 -- end
457 end
458
459 WidgetContainerBase.AddChild = function(self, child, beforeWidget)
460 if beforeWidget then
461 local siblingIndex = 1
462 for _, widget in pairs(self.children) do
463 if widget == beforeWidget then
464 break
465 end
466 siblingIndex = siblingIndex + 1
467 end
468 tinsert(self.children, siblingIndex, child)
469 else
470 tinsert(self.children, child)
471 end
472 child:SetParent(self)
473 child.frame:Show()
474 self:DoLayout()
475 end
476
477 WidgetContainerBase.AddChildren = function(self, ...)
478 for i = 1, select("#", ...) do
479 local child = select(i, ...)
480 tinsert(self.children, child)
481 child:SetParent(self)
482 child.frame:Show()
483 end
484 self:DoLayout()
485 end
486
487 WidgetContainerBase.ReleaseChildren = function(self)
488 local children = self.children
489 for i = 1,#children do
490 AceGUI:Release(children[i])
491 children[i] = nil
492 end
493 end
494
495 WidgetContainerBase.SetLayout = function(self, Layout)
496 self.LayoutFunc = AceGUI:GetLayout(Layout)
497 end
498
499 WidgetContainerBase.SetAutoAdjustHeight = function(self, adjust)
500 if adjust then
501 self.noAutoHeight = nil
502 else
503 self.noAutoHeight = true
504 end
505 end
506
507 local function FrameResize(this)
508 local self = this.obj
509 if this:GetWidth() and this:GetHeight() then
510 if self.OnWidthSet then
511 self:OnWidthSet(this:GetWidth())
512 end
513 if self.OnHeightSet then
514 self:OnHeightSet(this:GetHeight())
515 end
516 end
517 end
518
519 local function ContentResize(this)
520 if this:GetWidth() and this:GetHeight() then
521 this.width = this:GetWidth()
522 this.height = this:GetHeight()
523 this.obj:DoLayout()
524 end
525 end
526
527 setmetatable(WidgetContainerBase,{__index=WidgetBase})
528
529 --One of these function should be called on each Widget Instance as part of its creation process
530
531 --- Register a widget-class as a container for newly created widgets.
532 -- @param widget The widget class
533 function AceGUI:RegisterAsContainer(widget)
534 widget.children = {}
535 widget.userdata = {}
536 widget.events = {}
537 widget.base = WidgetContainerBase
538 widget.content.obj = widget
539 widget.frame.obj = widget
540 widget.content:SetScript("OnSizeChanged",ContentResize)
541 widget.frame:SetScript("OnSizeChanged",FrameResize)
542 setmetatable(widget,{__index=WidgetContainerBase})
543 widget:SetLayout("List")
544 end
545
546 --- Register a widget-class as a widget.
547 -- @param widget The widget class
548 function AceGUI:RegisterAsWidget(widget)
549 widget.userdata = {}
550 widget.events = {}
551 widget.base = WidgetBase
552 widget.frame.obj = widget
553 widget.frame:SetScript("OnSizeChanged",FrameResize)
554 setmetatable(widget,{__index=WidgetBase})
555 end
556 end
557
558
559
560
561 ------------------
562 -- Widget API --
563 ------------------
564
565 --- Registers a widget Constructor, this function returns a new instance of the Widget
566 -- @param Name The name of the widget
567 -- @param Constructor The widget constructor function
568 -- @param Version The version of the widget
569 function AceGUI:RegisterWidgetType(Name, Constructor, Version)
570 assert(type(Constructor) == "function")
571 assert(type(Version) == "number")
572
573 local oldVersion = WidgetVersions[Name]
574 if oldVersion and oldVersion >= Version then return end
575
576 WidgetVersions[Name] = Version
577 WidgetRegistry[Name] = Constructor
578 end
579
580 --- Registers a Layout Function
581 -- @param Name The name of the layout
582 -- @param LayoutFunc Reference to the layout function
583 function AceGUI:RegisterLayout(Name, LayoutFunc)
584 assert(type(LayoutFunc) == "function")
585 if type(Name) == "string" then
586 Name = Name:upper()
587 end
588 LayoutRegistry[Name] = LayoutFunc
589 end
590
591 --- Get a Layout Function from the registry
592 -- @param Name The name of the layout
593 function AceGUI:GetLayout(Name)
594 if type(Name) == "string" then
595 Name = Name:upper()
596 end
597 return LayoutRegistry[Name]
598 end
599
600 AceGUI.counts = AceGUI.counts or {}
601
602 --- A type-based counter to count the number of widgets created.
603 -- This is used by widgets that require a named frame, e.g. when a Blizzard
604 -- Template requires it.
605 -- @param type The widget type
606 function AceGUI:GetNextWidgetNum(type)
607 if not self.counts[type] then
608 self.counts[type] = 0
609 end
610 self.counts[type] = self.counts[type] + 1
611 return self.counts[type]
612 end
613
614 --[[ Widget Template
615
616 --------------------------
617 -- Widget Name --
618 --------------------------
619 do
620 local Type = "Type"
621
622 local function OnAcquire(self)
623
624 end
625
626 local function OnRelease(self)
627 self.frame:ClearAllPoints()
628 self.frame:Hide()
629 end
630
631
632 local function Constructor()
633 local frame = CreateFrame("Frame",nil,UIParent)
634 local self = {}
635 self.type = Type
636
637 self.OnRelease = OnRelease
638 self.OnAcquire = OnAcquire
639
640 self.frame = frame
641 frame.obj = self
642
643 --Container Support
644 --local content = CreateFrame("Frame",nil,frame)
645 --self.content = content
646
647 --AceGUI:RegisterAsContainer(self)
648 AceGUI:RegisterAsWidget(self)
649 return self
650 end
651
652 AceGUI:RegisterWidgetType(Type,Constructor)
653 end
654
655
656 ]]
657
658 -------------
659 -- Layouts --
660 -------------
661
662 --[[
663 A Layout is a func that takes 2 parameters
664 content - the frame that widgets will be placed inside
665 children - a table containing the widgets to layout
666
667 ]]
668
669 -- Very simple Layout, Children are stacked on top of each other down the left side
670 AceGUI:RegisterLayout("List",
671 function(content, children)
672
673 local height = 0
674 local width = content.width or content:GetWidth() or 0
675 for i = 1, #children do
676 local child = children[i]
677
678 local frame = child.frame
679 frame:ClearAllPoints()
680 frame:Show()
681 if i == 1 then
682 frame:SetPoint("TOPLEFT",content,"TOPLEFT",0,0)
683 else
684 frame:SetPoint("TOPLEFT",children[i-1].frame,"BOTTOMLEFT",0,0)
685 end
686
687 if child.width == "fill" then
688 child:SetWidth(width)
689 frame:SetPoint("RIGHT",content,"RIGHT")
690 if child.OnWidthSet then
691 child:OnWidthSet(content.width or content:GetWidth())
692 end
693 if child.DoLayout then
694 child:DoLayout()
695 end
696 elseif child.width == "relative" then
697 child:SetWidth(width * child.relWidth)
698 if child.OnWidthSet then
699 child:OnWidthSet(content.width or content:GetWidth())
700 end
701 if child.DoLayout then
702 child:DoLayout()
703 end
704 end
705
706 height = height + (frame.height or frame:GetHeight() or 0)
707 end
708 safecall( content.obj.LayoutFinished, content.obj, nil, height )
709 end
710 )
711
712 -- A single control fills the whole content area
713 AceGUI:RegisterLayout("Fill",
714 function(content, children)
715 if children[1] then
716 children[1]:SetWidth(content:GetWidth() or 0)
717 children[1]:SetHeight(content:GetHeight() or 0)
718 children[1].frame:SetAllPoints(content)
719 children[1].frame:Show()
720 safecall( content.obj.LayoutFinished, content.obj, nil, children[1].frame:GetHeight() )
721 end
722 end
723 )
724
725 AceGUI:RegisterLayout("Flow",
726 function(content, children)
727 --used height so far
728 local height = 0
729 --width used in the current row
730 local usedwidth = 0
731 --height of the current row
732 local rowheight = 0
733 local rowoffset = 0
734 local lastrowoffset
735
736 local width = content.width or content:GetWidth() or 0
737
738 --control at the start of the row
739 local rowstart
740 local rowstartoffset
741 local lastrowstart
742 local isfullheight
743
744 local frameoffset
745 local lastframeoffset
746 local oversize
747 for i = 1, #children do
748 local child = children[i]
749 oversize = nil
750 local frame = child.frame
751 local frameheight = frame.height or frame:GetHeight() or 0
752 local framewidth = frame.width or frame:GetWidth() or 0
753 lastframeoffset = frameoffset
754 -- HACK: Why did we set a frameoffset of (frameheight / 2) ?
755 -- That was moving all widgets half the widgets size down, is that intended?
756 -- Actually, it seems to be neccessary for many cases, we'll leave it in for now.
757 -- If widgets seem to anchor weirdly with this, provide a valid alignoffset for them.
758 -- TODO: Investigate moar!
759 frameoffset = child.alignoffset or (frameheight / 2)
760
761 if child.width == "relative" then
762 framewidth = width * child.relWidth
763 end
764
765 frame:Show()
766 frame:ClearAllPoints()
767 if i == 1 then
768 -- anchor the first control to the top left
769 frame:SetPoint("TOPLEFT",content,"TOPLEFT",0,0)
770 rowheight = frameheight
771 rowoffset = frameoffset
772 rowstart = frame
773 rowstartoffset = frameoffset
774 usedwidth = framewidth
775 if usedwidth > width then
776 oversize = true
777 end
778 else
779 -- if there isn't available width for the control start a new row
780 -- if a control is "fill" it will be on a row of its own full width
781 if usedwidth == 0 or ((framewidth) + usedwidth > width) or child.width == "fill" then
782 if isfullheight then
783 -- a previous row has already filled the entire height, there's nothing we can usefully do anymore
784 -- (maybe error/warn about this?)
785 break
786 end
787 --anchor the previous row, we will now know its height and offset
788 rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-(height+(rowoffset-rowstartoffset)+3))
789 height = height + rowheight + 3
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
791 rowstart = frame
792 rowstartoffset = frameoffset
793 rowheight = frameheight
794 rowoffset = frameoffset
795 usedwidth = framewidth
796 if usedwidth > width then
797 oversize = true
798 end
799 -- put the control on the current row, adding it to the width and checking if the height needs to be increased
800 else
801 --handles cases where the new height is higher than either control because of the offsets
802 --math.max(rowheight-rowoffset+frameoffset, frameheight-frameoffset+rowoffset)
803
804 --offset is always the larger of the two offsets
805 rowoffset = math_max(rowoffset, frameoffset)
806
807 rowheight = math_max(rowheight,rowoffset+(frameheight/2))
808 --print("type:", child.type, "offset:",frameoffset-lastframeoffset)
809 frame:SetPoint("TOPLEFT",children[i-1].frame,"TOPRIGHT",0,frameoffset-lastframeoffset)
810 usedwidth = framewidth + usedwidth
811 end
812 end
813
814 if child.width == "fill" then
815 child:SetWidth(width)
816 frame:SetPoint("RIGHT",content,"RIGHT",0,0)
817
818 usedwidth = 0
819 rowstart = frame
820 rowstartoffset = frameoffset
821
822 if child.OnWidthSet then
823 child:OnWidthSet(width)
824 end
825 if child.DoLayout then
826 child:DoLayout()
827 end
828 rowheight = frame.height or frame:GetHeight() or 0
829 rowoffset = child.alignoffset or (rowheight / 2)
830 rowstartoffset = rowoffset
831 elseif child.width == "relative" then
832 child:SetWidth(width * child.relWidth)
833
834 if child.OnWidthSet then
835 child:OnWidthSet(width)
836 end
837
838 if child.DoLayout then
839 child:DoLayout()
840 end
841 elseif oversize then
842 if width > 1 then
843 frame:SetPoint("RIGHT",content,"RIGHT",0,0)
844 end
845 end
846
847 if child.height == "fill" then
848 frame:SetPoint("BOTTOM",content,"BOTTOM")
849 isfullheight = true
850 end
851 end
852
853 --anchor the last row, if its full height needs a special case since its height has just been changed by the anchor
854 if isfullheight then
855 rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-height)
856 elseif rowstart then
857 rowstart:SetPoint("TOPLEFT",content,"TOPLEFT",0,-(height+(rowoffset-rowstartoffset)+3))
858 end
859
860 height = height + rowheight + 3
861 safecall( content.obj.LayoutFinished, content.obj, nil, height )
862 end
863 )