ca2b78e217
/fui keeps edit mode, reset and list. The thirty diagnostic and tuning functions behind the other subcommands (bar, bags, character, tabs, close, model, minimap, mouse, map, questlog, chat, chatlines, tabard...) go with them, with Panes.Report, the bags read-back and the 205 texts they alone used. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
968 lines
36 KiB
Lua
968 lines
36 KiB
Lua
-- Bottom of the screen: the micro menu and the bags bar, rebuilt from camelot.
|
|
-- Sources: camelot/MainMenuBarMicroMenu.xml, MicroMenuContainerOverrides.lua (order),
|
|
-- MainMenuBarBagButtons.xml, shared/BagsBar.lua, EditModePresetLayoutConstants.lua (positions).
|
|
-- The client's PvP and Help micro buttons are removed; the strip is as wide as its buttons.
|
|
|
|
-- camelot/MainMenuBarMicroMenu.xml: 32 x 40 buttons, childXPadding = -5 (5 px overlap)
|
|
local MICRO_W, MICRO_H = 32, 40
|
|
local MICRO_PADDING = -5
|
|
local MICRO_PITCH = MICRO_W + MICRO_PADDING
|
|
|
|
-- Displayed button height: the 40 px strip plus its 8 px frame on each side (56),
|
|
-- minus the 5 px bronze edge on each side, leaves a 46 px opening.
|
|
-- Camelot's 41 px art is stretched to fill it.
|
|
local MICRO_ART_H = 46
|
|
|
|
-- camelot/MainMenuBarBagButtons.xml: bags 45 x 45, bagPadding = 2, bag frame 46 x 46,
|
|
-- keyring 33 wide
|
|
local BAG_SIZE = 45
|
|
local BAG_PADDING = 2
|
|
local BAG_FRAME_W, BAG_FRAME_H = 46, 46
|
|
local KEYRING_W = 33
|
|
|
|
|
|
-- camelot/EditModePresetLayoutConstants.lua: micro menu at BOTTOM (116.5, 6); action bar
|
|
-- BOTTOMRIGHT on its BOTTOMLEFT (-4.5, -4); bags BOTTOMLEFT on its BOTTOMRIGHT (7, -4)
|
|
local MICRO_X, MICRO_Y = 116.5, 6
|
|
local BAR_OFFSET_X, BAR_OFFSET_Y = -4.5, -4
|
|
local BAGS_OFFSET_X, BAGS_OFFSET_Y = 7, -4
|
|
|
|
-- In a vehicle the buttons go in two rows (camelot OverrideMicroMenuPosition: stacked, the
|
|
-- strip's padding between rows too) at camelot's 0.85 scale, smaller if needed to fit the room
|
|
-- the vehicle bar keeps for them. That room, per skin, from VehicleMenuBar's BOTTOMRIGHT: between
|
|
-- the borders around it (VehicleMenuBar.lua SkinsData), above the bar's bottom, under its top border.
|
|
local VEHICLE_MICRO_SCALE = 0.85
|
|
local VEHICLE_MICRO_AREA = {
|
|
Mechanical = { left = -335, right = -219, bottom = 3, top = 77 },
|
|
Natural = { left = -363, right = -237, bottom = 3, top = 77 },
|
|
}
|
|
|
|
-- Backslash built with string.char, so the path needs no escaped separators.
|
|
local SEP = string.char(92)
|
|
local BAG_ICON = "Interface" .. SEP .. "ForeverUI" .. SEP .. "icons" .. SEP .. "ui-hud-actionbar-bag"
|
|
local PERFORMANCE_IMAGE = "Interface" .. SEP .. "ForeverUI" .. SEP .. "mainmenubar" .. SEP .. "ui-mainmenubar-performancebar"
|
|
local L = ForeverUI.L
|
|
|
|
-- Camelot's order, limited to the buttons this client has.
|
|
-- The professions button (camelot ProfessionMicroButton) does not exist in 3.3.5: it is
|
|
-- created here and opens ProfessionsBook.lua.
|
|
-- create: role of a created button, which picks its tooltip and click (MICRO_ROLES).
|
|
-- Other addons add their own buttons with ForeverUI.AddMicroButton (below).
|
|
local MICRO = {
|
|
{ name = "CharacterMicroButton", portrait = true },
|
|
{ name = "ForeverUIProfessionMicroButton", atlasSet = "professions", create = "professions" },
|
|
{ name = "SpellbookMicroButton", atlasSet = "spellbookabilities" },
|
|
{ name = "TalentMicroButton", atlasSet = "spectalents" },
|
|
{ name = "AchievementMicroButton", atlasSet = "achievements" },
|
|
{ name = "QuestLogMicroButton", atlasSet = "questlog" },
|
|
{ name = "SocialsMicroButton", atlasSet = "guildcommunities" },
|
|
{ name = "LFDMicroButton", atlasSet = "groupfinder" },
|
|
{ name = "MainMenuMicroButton", atlasSet = "gamemenu" },
|
|
}
|
|
|
|
local MICRO_STATES = {
|
|
up = "GetNormalTexture",
|
|
down = "GetPushedTexture",
|
|
disabled = "GetDisabledTexture",
|
|
mouseover = "GetHighlightTexture",
|
|
}
|
|
|
|
-- Camelot shows the c60 set. The logical atlas name resolves to the base variant (the c60
|
|
-- sheet sorts after it), so the c60 name is tried first, with the base one as fallback.
|
|
-- The disabled state is -disable on the c60 sheet and -disabled on the base sheet.
|
|
local function microAtlas(atlasSet, state)
|
|
local candidates = {
|
|
"ui-hud-micromenu-" .. atlasSet .. "-" .. state .. "-c60-2x",
|
|
"ui-hud-micromenu-" .. atlasSet .. "-" .. state .. "-2x",
|
|
}
|
|
if state == "disabled" then
|
|
table.insert(candidates, 1, "ui-hud-micromenu-" .. atlasSet .. "-disable-c60-2x")
|
|
end
|
|
for _, name in ipairs(candidates) do
|
|
if ForeverUI.AtlasEntry(name) then
|
|
return name
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- --------------------------------------------------------- Micro menu
|
|
local micro = CreateFrame("Frame", "ForeverUIMicroMenu", UIParent)
|
|
micro:SetHeight(MICRO_H)
|
|
|
|
local blockBackground = micro:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(blockBackground, "ui-hud-actionbar-iconframe-background", true)
|
|
|
|
local blockFrame = CreateFrame("Frame", nil, micro)
|
|
blockFrame:SetPoint("TOPLEFT", -8, 8)
|
|
blockFrame:SetPoint("BOTTOMRIGHT", 8, -8)
|
|
blockFrame:SetFrameLevel(micro:GetFrameLevel())
|
|
ForeverUI.SetBarFrameArt(blockFrame, "BORDER")
|
|
|
|
-- The background overflows the frame. The source declares it after the frame but in a
|
|
-- lower sublevel, so it draws below; 3.3.5 has no sublevels, so it uses a lower layer.
|
|
blockBackground:SetPoint("TOPLEFT", blockFrame, "TOPLEFT", -13, 0)
|
|
blockBackground:SetPoint("BOTTOMRIGHT", blockFrame, "BOTTOMRIGHT", 14, 4)
|
|
|
|
local microButtons = {}
|
|
|
|
-- Shows the normal or pushed art of a micro button entry, from its button state.
|
|
local function applyMicroState(entry)
|
|
local button = entry.button
|
|
local pressed = button:GetButtonState() == "PUSHED"
|
|
|
|
if pressed then
|
|
entry.background:Hide()
|
|
entry.pressedBackground:Show()
|
|
else
|
|
entry.background:Show()
|
|
entry.pressedBackground:Hide()
|
|
end
|
|
|
|
if entry.pressedShadow then
|
|
if pressed then
|
|
entry.pressedShadow:Show()
|
|
else
|
|
entry.pressedShadow:Hide()
|
|
end
|
|
end
|
|
|
|
|
|
-- tabard emblem: CENTER (0, 2), pushed (1, 1)
|
|
if entry.emblem then
|
|
for _, t in ipairs({ entry.emblem, entry.emblemHover }) do
|
|
t:ClearAllPoints()
|
|
t:SetPoint("CENTER", button, "CENTER", pressed and 1 or 0, pressed and 1 or 2)
|
|
end
|
|
end
|
|
|
|
-- CharacterMicroButton_SetPushed changes the portrait crop; camelot keeps the same crop
|
|
-- in both states.
|
|
if entry.portrait and MicroButtonPortrait then
|
|
MicroButtonPortrait:SetTexCoord(0.2, 0.8, 0.0666, 0.9)
|
|
end
|
|
|
|
local highlight = button:GetHighlightTexture()
|
|
if highlight and entry.atlasSet then
|
|
if pressed then
|
|
if ForeverUI.SetAtlas(highlight, microAtlas(entry.atlasSet, "down"), true) then
|
|
highlight:SetBlendMode("ADD")
|
|
highlight:SetAlpha(0.5)
|
|
end
|
|
else
|
|
if ForeverUI.SetAtlas(highlight, microAtlas(entry.atlasSet, "mouseover"), true) then
|
|
highlight:SetBlendMode("BLEND")
|
|
highlight:SetAlpha(1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Latency bar of the game menu button. Camelot anchors it at (0, -2); here the strip
|
|
-- frame's inner edge covers the button bottom, so it is anchored at (0, 0).
|
|
-- The bar's line is rows 58-59 of the 64-row image. The height gives each row 1.5 screen
|
|
-- pixels, so the line is 3 pixels thick; the width stays camelot's 19.
|
|
-- Pixels come from gxResolution and the button's effective scale.
|
|
local LATENCY = { rows = 64, pixelsPerLine = 1.5, width = 19 }
|
|
local function pixelsPerUnit(frame)
|
|
local h = tonumber(string.match(GetCVar("gxResolution") or "", "%d+x(%d+)")) or 768
|
|
return h / 768 * frame:GetEffectiveScale()
|
|
end
|
|
|
|
local function placeLatency(button)
|
|
MainMenuBarPerformanceBar:SetWidth(LATENCY.width)
|
|
MainMenuBarPerformanceBar:SetHeight(LATENCY.rows * LATENCY.pixelsPerLine / pixelsPerUnit(button))
|
|
MainMenuBarPerformanceBar:ClearAllPoints()
|
|
MainMenuBarPerformanceBar:SetPoint("BOTTOM", button, "BOTTOM", 0, 0)
|
|
end
|
|
|
|
-- Roles of created micro buttons: tooltip and click.
|
|
-- ProfessionMicroButtonMixin (mainline/mainmenubarmicrobuttons.lua): tooltip
|
|
-- MicroButtonTooltipText(PROFESSIONS_BUTTON, TOGGLEPROFESSIONBOOK); PROFESSIONS_BUTTON is
|
|
-- TRADE_SKILLS in 3.3.5, which has no such key binding.
|
|
-- Tooltip and textures are set like the client's micro buttons (MainMenuBarMicroButton
|
|
-- OnEnter, GameTooltip_AddNewbieTip), then skinMicro re-skins them.
|
|
-- A button added by another addon carries its own tooltip (text or function) and click.
|
|
local MICRO_ROLES = {
|
|
professions = {
|
|
tooltip = function() return MicroButtonTooltipText(TRADE_SKILLS, "TOGGLEPROFESSIONBOOK") end,
|
|
onClick = function()
|
|
if ForeverUI.ProfessionsBook then ForeverUI.ProfessionsBook.Toggle() end
|
|
end,
|
|
},
|
|
}
|
|
|
|
local function createMicro(definition)
|
|
local role = MICRO_ROLES[definition.create] or definition
|
|
local tooltip = role.tooltip
|
|
if type(tooltip) ~= "function" then
|
|
local text = tooltip
|
|
tooltip = function() return text end
|
|
end
|
|
local parent = (CharacterMicroButton and CharacterMicroButton:GetParent()) or micro
|
|
local button = CreateFrame("Button", definition.name, parent)
|
|
for state, method in pairs(MICRO_STATES) do
|
|
local e = ForeverUI.AtlasEntry(microAtlas(definition.atlasSet, state))
|
|
if e then
|
|
button[string.gsub(method, "^Get", "Set")](button, e[1])
|
|
end
|
|
end
|
|
button:RegisterForClicks("AnyUp")
|
|
button.tooltipText = tooltip()
|
|
button:SetScript("OnEnter", function(self)
|
|
self.tooltipText = tooltip()
|
|
GameTooltip_AddNewbieTip(self, self.tooltipText, 1.0, 1.0, 1.0, self.newbieText)
|
|
end)
|
|
button:SetScript("OnLeave", function()
|
|
GameTooltip:Hide()
|
|
end)
|
|
button:SetScript("OnClick", role.onClick)
|
|
return button
|
|
end
|
|
|
|
-- Skins a micro button with camelot art and places it in the strip; creates it if needed.
|
|
-- index: 1-based slot in the strip
|
|
local function skinMicro(definition, index)
|
|
if definition.create and not _G[definition.name] then
|
|
createMicro(definition)
|
|
end
|
|
local button = _G[definition.name]
|
|
if not button then
|
|
return nil
|
|
end
|
|
|
|
button:SetWidth(MICRO_W)
|
|
button:SetHeight(MICRO_ART_H)
|
|
-- 3.3.5 makes the top 18 px ignore the mouse (decor of the old button art); undo that.
|
|
button:SetHitRectInsets(0, 0, 0, 0)
|
|
|
|
local entry = { button = button, atlasSet = definition.atlasSet, portrait = definition.portrait, created = definition.create }
|
|
|
|
entry.background = button:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(entry.background, "ui-hud-micromenu-buttonbg-up-c60-2x", true)
|
|
entry.background:SetAllPoints(button)
|
|
|
|
entry.pressedBackground = button:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(entry.pressedBackground, "ui-hud-micromenu-buttonbg-down-c60-2x", true)
|
|
entry.pressedBackground:SetAllPoints(button)
|
|
entry.pressedBackground:Hide()
|
|
|
|
if definition.atlasSet then
|
|
for state, method in pairs(MICRO_STATES) do
|
|
local texture = button[method] and button[method](button)
|
|
if texture then
|
|
ForeverUI.SetAtlas(texture, microAtlas(definition.atlasSet, state), true)
|
|
texture:ClearAllPoints()
|
|
texture:SetAllPoints(button)
|
|
end
|
|
end
|
|
else
|
|
-- The portrait button has no icon set: hide the client's textures and let camelot's
|
|
-- background do all the drawing.
|
|
for _, method in ipairs({ "GetNormalTexture", "GetPushedTexture", "GetDisabledTexture" }) do
|
|
local texture = button[method] and button[method](button)
|
|
if texture then
|
|
texture:SetAlpha(0)
|
|
end
|
|
end
|
|
local highlight = button:GetHighlightTexture()
|
|
if highlight then
|
|
ForeverUI.SetAtlas(highlight, "ui-hud-micromenu-buttonbg-down-c60-2x", true)
|
|
highlight:ClearAllPoints()
|
|
highlight:SetAllPoints(button)
|
|
highlight:SetBlendMode("ADD")
|
|
highlight:SetAlpha(0.4)
|
|
end
|
|
end
|
|
|
|
if definition.portrait then
|
|
local shadow = button:CreateTexture(nil, "BORDER")
|
|
ForeverUI.SetAtlas(shadow, "ui-hud-micromenu-portrait-shadow-2x", true)
|
|
shadow:SetAllPoints(button)
|
|
entry.shadow = shadow
|
|
|
|
if MicroButtonPortrait then
|
|
-- The source insets the portrait 7 px on a 32 x 40 button, i.e. 18 x 26. Keep that
|
|
-- size: following the button height would stretch the face.
|
|
MicroButtonPortrait:SetDrawLayer("ARTWORK")
|
|
MicroButtonPortrait:ClearAllPoints()
|
|
MicroButtonPortrait:SetWidth(MICRO_W - 14)
|
|
MicroButtonPortrait:SetHeight(MICRO_H - 14)
|
|
MicroButtonPortrait:SetPoint("CENTER", button, "CENTER", 0, 0)
|
|
MicroButtonPortrait:SetTexCoord(0.2, 0.8, 0.0666, 0.9)
|
|
end
|
|
|
|
local pressedShadow = button:CreateTexture(nil, "OVERLAY")
|
|
ForeverUI.SetAtlas(pressedShadow, "ui-hud-micromenu-portrait-down-2x", true)
|
|
pressedShadow:SetWidth(MICRO_W)
|
|
pressedShadow:SetHeight(MICRO_ART_H)
|
|
pressedShadow:SetPoint("CENTER", button, "CENTER", 1, -4)
|
|
pressedShadow:Hide()
|
|
entry.pressedShadow = pressedShadow
|
|
end
|
|
|
|
if definition.name == "MainMenuMicroButton" and MainMenuBarPerformanceBar then
|
|
-- camelot image (32 x 64, a line at the bottom); the 3.3.5 one is a 16 x 8 block
|
|
-- that stretches into a big green square
|
|
MainMenuBarPerformanceBar:SetTexture(PERFORMANCE_IMAGE)
|
|
placeLatency(button)
|
|
-- the button's scale changes in a vehicle
|
|
entry.placeLatency = function() placeLatency(button) end
|
|
-- the pixel size changes with the screen or the scale
|
|
local latencyWatcher = CreateFrame("Frame")
|
|
latencyWatcher:RegisterEvent("DISPLAY_SIZE_CHANGED")
|
|
latencyWatcher:RegisterEvent("UI_SCALE_CHANGED")
|
|
latencyWatcher:RegisterEvent("PLAYER_ENTERING_WORLD")
|
|
latencyWatcher:SetScript("OnEvent", function() placeLatency(button) end)
|
|
-- 3.3.5 re-anchors the bar on every push and release (MainMenuMicroButton_SetPushed /
|
|
-- _SetNormal: SetPoint TOPLEFT (9, -36) / (10, -34), without ClearAllPoints). That
|
|
-- anchor adds to ours and squeezes the image to 12 x 12, so ours is re-applied after
|
|
-- them; camelot does not move the bar when the button is pushed.
|
|
for _, name in ipairs({ "MainMenuMicroButton_SetPushed", "MainMenuMicroButton_SetNormal" }) do
|
|
if _G[name] then
|
|
hooksecurefunc(name, function() placeLatency(button) end)
|
|
end
|
|
end
|
|
end
|
|
|
|
button:ClearAllPoints()
|
|
button:SetPoint("LEFT", micro, "LEFT", (index - 1) * MICRO_PITCH, 0)
|
|
|
|
return entry
|
|
end
|
|
|
|
local microCount = 0
|
|
for index, definition in ipairs(MICRO) do
|
|
local entry = skinMicro(definition, index)
|
|
if entry then
|
|
table.insert(microButtons, entry)
|
|
microCount = index
|
|
end
|
|
end
|
|
local BUTTONS_WIDTH = microCount * MICRO_W + (microCount - 1) * MICRO_PADDING
|
|
|
|
-- Guild tabard on the Social button (camelot: GuildMicroButtonMixin:UpdateTabard).
|
|
-- With a tabard: the GuildCommunities-GuildColor set tinted with the tabard background,
|
|
-- and the 12 x 14 emblem at CENTER (0, 2), (1, 1) pushed, in OVERLAY and HIGHLIGHT, cut
|
|
-- from GuildEmblems_01 (SetSmallGuildTabardTextures: 18/256 cells, 14 per row, inset
|
|
-- 1/256) and tinted with the emblem color. Without a tabard: the plain GuildCommunities set.
|
|
-- 3.3.5 lacks C_GuildInfo.GetGuildTabardInfo; GetGuildTabardFileNames only returns texture
|
|
-- names (Background_<bg>_TU_U, Emblem_<motif>_<color>_TU_U). The motif is the cell index on
|
|
-- the sheet; colors come from TabardColors.lua (tools/tabard_colors.py).
|
|
local EMBLEM_SHEET = "Interface" .. SEP .. "ForeverUI" .. SEP .. "guildframe" .. SEP .. "guildemblems_01"
|
|
local EMBLEM_CELL, EMBLEM_COLUMNS, EMBLEM_EDGE = 18 / 256, 14, 1 / 256
|
|
|
|
-- Returns background color, emblem cell index and emblem color; nil without a readable
|
|
-- tabard.
|
|
local function guildTabard()
|
|
if not (GetGuildTabardFileNames and IsInGuild and IsInGuild()) then return nil end
|
|
local background, _, emblem = GetGuildTabardFileNames()
|
|
if not background or not emblem then return nil end
|
|
local f = tonumber(string.match(string.lower(background), "background_(%d+)"))
|
|
local motif, color = string.match(string.lower(emblem), "emblem_(%d+)_(%d+)")
|
|
local colors = ForeverUI.TabardColors
|
|
if not (f and motif and colors) then return nil end
|
|
local cf, ce = colors.background[f], colors.emblem[tonumber(color)]
|
|
if not (cf and ce) then return nil end
|
|
return cf, tonumber(motif), ce
|
|
end
|
|
|
|
local social
|
|
for _, entry in ipairs(microButtons) do
|
|
if entry.button:GetName() == "SocialsMicroButton" then social = entry end
|
|
end
|
|
|
|
if social then
|
|
social.baseSet = social.atlasSet
|
|
social.emblem = social.button:CreateTexture(nil, "OVERLAY")
|
|
social.emblemHover = social.button:CreateTexture(nil, "HIGHLIGHT")
|
|
for _, t in ipairs({ social.emblem, social.emblemHover }) do
|
|
t:SetTexture(EMBLEM_SHEET)
|
|
t:SetWidth(12)
|
|
t:SetHeight(14)
|
|
t:SetPoint("CENTER", social.button, "CENTER", 0, 2)
|
|
t:Hide()
|
|
end
|
|
end
|
|
|
|
-- After an emblem change, GetGuildTabardFileNames returns nothing at GUILDTABARD_UPDATE
|
|
-- until the new guild data arrives, and no event follows that arrival. So in a guild an
|
|
-- unreadable tabard keeps the current look, and the tabard is re-read every
|
|
-- RECHECK_INTERVAL s, RECHECK_COUNT times after each event; the last read decides
|
|
-- (no tabard: base set).
|
|
local RECHECK_INTERVAL, RECHECK_COUNT = 0.5, 20
|
|
local rechecker = CreateFrame("Frame")
|
|
rechecker:Hide()
|
|
ForeverUI.TabardReread = rechecker
|
|
|
|
-- Applies the tabard look (values from guildTabard); nil background restores the base set.
|
|
local function applyTabard(background, motif, color)
|
|
social.atlasSet = background and (social.baseSet .. "-guildcolor") or social.baseSet
|
|
for state, method in pairs(MICRO_STATES) do
|
|
local texture = social.button[method] and social.button[method](social.button)
|
|
if texture and ForeverUI.SetAtlas(texture, microAtlas(social.atlasSet, state), true) then
|
|
if background then
|
|
texture:SetVertexColor(background[1], background[2], background[3])
|
|
else
|
|
texture:SetVertexColor(1, 1, 1)
|
|
end
|
|
end
|
|
end
|
|
if background then
|
|
local x = (motif % EMBLEM_COLUMNS) * EMBLEM_CELL
|
|
local y = math.floor(motif / EMBLEM_COLUMNS) * EMBLEM_CELL
|
|
for _, t in ipairs({ social.emblem, social.emblemHover }) do
|
|
t:SetTexCoord(x + EMBLEM_EDGE, x + EMBLEM_CELL - EMBLEM_EDGE, y + EMBLEM_EDGE, y + EMBLEM_CELL - EMBLEM_EDGE)
|
|
t:SetVertexColor(color[1], color[2], color[3])
|
|
t:Show()
|
|
end
|
|
else
|
|
social.emblem:Hide()
|
|
social.emblemHover:Hide()
|
|
end
|
|
applyMicroState(social)
|
|
end
|
|
|
|
-- GuildMicroButtonMixin:UpdateTabard; isFinal: last re-read, applies even without a tabard
|
|
function ForeverUI.UpdateSocialTabard(isFinal)
|
|
if not social then return end
|
|
local background, motif, color = guildTabard()
|
|
local inGuild = IsInGuild and IsInGuild()
|
|
if background or isFinal or not inGuild then
|
|
applyTabard(background, motif, color)
|
|
end
|
|
end
|
|
|
|
rechecker:SetScript("OnUpdate", function(self, elapsed)
|
|
self.pending = (self.pending or 0) + elapsed
|
|
if self.pending < RECHECK_INTERVAL then return end
|
|
self.pending = 0
|
|
self.remaining = (self.remaining or 0) - 1
|
|
local finish = self.remaining <= 0
|
|
ForeverUI.UpdateSocialTabard(finish)
|
|
if finish then self:Hide() end
|
|
end)
|
|
|
|
local tabardWatcher = CreateFrame("Frame")
|
|
for _, ev in ipairs({ "PLAYER_ENTERING_WORLD", "PLAYER_GUILD_UPDATE", "GUILDTABARD_UPDATE" }) do
|
|
tabardWatcher:RegisterEvent(ev)
|
|
end
|
|
tabardWatcher:SetScript("OnEvent", function()
|
|
ForeverUI.UpdateSocialTabard()
|
|
rechecker.remaining, rechecker.pending = RECHECK_COUNT, 0
|
|
rechecker:Show()
|
|
end)
|
|
micro:SetWidth(BUTTONS_WIDTH)
|
|
|
|
-- Remove the client's PvP button (PvP opens from the character sheet). Hiding is not
|
|
-- enough: UpdateMicroButtons and VehicleMenuBar_MoveMicroButtons show it again.
|
|
ForeverUI.Suppress(_G["PVPMicroButton"])
|
|
|
|
-- Remove the client's Help button; Help moves to the Esc menu (camelot: GAMEMENU_SUPPORT),
|
|
-- between AddOns and Log Out with a gap on each side. AddOns is ACP (patch-5.mpq), under
|
|
-- Macros; it re-anchors Log Out under itself on every show, so we re-place after its
|
|
-- OnShow. Without ACP, Help goes under Macros. The gap is the one the client menu leaves
|
|
-- before Return to Game (16, GameMenuFrame.xml). A plain button: a secure one would make
|
|
-- the whole Esc menu protected.
|
|
local MENU_GAP = 16
|
|
ForeverUI.Suppress(_G["HelpMicroButton"])
|
|
if GameMenuFrame and GameMenuButtonMacros and GameMenuButtonLogout then
|
|
local help = CreateFrame("Button", "ForeverUIGameMenuButtonHelp", GameMenuFrame, "GameMenuButtonTemplate")
|
|
help:SetText(HELP_BUTTON)
|
|
help:SetScript("OnClick", function()
|
|
PlaySound("igMainMenuOption")
|
|
HideUIPanel(GameMenuFrame)
|
|
ToggleHelpFrame()
|
|
end)
|
|
local function place()
|
|
help:ClearAllPoints()
|
|
help:SetPoint("TOP", _G["GameMenuButtonAddOns"] or GameMenuButtonMacros, "BOTTOM", 0, -MENU_GAP)
|
|
GameMenuButtonLogout:SetPoint("TOP", help, "BOTTOM", 0, -MENU_GAP)
|
|
end
|
|
-- ACP loads before us (alphabetical order); if it loads later, hook it as soon as it
|
|
-- arrives, before the menu is first opened
|
|
local hooked = false
|
|
local function attachACP()
|
|
local acp = _G["GameMenuButtonAddOns"]
|
|
if acp and not hooked and acp.HookScript then
|
|
hooked = true
|
|
acp:HookScript("OnShow", place)
|
|
end
|
|
place()
|
|
end
|
|
attachACP()
|
|
local acpWatcher = CreateFrame("Frame")
|
|
acpWatcher:RegisterEvent("ADDON_LOADED")
|
|
acpWatcher:SetScript("OnEvent", function(self)
|
|
attachACP()
|
|
if hooked then self:UnregisterEvent("ADDON_LOADED") end
|
|
end)
|
|
-- The client places Log Out 1 below its neighbour; add Help and two gaps
|
|
GameMenuFrame:SetHeight(GameMenuFrame:GetHeight() + help:GetHeight() + 2 * MENU_GAP - 1)
|
|
end
|
|
|
|
-- Skin of the vehicle bar holding the buttons, nil outside a vehicle
|
|
-- (VehicleMenuBar_MoveMicroButtons).
|
|
local vehicleSkin
|
|
|
|
-- Buttons run from the strip's LEFT edge (camelot: layoutFramesGoingRight).
|
|
-- Re-applied after VehicleMenuBar_MoveMicroButtons, which re-anchors CharacterMicroButton
|
|
-- and SocialsMicroButton on every vehicle enter or exit (MainMenuBar_ToPlayerArt /
|
|
-- _ToVehicleArt). In a vehicle the client moves its buttons to VehicleMenuBarArtFrame and
|
|
-- created buttons follow; there they fill the vehicle bar's room in two rows, the first
|
|
-- holding the extra button of an odd count, at the scale that fits however many there are.
|
|
-- Each button is scaled on its own, its anchor offsets divided by its scale. The right
|
|
-- button (in a vehicle, the lower row too) draws on top.
|
|
local function layoutMicro()
|
|
local area = vehicleSkin and VEHICLE_MICRO_AREA[vehicleSkin]
|
|
local scale, height, columns, left, top = 1, MICRO_ART_H
|
|
if area then
|
|
columns = math.ceil(#microButtons / 2)
|
|
local rows = #microButtons > 1 and 2 or 1
|
|
local blockWidth = columns * MICRO_PITCH - MICRO_PADDING
|
|
local blockHeight = rows * (MICRO_H + MICRO_PADDING) - MICRO_PADDING
|
|
local width, roomHeight = area.right - area.left, area.top - area.bottom
|
|
scale = math.min(VEHICLE_MICRO_SCALE, width / blockWidth, roomHeight / blockHeight)
|
|
height = MICRO_H
|
|
left = area.left + (width - blockWidth * scale) / 2
|
|
top = area.top - (roomHeight - blockHeight * scale) / 2
|
|
end
|
|
for index, entry in ipairs(microButtons) do
|
|
local button = entry.button
|
|
if entry.created and CharacterMicroButton then
|
|
local parent = CharacterMicroButton:GetParent()
|
|
if button:GetParent() ~= parent then
|
|
button:SetParent(parent)
|
|
end
|
|
end
|
|
button:SetFrameLevel(button:GetParent():GetFrameLevel() + index)
|
|
button:SetScale(scale)
|
|
button:SetHeight(height)
|
|
if entry.pressedShadow then
|
|
entry.pressedShadow:SetHeight(height)
|
|
end
|
|
button:ClearAllPoints()
|
|
if area then
|
|
local row = index > columns and 1 or 0
|
|
local x = left + (index - 1 - row * columns) * MICRO_PITCH * scale
|
|
local y = top - row * (MICRO_H + MICRO_PADDING) * scale
|
|
button:SetPoint("TOPLEFT", VehicleMenuBar, "BOTTOMRIGHT", x / scale, y / scale)
|
|
else
|
|
button:SetPoint("LEFT", micro, "LEFT", (index - 1) * MICRO_PITCH, 0)
|
|
end
|
|
if entry.placeLatency then
|
|
entry.placeLatency()
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Pushes a created button while its window is open.
|
|
-- role: create role, or the button name for one added by another addon
|
|
function ForeverUI.UpdateMicro(role, isOpen)
|
|
for _, entry in ipairs(microButtons) do
|
|
if entry.created == role then
|
|
if isOpen then
|
|
entry.button:SetButtonState("PUSHED", 1)
|
|
else
|
|
entry.button:SetButtonState("NORMAL")
|
|
end
|
|
applyMicroState(entry)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- professions button: pushed while the book or the crafting page is open
|
|
-- (ProfessionsBook.lua)
|
|
function ForeverUI.UpdateProfessionsMicro(isOpen)
|
|
ForeverUI.UpdateMicro("professions", isOpen)
|
|
end
|
|
|
|
layoutMicro()
|
|
|
|
if hooksecurefunc and type(_G["VehicleMenuBar_MoveMicroButtons"]) == "function" then
|
|
-- skinName: the vehicle bar's skin, nil when the player's bar comes back
|
|
hooksecurefunc("VehicleMenuBar_MoveMicroButtons", function(skinName)
|
|
vehicleSkin = skinName
|
|
layoutMicro()
|
|
end)
|
|
end
|
|
|
|
-- ------------------------------------------------------ Bags bar
|
|
local bags = CreateFrame("Frame", "ForeverUIBagsBar", UIParent)
|
|
bags:SetHeight(BAG_SIZE)
|
|
bags:SetWidth(5 * BAG_SIZE + KEYRING_W + 5 * BAG_PADDING)
|
|
|
|
local bagsFrame = CreateFrame("Frame", nil, bags)
|
|
bagsFrame:SetPoint("TOPLEFT", -6, 6)
|
|
bagsFrame:SetPoint("BOTTOMRIGHT", 5, -5)
|
|
bagsFrame:SetFrameLevel(bags:GetFrameLevel())
|
|
ForeverUI.SetBarFrameArt(bagsFrame)
|
|
|
|
-- Skins a bag slot with a camelot frame.
|
|
-- frameAtlas: frame art; frameWidth: art width (KEYRING_W for the keyring)
|
|
local function skinBag(button, frameAtlas, frameWidth)
|
|
if not button then
|
|
return false
|
|
end
|
|
|
|
button:SetWidth(frameWidth == KEYRING_W and KEYRING_W or BAG_SIZE)
|
|
button:SetHeight(BAG_SIZE)
|
|
|
|
local normalFont = button:GetNormalTexture()
|
|
if normalFont then
|
|
ForeverUI.SetAtlas(normalFont, frameAtlas, true)
|
|
normalFont:SetWidth(frameWidth)
|
|
normalFont:SetHeight(BAG_FRAME_H)
|
|
normalFont:ClearAllPoints()
|
|
normalFont:SetPoint("TOPLEFT", button, "TOPLEFT")
|
|
end
|
|
|
|
local pushed = button:GetPushedTexture()
|
|
if pushed then
|
|
ForeverUI.SetAtlas(pushed, frameAtlas, true)
|
|
pushed:SetWidth(frameWidth)
|
|
pushed:SetHeight(BAG_FRAME_H)
|
|
pushed:ClearAllPoints()
|
|
pushed:SetPoint("TOPLEFT", button, "TOPLEFT")
|
|
end
|
|
|
|
local highlight = button:GetHighlightTexture()
|
|
if highlight then
|
|
ForeverUI.SetAtlas(highlight, frameAtlas, true)
|
|
highlight:ClearAllPoints()
|
|
highlight:SetAllPoints(button)
|
|
highlight:SetBlendMode("ADD")
|
|
highlight:SetAlpha(0.4)
|
|
end
|
|
|
|
-- The 3.3.5 green check does not fit this frame: use the same art in ADD, as on the
|
|
-- action buttons.
|
|
local checked = button.GetCheckedTexture and button:GetCheckedTexture()
|
|
if checked then
|
|
ForeverUI.SetAtlas(checked, frameAtlas, true)
|
|
checked:SetWidth(frameWidth)
|
|
checked:SetHeight(BAG_FRAME_H)
|
|
checked:ClearAllPoints()
|
|
checked:SetPoint("TOPLEFT", button, "TOPLEFT")
|
|
checked:SetBlendMode("ADD")
|
|
end
|
|
|
|
local icon = _G[button:GetName() .. "IconTexture"]
|
|
if icon then
|
|
icon:ClearAllPoints()
|
|
icon:SetAllPoints(button)
|
|
icon:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-- Keyring: a narrower button with its own frame and image.
|
|
local keyringIcon
|
|
local function skinKeyring()
|
|
local button = KeyRingButton
|
|
if not button then
|
|
return
|
|
end
|
|
|
|
skinBag(button, "ui-hud-actionbar-iconframe-small", KEYRING_W)
|
|
|
|
if not keyringIcon then
|
|
keyringIcon = button:CreateTexture(nil, "BORDER")
|
|
keyringIcon:SetPoint("CENTER")
|
|
end
|
|
|
|
-- Deliberate difference: KeyRingMixin:OnBagUpdate shows the keyring image only when the
|
|
-- showKeyring CVar is on (set by a tutorial on the first key looted). In 3.3.5 the
|
|
-- keyring is a permanent part of the bar, so the image is always set.
|
|
-- The -2x variant (54 x 80 texels for 27 x 40 units) stays sharp; the 1x one looks
|
|
-- pixelated because a unit is more than one screen pixel.
|
|
ForeverUI.SetAtlas(keyringIcon, "ui-hud-actionbar-keyring-small-c60-2x")
|
|
|
|
-- camelot always keeps this button in the bar; 3.3.5 hides it until the player loots
|
|
-- a key.
|
|
button:Show()
|
|
end
|
|
|
|
-- Keyring fly-in (BaseBagSlotButtonTemplate, mainline/mainmenubarbagbuttontemplates.xml):
|
|
-- AnimIcon, OVERLAY, whole button; FlyIn: 1 s, scale 0.125 to 1, alpha 0 to 1, SMOOTH
|
|
-- path through (-15, 30) and (-75, 60). KeyRingMixin plays it reversed (FlyIn:Play(true)).
|
|
-- 3.3.5 has neither reverse play nor a start scale, so it runs frame by frame. It
|
|
-- replaces the 3.3.5 3D animation (KeyRingButtonItemAnim, ForcedBackpackItem.mdx).
|
|
local FLIGHT = { duration = 1, scale = 0.125, points = { { 0, 0 }, { -15, 30 }, { -75, 60 } } }
|
|
|
|
-- Smooth path: Catmull-Rom through the points, end points doubled.
|
|
-- q: progress from 0 to 1; returns x, y
|
|
local function curve(q)
|
|
local p = FLIGHT.points
|
|
local n = #p - 1
|
|
local s = math.min(n - 1e-9, math.max(0, q * n))
|
|
local i = math.floor(s) + 1
|
|
local u = s - (i - 1)
|
|
local a, b, c, d = p[math.max(1, i - 1)], p[i], p[i + 1], p[math.min(#p, i + 2)]
|
|
local function axis(k)
|
|
return 0.5 * (2 * b[k] + (c[k] - a[k]) * u + (2 * a[k] - 5 * b[k] + 4 * c[k] - d[k]) * u * u
|
|
+ (3 * b[k] - a[k] - 3 * c[k] + d[k]) * u * u * u)
|
|
end
|
|
return axis(1), axis(2)
|
|
end
|
|
|
|
local flight = CreateFrame("Frame")
|
|
flight:Hide()
|
|
flight:SetScript("OnUpdate", function(self, elapsed)
|
|
self.t = self.t + (elapsed or 0)
|
|
local icon = self.icon
|
|
if self.t >= FLIGHT.duration then
|
|
icon:Hide()
|
|
self:Hide()
|
|
return
|
|
end
|
|
-- reversed: progress goes from 1 to 0
|
|
local q = 1 - self.t / FLIGHT.duration
|
|
local x, y = curve(q)
|
|
local k = FLIGHT.scale + (1 - FLIGHT.scale) * q
|
|
local b = KeyRingButton
|
|
icon:ClearAllPoints()
|
|
icon:SetPoint("CENTER", b, "CENTER", x, y)
|
|
icon:SetWidth(b:GetWidth() * k)
|
|
icon:SetHeight(b:GetHeight() * k)
|
|
icon:SetAlpha(q)
|
|
icon:Show()
|
|
end)
|
|
flight:RegisterEvent("ITEM_PUSH")
|
|
flight:SetScript("OnEvent", function(self, _, bag, texture)
|
|
local b = KeyRingButton
|
|
if not b or bag ~= b:GetID() then
|
|
return
|
|
end
|
|
if not self.icon then
|
|
self.icon = b:CreateTexture(nil, "OVERLAY")
|
|
self.icon:Hide()
|
|
end
|
|
self.icon:SetTexture(texture)
|
|
self.t = 0
|
|
self:Show()
|
|
end)
|
|
ForeverUI.KeyRingFly = flight
|
|
if KeyRingButtonItemAnim then
|
|
KeyRingButtonItemAnim:UnregisterEvent("ITEM_PUSH")
|
|
KeyRingButtonItemAnim:Hide()
|
|
end
|
|
|
|
local BAG_ORDER = {
|
|
"MainMenuBarBackpackButton",
|
|
"CharacterBag0Slot", "CharacterBag1Slot", "CharacterBag2Slot", "CharacterBag3Slot",
|
|
}
|
|
|
|
local cells = {}
|
|
local separators = {}
|
|
|
|
local function layoutBags()
|
|
local previous = nil
|
|
cells = {}
|
|
|
|
for _, name in ipairs(BAG_ORDER) do
|
|
local button = _G[name]
|
|
if button then
|
|
skinBag(button, "ui-hud-actionbar-iconframe-bags", BAG_FRAME_W)
|
|
button:ClearAllPoints()
|
|
if previous then
|
|
button:SetPoint("RIGHT", previous, "LEFT", -BAG_PADDING, 0)
|
|
else
|
|
button:SetPoint("RIGHT", bags, "RIGHT", 0, 0)
|
|
end
|
|
previous = button
|
|
table.insert(cells, button)
|
|
end
|
|
end
|
|
|
|
skinKeyring()
|
|
if KeyRingButton and previous then
|
|
KeyRingButton:ClearAllPoints()
|
|
KeyRingButton:SetPoint("RIGHT", previous, "LEFT", -BAG_PADDING, 0)
|
|
table.insert(cells, KeyRingButton)
|
|
end
|
|
|
|
-- Divider between neighbouring cells: LEFT on the left cell's RIGHT, offset -5, so it
|
|
-- is centred on the 2 px gap.
|
|
for index = 2, #cells do
|
|
if not separators[index] then
|
|
local divider = ForeverUI.CreateDivider(bags, bags:GetFrameLevel() + 2)
|
|
divider:SetPoint("TOP", cells[index], "TOP", 0, 0)
|
|
divider:SetPoint("BOTTOM", cells[index], "BOTTOM", 0, 0)
|
|
divider:SetPoint("LEFT", cells[index], "RIGHT", -5, 0)
|
|
separators[index] = divider
|
|
end
|
|
end
|
|
end
|
|
|
|
-- The backpack uses camelot's icon, a separate file, not an atlas entry
|
|
-- (camelot/MainMenuBarBagButtons.xml: bagIcon).
|
|
local function setBackpackIcon()
|
|
local icon = MainMenuBarBackpackButtonIconTexture
|
|
if icon then
|
|
icon:SetTexture(BAG_ICON)
|
|
icon:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
end
|
|
|
|
-- ------------------------------------------------------------ Assembly
|
|
-- The row is a chain: the action bar and the bags sit on each side of the micro menu.
|
|
-- The chain is turned into screen positions so each element stays movable on its own.
|
|
local function applyDefaultPositions()
|
|
local half = micro:GetWidth() / 2
|
|
ForeverUI.Layout.SetDefaults("actionbar", "BOTTOMRIGHT", "BOTTOM",
|
|
MICRO_X - half + BAR_OFFSET_X, MICRO_Y + BAR_OFFSET_Y)
|
|
ForeverUI.Layout.SetDefaults("bags", "BOTTOMLEFT", "BOTTOM",
|
|
MICRO_X + half + BAGS_OFFSET_X, MICRO_Y + BAGS_OFFSET_Y)
|
|
end
|
|
|
|
-- Measures the row for what sits on top of it (status bars).
|
|
-- Like the default positions, x counts from the screen centre and y from the bottom.
|
|
local function measureRow()
|
|
local half = micro:GetWidth() / 2
|
|
local bar = ForeverUI.ActionBarHolder
|
|
local barRight = MICRO_X - half + BAR_OFFSET_X
|
|
local barLeft = barRight - (bar and bar:GetWidth() or 0)
|
|
local bagsLeft = MICRO_X + half + BAGS_OFFSET_X
|
|
|
|
-- Row top is the highest of the three frames: the action bar and bags frames overflow
|
|
-- by 6, the micro menu frame by 8.
|
|
local barTop = MICRO_Y + BAR_OFFSET_Y + BAG_SIZE + 6
|
|
local microTop = MICRO_Y + MICRO_H + 8
|
|
local bagsTop = MICRO_Y + BAGS_OFFSET_Y + BAG_SIZE + 6
|
|
|
|
ForeverUI.BottomRow = {
|
|
-- From the action bar's left edge to the bags bar's right edge. The end caps
|
|
-- overflow 30 px on each side, but what sits above aligns on the blocks, not the
|
|
-- griffins.
|
|
left = barLeft,
|
|
right = bagsLeft + bags:GetWidth(),
|
|
top = math.max(barTop, microTop, bagsTop),
|
|
}
|
|
end
|
|
|
|
-- Default positions of the end caps (gryphons), which frame the whole row: the left one on
|
|
-- the action bar's left edge, the right one on the bags bar's right edge, each overlapping its
|
|
-- bar and dropped like it (ActionBar.lua). A gryphon the player has moved stays where it is.
|
|
local function placeEndCaps()
|
|
local endCaps = ForeverUI.ActionBarEndCaps
|
|
local bar = ForeverUI.ActionBarHolder
|
|
if not endCaps or not bar then
|
|
return
|
|
end
|
|
local half = micro:GetWidth() / 2
|
|
local barLeft = MICRO_X - half + BAR_OFFSET_X - bar:GetWidth()
|
|
local bagsRight = MICRO_X + half + BAGS_OFFSET_X + bags:GetWidth()
|
|
ForeverUI.Layout.SetDefaults("leftgryphon", "BOTTOMRIGHT", "BOTTOM",
|
|
barLeft + endCaps.inset, MICRO_Y + BAR_OFFSET_Y + endCaps.drop)
|
|
ForeverUI.Layout.SetDefaults("rightgryphon", "BOTTOMLEFT", "BOTTOM",
|
|
bagsRight - endCaps.inset, MICRO_Y + BAGS_OFFSET_Y + endCaps.drop)
|
|
end
|
|
|
|
local function layoutAll()
|
|
layoutBags()
|
|
setBackpackIcon()
|
|
layoutMicro()
|
|
for _, entry in ipairs(microButtons) do
|
|
applyMicroState(entry)
|
|
end
|
|
end
|
|
|
|
ForeverUI.Layout.Register(micro, "micromenu", L.BOTTOMBAR_EDIT_LABEL_MICROMENU, "BOTTOM", "BOTTOM", MICRO_X, MICRO_Y)
|
|
ForeverUI.Layout.Register(bags, "bags", L.BOTTOMBAR_EDIT_LABEL_BAGS, "BOTTOMLEFT", "BOTTOM",
|
|
MICRO_X + micro:GetWidth() / 2 + BAGS_OFFSET_X, MICRO_Y + BAGS_OFFSET_Y)
|
|
applyDefaultPositions()
|
|
measureRow()
|
|
placeEndCaps()
|
|
layoutAll()
|
|
|
|
-- Micro button added by another addon, without ForeverUI knowing that addon.
|
|
-- ForeverUI.AddMicroButton(def), def fields:
|
|
-- name global button name; a button already in the strip is returned, never doubled
|
|
-- atlasSet camelot icon set (ui-hud-micromenu-<set>-<state>, c60 first)
|
|
-- after name of the button it follows; default: just before the game menu
|
|
-- tooltip text, or a function returning it (read on hover)
|
|
-- onClick click handler
|
|
-- ready optional, called with the button once placed
|
|
-- Returns the button, or nil while waiting for combat to end (the action bar holds
|
|
-- secure buttons and cannot move in combat). The strip widens and the action bar and bags
|
|
-- move apart (default positions only; a player-chosen position stays); end caps and
|
|
-- status bars follow. ForeverUI.UpdateMicro(name, isOpen) pushes or releases it.
|
|
local pendingMicro = CreateFrame("Frame")
|
|
local queued = {}
|
|
|
|
local function namedButton(name)
|
|
for _, entry in ipairs(microButtons) do
|
|
if entry.button:GetName() == name then return entry.button end
|
|
end
|
|
end
|
|
|
|
function ForeverUI.AddMicroButton(def)
|
|
if type(def) ~= "table" or type(def.name) ~= "string" then return nil end
|
|
local already = namedButton(def.name)
|
|
if already then return already end
|
|
if InCombatLockdown() then
|
|
queued[def.name] = def
|
|
pendingMicro:RegisterEvent("PLAYER_REGEN_ENABLED")
|
|
return nil
|
|
end
|
|
local definition = { name = def.name, atlasSet = def.atlasSet, create = def.name, tooltip = def.tooltip, onClick = def.onClick }
|
|
local rank, menu
|
|
for index, entry in ipairs(microButtons) do
|
|
local name = entry.button:GetName()
|
|
if def.after and name == def.after then rank = index + 1 end
|
|
if name == "MainMenuMicroButton" then menu = index end
|
|
end
|
|
rank = rank or menu or (#microButtons + 1)
|
|
local entry = skinMicro(definition, rank)
|
|
if not entry then return nil end
|
|
table.insert(microButtons, rank, entry)
|
|
local n = #microButtons
|
|
micro:SetWidth(n * MICRO_W + (n - 1) * MICRO_PADDING)
|
|
applyDefaultPositions()
|
|
measureRow()
|
|
placeEndCaps()
|
|
layoutAll()
|
|
if ForeverUI.StatusBarsLayout then ForeverUI.StatusBarsLayout() end
|
|
if type(def.ready) == "function" then def.ready(entry.button) end
|
|
return entry.button
|
|
end
|
|
|
|
pendingMicro:SetScript("OnEvent", function(self)
|
|
self:UnregisterEvent("PLAYER_REGEN_ENABLED")
|
|
local list = queued
|
|
queued = {}
|
|
for _, def in pairs(list) do ForeverUI.AddMicroButton(def) end
|
|
end)
|
|
|
|
if hooksecurefunc then
|
|
hooksecurefunc("UpdateMicroButtons", function()
|
|
for _, entry in ipairs(microButtons) do
|
|
applyMicroState(entry)
|
|
end
|
|
end)
|
|
end
|
|
|
|
local watcher = CreateFrame("Frame")
|
|
watcher:RegisterEvent("PLAYER_ENTERING_WORLD")
|
|
watcher:RegisterEvent("BAG_UPDATE")
|
|
watcher:RegisterEvent("CVAR_UPDATE")
|
|
watcher:SetScript("OnEvent", function()
|
|
layoutAll()
|
|
end)
|
|
|
|
-- Pure containers do not take the mouse. MainMenuBar is enableMouse=true and covers the
|
|
-- whole bottom of the screen, so it swallows clicks meant for the buttons. Its children
|
|
-- (action buttons, micro buttons) keep their mouse.
|
|
for _, name in ipairs({ "MainMenuBar", "MainMenuBarArtFrame" }) do
|
|
local frame = _G[name]
|
|
if frame and frame.EnableMouse then
|
|
frame:EnableMouse(false)
|
|
end
|
|
end
|
|
|
|
ForeverUI.MicroButtons = microButtons
|
|
ForeverUI.BagsCells = cells
|
|
ForeverUI.BagsDividers = separators
|