tercio@0: --- **AceGUI-3.0** provides access to numerous widgets which can be used to create GUIs. tercio@0: -- AceGUI is used by AceConfigDialog to create the option GUIs, but you can use it by itself tercio@0: -- to create any custom GUI. There are more extensive examples in the test suite in the Ace3 tercio@0: -- stand-alone distribution. tercio@0: -- tercio@0: -- **Note**: When using AceGUI-3.0 directly, please do not modify the frames of the widgets directly, tercio@0: -- as any "unknown" change to the widgets will cause addons that get your widget out of the widget pool tercio@0: -- to misbehave. If you think some part of a widget should be modifiable, please open a ticket, and we"ll tercio@0: -- implement a proper API to modify it. tercio@0: -- @usage tercio@0: -- local AceGUI = LibStub("AceGUI-3.0") tercio@0: -- -- Create a container frame tercio@0: -- local f = AceGUI:Create("Frame") tercio@0: -- f:SetCallback("OnClose",function(widget) AceGUI:Release(widget) end) tercio@0: -- f:SetTitle("AceGUI-3.0 Example") tercio@0: -- f:SetStatusText("Status Bar") tercio@0: -- f:SetLayout("Flow") tercio@0: -- -- Create a button tercio@0: -- local btn = AceGUI:Create("Button") tercio@0: -- btn:SetWidth(170) tercio@0: -- btn:SetText("Button !") tercio@0: -- btn:SetCallback("OnClick", function() print("Click!") end) tercio@0: -- -- Add the button to the container tercio@0: -- f:AddChild(btn) tercio@0: -- @class file tercio@0: -- @name AceGUI-3.0 tercio@0: -- @release $Id: AceGUI-3.0.lua 1102 2013-10-25 14:15:23Z nevcairiel $ tercio@0: local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 34 tercio@0: local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR) tercio@0: tercio@0: if not AceGUI then return end -- No upgrade needed tercio@0: tercio@0: -- Lua APIs tercio@0: local tconcat, tremove, tinsert = table.concat, table.remove, table.insert tercio@0: local select, pairs, next, type = select, pairs, next, type tercio@0: local error, assert, loadstring = error, assert, loadstring tercio@0: local setmetatable, rawget, rawset = setmetatable, rawget, rawset tercio@0: local math_max = math.max tercio@0: tercio@0: -- WoW APIs tercio@0: local UIParent = UIParent tercio@0: tercio@0: -- Global vars/functions that we don't upvalue since they might get hooked, or upgraded tercio@0: -- List them here for Mikk's FindGlobals script tercio@0: -- GLOBALS: geterrorhandler, LibStub tercio@0: tercio@0: --local con = LibStub("AceConsole-3.0",true) tercio@0: tercio@0: AceGUI.WidgetRegistry = AceGUI.WidgetRegistry or {} tercio@0: AceGUI.LayoutRegistry = AceGUI.LayoutRegistry or {} tercio@0: AceGUI.WidgetBase = AceGUI.WidgetBase or {} tercio@0: AceGUI.WidgetContainerBase = AceGUI.WidgetContainerBase or {} tercio@0: AceGUI.WidgetVersions = AceGUI.WidgetVersions or {} tercio@0: tercio@0: -- local upvalues tercio@0: local WidgetRegistry = AceGUI.WidgetRegistry tercio@0: local LayoutRegistry = AceGUI.LayoutRegistry tercio@0: local WidgetVersions = AceGUI.WidgetVersions tercio@0: tercio@0: --[[ tercio@0: xpcall safecall implementation tercio@0: ]] tercio@0: local xpcall = xpcall tercio@0: tercio@0: local function errorhandler(err) tercio@0: return geterrorhandler()(err) tercio@0: end tercio@0: tercio@0: local function CreateDispatcher(argCount) tercio@0: local code = [[ tercio@0: local xpcall, eh = ... tercio@0: local method, ARGS tercio@0: local function call() return method(ARGS) end tercio@0: tercio@0: local function dispatch(func, ...) tercio@0: method = func tercio@0: if not method then return end tercio@0: ARGS = ... tercio@0: return xpcall(call, eh) tercio@0: end tercio@0: tercio@0: return dispatch tercio@0: ]] tercio@0: tercio@0: local ARGS = {} tercio@0: for i = 1, argCount do ARGS[i] = "arg"..i end tercio@0: code = code:gsub("ARGS", tconcat(ARGS, ", ")) tercio@0: return assert(loadstring(code, "safecall Dispatcher["..argCount.."]"))(xpcall, errorhandler) tercio@0: end tercio@0: tercio@0: local Dispatchers = setmetatable({}, {__index=function(self, argCount) tercio@0: local dispatcher = CreateDispatcher(argCount) tercio@0: rawset(self, argCount, dispatcher) tercio@0: return dispatcher tercio@0: end}) tercio@0: Dispatchers[0] = function(func) tercio@0: return xpcall(func, errorhandler) tercio@0: end tercio@0: tercio@0: local function safecall(func, ...) tercio@0: return Dispatchers[select("#", ...)](func, ...) tercio@0: end tercio@0: tercio@0: -- Recycling functions tercio@0: local newWidget, delWidget tercio@0: do tercio@0: -- Version Upgrade in Minor 29 tercio@0: -- Internal Storage of the objects changed, from an array table tercio@0: -- to a hash table, and additionally we introduced versioning on tercio@0: -- the widgets which would discard all widgets from a pre-29 version tercio@0: -- anyway, so we just clear the storage now, and don't try to tercio@0: -- convert the storage tables to the new format. tercio@0: -- This should generally not cause *many* widgets to end up in trash, tercio@0: -- since once dialogs are opened, all addons should be loaded already tercio@0: -- and AceGUI should be on the latest version available on the users tercio@0: -- setup. tercio@0: -- -- nevcairiel - Nov 2nd, 2009 tercio@0: if oldminor and oldminor < 29 and AceGUI.objPools then tercio@0: AceGUI.objPools = nil tercio@0: end tercio@0: tercio@0: AceGUI.objPools = AceGUI.objPools or {} tercio@0: local objPools = AceGUI.objPools tercio@0: --Returns a new instance, if none are available either returns a new table or calls the given contructor tercio@0: function newWidget(type) tercio@0: if not WidgetRegistry[type] then tercio@0: error("Attempt to instantiate unknown widget type", 2) tercio@0: end tercio@0: tercio@0: if not objPools[type] then tercio@0: objPools[type] = {} tercio@0: end tercio@0: tercio@0: local newObj = next(objPools[type]) tercio@0: if not newObj then tercio@0: newObj = WidgetRegistry[type]() tercio@0: newObj.AceGUIWidgetVersion = WidgetVersions[type] tercio@0: else tercio@0: objPools[type][newObj] = nil tercio@0: -- if the widget is older then the latest, don't even try to reuse it tercio@0: -- just forget about it, and grab a new one. tercio@0: if not newObj.AceGUIWidgetVersion or newObj.AceGUIWidgetVersion < WidgetVersions[type] then tercio@0: return newWidget(type) tercio@0: end tercio@0: end tercio@0: return newObj tercio@0: end tercio@0: -- Releases an instance to the Pool tercio@0: function delWidget(obj,type) tercio@0: if not objPools[type] then tercio@0: objPools[type] = {} tercio@0: end tercio@0: if objPools[type][obj] then tercio@0: error("Attempt to Release Widget that is already released", 2) tercio@0: end tercio@0: objPools[type][obj] = true tercio@0: end tercio@0: end tercio@0: tercio@0: tercio@0: ------------------- tercio@0: -- API Functions -- tercio@0: ------------------- tercio@0: tercio@0: -- Gets a widget Object tercio@0: tercio@0: --- Create a new Widget of the given type. tercio@0: -- This function will instantiate a new widget (or use one from the widget pool), and call the tercio@0: -- OnAcquire function on it, before returning. tercio@0: -- @param type The type of the widget. tercio@0: -- @return The newly created widget. tercio@0: function AceGUI:Create(type) tercio@0: if WidgetRegistry[type] then tercio@0: local widget = newWidget(type) tercio@0: tercio@0: if rawget(widget, "Acquire") then tercio@0: widget.OnAcquire = widget.Acquire tercio@0: widget.Acquire = nil tercio@0: elseif rawget(widget, "Aquire") then tercio@0: widget.OnAcquire = widget.Aquire tercio@0: widget.Aquire = nil tercio@0: end tercio@0: tercio@0: if rawget(widget, "Release") then tercio@0: widget.OnRelease = rawget(widget, "Release") tercio@0: widget.Release = nil tercio@0: end tercio@0: tercio@0: if widget.OnAcquire then tercio@0: widget:OnAcquire() tercio@0: else tercio@0: error(("Widget type %s doesn't supply an OnAcquire Function"):format(type)) tercio@0: end tercio@0: -- Set the default Layout ("List") tercio@0: safecall(widget.SetLayout, widget, "List") tercio@0: safecall(widget.ResumeLayout, widget) tercio@0: return widget tercio@0: end tercio@0: end tercio@0: tercio@0: --- Releases a widget Object. tercio@0: -- This function calls OnRelease on the widget and places it back in the widget pool. tercio@0: -- Any data on the widget is being erased, and the widget will be hidden.\\ tercio@0: -- If this widget is a Container-Widget, all of its Child-Widgets will be releases as well. tercio@0: -- @param widget The widget to release tercio@0: function AceGUI:Release(widget) tercio@0: safecall(widget.PauseLayout, widget) tercio@0: widget:Fire("OnRelease") tercio@0: safecall(widget.ReleaseChildren, widget) tercio@0: tercio@0: if widget.OnRelease then tercio@0: widget:OnRelease() tercio@0: -- else tercio@0: -- error(("Widget type %s doesn't supply an OnRelease Function"):format(widget.type)) tercio@0: end tercio@0: for k in pairs(widget.userdata) do tercio@0: widget.userdata[k] = nil tercio@0: end tercio@0: for k in pairs(widget.events) do tercio@0: widget.events[k] = nil tercio@0: end tercio@0: widget.width = nil tercio@0: widget.relWidth = nil tercio@0: widget.height = nil tercio@0: widget.relHeight = nil tercio@0: widget.noAutoHeight = nil tercio@0: widget.frame:ClearAllPoints() tercio@0: widget.frame:Hide() tercio@0: widget.frame:SetParent(UIParent) tercio@0: widget.frame.width = nil tercio@0: widget.frame.height = nil tercio@0: if widget.content then tercio@0: widget.content.width = nil tercio@0: widget.content.height = nil tercio@0: end tercio@0: delWidget(widget, widget.type) tercio@0: end tercio@0: tercio@0: ----------- tercio@0: -- Focus -- tercio@0: ----------- tercio@0: tercio@0: tercio@0: --- Called when a widget has taken focus. tercio@0: -- e.g. Dropdowns opening, Editboxes gaining kb focus tercio@0: -- @param widget The widget that should be focused tercio@0: function AceGUI:SetFocus(widget) tercio@0: if self.FocusedWidget and self.FocusedWidget ~= widget then tercio@0: safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget) tercio@0: end tercio@0: self.FocusedWidget = widget tercio@0: end tercio@0: tercio@0: tercio@0: --- Called when something has happened that could cause widgets with focus to drop it tercio@0: -- e.g. titlebar of a frame being clicked tercio@0: function AceGUI:ClearFocus() tercio@0: if self.FocusedWidget then tercio@0: safecall(self.FocusedWidget.ClearFocus, self.FocusedWidget) tercio@0: self.FocusedWidget = nil tercio@0: end tercio@0: end tercio@0: tercio@0: ------------- tercio@0: -- Widgets -- tercio@0: ------------- tercio@0: --[[ tercio@0: Widgets must provide the following functions tercio@0: OnAcquire() - Called when the object is acquired, should set everything to a default hidden state tercio@0: tercio@0: And the following members tercio@0: frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes tercio@0: type - the type of the object, same as the name given to :RegisterWidget() tercio@0: tercio@0: Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet tercio@0: It will be cleared automatically when a widget is released tercio@0: Placing values directly into a widget object should be avoided tercio@0: tercio@0: If the Widget can act as a container for other Widgets the following tercio@0: content - frame or derivitive that children will be anchored to tercio@0: tercio@0: The Widget can supply the following Optional Members tercio@0: :OnRelease() - Called when the object is Released, should remove any additional anchors and clear any data tercio@0: :OnWidthSet(width) - Called when the width of the widget is changed tercio@0: :OnHeightSet(height) - Called when the height of the widget is changed tercio@0: Widgets should not use the OnSizeChanged events of thier frame or content members, use these methods instead tercio@0: AceGUI already sets a handler to the event tercio@0: :LayoutFinished(width, height) - called after a layout has finished, the width and height will be the width and height of the tercio@0: area used for controls. These can be nil if the layout used the existing size to layout the controls. tercio@0: tercio@0: ]] tercio@0: tercio@0: -------------------------- tercio@0: -- Widget Base Template -- tercio@0: -------------------------- tercio@0: do tercio@0: local WidgetBase = AceGUI.WidgetBase tercio@0: tercio@0: WidgetBase.SetParent = function(self, parent) tercio@0: local frame = self.frame tercio@0: frame:SetParent(nil) tercio@0: frame:SetParent(parent.content) tercio@0: self.parent = parent tercio@0: end tercio@0: tercio@0: WidgetBase.SetCallback = function(self, name, func) tercio@0: if type(func) == "function" then tercio@0: self.events[name] = func tercio@0: end tercio@0: end tercio@0: tercio@0: WidgetBase.Fire = function(self, name, ...) tercio@0: if self.events[name] then tercio@0: local success, ret = safecall(self.events[name], self, name, ...) tercio@0: if success then tercio@0: return ret tercio@0: end tercio@0: end tercio@0: end tercio@0: tercio@0: WidgetBase.SetWidth = function(self, width) tercio@0: self.frame:SetWidth(width) tercio@0: self.frame.width = width tercio@0: if self.OnWidthSet then tercio@0: self:OnWidthSet(width) tercio@0: end tercio@0: end tercio@0: tercio@0: WidgetBase.SetRelativeWidth = function(self, width) tercio@0: if width <= 0 or width > 1 then tercio@0: error(":SetRelativeWidth(width): Invalid relative width.", 2) tercio@0: end tercio@0: self.relWidth = width tercio@0: self.width = "relative" tercio@0: end tercio@0: tercio@0: WidgetBase.SetHeight = function(self, height) tercio@0: self.frame:SetHeight(height) tercio@0: self.frame.height = height tercio@0: if self.OnHeightSet then tercio@0: self:OnHeightSet(height) tercio@0: end tercio@0: end tercio@0: tercio@0: --[[ WidgetBase.SetRelativeHeight = function(self, height) tercio@0: if height <= 0 or height > 1 then tercio@0: error(":SetRelativeHeight(height): Invalid relative height.", 2) tercio@0: end tercio@0: self.relHeight = height tercio@0: self.height = "relative" tercio@0: end ]] tercio@0: tercio@0: WidgetBase.IsVisible = function(self) tercio@0: return self.frame:IsVisible() tercio@0: end tercio@0: tercio@0: WidgetBase.IsShown= function(self) tercio@0: return self.frame:IsShown() tercio@0: end tercio@0: tercio@0: WidgetBase.Release = function(self) tercio@0: AceGUI:Release(self) tercio@0: end tercio@0: tercio@0: WidgetBase.SetPoint = function(self, ...) tercio@0: return self.frame:SetPoint(...) tercio@0: end tercio@0: tercio@0: WidgetBase.ClearAllPoints = function(self) tercio@0: return self.frame:ClearAllPoints() tercio@0: end tercio@0: tercio@0: WidgetBase.GetNumPoints = function(self) tercio@0: return self.frame:GetNumPoints() tercio@0: end tercio@0: tercio@0: WidgetBase.GetPoint = function(self, ...) tercio@0: return self.frame:GetPoint(...) tercio@0: end tercio@0: tercio@0: WidgetBase.GetUserDataTable = function(self) tercio@0: return self.userdata tercio@0: end tercio@0: tercio@0: WidgetBase.SetUserData = function(self, key, value) tercio@0: self.userdata[key] = value tercio@0: end tercio@0: tercio@0: WidgetBase.GetUserData = function(self, key) tercio@0: return self.userdata[key] tercio@0: end tercio@0: tercio@0: WidgetBase.IsFullHeight = function(self) tercio@0: return self.height == "fill" tercio@0: end tercio@0: tercio@0: WidgetBase.SetFullHeight = function(self, isFull) tercio@0: if isFull then tercio@0: self.height = "fill" tercio@0: else tercio@0: self.height = nil tercio@0: end tercio@0: end tercio@0: tercio@0: WidgetBase.IsFullWidth = function(self) tercio@0: return self.width == "fill" tercio@0: end tercio@0: tercio@0: WidgetBase.SetFullWidth = function(self, isFull) tercio@0: if isFull then tercio@0: self.width = "fill" tercio@0: else tercio@0: self.width = nil tercio@0: end tercio@0: end tercio@0: tercio@0: -- local function LayoutOnUpdate(this) tercio@0: -- this:SetScript("OnUpdate",nil) tercio@0: -- this.obj:PerformLayout() tercio@0: -- end tercio@0: tercio@0: local WidgetContainerBase = AceGUI.WidgetContainerBase tercio@0: tercio@0: WidgetContainerBase.PauseLayout = function(self) tercio@0: self.LayoutPaused = true tercio@0: end tercio@0: tercio@0: WidgetContainerBase.ResumeLayout = function(self) tercio@0: self.LayoutPaused = nil tercio@0: end tercio@0: tercio@0: WidgetContainerBase.PerformLayout = function(self) tercio@0: if self.LayoutPaused then tercio@0: return tercio@0: end tercio@0: safecall(self.LayoutFunc, self.content, self.children) tercio@0: end tercio@0: tercio@0: --call this function to layout, makes sure layed out objects get a frame to get sizes etc tercio@0: WidgetContainerBase.DoLayout = function(self) tercio@0: self:PerformLayout() tercio@0: -- if not self.parent then tercio@0: -- self.frame:SetScript("OnUpdate", LayoutOnUpdate) tercio@0: -- end tercio@0: end tercio@0: tercio@0: WidgetContainerBase.AddChild = function(self, child, beforeWidget) tercio@0: if beforeWidget then tercio@0: local siblingIndex = 1 tercio@0: for _, widget in pairs(self.children) do tercio@0: if widget == beforeWidget then tercio@0: break tercio@0: end tercio@0: siblingIndex = siblingIndex + 1 tercio@0: end tercio@0: tinsert(self.children, siblingIndex, child) tercio@0: else tercio@0: tinsert(self.children, child) tercio@0: end tercio@0: child:SetParent(self) tercio@0: child.frame:Show() tercio@0: self:DoLayout() tercio@0: end tercio@0: tercio@0: WidgetContainerBase.AddChildren = function(self, ...) tercio@0: for i = 1, select("#", ...) do tercio@0: local child = select(i, ...) tercio@0: tinsert(self.children, child) tercio@0: child:SetParent(self) tercio@0: child.frame:Show() tercio@0: end tercio@0: self:DoLayout() tercio@0: end tercio@0: tercio@0: WidgetContainerBase.ReleaseChildren = function(self) tercio@0: local children = self.children tercio@0: for i = 1,#children do tercio@0: AceGUI:Release(children[i]) tercio@0: children[i] = nil tercio@0: end tercio@0: end tercio@0: tercio@0: WidgetContainerBase.SetLayout = function(self, Layout) tercio@0: self.LayoutFunc = AceGUI:GetLayout(Layout) tercio@0: end tercio@0: tercio@0: WidgetContainerBase.SetAutoAdjustHeight = function(self, adjust) tercio@0: if adjust then tercio@0: self.noAutoHeight = nil tercio@0: else tercio@0: self.noAutoHeight = true tercio@0: end tercio@0: end tercio@0: tercio@0: local function FrameResize(this) tercio@0: local self = this.obj tercio@0: if this:GetWidth() and this:GetHeight() then tercio@0: if self.OnWidthSet then tercio@0: self:OnWidthSet(this:GetWidth()) tercio@0: end tercio@0: if self.OnHeightSet then tercio@0: self:OnHeightSet(this:GetHeight()) tercio@0: end tercio@0: end tercio@0: end tercio@0: tercio@0: local function ContentResize(this) tercio@0: if this:GetWidth() and this:GetHeight() then tercio@0: this.width = this:GetWidth() tercio@0: this.height = this:GetHeight() tercio@0: this.obj:DoLayout() tercio@0: end tercio@0: end tercio@0: tercio@0: setmetatable(WidgetContainerBase, {__index=WidgetBase}) tercio@0: tercio@0: --One of these function should be called on each Widget Instance as part of its creation process tercio@0: tercio@0: --- Register a widget-class as a container for newly created widgets. tercio@0: -- @param widget The widget class tercio@0: function AceGUI:RegisterAsContainer(widget) tercio@0: widget.children = {} tercio@0: widget.userdata = {} tercio@0: widget.events = {} tercio@0: widget.base = WidgetContainerBase tercio@0: widget.content.obj = widget tercio@0: widget.frame.obj = widget tercio@0: widget.content:SetScript("OnSizeChanged", ContentResize) tercio@0: widget.frame:SetScript("OnSizeChanged", FrameResize) tercio@0: setmetatable(widget, {__index = WidgetContainerBase}) tercio@0: widget:SetLayout("List") tercio@0: return widget tercio@0: end tercio@0: tercio@0: --- Register a widget-class as a widget. tercio@0: -- @param widget The widget class tercio@0: function AceGUI:RegisterAsWidget(widget) tercio@0: widget.userdata = {} tercio@0: widget.events = {} tercio@0: widget.base = WidgetBase tercio@0: widget.frame.obj = widget tercio@0: widget.frame:SetScript("OnSizeChanged", FrameResize) tercio@0: setmetatable(widget, {__index = WidgetBase}) tercio@0: return widget tercio@0: end tercio@0: end tercio@0: tercio@0: tercio@0: tercio@0: tercio@0: ------------------ tercio@0: -- Widget API -- tercio@0: ------------------ tercio@0: tercio@0: --- Registers a widget Constructor, this function returns a new instance of the Widget tercio@0: -- @param Name The name of the widget tercio@0: -- @param Constructor The widget constructor function tercio@0: -- @param Version The version of the widget tercio@0: function AceGUI:RegisterWidgetType(Name, Constructor, Version) tercio@0: assert(type(Constructor) == "function") tercio@0: assert(type(Version) == "number") tercio@0: tercio@0: local oldVersion = WidgetVersions[Name] tercio@0: if oldVersion and oldVersion >= Version then return end tercio@0: tercio@0: WidgetVersions[Name] = Version tercio@0: WidgetRegistry[Name] = Constructor tercio@0: end tercio@0: tercio@0: --- Registers a Layout Function tercio@0: -- @param Name The name of the layout tercio@0: -- @param LayoutFunc Reference to the layout function tercio@0: function AceGUI:RegisterLayout(Name, LayoutFunc) tercio@0: assert(type(LayoutFunc) == "function") tercio@0: if type(Name) == "string" then tercio@0: Name = Name:upper() tercio@0: end tercio@0: LayoutRegistry[Name] = LayoutFunc tercio@0: end tercio@0: tercio@0: --- Get a Layout Function from the registry tercio@0: -- @param Name The name of the layout tercio@0: function AceGUI:GetLayout(Name) tercio@0: if type(Name) == "string" then tercio@0: Name = Name:upper() tercio@0: end tercio@0: return LayoutRegistry[Name] tercio@0: end tercio@0: tercio@0: AceGUI.counts = AceGUI.counts or {} tercio@0: tercio@0: --- A type-based counter to count the number of widgets created. tercio@0: -- This is used by widgets that require a named frame, e.g. when a Blizzard tercio@0: -- Template requires it. tercio@0: -- @param type The widget type tercio@0: function AceGUI:GetNextWidgetNum(type) tercio@0: if not self.counts[type] then tercio@0: self.counts[type] = 0 tercio@0: end tercio@0: self.counts[type] = self.counts[type] + 1 tercio@0: return self.counts[type] tercio@0: end tercio@0: tercio@0: --- Return the number of created widgets for this type. tercio@0: -- In contrast to GetNextWidgetNum, the number is not incremented. tercio@0: -- @param type The widget type tercio@0: function AceGUI:GetWidgetCount(type) tercio@0: return self.counts[type] or 0 tercio@0: end tercio@0: tercio@0: --- Return the version of the currently registered widget type. tercio@0: -- @param type The widget type tercio@0: function AceGUI:GetWidgetVersion(type) tercio@0: return WidgetVersions[type] tercio@0: end tercio@0: tercio@0: ------------- tercio@0: -- Layouts -- tercio@0: ------------- tercio@0: tercio@0: --[[ tercio@0: A Layout is a func that takes 2 parameters tercio@0: content - the frame that widgets will be placed inside tercio@0: children - a table containing the widgets to layout tercio@0: ]] tercio@0: tercio@0: -- Very simple Layout, Children are stacked on top of each other down the left side tercio@0: AceGUI:RegisterLayout("List", tercio@0: function(content, children) tercio@0: local height = 0 tercio@0: local width = content.width or content:GetWidth() or 0 tercio@0: for i = 1, #children do tercio@0: local child = children[i] tercio@0: tercio@0: local frame = child.frame tercio@0: frame:ClearAllPoints() tercio@0: frame:Show() tercio@0: if i == 1 then tercio@0: frame:SetPoint("TOPLEFT", content) tercio@0: else tercio@0: frame:SetPoint("TOPLEFT", children[i-1].frame, "BOTTOMLEFT") tercio@0: end tercio@0: tercio@0: if child.width == "fill" then tercio@0: child:SetWidth(width) tercio@0: frame:SetPoint("RIGHT", content) tercio@0: tercio@0: if child.DoLayout then tercio@0: child:DoLayout() tercio@0: end tercio@0: elseif child.width == "relative" then tercio@0: child:SetWidth(width * child.relWidth) tercio@0: tercio@0: if child.DoLayout then tercio@0: child:DoLayout() tercio@0: end tercio@0: end tercio@0: tercio@0: height = height + (frame.height or frame:GetHeight() or 0) tercio@0: end tercio@0: safecall(content.obj.LayoutFinished, content.obj, nil, height) tercio@0: end) tercio@0: tercio@0: -- A single control fills the whole content area tercio@0: AceGUI:RegisterLayout("Fill", tercio@0: function(content, children) tercio@0: if children[1] then tercio@0: children[1]:SetWidth(content:GetWidth() or 0) tercio@0: children[1]:SetHeight(content:GetHeight() or 0) tercio@0: children[1].frame:SetAllPoints(content) tercio@0: children[1].frame:Show() tercio@0: safecall(content.obj.LayoutFinished, content.obj, nil, children[1].frame:GetHeight()) tercio@0: end tercio@0: end) tercio@0: tercio@0: local layoutrecursionblock = nil tercio@0: local function safelayoutcall(object, func, ...) tercio@0: layoutrecursionblock = true tercio@0: object[func](object, ...) tercio@0: layoutrecursionblock = nil tercio@0: end tercio@0: tercio@0: AceGUI:RegisterLayout("Flow", tercio@0: function(content, children) tercio@0: if layoutrecursionblock then return end tercio@0: --used height so far tercio@0: local height = 0 tercio@0: --width used in the current row tercio@0: local usedwidth = 0 tercio@0: --height of the current row tercio@0: local rowheight = 0 tercio@0: local rowoffset = 0 tercio@0: local lastrowoffset tercio@0: tercio@0: local width = content.width or content:GetWidth() or 0 tercio@0: tercio@0: --control at the start of the row tercio@0: local rowstart tercio@0: local rowstartoffset tercio@0: local lastrowstart tercio@0: local isfullheight tercio@0: tercio@0: local frameoffset tercio@0: local lastframeoffset tercio@0: local oversize tercio@0: for i = 1, #children do tercio@0: local child = children[i] tercio@0: oversize = nil tercio@0: local frame = child.frame tercio@0: local frameheight = frame.height or frame:GetHeight() or 0 tercio@0: local framewidth = frame.width or frame:GetWidth() or 0 tercio@0: lastframeoffset = frameoffset tercio@0: -- HACK: Why did we set a frameoffset of (frameheight / 2) ? tercio@0: -- That was moving all widgets half the widgets size down, is that intended? tercio@0: -- Actually, it seems to be neccessary for many cases, we'll leave it in for now. tercio@0: -- If widgets seem to anchor weirdly with this, provide a valid alignoffset for them. tercio@0: -- TODO: Investigate moar! tercio@0: frameoffset = child.alignoffset or (frameheight / 2) tercio@0: tercio@0: if child.width == "relative" then tercio@0: framewidth = width * child.relWidth tercio@0: end tercio@0: tercio@0: frame:Show() tercio@0: frame:ClearAllPoints() tercio@0: if i == 1 then tercio@0: -- anchor the first control to the top left tercio@0: frame:SetPoint("TOPLEFT", content) tercio@0: rowheight = frameheight tercio@0: rowoffset = frameoffset tercio@0: rowstart = frame tercio@0: rowstartoffset = frameoffset tercio@0: usedwidth = framewidth tercio@0: if usedwidth > width then tercio@0: oversize = true tercio@0: end tercio@0: else tercio@0: -- if there isn't available width for the control start a new row tercio@0: -- if a control is "fill" it will be on a row of its own full width tercio@0: if usedwidth == 0 or ((framewidth) + usedwidth > width) or child.width == "fill" then tercio@0: if isfullheight then tercio@0: -- a previous row has already filled the entire height, there's nothing we can usefully do anymore tercio@0: -- (maybe error/warn about this?) tercio@0: break tercio@0: end tercio@0: --anchor the previous row, we will now know its height and offset tercio@0: rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -(height + (rowoffset - rowstartoffset) + 3)) tercio@0: height = height + rowheight + 3 tercio@0: --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: rowstart = frame tercio@0: rowstartoffset = frameoffset tercio@0: rowheight = frameheight tercio@0: rowoffset = frameoffset tercio@0: usedwidth = framewidth tercio@0: if usedwidth > width then tercio@0: oversize = true tercio@0: end tercio@0: -- put the control on the current row, adding it to the width and checking if the height needs to be increased tercio@0: else tercio@0: --handles cases where the new height is higher than either control because of the offsets tercio@0: --math.max(rowheight-rowoffset+frameoffset, frameheight-frameoffset+rowoffset) tercio@0: tercio@0: --offset is always the larger of the two offsets tercio@0: rowoffset = math_max(rowoffset, frameoffset) tercio@0: rowheight = math_max(rowheight, rowoffset + (frameheight / 2)) tercio@0: tercio@0: frame:SetPoint("TOPLEFT", children[i-1].frame, "TOPRIGHT", 0, frameoffset - lastframeoffset) tercio@0: usedwidth = framewidth + usedwidth tercio@0: end tercio@0: end tercio@0: tercio@0: if child.width == "fill" then tercio@0: safelayoutcall(child, "SetWidth", width) tercio@0: frame:SetPoint("RIGHT", content) tercio@0: tercio@0: usedwidth = 0 tercio@0: rowstart = frame tercio@0: rowstartoffset = frameoffset tercio@0: tercio@0: if child.DoLayout then tercio@0: child:DoLayout() tercio@0: end tercio@0: rowheight = frame.height or frame:GetHeight() or 0 tercio@0: rowoffset = child.alignoffset or (rowheight / 2) tercio@0: rowstartoffset = rowoffset tercio@0: elseif child.width == "relative" then tercio@0: safelayoutcall(child, "SetWidth", width * child.relWidth) tercio@0: tercio@0: if child.DoLayout then tercio@0: child:DoLayout() tercio@0: end tercio@0: elseif oversize then tercio@0: if width > 1 then tercio@0: frame:SetPoint("RIGHT", content) tercio@0: end tercio@0: end tercio@0: tercio@0: if child.height == "fill" then tercio@0: frame:SetPoint("BOTTOM", content) tercio@0: isfullheight = true tercio@0: end tercio@0: end tercio@0: tercio@0: --anchor the last row, if its full height needs a special case since its height has just been changed by the anchor tercio@0: if isfullheight then tercio@0: rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -height) tercio@0: elseif rowstart then tercio@0: rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -(height + (rowoffset - rowstartoffset) + 3)) tercio@0: end tercio@0: tercio@0: height = height + rowheight + 3 tercio@0: safecall(content.obj.LayoutFinished, content.obj, nil, height) tercio@0: end)