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