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
|