comparison classes/MultiCastButton.lua @ 161:d0a41fc7b0d7

Totem bar support
author Flick <flickerstreak@gmail.com>
date Fri, 21 Aug 2009 04:15:09 +0000
parents
children fc08372f0c7a
comparison
equal deleted inserted replaced
160:caec78119a17 161:d0a41fc7b0d7
1 local ReAction = ReAction
2 local L = ReAction.L
3 local _G = _G
4 local CreateFrame = CreateFrame
5 local format = string.format
6 local unpack = unpack
7 local GetCVar = GetCVar
8 local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor
9 local CooldownFrame_SetTimer = CooldownFrame_SetTimer
10 local InCombatLockdown = InCombatLockdown
11 local IsUsableSpell = IsUsableSpell
12 local IsUsableAction = IsUsableAction
13 local IsSpellKnown = IsSpellKnown
14 local IsSpellInRange = IsSpellInRange
15 local IsActionInRange = IsActionInRange
16 local GetSpellInfo = GetSpellInfo
17 local GetSpellCooldown = GetSpellCooldown
18 local GetActionCooldown = GetActionCooldown
19 local GetSpellTexture = GetSpellTexture
20 local GetActionTexture = GetActionTexture
21 local GetMultiCastTotemSpells = GetMultiCastTotemSpells
22
23 ReAction:UpdateRevision("$Revision: 154 $")
24
25
26 --[[
27 Blizzard Constants:
28 - NUM_MULTI_CAST_BUTTONS_PER_PAGE = 4
29 - NUM_MULTI_CAST_PAGES = 3
30 - TOTEM_PRIORITIES = { } -- sets the order of the totems
31 - TOTEM_MULTI_CAST_SUMMON_SPELLS = { } -- list of summon spellIDs
32 - TOTEM_MULTI_CAST_RECALL_SPELLS = { } -- list of recall spellIDs
33
34 Blizzard Events:
35 - UPDATE_MULTI_CAST_ACTIONBAR
36
37 Blizzard APIs:
38 - GetMultiCastBarOffset() : returns 6
39
40 - SetMultiCastSpell(actionID, spellID) (protected) OR
41 SetAttribute("type","multispell")
42 SetAttribute("action",actionID)
43 SetAttribute("spell",spellID)
44
45 note: multicast actionID page is NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset(),
46 so that's action ID 132-144.
47
48 - spell1, spell2, spell3, ... = GetMultiCastTotemSpells(slot)
49 returns spellIDs for all totems that fit that slot.
50 Note this is available in the secure environment, but because IsSpellKnown() is not,
51 it makes it pretty much useless.
52
53 Blizzard textures:
54 All the textures for the multicast bar (arrows, empty-slot icons, etc) are part of a single
55 texture: each texture uses SetTexCoord() to display only a slice of the textures. I suppose
56 this is to slightly optimize texture load performance, but it makes the UI code more clumsy.
57
58 Each totem button and arrow has a colored border indicating its elemental type.
59 TODO: update code to use these pretty per-slot icons, or start setting a border.
60
61 Design Notes:
62 - Only the header has a secure context. All other frames execute in its context.
63
64 - Each button is either type "spell" (summon/recall) or type "action" (totem action IDs are
65 GetBonusBarOffset()=6, 132-144) with 3 pages of 4 buttons. The paging is controlled by
66 the summon flyout, which is also paged with the summon spells (the recall button is not paged)
67
68 - A spell list is updated in the secure context at setup time (TODO: redo setup when learning new
69 spells) with the list of spells known for each slot.
70
71 - Each button (except recall) has an arrow button which appears on mouseover and when clicked
72 opens the flyout via a wrapped OnClick handler. When the flyout is open, the arrow does not
73 appear.
74 TODO: add an alt-button ("SHOWMULTICASTFLYOUT") statemachine to the bar to listen for alt key
75 presses and open/close the bar. Tapping the alt key toggles the flyout.
76
77 - A single flyout with N+1 (1 slot is to select no totem for the set) flyout-buttons is a child
78 of the bar. Each time the flyout panel is opened, the individual buttons grab their corresponding
79 spell/type from the list, according to the slot which opened the flyout. Each button either sets
80 the current page (summon) or sets a multispell to an actionID via type="multispell". None of them
81 actually cast any spells (though, I suppose we could modify this so that e.g. configurable
82 right-click casts the spell). The flyout also has a close button which closes the flyout: the
83 flyout-open code positions the close button anchored to the last button in the flyout (which
84 changes dynamically because each slot has a different number of items in the list).
85
86 - Multicast sets are not stances, there's no need (or ability) to handle swapping sets if one of
87 the summon spells is cast from elsewhere. Additionally, in the default UI Call of the Elements
88 always appears as the active summon when the UI is loaded, which is bad design that could be improved
89 upon. TODO: Store state in a config variable and restore it at load time.
90
91
92 ]]--
93
94
95 --
96 -- Secure snippets
97 --
98
99 -- bar
100 local _bar_init = -- function(self)
101 [[
102 -- set up some globals in the secure environment
103 flyout = self:GetFrameRef("flyout")
104 flyoutChildren = newtable()
105 nMultiCastSlots = self:GetAttribute("nMultiCastSlots")
106 baseActionID = self:GetAttribute("baseActionID")
107 currentMultiCastPage = currentMultiCastPage or 1
108 multiCastSpellList = newtable()
109 for i = 1, nMultiCastSlots do
110 tinsert(multiCastSpellList, newtable())
111 end
112 ]]
113
114 local _onstate_multispellpage = -- function(self, stateid, newstate)
115 [[
116 currentMultiCastPage = tonumber(newstate)
117 control:ChildUpdate()
118 ]]
119
120
121 -- buttons
122 local _childupdate = -- function(self, snippetid, message)
123 [[
124 if self:GetAttribute("type") == "spell" then
125 self:SetAttribute("spell", self:GetAttribute("spell-page"..currentMultiCastPage))
126 elseif self:GetAttribute("type") == "action" then
127 self:SetAttribute("action", self:GetAttribute("action-page"..currentMultiCastPage))
128 end
129 ]]
130
131 local _onEnter = -- function(self)
132 -- for whatever reason, RegisterAutoHide is unreliable
133 -- unless you re-anchor the frame prior to calling it.
134 -- Even then, it's still not terribly reliable.
135 [[
136 local idx = self:GetAttribute("bar-idx")
137 if not (flyout:IsVisible() and flyoutIdx == idx) then
138 local arrow = owner:GetFrameRef("arrow-"..idx)
139 if arrow and not arrow:IsShown() then
140 arrow:ClearAllPoints()
141 arrow:SetPoint("BOTTOM",self,"TOP",0,0) -- TODO: better anchoring
142 arrow:Show()
143 arrow:RegisterAutoHide(0)
144 arrow:AddToAutoHide(self)
145 end
146 end
147 ]]
148
149 local _onLeave = -- function(self)
150 -- to increase reliability (somewhat), re-register it for hide on leave
151 [[
152 local arrow = owner:GetFrameRef("arrow-"..self:GetAttribute("bar-idx"))
153 if arrow then
154 arrow:RegisterAutoHide(0)
155 arrow:AddToAutoHide(self)
156 end
157 ]]
158
159
160 -- flyout arrow
161 local _arrow_openFlyout = -- function(self)
162 [[
163 local currentMultiCastSlot = self:GetAttribute("bar-idx")
164 local lastButton, lastIdx
165 for idx, b in ipairs(flyoutChildren) do
166 b:Hide() -- force the OnShow handler to run later
167 local spellID = multiCastSpellList[currentMultiCastSlot][idx]
168 if spellID then
169 b:SetAttribute("spell",spellID) -- does passing 0 work for no-totem? Do we have to convert to nil?
170 if currentMultiCastSlot == 1 then
171 b:SetAttribute("type","changePage")
172 else
173 b:SetAttribute("type","multispell")
174 local totemID = owner:GetAttribute("TOTEM_PRIORITY_"..(currentMultiCastSlot - 1))
175 b:SetAttribute("action", baseActionID + (currentMultiCastPage - 1)*(nMultiCastSlots-2) + totemID)
176 end
177 b:Show()
178 lastButton = b
179 lastIdx = idx
180 end
181 end
182
183 local close = owner:GetFrameRef("close")
184 if lastButton and close then
185 close:ClearAllPoints()
186 close:SetPoint("BOTTOM",lastButton,"TOP",0,0) -- TODO: better anchoring
187 close:Show()
188 end
189
190 flyout:ClearAllPoints()
191 flyout:SetPoint("BOTTOM",self,"BOTTOM",0,0) -- TODO: better anchoring
192 if lastIdx then
193 flyout:SetHeight(lastIdx * 27 + (close and close:GetHeight() or 0))
194 end
195 flyout:Show()
196 flyout:RegisterAutoHide(1) -- TODO: configurable
197 flyout:AddToAutoHide(owner)
198 flyoutIdx = currentMultiCastSlot
199 self:Hide()
200 ]]
201
202 local _closeFlyout = -- function(self)
203 [[
204 flyout:Hide()
205 ]]
206
207
208 -- flyout child buttons
209 local _flyout_child_preClick = -- function(self, button, down)
210 [[
211 local button = button
212 if self:GetAttribute("type") == "changePage" then
213 owner:SetAttribute("state-multispellpage",self:GetAttribute("index"))
214 self:GetParent():Hide()
215 return false
216 else
217 return nil, "close"
218 end
219 ]]
220
221 local _flyout_child_postClick = -- function(self, message, button, down)
222 [[
223 if message == "close" then
224 self:GetParent():Hide() -- hide flyout after selecting
225 end
226 ]]
227
228
229 --
230 -- The Blizzard totem bar textures are all actually one big texture,
231 -- with texcoord offsets
232 --
233 local TOTEM_TEXTURE = "Interface\\Buttons\\UI-TotemBar"
234 local FLYOUT_UP_BUTTON_TCOORDS = { 99/128, 127/128, 84/256, 102/256 }
235 local FLYOUT_UP_BUTTON_HL_TCOORDS = { 72/128, 92/128, 88/256, 98/256 }
236 local FLYOUT_DOWN_BUTTON_TCOORDS = { 99/128, 127/128, 65/256, 83/256 }
237 local FLYOUT_DOWN_BUTTON_HL_TCOORDS = { 72/128, 92/128, 69/256, 79/256 }
238 local EMPTY_SLOT_TCOORDS = { 66/128, 96/128, 3/256, 33/256 }
239
240 local eventList = {
241 -- TODO
242 "PLAYER_REGEN_ENABLED",
243 "PLAYER_ENTERING_WORLD",
244 "ACTIONBAR_PAGE_CHANGED",
245 "ACTIONBAR_SLOT_CHANGED",
246 "UPDATE_BINDINGS",
247 "ACTIONBAR_UPDATE_STATE",
248 "ACTIONBAR_UPDATE_USABLE",
249 "ACTIONBAR_UPDATE_COOLDOWN",
250 "UPDATE_INVENTORY_ALERTS",
251 "PLAYER_TARGET_CHANGED",
252 "TRADE_SKILL_SHOW",
253 "TRADE_SKILL_CLOSE",
254 "PLAYER_ENTER_COMBAT",
255 "PLAYER_LEAVE_COMBAT",
256 "START_AUTOREPEAT_SPELL",
257 "STOP_AUTOREPEAT_SPELL",
258 "UNIT_ENTERED_VEHICLE",
259 "UNIT_EXITED_VEHICLE",
260 "COMPANION_UPDATE",
261 "UPDATE_MULTI_CAST_ACTIONBAR",
262 }
263
264 --
265 -- MultiCast Button class
266 -- Inherits implementation methods from Action button class, but circumvents the constructor
267 -- and redefines/removes some methods.
268 --
269 local Super = ReAction.Button
270 local Action = ReAction.Button.Action
271 local MultiCast = setmetatable( { }, { __index = Action } )
272 ReAction.Button.MultiCast = MultiCast
273
274 function MultiCast:New( idx, btnConfig, bar )
275 if idx < 1 or idx > NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then
276 error("Multicast button index out of range")
277 end
278
279 local name = format("ReAction_%s_Action_%d",bar:GetName(),idx)
280
281 self = Super.New(self, name, btnConfig, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" )
282
283 local barFrame = bar:GetFrame()
284 local f = self:GetFrame()
285
286 -- attributes
287 if idx == 1 or idx == NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then
288 f:SetAttribute("type","spell")
289 local spells = idx == 1 and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS
290 for i, spell in ipairs(spells) do
291 if spell and IsSpellKnown(spell) then
292 f:SetAttribute("spell-page"..i, spell)
293 if i == 1 then
294 -- TODO: store/restore last used summon
295 f:SetAttribute("spell",spell)
296 end
297 end
298 end
299 else
300 local baseAction = barFrame:GetAttribute("baseActionID") + TOTEM_PRIORITIES[idx-1]
301 f:SetAttribute("type","action")
302 f:SetAttribute("action", baseAction)
303 for i = 1, NUM_MULTI_CAST_PAGES do
304 f:SetAttribute("action-page"..i, baseAction + (i-1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE)
305 end
306 end
307 f:SetAttribute("bar-idx",idx)
308 barFrame:SetFrameRef("slot-"..idx,f)
309
310 -- non secure scripts
311 f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end)
312 f:SetScript("OnEnter", function(frame) self:OnEnter() end)
313 f:SetScript("OnLeave", function(frame) self:OnLeave() end)
314 f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end)
315 f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end)
316
317 -- secure handlers
318 if idx ~= NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then
319 f:SetAttribute("_childupdate",_childupdate)
320 end
321 barFrame:WrapScript(f, "OnEnter", _onEnter)
322
323 -- event registration
324 f:EnableMouse(true)
325 f:RegisterForClicks("AnyUp")
326 for _, evt in pairs(eventList) do
327 f:RegisterEvent(evt)
328 end
329
330 -- attach to skinner
331 bar:SkinButton(self)
332
333 f:Show()
334
335 -- open arrow
336 if idx ~= NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then
337 local arrow = CreateFrame("Button", nil, f, "SecureFrameTemplate")
338 arrow:SetWidth(28)
339 arrow:SetHeight(12)
340 arrow:SetPoint("BOTTOM",self:GetFrame(),"TOP",0,0) -- TODO: better anchoring
341 arrow:SetNormalTexture(TOTEM_TEXTURE)
342 arrow:GetNormalTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_TCOORDS) )
343 arrow:SetHighlightTexture(TOTEM_TEXTURE)
344 arrow:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_HL_TCOORDS) )
345 arrow:SetAttribute("bar-idx",idx)
346 arrow:Hide()
347 barFrame:WrapScript(arrow, "OnClick", _arrow_openFlyout)
348 local arrowRef = "arrow-"..idx
349 barFrame:SetFrameRef(arrowRef,arrow)
350 end
351
352 self:Refresh()
353
354 return self
355 end
356
357 function MultiCast:Destroy()
358 local barFrame = self:GetFrame()
359 Super.Destroy(self)
360 end
361
362 function MultiCast:Refresh()
363 Super.Refresh(self)
364 self:UpdateAction()
365 end
366
367 function MultiCast:ShowGrid( show )
368 end
369
370 function MultiCast:ShowGridTemp( show )
371 end
372
373 function MultiCast:AcquireActionID()
374 end
375
376 function MultiCast:ReleaseActionID()
377 end
378
379 function MultiCast:UpdateShowGrid()
380 end
381
382 function MultiCast:UpdateBorder()
383 end
384
385 function MultiCast:UpdateMacroText()
386 end
387
388 function MultiCast:UpdateCount()
389 end
390
391 function MultiCast:UpdateCheckedState()
392 local action = self:GetActionID()
393 if action and IsCurrentAction(action) then
394 self:GetFrame():SetChecked(1)
395 else
396 self:GetFrame():SetChecked(0)
397 end
398 end
399
400 function MultiCast:RefreshHasActionAttributes()
401 end
402
403 function MultiCast:UpdateFlash()
404 end
405
406 function MultiCast:GetIconTexture()
407 local tx
408 if self.spellID then
409 tx = GetSpellTexture(GetSpellInfo(self.spellID))
410 elseif self.actionID then
411 tx = GetActionTexture(self.actionID)
412 end
413 if tx then
414 return tx
415 else
416 return TOTEM_TEXTURE, unpack(EMPTY_SLOT_TCOORDS)
417 end
418 end
419
420 function MultiCast:UpdateAction()
421 local action = self:GetActionID()
422 if action then
423 if action ~= self.actionID then
424 self.actionID = action
425 self:UpdateAll()
426 end
427 else
428 local spellID = self:GetSpellID()
429 if spellID ~= self.spellID then
430 self.spellID = spellID
431 self:UpdateAll()
432 end
433 end
434 end
435
436 function MultiCast:GetActionID(page)
437 return self:GetFrame():GetAttribute("action")
438 end
439
440 function MultiCast:GetSpellID(page)
441 return self:GetFrame():GetAttribute("spell")
442 end
443
444 function MultiCast:SetActionID( id )
445 error("Can not set action ID of multicast buttons")
446 end
447
448 function MultiCast:SetTooltip()
449 local barFrame = self:GetFrame()
450 if GetCVar("UberTooltips") == "1" then
451 GameTooltip_SetDefaultAnchor(GameTooltip, barFrame)
452 else
453 GameTooltip:SetOwner(barFrame)
454 end
455 if self.spellID then
456 GameTooltip:SetSpellByID(self.spellID,false,true)
457 elseif self.actionID then
458 GameTooltip:SetAction(self.actionID)
459 end
460 end
461
462 function MultiCast:GetUsable()
463 if self.spellID then
464 return IsUsableSpell((GetSpellInfo(self.spellID)))
465 elseif self.actionID then
466 return IsUsableAction(self.actionID)
467 end
468 end
469
470 function MultiCast:GetInRange()
471 if self.spellID then
472 return IsSpellInRange((GetSpellInfo(self.spellID))) == 0
473 elseif self.actionID then
474 return IsActionInRange(self.actionID) == 0
475 end
476 end
477
478 function MultiCast:GetCooldown()
479 if self.spellID then
480 return GetSpellCooldown((GetSpellInfo(self.spellID)))
481 elseif self.actionID then
482 return GetActionCooldown(self.actionID)
483 else
484 return 0, 0, 0
485 end
486 end
487
488 function MultiCast:UPDATE_MULTI_CAST_ACTIONBAR()
489 self:UpdateAll()
490 end
491
492
493 --
494 -- flyout setup
495 --
496 local function ShowFlyoutTooltip(barFrame)
497 if GetCVar("UberTooltips") == "1" then
498 GameTooltip_SetDefaultAnchor(GameTooltip, barFrame)
499 else
500 GameTooltip:SetOwner(barFrame)
501 end
502 local spell = barFrame:GetAttribute("spell")
503 if barFrame == 0 then
504 GameTooltip:SetText(MULTI_CAST_TOOLTIP_NO_TOTEM, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
505 else
506 GameTooltip:SetSpellByID(barFrame:GetAttribute("spell"),false,true)
507 end
508 end
509
510 local function HideFlyoutTooltip()
511 GameTooltip:Hide()
512 end
513
514 local function UpdateFlyoutIcon(frame)
515 local spellID = frame:GetAttribute("spell")
516 if spellID == 0 then
517 frame.icon:SetTexture(TOTEM_TEXTURE)
518 frame.icon:SetTexCoord( unpack(EMPTY_SLOT_TCOORDS) )
519 elseif spellID then
520 frame.icon:SetTexture(GetSpellTexture(GetSpellInfo(spellID)))
521 frame.icon:SetTexCoord(0,1,0,1)
522 end
523 end
524
525 function MultiCast.SetupBarHeader( bar ) -- call this as a static method
526 local summon = { }
527 local recall = { }
528 local maxIdx = 1
529
530 for idx, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do
531 if spell and IsSpellKnown(spell) then
532 tinsert(summon,spell)
533 maxIdx = max(idx,maxIdx)
534 end
535 end
536
537 for idx, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do
538 if spell and IsSpellKnown(spell) then
539 tinsert(recall,spell)
540 maxIdx = max(idx,maxIdx)
541 end
542 end
543
544 if #summon == 0 and #recall == 0 then
545 return 0 -- no multicast capability
546 end
547
548 local slots = { }
549
550 tinsert(slots, summon)
551
552 for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do
553 local slotSpells = { 0, GetMultiCastTotemSpells(TOTEM_PRIORITIES[i]) }
554 maxIdx = max(maxIdx, #slotSpells)
555 tinsert(slots,slotSpells)
556 end
557
558 tinsert(slots, recall)
559
560 local barFrame = bar:GetFrame()
561
562 -- init bar secure environment
563 barFrame:SetAttribute("nMultiCastSlots",#slots)
564 barFrame:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS)
565 barFrame:SetAttribute("_onstate-multispellpage", _onstate_multispellpage)
566 barFrame:Execute(_bar_init)
567
568 for i, p in ipairs(TOTEM_PRIORITIES) do
569 barFrame:SetAttribute("TOTEM_PRIORITY_"..i,p)
570 end
571
572 -- create flyout container frame and close arrow
573 local flyout = bar._flyoutFrame
574 if not flyout then
575 flyout = CreateFrame("Frame", nil, barFrame, "SecureFrameTemplate")
576 bar._flyoutFrame = flyout
577 barFrame:SetFrameRef("flyout",flyout)
578 flyout.buttons = { }
579 flyout:Hide()
580 flyout:SetWidth(24)
581 flyout:SetHeight(1)
582 flyout:SetPoint("BOTTOM",barFrame,"TOP",0,0)
583
584 local close = CreateFrame("Button", nil, flyout, "SecureFrameTemplate")
585 close:SetWidth(28)
586 close:SetHeight(12)
587 close:SetPoint("TOP")
588 close:SetNormalTexture(TOTEM_TEXTURE)
589 close:GetNormalTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_TCOORDS) )
590 close:SetHighlightTexture(TOTEM_TEXTURE)
591 close:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_HL_TCOORDS) )
592 barFrame:SetFrameRef("close",close)
593 barFrame:WrapScript(close, "OnClick", _closeFlyout)
594 end
595
596 -- create flyout buttons
597 for i = #flyout.buttons + 1, maxIdx do
598 local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate")
599 b:SetWidth(24)
600 b:SetHeight(24)
601 local prev = flyout.buttons[i-1]
602 b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring
603 b.icon = b:CreateTexture("BACKGROUND")
604 b.icon:SetAllPoints()
605 b.icon:Show()
606 b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square")
607 b:GetHighlightTexture():SetBlendMode("ADD")
608 b:RegisterForClicks("AnyUp")
609 b:SetScript("OnShow",UpdateFlyoutIcon)
610 b:SetScript("OnEnter",ShowFlyoutTooltip)
611 b:SetScript("OnLeave",HideFlyoutTooltip)
612 b:SetAttribute("index",i)
613 b:Show()
614 barFrame:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick)
615 flyout.buttons[i] = b
616 end
617
618 for i, b in ipairs(flyout.buttons) do
619 barFrame:SetFrameRef("flyout-child",b)
620 barFrame:Execute([[
621 tinsert(flyoutChildren,self:GetFrameRef("flyout-child"))
622 ]])
623 end
624
625 -- transfer the table of spell IDs into the secure environment
626 for i, spells in ipairs(slots) do
627 barFrame:SetAttribute("spell-slot", i)
628 for j, spell in ipairs(spells) do
629 barFrame:SetAttribute("spell-index", j)
630 barFrame:SetAttribute("spell-id", spell)
631 barFrame:Execute([[
632 multiCastSpellList[self:GetAttribute("spell-slot")][self:GetAttribute("spell-index")] = self:GetAttribute("spell-id")
633 ]])
634 end
635 end
636
637 return #slots
638 end
639