Nenue@1
|
1 -- User: Krakyn
|
Nenue@1
|
2 -- Created: 12/4/2015 11:13 PM
|
Nenue@1
|
3 --[[
|
Nenue@1
|
4 -- Core tools, mostly frame handling business. It's an interface script after all.
|
Nenue@1
|
5 -- Types:
|
Nenue@1
|
6 -- StatusFrame
|
Nenue@1
|
7 -- Primary container for status data, carries primary value information by default
|
Nenue@1
|
8 -- FIELDS
|
Nenue@1
|
9 -- value = exact primary value represented by frame
|
Nenue@1
|
10 -- min = exact minimum for primary value
|
Nenue@1
|
11 -- max = exact maximum for primary value
|
Nenue@1
|
12 -- percent = value / max
|
Nenue@1
|
13 -- alpha = base frame alpha level, set when a fade animation has completed
|
Nenue@1
|
14 -- fadeTo = when set, the frame will begin fading to this level
|
Nenue@1
|
15 -- fadeDuration = set with fadeTo when the transition needs to be time-adjusted for unpredictable alpha states
|
Nenue@1
|
16 -- fadeTime = set with fadeTo when the transition has an explicit desired timing
|
Nenue@1
|
17 -- throttle_rate = animation frame delay
|
Nenue@1
|
18 --
|
Nenue@1
|
19 -- METHODS
|
Nenue@1
|
20 -- Update() = Runs the frame's Update script, and calls UpdateText()
|
Nenue@1
|
21 -- UpdateText() = Iterates over frame children and calls Update() if it is a StatusText frame
|
Nenue@1
|
22 --
|
Nenue@1
|
23 -- StatusText
|
Nenue@1
|
24 -- Sub-container for individual text elements, carries string output values, controlled by from Update(). By default
|
Nenue@1
|
25 -- these are derived from the parent's primary value data, but is mostly overridden in module code.
|
Nenue@1
|
26 -- FIELDS
|
Nenue@1
|
27 -- text = WoW FontString object housing text data
|
Nenue@1
|
28 -- format = a format string for expressing complex data (timer bars, etc.)
|
Nenue@1
|
29 -- METHODS
|
Nenue@1
|
30 -- SetText() = shortcut for text:SetText(...)
|
Nenue@1
|
31 -- SetJustifyH() = shortcut for text:SetJustifyH(...)
|
Nenue@1
|
32 -- Implements:
|
Nenue@1
|
33 -- frame = CreateBar(name, config root)
|
Nenue@1
|
34 -- Creates and displays a generic bar frame with progress textures and default updater, based on the variables from
|
Nenue@1
|
35 -- the smart config table. Seeded with methods Update() and UpdateText().
|
Nenue@1
|
36 -- Update() will refresh the entire frame and tail call UpdateText()
|
Nenue@1
|
37 -- UpdateText() will iterate over the contents of frame:GetChildren() and call Update()
|
Nenue@1
|
38 -- NEVER CREATE A CHILD FRAME WITHOUT Update() DEFINED
|
Nenue@1
|
39 -- NEVER INVOKE METHODS FROM A GetParent() RETURN
|
Nenue@1
|
40 --
|
Nenue@1
|
41 -- Bar_SetUpdateHandler(frame, function)
|
Nenue@1
|
42 -- Takes the desired OnUpdate script and wraps it inside frame throttling logic, then does SetScript
|
Nenue@1
|
43 --
|
Nenue@1
|
44 -- frame = AddLabel(frame, config root, name)
|
Nenue@1
|
45 -- Constructs a text display and adds it to the frame's list of internal text objects. Contains method Update(), which
|
Nenue@1
|
46 -- refreshed text.
|
Nenue@1
|
47 --
|
Nenue@1
|
48 -- HANDLERS
|
Nenue@1
|
49 -- Bar_Update([value, min/duration, max/end])
|
Nenue@1
|
50 -- Default handler for any non-timed bar (where isTimer = false in config vars. Invoked by frame:Update()
|
Nenue@1
|
51 ]]
|
Nenue@1
|
52 local T = LibStub("AceAddon-3.0"):GetAddon("Turok")
|
Nenue@1
|
53 local LSM = LibStub("LibSharedMedia-3.0")
|
Nenue@1
|
54 local TL = 'Fog'
|
Nenue@1
|
55 local print = function(...)
|
Nenue@1
|
56 _G.print(TL, ...)
|
Nenue@1
|
57 end
|
Nenue@1
|
58 --setprinthandler(function (...) T:debug('Fog', nil, ...) end)
|
Nenue@1
|
59 local FADE_OUT_TIME = 1 -- duration per 1-0 alpha transition
|
Nenue@1
|
60 local FADE_IN_TIME = 0.4 -- duration per 0-1 alpha transition
|
Nenue@1
|
61
|
Nenue@1
|
62 local inset_factor = {
|
Nenue@1
|
63 ['TOPLEFT'] = {-1, 1},
|
Nenue@1
|
64 ['TOP'] = {0, 1},
|
Nenue@1
|
65 ['TOPRIGHT'] = {1, 1},
|
Nenue@1
|
66 ['RIGHT'] = {1, 0},
|
Nenue@1
|
67 ['BOTTOMRIGHT'] = {1, -1},
|
Nenue@1
|
68 ['BOTTOM'] = {0, -1},
|
Nenue@1
|
69 ['BOTTOMLEFT'] = {-1,-1},
|
Nenue@1
|
70 ['LEFT'] = {-1, 0}
|
Nenue@1
|
71 }
|
Nenue@1
|
72
|
Nenue@1
|
73 T.anchor_inverse = { ['LEFT'] = 'RIGHT', ['RIGHT'] = 'LEFT', ['UP'] = 'DOWN', ['DOWN'] = 'UP' } -- directional inverse
|
Nenue@1
|
74 T.direction_coord = { ['LEFT'] = {1, 0}, ['RIGHT'] = {-1,0}, ['UP'] = {0, -1}, ['DOWN'] = {0, 1 } } -- directional derivatives
|
Nenue@1
|
75 T.anchor_direction = { ['LEFT'] = 'RIGHT', ['RIGHT'] = 'LEFT', ['UP'] = 'BOTTOM', ['DOWN'] = 'TOP'} -- directional anchors
|
Nenue@1
|
76
|
Nenue@1
|
77 function T:CreateBar(name, config)
|
Nenue@1
|
78 local parent = type(config.parent) == 'string' and _G[config.parent] or parent
|
Nenue@1
|
79 if not parent then
|
Nenue@1
|
80 parent = CreateFrame('Frame', config.parent, UIParent)
|
Nenue@1
|
81 print('creating dry frame |cFFDDDDDD' .. config.parent .. '|r for |cFFFFFF00' .. name .. '|r')
|
Nenue@1
|
82 end
|
Nenue@1
|
83
|
Nenue@1
|
84 local f
|
Nenue@1
|
85 if _G[name] and _G[name].GetFrameType and _G[name].GetFrameType() == 'Frame' then
|
Nenue@1
|
86 f = _G[name]
|
Nenue@1
|
87 print('found existing table |cFFFFFF00' .. name .. '|r')
|
Nenue@1
|
88 else
|
Nenue@1
|
89 print('creating statusbar '.. name, config.anchor, parent:GetName(), config.anchorTo, config.posX, config.posY)
|
Nenue@1
|
90 end
|
Nenue@1
|
91
|
Nenue@1
|
92
|
Nenue@1
|
93 f = CreateFrame('Frame', name, UIParent)
|
Nenue@1
|
94 f.db = config
|
Nenue@1
|
95
|
Nenue@1
|
96 -- state vars
|
Nenue@1
|
97 f.combat = InCombatLockdown()
|
Nenue@1
|
98 f.min = 0
|
Nenue@1
|
99 f.max = 1
|
Nenue@1
|
100 f.value = 0
|
Nenue@1
|
101 f.percent = 0
|
Nenue@1
|
102 f.alpha = f.combat and config.alpha or config.alpha_ooc
|
Nenue@1
|
103 f:SetAlpha(f.combat and config.alpha or config.alpha_ooc)
|
Nenue@1
|
104 f:SetPoint(config.anchor, config.parent, config.anchorTo, config.posX, config.posY)
|
Nenue@1
|
105 f:SetSize(config.width, config.height)
|
Nenue@1
|
106 f:SetFrameStrata(config.strata)
|
Nenue@1
|
107 self:CreateStatusTextures(f, config)
|
Nenue@1
|
108
|
Nenue@1
|
109 f.throttle_rate = 0.0166666666 -- ~60fps
|
Nenue@2
|
110 f.throttle_point = GetTime()
|
Nenue@1
|
111
|
Nenue@1
|
112 -- default handler
|
Nenue@1
|
113 f.Update = T.Bar_Update
|
Nenue@1
|
114 f.UpdateText = T.Bar_UpdateText
|
Nenue@1
|
115 T:Bar_SetUpdateHandler(f, T.Bar_Update)
|
Nenue@1
|
116
|
Nenue@1
|
117 return f
|
Nenue@1
|
118 end
|
Nenue@1
|
119
|
Nenue@1
|
120 function T:CreateStatusTextures(region, c)
|
Nenue@1
|
121 c = c and c or region.db
|
Nenue@1
|
122 print('STATUSTEX_make', region:GetName(), c.background_texture, c.background_color, c.foreground_texture, c.foreground_color)
|
Nenue@1
|
123 region.background = region:CreateTexture('background', 'BACKGROUND')
|
Nenue@1
|
124 region.background:SetAllPoints(region)
|
Nenue@1
|
125 if c.background_texture then
|
Nenue@1
|
126 region.background:SetTexture(LSM:Fetch('statusbar', c.background_texture))
|
Nenue@1
|
127 region.background:SetVertexColor(unpack(c.background_color))
|
Nenue@1
|
128 else
|
Nenue@1
|
129 region.background:SetTexture(unpack(c.background_color))
|
Nenue@1
|
130 end
|
Nenue@1
|
131 region.background:SetBlendMode(c.background_blend)
|
Nenue@1
|
132
|
Nenue@1
|
133 region.foreground = region:CreateTexture('foreground', 'ARTWORK')
|
Nenue@1
|
134 region.foreground:SetPoint('TOPLEFT', region, 'TOPLEFT', 0-c.foreground_inset, c.foreground_inset)
|
Nenue@1
|
135 region.foreground:SetPoint('BOTTOMRIGHT', region, 'BOTTOMRIGHT', c.foreground_inset, 0-c.foreground_inset)
|
Nenue@1
|
136 if c.foreground_texture then
|
Nenue@1
|
137 region.foreground:SetTexture(LSM:Fetch('statusbar', c.foreground_texture))
|
Nenue@1
|
138 region.foreground:SetVertexColor(unpack(c.foreground_color))
|
Nenue@1
|
139 else
|
Nenue@1
|
140 region.foreground:SetTexture(unpack(c.foreground_color))
|
Nenue@1
|
141 end
|
Nenue@1
|
142 region.foreground:SetBlendMode(c.foreground_blend)
|
Nenue@1
|
143 end
|
Nenue@1
|
144
|
Nenue@1
|
145 function T:CreateStatusIcon(region, c)
|
Nenue@1
|
146 local file = type(c) == 'string' and c or region.icon
|
Nenue@1
|
147 if type(c) ~= 'table' then
|
Nenue@1
|
148 c = region.db
|
Nenue@1
|
149 end
|
Nenue@1
|
150 print('STATUSICON', region:GetName(), file, c.icon_show)
|
Nenue@1
|
151
|
Nenue@1
|
152 end
|
Nenue@1
|
153
|
Nenue@1
|
154 -- Sets the OnUpdate handler within a timing scheme
|
Nenue@1
|
155 function T:Bar_SetUpdateHandler(bar, func)
|
Nenue@1
|
156 bar:SetScript('OnUpdate', function(bar)
|
Nenue@1
|
157 if GetTime() < bar.throttle_point then
|
Nenue@1
|
158 return
|
Nenue@1
|
159 end
|
Nenue@1
|
160
|
Nenue@1
|
161 bar.throttle_point = bar.throttle_point + bar.throttle_rate
|
Nenue@1
|
162 if type(func) == 'string' then
|
Nenue@1
|
163 if type(bar[func]) ~= 'function' then
|
Nenue@1
|
164 error('TurokBar:SetUpdateScript(); string "' ..func.. '" is not a valid method name')
|
Nenue@1
|
165 return
|
Nenue@1
|
166 end
|
Nenue@1
|
167 elseif type(func) ~= 'function' then
|
Nenue@1
|
168 error('TurokBar:SetUpdateScript(function or string); got '.. type(func) .. 'instead')
|
Nenue@1
|
169 return
|
Nenue@1
|
170 end
|
Nenue@1
|
171
|
Nenue@1
|
172 func(bar)
|
Nenue@1
|
173 end)
|
Nenue@1
|
174 end
|
Nenue@1
|
175
|
Nenue@1
|
176 -- Default update loop
|
Nenue@1
|
177 function T:Bar_Update (value, min, max)
|
Nenue@1
|
178
|
Nenue@1
|
179 if value ~= nil then -- could be 0
|
Nenue@1
|
180 self.value = value
|
Nenue@1
|
181 end
|
Nenue@1
|
182 if (min ~= nil and max ~= nil) then
|
Nenue@1
|
183 if self.isTimer then
|
Nenue@1
|
184 self.duration = min
|
Nenue@1
|
185 self.endTime = max
|
Nenue@1
|
186 else
|
Nenue@1
|
187 self.min = min
|
Nenue@1
|
188 self.max = max
|
Nenue@1
|
189 self.duration = max
|
Nenue@1
|
190 end
|
Nenue@1
|
191 end
|
Nenue@1
|
192
|
Nenue@1
|
193 if self.combat ~= InCombatLockdown() then
|
Nenue@1
|
194 self.combat = InCombatLockdown()
|
Nenue@1
|
195 if self.combat then
|
Nenue@1
|
196 self.fadeTo = self.db.alpha
|
Nenue@1
|
197 self.fadeDuration = FADE_IN_TIME
|
Nenue@1
|
198 else
|
Nenue@1
|
199 self.fadeTo = self.db.alpha_ooc
|
Nenue@1
|
200 self.fadeDuration = FADE_OUT_TIME
|
Nenue@1
|
201 end
|
Nenue@1
|
202 end
|
Nenue@1
|
203
|
Nenue@1
|
204 -- we need the time for something
|
Nenue@1
|
205 if self.isTimer or self.fadeTo ~= nil then
|
Nenue@1
|
206 local time = GetTime()
|
Nenue@1
|
207
|
Nenue@1
|
208 -- start doing fade animation things
|
Nenue@1
|
209 if self.fadeTo ~= nil then
|
Nenue@1
|
210
|
Nenue@1
|
211 if not self.fadeFrom then
|
Nenue@1
|
212 self.fadeFrom = self:GetAlpha()
|
Nenue@1
|
213 end
|
Nenue@1
|
214
|
Nenue@1
|
215 -- fadeDuration missing, fill it in
|
Nenue@1
|
216 if not self.fadeDuration then
|
Nenue@1
|
217 self.fadeDuration = self.fadeFrom > self.fadeTo and FADE_OUT_TIME or FADE_IN_TIME
|
Nenue@1
|
218 end
|
Nenue@1
|
219
|
Nenue@1
|
220 -- fadeTime missing,
|
Nenue@1
|
221 if not self.fadeTime then
|
Nenue@1
|
222 local fadeRatio = 1
|
Nenue@1
|
223 if self.fadeFrom ~= self.alpha then
|
Nenue@1
|
224 fadeRatio = (self.fadeTo - self.fadeFrom) / (self.fadeTo - self.alpha) -- target delta
|
Nenue@1
|
225 end
|
Nenue@1
|
226 --print(fadeRatio, self.fadeDuration)
|
Nenue@1
|
227 self.fadeTime = time + fadeRatio * self.fadeDuration -- fixed rate of change
|
Nenue@1
|
228 end
|
Nenue@1
|
229
|
Nenue@1
|
230 -- are we done?
|
Nenue@1
|
231 if time >= self.fadeTime then
|
Nenue@1
|
232 self:SetAlpha(self.fadeTo)
|
Nenue@1
|
233 self.alpha = self.fadeTo
|
Nenue@1
|
234 self.fadeTo = nil
|
Nenue@1
|
235 self.fadeFrom = nil
|
Nenue@1
|
236 self.fadeDuration = nil
|
Nenue@1
|
237 self.fadeTime = nil
|
Nenue@1
|
238 else --nope
|
Nenue@1
|
239 local remaining = (self.fadeTime - time) / self.fadeDuration
|
Nenue@1
|
240 local fadeTotal = (self.fadeTo - self.fadeFrom)
|
Nenue@1
|
241 --print(self.fadeTo - (fadeTotal * remaining), fadeTotal * remaining)
|
Nenue@1
|
242 self:SetAlpha(self.fadeTo - (fadeTotal * remaining))
|
Nenue@1
|
243 end
|
Nenue@1
|
244 end
|
Nenue@1
|
245
|
Nenue@1
|
246 -- termination check
|
Nenue@1
|
247 if self.isTimer then
|
Nenue@1
|
248 if time >= self.endTime and self.fadeTo == nil then
|
Nenue@1
|
249 self:Hide()
|
Nenue@1
|
250 return
|
Nenue@1
|
251 else
|
Nenue@1
|
252 self.percent = (self.endTime - time) / self.duration
|
Nenue@1
|
253 end
|
Nenue@1
|
254 end
|
Nenue@1
|
255
|
Nenue@1
|
256 else
|
Nenue@1
|
257 self.percent = (self.value - self.min) / (self.max - self.min)
|
Nenue@1
|
258 end
|
Nenue@1
|
259
|
Nenue@1
|
260 self:UpdateText()
|
Nenue@1
|
261 self.foreground:SetPoint('BOTTOMRIGHT', self, 'BOTTOMLEFT', self.db.width * self.percent + self.db.foreground_inset, 0-self.db.foreground_inset)
|
Nenue@1
|
262 end
|
Nenue@1
|
263
|
Nenue@1
|
264 function T:Bar_UpdateText()
|
Nenue@1
|
265 local c = {self:GetChildren()}
|
Nenue@1
|
266 for _, rl in ipairs(c) do
|
Nenue@1
|
267 if rl.text then
|
Nenue@1
|
268 rl.format = self.db.label_string
|
Nenue@1
|
269 rl:Update()
|
Nenue@1
|
270 end
|
Nenue@1
|
271 end
|
Nenue@1
|
272 end
|
Nenue@1
|
273
|
Nenue@1
|
274 -- addon:AddLabel
|
Nenue@1
|
275 -- Constructs a text display and adds it to that region
|
Nenue@1
|
276 --
|
Nenue@1
|
277 function T:AddLabel (region, config, labelname)
|
Nenue@1
|
278 assert(region:IsObjectType('Frame'), "T:CreateLabel(table, table [, string])")
|
Nenue@1
|
279 labelname = (labelname or string.format('%x', GetTime() % 1000000))
|
Nenue@1
|
280 print('assigning frame for text object |cFFFF9922'..labelname..'|r to '..region:GetName())
|
Nenue@1
|
281
|
Nenue@1
|
282 local lf = CreateFrame('Frame', region:GetName()..'_'..labelname, region)
|
Nenue@1
|
283 local ft = lf:CreateFontString(labelname, 'OVERLAY')
|
Nenue@1
|
284 local lx, ly, lp = 0,0, config.label_point
|
Nenue@1
|
285 if config.label_inset ~= 0 and lp ~= 'CENTER' then
|
Nenue@1
|
286 lx = config.label_inset * inset_factor[lp][1]
|
Nenue@1
|
287 ly = config.label_inset * inset_factor[lp][2]
|
Nenue@1
|
288 end
|
Nenue@1
|
289
|
Nenue@1
|
290 lf:SetPoint(lp, region, lp, lx, ly)
|
Nenue@1
|
291 lf:SetSize(region:GetWidth(), region:GetHeight())
|
Nenue@1
|
292 lf:SetFrameStrata(config.label_strata)
|
Nenue@1
|
293
|
Nenue@1
|
294 ft:SetFont(LSM:Fetch('font', config.label_font), config.label_size, config.label_outline)
|
Nenue@1
|
295 ft:SetAllPoints(lf)
|
Nenue@1
|
296 ft:SetJustifyH(config.label_justifyH or (config.label_point:find('LEFT') and 'LEFT' or (config.label_point:find('RIGHT') and 'RIGHT' or 'CENTER')))
|
Nenue@1
|
297 ft:SetJustifyV(config.label_justifyV or (config.label_point:find('TOP') and 'TOP' or (config.label_point:find('BOTTOM') and 'BOTTOM' or 'MIDDLE')))
|
Nenue@1
|
298 ft:SetTextColor(1, 1, 1)
|
Nenue@1
|
299 ft:SetText('SET ME!')
|
Nenue@1
|
300
|
Nenue@1
|
301 lf.Update = region.isTimer and T.Label_UpdateFormat or T.Label_Update
|
Nenue@1
|
302 lf.SetJustifyH = function(self, justify) ft:SetJustifyH(justify) end
|
Nenue@1
|
303 lf.SetText = function(self, text) ft:SetText(text) end
|
Nenue@1
|
304
|
Nenue@1
|
305 lf.text = ft
|
Nenue@1
|
306 region[labelname] = lf
|
Nenue@1
|
307 return lf
|
Nenue@1
|
308 end
|
Nenue@1
|
309
|
Nenue@1
|
310 -- one of two possible assignments for label:Update()
|
Nenue@1
|
311 function T:Label_UpdateFormat()
|
Nenue@1
|
312 local region = self:GetParent()
|
Nenue@1
|
313 if self.format then
|
Nenue@1
|
314 self.value = string.gsub(self.format, '%%p', string.format('%.1f', region.value))
|
Nenue@1
|
315 self.value = string.gsub(self.value, '%%d', region.duration)
|
Nenue@1
|
316 if region.name then
|
Nenue@1
|
317 self.value = string.gsub(self.value, '%%n', region.name)
|
Nenue@1
|
318 end
|
Nenue@1
|
319 end
|
Nenue@1
|
320 self.text:SetText(self.value)
|
Nenue@1
|
321 end
|
Nenue@1
|
322 function T:Label_Update()
|
Nenue@1
|
323 self.text:SetText(self:GetParent().value)
|
Nenue@1
|
324 end
|
Nenue@1
|
325
|