Files
Adrestie 4df044493c ForeverUI : rangée des barres de posture après un rechargement en véhicule
Après un rechargement ou un écran de chargement dans un véhicule, le
client rebascule sur la barre du véhicule sans événement de montée et
cache les barres 2 et 3 avant que Vehicle.lua l'apprenne : la hauteur
sous les barres de posture, de familier et de totems se figeait à zéro.
L'état du client (MainMenuBar.state) la fige désormais aussi.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 11:57:50 +02:00

202 lines
8.4 KiB
Lua

-- ForeverUI: the four extra action bars of 3.3.5 placed as camelot's Action Bars 2 to 5
-- (mainline EditModePresetLayouts, shared EditModeManager): twelve buttons of 45, 2 apart, no
-- slot background. Bars 2 and 3 (bottom left / right) stand side by side above the experience
-- and reputation bars, 22 and 606 right of the main bar's left edge; the stance and pet bars
-- then rise by one row. Bars 4 and 5 (right, right 2) stand upright against the right edge,
-- RIGHT (-5, -77), the fifth 5 left of the fourth, shrunk if needed to fit between the right
-- gryphon and the minimap (UpdateRightActionBarPositions). In the LOW strata, under windows
-- and bags; kept on screen; movable and resizable in edit mode. Their buttons are secure:
-- nothing is laid out in combat.
ForeverUI = ForeverUI or {}
local BUTTON_COUNT = 12
local BUTTON_PITCH = 45 + 2 -- ActionButtonTemplate 45, IconPadding 2
local LENGTH = BUTTON_COUNT * BUTTON_PITCH - 2
local LEFT_EDGE = -587.5 -- left edge of the main bar, from the screen center
local ROW_Y = 84 -- above the reputation bar
local ROW_SPACING = 4 -- BOTTOM_ACTION_BARS_SPACER_Y
local RIGHT_X, RIGHT_Y = -5, -77 -- RIGHT_ACTION_BAR_DEFAULT_OFFSET_X / _Y
local RIGHT_GAP = 5 -- between the two right bars
local FIT_MARGIN = 10 -- GetRightActionBarTopLimit: 10 under the minimap; as much above the gryphon
local M = {}
ForeverUI.MultiBars = M
-- frame: the client bar; prefix: its buttons; label: edit mode name (client strings);
-- vertical: one column; point, x, y: default anchor on UIParent (relative point = point,
-- except the bottom row, anchored on the screen's bottom center)
M.bars = {
{ frame = MultiBarBottomLeft, prefix = "MultiBarBottomLeftButton", id = "multibarbottomleft",
label = SHOW_MULTIBAR1_TEXT, point = "BOTTOMLEFT", relativePoint = "BOTTOM", x = LEFT_EDGE + 22, y = ROW_Y },
{ frame = MultiBarBottomRight, prefix = "MultiBarBottomRightButton", id = "multibarbottomright",
label = SHOW_MULTIBAR2_TEXT, point = "BOTTOMLEFT", relativePoint = "BOTTOM", x = LEFT_EDGE + 606, y = ROW_Y },
{ frame = MultiBarRight, prefix = "MultiBarRightButton", id = "multibarright", vertical = true,
label = SHOW_MULTIBAR3_TEXT, point = "RIGHT", relativePoint = "RIGHT", x = RIGHT_X, y = RIGHT_Y },
{ frame = MultiBarLeft, prefix = "MultiBarLeftButton", id = "multibarleft", vertical = true,
label = SHOW_MULTIBAR4_TEXT, point = "RIGHT", relativePoint = "RIGHT",
x = RIGHT_X - 45 - RIGHT_GAP, y = RIGHT_Y },
}
-- Buttons in one row or one column without slot background, the bar sized to them, under
-- every window and bag (the client puts these bars in HIGH)
local function layout(bar)
local f = bar.frame
f:SetWidth(bar.vertical and 45 or LENGTH)
f:SetHeight(bar.vertical and LENGTH or 45)
f:SetFrameStrata("LOW")
f:SetFrameLevel(1)
for i = 1, BUTTON_COUNT do
local button = _G[bar.prefix .. i]
if button then
button:ClearAllPoints()
if bar.vertical then
button:SetPoint("TOP", f, "TOP", 0, -(i - 1) * BUTTON_PITCH)
else
button:SetPoint("LEFT", f, "LEFT", (i - 1) * BUTTON_PITCH, 0)
end
if button.foreverBackground then button.foreverBackground:Hide() end
if button.foreverSlot then button.foreverSlot:Hide() end
end
end
end
-- Height the bottom row adds under the stance and pet bars: a bar 2 or 3 shown where the
-- stack puts it (a bar the player moved leaves the stack, as in camelot). While a vehicle bar
-- replaces the player's bars (Vehicle.lua) the client hides bars 2 and 3: the height stays the
-- last one, so that bars shown again in combat (where nothing moves) are where they belong.
-- The client's own state counts too: a swap without a mount event (reload, loading screen in a
-- vehicle) hides those bars before Vehicle.lua hears of it.
local lastLift = 0
function M.Lift()
if ForeverUI.VehicleArt or (MainMenuBar and MainMenuBar.state == "vehicle") then
return lastLift
end
lastLift = 0
for k = 1, 2 do
local bar = M.bars[k]
if bar.frame and bar.frame:IsShown() and ForeverUI.Layout.IsDefault(bar.id) then
lastLift = 45 + ROW_SPACING
break
end
end
return lastLift
end
-- Top of a region in UIParent units
local function top(region)
local t = region:GetTop()
return t and t * region:GetEffectiveScale() / UIParent:GetEffectiveScale()
end
-- Right bars in their default place: one scale for both, at most 1, so that their height fits
-- between the right gryphon and the minimap; then moved down or up as little as needed to
-- stay between them. A bar the player moved keeps its size.
local function fitRight()
local minimap, caps = MinimapCluster, ForeverUI.ActionBarEndCaps
local gryphon = caps and caps.right
-- While a vehicle bar replaces the player's bars (Vehicle.lua), the gryphon is hidden and the
-- client hides these bars: they keep their last fit, so that shown again in combat (where
-- nothing moves) they do not cover the gryphon.
if ForeverUI.VehicleArt then
return
end
local high = UIParent:GetHeight()
if minimap and minimap:IsShown() and ForeverUI.Layout.IsDefault("minimap") and minimap:GetBottom() then
high = minimap:GetBottom() * minimap:GetEffectiveScale() / UIParent:GetEffectiveScale() - FIT_MARGIN
end
local low = 0
if gryphon and gryphon:IsShown() and top(gryphon) then
low = top(gryphon) + FIT_MARGIN
end
local scale = math.max(0.1, math.min(1, (high - low) / LENGTH))
local half = LENGTH * scale / 2
local center = UIParent:GetHeight() / 2 + RIGHT_Y
if center + half > high then center = high - half end
if center - half < low then center = low + half end
for k = 3, 4 do
local bar = M.bars[k]
if bar.frame then
local fixed = ForeverUI.Layout.IsDefault(bar.id)
ForeverUI.Layout.SetDefaults(bar.id, bar.point, bar.relativePoint, bar.x,
(center - UIParent:GetHeight() / 2) / scale)
ForeverUI.Layout.SetBaseScale(bar.id, fixed and scale or 1)
end
end
end
-- The stance, totem and pet bars follow the bottom row, the right bars their room; out of
-- combat only (secure buttons)
local pending, busy = false, false
function M.Restack()
if InCombatLockdown() then
pending = true
return
end
if busy then return end
busy, pending = true, false
if ForeverUI.StanceBar and ForeverUI.StanceBar.PlaceDefault then ForeverUI.StanceBar.PlaceDefault() end
if ForeverUI.TotemBar then ForeverUI.TotemBar.PlaceDefault() end
if ForeverUI.PetBar and ForeverUI.PetBar.PlaceDefault then ForeverUI.PetBar.PlaceDefault() end
fitRight()
busy = false
end
local laidOut = false
local function setup()
if laidOut or InCombatLockdown() then return end
laidOut = true
for _, bar in ipairs(M.bars) do
if bar.frame then
layout(bar)
ForeverUI.Layout.Register(bar.frame, bar.id, bar.label, bar.point, bar.relativePoint, bar.x, bar.y)
-- UIParent_ManageFramePositions moves MultiBarBottomLeft and MultiBarRight again on many
-- events; it leaves alone a frame marked as placed by the player
bar.frame:SetUserPlaced(true)
bar.frame:HookScript("OnShow", M.Restack)
bar.frame:HookScript("OnHide", M.Restack)
end
end
M.Restack()
end
-- The client slides MultiBarRight back in whenever it brings its bar back (vehicle exit,
-- loading screen, talent spec switch: MainMenuBar_ToPlayerArt), anchoring it by BOTTOMRIGHT
-- over ours without clearing it. When the slide ends (MainMenuBar_UnlockAB), ours is put back
-- alone, after combat if need be.
local rightPending = false
local function reanchorRight()
if InCombatLockdown() then
rightPending = true
return
end
rightPending = false
ForeverUI.Layout.Apply("multibarright")
end
if type(_G["MainMenuBar_UnlockAB"]) == "function" then
hooksecurefunc("MainMenuBar_UnlockAB", reanchorRight)
end
-- An extra bar, the totem bar, the minimap or the right gryphon moved, resized or put back in
-- edit mode
local FOLLOWED = { multibarbottomleft = true, multibarbottomright = true, multibarright = true,
multibarleft = true, totembar = true, minimap = true, rightgryphon = true }
hooksecurefunc(ForeverUI.Layout, "Apply", function(id)
if laidOut and FOLLOWED[id] then M.Restack() end
end)
local watcher = CreateFrame("Frame")
for _, event in ipairs({ "PLAYER_LOGIN", "PLAYER_REGEN_ENABLED", "DISPLAY_SIZE_CHANGED", "UI_SCALE_CHANGED" }) do
watcher:RegisterEvent(event)
end
watcher:SetScript("OnEvent", function(_, event)
if not laidOut then
setup()
elseif event == "PLAYER_REGEN_ENABLED" and rightPending then
reanchorRight() -- re-stacks too (the Layout.Apply hook above)
elseif event ~= "PLAYER_REGEN_ENABLED" or pending then
M.Restack()
end
end)
setup()