Mercurial > wow > ouroloot
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 |