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
|