cc7d2d45fa
- Talents : trois arbres cote a cote facon camelot sur PlayerTalentFrame, noeuds carres/ronds (TalentsData.lua genere des DBC), fleches, portes, changements en attente (Apply / Undo), onglets lateraux des specialisations, Activate, talents du familier en fenetre reduite, recherche (marques sur les noeuds, apercu, options, absents des barres), confirmation a la fermeture, glisser un sort de talent appris vers une barre - Glyphes : page refaite sur l'art fourni (tools/cuire_glyphes.py), lueurs, etincelles, cercles concentriques tournants ; le GlyphFrame de WotLK reste sous PlayerTalentFrame (le reparenter plantait le client a la deconnexion) - Cadre du familier facon camelot (PetFrame.lua) - Superposition.lua : feuille, grimoire et talents passent entierement l'un devant l'autre, grimoire et talents deplacables - Grimoire : la recherche "Missing from action bar" suit les barres Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
188 lines
6.3 KiB
Lua
188 lines
6.3 KiB
Lua
-- ForeverUI : les grandes fenetres qui se superposent (feuille, grimoire,
|
|
-- talents).
|
|
--
|
|
-- LE PROBLEME. Chaque fenetre pose ses cadres a des niveaux explicites
|
|
-- (SetFrameLevel), relatifs a sa racine. Deux racines de la meme strate dont
|
|
-- les plages de niveaux se chevauchent se melangent a l'ecran : le fond de
|
|
-- l'une passe sous les boutons de l'autre. Raise, lui, ne leve que la racine.
|
|
--
|
|
-- LA REGLE (demande du 2026-09-25) : une fenetre passe ENTIEREMENT au-dessus
|
|
-- de l'autre, et cliquer sur une fenetre la ramene au premier plan ; une
|
|
-- fenetre qui s'ouvre vient devant.
|
|
--
|
|
-- LE MOYEN. Les fenetres ouvertes sont empilees de l'arriere vers l'avant :
|
|
-- le plus bas cadre de chacune est pose un niveau au-dessus du plus haut
|
|
-- cadre de la precedente, et toute sa descendance est decalee d'autant. Le clic se voit
|
|
-- a l'image suivante (IsMouseButtonDown), sous la souris (MouseIsOver) : la
|
|
-- 3.3.5 n'a pas d'evenement de clic global, et un clic sur un bouton ne
|
|
-- remonte pas au cadre qui le porte.
|
|
--
|
|
-- La fenetre du fond reprend son niveau d'origine : les niveaux ne montent
|
|
-- pas d'un clic a l'autre.
|
|
--
|
|
-- EN COMBAT, une racine protegee (celle du grimoire porte des boutons
|
|
-- securises) ne peut pas changer de niveau : elle reste ou elle est, les
|
|
-- autres s'empilent autour.
|
|
|
|
ForeverUI = ForeverUI or {}
|
|
|
|
local P = { fenetres = {}, ordre = {} }
|
|
ForeverUI.Superposition = P
|
|
|
|
-- le plus haut niveau d'un sous-arbre
|
|
local function plusHaut(cadre)
|
|
local n = cadre:GetFrameLevel()
|
|
for _, enfant in ipairs({ cadre:GetChildren() }) do
|
|
local m = plusHaut(enfant)
|
|
if m > n then n = m end
|
|
end
|
|
return n
|
|
end
|
|
|
|
-- le plus bas : un cadre du client peut etre sous sa racine
|
|
local function plusBas(cadre)
|
|
local n = cadre:GetFrameLevel()
|
|
for _, enfant in ipairs({ cadre:GetChildren() }) do
|
|
local m = plusBas(enfant)
|
|
if m < n then n = m end
|
|
end
|
|
return n
|
|
end
|
|
|
|
-- les niveaux d'un sous-arbre, parents avant enfants
|
|
local function releve(cadre, liste)
|
|
table.insert(liste, { cadre, cadre:GetFrameLevel() })
|
|
for _, enfant in ipairs({ cadre:GetChildren() }) do releve(enfant, liste) end
|
|
return liste
|
|
end
|
|
|
|
-- poser la racine a un niveau, sa descendance suit du meme ecart. Selon le
|
|
-- client, SetFrameLevel entraine deja les enfants ou non : on ne repose que
|
|
-- ceux qui ne sont pas ou ils doivent etre.
|
|
local function poserNiveau(racine, niveau)
|
|
local ecart = niveau - racine:GetFrameLevel()
|
|
if ecart == 0 then return end
|
|
for _, e in ipairs(releve(racine, {})) do
|
|
local voulu = math.max(0, e[2] + ecart)
|
|
if e[1]:GetFrameLevel() ~= voulu then e[1]:SetFrameLevel(voulu) end
|
|
end
|
|
end
|
|
|
|
local function bloquee(f)
|
|
return InCombatLockdown() and f.racine.IsProtected and f.racine:IsProtected()
|
|
end
|
|
|
|
local function ouvertes()
|
|
local l = {}
|
|
for _, cle in ipairs(P.ordre) do
|
|
local f = P.fenetres[cle]
|
|
if f.racine:IsVisible() then table.insert(l, f) end
|
|
end
|
|
return l
|
|
end
|
|
|
|
-- empiler : l'ordre retenu, la fenetre donnee devant
|
|
function P.devant(cle)
|
|
local f = P.fenetres[cle]
|
|
if not f then return end
|
|
for i, c in ipairs(P.ordre) do
|
|
if c == cle then table.remove(P.ordre, i) break end
|
|
end
|
|
table.insert(P.ordre, cle)
|
|
local niveau
|
|
for _, g in ipairs(ouvertes()) do
|
|
-- le plus bas cadre de celle-ci juste au-dessus du plus haut de la
|
|
-- precedente
|
|
if not bloquee(g) then
|
|
if niveau then
|
|
poserNiveau(g.racine, g.racine:GetFrameLevel() + niveau - plusBas(g.racine))
|
|
else
|
|
poserNiveau(g.racine, g.base)
|
|
end
|
|
end
|
|
niveau = plusHaut(g.racine) + 1
|
|
end
|
|
end
|
|
|
|
-- la fenetre de devant parmi celles sous la souris
|
|
local function sousLaSouris()
|
|
local l = ouvertes()
|
|
for i = #l, 1, -1 do
|
|
for _, zone in ipairs(l[i].zones()) do
|
|
if zone and zone:IsVisible() and MouseIsOver(zone) then return l[i] end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- inscrire une fenetre : sa racine (le cadre dont le niveau bouge) et ses
|
|
-- zones (ce qui, a l'ecran, lui appartient -- l'art debordant, les onglets
|
|
-- dehors)
|
|
function P.inscrire(cle, racine, zones)
|
|
if P.fenetres[cle] then return end
|
|
P.fenetres[cle] = { cle = cle, racine = racine, zones = zones, base = racine:GetFrameLevel() }
|
|
table.insert(P.ordre, cle)
|
|
racine:HookScript("OnShow", function() P.devant(cle) end)
|
|
if racine:IsVisible() then P.devant(cle) end
|
|
end
|
|
|
|
-- le clic, vu a l'image suivante ; rien a faire tant qu'une seule est ouverte
|
|
local veille = CreateFrame("Frame")
|
|
P.veille = veille
|
|
local enfonce = false
|
|
veille:SetScript("OnUpdate", function()
|
|
local bas = IsMouseButtonDown("LeftButton") or IsMouseButtonDown("RightButton")
|
|
if bas and not enfonce then
|
|
local l = ouvertes()
|
|
-- meme la fenetre de devant se repose : le Raise du client (racines
|
|
-- toplevel) ne leve que la racine
|
|
if #l > 1 then
|
|
local f = sousLaSouris()
|
|
if f then P.devant(f.cle) end
|
|
end
|
|
end
|
|
enfonce = bas and true or false
|
|
end)
|
|
|
|
-- ------------------------------------------------------------ le deplacement
|
|
-- La barre du titre sert de poignee, comme pour la feuille. La fenetre est
|
|
-- reancree par le HAUT-CENTRE : sa largeur change (grimoire reduit, talents
|
|
-- du familier) autour de son milieu, comme a sa place d'origine. La place est
|
|
-- retenue dans ForeverUIDB, relue a chaque ouverture (les variables sauvees
|
|
-- arrivent apres le chargement des fichiers). Hors combat seulement : le
|
|
-- grimoire porte des boutons securises.
|
|
local function positions()
|
|
ForeverUIDB = ForeverUIDB or {}
|
|
ForeverUIDB.positions = ForeverUIDB.positions or {}
|
|
return ForeverUIDB.positions
|
|
end
|
|
|
|
function P.deplacable(cadre, poignee, cle)
|
|
cadre:SetMovable(true)
|
|
cadre:SetClampedToScreen(true)
|
|
poignee:EnableMouse(true)
|
|
poignee:RegisterForDrag("LeftButton")
|
|
poignee:SetScript("OnDragStart", function()
|
|
if not InCombatLockdown() then cadre:StartMoving() end
|
|
end)
|
|
poignee:SetScript("OnDragStop", function()
|
|
cadre:StopMovingOrSizing()
|
|
if InCombatLockdown() then return end
|
|
local cx = cadre:GetCenter()
|
|
local ux = UIParent:GetCenter()
|
|
local x, y = cx - ux, cadre:GetTop() - UIParent:GetTop()
|
|
cadre:ClearAllPoints()
|
|
cadre:SetPoint("TOP", UIParent, "TOP", x, y)
|
|
-- la place est a nous : le client ne la retient pas en plus
|
|
if cadre.SetUserPlaced then cadre:SetUserPlaced(false) end
|
|
positions()[cle] = { x = x, y = y }
|
|
end)
|
|
local function reposer()
|
|
local p = positions()[cle]
|
|
if not p or InCombatLockdown() then return end
|
|
cadre:ClearAllPoints()
|
|
cadre:SetPoint("TOP", UIParent, "TOP", p.x, p.y)
|
|
end
|
|
cadre:HookScript("OnShow", reposer)
|
|
reposer()
|
|
end
|