annotate AceGUIWidget-lib-st.lua @ 146:543fcf15add7

Handle new itemstring format for item variants. Heroic, warforged, etc, are now additional fields in the itemstring rather than separate item IDs as in the last few expansions. This fixes the tooltip displays for such items; there's nothing yet in place for determining the exact variations (need to finish the list of bonus codes). Some minor efficiency tweaks.
author Farmbuyer of US-Kilrogg <farmbuyer@gmail.com>
date Tue, 30 Dec 2014 17:52:30 -0500
parents 9232cacc9136
children
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@126 29 local Type, Version = "lib-st", 5
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@1 80 self.st = st
farmbuyer@1 81 if not st.head then
farmbuyer@49 82 error"lib-st instance has no '.head' field, must use either ScrollingTable:CreateST or this widget's CreateST first"
farmbuyer@1 83 end
farmbuyer@1 84 self.frame = st.frame -- gutsy, but looks doable
farmbuyer@1 85
farmbuyer@1 86 -- Possibly have already wrapped this ST in a previous widget, careful.
farmbuyer@126 87 self.frame.customSetPoint = rawget(self.frame,"SetPoint")
farmbuyer@126 88 self.frame.realSetPoint = self.frame.SetPoint
farmbuyer@126 89 self.frame.SetPoint = ShiftingSetPoint
farmbuyer@126 90 self.frame.SetAllPoints = ShiftingSetAllPoints
farmbuyer@1 91
farmbuyer@47 92 -- This needs the .frame field. This also unconditionally creates .obj
farmbuyer@47 93 -- inside that field and calls a SetScript on it as well.
farmbuyer@1 94 return AceGUI:RegisterAsWidget(self)
farmbuyer@1 95 end
farmbuyer@1 96
farmbuyer@1 97
farmbuyer@1 98 --[[-----------------------------------------------------------------------------
farmbuyer@1 99 Scripts
farmbuyer@1 100 -------------------------------------------------------------------------------]]
farmbuyer@1 101 --[[
farmbuyer@1 102 All of an ST's subframes are attached to its main frame, which we have in
farmbuyer@1 103 the st.frame link, and that's what AceGUI uses for all positioning. Except
farmbuyer@1 104 that ST:SetDisplayCols creates its "head" row /above/ the main frame, and
farmbuyer@1 105 so the row of labels eats into whatever upper border space AceGUI calculates,
farmbuyer@1 106 often overlapping other elements.
farmbuyer@1 107
farmbuyer@1 108 We get around this by replacing ST's main frame's SetPoint with a custom
farmbuyer@1 109 version that just moves everything down a few pixels to allow room for the
farmbuyer@1 110 head row.
farmbuyer@1 111
farmbuyer@1 112 FIXME this may need to be a secure hook (ugh, would end up calling the real
farmbuyer@1 113 setpoint twice) rather than a replacement.
farmbuyer@1 114 ]]
farmbuyer@1 115 local DEFAULT_OFFSET = 7
farmbuyer@1 116 function ShiftingSetPoint(frame,anchor,other,otheranchor,xoff,yoff)
farmbuyer@1 117 local ho,to = frame.obj.head_offset, frame.obj.tail_offset
farmbuyer@1 118 yoff = yoff or 0
farmbuyer@1 119 if anchor:sub(1,3) == "TOP" then
farmbuyer@1 120 yoff = yoff - ho
farmbuyer@1 121 elseif anchor:sub(1,6) == "BOTTOM" then
farmbuyer@1 122 yoff = yoff + to
farmbuyer@1 123 end
farmbuyer@1 124 return frame.realSetPoint(frame,anchor,other,otheranchor,xoff,yoff)
farmbuyer@1 125 end
farmbuyer@1 126 function ShiftingSetAllPoints(frame,other)
farmbuyer@1 127 ShiftingSetPoint(frame,"TOPLEFT",other,"TOPLEFT",0,0)
farmbuyer@1 128 ShiftingSetPoint(frame,"BOTTOMRIGHT",other,"BOTTOMRIGHT",0,0)
farmbuyer@1 129 end
farmbuyer@1 130
farmbuyer@1 131
farmbuyer@1 132 --[[-----------------------------------------------------------------------------
farmbuyer@1 133 Methods
farmbuyer@1 134 -------------------------------------------------------------------------------]]
farmbuyer@1 135 local methods = {
farmbuyer@1 136 -- --------------------------------------------------------------
farmbuyer@1 137 -- These are expected by AceGUI containers (and AceGUI users)
farmbuyer@1 138 --
farmbuyer@1 139 ["OnAcquire"] = function (self)
farmbuyer@1 140 -- Almost nothing can usefully be done here.
farmbuyer@1 141 self.head_offset = DEFAULT_OFFSET
farmbuyer@1 142 self.tail_offset = DEFAULT_OFFSET
farmbuyer@1 143 end,
farmbuyer@1 144
farmbuyer@1 145 ["OnRelease"] = function (self)
farmbuyer@1 146 if self.st then
farmbuyer@1 147 self.st.frame:ClearAllPoints()
farmbuyer@1 148 self.st:Hide()
farmbuyer@1 149 end
farmbuyer@1 150 self.st = nil
farmbuyer@47 151 self.frame.realSetPoint = nil
farmbuyer@47 152 self.frame.SetAllPoints = nil
farmbuyer@47 153 self.frame.SetPoint = self.frame.customSetPoint
farmbuyer@47 154 self.frame.customSetPoint = nil
farmbuyer@1 155 end,
farmbuyer@1 156
farmbuyer@1 157 --[[
farmbuyer@1 158 STs don't use a "normal" SetWidth, if we define "normal" to be the
farmbuyer@1 159 behavior of the blizzard :SetWidth. Column width is passed in during
farmbuyer@1 160 creation of the whole ST. The SetWidth defined by an ST takes no
farmbuyer@1 161 arguments; "ReCalculateWidth" would be a more precise description of
farmbuyer@1 162 what it does.
farmbuyer@1 163
farmbuyer@1 164 Parts of AceGUI look for a .width field because a widget's SetWidth
farmbuyer@1 165 sets such. ST calculates a total width and dispatches it to its member
farmbuyer@1 166 frame... but doesn't store a local copy. We need to bridge these
farmbuyer@1 167 differences.
farmbuyer@1 168
farmbuyer@1 169 This widget wrapper does not make use of On{Width,Height}Set hooks,
farmbuyer@1 170 but the acegui widget base functions do. Since we're not inheriting
farmbuyer@1 171 them, we may as well supply them.
farmbuyer@1 172 ]]
farmbuyer@1 173 ["SetWidth"] = function (self)
farmbuyer@1 174 self.st:SetWidth() -- re-total the columns
farmbuyer@1 175 local w = self.st.frame:GetWidth() -- fetch the answer back
farmbuyer@1 176 self.frame.width = w -- store it for acegui
farmbuyer@1 177 if self.OnWidthSet then
farmbuyer@1 178 self:OnWidthSet(w)
farmbuyer@1 179 end
farmbuyer@1 180 end,
farmbuyer@1 181
farmbuyer@1 182 -- Everything said about SetWidth applies here too.
farmbuyer@1 183 ["SetHeight"] = function (self)
farmbuyer@1 184 self.st:SetHeight()
farmbuyer@1 185 local h = self.st.frame:GetHeight()
farmbuyer@1 186 self.frame.height = h
farmbuyer@1 187 if self.OnHeightSet then
farmbuyer@1 188 self:OnHeightSet(h)
farmbuyer@1 189 end
farmbuyer@1 190 end,
farmbuyer@1 191
farmbuyer@1 192 -- Some of the container layouts call Show/Hide on the innermost frame
farmbuyer@1 193 -- directly. We need to make sure the slightly-higher-level routine is
farmbuyer@1 194 -- also called.
farmbuyer@1 195 ["LayoutFinished"] = function (self)
farmbuyer@1 196 if self.frame:IsShown() then
farmbuyer@1 197 self.st:Show()
farmbuyer@1 198 else
farmbuyer@1 199 self.st:Hide()
farmbuyer@1 200 end
farmbuyer@1 201 end,
farmbuyer@1 202
farmbuyer@1 203 -- --------------------------------------------------------------
farmbuyer@1 204 -- Functions specific to this widget
farmbuyer@1 205 --
farmbuyer@1 206
farmbuyer@1 207 ["GetSTLibrary"] = function (self) -- Purely for convenience
farmbuyer@1 208 return LibST
farmbuyer@1 209 end,
farmbuyer@1 210
farmbuyer@1 211 --[[
farmbuyer@1 212 Replacement wrapper, so that instead of
farmbuyer@1 213 st = ScrollingTable:CreateST( args )
farmbuyer@1 214 the user should be able to do
farmbuyer@1 215 st = AceGUI:Create("lib-st"):CreateST( args )
farmbuyer@1 216 instead, without needing to get a lib-st handle.
farmbuyer@1 217 ]]
farmbuyer@1 218 ["CreateST"] = function (self, ...)
farmbuyer@1 219 return self:WrapST( LibST:CreateST(...) )
farmbuyer@1 220 end,
farmbuyer@1 221
farmbuyer@1 222 ["WrapST"] = WrapST,
farmbuyer@1 223 }
farmbuyer@1 224
farmbuyer@1 225
farmbuyer@1 226 --[[-----------------------------------------------------------------------------
farmbuyer@1 227 Constructor
farmbuyer@1 228 -------------------------------------------------------------------------------]]
farmbuyer@1 229 local function Constructor()
farmbuyer@1 230 -- .frame not done here, see WrapST
farmbuyer@1 231 local widget = {
farmbuyer@1 232 type = Type
farmbuyer@1 233 }
farmbuyer@1 234 for method, func in pairs(methods) do
farmbuyer@1 235 widget[method] = func
farmbuyer@1 236 end
farmbuyer@1 237
farmbuyer@1 238 for _,func in ipairs(oopsfuncs) do
farmbuyer@1 239 widget[func] = Oops
farmbuyer@1 240 end
farmbuyer@1 241
farmbuyer@1 242 -- AceGUI:RegisterAsWidget needs .frame
farmbuyer@1 243 return widget
farmbuyer@1 244 end
farmbuyer@1 245
farmbuyer@1 246 AceGUI:RegisterWidgetType(Type,Constructor,Version)
farmbuyer@1 247