comparison MultiCastButton.lua @ 257:920d17851a93 stable

Merge 1.1 beta 4 to stable
author Flick
date Tue, 12 Apr 2011 16:06:31 -0700
parents 65f2805957a0
children c918ff9ac787
comparison
equal deleted inserted replaced
210:b2b105747466 257:920d17851a93
1 local addonName, addonTable = ...
2 local ReAction = addonTable.ReAction
3 local L = ReAction.L
4 local _G = _G
5 local CreateFrame = CreateFrame
6 local format = string.format
7 local unpack = unpack
8 local GetCVar = GetCVar
9 local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor
10 local CooldownFrame_SetTimer = CooldownFrame_SetTimer
11 local InCombatLockdown = InCombatLockdown
12 local IsUsableSpell = IsUsableSpell
13 local IsUsableAction = IsUsableAction
14 local IsSpellKnown = IsSpellKnown
15 local IsSpellInRange = IsSpellInRange
16 local IsActionInRange = IsActionInRange
17 local GetSpellInfo = GetSpellInfo
18 local GetSpellCooldown = GetSpellCooldown
19 local GetActionCooldown = GetActionCooldown
20 local GetSpellTexture = GetSpellTexture
21 local GetActionTexture = GetActionTexture
22 local GetMultiCastTotemSpells = GetMultiCastTotemSpells
23
24 --[[
25 Blizzard Constants:
26 - NUM_MULTI_CAST_BUTTONS_PER_PAGE = 4
27 - NUM_MULTI_CAST_PAGES = 3
28 - SHAMAN_TOTEM_PRIORITIES = { } -- sets the order of the totems
29 - TOTEM_MULTI_CAST_SUMMON_SPELLS = { } -- list of summon spellIDs
30 - TOTEM_MULTI_CAST_RECALL_SPELLS = { } -- list of recall spellIDs
31
32 Blizzard Events:
33 - UPDATE_MULTI_CAST_ACTIONBAR
34
35 Blizzard APIs:
36 - GetMultiCastBarOffset() : returns 6
37
38 - SetMultiCastSpell(actionID, spellID) (protected) OR
39 SetAttribute("type","multispell")
40 SetAttribute("action",actionID)
41 SetAttribute("spell",spellID)
42
43 note: multicast actionID page is NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset(),
44 so that's action ID 132-144.
45
46 - spell1, spell2, spell3, ... = GetMultiCastTotemSpells(slot)
47 returns spellIDs for all known totems that fit that slot. This function is available in
48 the secure environment.
49
50 Blizzard textures:
51 All the textures for the multicast bar (arrows, empty-slot icons, etc) are part of a single
52 texture: each texture uses SetTexCoord() to display only a slice of the textures. I suppose
53 this is to slightly optimize texture load performance, but it makes the UI code more clumsy.
54
55 Each totem button and arrow has a colored border indicating its elemental type.
56
57 TODO:
58 - make whether to show the colored border configurable (looks really bad with ButtonFacade:Zoomed)
59 - apply ButtonFacade to the flyout buttons? Or at least zoom the textures slightly?
60 - use a multiplier with SetTexCoord on totem bar texture?
61
62 Design Notes:
63 - Only the header has a secure context. All other frames execute in its context.
64
65 - Each button is either type "spell" (summon/recall) or type "action" (totem action IDs are
66 GetBonusBarOffset()=6, 132-144) with 3 pages of 4 buttons. The paging is controlled by
67 the summon flyout, which is also paged with the summon spells (the recall button is not paged)
68
69 - A spell list is updated in the secure context at setup time (TODO: redo setup when learning new
70 spells) with the list of spells known for each slot.
71
72 - Each button (except recall) has an arrow button which appears on mouseover and when clicked
73 opens the flyout via a wrapped OnClick handler. When the flyout is open, the arrow does not
74 appear.
75
76 - A single flyout with N+1 (1 slot is to select no totem for the set) flyout-buttons is a child
77 of the bar. Each time the flyout panel is opened, the individual buttons grab their corresponding
78 spell/type from the list, according to the slot which opened the flyout. Each button either sets
79 the current page (summon) or sets a multispell to an actionID via type="multispell". None of them
80 actually cast any spells (though, I suppose we could modify this so that e.g. configurable
81 right-click casts the spell). The flyout also has a close button which closes the flyout: the
82 flyout-open code positions the close button anchored to the last button in the flyout (which
83 changes dynamically because each slot has a different number of items in the list).
84
85 - Multicast sets are not stances, there's no need (or ability) to handle swapping sets if one of
86 the summon spells is cast from elsewhere.
87
88 - The default UI has Call of the Elements always selected on UI load. This module remembers the last
89 selected one and restores it.
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 flyoutSlot = nil
105 summonSlot = self:GetAttribute("summonSlot")
106 recallSlot = self:GetAttribute("recallSlot")
107 baseActionID = self:GetAttribute("baseActionID")
108 slotsPerPage = self:GetAttribute("slotsPerPage")
109 currentPage = currentPage or self:GetAttribute("lastSummon") or 1
110
111 totemIDsBySlot = newtable()
112 for i = 1, slotsPerPage do
113 totemIDsBySlot[i] = self:GetAttribute("TOTEM_PRIORITY_"..i)
114 end
115
116 -- these are set up in bar:SetupBar()
117 flyoutChildren = flyoutChildren or newtable()
118 summonSpells = summonSpells or newtable()
119 ]]
120
121 local _onstate_multispellpage = -- function(self, stateid, newstate)
122 [[
123 currentPage = tonumber(newstate)
124 control:CallMethod("UpdateLastSummon",currentPage)
125 control:ChildUpdate()
126 ]]
127
128
129 -- buttons
130 local _childupdate = -- function(self, snippetid, message)
131 [[
132 local t = self:GetAttribute("type")
133 self:SetAttribute(t, self:GetAttribute(t.."-page"..currentPage))
134 ]]
135
136 local _onEnter = -- function(self)
137 -- for whatever reason, RegisterAutoHide is unreliable
138 -- unless you re-anchor the frame prior to calling it.
139 -- Even then, it's still not terribly reliable.
140 [[
141 local slot = self:GetAttribute("bar-idx")
142 local arrow = owner:GetFrameRef("arrow-"..slot)
143 if arrow and not arrow:IsShown() and not (flyout:IsVisible() and flyoutSlot == slot) then
144 arrow:ClearAllPoints()
145 arrow:SetPoint("BOTTOM",self,"TOP",0,0)
146 arrow:Show()
147 arrow:RegisterAutoHide(0)
148 arrow:AddToAutoHide(self)
149 end
150 ]]
151
152
153 -- flyout arrow
154 local _arrow_openFlyout = -- function(self)
155 [[
156 local slot = self:GetAttribute("bar-idx")
157 local totemID = totemIDsBySlot[slot - (summonSlot or 0)]
158 if totemID == 0 then
159 totemID = "summon"
160 end
161
162 local lastButton, lastPage
163 for page, b in ipairs(flyoutChildren) do
164 b:Hide()
165 b:SetAttribute("totemSlot",totemID)
166 if slot == summonSlot then
167 local spellID = self:GetParent():GetAttribute("spell-page"..page)
168 if spellID then
169 b:SetAttribute("type","changePage")
170 b:SetAttribute("spell",spellID)
171 b:Show()
172 lastButton = b
173 lastPage = page
174 end
175 else
176 local spell = select(page, 0, GetMultiCastTotemSpells(totemID) )
177 if spell then
178 b:SetAttribute("type","multispell")
179 b:SetAttribute("action", baseActionID + (currentPage - 1)*slotsPerPage + totemID)
180 b:SetAttribute("spell", spell)
181 b:Show()
182 lastButton = b
183 lastPage = page
184 end
185 end
186 end
187
188 local close = owner:GetFrameRef("close")
189 if lastButton and close then
190 close:ClearAllPoints()
191 close:SetPoint("BOTTOM",lastButton,"TOP",0,0) -- TODO: better anchoring
192 close:Show()
193 control:CallMethod("UpdateFlyoutTextures",totemID)
194 end
195
196 flyout:ClearAllPoints()
197 flyout:SetPoint("BOTTOM",self,"BOTTOM",0,0) -- TODO: better anchoring
198 if lastPage then
199 flyout:SetHeight(lastPage * 27 + (close and close:GetHeight() or 0))
200 end
201 flyout:Show()
202 flyout:RegisterAutoHide(1) -- TODO: configurable
203 flyout:AddToAutoHide(owner)
204 flyoutSlot = slot
205 self:Hide()
206 ]]
207
208 local _closeFlyout = -- function(self)
209 [[
210 flyout:Hide()
211 ]]
212
213
214 -- flyout child buttons
215 local _flyout_child_preClick = -- function(self, button, down)
216 [[
217 local button = button
218 if self:GetAttribute("type") == "changePage" then
219 owner:SetAttribute("state-multispellpage",self:GetAttribute("index"))
220 self:GetParent():Hide()
221 return false
222 else
223 return nil, "close"
224 end
225 ]]
226
227 local _flyout_child_postClick = -- function(self, message, button, down)
228 [[
229 if message == "close" then
230 self:GetParent():Hide() -- hide flyout after selecting
231 end
232 ]]
233
234
235 --
236 -- The Blizzard totem bar textures are all actually one big texture,
237 -- with texcoord offsets. Shamelessly stolen from FrameXML/MultiCastActionBarFrame.lua
238 --
239 local TOTEM_TEXTURE = "Interface\\Buttons\\UI-TotemBar"
240 local FLYOUT_UP_BUTTON_HL_TCOORDS = { 72/128, 92/128, 88/256, 98/256 }
241 local FLYOUT_DOWN_BUTTON_HL_TCOORDS = { 72/128, 92/128, 69/256, 79/256 }
242
243 local SLOT_EMPTY_TCOORDS = {
244 [EARTH_TOTEM_SLOT] = { 66/128, 96/128, 3/256, 33/256 },
245 [FIRE_TOTEM_SLOT] = { 67/128, 97/128, 100/256, 130/256 },
246 [WATER_TOTEM_SLOT] = { 39/128, 69/128, 209/256, 239/256 },
247 [AIR_TOTEM_SLOT] = { 66/128, 96/128, 36/256, 66/256 },
248 }
249
250 local SLOT_OVERLAY_TCOORDS = {
251 [EARTH_TOTEM_SLOT] = { 1/128, 35/128, 172/256, 206/256 },
252 [FIRE_TOTEM_SLOT] = { 36/128, 70/128, 172/256, 206/256 },
253 [WATER_TOTEM_SLOT] = { 1/128, 35/128, 207/256, 240/256 },
254 [AIR_TOTEM_SLOT] = { 36/128, 70/128, 137/256, 171/256 },
255 }
256
257 local FLYOUT_UP_BUTTON_TCOORDS = {
258 ["summon"] = { 99/128, 127/128, 84/256, 102/256 },
259 [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 160/256, 178/256 },
260 [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 122/256, 140/256 },
261 [WATER_TOTEM_SLOT] = { 99/128, 127/128, 199/256, 217/256 },
262 [AIR_TOTEM_SLOT] = { 99/128, 127/128, 237/256, 255/256 },
263 }
264
265 local FLYOUT_DOWN_BUTTON_TCOORDS = {
266 ["summon"] = { 99/128, 127/128, 65/256, 83/256 },
267 [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 141/256, 159/256 },
268 [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 103/256, 121/256 },
269 [WATER_TOTEM_SLOT] = { 99/128, 127/128, 180/256, 198/256 },
270 [AIR_TOTEM_SLOT] = { 99/128, 127/128, 218/256, 236/256 },
271 }
272
273 local FLYOUT_TOP_TCOORDS = {
274 ["summon"] = { 33/128, 65/128, 1/256, 23/256 },
275 [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 46/256, 68/256 },
276 [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 46/256, 68/256 },
277 [WATER_TOTEM_SLOT] = { 0/128, 32/128, 1/256, 23/256 },
278 [AIR_TOTEM_SLOT] = { 0/128, 32/128, 91/256, 113/256 },
279 }
280
281 local FLYOUT_MIDDLE_TCOORDS = {
282 ["summon"] = { 33/128, 65/128, 23/256, 43/256 },
283 [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 68/256, 88/256 },
284 [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 68/256, 88/256 },
285 [WATER_TOTEM_SLOT] = { 0/128, 32/128, 23/256, 43/256 },
286 [AIR_TOTEM_SLOT] = { 0/128, 32/128, 113/256, 133/256 },
287 }
288
289 local eventList = {
290 "ACTIONBAR_SLOT_CHANGED",
291 "ACTIONBAR_UPDATE_STATE",
292 "ACTIONBAR_UPDATE_USABLE",
293 "ACTIONBAR_UPDATE_COOLDOWN",
294 "UPDATE_BINDINGS",
295 "UPDATE_MULTI_CAST_ACTIONBAR",
296 }
297
298 --
299 -- MultiCast Button class
300 -- Inherits implementation methods from Action button class, but circumvents the constructor
301 -- and redefines/removes some methods.
302 --
303 local buttonTypeID = "Totem"
304 local Super = ReAction.Button
305 local Action = ReAction.Button.Action
306 local MultiCast = setmetatable(
307 {
308 defaultBarConfig = {
309 type = buttonTypeID,
310 btnWidth = 36,
311 btnHeight = 36,
312 btnRows = 1,
313 btnColumns = 6,
314 spacing = 3,
315 buttons = { }
316 },
317
318 barType = L["Totem Bar"],
319 buttonTypeID = buttonTypeID
320 },
321 { __index = Action } )
322
323 ReAction.Button.MultiCast = MultiCast
324 ReAction:RegisterBarType(MultiCast)
325
326 function MultiCast:New( btnConfig, bar, idx )
327 local maxIndex = bar.nTotemSlots or 0
328 if bar.summonSlot then
329 maxIndex = maxIndex + 1
330 end
331 if bar.recallSlot then
332 maxIndex = maxIndex + 1
333 end
334
335 if not bar.hasMulticast or idx > maxIndex then
336 return false
337 end
338
339 if idx < 1 then
340 error("invalid index")
341 end
342
343 local name = format("ReAction_%s_Totem_%d",bar:GetName(),idx)
344
345 self = Super.New(self, name, btnConfig, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" )
346
347 local barFrame = bar:GetFrame()
348 local f = self:GetFrame()
349
350 -- attributes
351 local page = (idx == bar.recallSlot) and 1 or bar:GetConfig().lastSummon or 1
352 if idx == bar.recallSlot or idx == bar.summonSlot then
353 f:SetAttribute("type","spell")
354 local spells = (idx == bar.summonSlot) and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS
355 f:SetAttribute("spell",spells[page])
356 for i, spell in ipairs(spells) do
357 if spell and IsSpellKnown(spell) then
358 f:SetAttribute("spell-page"..i, spell)
359 end
360 end
361 else
362 local offset = bar.summonSlot and 1 or 0
363 local slot = SHAMAN_TOTEM_PRIORITIES[idx - offset]
364 local baseAction = barFrame:GetAttribute("baseActionID") + slot
365 self.totemSlot = slot
366 f:SetAttribute("type","action")
367 f:SetAttribute("action", baseAction + (page - 1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE)
368 for i = 1, NUM_MULTI_CAST_PAGES do
369 f:SetAttribute("action-page"..i, baseAction + (i-1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE)
370 end
371 if not f.overlayTex then
372 local tx = f:CreateTexture("OVERLAY")
373 tx:SetTexture(TOTEM_TEXTURE)
374 tx:SetTexCoord(unpack(SLOT_OVERLAY_TCOORDS[self.totemSlot]))
375 tx:SetWidth(34)
376 tx:SetHeight(34)
377 tx:SetPoint("CENTER")
378 tx:Show()
379 f.overlayTex = tx
380 end
381 end
382 f:SetAttribute("bar-idx",idx)
383
384 -- non secure scripts
385 f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end)
386 f:SetScript("OnEnter", function(frame) self:OnEnter() end)
387 f:SetScript("OnLeave", function(frame) self:OnLeave() end)
388 f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end)
389 f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end)
390
391 -- secure handlers
392 if idx ~= bar.recallSlot then
393 f:SetAttribute("_childupdate",_childupdate)
394 end
395 barFrame:WrapScript(f, "OnEnter", _onEnter)
396
397 -- event registration
398 f:EnableMouse(true)
399 f:RegisterForClicks("AnyUp")
400 for _, evt in pairs(eventList) do
401 f:RegisterEvent(evt)
402 end
403
404 -- Set up a proxy for the icon texture for use with ButtonFacade
405 local SetTexCoordRaw = self.frames.icon.SetTexCoord
406 self.frames.icon.SetTexCoord = function( tx, ... )
407 if self:GetIconTexture() == TOTEM_TEXTURE then
408 SetTexCoordRaw(tx,select(2,self:GetIconTexture()))
409 else
410 SetTexCoordRaw(tx,...)
411 end
412 end
413
414 -- attach to skinner
415 bar:SkinButton(self)
416
417 f:Show()
418
419 -- open arrow and flyout background textures
420 if idx ~= bar.recallSlot then
421 local arrow = f._arrowFrame or CreateFrame("Button", nil, f, "SecureFrameTemplate")
422 f._arrowFrame = arrow
423 arrow:SetWidth(28)
424 arrow:SetHeight(18)
425 arrow:SetPoint("BOTTOM",self:GetFrame(),"TOP",0,0) -- TODO: better anchoring
426 arrow:SetNormalTexture(TOTEM_TEXTURE)
427 local slot = self.totemSlot or "summon"
428 arrow:GetNormalTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_TCOORDS[slot]) )
429 arrow:SetHighlightTexture(TOTEM_TEXTURE)
430 arrow:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_HL_TCOORDS) )
431 arrow:SetAttribute("bar-idx",idx)
432 arrow:Hide()
433 barFrame:WrapScript(arrow, "OnClick", _arrow_openFlyout)
434 barFrame:SetFrameRef("arrow-"..idx,arrow)
435 end
436
437 self:Refresh()
438
439 return self
440 end
441
442 function MultiCast:Destroy()
443 local barFrame = self.bar:GetFrame()
444 local f = self:GetFrame()
445 pcall( barFrame.UnwrapScript, barFrame, f, "OnEnter" ) -- ignore errors
446 if f._arrowFrame then
447 pcall( barFrame.UnwrapScript, barFrame, f._arrowFrame,"OnClick" ) -- ignore errors
448 end
449 Super.Destroy(self)
450 end
451
452 function MultiCast:Refresh()
453 Super.Refresh(self)
454 self:UpdateAction()
455 end
456
457 function MultiCast:ShowGrid( show )
458 end
459
460 function MultiCast:ShowGridTemp( show )
461 end
462
463 function MultiCast:AcquireActionID()
464 end
465
466 function MultiCast:ReleaseActionID()
467 end
468
469 function MultiCast:UpdateShowGrid()
470 end
471
472 function MultiCast:UpdateBorder()
473 end
474
475 function MultiCast:UpdateMacroText()
476 end
477
478 function MultiCast:UpdateCount()
479 end
480
481 function MultiCast:UpdateCheckedState()
482 local action = self:GetActionID()
483 if action and IsCurrentAction(action) then
484 self:GetFrame():SetChecked(1)
485 else
486 self:GetFrame():SetChecked(0)
487 end
488 end
489
490 function MultiCast:RefreshHasActionAttributes()
491 end
492
493 function MultiCast:UpdateFlash()
494 end
495
496 function MultiCast:GetIconTexture()
497 local tx
498 if self.spellID then
499 tx = GetSpellTexture(GetSpellInfo(self.spellID))
500 elseif self.actionID then
501 tx = GetActionTexture(self.actionID)
502 end
503 if tx then
504 return tx
505 else
506 return TOTEM_TEXTURE, unpack(SLOT_EMPTY_TCOORDS[self.totemSlot or 1])
507 end
508 end
509
510 function MultiCast:UpdateAction()
511 local action = self:GetActionID()
512 if action then
513 if action ~= self.actionID then
514 self.actionID = action
515 self:UpdateAll()
516 end
517 else
518 local spellID = self:GetSpellID()
519 if spellID ~= self.spellID then
520 self.spellID = spellID
521 self:UpdateAll()
522 end
523 end
524 end
525
526 function MultiCast:GetActionID(page)
527 return self:GetFrame():GetAttribute("action")
528 end
529
530 function MultiCast:GetSpellID(page)
531 return self:GetFrame():GetAttribute("spell")
532 end
533
534 function MultiCast:SetActionID( id )
535 error("Can not set action ID of multicast buttons")
536 end
537
538 function MultiCast:SetTooltip()
539 local barFrame = self:GetFrame()
540 if GetCVar("UberTooltips") == "1" then
541 GameTooltip_SetDefaultAnchor(GameTooltip, barFrame)
542 else
543 GameTooltip:SetOwner(barFrame)
544 end
545 if self.spellID then
546 GameTooltip:SetSpellByID(self.spellID,false,true)
547 elseif self.actionID then
548 GameTooltip:SetAction(self.actionID)
549 end
550 end
551
552 function MultiCast:GetUsable()
553 if self.spellID then
554 return IsUsableSpell((GetSpellInfo(self.spellID)))
555 elseif self.actionID then
556 return IsUsableAction(self.actionID)
557 end
558 end
559
560 function MultiCast:GetInRange()
561 if self.spellID then
562 return IsSpellInRange((GetSpellInfo(self.spellID))) == 0
563 elseif self.actionID then
564 return IsActionInRange(self.actionID) == 0
565 end
566 end
567
568 function MultiCast:GetCooldown()
569 if self.spellID then
570 return GetSpellCooldown((GetSpellInfo(self.spellID)))
571 elseif self.actionID then
572 return GetActionCooldown(self.actionID)
573 else
574 return 0, 0, 0
575 end
576 end
577
578 function MultiCast:UPDATE_MULTI_CAST_ACTIONBAR()
579 self:UpdateAll()
580 end
581
582
583 --
584 -- flyout setup
585 --
586 local function ShowFlyoutTooltip(frame)
587 if GetCVar("UberTooltips") == "1" then
588 GameTooltip_SetDefaultAnchor(GameTooltip, frame)
589 else
590 GameTooltip:SetOwner(frame)
591 end
592 local spell = frame:GetAttribute("spell")
593 if spell == nil or spell == 0 then
594 GameTooltip:SetText(MULTI_CAST_TOOLTIP_NO_TOTEM, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
595 else
596 GameTooltip:SetSpellByID(spell,false,true)
597 end
598 end
599
600 local function HideFlyoutTooltip()
601 GameTooltip:Hide()
602 end
603
604 local function UpdateFlyoutIcon(frame)
605 local spellID = frame:GetAttribute("spell")
606 if spellID == 0 or spellID == nil then
607 frame.icon:SetTexture(TOTEM_TEXTURE)
608 local slot = tonumber(frame:GetAttribute("totemSlot")) or 1
609 frame.icon:SetTexCoord( unpack(SLOT_EMPTY_TCOORDS[slot]) )
610 else
611 frame.icon:SetTexture(GetSpellTexture(GetSpellInfo(spellID)))
612 frame.icon:SetTexCoord(0,1,0,1)
613 end
614 end
615
616 function MultiCast:SetupBar( bar )
617 Super.SetupBar(self,bar)
618
619 local slot = 0
620 local nTotemSlots = 0
621 local summonSlot = nil
622 local recallSlot = nil
623
624 -- figure out the capabilities of the character
625 for i, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do
626 if spell and IsSpellKnown(spell) then
627 slot = 1
628 summonSlot = 1
629 end
630 end
631
632 for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do
633 local totem = SHAMAN_TOTEM_PRIORITIES[i];
634 if GetTotemInfo(totem) and GetMultiCastTotemSpells(totem) then
635 nTotemSlots = nTotemSlots + 1
636 slot = slot + 1
637 end
638 end
639
640 slot = slot + 1
641 for i, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do
642 if spell and IsSpellKnown(spell) then
643 recallSlot = slot
644 end
645 end
646
647 if nTotemSlots == 0 then
648 bar.hasMulticast = false -- no multicast capability
649 return
650 end
651
652 bar.hasMulticast = true
653 bar.summonSlot = summonSlot
654 bar.recallSlot = recallSlot
655 bar.nTotemSlots = nTotemSlots
656
657
658 local f = bar:GetFrame()
659
660 -- init bar secure environment
661 f:SetAttribute("lastSummon", bar:GetConfig().lastSummon)
662 f:SetAttribute("summonSlot", summonSlot)
663 f:SetAttribute("recallSlot", recallSlot)
664 f:SetAttribute("slotsPerPage", NUM_MULTI_CAST_BUTTONS_PER_PAGE)
665 f:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS)
666 for i, p in ipairs(SHAMAN_TOTEM_PRIORITIES) do
667 f:SetAttribute("TOTEM_PRIORITY_"..i,p)
668 end
669 f:SetAttribute("_onstate-multispellpage", _onstate_multispellpage)
670
671 function f:UpdateLastSummon(value)
672 bar:GetConfig().lastSummon = value
673 end
674
675 -- create flyout container frame and close arrow
676 local flyout = bar._flyoutFrame
677 if not flyout then
678 flyout = CreateFrame("Frame", nil, f, "SecureFrameTemplate")
679 bar._flyoutFrame = flyout
680 f:SetFrameRef("flyout",flyout)
681 flyout.buttons = { }
682 flyout:Hide()
683 flyout:SetWidth(24)
684 flyout:SetHeight(1)
685 flyout:SetPoint("BOTTOM",f,"TOP",0,0)
686
687 local close = CreateFrame("Button", nil, flyout, "SecureFrameTemplate")
688 close:SetWidth(28)
689 close:SetHeight(18)
690 close:SetPoint("BOTTOM",flyout,"TOP")
691 close:SetNormalTexture(TOTEM_TEXTURE)
692 close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS["summon"]))
693 close:SetHighlightTexture(TOTEM_TEXTURE)
694 close:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_HL_TCOORDS) )
695 f:SetFrameRef("close",close)
696 f:WrapScript(close, "OnClick", _closeFlyout)
697 close:Show()
698
699 local midTx = flyout:CreateTexture("BACKGROUND")
700 midTx:SetWidth(32)
701 midTx:SetHeight(20)
702 midTx:SetPoint("BOTTOM")
703 midTx:SetTexture(TOTEM_TEXTURE)
704 midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS["summon"]))
705 midTx:Show()
706
707 local topTx = flyout:CreateTexture("BACKGROUND")
708 topTx:SetWidth(32)
709 topTx:SetHeight(20)
710 topTx:SetTexture(TOTEM_TEXTURE)
711 midTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS["summon"]))
712 topTx:SetPoint("BOTTOM",midTx,"TOP",0,-10)
713 topTx:Show()
714
715 function flyout:UpdateTextures(slot)
716 slot = slot or "summon"
717 close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS[slot]))
718 midTx:ClearAllPoints()
719 midTx:SetPoint("BOTTOM")
720 midTx:SetPoint("TOP",close,"BOTTOM",0,0)
721 midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS[slot]))
722 topTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS[slot]))
723 end
724
725 -- create flyout buttons
726 for i = 1, 10 do -- maximum 9 spells + 1 empty slot
727 local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate")
728 b:SetWidth(24)
729 b:SetHeight(24)
730 local prev = flyout.buttons[i-1]
731 b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring
732 b.icon = b:CreateTexture("BACKGROUND")
733 b.icon:SetAllPoints()
734 b.icon:Show()
735 b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square")
736 b:GetHighlightTexture():SetBlendMode("ADD")
737 b:RegisterForClicks("AnyUp")
738 b:SetScript("OnShow",UpdateFlyoutIcon)
739 b:SetScript("OnEnter",ShowFlyoutTooltip)
740 b:SetScript("OnLeave",HideFlyoutTooltip)
741 b:SetAttribute("index",i)
742 f:SetAttribute("flyout-child-idx",i)
743 f:SetFrameRef("flyout-child",b)
744 f:Execute([[
745 flyoutChildren = flyoutChildren or newtable()
746 flyoutChildren[self:GetAttribute("flyout-child-idx")] = self:GetFrameRef("flyout-child")
747 ]])
748 f:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick)
749 b:Show()
750 flyout.buttons[i] = b
751 end
752 end
753
754 -- scale flyout frame
755 local scale = bar:GetButtonSize() / 36
756 flyout:SetScale(scale)
757
758 function f:UpdateFlyoutTextures(slot)
759 flyout:UpdateTextures(slot)
760 end
761
762 -- re-execute setup when new spells are loaded
763 if not f.events_registered then
764 f:RegisterEvent("UPDATE_MULTI_CAST_ACTIONBAR")
765 f:RegisterEvent("PLAYER_ENTERING_WORLD")
766 -- Bar.frame does not use OnEvent
767 f:SetScript("OnEvent",
768 function()
769 if not InCombatLockdown() then
770 self:SetupBar(bar)
771 end
772 end)
773 f.events_registered = true
774 end
775
776
777 f:Execute(_bar_init)
778 end
779