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