comparison AceGUIWidget-lib-st.lua @ 1:822b6ca3ef89

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