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