Mercurial > wow > reaction
comparison modules/ReAction_Action/ReAction_Action.lua @ 87:3499ac7c3a9b
Implemented paged actions and mind control actions, and config menus to suit.
There's still some sort of bug in the actionID-selection routine, it doesn't always auto-select the IDs that I think it's going to if you resize a bar several times (especially in the presence of multiple pages).
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Sat, 28 Jun 2008 00:54:21 +0000 |
parents | 502cdb5666e2 |
children | fc83b3f5b322 |
comparison
equal
deleted
inserted
replaced
86:f32e2375e39b | 87:3499ac7c3a9b |
---|---|
1 --[[ | 1 --[[ |
2 ReAction Action button module. | 2 ReAction Action button module. |
3 | 3 |
4 The button module implements standard action button functionality by wrapping Blizzard's | 4 The button module implements standard action button functionality by wrapping Blizzard's |
5 ActionButton frame and associated functions. | 5 ActionBarButtonTemplate frame and associated functions. |
6 | 6 |
7 It also provides support for multiple pages (interacting with the State module) as well | 7 It also provides action remapping support for multiple pages and possessed targets |
8 as optional action remapping for possessed targets (mind control). | 8 (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc). This is done |
9 | 9 by interacting with the built-in State module to map these features to states via the |
10 "statebutton" attribute. | |
10 --]] | 11 --]] |
11 | 12 |
12 -- local imports | 13 -- local imports |
13 local ReAction = ReAction | 14 local ReAction = ReAction |
14 local L = ReAction.L | 15 local L = ReAction.L |
15 local _G = _G | 16 local _G = _G |
16 local CreateFrame = CreateFrame | 17 local CreateFrame = CreateFrame |
17 | 18 |
18 ReAction:UpdateRevision("$Revision: 103 $") | 19 ReAction:UpdateRevision("$Revision$") |
19 | 20 |
20 -- module declaration | 21 -- module declaration |
21 local moduleID = "Action" | 22 local moduleID = "Action" |
22 local module = ReAction:NewModule( moduleID ) | 23 local module = ReAction:NewModule( moduleID ) |
23 | 24 |
24 -- Button class declaration | 25 -- Button class declaration |
25 local Button = { } | 26 local Button = { } |
26 | 27 |
27 -- private -- | 28 -- private utility -- |
28 local function RefreshLite(bar) | 29 local function RefreshLite(bar) |
29 local btns = module.buttons[bar] | 30 local btns = module.buttons[bar] |
30 if btns then | 31 if btns then |
31 for _, b in ipairs(btns) do | 32 for _, b in ipairs(btns) do |
32 b:Refresh() | 33 b:Refresh() |
33 end | 34 end |
34 end | 35 end |
35 end | 36 end |
37 | |
38 local function GetBarConfig(bar) | |
39 return module.db.profile.bars[bar:GetName()] | |
40 end | |
41 | |
36 | 42 |
37 -- Event handlers | 43 -- Event handlers |
38 function module:OnInitialize() | 44 function module:OnInitialize() |
39 self.db = ReAction.db:RegisterNamespace( moduleID, | 45 self.db = ReAction.db:RegisterNamespace( moduleID, |
40 { | 46 { |
88 local btnCfg = profile.buttons[name] | 94 local btnCfg = profile.buttons[name] |
89 local barCfg = profile.bars[name] | 95 local barCfg = profile.bars[name] |
90 | 96 |
91 local r, c = bar:GetButtonGrid() | 97 local r, c = bar:GetButtonGrid() |
92 local n = r*c | 98 local n = r*c |
93 for i = 1, n do | 99 if n ~= #btns then |
94 if btnCfg[i] == nil then | 100 for i = 1, n do |
95 btnCfg[i] = {} | 101 if btnCfg[i] == nil then |
96 end | 102 btnCfg[i] = {} |
97 if btns[i] == nil then | 103 end |
98 local b = Button:New(bar, i, btnCfg[i], barCfg) | 104 if btns[i] == nil then |
99 btns[i] = b | 105 local b = Button:New(bar, i, btnCfg[i], barCfg) |
100 bar:AddButton(i,b) | 106 btns[i] = b |
101 end | 107 bar:AddButton(i,b) |
102 end | 108 end |
103 for i = n+1, #btns do | 109 end |
104 if btns[i] then | 110 for i = n+1, #btns do |
105 bar:RemoveButton(btns[i]) | 111 if btns[i] then |
106 btns[i] = btns[i]:Destroy() | 112 bar:RemoveButton(btns[i]) |
107 if btnCfg[i] then | 113 btns[i] = btns[i]:Destroy() |
108 btnCfg[i] = nil | 114 if btnCfg[i] then |
115 btnCfg[i] = nil | |
116 end | |
109 end | 117 end |
110 end | 118 end |
111 end | 119 end |
112 RefreshLite(bar) | 120 RefreshLite(bar) |
113 end | 121 end |
147 end | 155 end |
148 end | 156 end |
149 | 157 |
150 | 158 |
151 ---- Options ---- | 159 ---- Options ---- |
152 local Handler = { } | 160 do |
153 | 161 local Handler = { } |
154 local options = { | 162 |
155 hideEmpty = { | 163 local options = { |
156 name = L["Hide Empty Buttons"], | 164 hideEmpty = { |
157 desc = L["Hide buttons when empty. This option is not supported for multi-state bars"], | 165 name = L["Hide Empty Buttons"], |
158 order = 1, | 166 order = 1, |
159 type = "toggle", | 167 type = "toggle", |
160 get = "GetHideEmpty", | 168 width = "double", |
161 set = "SetHideEmpty", | 169 get = "GetHideEmpty", |
162 }, | 170 set = "SetHideEmpty", |
163 } | 171 }, |
164 | 172 pages = { |
165 function module:GetBarOptions(bar) | 173 name = L["# Pages"], |
166 return { | 174 desc = L["Use the Dynamic State tab to specify page transitions"], |
167 type = "group", | 175 order = 2, |
168 name = L["Action Buttons"], | 176 type = "range", |
169 handler = Handler:New(bar), | 177 min = 1, |
170 hidden = "Hidden", | 178 max = 10, |
171 args = options | 179 step = 1, |
180 get = "GetNumPages", | |
181 set = "SetNumPages", | |
182 }, | |
183 actions = { | |
184 name = L["Edit Action IDs"], | |
185 order = 13, | |
186 type = "group", | |
187 inline = true, | |
188 args = { | |
189 method = { | |
190 name = L["Assign"], | |
191 order = 1, | |
192 type = "select", | |
193 width = "double", | |
194 values = { [0] = L["Choose Method..."], | |
195 [1] = L["Individually"], | |
196 [2] = L["All at Once"], }, | |
197 get = "GetActionEditMethod", | |
198 set = "SetActionEditMethod", | |
199 }, | |
200 rowSelect = { | |
201 name = L["Row"], | |
202 desc = L["Rows are numbered top to bottom"], | |
203 order = 2, | |
204 type = "select", | |
205 width = "half", | |
206 hidden = "IsButtonSelectHidden", | |
207 values = "GetRowList", | |
208 get = "GetSelectedRow", | |
209 set = "SetSelectedRow", | |
210 }, | |
211 colSelect = { | |
212 name = L["Col"], | |
213 desc = L["Columns are numbered left to right"], | |
214 order = 3, | |
215 type = "select", | |
216 width = "half", | |
217 hidden = "IsButtonSelectHidden", | |
218 values = "GetColumnList", | |
219 get = "GetSelectedColumn", | |
220 set = "SetSelectedColumn", | |
221 }, | |
222 pageSelect = { | |
223 name = L["Page"], | |
224 order = 4, | |
225 type = "select", | |
226 width = "half", | |
227 hidden = "IsPageSelectHidden", | |
228 values = "GetPageList", | |
229 get = "GetSelectedPage", | |
230 set = "SetSelectedPage", | |
231 }, | |
232 single = { | |
233 name = L["Action ID"], | |
234 usage = L["Specify ID 1-120"], | |
235 order = 5, | |
236 type = "input", | |
237 width = "half", | |
238 hidden = "IsButtonSelectHidden", | |
239 get = "GetActionID", | |
240 set = "SetActionID", | |
241 validate = "ValidateActionID", | |
242 }, | |
243 multi = { | |
244 name = L["ID List"], | |
245 usage = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"], | |
246 order = 6, | |
247 type = "input", | |
248 multiline = true, | |
249 width = "double", | |
250 hidden = "IsMultiIDHidden", | |
251 get = "GetMultiID", | |
252 set = "SetMultiID", | |
253 validate = "ValidateMultiID", | |
254 } | |
255 } | |
256 }, | |
172 } | 257 } |
173 end | 258 |
174 | 259 function module:GetBarOptions(bar) |
175 -- options handler private | 260 return { |
176 do | 261 type = "group", |
177 local function GetBarConfig( bar ) | 262 name = L["Action Buttons"], |
178 return module.db.profile.bars[bar:GetName()] | 263 handler = Handler:New(bar), |
179 end | 264 hidden = "Hidden", |
180 | 265 args = options |
266 } | |
267 end | |
268 | |
269 -- options handler private | |
181 function Handler:New(bar) | 270 function Handler:New(bar) |
182 return setmetatable( { bar = bar }, { __index = Handler } ) | 271 return setmetatable( { bar = bar }, { __index = Handler } ) |
183 end | 272 end |
184 | 273 |
185 function Handler:Hidden() | 274 function Handler:Hidden() |
197 end | 286 end |
198 | 287 |
199 function Handler:GetHideEmpty() | 288 function Handler:GetHideEmpty() |
200 return GetBarConfig(self.bar).hideEmpty | 289 return GetBarConfig(self.bar).hideEmpty |
201 end | 290 end |
202 end | 291 |
203 | 292 function Handler:GetNumPages() |
293 return GetBarConfig(self.bar).nPages | |
294 end | |
295 | |
296 function Handler:SetNumPages(info, value) | |
297 GetBarConfig(self.bar).nPages = value | |
298 RefreshLite(self.bar) | |
299 end | |
300 | |
301 function Handler:GetActionEditMethod() | |
302 return self.editMethod or 0 | |
303 end | |
304 | |
305 function Handler:SetActionEditMethod(info, value) | |
306 self.editMethod = value | |
307 end | |
308 | |
309 function Handler:IsButtonSelectHidden() | |
310 return self.editMethod ~= 1 | |
311 end | |
312 | |
313 function Handler:GetRowList() | |
314 local r,c = self.bar:GetButtonGrid() | |
315 if self.rowList == nil or #self.rowList ~= r then | |
316 local list = { } | |
317 for i = 1, r do | |
318 table.insert(list,i) | |
319 end | |
320 self.rowList = list | |
321 end | |
322 return self.rowList | |
323 end | |
324 | |
325 function Handler:GetSelectedRow() | |
326 local r, c = self.bar:GetButtonGrid() | |
327 local row = self.selectedRow or 1 | |
328 if row > r then | |
329 row = 1 | |
330 end | |
331 self.selectedRow = row | |
332 return row | |
333 end | |
334 | |
335 function Handler:SetSelectedRow(info, value) | |
336 self.selectedRow = value | |
337 end | |
338 | |
339 function Handler:GetColumnList() | |
340 local r,c = self.bar:GetButtonGrid() | |
341 if self.columnList == nil or #self.columnList ~= c then | |
342 local list = { } | |
343 for i = 1, c do | |
344 table.insert(list,i) | |
345 end | |
346 self.columnList = list | |
347 end | |
348 return self.columnList | |
349 end | |
350 | |
351 function Handler:GetSelectedColumn() | |
352 local r, c = self.bar:GetButtonGrid() | |
353 local col = self.selectedColumn or 1 | |
354 if col > c then | |
355 col = 1 | |
356 end | |
357 self.selectedColumn = col | |
358 return col | |
359 end | |
360 | |
361 function Handler:SetSelectedColumn(info, value) | |
362 self.selectedColumn = value | |
363 end | |
364 | |
365 function Handler:IsPageSelectHidden() | |
366 return self.editMethod ~= 1 or (GetBarConfig(self.bar).nPages or 1) < 2 | |
367 end | |
368 | |
369 function Handler:GetPageList() | |
370 local n = GetBarConfig(self.bar).nPages or 1 | |
371 if self.pageList == nil or #self.pageList ~= n then | |
372 local p = { } | |
373 for i = 1, n do | |
374 table.insert(p,i) | |
375 end | |
376 self.pageList = p | |
377 end | |
378 return self.pageList | |
379 end | |
380 | |
381 function Handler:GetSelectedPage() | |
382 local p = self.selectedPage or 1 | |
383 if p > (GetBarConfig(self.bar).nPages or 1) then | |
384 p = 1 | |
385 end | |
386 self.selectedPage = p | |
387 return p | |
388 end | |
389 | |
390 function Handler:SetSelectedPage(info, value) | |
391 self.selectedPage = value | |
392 end | |
393 | |
394 function Handler:GetActionID() | |
395 local row = self.selectedRow or 1 | |
396 local col = self.selectedColumn or 1 | |
397 local r, c = self.bar:GetButtonGrid() | |
398 local n = (row-1) * c + col | |
399 local btn = module.buttons[self.bar][n] | |
400 if btn then | |
401 return tostring(btn:GetActionID(self.selectedPage or 1)) | |
402 end | |
403 end | |
404 | |
405 function Handler:SetActionID(info, value) | |
406 local row = self.selectedRow or 1 | |
407 local col = self.selectedColumn or 1 | |
408 local r, c = self.bar:GetButtonGrid() | |
409 local n = (row-1) * c + col | |
410 local btn = module.buttons[self.bar][n] | |
411 if btn then | |
412 btn:SetActionID(tonumber(value), self.selectedPage or 1) | |
413 end | |
414 end | |
415 | |
416 function Handler:ValidateActionID(info, value) | |
417 value = tonumber(value) | |
418 if value == nil or value < 1 or value > 120 then | |
419 return L["Specify ID 1-120"] | |
420 end | |
421 return true | |
422 end | |
423 | |
424 function Handler:IsMultiIDHidden() | |
425 return self.editMethod ~= 2 | |
426 end | |
427 | |
428 function Handler:GetMultiID() | |
429 local p = { } | |
430 for i = 1, GetBarConfig(self.bar).nPages or 1 do | |
431 local b = { } | |
432 for _, btn in ipairs(module.buttons[self.bar]) do | |
433 table.insert(b, btn:GetActionID(i)) | |
434 end | |
435 table.insert(p, table.concat(b,",")) | |
436 end | |
437 return table.concat(p,";\n") | |
438 end | |
439 | |
440 | |
441 local function ParseMultiID(nBtns, nPages, s) | |
442 if s:match("[^%d%s,;]") then | |
443 ReAction:Print("items other than digits, spaces, commas, and semicolons in string",s) | |
444 return nil | |
445 end | |
446 local p = { } | |
447 for list in s:gmatch("[^;]+") do | |
448 local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns)) | |
449 local ids = { list:match(pattern) } | |
450 if #ids ~= nBtns then | |
451 ReAction:Print("found",#ids,"buttons instead of",nBtns) | |
452 return nil | |
453 end | |
454 table.insert(p,ids) | |
455 end | |
456 if #p ~= nPages then | |
457 ReAction:Print("found",#p,"pages instead of",nPages) | |
458 return nil | |
459 end | |
460 return p | |
461 end | |
462 | |
463 function Handler:SetMultiID(info, value) | |
464 local btns = module.buttons[self.bar] | |
465 local p = ParseMultiID(#btns, GetBarConfig(self.bar).nPages or 1, value) | |
466 for page, b in ipairs(p) do | |
467 for button, id in ipairs(b) do | |
468 btns[button]:SetActionID(id, page) | |
469 end | |
470 end | |
471 end | |
472 | |
473 function Handler:ValidateMultiID(info, value) | |
474 local bad = L["Invalid action ID list string"] | |
475 if value == nil or ParseMultiID(#module.buttons[self.bar], GetBarConfig(self.bar).nPages or 1, value) == nil then | |
476 return bad | |
477 end | |
478 return true | |
479 end | |
480 end | |
481 | |
482 | |
483 ------ State property options ------ | |
484 do | |
485 local pageOptions = { | |
486 mindcontrol = { | |
487 name = L["Mind Control Support"], | |
488 desc = L["When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions. Select the 'Mind Control' option for the rule type to enable."], | |
489 order = 11, | |
490 type = "toggle", | |
491 disabled = "IsMCDisabled", | |
492 hidden = "IsPageHidden", | |
493 width = "double", | |
494 set = "SetProp", | |
495 get = "GetProp", | |
496 }, | |
497 page = { | |
498 name = L["Show Page #"], | |
499 order = 12, | |
500 type = "select", | |
501 width = "half", | |
502 disabled = "IsPageDisabled", | |
503 hidden = "IsPageHidden", | |
504 values = "GetPageValues", | |
505 set = "SetProp", | |
506 get = "GetPage", | |
507 }, | |
508 } | |
509 | |
510 local function pageImpl( bar, states ) | |
511 local map = { } | |
512 for state, c in pairs(states) do | |
513 if c.mindcontrol then | |
514 map[state] = "mc" | |
515 elseif c.page then | |
516 map[state] = ("page%d"):format(c.page) | |
517 end | |
518 end | |
519 bar:SetStateAttribute("statebutton", map, 1, true) | |
520 end | |
521 | |
522 local PageOptsHandler = { } -- will inherit properties and methods via State:RegisterStateProperty | |
523 | |
524 function PageOptsHandler:IsMCDisabled(info) | |
525 if self:IsPageHidden() then | |
526 return true | |
527 end | |
528 -- only allow this if the mind-control selector or custom/keybind is chosen | |
529 -- see State.lua for the structure of the 'rule' config element | |
530 local rule = self.states[self:GetName()].rule | |
531 if rule then | |
532 if rule.type == "custom" or rule.type == "keybind" then | |
533 return false | |
534 else | |
535 if rule.values and rule.values.possess then | |
536 return false | |
537 end | |
538 end | |
539 end | |
540 return true | |
541 end | |
542 | |
543 function PageOptsHandler:IsPageDisabled() | |
544 -- disabled if not an action button | |
545 return not GetBarConfig(self.bar) or | |
546 -- OR mind-control remapping is enabled | |
547 self.states[self:GetName()].mindcontrol or | |
548 -- OR only one page is enabled | |
549 (GetBarConfig(self.bar).nPages and GetBarConfig(self.bar).nPages < 2) | |
550 end | |
551 | |
552 function PageOptsHandler:IsPageHidden() | |
553 return not GetBarConfig(self.bar) | |
554 end | |
555 | |
556 function PageOptsHandler:GetPageValues() | |
557 local c = GetBarConfig(self.bar) | |
558 if c then | |
559 local n = c.nPages | |
560 if self._npages ~= n then | |
561 self._pagevalues = { } | |
562 self._npages = n | |
563 -- cache the results | |
564 for i = 1, n do | |
565 self._pagevalues[i] = i | |
566 end | |
567 end | |
568 return self._pagevalues | |
569 end | |
570 end | |
571 | |
572 function PageOptsHandler:GetPage(info) | |
573 return self:GetProp(info) or 1 | |
574 end | |
575 | |
576 ReAction:GetModule("State"):RegisterStateProperty("page", pageImpl, pageOptions, PageOptsHandler) | |
577 end | |
578 | |
579 ------ ActionID allocation ------ | |
580 -- this needs to be high performance when requesting new IDs, | |
581 -- or certain controls will become sluggish. However, the new-request | |
582 -- infrastructure can be built lazily the first time that a new request | |
583 -- comes in (which will only happen at user config time: at static startup | |
584 -- config time all actionIDs should already have been assigned and stored | |
585 -- in the config file) | |
586 | |
587 local IDAlloc | |
588 do | |
589 local n = 120 | |
590 | |
591 IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end}) | |
592 | |
593 function IDAlloc:Acquire(id, hint) | |
594 id = tonumber(id) | |
595 hint = tonumber(hint) | |
596 if id and (id < 1 or id > n) then | |
597 id = nil | |
598 end | |
599 if hint and (hint < 1 or hint > n) then | |
600 hint = nil | |
601 end | |
602 if id == nil then | |
603 -- get a free ID | |
604 if hint and self[hint] == 0 then | |
605 -- use the hint if it's free | |
606 id = hint | |
607 elseif self.freecount > 0 then | |
608 -- if neither the id nor the hint are defined or free, but | |
609 -- the list is known to have free IDs, then start searching | |
610 -- at the hint for a free one | |
611 for i = hint or 1, n do | |
612 if self[i] == 0 then | |
613 id = i | |
614 break | |
615 end | |
616 end | |
617 -- self.wrap the search | |
618 if id == nil and hint and hint > 1 then | |
619 for i = 1, hint - 1 do | |
620 if self[i] == 0 then | |
621 id = i | |
622 break | |
623 end | |
624 end | |
625 end | |
626 end | |
627 if id == nil then | |
628 -- if there are no free IDs, start wrapping at 1 | |
629 id = self.wrap | |
630 self.wrap = id + 1 | |
631 if self.wrap > n then | |
632 self.wrap = 1 | |
633 end | |
634 end | |
635 end | |
636 if self[id] == 0 then | |
637 self.freecount = self.freecount - 1 | |
638 end | |
639 self[id] = self[id] + 1 | |
640 return id | |
641 end | |
642 | |
643 function IDAlloc:Release(id) | |
644 id = tonumber(id) | |
645 if id and (id >= 1 or id <= n) then | |
646 self[id] = self[id] - 1 | |
647 if self[id] == 0 then | |
648 self.freecount = self.freecount + 1 | |
649 self.wrap = 1 | |
650 end | |
651 end | |
652 end | |
653 end | |
654 | |
655 local frameRecycler = { } | |
204 | 656 |
205 ------ Button class ------ | 657 ------ Button class ------ |
206 | |
207 -- use-count of action IDs | |
208 local nActionIDs = 120 | |
209 local ActionIDList = setmetatable( {}, { | |
210 __index = function(self, idx) | |
211 if idx == nil then | |
212 for i = 1, nActionIDs do | |
213 if rawget(self,i) == nil then | |
214 rawset(self,i,1) | |
215 return i | |
216 end | |
217 end | |
218 error("ran out of action IDs") | |
219 else | |
220 local c = rawget(self,idx) or 0 | |
221 rawset(self,idx,c+1) | |
222 return idx | |
223 end | |
224 end, | |
225 __newindex = function(self,idx,value) | |
226 if value == nil then | |
227 value = rawget(self,idx) | |
228 if value == 1 then | |
229 value = nil | |
230 elseif value then | |
231 value = value - 1 | |
232 end | |
233 end | |
234 rawset(self,idx,value) | |
235 end | |
236 }) | |
237 | |
238 function Button:New( bar, idx, config, barConfig ) | 658 function Button:New( bar, idx, config, barConfig ) |
239 -- create new self | 659 -- create new self |
240 self = setmetatable( { }, {__index = Button} ) | 660 self = setmetatable( { }, {__index = Button} ) |
241 self.bar, self.idx, self.config, self.barConfig = bar, idx, config, barConfig | 661 self.bar, self.idx, self.config, self.barConfig = bar, idx, config, barConfig |
242 | 662 |
243 config.name = config.name or ("ReAction_%s_%d"):format(bar:GetName(),idx) | 663 local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx) |
244 self.name = config.name | 664 self.name = name |
245 config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured | 665 config.name = name |
666 local lastButton = module.buttons[bar][#module.buttons[bar]] | |
667 config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured | |
246 self.nPages = 1 | 668 self.nPages = 1 |
247 | 669 |
248 local f = CreateFrame("CheckButton", self.name, bar:GetButtonFrame(), "ActionBarButtonTemplate") | 670 -- have to recycle frames with the same name: CreateFrame() |
671 -- doesn't overwrite existing globals (below). Can't set to nil in the global | |
672 -- table because you end up getting taint | |
673 local parent = bar:GetButtonFrame() | |
674 local f = frameRecycler[name] | |
675 if f then | |
676 f:SetParent(parent) | |
677 else | |
678 f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate") | |
679 end | |
249 | 680 |
250 f:SetAttribute("action", config.actionID) | 681 f:SetAttribute("action", config.actionID) |
251 -- install mind control action support for all buttons here just for simplicity | 682 -- install mind control action support for all buttons here just for simplicity |
252 if self.idx <= 12 then | 683 if self.idx <= 12 then |
253 f:SetAttribute("action-mc", 120 + self.idx) | 684 f:SetAttribute("*action-mc", 120 + self.idx) |
254 end | 685 end |
255 | 686 |
256 self.frame = f | 687 self.frame = f |
257 self.normalTexture = getglobal(format("%sNormalTexture",f:GetName())) | 688 self.normalTexture = getglobal(format("%sNormalTexture",f:GetName())) |
258 | 689 |
259 -- initialize the hide state | 690 -- initialize the hide state |
691 f:SetAttribute("showgrid",0) | |
260 self:ShowGrid(not barConfig.hideEmpty) | 692 self:ShowGrid(not barConfig.hideEmpty) |
261 if ReAction:GetConfigMode() then | 693 if ReAction:GetConfigMode() then |
262 self:ShowGrid(true) | 694 self:ShowGrid(true) |
263 end | 695 end |
264 | 696 |
274 f:UnregisterAllEvents() | 706 f:UnregisterAllEvents() |
275 f:Hide() | 707 f:Hide() |
276 f:SetParent(UIParent) | 708 f:SetParent(UIParent) |
277 f:ClearAllPoints() | 709 f:ClearAllPoints() |
278 if self.name then | 710 if self.name then |
279 _G[self.name] = nil | 711 frameRecycler[self.name] = f |
280 end | 712 end |
281 if self.config.actionID then | 713 if self.config.actionID then |
282 ActionIDList[self.config.actionID] = nil | 714 IDAlloc:Release(self.config.actionID) |
283 end | 715 end |
284 if self.config.pages then | 716 if self.config.pages then |
285 for _, id in ipairs(self.config.pages) do | 717 for _, id in ipairs(self.config.pageactions) do |
286 ActionIDList[id] = nil | 718 IDAlloc:Release(id) |
287 end | 719 end |
288 end | 720 end |
289 self.frame = nil | 721 self.frame = nil |
290 self.config = nil | 722 self.config = nil |
291 self.bar = nil | 723 self.bar = nil |
306 | 738 |
307 function Button:GetName() | 739 function Button:GetName() |
308 return self.name | 740 return self.name |
309 end | 741 end |
310 | 742 |
311 function Button:GetActionID() | 743 function Button:GetActionID(page) |
312 return SecureButton_GetModifiedAttribute(self.frame, "action") | 744 if page == nil then |
313 end | 745 -- get the effective ID |
314 | 746 return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction() |
315 function Button:RefreshPages() | 747 else |
316 local nPages = 1 --self.bar:GetNumPages() | 748 if page == 1 then |
317 if nPages ~= self.nPages then | 749 return self.config.actionID |
750 else | |
751 return self.config.pageactions and self.config.pageactions[page] or self.config.actionID | |
752 end | |
753 end | |
754 end | |
755 | |
756 function Button:SetActionID( id, page ) | |
757 id = tonumber(id) | |
758 page = tonumber(page) | |
759 if id == nil or id < 1 or id > 120 then | |
760 error("Button:SetActionID - invalid action ID") | |
761 end | |
762 if page and page ~= 1 then | |
763 if not self.config.pageactions then | |
764 self.config.pageactions = { } | |
765 end | |
766 if self.config.pageactions[page] then | |
767 IDAlloc:Release(self.config.pageactions[page]) | |
768 end | |
769 self.config.pageactions[page] = id | |
770 IDAlloc:Acquire(self.config.pageactions[page]) | |
771 self.frame:SetAttribute(("*action-page%d"):format(page),id) | |
772 else | |
773 IDAlloc:Release(self.config.actionID) | |
774 self.config.actionID = id | |
775 IDAlloc:Acquire(self.config.actionID) | |
776 self.frame:SetAttribute("action",id) | |
777 if self.config.pageactions then | |
778 self.config.pageactions[1] = id | |
779 self.frame:SetAttribute("*action-page1",id) | |
780 end | |
781 end | |
782 end | |
783 | |
784 function Button:RefreshPages( force ) | |
785 local nPages = self.barConfig.nPages | |
786 if nPages and (nPages ~= self.nPages or force) then | |
318 local f = self:GetFrame() | 787 local f = self:GetFrame() |
319 local c = self.config.pages | 788 local c = self.config.pageactions |
320 if nPages > 1 and not c then | 789 if nPages > 1 and not c then |
321 c = { } | 790 c = { } |
322 self.config.pages = c | 791 self.config.pageactions = c |
323 end | 792 end |
324 for i = 1, nPages do | 793 for i = 1, nPages do |
325 c[i] = ActionIDList[c[i]] -- gets a free one if none configured | 794 if i > 1 then |
326 f:SetAttribute(("action-page%d"):format(i)) | 795 c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons()) |
796 else | |
797 c[i] = self.config.actionID -- page 1 is the same as the base actionID | |
798 end | |
799 f:SetAttribute(("*action-page%d"):format(i),c[i]) | |
327 end | 800 end |
328 for i = nPages+1, #c do | 801 for i = nPages+1, #c do |
329 ActionIDList[c[i]] = nil | 802 IDAlloc:Release(c[i]) |
330 c[i] = nil | 803 c[i] = nil |
331 f:SetAttribute(("action-page%d"):format(i)) | 804 f:SetAttribute(("*action-page%d"):format(i),nil) |
332 end | 805 end |
333 | 806 self.nPages = nPages |
334 -- TODO: | |
335 -- apply next-page, prev-page, and direct-page keybinds (via bar:SetStateKeybind abstraction) | |
336 end | 807 end |
337 end | 808 end |
338 | 809 |
339 function Button:ShowGrid( show ) | 810 function Button:ShowGrid( show ) |
340 if not InCombatLockdown() then | 811 if not InCombatLockdown() then |
359 end | 830 end |
360 end | 831 end |
361 end | 832 end |
362 | 833 |
363 function Button:ShowActionIDLabel( show ) | 834 function Button:ShowActionIDLabel( show ) |
835 local f = self:GetFrame() | |
364 if show then | 836 if show then |
365 local id = self:GetActionID() | 837 local id = self:GetActionID() |
366 if not self.actionIDLabel and id and id ~= 0 then | 838 if not f.actionIDLabel then |
367 local f = self:GetFrame() | |
368 local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") | 839 local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge") |
369 label:SetAllPoints() | 840 label:SetAllPoints() |
370 label:SetJustifyH("CENTER") | 841 label:SetJustifyH("CENTER") |
371 label:SetShadowColor(0,0,0,1) | 842 label:SetShadowColor(0,0,0,1) |
372 label:SetShadowOffset(2,-2) | 843 label:SetShadowOffset(2,-2) |
373 label:SetText(tostring(id)) | 844 f.actionIDLabel = label -- store the label with the frame for recycling |
374 self.actionIDLabel = label | |
375 f:HookScript("OnAttributeChanged", | 845 f:HookScript("OnAttributeChanged", |
376 function(frame, attr, value) | 846 function(frame, attr, value) |
377 if attr == "state-parent" then | 847 if label:IsVisible() and (attr == "state-parent" or attr:match("action")) then |
378 label:SetText(tostring(self:GetActionID())) | 848 label:SetText(tostring(frame.action)) |
379 end | 849 end |
380 end) | 850 end) |
381 end | 851 end |
382 self.actionIDLabel:Show() | 852 f.actionIDLabel:SetText(tostring(id)) |
383 elseif self.actionIDLabel then | 853 f.actionIDLabel:Show() |
384 self.actionIDLabel:Hide() | 854 elseif f.actionIDLabel then |
385 end | 855 f.actionIDLabel:Hide() |
386 end | 856 end |
387 | 857 end |
858 |