Mercurial > wow > ouroloot
diff AceGUIWidget-lib-st.lua @ 1:822b6ca3ef89
Import of 2.15, moving to wowace svn.
author | Farmbuyer of US-Kilrogg <farmbuyer@gmail.com> |
---|---|
date | Sat, 16 Apr 2011 06:03:29 +0000 |
parents | |
children | 1070a14cfee4 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AceGUIWidget-lib-st.lua Sat Apr 16 06:03:29 2011 +0000 @@ -0,0 +1,249 @@ +--[[----------------------------------------------------------------------------- +lib-st Beta Wrapper Widget + +lib-st does not recycle the objects (called "ST" here) that it creates and +returns. We therefore do not try to hold onto an ST when the widget is +being recycled. This means that Constructor() does very little work, and +does not actually construct an ST. + +OnAcquire cannot construct an ST either, because we don't yet have any +creation parameters from the user to feed to CreateST. (Allowing such to +be passed along from AceGUI:Create() would require changes to core AceGUI +code, and I don't feel like trying to overcome that inertia.) + +The upshot is that the widget returned from Create is broken and useless +until its CreateST member has been called. This means that correct behavior +depends entirely on the user remembering to do so. + +"The gods do not protect fools. Fools are protected by more capable fools." +- Ringworld + + +Version 1 initial functioning implementation +Version 2 reshuffle to follow new AceGUI widget coding style +Version 3 add .tail_offset, defaulting to same absolute value as .head_offset +-farmbuyer +-------------------------------------------------------------------------------]] +local Type, Version = "lib-st", 3 +local AceGUI = LibStub and LibStub("AceGUI-3.0", true) +if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end + +-- Lua APIs +local ipairs, error = ipairs, error + +-- WoW APIs +local debugstack = debugstack +local CreateFrame = CreateFrame + + +--[[----------------------------------------------------------------------------- +Support functions +-------------------------------------------------------------------------------]] + +-- Some AceGUI functions simply Won't Work in this context. Name them +-- here, and code calling them will get a somewhat informative error(). +local oopsfuncs = { + 'SetRelativeWidth', 'SetRelativeHeight', + 'SetFullWidth', 'SetFullHeight', +} +local err = "Oops! The AceGUI function you tried to call (%s) does not " + .. "make sense with lib-st and has not been implemented." + +local function Oops(self) + -- if you ever wanted an example of "brown paper bag" code, here it is + local ds = debugstack(0) + local func = ds:match("AceGUIWidget%-lib%-st%.lua:%d+:%s+in function `(%a+)'") + error(err:format(func or "?")) +end + + +--[[ + Users with an ST already constructed can drop it into a widget directly + using this routine. It must be safe to call this more than once with + new widgets on the same ST. + + This is where most of the intelligence of the wrapper is located. That + is, if you can call my code "intelligent" with a straight face. Lemme + try again. + + Think of the widget wrapper as a brain. When ALL THREE neurons manage + to fire at the same time and produce a thought, this function represents + the thought. Sigh. +]] +local ShiftingSetPoint, ShiftingSetAllPoints +local function WrapST (self, st) + if not st.frame then + error"lib-st instance has no '.frame' field... wtf did you pass to this function?" + end + if st.frame.obj and (st.frame.obj ~= self) then + error"lib-st instance already has an '.obj' field from a different widget, cannot use with AceGUI!" + end + self.st = st + if not st.head then + error"lib-st instance has no '.head' field, must use either ScrollingTable:CreateST or this widget's CreatST first" + end + self.frame = st.frame -- gutsy, but looks doable + + -- Possibly have already wrapped this ST in a previous widget, careful. + if st.frame.obj ~= self then + self.frame.realSetPoint = self.frame.SetPoint + self.frame.SetPoint = ShiftingSetPoint + self.frame.SetAllPoints = ShiftingSetAllPoints + end + + -- This needs the .frame field. This also creates .obj inside that + -- field and calls a SetScript as well. + return AceGUI:RegisterAsWidget(self) +end + + +--[[----------------------------------------------------------------------------- +Scripts +-------------------------------------------------------------------------------]] +--[[ + All of an ST's subframes are attached to its main frame, which we have in + the st.frame link, and that's what AceGUI uses for all positioning. Except + that ST:SetDisplayCols creates its "head" row /above/ the main frame, and + so the row of labels eats into whatever upper border space AceGUI calculates, + often overlapping other elements. + + We get around this by replacing ST's main frame's SetPoint with a custom + version that just moves everything down a few pixels to allow room for the + head row. + + FIXME this may need to be a secure hook (ugh, would end up calling the real + setpoint twice) rather than a replacement. +]] +local DEFAULT_OFFSET = 7 +function ShiftingSetPoint(frame,anchor,other,otheranchor,xoff,yoff) + local ho,to = frame.obj.head_offset, frame.obj.tail_offset + yoff = yoff or 0 + if anchor:sub(1,3) == "TOP" then + yoff = yoff - ho + elseif anchor:sub(1,6) == "BOTTOM" then + yoff = yoff + to + end + return frame.realSetPoint(frame,anchor,other,otheranchor,xoff,yoff) +end +function ShiftingSetAllPoints(frame,other) + ShiftingSetPoint(frame,"TOPLEFT",other,"TOPLEFT",0,0) + ShiftingSetPoint(frame,"BOTTOMRIGHT",other,"BOTTOMRIGHT",0,0) +end + + +--[[----------------------------------------------------------------------------- +Methods +-------------------------------------------------------------------------------]] +local methods = { + -- -------------------------------------------------------------- + -- These are expected by AceGUI containers (and AceGUI users) + -- + ["OnAcquire"] = function (self) + -- Almost nothing can usefully be done here. + self.head_offset = DEFAULT_OFFSET + self.tail_offset = DEFAULT_OFFSET + end, + + ["OnRelease"] = function (self) + if self.st then + self.st.frame:ClearAllPoints() + self.st:Hide() + end + self.st = nil + -- XXX should also undo the frame hooks. would most likely be wasted + -- cycles. if somebody actually wants to make an ST, include it inside + -- an ace container, then hide the container and continue displaying + -- the ST by other means, they can file a ticket + end, + + --[[ + STs don't use a "normal" SetWidth, if we define "normal" to be the + behavior of the blizzard :SetWidth. Column width is passed in during + creation of the whole ST. The SetWidth defined by an ST takes no + arguments; "ReCalculateWidth" would be a more precise description of + what it does. + + Parts of AceGUI look for a .width field because a widget's SetWidth + sets such. ST calculates a total width and dispatches it to its member + frame... but doesn't store a local copy. We need to bridge these + differences. + + This widget wrapper does not make use of On{Width,Height}Set hooks, + but the acegui widget base functions do. Since we're not inheriting + them, we may as well supply them. + ]] + ["SetWidth"] = function (self) + self.st:SetWidth() -- re-total the columns + local w = self.st.frame:GetWidth() -- fetch the answer back + self.frame.width = w -- store it for acegui + if self.OnWidthSet then + self:OnWidthSet(w) + end + end, + + -- Everything said about SetWidth applies here too. + ["SetHeight"] = function (self) + self.st:SetHeight() + local h = self.st.frame:GetHeight() + self.frame.height = h + if self.OnHeightSet then + self:OnHeightSet(h) + end + end, + + -- Some of the container layouts call Show/Hide on the innermost frame + -- directly. We need to make sure the slightly-higher-level routine is + -- also called. + ["LayoutFinished"] = function (self) + if self.frame:IsShown() then + self.st:Show() + else + self.st:Hide() + end + end, + + -- -------------------------------------------------------------- + -- Functions specific to this widget + -- + + ["GetSTLibrary"] = function (self) -- Purely for convenience + return LibST + end, + + --[[ + Replacement wrapper, so that instead of + st = ScrollingTable:CreateST( args ) + the user should be able to do + st = AceGUI:Create("lib-st"):CreateST( args ) + instead, without needing to get a lib-st handle. + ]] + ["CreateST"] = function (self, ...) + return self:WrapST( LibST:CreateST(...) ) + end, + + ["WrapST"] = WrapST, +} + + +--[[----------------------------------------------------------------------------- +Constructor +-------------------------------------------------------------------------------]] +local function Constructor() + -- .frame not done here, see WrapST + local widget = { + type = Type + } + for method, func in pairs(methods) do + widget[method] = func + end + + for _,func in ipairs(oopsfuncs) do + widget[func] = Oops + end + + -- AceGUI:RegisterAsWidget needs .frame + return widget +end + +AceGUI:RegisterWidgetType(Type,Constructor,Version) +