Files
moon-well-ui/data/addon/ForeverUI/EquipmentManager.lua
T
Adrestie fb2b852dea Gestionnaire : la fenetre du client se hissait au-dessus du bouton
GearManagerDialog est declaree toplevel dans le FrameXML et son OnShow
finit par GearManagerDialog:Raise() : a chaque ouverture elle repasse au
sommet de sa strate, apres tout niveau pose une fois pour toutes. D'ou un
bouton qui ne recevait rien malgre un niveau superieur.

SetToplevel(false), puisqu'elle n'est plus une fenetre, et le niveau du
bouton recalcule depuis le sien a chaque passage.

Le banc reproduit desormais Raise et toplevel.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-22 15:52:11 +02:00

454 lines
15 KiB
Lua

-- ForeverUI : le gestionnaire d'equipement, dans le volet droit.
--
-- RELEVE -- camelot/PaperDollFrame.xml, PaperDollFrame.EquipmentManagerPane :
-- ancre TOPLEFT sur le BOTTOMLEFT de la bande de pierre du volet,
-- BOTTOMRIGHT sur le volet lui-meme.
-- bordure common-insideframe, TOPLEFT (1, 1) et BOTTOMRIGHT (-4, 2).
-- liste TOPLEFT (5, -8), BOTTOMRIGHT (-20, 105).
-- trait UI-Character-Info-ScrollLine, au TOP du BOTTOM de la liste.
-- Equip 99 x 28, BOTTOM (-50, 20), intitule EQUIPSET_EQUIP.
-- Save 99 x 28, BOTTOM (50, 20), intitule SAVE.
-- New Set 180 x 34, BOTTOM (0, 50), avec UI-Character-Info-Icon-Add
-- a LEFT (13).
--
-- RELEVE -- camelot, GearSetButtonTemplate, la carte d'un ensemble :
-- cadre 169 x 44
-- fond UI-Character-Info-OutfitCard, 152 x 49, TOPLEFT x = 42
-- survol UI-Character-Info-OutfitCard-Hover, meme place
-- choisi UI-Character-Info-OutfitCard-Selected, meme place
-- coche UI-Character-Info-Icon-Tick, RIGHT (-23, 0), montree quand
-- l'ensemble est PORTE (PaperDollEquipmentManagerPane_InitButton)
-- intitule GameFontNormalLeft, 98 x 38, LEFT (55)
-- icone 36 x 36, LEFT (4), cerclee de
-- UI-Character-Info-OutfitIcon-Frame, centree dessus
--
-- CE QUE LE CLIENT PORTE. GearManagerDialog, une fenetre de 261 x 155 sur
-- UIPanelDialogTemplate, avec GearSetButton1..MAX_EQUIPMENT_SETS_PER_PLAYER
-- poses EN GRILLE de cinq, et trois boutons : Delete, Equip, Save. Une carte
-- y est un CheckButton de 36 sur PopupButtonTemplate -- son icone est sa
-- NormalTexture, son intitule le $parentName sous elle, et un fond
-- UI-EmptySlot-Disabled derriere.
--
-- Rien n'est recree : la fenetre du client passe dans le volet, ses cartes
-- se reposent en colonne et se rhabillent, ses boutons gardent leur clic.
-- C'est lui qui tient la liste, la selection et les infobulles.
--
-- CE QUI DIFFERE, ET POURQUOI.
-- 3.3.5 n'a pas de bouton "New Set" : on y cree un ensemble par Save, qui
-- ouvre la fenetre de nom. Le notre fait la meme chose apres avoir vide la
-- selection, ce qui est exactement "enregistrer sous un nouveau nom".
-- Le bouton Delete du client n'a pas d'equivalent chez camelot, qui efface
-- un ensemble par le menu de sa carte -- menu que 3.3.5 n'a pas. Il est
-- donc GARDE, a droite de Save, plutot que de retirer la seule facon
-- d'effacer un ensemble.
-- GetEquipmentSetInfo ne dit pas si un ensemble est porte. La coche se
-- calcule donc depuis GetEquipmentSetLocations : l'ensemble est porte
-- quand chacune de ses pieces est sur le joueur et hors des sacs.
-- La barre de defilement de camelot (MinimalScrollBar) n'est pas portee :
-- la liste defile a la molette, et le trait de camelot marque son bas.
ForeverUI = ForeverUI or {}
local CARTE_L, CARTE_H = 169, 44
local CARTE_FOND_L, CARTE_FOND_H = 152, 49
local CARTE_FOND_X = 42
local CARTE_ICONE, CARTE_ICONE_X = 36, 4
local CARTE_TEXTE_X = 55
local CARTE_TEXTE_L, CARTE_TEXTE_H = 98, 38
local COCHE_X = -23
local LISTE_X, LISTE_Y = 5, -8
local LISTE_X2, LISTE_Y2 = -20, 105
local BOUTON_L, BOUTON_H = 99, 28
local BOUTON_Y, BOUTON_ECART = 20, 50
local NOUVEAU_L, NOUVEAU_H = 180, 34
local NOUVEAU_Y, NOUVEAU_ICONE_X = 50, 13
-- LA BORDURE SE DECOUPE, ELLE NE S'ETIRE PAS. common-insideframe fait
-- 107 x 107 et porte un MOTIF dans chaque angle : tendue sur les 233 x 379
-- du panneau, elle est multipliee par deux en largeur et par trois et demi
-- en hauteur, et tout se brouille.
--
-- Mesure sur l'art : le filet occupe 2..12 et 94..104 sur les deux axes, et
-- le motif d'angle s'arrete a 19 -- des x = 20 le profil n'est plus que le
-- filet. Le coin vaut donc 20, ce qui laisse une bande centrale de 67.
--
-- Les marges viennent de camelot, qui ancre sa bordure en TOPLEFT (1, 1) et
-- BOTTOMRIGHT (-4, 2) : converties dans la convention du decoupage -- de
-- combien l'image deborde du cadre -- cela donne -1, 1, -4, -2.
--
-- ECART ASSUME, sur demande : toute la bordure est decalee de 3 px vers la
-- droite. Les deux bords bougent ensemble -- le gauche de -1 a -4, le droit
-- de -4 a -1 -- sinon elle s'elargirait au lieu de glisser.
local BORDURE_COIN = 20
local BORDURE_MARGES = { -4, 1, -1, -2 }
local ATLAS_FOND = "ui-character-info-outfitcard"
local ATLAS_SURVOL = "ui-character-info-outfitcard-hover"
local ATLAS_CHOISI = "ui-character-info-outfitcard-selected"
local ATLAS_COCHE = "ui-character-info-icon-tick"
local ATLAS_CERCLE = "ui-character-info-outfiticon-frame"
local ATLAS_PLUS = "ui-character-info-icon-add"
local ATLAS_BORDURE = "common-insideframe"
local ATLAS_TRAIT = "ui-character-info-scrollline"
local panneau, decalage = nil, 0
-- Un ensemble est PORTE quand chacune de ses pieces est sur le joueur et
-- hors des sacs. EquipmentManager_UnpackLocation rend ces deux drapeaux.
local function ensemblePorte(nom)
if not nom or not GetEquipmentSetLocations or not EquipmentManager_UnpackLocation then
return false
end
local places = GetEquipmentSetLocations(nom)
if not places then
return false
end
local vu = false
for _, place in pairs(places) do
if type(place) == "number" and place > 1 then
local joueur, _, sacs = EquipmentManager_UnpackLocation(place)
if not joueur or sacs then
return false
end
vu = true
end
end
return vu
end
-- Combien de cartes tiennent dans la liste, et ou elle commence.
local function hauteurListe()
if not panneau then
return 0
end
return panneau:GetHeight() - (-LISTE_Y) - LISTE_Y2
end
local function cartesVisibles()
local place = math.floor(hauteurListe() / CARTE_H)
if place < 1 then
place = 1
end
return place
end
-- La carte : le cadre du client, rhabille une seule fois.
local function habillerCarte(bouton)
if bouton.foreverCarte then
return
end
bouton:SetWidth(CARTE_L)
bouton:SetHeight(CARTE_H)
-- Le fond d'emplacement vide de 3.3.5 s'en va ; l'icone, elle, est la
-- NormalTexture du bouton et doit rester.
local icone = bouton:GetNormalTexture()
local regions = { bouton:GetRegions() }
for _, region in ipairs(regions) do
if region ~= icone and region.GetObjectType and region:GetObjectType() == "Texture" then
local chemin = region.GetTexture and region:GetTexture()
if type(chemin) == "string" and string.find(string.lower(chemin), "emptyslot") then
region:SetAlpha(0)
end
end
end
if bouton:GetHighlightTexture() then
bouton:GetHighlightTexture():SetAlpha(0)
end
if bouton.GetCheckedTexture and bouton:GetCheckedTexture() then
bouton:GetCheckedTexture():SetAlpha(0)
end
local function carte(atlas, couche)
local t = bouton:CreateTexture(nil, couche)
ForeverUI.SetAtlas(t, atlas, true)
t:SetWidth(CARTE_FOND_L)
t:SetHeight(CARTE_FOND_H)
t:SetPoint("TOPLEFT", bouton, "TOPLEFT", CARTE_FOND_X, 0)
return t
end
bouton.foreverFond = carte(ATLAS_FOND, "BACKGROUND")
bouton.foreverSurvol = carte(ATLAS_SURVOL, "BORDER")
bouton.foreverSurvol:Hide()
bouton.foreverChoisi = carte(ATLAS_CHOISI, "BORDER")
bouton.foreverChoisi:Hide()
if icone then
icone:ClearAllPoints()
icone:SetWidth(CARTE_ICONE)
icone:SetHeight(CARTE_ICONE)
icone:SetPoint("LEFT", bouton, "LEFT", CARTE_ICONE_X, 0)
icone:SetDrawLayer("ARTWORK")
end
local cercle = bouton:CreateTexture(nil, "OVERLAY")
ForeverUI.SetAtlas(cercle, ATLAS_CERCLE)
cercle:SetPoint("CENTER", icone or bouton, "CENTER", 0, 0)
local coche = bouton:CreateTexture(nil, "OVERLAY")
ForeverUI.SetAtlas(coche, ATLAS_COCHE)
coche:SetPoint("RIGHT", bouton, "RIGHT", COCHE_X, 0)
coche:Hide()
bouton.foreverCoche = coche
local texte = _G[bouton:GetName() .. "Name"]
if texte then
texte:ClearAllPoints()
texte:SetFontObject(GameFontNormalLeft or GameFontNormal)
texte:SetWidth(CARTE_TEXTE_L)
texte:SetHeight(CARTE_TEXTE_H)
texte:SetJustifyH("LEFT")
texte:SetPoint("LEFT", bouton, "LEFT", CARTE_TEXTE_X, 0)
end
bouton:HookScript("OnEnter", function(self)
if self.name and self.name ~= "" then
self.foreverSurvol:Show()
end
end)
bouton:HookScript("OnLeave", function(self)
self.foreverSurvol:Hide()
end)
bouton.foreverCarte = true
end
-- La liste : les ensembles existants, en colonne, a partir du decalage.
local function poserCartes()
local dialogue = _G["GearManagerDialog"]
if not dialogue or not dialogue.buttons or not panneau then
return
end
local total = (GetNumEquipmentSets and GetNumEquipmentSets()) or 0
local place = cartesVisibles()
if decalage > total - place then
decalage = math.max(0, total - place)
end
local precedent
for index, bouton in ipairs(dialogue.buttons) do
habillerCarte(bouton)
local rang = index - decalage
if index <= total and rang >= 1 and rang <= place then
bouton:ClearAllPoints()
if precedent then
bouton:SetPoint("TOPLEFT", precedent, "BOTTOMLEFT", 0, 0)
else
bouton:SetPoint("TOPLEFT", panneau, "TOPLEFT", LISTE_X, LISTE_Y)
end
precedent = bouton
bouton:Show()
if bouton.foreverChoisi then
if bouton:GetChecked() then
bouton.foreverChoisi:Show()
else
bouton.foreverChoisi:Hide()
end
end
if bouton.foreverCoche then
if ensemblePorte(bouton.name) then
bouton.foreverCoche:Show()
else
bouton.foreverCoche:Hide()
end
end
else
bouton:Hide()
end
end
end
ForeverUI.EquipmentSetsLayout = poserCartes
local function monter(volet)
panneau = CreateFrame("Frame", "ForeverUIEquipmentPane", volet)
panneau:SetPoint("TOPLEFT", volet.pierre, "BOTTOMLEFT", 0, 0)
panneau:SetPoint("BOTTOMRIGHT", volet, "BOTTOMRIGHT", 0, 0)
panneau:SetFrameLevel(volet:GetFrameLevel() + 3)
panneau.bordure = ForeverUI.CreateNineSlice(panneau, ATLAS_BORDURE,
BORDURE_COIN, BORDURE_MARGES, "BORDER")
local trait = panneau:CreateTexture(nil, "BORDER")
ForeverUI.SetAtlas(trait, ATLAS_TRAIT)
trait:SetPoint("TOP", panneau, "BOTTOM", 0, LISTE_Y2)
-- La molette fait defiler : camelot a une barre, que nous n'avons pas.
panneau:EnableMouseWheel(true)
panneau:SetScript("OnMouseWheel", function(self, sens)
local total = (GetNumEquipmentSets and GetNumEquipmentSets()) or 0
decalage = math.max(0, math.min(decalage - sens, total - cartesVisibles()))
poserCartes()
end)
return panneau
end
-- Les trois boutons du bas, plus celui que le client garde pour effacer.
local function poserBoutons()
local equiper = _G["GearManagerDialogEquipSet"]
local enregistrer = _G["GearManagerDialogSaveSet"]
local effacer = _G["GearManagerDialogDeleteSet"]
if equiper then
equiper:SetWidth(BOUTON_L)
equiper:SetHeight(BOUTON_H)
equiper:ClearAllPoints()
equiper:SetPoint("BOTTOM", panneau, "BOTTOM", -BOUTON_ECART, BOUTON_Y)
end
if enregistrer then
enregistrer:SetWidth(BOUTON_L)
enregistrer:SetHeight(BOUTON_H)
enregistrer:ClearAllPoints()
enregistrer:SetPoint("BOTTOM", panneau, "BOTTOM", BOUTON_ECART, BOUTON_Y)
end
if effacer then
effacer:SetWidth(BOUTON_L)
effacer:SetHeight(BOUTON_H)
effacer:ClearAllPoints()
effacer:SetPoint("BOTTOM", enregistrer or panneau, "TOP", 0, 0)
effacer:Hide()
end
if not panneau.nouveau then
local nouveau = CreateFrame("Button", "ForeverUIEquipmentNewSet", panneau,
"UIPanelButtonTemplate")
nouveau:SetWidth(NOUVEAU_L)
nouveau:SetHeight(NOUVEAU_H)
nouveau:SetPoint("BOTTOM", panneau, "BOTTOM", 0, NOUVEAU_Y)
-- ECRIT EN DUR, faute de mieux : camelot ecrit
-- PAPERDOLL_NEWEQUIPMENTSET, et ce client ne porte aucune chaine
-- equivalente -- ni celle-la, ni "New Set" sous un autre nom. Ce
-- libelle ne se traduira donc pas.
nouveau:SetText("New Set")
-- Le meme visuel que les selecteurs de statistiques : le bouton
-- tertiaire, presse tant qu'on le tient.
ForeverUI.SkinTertiaryButton(nouveau, true)
local plus = nouveau:CreateTexture(nil, "OVERLAY")
ForeverUI.SetAtlas(plus, ATLAS_PLUS)
plus:SetPoint("LEFT", nouveau, "LEFT", NOUVEAU_ICONE_X, 0)
nouveau:SetScript("OnClick", function()
local dialogue = _G["GearManagerDialog"]
if dialogue then
dialogue.selectedSetName = nil
dialogue.selectedSet = nil
end
if GearManagerDialogSaveSet_OnClick then
GearManagerDialogSaveSet_OnClick(_G["GearManagerDialogSaveSet"])
end
end)
panneau.nouveau = nouveau
end
-- AU-DESSUS DE LA FENETRE DU CLIENT, ET A CHAQUE PASSAGE. Elle est
-- etalee sur tout le panneau et elle prend la souris ; un bouton fils du
-- PANNEAU passe dessous et ne recoit plus rien. Ceux du client -- Equip,
-- Save -- sont ses enfants a elle, donc epargnes.
--
-- Le niveau se calcule depuis LE SIEN, releve maintenant : elle se hisse
-- toute seule a chaque ouverture, et cette fonction tourne justement
-- apres son OnShow.
local dialogue = _G["GearManagerDialog"]
if panneau.nouveau and dialogue then
panneau.nouveau:SetFrameLevel(dialogue:GetFrameLevel() + 5)
end
end
-- LA FENETRE DU CLIENT PASSE DANS LE VOLET. On la vide de son art de
-- fenetre -- UIPanelDialogTemplate porte un fond, une bordure et une barre
-- de titre -- et on l'etale sur le panneau : elle n'est plus qu'un support
-- pour ses cartes et ses boutons.
local function accueillirDialogue()
local dialogue = _G["GearManagerDialog"]
if not dialogue or dialogue.foreverAccueilli then
return
end
dialogue:SetParent(panneau)
dialogue:ClearAllPoints()
dialogue:SetAllPoints(panneau)
dialogue:SetFrameLevel(panneau:GetFrameLevel() + 1)
-- ELLE N'EST PLUS UNE FENETRE, ELLE NE DOIT PLUS SE HISSER.
-- GearManagerDialog est declaree toplevel dans le FrameXML, et son
-- OnShow finit par GearManagerDialog:Raise() : a chaque ouverture elle
-- repasse au sommet de sa strate, donc au-dessus de tout ce que le
-- panneau porte. Un niveau pose une fois pour toutes ne tenait pas.
if dialogue.SetToplevel then
dialogue:SetToplevel(false)
end
local regions = { dialogue:GetRegions() }
for _, region in ipairs(regions) do
if region.GetObjectType and region:GetObjectType() == "Texture" then
region:SetAlpha(0)
end
end
if dialogue.title then
dialogue.title:Hide()
end
dialogue.foreverAccueilli = true
end
-- LA CROIX ROUGE S'EN VA. UIPanelDialogTemplate nomme son bouton
-- $parentClose -- GearManagerDialogClose -- et non $parentCloseButton : le
-- masquer sous le mauvais nom ne faisait rien. Le panneau n'est plus une
-- fenetre, il se ferme par son onglet.
--
-- A refaire a chaque passage : le client remontre ses morceaux quand il
-- rouvre sa fenetre.
local function masquerFermeture()
for _, nom in ipairs({ "GearManagerDialogClose", "GearManagerDialogCloseButton" }) do
local bouton = _G[nom]
if bouton then
bouton:Hide()
end
end
end
local function habiller()
local panes = ForeverUI.CharacterPanes
local volet = panes and panes.droit
if not volet or not volet.pierre or InCombatLockdown() then
return
end
if not panneau then
monter(volet)
end
-- LE PANNEAU SUIT SON ONGLET. Ses boutons sont des cadres fils du
-- PANNEAU, pas de la fenetre du client : masquer celle-ci ne les
-- emportait pas, et "New" restait visible sous les statistiques.
if volet.statsMontrees == false then
panneau:Show()
else
panneau:Hide()
end
accueillirDialogue()
masquerFermeture()
poserBoutons()
poserCartes()
end
ForeverUI.EquipmentPane = { Apply = habiller, Frame = function() return panneau end }
if hooksecurefunc then
for _, nom in ipairs({ "GearManagerDialog_Update", "GearManagerDialog_OnShow" }) do
if type(_G[nom]) == "function" then
hooksecurefunc(nom, function() habiller() end)
end
end
end