annotate AceGUIWidget-lib-st.lua @ 112:ccf90050cdc1 beta-mhg-1

Work around acegui bugs, revert when tickets fixed.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Thu, 09 Aug 2012 23:58:30 -0400
parents fd3dd12f96ce
children 9232cacc9136
rev   line source
farmbuyer@1 1 --[[-----------------------------------------------------------------------------
farmbuyer@1 2 lib-st Beta Wrapper Widget
farmbuyer@1 3
farmbuyer@1 4 lib-st does not recycle the objects (called "ST" here) that it creates and
farmbuyer@1 5 returns. We therefore do not try to hold onto an ST when the widget is
farmbuyer@1 6 being recycled. This means that Constructor() does very little work, and
farmbuyer@1 7 does not actually construct an ST.
farmbuyer@1 8
farmbuyer@1 9 OnAcquire cannot construct an ST either, because we don't yet have any
farmbuyer@1 10 creation parameters from the user to feed to CreateST. (Allowing such to
farmbuyer@1 11 be passed along from AceGUI:Create() would require changes to core AceGUI
farmbuyer@1 12 code, and I don't feel like trying to overcome that inertia.)
farmbuyer@1 13
farmbuyer@1 14 The upshot is that the widget returned from Create is broken and useless
farmbuyer@1 15 until its CreateST member has been called. This means that correct behavior
farmbuyer@1 16 depends entirely on the user remembering to do so.
farmbuyer@1 17
farmbuyer@1 18 "The gods do not protect fools. Fools are protected by more capable fools."
farmbuyer@1 19 - Ringworld
farmbuyer@1 20
farmbuyer@1 21
farmbuyer@1 22 Version 1 initial functioning implementation
farmbuyer@1 23 Version 2 reshuffle to follow new AceGUI widget coding style
farmbuyer@1 24 Version 3 add .tail_offset, defaulting to same absolute value as .head_offset
farmbuyer@47 25 Version 4 restore original frame methods, as fortold by ancient prophecy
farmbuyer@49 26 Version 5 don't bogart the widget object
farmbuyer@1 27 -farmbuyer
farmbuyer@1 28 -------------------------------------------------------------------------------]]
farmbuyer@47 29 local Type, Version = "lib-st", 4
farmbuyer@1 30 local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
farmbuyer@1 31 if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
farmbuyer@1 32
farmbuyer@1 33 -- Lua APIs
farmbuyer@1 34 local ipairs, error = ipairs, error
farmbuyer@1 35
farmbuyer@1 36 -- WoW APIs
farmbuyer@1 37 local debugstack = debugstack
farmbuyer@1 38 local CreateFrame = CreateFrame
farmbuyer@1 39
farmbuyer@1 40
farmbuyer@1 41 --[[-----------------------------------------------------------------------------
farmbuyer@1 42 Support functions
farmbuyer@1 43 -------------------------------------------------------------------------------]]
farmbuyer@1 44
farmbuyer@1 45 -- Some AceGUI functions simply Won't Work in this context. Name them
farmbuyer@1 46 -- here, and code calling them will get a somewhat informative error().
farmbuyer@1 47 local oopsfuncs = {
farmbuyer@1 48 'SetRelativeWidth', 'SetRelativeHeight',
farmbuyer@1 49 'SetFullWidth', 'SetFullHeight',
farmbuyer@1 50 }
farmbuyer@1 51 local err = "Oops! The AceGUI function you tried to call (%s) does not "
farmbuyer@1 52 .. "make sense with lib-st and has not been implemented."
farmbuyer@1 53
farmbuyer@1 54 local function Oops(self)
farmbuyer@1 55 -- if you ever wanted an example of "brown paper bag" code, here it is
farmbuyer@1 56 local ds = debugstack(0)
farmbuyer@1 57 local func = ds:match("AceGUIWidget%-lib%-st%.lua:%d+:%s+in function `(%a+)'")
farmbuyer@1 58 error(err:format(func or "?"))
farmbuyer@1 59 end
farmbuyer@1 60
farmbuyer@1 61
farmbuyer@1 62 --[[
farmbuyer@1 63 Users with an ST already constructed can drop it into a widget directly
farmbuyer@1 64 using this routine. It must be safe to call this more than once with
farmbuyer@1 65 new widgets on the same ST.
farmbuyer@1 66
farmbuyer@1 67 This is where most of the intelligence of the wrapper is located. That
farmbuyer@1 68 is, if you can call my code "intelligent" with a straight face. Lemme
farmbuyer@1 69 try again.
farmbuyer@1 70
farmbuyer@1 71 Think of the widget wrapper as a brain. When ALL THREE neurons manage
farmbuyer@1 72 to fire at the same time and produce a thought, this function represents
farmbuyer@1 73 the thought. Sigh.
farmbuyer@1 74 ]]
farmbuyer@1 75 local ShiftingSetPoint, ShiftingSetAllPoints
farmbuyer@1 76 local function WrapST (self, st)
farmbuyer@1 77 if not st.frame then
farmbuyer@1 78 error"lib-st instance has no '.frame' field... wtf did you pass to this function?"
farmbuyer@1 79 end
farmbuyer@49 80 --if st.frame.obj and (st.frame.obj ~= self) then
farmbuyer@49 81 -- error"lib-st instance already has an '.obj' field from a different widget, cannot use with AceGUI!"
farmbuyer@49 82 --end
farmbuyer@1 83 self.st = st
farmbuyer@1 84 if not st.head then
farmbuyer@49 85 error"lib-st instance has no '.head' field, must use either ScrollingTable:CreateST or this widget's CreateST first"
farmbuyer@1 86 end
farmbuyer@1 87 self.frame = st.frame -- gutsy, but looks doable
farmbuyer@1 88
farmbuyer@1 89 -- Possibly have already wrapped this ST in a previous widget, careful.
farmbuyer@49 90 --if st.frame.obj ~= self then
farmbuyer@47 91 self.frame.customSetPoint = rawget(self.frame,"SetPoint")
farmbuyer@1 92 self.frame.realSetPoint = self.frame.SetPoint
farmbuyer@1 93 self.frame.SetPoint = ShiftingSetPoint
farmbuyer@1 94 self.frame.SetAllPoints = ShiftingSetAllPoints
farmbuyer@49 95 --end
farmbuyer@1 96
farmbuyer@47 97 -- This needs the .frame field. This also unconditionally creates .obj
farmbuyer@47 98 -- inside that field and calls a SetScript on it as well.
farmbuyer@1 99 return AceGUI:RegisterAsWidget(self)
farmbuyer@1 100 end
farmbuyer@1 101
farmbuyer@1 102
farmbuyer@1 103 --[[-----------------------------------------------------------------------------
farmbuyer@1 104 Scripts
farmbuyer@1 105 -------------------------------------------------------------------------------]]
farmbuyer@1 106 --[[
farmbuyer@1 107 All of an ST's subframes are attached to its main frame, which we have in
farmbuyer@1 108 the st.frame link, and that's what AceGUI uses for all positioning. Except
farmbuyer@1 109 that ST:SetDisplayCols creates its "head" row /above/ the main frame, and
farmbuyer@1 110 so the row of labels eats into whatever upper border space AceGUI calculates,
farmbuyer@1 111 often overlapping other elements.
farmbuyer@1 112
farmbuyer@1 113 We get around this by replacing ST's main frame's SetPoint with a custom
farmbuyer@1 114 version that just moves everything down a few pixels to allow room for the
farmbuyer@1 115 head row.
farmbuyer@1 116
farmbuyer@1 117 FIXME this may need to be a secure hook (ugh, would end up calling the real
farmbuyer@1 118 setpoint twice) rather than a replacement.
farmbuyer@1 119 ]]
farmbuyer@1 120 local DEFAULT_OFFSET = 7
farmbuyer@1 121 function ShiftingSetPoint(frame,anchor,other,otheranchor,xoff,yoff)
farmbuyer@1 122 local ho,to = frame.obj.head_offset, frame.obj.tail_offset
farmbuyer@1 123 yoff = yoff or 0
farmbuyer@1 124 if anchor:sub(1,3) == "TOP" then
farmbuyer@1 125 yoff = yoff - ho
farmbuyer@1 126 elseif anchor:sub(1,6) == "BOTTOM" then
farmbuyer@1 127 yoff = yoff + to
farmbuyer@1 128 end
farmbuyer@1 129 return frame.realSetPoint(frame,anchor,other,otheranchor,xoff,yoff)
farmbuyer@1 130 end
farmbuyer@1 131 function ShiftingSetAllPoints(frame,other)
farmbuyer@1 132 ShiftingSetPoint(frame,"TOPLEFT",other,"TOPLEFT",0,0)
farmbuyer@1 133 ShiftingSetPoint(frame,"BOTTOMRIGHT",other,"BOTTOMRIGHT",0,0)
farmbuyer@1 134 end
farmbuyer@1 135
farmbuyer@1 136
farmbuyer@1 137 --[[-----------------------------------------------------------------------------
farmbuyer@1 138 Methods
farmbuyer@1 139 -------------------------------------------------------------------------------]]
farmbuyer@1 140 local methods = {
farmbuyer@1 141 -- --------------------------------------------------------------
farmbuyer@1 142 -- These are expected by AceGUI containers (and AceGUI users)
farmbuyer@1 143 --
farmbuyer@1 144 ["OnAcquire"] = function (self)
farmbuyer@1 145 -- Almost nothing can usefully be done here.
farmbuyer@1 146 self.head_offset = DEFAULT_OFFSET
farmbuyer@1 147 self.tail_offset = DEFAULT_OFFSET
farmbuyer@1 148 end,
farmbuyer@1 149
farmbuyer@1 150 ["OnRelease"] = function (self)
farmbuyer@1 151 if self.st then
farmbuyer@1 152 self.st.frame:ClearAllPoints()
farmbuyer@1 153 self.st:Hide()
farmbuyer@1 154 end
farmbuyer@1 155 self.st = nil
farmbuyer@47 156 self.frame.realSetPoint = nil
farmbuyer@47 157 self.frame.SetAllPoints = nil
farmbuyer@47 158 self.frame.SetPoint = self.frame.customSetPoint
farmbuyer@47 159 self.frame.customSetPoint = nil
farmbuyer@1 160 end,
farmbuyer@1 161
farmbuyer@1 162 --[[
farmbuyer@1 163 STs don't use a "normal" SetWidth, if we define "normal" to be the
farmbuyer@1 164 behavior of the blizzard :SetWidth. Column width is passed in during
farmbuyer@1 165 creation of the whole ST. The SetWidth defined by an ST takes no
farmbuyer@1 166 arguments; "ReCalculateWidth" would be a more precise description of
farmbuyer@1 167 what it does.
farmbuyer@1 168
farmbuyer@1 169 Parts of AceGUI look for a .width field because a widget's SetWidth
farmbuyer@1 170 sets such. ST calculates a total width and dispatches it to its member
farmbuyer@1 171 frame... but doesn't store a local copy. We need to bridge these
farmbuyer@1 172 differences.
farmbuyer@1 173
farmbuyer@1 174 This widget wrapper does not make use of On{Width,Height}Set hooks,
farmbuyer@1 175 but the acegui widget base functions do. Since we're not inheriting
farmbuyer@1 176 them, we may as well supply them.
farmbuyer@1 177 ]]
farmbuyer@1 178 ["SetWidth"] = function (self)
farmbuyer@1 179 self.st:SetWidth() -- re-total the columns
farmbuyer@1 180 local w = self.st.frame:GetWidth() -- fetch the answer back
farmbuyer@1 181 self.frame.width = w -- store it for acegui
farmbuyer@1 182 if self.OnWidthSet then
farmbuyer@1 183 self:OnWidthSet(w)
farmbuyer@1 184 end
farmbuyer@1 185 end,
farmbuyer@1 186
farmbuyer@1 187 -- Everything said about SetWidth applies here too.
farmbuyer@1 188 ["SetHeight"] = function (self)
farmbuyer@1 189 self.st:SetHeight()
farmbuyer@1 190 local h = self.st.frame:GetHeight()
farmbuyer@1 191 self.frame.height = h
farmbuyer@1 192 if self.OnHeightSet then
farmbuyer@1 193 self:OnHeightSet(h)
farmbuyer@1 194 end
farmbuyer@1 195 end,
farmbuyer@1 196
farmbuyer@1 197 -- Some of the container layouts call Show/Hide on the innermost frame
farmbuyer@1 198 -- directly. We need to make sure the slightly-higher-level routine is
farmbuyer@1 199 -- also called.
farmbuyer@1 200 ["LayoutFinished"] = function (self)
farmbuyer@1 201 if self.frame:IsShown() then
farmbuyer@1 202 self.st:Show()
farmbuyer@1 203 else
farmbuyer@1 204 self.st:Hide()
farmbuyer@1 205 end
farmbuyer@1 206 end,
farmbuyer@1 207
farmbuyer@1 208 -- --------------------------------------------------------------
farmbuyer@1 209 -- Functions specific to this widget
farmbuyer@1 210 --
farmbuyer@1 211
farmbuyer@1 212 ["GetSTLibrary"] = function (self) -- Purely for convenience
farmbuyer@1 213 return LibST
farmbuyer@1 214 end,
farmbuyer@1 215
farmbuyer@1 216 --[[
farmbuyer@1 217 Replacement wrapper, so that instead of
farmbuyer@1 218 st = ScrollingTable:CreateST( args )
farmbuyer@1 219 the user should be able to do
farmbuyer@1 220 st = AceGUI:Create("lib-st"):CreateST( args )
farmbuyer@1 221 instead, without needing to get a lib-st handle.
farmbuyer@1 222 ]]
farmbuyer@1 223 ["CreateST"] = function (self, ...)
farmbuyer@1 224 return self:WrapST( LibST:CreateST(...) )
farmbuyer@1 225 end,
farmbuyer@1 226
farmbuyer@1 227 ["WrapST"] = WrapST,
farmbuyer@1 228 }
farmbuyer@1 229
farmbuyer@1 230
farmbuyer@1 231 --[[-----------------------------------------------------------------------------
farmbuyer@1 232 Constructor
farmbuyer@1 233 -------------------------------------------------------------------------------]]
farmbuyer@1 234 local function Constructor()
farmbuyer@1 235 -- .frame not done here, see WrapST
farmbuyer@1 236 local widget = {
farmbuyer@1 237 type = Type
farmbuyer@1 238 }
farmbuyer@1 239 for method, func in pairs(methods) do
farmbuyer@1 240 widget[method] = func
farmbuyer@1 241 end
farmbuyer@1 242
farmbuyer@1 243 for _,func in ipairs(oopsfuncs) do
farmbuyer@1 244 widget[func] = Oops
farmbuyer@1 245 end
farmbuyer@1 246
farmbuyer@1 247 -- AceGUI:RegisterAsWidget needs .frame
farmbuyer@1 248 return widget
farmbuyer@1 249 end
farmbuyer@1 250
farmbuyer@1 251 AceGUI:RegisterWidgetType(Type,Constructor,Version)
farmbuyer@1 252