annotate MultiCastButton.lua @ 291:54d7865baae9 stable

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