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