flickerstreak@24
|
1 --[[
|
flickerstreak@53
|
2 ReAction Action button module.
|
flickerstreak@24
|
3
|
flickerstreak@24
|
4 The button module implements standard action button functionality by wrapping Blizzard's
|
flickerstreak@24
|
5 ActionButton frame and associated functions. It also provides some button layout
|
flickerstreak@24
|
6 modification tools.
|
flickerstreak@24
|
7
|
flickerstreak@24
|
8 --]]
|
flickerstreak@24
|
9
|
flickerstreak@24
|
10 -- local imports
|
flickerstreak@24
|
11 local ReAction = ReAction
|
flickerstreak@24
|
12 local L = ReAction.L
|
flickerstreak@24
|
13 local _G = _G
|
flickerstreak@24
|
14 local CreateFrame = CreateFrame
|
flickerstreak@24
|
15
|
flickerstreak@24
|
16 -- module declaration
|
flickerstreak@24
|
17 local moduleID = "Action"
|
flickerstreak@28
|
18 local module = ReAction:NewModule( moduleID )
|
flickerstreak@24
|
19
|
flickerstreak@75
|
20 -- private --
|
flickerstreak@75
|
21 local function GetBarConfig(bar)
|
flickerstreak@75
|
22 return module.db.profile.bars[bar:GetName()]
|
flickerstreak@75
|
23 end
|
flickerstreak@75
|
24
|
flickerstreak@75
|
25 local function RefreshLite(bar)
|
flickerstreak@75
|
26 local btns = module.buttons[bar]
|
flickerstreak@75
|
27 if btns then
|
flickerstreak@75
|
28 for _, b in ipairs(btns) do
|
flickerstreak@75
|
29 b:Refresh()
|
flickerstreak@75
|
30 end
|
flickerstreak@75
|
31 end
|
flickerstreak@75
|
32 end
|
flickerstreak@75
|
33
|
flickerstreak@75
|
34
|
flickerstreak@24
|
35 -- module methods
|
flickerstreak@24
|
36 function module:OnInitialize()
|
flickerstreak@28
|
37 self.db = ReAction.db:RegisterNamespace( moduleID,
|
flickerstreak@24
|
38 {
|
flickerstreak@28
|
39 profile = {
|
flickerstreak@75
|
40 buttons = { },
|
flickerstreak@75
|
41 bars = { },
|
flickerstreak@28
|
42 }
|
flickerstreak@24
|
43 }
|
flickerstreak@24
|
44 )
|
flickerstreak@24
|
45 self.buttons = { }
|
flickerstreak@49
|
46
|
flickerstreak@60
|
47 ReAction:RegisterOptions(self, {
|
flickerstreak@60
|
48 [moduleID] = {
|
flickerstreak@60
|
49 type = "group",
|
flickerstreak@60
|
50 name = L["Action Bars"],
|
flickerstreak@60
|
51 args = {
|
flickerstreak@60
|
52 hideEmpty = {
|
flickerstreak@60
|
53 type = "toggle",
|
flickerstreak@60
|
54 name = L["Hide Empty Buttons"],
|
flickerstreak@60
|
55 get = function() return self.db.profile.hideEmptyButtons end,
|
flickerstreak@60
|
56 set = function(info, val) module:SetHideEmptyButtons(val) end,
|
flickerstreak@60
|
57 }
|
flickerstreak@60
|
58 }
|
flickerstreak@60
|
59 }
|
flickerstreak@60
|
60 })
|
flickerstreak@63
|
61
|
flickerstreak@63
|
62 ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
|
flickerstreak@63
|
63
|
flickerstreak@63
|
64 ReAction.RegisterCallback(self, "OnCreateBar", "OnRefreshBar")
|
flickerstreak@63
|
65 ReAction.RegisterCallback(self, "OnDestroyBar")
|
flickerstreak@63
|
66 ReAction.RegisterCallback(self, "OnRefreshBar")
|
flickerstreak@63
|
67 ReAction.RegisterCallback(self, "OnEraseBar")
|
flickerstreak@63
|
68 ReAction.RegisterCallback(self, "OnRenameBar")
|
flickerstreak@63
|
69 ReAction.RegisterCallback(self, "OnConfigModeChanged")
|
flickerstreak@63
|
70
|
flickerstreak@24
|
71 end
|
flickerstreak@24
|
72
|
flickerstreak@24
|
73 function module:OnEnable()
|
flickerstreak@53
|
74 ReAction:RegisterBarType(L["Action Bar"],
|
flickerstreak@53
|
75 {
|
flickerstreak@53
|
76 type = moduleID,
|
flickerstreak@53
|
77 defaultButtonSize = 36,
|
flickerstreak@53
|
78 defaultBarRows = 1,
|
flickerstreak@53
|
79 defaultBarCols = 12,
|
flickerstreak@53
|
80 defaultBarSpacing = 3
|
flickerstreak@53
|
81 }, true)
|
flickerstreak@24
|
82 end
|
flickerstreak@24
|
83
|
flickerstreak@24
|
84 function module:OnDisable()
|
flickerstreak@53
|
85 ReAction:UnregisterBarType(L["Action Bar"])
|
flickerstreak@24
|
86 end
|
flickerstreak@24
|
87
|
flickerstreak@63
|
88 function module:OnRefreshBar(event, bar, name)
|
flickerstreak@53
|
89 if bar.config.type == moduleID then
|
flickerstreak@48
|
90 if self.buttons[bar] == nil then
|
flickerstreak@48
|
91 self.buttons[bar] = { }
|
flickerstreak@48
|
92 end
|
flickerstreak@48
|
93 local btns = self.buttons[bar]
|
flickerstreak@48
|
94 local profile = self.db.profile
|
flickerstreak@63
|
95 if profile.buttons[name] == nil then
|
flickerstreak@63
|
96 profile.buttons[name] = {}
|
flickerstreak@48
|
97 end
|
flickerstreak@75
|
98 if profile.bars[name] == nil then
|
flickerstreak@75
|
99 profile.bars[name] = {}
|
flickerstreak@75
|
100 end
|
flickerstreak@63
|
101 local btnCfg = profile.buttons[name]
|
flickerstreak@75
|
102 local barCfg = profile.bars[name]
|
flickerstreak@24
|
103
|
flickerstreak@48
|
104 local r, c = bar:GetButtonGrid()
|
flickerstreak@48
|
105 local n = r*c
|
flickerstreak@48
|
106 for i = 1, n do
|
flickerstreak@48
|
107 if btnCfg[i] == nil then
|
flickerstreak@48
|
108 btnCfg[i] = {}
|
flickerstreak@48
|
109 end
|
flickerstreak@48
|
110 if btns[i] == nil then
|
flickerstreak@75
|
111 local ok, b = pcall(self.BtnClass.New, self.BtnClass, bar, i, btnCfg[i], barCfg)
|
flickerstreak@52
|
112 if ok and b then
|
flickerstreak@52
|
113 btns[i] = b
|
flickerstreak@75
|
114 bar:AddButton(i,b)
|
flickerstreak@52
|
115 end
|
flickerstreak@48
|
116 end
|
flickerstreak@24
|
117 end
|
flickerstreak@48
|
118 for i = n+1, #btns do
|
flickerstreak@52
|
119 if btns[i] then
|
flickerstreak@75
|
120 bar:RemoveButton(btns[i])
|
flickerstreak@52
|
121 btns[i] = btns[i]:Destroy()
|
flickerstreak@52
|
122 if btnCfg[i] then
|
flickerstreak@52
|
123 btnCfg[i] = nil
|
flickerstreak@52
|
124 end
|
flickerstreak@48
|
125 end
|
flickerstreak@24
|
126 end
|
flickerstreak@75
|
127 RefreshLite(bar)
|
flickerstreak@24
|
128 end
|
flickerstreak@24
|
129 end
|
flickerstreak@24
|
130
|
flickerstreak@63
|
131 function module:OnDestroyBar(event, bar, name)
|
flickerstreak@24
|
132 if self.buttons[bar] then
|
flickerstreak@24
|
133 local btns = self.buttons[bar]
|
flickerstreak@24
|
134 for _,b in pairs(btns) do
|
flickerstreak@24
|
135 if b then
|
flickerstreak@24
|
136 b:Destroy()
|
flickerstreak@24
|
137 end
|
flickerstreak@24
|
138 end
|
flickerstreak@24
|
139 self.buttons[bar] = nil
|
flickerstreak@24
|
140 end
|
flickerstreak@24
|
141 end
|
flickerstreak@24
|
142
|
flickerstreak@63
|
143 function module:OnEraseBar(event, bar, name)
|
flickerstreak@63
|
144 self.db.profile.buttons[name] = nil
|
flickerstreak@75
|
145 self.db.profile.bars[name] = nil
|
flickerstreak@24
|
146 end
|
flickerstreak@24
|
147
|
flickerstreak@63
|
148 function module:OnRenameBar(event, bar, oldname, newname)
|
flickerstreak@48
|
149 local b = self.db.profile.buttons
|
flickerstreak@48
|
150 b[newname], b[oldname] = b[oldname], nil
|
flickerstreak@75
|
151
|
flickerstreak@75
|
152 b = self.db.profile.bars
|
flickerstreak@75
|
153 b[newname], b[oldname] = b[oldname], nil
|
flickerstreak@48
|
154 end
|
flickerstreak@48
|
155
|
flickerstreak@49
|
156 function module:SetHideEmptyButtons(hide)
|
flickerstreak@49
|
157 if hide ~= self.db.profile.hideEmptyButtons then
|
flickerstreak@49
|
158 for _, bar in pairs(self.buttons) do
|
flickerstreak@49
|
159 for _, b in pairs(bar) do
|
flickerstreak@49
|
160 if hide then
|
flickerstreak@49
|
161 ActionButton_HideGrid(b.frame)
|
flickerstreak@49
|
162 else
|
flickerstreak@49
|
163 ActionButton_ShowGrid(b.frame)
|
flickerstreak@49
|
164 end
|
flickerstreak@49
|
165 end
|
flickerstreak@49
|
166 end
|
flickerstreak@49
|
167 self.db.profile.hideEmptyButtons = hide
|
flickerstreak@49
|
168 end
|
flickerstreak@49
|
169 end
|
flickerstreak@49
|
170
|
flickerstreak@63
|
171 function module:OnConfigModeChanged(event, mode)
|
flickerstreak@63
|
172 for _, bar in ReAction:IterateBars() do
|
flickerstreak@49
|
173 if bar and self.buttons[bar] then
|
flickerstreak@49
|
174 for _, b in pairs(self.buttons[bar]) do
|
flickerstreak@24
|
175 if b then
|
flickerstreak@24
|
176 if mode then
|
flickerstreak@49
|
177 ActionButton_ShowGrid(b.frame)
|
flickerstreak@50
|
178 self:showActionIDLabel(b)
|
flickerstreak@49
|
179 else
|
flickerstreak@49
|
180 ActionButton_HideGrid(b.frame)
|
flickerstreak@50
|
181 self:hideActionIDLabel(b)
|
flickerstreak@24
|
182 end
|
flickerstreak@24
|
183 end
|
flickerstreak@24
|
184 end
|
flickerstreak@24
|
185 end
|
flickerstreak@24
|
186 end
|
flickerstreak@24
|
187 end
|
flickerstreak@24
|
188
|
flickerstreak@50
|
189 function module:showActionIDLabel(button)
|
flickerstreak@52
|
190 if not button.actionIDLabel and button:GetActionID() then
|
flickerstreak@75
|
191 local f = button:GetFrame()
|
flickerstreak@75
|
192 local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
|
flickerstreak@50
|
193 label:SetAllPoints()
|
flickerstreak@50
|
194 label:SetJustifyH("CENTER")
|
flickerstreak@50
|
195 label:SetShadowColor(0,0,0,1)
|
flickerstreak@50
|
196 label:SetShadowOffset(2,-2)
|
flickerstreak@50
|
197 label:SetText(tostring(button:GetActionID()))
|
flickerstreak@50
|
198 button.actionIDLabel = label
|
flickerstreak@75
|
199 f:HookScript("OnAttributeChanged",
|
flickerstreak@75
|
200 function(frame, attr, value)
|
flickerstreak@75
|
201 if attr == "state-parent" then
|
flickerstreak@75
|
202 label:SetText(tostring(button:GetActionID()))
|
flickerstreak@75
|
203 end
|
flickerstreak@75
|
204 end)
|
flickerstreak@50
|
205 end
|
flickerstreak@50
|
206 button.actionIDLabel:Show()
|
flickerstreak@50
|
207 end
|
flickerstreak@50
|
208
|
flickerstreak@50
|
209 function module:hideActionIDLabel(button)
|
flickerstreak@50
|
210 if button.actionIDLabel then
|
flickerstreak@50
|
211 button.actionIDLabel:Hide()
|
flickerstreak@50
|
212 end
|
flickerstreak@50
|
213 end
|
flickerstreak@50
|
214
|
flickerstreak@50
|
215
|
flickerstreak@60
|
216 ---- Options ----
|
flickerstreak@60
|
217 function module:GetBarOptions(bar)
|
flickerstreak@63
|
218 return {
|
flickerstreak@63
|
219 type = "group",
|
flickerstreak@63
|
220 name = L["Action Buttons"],
|
flickerstreak@63
|
221 hidden = function() return bar.config.type ~= moduleID end,
|
flickerstreak@63
|
222 args = {
|
flickerstreak@60
|
223 }
|
flickerstreak@63
|
224 }
|
flickerstreak@59
|
225 end
|
flickerstreak@59
|
226
|
flickerstreak@50
|
227
|
flickerstreak@24
|
228 -- use-count of action IDs
|
flickerstreak@53
|
229 local nActionIDs = 120
|
flickerstreak@24
|
230 local ActionIDList = setmetatable( {}, {
|
flickerstreak@24
|
231 __index = function(self, idx)
|
flickerstreak@24
|
232 if idx == nil then
|
flickerstreak@53
|
233 for i = 1, nActionIDs do
|
flickerstreak@24
|
234 if rawget(self,i) == nil then
|
flickerstreak@24
|
235 rawset(self,i,1)
|
flickerstreak@24
|
236 return i
|
flickerstreak@24
|
237 end
|
flickerstreak@24
|
238 end
|
flickerstreak@52
|
239 error("ran out of action IDs")
|
flickerstreak@24
|
240 else
|
flickerstreak@24
|
241 local c = rawget(self,idx) or 0
|
flickerstreak@24
|
242 rawset(self,idx,c+1)
|
flickerstreak@24
|
243 return idx
|
flickerstreak@24
|
244 end
|
flickerstreak@24
|
245 end,
|
flickerstreak@24
|
246 __newindex = function(self,idx,value)
|
flickerstreak@24
|
247 if value == nil then
|
flickerstreak@24
|
248 value = rawget(self,idx)
|
flickerstreak@24
|
249 if value == 1 then
|
flickerstreak@24
|
250 value = nil
|
flickerstreak@24
|
251 elseif value then
|
flickerstreak@24
|
252 value = value - 1
|
flickerstreak@24
|
253 end
|
flickerstreak@24
|
254 end
|
flickerstreak@24
|
255 rawset(self,idx,value)
|
flickerstreak@24
|
256 end
|
flickerstreak@24
|
257 })
|
flickerstreak@24
|
258
|
flickerstreak@24
|
259
|
flickerstreak@24
|
260
|
flickerstreak@28
|
261
|
flickerstreak@28
|
262 ------ Button class ------
|
flickerstreak@28
|
263 local Button = { }
|
flickerstreak@28
|
264
|
flickerstreak@75
|
265 local function Constructor( self, bar, idx, config, barConfig )
|
flickerstreak@75
|
266 self.bar, self.idx, self.config, self.barConfig = bar, idx, config, barConfig
|
flickerstreak@24
|
267
|
flickerstreak@24
|
268 local barFrame = bar:GetFrame()
|
flickerstreak@24
|
269
|
flickerstreak@56
|
270 config.name = config.name or ("ReAction_%s_%d"):format(bar:GetName(),idx)
|
flickerstreak@49
|
271 self.name = config.name
|
flickerstreak@24
|
272 config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured
|
flickerstreak@75
|
273 self.nPages = 1
|
flickerstreak@24
|
274
|
flickerstreak@24
|
275 local f = CreateFrame("CheckButton", self.name, barFrame, "ActionBarButtonTemplate")
|
flickerstreak@49
|
276
|
flickerstreak@24
|
277 -- TODO: re-implement ActionButton event handlers that don't do secure stuff
|
flickerstreak@24
|
278
|
flickerstreak@75
|
279 -- this will probably cause taint and/or performance problems, using right now for display/debugging purposes
|
flickerstreak@30
|
280 f:SetScript("OnAttributeChanged", ActionButton_UpdateAction)
|
flickerstreak@75
|
281
|
flickerstreak@24
|
282 f:SetAttribute("action", config.actionID)
|
flickerstreak@75
|
283 -- install mind control action support for all buttons here just for simplicity
|
flickerstreak@75
|
284 if self.idx <= 12 then
|
flickerstreak@75
|
285 f:SetAttribute("action-mc", 120 + self.idx)
|
flickerstreak@75
|
286 end
|
flickerstreak@49
|
287
|
flickerstreak@24
|
288 barFrame:SetAttribute("addchild",f)
|
flickerstreak@49
|
289
|
flickerstreak@24
|
290 self.frame = f
|
flickerstreak@75
|
291 self:Refresh()
|
flickerstreak@49
|
292
|
flickerstreak@49
|
293 if not module.db.profile.hideEmptyButtons then
|
flickerstreak@49
|
294 ActionButton_ShowGrid(self.frame)
|
flickerstreak@49
|
295 end
|
flickerstreak@50
|
296
|
flickerstreak@50
|
297 if ReAction.configMode then
|
flickerstreak@50
|
298 ActionButton_ShowGrid(self.frame)
|
flickerstreak@50
|
299 module:showActionIDLabel(self)
|
flickerstreak@50
|
300 end
|
flickerstreak@24
|
301 end
|
flickerstreak@24
|
302
|
flickerstreak@24
|
303 function Button:Destroy()
|
flickerstreak@24
|
304 local f = self.frame
|
flickerstreak@24
|
305 f:UnregisterAllEvents()
|
flickerstreak@24
|
306 f:Hide()
|
flickerstreak@24
|
307 f:SetParent(UIParent)
|
flickerstreak@24
|
308 f:ClearAllPoints()
|
flickerstreak@28
|
309 if self.name then
|
flickerstreak@28
|
310 _G[self.name] = nil
|
flickerstreak@24
|
311 end
|
flickerstreak@52
|
312 if self.config.actionID then
|
flickerstreak@52
|
313 ActionIDList[self.config.actionID] = nil
|
flickerstreak@52
|
314 end
|
flickerstreak@75
|
315 if self.config.pages then
|
flickerstreak@75
|
316 for _, id in ipairs(self.config.pages) do
|
flickerstreak@75
|
317 ActionIDList[id] = nil
|
flickerstreak@75
|
318 end
|
flickerstreak@75
|
319 end
|
flickerstreak@24
|
320 self.frame = nil
|
flickerstreak@24
|
321 self.config = nil
|
flickerstreak@24
|
322 self.bar = nil
|
flickerstreak@24
|
323 end
|
flickerstreak@24
|
324
|
flickerstreak@75
|
325 function Button:Refresh()
|
flickerstreak@75
|
326 local f = self.frame
|
flickerstreak@75
|
327 self.bar:PlaceButton(self, 36, 36)
|
flickerstreak@75
|
328 if self.barConfig.mckeybinds then
|
flickerstreak@75
|
329 f:SetAttribute("bindings-mc", self.barConfig.mckeybinds[self.idx])
|
flickerstreak@75
|
330 end
|
flickerstreak@75
|
331 self:RefreshPages()
|
flickerstreak@24
|
332 end
|
flickerstreak@24
|
333
|
flickerstreak@24
|
334 function Button:GetFrame()
|
flickerstreak@24
|
335 return self.frame
|
flickerstreak@24
|
336 end
|
flickerstreak@24
|
337
|
flickerstreak@24
|
338 function Button:GetName()
|
flickerstreak@24
|
339 return self.name
|
flickerstreak@24
|
340 end
|
flickerstreak@24
|
341
|
flickerstreak@24
|
342 function Button:GetActionID()
|
flickerstreak@75
|
343 return SecureButton_GetModifiedAttribute(self.frame, "action")
|
flickerstreak@24
|
344 end
|
flickerstreak@28
|
345
|
flickerstreak@75
|
346 function Button:RefreshPages()
|
flickerstreak@75
|
347 local nPages = 1 --self.bar:GetNumPages()
|
flickerstreak@75
|
348 if nPages ~= self.nPages then
|
flickerstreak@75
|
349 local f = self:GetFrame()
|
flickerstreak@75
|
350 local c = self.config.pages
|
flickerstreak@75
|
351 if nPages > 1 and not c then
|
flickerstreak@75
|
352 c = { }
|
flickerstreak@75
|
353 self.config.pages = c
|
flickerstreak@75
|
354 end
|
flickerstreak@75
|
355 for i = 1, nPages do
|
flickerstreak@75
|
356 c[i] = ActionIDList[c[i]] -- gets a free one if none configured
|
flickerstreak@75
|
357 f:SetAttribute(("action-page%d"):format(i))
|
flickerstreak@75
|
358 end
|
flickerstreak@75
|
359 for i = nPages+1, #c do
|
flickerstreak@75
|
360 ActionIDList[c[i]] = nil
|
flickerstreak@75
|
361 c[i] = nil
|
flickerstreak@75
|
362 f:SetAttribute(("action-page%d"):format(i))
|
flickerstreak@75
|
363 end
|
flickerstreak@75
|
364
|
flickerstreak@75
|
365 -- TODO:
|
flickerstreak@75
|
366 -- apply next-page, prev-page, and direct-page keybinds (via bar:SetStateKeybind abstraction)
|
flickerstreak@75
|
367 end
|
flickerstreak@75
|
368 end
|
flickerstreak@28
|
369
|
flickerstreak@28
|
370 -- export as a class-factory to module
|
flickerstreak@28
|
371 module.BtnClass = {
|
flickerstreak@75
|
372 New = function(self, ...)
|
flickerstreak@28
|
373 local x = { }
|
flickerstreak@28
|
374 for k,v in pairs(Button) do
|
flickerstreak@28
|
375 x[k] = v
|
flickerstreak@28
|
376 end
|
flickerstreak@28
|
377 Constructor(x, ...)
|
flickerstreak@28
|
378 return x
|
flickerstreak@28
|
379 end
|
flickerstreak@28
|
380 }
|