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