Files
moon-well-ui/data/addon/ForeverUI/EquipmentManager.lua
T
Adrestie 6bd9aa1bf3 Gestionnaire : le nom etait lu apres la fermeture de la fenetre
GearManagerDialogPopup_OnHide remet popup.name a nil : je le lisais apres
popup:Hide(), d'ou SaveEquipmentSet(nil, ...) et son Usage. Le nom et
l'icone sont desormais lus avant, et un drapeau dit au greffon du OnHide
que la fermeture vient de nous, pour qu'il n'abandonne pas l'edition.

Trois manques du banc l'avaient laisse passer : son Hide ne declenchait
pas OnHide, son SaveEquipmentSet acceptait un nom vide, et son
GearManagerDialogSaveSet_OnClick ne montrait pas la fenetre. Les trois
sont corriges, et le banc echoue bien sur l'ancien code.

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

902 lines
31 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 {}
-- ECARTS ASSUMES, sur demande. camelot donne une carte de 169 x 44 dont le
-- fond fait 152 x 49 pose a x = 42. Ici :
--
-- * la liste glisse de 4 vers la droite (LISTE_X de 5 a 9) ;
-- * la carte est moins haute -- 40 au lieu de 44, le fond suivant a 45,
-- l'image gardant les 5 de debord de la source ;
-- * le fond est plus large, pour que l'ecart entre le separateur des
-- volets et le bord gauche d'une icone soit CELUI du bord droit de la
-- fenetre au bord droit de la carte.
--
-- Le calcul de cette largeur, en abscisses du volet (233 de large) :
-- le separateur est pose a -6 et fait 11 -> son bord droit tombe a 5
-- l'icone commence a LISTE_X + 4 -> 13, donc 8 d'ecart
-- la carte finit a LISTE_X + 42 + largeur -> il faut 233 - 8 = 225
-- d'ou largeur = 225 - 9 - 42 = 174
-- Le bouton, lui, couvre toute la carte : 42 + 174.
local CARTE_FOND_L, CARTE_FOND_H = 174, 45
local CARTE_FOND_X = 42
local CARTE_L, CARTE_H = CARTE_FOND_X + CARTE_FOND_L, 40
local CARTE_ICONE, CARTE_ICONE_X = 36, 4
local CARTE_TEXTE_X = 55
local CARTE_TEXTE_L, CARTE_TEXTE_H = 98, 38
-- ECART ASSUME, sur demande. camelot ancre sa coche a RIGHT (-23) du
-- BOUTON, qui chez lui s'arrete 25 px avant le bord de la carte : la coche
-- tombe donc bien a l'interieur. Ici le bouton couvre toute la carte, si
-- bien que le meme -23 la ramenait trop vers la gauche. Elle est donc posee
-- par rapport au bord DROIT de la carte, qu'elle longe a 12.
local COCHE_X = -12
local LISTE_X, LISTE_Y = 9, -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"
-- RELEVE -- camelot, GearSetButtonTemplate, les deux boutons de survol :
--
-- $parentDeleteButton 14 x 14, BOTTOMRIGHT (-21, 2)
-- Interface\\Buttons\\UI-GroupLoot-Pass-Up, alpha 0,5
-- au repos et 1 au survol ; enfonce, la texture
-- glisse de (1, -1). Infobulle DELETE. Au clic :
-- StaticPopup_Show("CONFIRM_DELETE_EQUIPMENT_SET").
-- $parentEditButton 16 x 16, RIGHT sur le LEFT du precedent, x = -1
-- Interface\\WorldMap\\GEAR_64GREY, memes alphas.
--
-- Les deux ne paraissent QUE sur la carte survolee -- camelot le decide
-- dans PaperDollEquipmentManagerPane_OnUpdate, en interrogeant IsMouseOver
-- a chaque image. On fait de meme : un OnEnter ne suffirait pas, il part
-- des qu'on entre sur l'un de ces deux boutons, qui sont des cadres fils.
--
-- CE QUI DIFFERE. L'engrenage de camelot ouvre un menu d'assignation de
-- SPECIALISATION (C_EquipmentSet.AssignSpecToEquipmentSet), qui n'existe
-- pas en 3.3.5. A la demande, il rouvre ici la fenetre de creation sur
-- l'ensemble choisi : le client la remplit alors de son nom et de son
-- icone (RecalculateGearManagerDialogPopup), et son Okay voit que le nom
-- existe deja -- il demande confirmation puis ECRASE l'ensemble au lieu
-- d'en creer un.
-- EQUIPMENT_SET_SETTINGS, l'infobulle de camelot, n'existe pas ici :
-- SETTINGS est la plus proche que ce client porte.
local SUPPRIMER = 14
local SUPPRIMER_X, SUPPRIMER_Y = -21, 2
local EDITER = 16
local EDITER_X = -1
local ICONE_SUPPRIMER = "Interface\\Buttons\\UI-GroupLoot-Pass-Up"
local ICONE_EDITER = "Interface\\WorldMap\\GEAR_64GREY"
local REPOS, SURVOL = 0.5, 1.0
local panneau, decalage = nil, 0
-- MODIFIER UN ENSEMBLE : CE QUE 3.3.5 PERMET, ET COMMENT.
--
-- Ce client n'a PAS de ModifyEquipmentSet -- verifie dans Wow.exe. Il n'a
-- que SaveEquipmentSet(nom, icone), qui enregistre L'EQUIPEMENT PORTE sous
-- ce nom, et DeleteEquipmentSet(nom). Changer le nom ou l'icone sans
-- toucher a la liste d'objets est donc impossible directement.
--
-- LE CHEMIN RETENU, sur decision : equiper l'ancien ensemble -- l'equipement
-- porte DEVIENT alors sa liste -- l'enregistrer sous le nouveau nom et la
-- nouvelle icone, effacer l'ancien, et remettre le nouveau a la place de
-- l'ancien dans la liste. Les objets sont ainsi conserves a l'identique.
--
-- Deux consequences assumees :
-- * le personnage change reellement d'equipement le temps de l'operation ;
-- * elle est ASYNCHRONE -- UseEquipmentSet rend la main avant la fin, et
-- c'est EQUIPMENT_SWAP_FINISHED(termine, nom) qui l'annonce. Si
-- l'ensemble est deja porte, rien n'est a equiper et on enchaine.
--
-- L'ORDRE D'AFFICHAGE est tenu par nous : 3.3.5 n'a aucun moyen de replacer
-- un ensemble dans sa liste, l'ordre du client etant celui de creation. Le
-- notre vit dans ForeverUIDB, et c'est lui qui pose les cartes.
local edition
local fermetureVoulue -- on ferme la fenetre nous-memes
local EDITION_DELAI = 10 -- secondes avant d'abandonner
local function ordreRetenu()
ForeverUIDB = ForeverUIDB or {}
ForeverUIDB.ordreEnsembles = ForeverUIDB.ordreEnsembles or {}
return ForeverUIDB.ordreEnsembles
end
local function rangDe(nom)
for rang, connu in ipairs(ordreRetenu()) do
if connu == nom then
return rang
end
end
return nil
end
-- Les ensembles du client, dans NOTRE ordre ; ceux qu'on ne connait pas
-- encore prennent la fin de la liste.
local function ensemblesOrdonnes()
local total = (GetNumEquipmentSets and GetNumEquipmentSets()) or 0
local parNom, dans = {}, {}
for index = 1, total do
local nom = GetEquipmentSetInfo(index)
if nom then
parNom[nom] = index
end
end
local liste = {}
for _, nom in ipairs(ordreRetenu()) do
if parNom[nom] then
liste[#liste + 1] = parNom[nom]
dans[nom] = true
end
end
for index = 1, total do
local nom = GetEquipmentSetInfo(index)
if nom and not dans[nom] then
liste[#liste + 1] = index
ordreRetenu()[#ordreRetenu() + 1] = nom
end
end
return liste
end
ForeverUI.EquipmentSetsOrder = ensemblesOrdonnes
-- QUEL ENSEMBLE EST PORTE. Deux questions, et je n'en avais traite
-- qu'une, mal.
--
-- 1. LA PIECE EST-ELLE DANS LE BON EMPLACEMENT ? GetEquipmentSetLocations
-- rend une table indexee par EMPLACEMENT d'equipement, et
-- EquipmentManager_UnpackLocation rend "joueur, banque, sacs, SLOT".
-- Je ne lisais que les deux premiers drapeaux : une piece portee dans un
-- AUTRE emplacement passait pour bonne, d'ou des coches sur des
-- ensembles sans rapport. Il faut comparer le slot rendu a la CLE.
--
-- 2. DEUX ENSEMBLES AUX MEMES PIECES. Si deux ensembles decrivent le meme
-- equipement, la geometrie ne peut pas les departager : tous deux sont
-- "portes". 3.3.5 n'a AUCUNE notion d'ensemble actif -- son propre
-- gestionnaire n'affiche d'ailleurs rien de tel. On retient donc le
-- dernier ensemble equipe, en se greffant sur UseEquipmentSet, et la
-- coche va a celui-la -- a condition qu'il soit encore porte, sinon
-- elle disparait des que le joueur change une piece a la main.
local function piecesEnPlace(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 emplacement, place in pairs(places) do
if type(place) == "number" and place > 1 then
local joueur, _, sacs, slot = EquipmentManager_UnpackLocation(place)
if not joueur or sacs or slot ~= emplacement then
return false
end
vu = true
end
end
return vu
end
local function ensembleActif()
ForeverUIDB = ForeverUIDB or {}
return ForeverUIDB.ensembleEquipe
end
local function ensemblePorte(nom)
return nom ~= nil and nom == ensembleActif() and piecesEnPlace(nom)
end
ForeverUI.EquipmentSetWorn = ensemblePorte
-- Le dernier ensemble equipe, retenu d'une session a l'autre.
if hooksecurefunc and type(UseEquipmentSet) == "function" then
hooksecurefunc("UseEquipmentSet", function(nom)
ForeverUIDB = ForeverUIDB or {}
ForeverUIDB.ensembleEquipe = nom
if ForeverUI.EquipmentSetsLayout then
ForeverUI.EquipmentSetsLayout()
end
end)
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
-- LE CHOIX PASSE AU-DESSUS DU SURVOL. Ils etaient sur le meme calque,
-- ou seul l'ordre de creation departage -- trop fragile pour une regle
-- d'affichage. Le survol reste en BORDER, le choix monte en ARTWORK :
-- l'ordre ne depend plus de rien.
bouton.foreverFond = carte(ATLAS_FOND, "BACKGROUND")
bouton.foreverSurvol = carte(ATLAS_SURVOL, "BORDER")
bouton.foreverSurvol:Hide()
bouton.foreverChoisi = carte(ATLAS_CHOISI, "ARTWORK")
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
-- LES DEUX BOUTONS DE SURVOL.
local function petitBouton(nom, cote, icone, infobulle, clic)
local b = CreateFrame("Button", bouton:GetName() .. nom, bouton)
b:SetWidth(cote)
b:SetHeight(cote)
b:SetFrameLevel(bouton:GetFrameLevel() + 2)
local t = b:CreateTexture(nil, "ARTWORK")
t:SetTexture(icone)
t:SetAllPoints(b)
t:SetAlpha(REPOS)
b.texture = t
b:SetScript("OnEnter", function(self)
self.texture:SetAlpha(SURVOL)
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
GameTooltip:SetText(infobulle)
GameTooltip:Show()
end)
b:SetScript("OnLeave", function(self)
self.texture:SetAlpha(REPOS)
GameTooltip:Hide()
end)
b:SetScript("OnMouseDown", function(self)
self.texture:ClearAllPoints()
self.texture:SetPoint("TOPLEFT", self, "TOPLEFT", 1, -1)
self.texture:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", 1, -1)
end)
b:SetScript("OnMouseUp", function(self)
self.texture:ClearAllPoints()
self.texture:SetAllPoints(self)
end)
b:SetScript("OnClick", clic)
b:Hide()
return b
end
bouton.foreverSupprimer = petitBouton("ForeverUIDelete", SUPPRIMER,
ICONE_SUPPRIMER, DELETE or "Delete", function(self)
local carte = self:GetParent()
if not carte.name or carte.name == "" then
return
end
local fenetre = StaticPopup_Show("CONFIRM_DELETE_EQUIPMENT_SET", carte.name)
if fenetre then
fenetre.data = carte.name
elseif UIErrorsFrame then
UIErrorsFrame:AddMessage(ERR_CLIENT_LOCKED_OUT, 1.0, 0.1, 0.1, 1.0)
end
end)
bouton.foreverSupprimer:SetPoint("BOTTOMRIGHT", bouton, "BOTTOMRIGHT",
SUPPRIMER_X, SUPPRIMER_Y)
bouton.foreverEditer = petitBouton("ForeverUIEdit", EDITER,
ICONE_EDITER, SETTINGS or "Settings", function(self)
local carte = self:GetParent()
if not carte.name or carte.name == "" then
return
end
if ForeverUI.EquipmentSetEdit then
ForeverUI.EquipmentSetEdit(carte.name)
end
end)
bouton.foreverEditer:SetPoint("RIGHT", bouton.foreverSupprimer, "LEFT",
EDITER_X, 0)
bouton.foreverCarte = true
end
-- LE SURVOL SE SUIT A CHAQUE IMAGE, PAS PAR OnEnter. Les deux boutons sont
-- des cadres fils de la carte : y entrer declencherait le OnLeave de la
-- carte, qui les masquerait aussitot. camelot interroge IsMouseOver dans
-- PaperDollEquipmentManagerPane_OnUpdate, on fait pareil.
local function suivreSurvol()
local dialogue = _G["GearManagerDialog"]
if not dialogue or not dialogue.buttons then
return
end
for _, bouton in ipairs(dialogue.buttons) do
if bouton.foreverCarte and bouton:IsShown() then
local dessus = bouton:IsMouseOver() and bouton.name and bouton.name ~= ""
if dessus then
bouton.foreverSurvol:Show()
bouton.foreverSupprimer:Show()
bouton.foreverEditer:Show()
else
bouton.foreverSurvol:Hide()
bouton.foreverSupprimer:Hide()
bouton.foreverEditer:Hide()
end
end
end
end
ForeverUI.EquipmentSetsHover = suivreSurvol
-- 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
-- NOTRE ORDRE, pas celui du client : le bouton d'indice i porte
-- l'ensemble i, mais c'est nous qui decidons ou il se pose.
local ordre = ensemblesOrdonnes()
local rangDuBouton = {}
for position, index in ipairs(ordre) do
rangDuBouton[index] = position
end
local precedent
local parRang = {}
for index, bouton in ipairs(dialogue.buttons) do
habillerCarte(bouton)
if rangDuBouton[index] then
parRang[rangDuBouton[index]] = bouton
end
end
for position = 1, #dialogue.buttons do
local bouton = parRang[position]
local index = position
local rang = position - decalage
if bouton and 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
elseif bouton then
bouton:Hide()
end
end
-- Les cartes sans ensemble ne s'affichent pas.
for index, bouton in ipairs(dialogue.buttons) do
if not rangDuBouton[index] then
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:SetScript("OnUpdate", suivreSurvol)
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
-- ============================================== modifier un ensemble
--
-- Le flux, dans l'ordre : equiper l'ancien, enregistrer sous le nouveau nom
-- et la nouvelle icone, effacer l'ancien, le remplacer a sa place dans la
-- liste. Voir le releve en tete de fichier pour le pourquoi.
local attenteEdition = CreateFrame("Frame", "ForeverUIEquipmentEdit")
attenteEdition:Hide()
local function remplacerDansOrdre(ancien, nouveau)
local ordre = ordreRetenu()
for rang, connu in ipairs(ordre) do
if connu == ancien then
ordre[rang] = nouveau
return
end
end
ordre[#ordre + 1] = nouveau
end
local function terminerEdition()
local e = edition
edition = nil
attenteEdition:Hide()
if not e then
return
end
-- Le nom est indispensable : SaveEquipmentSet le refuse vide.
if not e.nom or e.nom == "" then
return
end
if SaveEquipmentSet then
SaveEquipmentSet(e.nom, e.icone)
end
if e.nom ~= e.ancien and DeleteEquipmentSet then
DeleteEquipmentSet(e.ancien)
remplacerDansOrdre(e.ancien, e.nom)
end
ForeverUIDB = ForeverUIDB or {}
if ForeverUIDB.ensembleEquipe == e.ancien then
ForeverUIDB.ensembleEquipe = e.nom
end
local dialogue = _G["GearManagerDialog"]
if dialogue then
dialogue.selectedSetName = e.nom
end
if GearManagerDialog_Update then
GearManagerDialog_Update()
end
poserCartes()
end
ForeverUI.EquipmentSetEditFinish = terminerEdition
attenteEdition:RegisterEvent("EQUIPMENT_SWAP_FINISHED")
attenteEdition:SetScript("OnEvent", function(self, evenement, termine, nom)
if edition and termine and nom == edition.ancien then
terminerEdition()
end
end)
-- Si le remplacement n'aboutit pas -- combat, piece verrouillee -- on
-- abandonne plutot que de laisser une edition en suspens.
attenteEdition:SetScript("OnUpdate", function(self, ecoule)
if not edition then
self:Hide()
return
end
edition.reste = (edition.reste or EDITION_DELAI) - (ecoule or 0)
if edition.reste <= 0 then
edition = nil
self:Hide()
if UIErrorsFrame then
UIErrorsFrame:AddMessage(ERR_CLIENT_LOCKED_OUT, 1.0, 0.1, 0.1, 1.0)
end
end
end)
local function lancerEdition(nom, icone)
if not edition or not nom or nom == "" then
edition = nil
return
end
edition.nom = nom
edition.icone = icone
-- Deja porte : rien a equiper, on enchaine.
if piecesEnPlace(edition.ancien) then
terminerEdition()
return
end
if UseEquipmentSet then
UseEquipmentSet(edition.ancien)
end
edition.reste = EDITION_DELAI
attenteEdition:Show()
end
-- LE OKAY DE LA FENETRE EST REPRIS, pas greffe. Un greffon passerait APRES
-- le client, qui aurait deja enregistre l'equipement porte sous ce nom --
-- c'est-a-dire tout sauf ce qu'on veut. Hors edition, on lui rend la main
-- telle quelle.
local function reprendreOkay()
local okay = _G["GearManagerDialogPopupOkay"]
if not okay or okay.foreverOkay then
return
end
okay:SetScript("OnClick", function(self, bouton, enfonce)
local popup = _G["GearManagerDialogPopup"]
if edition and popup and popup.name and popup.name ~= "" then
-- LIRE AVANT DE FERMER. GearManagerDialogPopup_OnHide remet
-- popup.name a nil : lire le nom apres l'avoir cachee le rendait
-- vide, et SaveEquipmentSet refusait. Le meme OnHide abandonne
-- l'edition, d'ou le drapeau qui dit que la fermeture vient de
-- nous.
local nom = popup.name
local _, indiceIcone = GetEquipmentSetIconInfo(popup.selectedIcon)
local enCours = edition
fermetureVoulue = true
popup:Hide()
fermetureVoulue = nil
edition = enCours
lancerEdition(nom, indiceIcone)
return
end
if GearManagerDialogPopupOkay_OnClick then
GearManagerDialogPopupOkay_OnClick(self, bouton, enfonce)
end
end)
okay.foreverOkay = true
end
ForeverUI.EquipmentSetEditReclaim = reprendreOkay
-- Ouvrir la fenetre sur un ensemble : c'est l'engrenage qui appelle.
function ForeverUI.EquipmentSetEdit(nom)
if not nom or nom == "" then
return
end
local dialogue = _G["GearManagerDialog"]
if dialogue then
dialogue.selectedSetName = nom
if GearManagerDialog_Update then
GearManagerDialog_Update()
end
end
edition = { ancien = nom }
reprendreOkay()
if GearManagerDialogSaveSet_OnClick then
GearManagerDialogSaveSet_OnClick(_G["GearManagerDialogSaveSet"])
end
-- Le nom est pose explicitement : le client ne remplit son champ que
-- dans RecalculateGearManagerDialogPopup, appele par le seul OnShow.
local champ = _G["GearManagerDialogPopupEditBox"]
if champ then
champ:SetText(nom)
end
end
-- Fermee autrement -- Annuler, Echap -- l'edition est abandonnee.
if hooksecurefunc and type(GearManagerDialogPopup_OnHide) == "function" then
hooksecurefunc("GearManagerDialogPopup_OnHide", function()
if edition and not fermetureVoulue and not attenteEdition:IsShown() then
edition = nil
end
end)
end