Mercurial > wow > reaction
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MultiCastButton.lua Tue Apr 12 16:06:31 2011 -0700 @@ -0,0 +1,779 @@ +local addonName, addonTable = ... +local ReAction = addonTable.ReAction +local L = ReAction.L +local _G = _G +local CreateFrame = CreateFrame +local format = string.format +local unpack = unpack +local GetCVar = GetCVar +local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor +local CooldownFrame_SetTimer = CooldownFrame_SetTimer +local InCombatLockdown = InCombatLockdown +local IsUsableSpell = IsUsableSpell +local IsUsableAction = IsUsableAction +local IsSpellKnown = IsSpellKnown +local IsSpellInRange = IsSpellInRange +local IsActionInRange = IsActionInRange +local GetSpellInfo = GetSpellInfo +local GetSpellCooldown = GetSpellCooldown +local GetActionCooldown = GetActionCooldown +local GetSpellTexture = GetSpellTexture +local GetActionTexture = GetActionTexture +local GetMultiCastTotemSpells = GetMultiCastTotemSpells + +--[[ + Blizzard Constants: + - NUM_MULTI_CAST_BUTTONS_PER_PAGE = 4 + - NUM_MULTI_CAST_PAGES = 3 + - SHAMAN_TOTEM_PRIORITIES = { } -- sets the order of the totems + - TOTEM_MULTI_CAST_SUMMON_SPELLS = { } -- list of summon spellIDs + - TOTEM_MULTI_CAST_RECALL_SPELLS = { } -- list of recall spellIDs + + Blizzard Events: + - UPDATE_MULTI_CAST_ACTIONBAR + + Blizzard APIs: + - GetMultiCastBarOffset() : returns 6 + + - SetMultiCastSpell(actionID, spellID) (protected) OR + SetAttribute("type","multispell") + SetAttribute("action",actionID) + SetAttribute("spell",spellID) + + note: multicast actionID page is NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset(), + so that's action ID 132-144. + + - spell1, spell2, spell3, ... = GetMultiCastTotemSpells(slot) + returns spellIDs for all known totems that fit that slot. This function is available in + the secure environment. + + Blizzard textures: + All the textures for the multicast bar (arrows, empty-slot icons, etc) are part of a single + texture: each texture uses SetTexCoord() to display only a slice of the textures. I suppose + this is to slightly optimize texture load performance, but it makes the UI code more clumsy. + + Each totem button and arrow has a colored border indicating its elemental type. + + TODO: + - make whether to show the colored border configurable (looks really bad with ButtonFacade:Zoomed) + - apply ButtonFacade to the flyout buttons? Or at least zoom the textures slightly? + - use a multiplier with SetTexCoord on totem bar texture? + + Design Notes: + - Only the header has a secure context. All other frames execute in its context. + + - Each button is either type "spell" (summon/recall) or type "action" (totem action IDs are + GetBonusBarOffset()=6, 132-144) with 3 pages of 4 buttons. The paging is controlled by + the summon flyout, which is also paged with the summon spells (the recall button is not paged) + + - A spell list is updated in the secure context at setup time (TODO: redo setup when learning new + spells) with the list of spells known for each slot. + + - Each button (except recall) has an arrow button which appears on mouseover and when clicked + opens the flyout via a wrapped OnClick handler. When the flyout is open, the arrow does not + appear. + + - A single flyout with N+1 (1 slot is to select no totem for the set) flyout-buttons is a child + of the bar. Each time the flyout panel is opened, the individual buttons grab their corresponding + spell/type from the list, according to the slot which opened the flyout. Each button either sets + the current page (summon) or sets a multispell to an actionID via type="multispell". None of them + actually cast any spells (though, I suppose we could modify this so that e.g. configurable + right-click casts the spell). The flyout also has a close button which closes the flyout: the + flyout-open code positions the close button anchored to the last button in the flyout (which + changes dynamically because each slot has a different number of items in the list). + + - Multicast sets are not stances, there's no need (or ability) to handle swapping sets if one of + the summon spells is cast from elsewhere. + + - The default UI has Call of the Elements always selected on UI load. This module remembers the last + selected one and restores it. + + +]]-- + + +-- +-- Secure snippets +-- + +-- bar +local _bar_init = -- function(self) +[[ + -- set up some globals in the secure environment + flyout = self:GetFrameRef("flyout") + flyoutSlot = nil + summonSlot = self:GetAttribute("summonSlot") + recallSlot = self:GetAttribute("recallSlot") + baseActionID = self:GetAttribute("baseActionID") + slotsPerPage = self:GetAttribute("slotsPerPage") + currentPage = currentPage or self:GetAttribute("lastSummon") or 1 + + totemIDsBySlot = newtable() + for i = 1, slotsPerPage do + totemIDsBySlot[i] = self:GetAttribute("TOTEM_PRIORITY_"..i) + end + + -- these are set up in bar:SetupBar() + flyoutChildren = flyoutChildren or newtable() + summonSpells = summonSpells or newtable() +]] + +local _onstate_multispellpage = -- function(self, stateid, newstate) +[[ + currentPage = tonumber(newstate) + control:CallMethod("UpdateLastSummon",currentPage) + control:ChildUpdate() +]] + + +-- buttons +local _childupdate = -- function(self, snippetid, message) +[[ + local t = self:GetAttribute("type") + self:SetAttribute(t, self:GetAttribute(t.."-page"..currentPage)) +]] + +local _onEnter = -- function(self) + -- for whatever reason, RegisterAutoHide is unreliable + -- unless you re-anchor the frame prior to calling it. + -- Even then, it's still not terribly reliable. +[[ + local slot = self:GetAttribute("bar-idx") + local arrow = owner:GetFrameRef("arrow-"..slot) + if arrow and not arrow:IsShown() and not (flyout:IsVisible() and flyoutSlot == slot) then + arrow:ClearAllPoints() + arrow:SetPoint("BOTTOM",self,"TOP",0,0) + arrow:Show() + arrow:RegisterAutoHide(0) + arrow:AddToAutoHide(self) + end +]] + + +-- flyout arrow +local _arrow_openFlyout = -- function(self) +[[ + local slot = self:GetAttribute("bar-idx") + local totemID = totemIDsBySlot[slot - (summonSlot or 0)] + if totemID == 0 then + totemID = "summon" + end + + local lastButton, lastPage + for page, b in ipairs(flyoutChildren) do + b:Hide() + b:SetAttribute("totemSlot",totemID) + if slot == summonSlot then + local spellID = self:GetParent():GetAttribute("spell-page"..page) + if spellID then + b:SetAttribute("type","changePage") + b:SetAttribute("spell",spellID) + b:Show() + lastButton = b + lastPage = page + end + else + local spell = select(page, 0, GetMultiCastTotemSpells(totemID) ) + if spell then + b:SetAttribute("type","multispell") + b:SetAttribute("action", baseActionID + (currentPage - 1)*slotsPerPage + totemID) + b:SetAttribute("spell", spell) + b:Show() + lastButton = b + lastPage = page + end + end + end + + local close = owner:GetFrameRef("close") + if lastButton and close then + close:ClearAllPoints() + close:SetPoint("BOTTOM",lastButton,"TOP",0,0) -- TODO: better anchoring + close:Show() + control:CallMethod("UpdateFlyoutTextures",totemID) + end + + flyout:ClearAllPoints() + flyout:SetPoint("BOTTOM",self,"BOTTOM",0,0) -- TODO: better anchoring + if lastPage then + flyout:SetHeight(lastPage * 27 + (close and close:GetHeight() or 0)) + end + flyout:Show() + flyout:RegisterAutoHide(1) -- TODO: configurable + flyout:AddToAutoHide(owner) + flyoutSlot = slot + self:Hide() +]] + +local _closeFlyout = -- function(self) +[[ + flyout:Hide() +]] + + +-- flyout child buttons +local _flyout_child_preClick = -- function(self, button, down) +[[ + local button = button + if self:GetAttribute("type") == "changePage" then + owner:SetAttribute("state-multispellpage",self:GetAttribute("index")) + self:GetParent():Hide() + return false + else + return nil, "close" + end +]] + +local _flyout_child_postClick = -- function(self, message, button, down) +[[ + if message == "close" then + self:GetParent():Hide() -- hide flyout after selecting + end +]] + + +-- +-- The Blizzard totem bar textures are all actually one big texture, +-- with texcoord offsets. Shamelessly stolen from FrameXML/MultiCastActionBarFrame.lua +-- +local TOTEM_TEXTURE = "Interface\\Buttons\\UI-TotemBar" +local FLYOUT_UP_BUTTON_HL_TCOORDS = { 72/128, 92/128, 88/256, 98/256 } +local FLYOUT_DOWN_BUTTON_HL_TCOORDS = { 72/128, 92/128, 69/256, 79/256 } + +local SLOT_EMPTY_TCOORDS = { + [EARTH_TOTEM_SLOT] = { 66/128, 96/128, 3/256, 33/256 }, + [FIRE_TOTEM_SLOT] = { 67/128, 97/128, 100/256, 130/256 }, + [WATER_TOTEM_SLOT] = { 39/128, 69/128, 209/256, 239/256 }, + [AIR_TOTEM_SLOT] = { 66/128, 96/128, 36/256, 66/256 }, +} + +local SLOT_OVERLAY_TCOORDS = { + [EARTH_TOTEM_SLOT] = { 1/128, 35/128, 172/256, 206/256 }, + [FIRE_TOTEM_SLOT] = { 36/128, 70/128, 172/256, 206/256 }, + [WATER_TOTEM_SLOT] = { 1/128, 35/128, 207/256, 240/256 }, + [AIR_TOTEM_SLOT] = { 36/128, 70/128, 137/256, 171/256 }, +} + +local FLYOUT_UP_BUTTON_TCOORDS = { + ["summon"] = { 99/128, 127/128, 84/256, 102/256 }, + [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 160/256, 178/256 }, + [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 122/256, 140/256 }, + [WATER_TOTEM_SLOT] = { 99/128, 127/128, 199/256, 217/256 }, + [AIR_TOTEM_SLOT] = { 99/128, 127/128, 237/256, 255/256 }, +} + +local FLYOUT_DOWN_BUTTON_TCOORDS = { + ["summon"] = { 99/128, 127/128, 65/256, 83/256 }, + [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 141/256, 159/256 }, + [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 103/256, 121/256 }, + [WATER_TOTEM_SLOT] = { 99/128, 127/128, 180/256, 198/256 }, + [AIR_TOTEM_SLOT] = { 99/128, 127/128, 218/256, 236/256 }, +} + +local FLYOUT_TOP_TCOORDS = { + ["summon"] = { 33/128, 65/128, 1/256, 23/256 }, + [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 46/256, 68/256 }, + [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 46/256, 68/256 }, + [WATER_TOTEM_SLOT] = { 0/128, 32/128, 1/256, 23/256 }, + [AIR_TOTEM_SLOT] = { 0/128, 32/128, 91/256, 113/256 }, +} + +local FLYOUT_MIDDLE_TCOORDS = { + ["summon"] = { 33/128, 65/128, 23/256, 43/256 }, + [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 68/256, 88/256 }, + [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 68/256, 88/256 }, + [WATER_TOTEM_SLOT] = { 0/128, 32/128, 23/256, 43/256 }, + [AIR_TOTEM_SLOT] = { 0/128, 32/128, 113/256, 133/256 }, +} + +local eventList = { + "ACTIONBAR_SLOT_CHANGED", + "ACTIONBAR_UPDATE_STATE", + "ACTIONBAR_UPDATE_USABLE", + "ACTIONBAR_UPDATE_COOLDOWN", + "UPDATE_BINDINGS", + "UPDATE_MULTI_CAST_ACTIONBAR", +} + +-- +-- MultiCast Button class +-- Inherits implementation methods from Action button class, but circumvents the constructor +-- and redefines/removes some methods. +-- +local buttonTypeID = "Totem" +local Super = ReAction.Button +local Action = ReAction.Button.Action +local MultiCast = setmetatable( + { + defaultBarConfig = { + type = buttonTypeID, + btnWidth = 36, + btnHeight = 36, + btnRows = 1, + btnColumns = 6, + spacing = 3, + buttons = { } + }, + + barType = L["Totem Bar"], + buttonTypeID = buttonTypeID + }, + { __index = Action } ) + +ReAction.Button.MultiCast = MultiCast +ReAction:RegisterBarType(MultiCast) + +function MultiCast:New( btnConfig, bar, idx ) + local maxIndex = bar.nTotemSlots or 0 + if bar.summonSlot then + maxIndex = maxIndex + 1 + end + if bar.recallSlot then + maxIndex = maxIndex + 1 + end + + if not bar.hasMulticast or idx > maxIndex then + return false + end + + if idx < 1 then + error("invalid index") + end + + local name = format("ReAction_%s_Totem_%d",bar:GetName(),idx) + + self = Super.New(self, name, btnConfig, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) + + local barFrame = bar:GetFrame() + local f = self:GetFrame() + + -- attributes + local page = (idx == bar.recallSlot) and 1 or bar:GetConfig().lastSummon or 1 + if idx == bar.recallSlot or idx == bar.summonSlot then + f:SetAttribute("type","spell") + local spells = (idx == bar.summonSlot) and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS + f:SetAttribute("spell",spells[page]) + for i, spell in ipairs(spells) do + if spell and IsSpellKnown(spell) then + f:SetAttribute("spell-page"..i, spell) + end + end + else + local offset = bar.summonSlot and 1 or 0 + local slot = SHAMAN_TOTEM_PRIORITIES[idx - offset] + local baseAction = barFrame:GetAttribute("baseActionID") + slot + self.totemSlot = slot + f:SetAttribute("type","action") + f:SetAttribute("action", baseAction + (page - 1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) + for i = 1, NUM_MULTI_CAST_PAGES do + f:SetAttribute("action-page"..i, baseAction + (i-1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) + end + if not f.overlayTex then + local tx = f:CreateTexture("OVERLAY") + tx:SetTexture(TOTEM_TEXTURE) + tx:SetTexCoord(unpack(SLOT_OVERLAY_TCOORDS[self.totemSlot])) + tx:SetWidth(34) + tx:SetHeight(34) + tx:SetPoint("CENTER") + tx:Show() + f.overlayTex = tx + end + end + f:SetAttribute("bar-idx",idx) + + -- non secure scripts + f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) + f:SetScript("OnEnter", function(frame) self:OnEnter() end) + f:SetScript("OnLeave", function(frame) self:OnLeave() end) + f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end) + f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end) + + -- secure handlers + if idx ~= bar.recallSlot then + f:SetAttribute("_childupdate",_childupdate) + end + barFrame:WrapScript(f, "OnEnter", _onEnter) + + -- event registration + f:EnableMouse(true) + f:RegisterForClicks("AnyUp") + for _, evt in pairs(eventList) do + f:RegisterEvent(evt) + end + + -- Set up a proxy for the icon texture for use with ButtonFacade + local SetTexCoordRaw = self.frames.icon.SetTexCoord + self.frames.icon.SetTexCoord = function( tx, ... ) + if self:GetIconTexture() == TOTEM_TEXTURE then + SetTexCoordRaw(tx,select(2,self:GetIconTexture())) + else + SetTexCoordRaw(tx,...) + end + end + + -- attach to skinner + bar:SkinButton(self) + + f:Show() + + -- open arrow and flyout background textures + if idx ~= bar.recallSlot then + local arrow = f._arrowFrame or CreateFrame("Button", nil, f, "SecureFrameTemplate") + f._arrowFrame = arrow + arrow:SetWidth(28) + arrow:SetHeight(18) + arrow:SetPoint("BOTTOM",self:GetFrame(),"TOP",0,0) -- TODO: better anchoring + arrow:SetNormalTexture(TOTEM_TEXTURE) + local slot = self.totemSlot or "summon" + arrow:GetNormalTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_TCOORDS[slot]) ) + arrow:SetHighlightTexture(TOTEM_TEXTURE) + arrow:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_HL_TCOORDS) ) + arrow:SetAttribute("bar-idx",idx) + arrow:Hide() + barFrame:WrapScript(arrow, "OnClick", _arrow_openFlyout) + barFrame:SetFrameRef("arrow-"..idx,arrow) + end + + self:Refresh() + + return self +end + +function MultiCast:Destroy() + local barFrame = self.bar:GetFrame() + local f = self:GetFrame() + pcall( barFrame.UnwrapScript, barFrame, f, "OnEnter" ) -- ignore errors + if f._arrowFrame then + pcall( barFrame.UnwrapScript, barFrame, f._arrowFrame,"OnClick" ) -- ignore errors + end + Super.Destroy(self) +end + +function MultiCast:Refresh() + Super.Refresh(self) + self:UpdateAction() +end + +function MultiCast:ShowGrid( show ) +end + +function MultiCast:ShowGridTemp( show ) +end + +function MultiCast:AcquireActionID() +end + +function MultiCast:ReleaseActionID() +end + +function MultiCast:UpdateShowGrid() +end + +function MultiCast:UpdateBorder() +end + +function MultiCast:UpdateMacroText() +end + +function MultiCast:UpdateCount() +end + +function MultiCast:UpdateCheckedState() + local action = self:GetActionID() + if action and IsCurrentAction(action) then + self:GetFrame():SetChecked(1) + else + self:GetFrame():SetChecked(0) + end +end + +function MultiCast:RefreshHasActionAttributes() +end + +function MultiCast:UpdateFlash() +end + +function MultiCast:GetIconTexture() + local tx + if self.spellID then + tx = GetSpellTexture(GetSpellInfo(self.spellID)) + elseif self.actionID then + tx = GetActionTexture(self.actionID) + end + if tx then + return tx + else + return TOTEM_TEXTURE, unpack(SLOT_EMPTY_TCOORDS[self.totemSlot or 1]) + end +end + +function MultiCast:UpdateAction() + local action = self:GetActionID() + if action then + if action ~= self.actionID then + self.actionID = action + self:UpdateAll() + end + else + local spellID = self:GetSpellID() + if spellID ~= self.spellID then + self.spellID = spellID + self:UpdateAll() + end + end +end + +function MultiCast:GetActionID(page) + return self:GetFrame():GetAttribute("action") +end + +function MultiCast:GetSpellID(page) + return self:GetFrame():GetAttribute("spell") +end + +function MultiCast:SetActionID( id ) + error("Can not set action ID of multicast buttons") +end + +function MultiCast:SetTooltip() + local barFrame = self:GetFrame() + if GetCVar("UberTooltips") == "1" then + GameTooltip_SetDefaultAnchor(GameTooltip, barFrame) + else + GameTooltip:SetOwner(barFrame) + end + if self.spellID then + GameTooltip:SetSpellByID(self.spellID,false,true) + elseif self.actionID then + GameTooltip:SetAction(self.actionID) + end +end + +function MultiCast:GetUsable() + if self.spellID then + return IsUsableSpell((GetSpellInfo(self.spellID))) + elseif self.actionID then + return IsUsableAction(self.actionID) + end +end + +function MultiCast:GetInRange() + if self.spellID then + return IsSpellInRange((GetSpellInfo(self.spellID))) == 0 + elseif self.actionID then + return IsActionInRange(self.actionID) == 0 + end +end + +function MultiCast:GetCooldown() + if self.spellID then + return GetSpellCooldown((GetSpellInfo(self.spellID))) + elseif self.actionID then + return GetActionCooldown(self.actionID) + else + return 0, 0, 0 + end +end + +function MultiCast:UPDATE_MULTI_CAST_ACTIONBAR() + self:UpdateAll() +end + + +-- +-- flyout setup +-- +local function ShowFlyoutTooltip(frame) + if GetCVar("UberTooltips") == "1" then + GameTooltip_SetDefaultAnchor(GameTooltip, frame) + else + GameTooltip:SetOwner(frame) + end + local spell = frame:GetAttribute("spell") + if spell == nil or spell == 0 then + GameTooltip:SetText(MULTI_CAST_TOOLTIP_NO_TOTEM, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b) + else + GameTooltip:SetSpellByID(spell,false,true) + end +end + +local function HideFlyoutTooltip() + GameTooltip:Hide() +end + +local function UpdateFlyoutIcon(frame) + local spellID = frame:GetAttribute("spell") + if spellID == 0 or spellID == nil then + frame.icon:SetTexture(TOTEM_TEXTURE) + local slot = tonumber(frame:GetAttribute("totemSlot")) or 1 + frame.icon:SetTexCoord( unpack(SLOT_EMPTY_TCOORDS[slot]) ) + else + frame.icon:SetTexture(GetSpellTexture(GetSpellInfo(spellID))) + frame.icon:SetTexCoord(0,1,0,1) + end +end + +function MultiCast:SetupBar( bar ) + Super.SetupBar(self,bar) + + local slot = 0 + local nTotemSlots = 0 + local summonSlot = nil + local recallSlot = nil + + -- figure out the capabilities of the character + for i, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do + if spell and IsSpellKnown(spell) then + slot = 1 + summonSlot = 1 + end + end + + for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do + local totem = SHAMAN_TOTEM_PRIORITIES[i]; + if GetTotemInfo(totem) and GetMultiCastTotemSpells(totem) then + nTotemSlots = nTotemSlots + 1 + slot = slot + 1 + end + end + + slot = slot + 1 + for i, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do + if spell and IsSpellKnown(spell) then + recallSlot = slot + end + end + + if nTotemSlots == 0 then + bar.hasMulticast = false -- no multicast capability + return + end + + bar.hasMulticast = true + bar.summonSlot = summonSlot + bar.recallSlot = recallSlot + bar.nTotemSlots = nTotemSlots + + + local f = bar:GetFrame() + + -- init bar secure environment + f:SetAttribute("lastSummon", bar:GetConfig().lastSummon) + f:SetAttribute("summonSlot", summonSlot) + f:SetAttribute("recallSlot", recallSlot) + f:SetAttribute("slotsPerPage", NUM_MULTI_CAST_BUTTONS_PER_PAGE) + f:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS) + for i, p in ipairs(SHAMAN_TOTEM_PRIORITIES) do + f:SetAttribute("TOTEM_PRIORITY_"..i,p) + end + f:SetAttribute("_onstate-multispellpage", _onstate_multispellpage) + + function f:UpdateLastSummon(value) + bar:GetConfig().lastSummon = value + end + + -- create flyout container frame and close arrow + local flyout = bar._flyoutFrame + if not flyout then + flyout = CreateFrame("Frame", nil, f, "SecureFrameTemplate") + bar._flyoutFrame = flyout + f:SetFrameRef("flyout",flyout) + flyout.buttons = { } + flyout:Hide() + flyout:SetWidth(24) + flyout:SetHeight(1) + flyout:SetPoint("BOTTOM",f,"TOP",0,0) + + local close = CreateFrame("Button", nil, flyout, "SecureFrameTemplate") + close:SetWidth(28) + close:SetHeight(18) + close:SetPoint("BOTTOM",flyout,"TOP") + close:SetNormalTexture(TOTEM_TEXTURE) + close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS["summon"])) + close:SetHighlightTexture(TOTEM_TEXTURE) + close:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_HL_TCOORDS) ) + f:SetFrameRef("close",close) + f:WrapScript(close, "OnClick", _closeFlyout) + close:Show() + + local midTx = flyout:CreateTexture("BACKGROUND") + midTx:SetWidth(32) + midTx:SetHeight(20) + midTx:SetPoint("BOTTOM") + midTx:SetTexture(TOTEM_TEXTURE) + midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS["summon"])) + midTx:Show() + + local topTx = flyout:CreateTexture("BACKGROUND") + topTx:SetWidth(32) + topTx:SetHeight(20) + topTx:SetTexture(TOTEM_TEXTURE) + midTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS["summon"])) + topTx:SetPoint("BOTTOM",midTx,"TOP",0,-10) + topTx:Show() + + function flyout:UpdateTextures(slot) + slot = slot or "summon" + close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS[slot])) + midTx:ClearAllPoints() + midTx:SetPoint("BOTTOM") + midTx:SetPoint("TOP",close,"BOTTOM",0,0) + midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS[slot])) + topTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS[slot])) + end + + -- create flyout buttons + for i = 1, 10 do -- maximum 9 spells + 1 empty slot + local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate") + b:SetWidth(24) + b:SetHeight(24) + local prev = flyout.buttons[i-1] + b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring + b.icon = b:CreateTexture("BACKGROUND") + b.icon:SetAllPoints() + b.icon:Show() + b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") + b:GetHighlightTexture():SetBlendMode("ADD") + b:RegisterForClicks("AnyUp") + b:SetScript("OnShow",UpdateFlyoutIcon) + b:SetScript("OnEnter",ShowFlyoutTooltip) + b:SetScript("OnLeave",HideFlyoutTooltip) + b:SetAttribute("index",i) + f:SetAttribute("flyout-child-idx",i) + f:SetFrameRef("flyout-child",b) + f:Execute([[ + flyoutChildren = flyoutChildren or newtable() + flyoutChildren[self:GetAttribute("flyout-child-idx")] = self:GetFrameRef("flyout-child") + ]]) + f:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick) + b:Show() + flyout.buttons[i] = b + end + end + + -- scale flyout frame + local scale = bar:GetButtonSize() / 36 + flyout:SetScale(scale) + + function f:UpdateFlyoutTextures(slot) + flyout:UpdateTextures(slot) + end + + -- re-execute setup when new spells are loaded + if not f.events_registered then + f:RegisterEvent("UPDATE_MULTI_CAST_ACTIONBAR") + f:RegisterEvent("PLAYER_ENTERING_WORLD") + -- Bar.frame does not use OnEvent + f:SetScript("OnEvent", + function() + if not InCombatLockdown() then + self:SetupBar(bar) + end + end) + f.events_registered = true + end + + + f:Execute(_bar_init) +end +