Files
moon-well-ui/data/addon/ForeverUI/Bags.lua
T
Adrestie eb681aa9cd Aucun texte en dur : anglais en base, francais, langues extensibles
- socle : Textes.lua (ForeverUI.L) et ForeverUIGlueTextes.lua (G.L) ; les
  textes dans Textes_enUS / Textes_frFR (325 cles en jeu) et
  ForeverUIGlueTextes_enUS / _frFR (41 cles aux ecrans d'accueil) ; une cle
  absente prend l'anglais, une langue sans fichier lit l'anglais ; ajouter
  une langue = un fichier de plus
- les textes que le client 3.3.5 connait se lisent dans ses globales
  (GlobalStrings, GlueStrings en glue) ; les textes de secours sont retires
- tous les fichiers repris : libelles, infobulles, mode edition, messages et
  aide de /fui, erreurs internes ; .toc : Notes + Notes-frFR
- selection des personnages : race et classe traduites ramenees a leur jeton
  par les tables de langue (GetAvailableRaces / GetAvailableClasses font
  planter le client hors de la creation)
- tools/cartes_instances.py : noms des instances dans toutes les langues de
  la LibBabble-Zone de WDM (portails de la carte sur tout client)
- banc : toute cle employee existe en anglais et en francais, memes cles dans
  les deux, bascule frFR / deDE essayee ; chaines 3.3.5 ajoutees au faux client
- README : etat des sections mis a jour, langues, glue, deploiement --glue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 03:22:30 +02:00

1542 lines
53 KiB
Lua

-- ForeverUI : l'interface des sacs.
--
-- RELEVE DES SOURCES -- tout vient du code extrait de camelot, qui pour les
-- sacs se sert du fichier commun (camelot/ContainerFrame.lua ne fait qu'une
-- chose : recadrer le masque rond du portrait).
--
-- mainline/ContainerFrame.lua -- LA GEOMETRIE, chiffre par chiffre
-- CONTAINER_WIDTH = 178, ITEM_SPACING_X = ITEM_SPACING_Y = 5, quatre
-- colonnes, boutons de 37.
-- ContainerFrameMixin:GetFirstButtonOffsetY = 9
-- ContainerFrameMixin:GetPaddingHeight = 9 + 48, et +30 pour le sac a dos
-- (la bande du champ de recherche)
-- ContainerFrameMixin:CalculateHeight = hauteur des rangees + ce
-- remplissage + CalculateExtraHeight, qui vaut la hauteur de la bourse pour
-- le sac a dos.
-- sac ordinaire : premier bouton BOTTOMRIGHT (-7, 9) du cadre.
-- sac a dos : bourse BOTTOMLEFT (8, 8) et BOTTOMRIGHT (-8, 8), haute de
-- 13 ; premier bouton BOTTOMRIGHT sur le TOPRIGHT de la
-- bourse, decale de (0, 4).
-- bourse : ContainerFrameCurrencyBorderTemplate, haut de 17, bouts
-- de 8 x 17 (common-coinbox-left et -right) et milieu tendu.
--
-- mainline/ContainerFrame.xml
-- cadre ContainerFrameTemplate herite de PortraitFrameFlatTemplate :
-- fond plat + encadrement de metal en neuf tranches, jeu
-- HeldBagLayout. Monte par ForeverUI.SetPanelArt.
-- emplacement ContainerFrameItemButtonTemplate, 37 x 37, fond d'un
-- emplacement vide = bags-item-slot64.
--
-- shared/ItemButtonTemplate.lua : SetItemButtonQuality_Base
-- Le contour d'un objet, c'est IconBorder : Interface\Common\WhiteIconFrame
-- teinte par la couleur de qualite de l'objet. Une case vide n'en a pas ;
-- c'est son fond qui se voit.
-- champ BagItemSearchBox, 96 x 18, 15 lettres, TOPLEFT (42, -37)
-- du sac principal (ContainerFrameMixin:SetSearchBoxPoint).
-- bouton de tri BagItemAutoSortButton, 28 x 26, TOPRIGHT (-9, -34),
-- images bags-button-autosort-up et -down, survol
-- Interface\Buttons\ButtonHilight-Square en ADD, 24 x 23
-- centre. Au clic : un son, puis C_Container.SortBags().
-- les deux ne se montrent QUE sur le sac principal
-- (ContainerFrameMixin:UpdateSearchBox).
--
-- mainline/SharedUIPanelTemplates.xml
-- titre TitleContainer de TOPLEFT (58, -1) a TOPRIGHT (-24, -1),
-- haut de 20 ; le texte centre dedans, 5 px sous son haut.
-- portrait 62 x 62, dans l'anneau du coin haut gauche.
--
-- shared/UIPanelTemplatesShared.lua
-- BagSearch_OnTextChanged pousse le texte dans toutes les barres de
-- recherche puis appelle C_Container.SetItemSearch ; BagSearch_OnChar rend
-- la main des que les quatre derniers caracteres sont identiques ;
-- BagSearch_OnHide efface la recherche quand plus aucune barre n'est
-- visible. Ces trois comportements sont repris tels quels.
--
-- CE QUE 3.3.5 N'A PAS, ET COMMENT C'EST FAIT ICI.
-- C_Container.SetItemSearch n'existe pas : le client moderne marque
-- lui-meme chaque objet et le code ne fait que lire `isFiltered`. Ici le
-- tri des correspondances est fait en Lua, sur le nom, le type et le
-- sous-type de l'objet, et l'objet qui ne correspond pas recoit le meme
-- voile que la source (ItemButton : searchOverlay, noir a 80 %).
-- C_Container.SortBags n'existe pas non plus, et son ordre est decide dans
-- le client : il n'est pas lisible. Le rangement est donc refait ici, en
-- deplacant les objets un par un ; l'ordre suit celui que le jeu lui-meme
-- emploie pour ses categories (GetAuctionItemClasses), puis la qualite, le
-- nom et la taille de la pile. CHOIX ASSUME, faute de source.
-- =====================================================================
-- REGLAGES DE L'INTERFACE DES SACS
--
-- RELEVE -- blizzard_uipanels_game/mainline/containerframe.lua, ContainerFrameMixin.
-- Tout ce bloc est recopie de la source ; rien n'y est mesure ni estime.
--
-- CalculateWidth() = CONTAINER_WIDTH, une CONSTANTE (178) -- la
-- largeur ne se deduit PAS de la grille
-- CalculateHeight() = rangees x 37 + (rangees - 1) x 5
-- + GetPaddingHeight() + CalculateExtraHeight()
-- GetPaddingHeight() = GetFirstButtonOffsetY() (9) + 48
-- + 30 sur le sac a dos (ContainerFrameBackpackMixin,
-- "Account for the search box in the backpack")
-- CalculateExtraHeight() = 0 ; + la hauteur de la bourse sur le sac a dos
-- GetInitialItemAnchor() = sac porte : BOTTOMRIGHT du cadre, (-7, 9)
-- sac a dos : BOTTOMRIGHT de la BOURSE, TOPRIGHT (0, 4)
-- UpdateCurrencyFrames() = bourse BOTTOMLEFT (8, 8) / BOTTOMRIGHT (-8, 8)
-- SetSearchBoxPoint() = champ TOPLEFT (42, -37), 96 de large
-- UpdateSearchBox() = tri TOPRIGHT (-9, -34)
-- ContainerFrameItemButtonTemplate (containerframe.xml, ligne 82) : 37 x 37
-- ITEM_SPACING_X = ITEM_SPACING_Y = 5 ; colonnes = 4
--
-- CE QUI FAIT QUE LA FENETRE SUIT SON CONTENU : la hauteur est un CALCUL a
-- partir du nombre de rangees, pas une somme d'ancrages. Le comble (titre,
-- champ de recherche) est une hauteur fixe ancree en HAUT, exactement comme
-- dans la source -- c'est la formule qui s'adapte, pas les ancrages.
-- sac a dos, 16 cases : 163 + (9 + 48 + 30) + 13 = 263
-- sac porte, 16 cases : 163 + (9 + 48) = 220
--
-- En jeu, pour essayer sans rien reinstaller :
-- /fui sacs la hauteur calculee, celle du cadre, le detail
-- /fui sacs emplacement 44 change une valeur et refait la fenetre
-- Quand le reglage convient, il se fige dans ce bloc.
local R = {
-- LA GRILLE
emplacement = 37, -- ContainerFrameItemButtonTemplate
ecartCases = 5, -- ITEM_SPACING_X et ITEM_SPACING_Y
colonnes = 4, -- GetColumns()
largeur = 178, -- CONTAINER_WIDTH
-- LE CALCUL DE LA HAUTEUR
-- ECART ASSUME. camelot donne 9 a GetFirstButtonOffsetY(), mais avec son
-- art de metal pose ici, le bord interieur du bas remonte a 7,5 du bord
-- du cadre : il ne restait que 1,5 sous la derniere rangee, contre 7,5 a
-- gauche et 6 a droite (mesure en jeu, echelle 4/3). Les 6 de plus
-- egalisent l'espace des quatre cotes ; la fenetre grandit d'autant,
-- puisque la formule de la hauteur compte ce nombre.
premierBoutonY = 15, -- camelot 9
premierBoutonX = -7, -- GetInitialItemAnchor()
entete = 48, -- GetPaddingHeight() : "titlebar and attic"
bandeRecherche = 30, -- le meme, + 30 sur le sac a dos
-- LA BOURSE, et la grille qui s'y accroche sur le sac a dos
bourseHauteur = 13, -- UpdateMoneyFrame()
bourseCote = 8, -- UpdateCurrencyFrames()
bourseBas = 14, -- camelot 8, remonte des memes 6
ecartBourseGrille = 4, -- ContainerFrameBackpackMixin:GetInitialItemAnchor()
bourseCadre = 17, -- l'encadre de camelot deborde de la bourse
-- LE SEGMENT DES MONNAIES SUIVIES, sous la bourse.
--
-- RELEVE -- ContainerFrameTokenWatcherMixin:UpdateCurrencyFrames : le
-- segment se pose au BAS de la fenetre, BOTTOMLEFT (8, 8) et
-- BOTTOMRIGHT (-8, 8), et c'est LA BOURSE QUI MONTE -- son BOTTOMLEFT
-- et son BOTTOMRIGHT sur le TOP du segment, (0, 3). Sans monnaie
-- suivie, la bourse reprend le bas, et rien ne change.
-- CalculateExtraHeight ajoute alors la hauteur du segment.
--
-- BackpackTokenFrameTemplate : 17 de haut, encadre par
-- ContainerFrameCurrencyBorderTemplate -- deux bouts de 8 x 17 et un
-- milieu tendu. BackpackTokenTemplate : 50 x 12, icone de 12 ancree
-- RIGHT (4, 1), compte cale a droite jusqu'au bord gauche de l'icone.
-- GetInitialTokenAnchor : RIGHT du segment (-17, -1), et les jetons
-- s'enchainent vers la GAUCHE.
jetonHauteur = 17, -- BackpackTokenFrameTemplate
jetonCote = 8, -- UpdateCurrencyFrames()
jetonEcart = 3, -- ce que la bourse laisse au-dessus du segment
jetonLargeur = 50, -- BackpackTokenTemplate
jetonPiece = 12, -- sa hauteur, et celle de l'icone
jetonIconeX = 4,
jetonIconeY = 1,
jetonDepartX = -17, -- GetInitialTokenAnchor()
jetonDepartY = -1,
-- LE COMBLE
-- Le champ et le tri redescendent des 6 dont la grille est remontee :
-- ancres en haut, ils suivaient sinon le bord superieur, qui s'est
-- eloigne d'autant.
champLargeur = 96, -- SetSearchBoxPoint()
champHauteur = 18,
champX = 42,
champY = -43, -- camelot -37
triLargeur = 28,
triHauteur = 26,
triX = -9, -- UpdateSearchBox()
triY = -40, -- camelot -34
-- LES DECORS DU CADRE
titreGauche = 35, -- SetTitleOffsets(35) : le bord gauche du titre
titreDroite = -24, -- la valeur par defaut de SetTitleOffsets
titreConteneur = -1, -- TitleContainer : TOPLEFT / TOPRIGHT a -1
titreBande = 20, -- sa hauteur
titreTexte = -5, -- TitleText : TOP (0, -5) dans le conteneur
fermeture = 24,
fermetureX = 1,
fermetureY = 0,
-- LE PORTRAIT, a la maniere du client 3.3.5. Celui-ci ne masque JAMAIS
-- une icone : pour ses boutons de minimap il pose un anneau par-dessus
-- (MiniMap-TrackingBorder, une couronne opaque a trou transparent),
-- dessine l'icone assez petite pour tenir dans le trou, et la ROGNE de
-- sa bordure. C'est l'anneau qui fait le rond.
--
-- Ici l'anneau est celui de camelot, mesure au pixel a la taille ou il
-- est dessine : trou net jusqu'a 10,3 du centre, degrade jusqu'a 14,3,
-- metal OPAQUE de 14,3 a 20,4. Pour qu'un carre disparaisse, ses bords
-- doivent couvrir le degrade et ses coins tomber dans le metal :
-- cote / 2 >= 14,3 -> cote >= 28,6
-- (cote / 2) x racine(2) <= 20,4 -> cote <= 28,8
-- 28 est la seule taille qui tienne, et ses coins finissent SOUS le
-- metal -- mieux que les boutons de minimap, dont les coins flottent
-- dans le trou.
portrait = 28,
portraitX = 14, -- le centre de l'anneau, mesure sur son art
portraitY = -17, -- et le centre du portrait de la source
-- L'EMPILEMENT DES SACS -- UpdateContainerFrameAnchors, lignes 1372-1401
ecartSacs = 8, -- CONTAINER_SPACING
ecartColonnes = -11, -- le saut de colonne
bordDroit = 10, -- GetInitialContainerFrameOffsetX, hors barres
bordBas = 85, -- CONTAINER_OFFSET_Y
-- L'ENSEMBLE
echelle = 1, -- 1 = taille de camelot ; 1.25 = un quart de plus
}
ForeverUI = ForeverUI or {}
ForeverUI.BagsSettings = R
-- =====================================================================
local NB_CADRES = NUM_CONTAINER_FRAMES or 13
local SACS = { 0, 1, 2, 3, 4 } -- sac a dos et les quatre sacs portes
-- Lua 5.1 lit les antislashs comme des echappements : on pose le separateur
-- en clair.
local SEP = string.char(92)
local SURVOL_CARRE = "Interface" .. SEP .. "Buttons" .. SEP .. "ButtonHilight-Square"
local CADRE_QUALITE = "Interface" .. SEP .. "ForeverUI" .. SEP .. "common" .. SEP .. "whiteiconframe"
-- RELEVE -- ContainerFrameMixin:UpdateMiscellaneousFrames : le sac a dos
-- porte Inv_misc_bag_08, le trousseau une icone a lui, et un sac porte
-- montre l'icone de l'objet qu'il est.
-- RELEVE -- le rognage que le client applique a toute icone posee dans un
-- anneau : une icone fait 64 px et porte 4 px de bordure sombre.
local BORDURE_ICONE = 4 / 64
local PORTRAIT_SAC_A_DOS = "Interface" .. SEP .. "Icons" .. SEP .. "INV_Misc_Bag_08"
-- ECART : camelot demande "Interface/Icons/ui-hud-actionbar-keyring", qui
-- n'existe pas en 3.3.5 ; le client y range son trousseau ici.
local PORTRAIT_TROUSSEAU = "Interface" .. SEP .. "ContainerFrame" .. SEP .. "KeyRing-Bag-Icon"
-- MESURE SUR LA CAPTURE. camelot pose un cadre sur CHAQUE case, pleine ou
-- vide : gris sombre, bords entre 25 et 49, coins vers 100. L'image porte ces
-- memes valeurs a 255 et 140 : la teinte vaut donc 0,39. La couleur de
-- qualite ne prend le relais qu'a partir de peu commun.
local CADRE_GRIS = 0.39
local QUALITE_TEINTEE = 2
local L = ForeverUI.L
local RANGER = L.BAGS_CLEANUP
local RANGER_AIDE = L.BAGS_CLEANUP_TOOLTIP
ForeverUI = ForeverUI or {}
-- ------------------------------------------------------------ les cadres
local cadres = {}
local function habillerBouton(bouton)
if not bouton or bouton.foreverSkinned then
return
end
local nom = bouton:GetName()
-- RELEVE -- SetItemButtonTexture_Base (itembuttontemplate.lua) : quand la
-- case est vide, emptyBackgroundAtlas est pose SUR L'ICONE elle-meme, et
-- non derriere. Il n'y a jamais deux textures superposees. L'art dore de
-- 3.3.5, lui, s'efface : camelot n'a pas de cadre autour d'une case.
local normale = bouton:GetNormalTexture()
if normale then
normale:SetAlpha(0)
end
local survol = bouton:GetHighlightTexture()
if survol then
survol:SetTexture(SURVOL_CARRE)
survol:SetBlendMode("ADD")
survol:ClearAllPoints()
survol:SetAllPoints(bouton)
end
local icone = _G[nom .. "IconTexture"]
if icone then
icone:ClearAllPoints()
icone:SetAllPoints(bouton)
icone:SetTexCoord(0, 1, 0, 1)
icone:SetDrawLayer("BORDER")
end
-- Le contour d'un objet : WhiteIconFrame teinte par sa qualite, comme
-- SetItemButtonQuality_Base le fait. Une case vide n'en a pas.
local contour = bouton:CreateTexture(nil, "OVERLAY")
contour:SetTexture(CADRE_QUALITE)
contour:SetAllPoints(bouton)
contour:SetVertexColor(CADRE_GRIS, CADRE_GRIS, CADRE_GRIS)
bouton.foreverContour = contour
-- Le voile de recherche : la source le declare sur le bouton d'objet
-- lui-meme, noir a 80 %, sur toute la surface.
local voile = bouton:CreateTexture(nil, "OVERLAY")
voile:SetTexture(0, 0, 0, 0.8)
voile:SetAllPoints(bouton)
voile:Hide()
bouton.foreverVoile = voile
bouton.foreverSkinned = true
end
local function habillerCadre(cadre)
if not cadre or cadre.foreverSkinned then
return
end
local nom = cadre:GetName()
-- L'habillage d'epoque : quatre morceaux empiles, plus la version a un
-- seul emplacement. ContainerFrame_GenerateFrame les reaffiche et leur
-- change de texture a chaque ouverture, mais ne touche pas leur alpha.
for _, suffixe in ipairs({ "BackgroundTop", "BackgroundMiddle1", "BackgroundMiddle2",
"BackgroundBottom", "Background1Slot" }) do
local texture = _G[nom .. suffixe]
if texture then
texture:SetAlpha(0)
end
end
ForeverUI.SetPanelArt(cadre)
-- L'ICONE SE GLISSE ENTRE LE FOND ET LE CONTOUR. Elle est donc creee
-- APRES l'art du panneau, dans le meme calque que son fond -- deux
-- regions d'un meme calque ne sont ordonnees que par leur ordre de
-- creation, celle-ci passe donc au-dessus -- et le metal, en OVERLAY,
-- reste au-dessus d'elle.
--
-- L'ordre compte : sous le fond, qui est OPAQUE, l'icone ne se verrait
-- meme pas par le trou de l'anneau. C'est le contour seul qui la
-- decoupe, exactement comme le cadre de sac de 3.3.5 decoupe la sienne
-- (UI-Bag-4x4.blp porte un trou circulaire de 32 px, rien n'y est
-- masque). Taille et place sont celles de la source :
-- SetPortraitTextureSizeAndOffset(36, -4, 1).
local ancien = _G[nom .. "Portrait"]
if ancien then
ancien:SetAlpha(0)
end
-- LE CALQUE DECIDE, PAS L'ORDRE DE CREATION. Le fond du panneau est en
-- BACKGROUND et le metal en OVERLAY : entre les deux, BORDER est libre.
-- Une texture posee la est TOUJOURS au-dessus du fond et TOUJOURS sous
-- le metal, quel que soit l'ordre dans lequel les morceaux ont ete
-- crees. C'est ce qui manquait : le fond du panneau (corps, ancre a 20
-- px du haut) coupait l'anneau en deux et passait devant l'icone.
local portrait = cadre:CreateTexture(nil, "BORDER")
portrait:SetWidth(R.portrait)
portrait:SetHeight(R.portrait)
portrait:SetPoint("CENTER", cadre, "TOPLEFT", R.portraitX, R.portraitY)
cadre.foreverPortrait = portrait
-- RELEVE -- TitledPanelMixin:SetTitleOffsets, que ContainerFrame appelle
-- avec 35, et le modele TitleContainer : un CADRE de 20 de haut allant de
-- 35 a la largeur moins 24, pose a -1, contenant un TitleText ancre TOP
-- (0, -5), LEFT et RIGHT. Le titre n'est donc PAS centre sur la fenetre :
-- il l'est entre le portrait et le bouton de fermeture, dont la largeur
-- est ainsi prise en compte.
--
-- C'est un cadre FILS, et il le faut : le metal de HeldBagLayout est en
-- OVERLAY et couvrirait une simple region du cadre. Le titre du client,
-- qui en est une, s'efface.
local ancienTitre = _G[nom .. "Name"]
if ancienTitre then
ancienTitre:SetAlpha(0)
end
local bandeTitre = CreateFrame("Frame", nil, cadre)
bandeTitre:SetFrameLevel(cadre:GetFrameLevel() + 2)
bandeTitre:SetHeight(R.titreBande)
bandeTitre:SetPoint("TOPLEFT", cadre, "TOPLEFT", R.titreGauche, R.titreConteneur)
bandeTitre:SetPoint("TOPRIGHT", cadre, "TOPRIGHT", R.titreDroite, R.titreConteneur)
local titre = bandeTitre:CreateFontString(nil, "OVERLAY", "GameFontNormal")
titre:SetPoint("TOP", bandeTitre, "TOP", 0, R.titreTexte)
titre:SetPoint("LEFT", bandeTitre, "LEFT")
titre:SetPoint("RIGHT", bandeTitre, "RIGHT")
titre:SetJustifyH("CENTER")
cadre.foreverBandeTitre = bandeTitre
cadre.foreverTitre = titre
-- Le bouton de fermeture : 24 x 24, TOPRIGHT (1, 0), le X rouge des
-- panneaux modernes (UIPanelCloseButtonNoScripts, atlas RedButton-Exit).
local fermer = _G[nom .. "CloseButton"]
if fermer then
fermer:SetWidth(R.fermeture)
fermer:SetHeight(R.fermeture)
fermer:ClearAllPoints()
fermer:SetPoint("TOPRIGHT", cadre, "TOPRIGHT", R.fermetureX, R.fermetureY)
for atlas, methode in pairs({ ["redbutton-exit"] = "GetNormalTexture",
["redbutton-exit-pressed"] = "GetPushedTexture",
["redbutton-exit-disabled"] = "GetDisabledTexture",
["redbutton-highlight"] = "GetHighlightTexture" }) do
local texture = fermer[methode] and fermer[methode](fermer)
if texture then
ForeverUI.SetAtlas(texture, atlas, true)
texture:ClearAllPoints()
texture:SetAllPoints(fermer)
if atlas == "redbutton-highlight" then
texture:SetBlendMode("ADD")
end
end
end
end
for index = 1, MAX_CONTAINER_ITEMS do
habillerBouton(_G[nom .. "Item" .. index])
end
cadre.foreverSkinned = true
table.insert(cadres, cadre)
end
-- ---------------------------------------------------------- la recherche
local Recherche = { texte = "" }
ForeverUI.BagSearch = Recherche
-- Le nom d'un objet, meme quand le client ne l'a pas encore en cache : le
-- lien porte toujours le nom entre crochets.
local function nomDeLObjet(lien)
if not lien then
return nil
end
local nom, _, _, _, _, type_, sousType = GetItemInfo(lien)
if nom then
return nom, type_, sousType
end
return string.match(lien, "%[(.+)%]")
end
function Recherche.Correspond(lien)
if Recherche.texte == "" then
return true
end
if not lien then
return false
end
local nom, type_, sousType = nomDeLObjet(lien)
for _, champ in ipairs({ nom, type_, sousType }) do
if champ and string.find(string.lower(champ), Recherche.texte, 1, true) then
return true
end
end
return false
end
-- Le voile de recherche ET le contour de qualite se decident au meme moment :
-- les deux dependent de ce que la case contient.
-- RELEVE -- SetItemButtonTexture_Base : une seule texture porte soit l'objet,
-- soit le fond de case vide. 3.3.5 masque l'icone d'une case vide ; on la
-- remontre avec l'element d'atlas, et on rend ses coordonnees pleines des
-- qu'un objet revient.
local function poserIcone(bouton, texture)
local icone = _G[bouton:GetName() .. "IconTexture"]
if not icone then
return
end
if texture then
icone:SetTexture(texture)
icone:SetTexCoord(0, 1, 0, 1)
else
ForeverUI.SetAtlas(icone, "bags-item-slot64", true)
end
icone:Show()
end
function Recherche.Appliquer(cadre)
local nom = cadre:GetName()
local sac = cadre:GetID()
for index = 1, cadre.size or 0 do
local bouton = _G[nom .. "Item" .. index]
if bouton and bouton.foreverVoile then
local emplacement = bouton:GetID()
local lien = GetContainerItemLink(sac, emplacement)
poserIcone(bouton, GetContainerItemInfo(sac, emplacement))
if lien and not Recherche.Correspond(lien) then
bouton.foreverVoile:Show()
else
bouton.foreverVoile:Hide()
end
-- Le cadre est toujours la ; seule sa teinte change, et seulement
-- a partir de peu commun.
local contour = bouton.foreverContour
if contour then
local qualite = select(4, GetContainerItemInfo(sac, emplacement))
local couleur = lien and qualite and qualite >= QUALITE_TEINTEE
and ITEM_QUALITY_COLORS and ITEM_QUALITY_COLORS[qualite]
if couleur then
contour:SetVertexColor(couleur.r, couleur.g, couleur.b)
else
contour:SetVertexColor(CADRE_GRIS, CADRE_GRIS, CADRE_GRIS)
end
contour:Show()
end
end
end
end
function Recherche.Tout()
for _, cadre in ipairs(cadres) do
if cadre:IsShown() then
Recherche.Appliquer(cadre)
end
end
end
function Recherche.Set(texte)
Recherche.texte = string.lower(texte or "")
Recherche.Tout()
end
-- -------------------------------------------------------------- le tri
--
-- L'ordre des categories est celui du jeu lui-meme : GetAuctionItemClasses
-- rend les classes d'objets dans l'ordre ou le client les presente.
local Tri = { file = {}, actif = false }
ForeverUI.BagSort = Tri
local rangClasse
local function construireRangs()
if rangClasse then
return
end
rangClasse = {}
if GetAuctionItemClasses then
local classes = { GetAuctionItemClasses() }
for index, nom in ipairs(classes) do
rangClasse[nom] = index
end
end
end
local function lireCase(sac, emplacement)
local lien = GetContainerItemLink(sac, emplacement)
if not lien then
return nil
end
local _, nombre, verrouille = GetContainerItemInfo(sac, emplacement)
local nom, _, qualite, _, _, type_, sousType, pileMax = GetItemInfo(lien)
return {
lien = lien,
nombre = nombre or 1,
verrouille = verrouille,
nom = nom or string.match(lien, "%[(.+)%]") or lien,
qualite = qualite or 0,
classe = rangClasse[type_ or ""] or 99,
sousType = sousType or "",
pileMax = pileMax or 1,
}
end
local function avant(a, b)
if a.classe ~= b.classe then
return a.classe < b.classe
end
if a.sousType ~= b.sousType then
return a.sousType < b.sousType
end
if a.qualite ~= b.qualite then
return a.qualite > b.qualite
end
if a.nom ~= b.nom then
return a.nom < b.nom
end
return a.nombre > b.nombre
end
-- L'etat courant des sacs : une case par emplacement, dans l'ordre.
local function relever()
local cases = {}
for _, sac in ipairs(SACS) do
for emplacement = 1, (GetContainerNumSlots(sac) or 0) do
table.insert(cases, {
sac = sac,
emplacement = emplacement,
objet = lireCase(sac, emplacement),
})
end
end
return cases
end
local function verrouille(case)
local _, _, estVerrouille = GetContainerItemInfo(case.sac, case.emplacement)
return estVerrouille
end
-- Une seule action par passage : le serveur doit confirmer chaque
-- deplacement avant le suivant, sinon la case est encore verrouillee.
local function uneEtape()
local cases = relever()
-- 1. reunir les piles entamees du meme objet
for i = 1, #cases do
local a = cases[i].objet
if a and a.nombre < a.pileMax then
for j = i + 1, #cases do
local b = cases[j].objet
if b and b.lien == a.lien and b.nombre < b.pileMax then
if verrouille(cases[i]) or verrouille(cases[j]) then
return true
end
PickupContainerItem(cases[j].sac, cases[j].emplacement)
PickupContainerItem(cases[i].sac, cases[i].emplacement)
return true
end
end
end
end
-- 2. mettre en ordre : la case i doit porter le i-eme objet trie
local objets = {}
for _, case in ipairs(cases) do
if case.objet then
table.insert(objets, case.objet)
end
end
table.sort(objets, avant)
for i = 1, #cases do
local voulu = objets[i]
local present = cases[i].objet
local memeObjet = (voulu == nil and present == nil)
or (voulu and present and voulu.lien == present.lien and voulu.nombre == present.nombre)
if not memeObjet then
if voulu == nil then
return false -- plus rien a placer
end
-- trouver la case qui porte l'objet voulu
for j = i + 1, #cases do
local candidat = cases[j].objet
if candidat and candidat.lien == voulu.lien and candidat.nombre == voulu.nombre then
if verrouille(cases[i]) or verrouille(cases[j]) then
return true
end
PickupContainerItem(cases[j].sac, cases[j].emplacement)
PickupContainerItem(cases[i].sac, cases[i].emplacement)
return true
end
end
return false -- introuvable : on s'arrete plutot que tourner
end
end
return false -- tout est en place
end
local MAX_ETAPES = 400
local tempsDepuisEtape = 0
local horloge = CreateFrame("Frame", "ForeverUIBagSortTicker")
horloge:Hide()
horloge:SetScript("OnUpdate", function(self, elapsed)
tempsDepuisEtape = tempsDepuisEtape + elapsed
if tempsDepuisEtape < 0.1 then
return
end
tempsDepuisEtape = 0
Tri.etapes = (Tri.etapes or 0) + 1
if Tri.etapes > MAX_ETAPES or not uneEtape() then
Tri.actif = false
self:Hide()
Recherche.Tout()
end
end)
function Tri.Lancer()
if Tri.actif then
return
end
if CursorHasItem() then
ClearCursor()
end
construireRangs()
Tri.actif = true
Tri.etapes = 0
tempsDepuisEtape = 0
horloge:Show()
end
-- ------------------------------------------- le champ et le bouton de tri
local champ = CreateFrame("EditBox", "ForeverUIBagSearchBox", UIParent, "InputBoxTemplate")
champ:SetWidth(R.champLargeur)
champ:SetHeight(R.champHauteur)
champ:SetAutoFocus(false)
champ:SetMaxLetters(15)
champ:SetTextInsets(16, 20, 0, 0)
champ:Hide()
local loupe = champ:CreateTexture(nil, "OVERLAY")
ForeverUI.SetAtlas(loupe, "common-search-magnifyingglass", true)
loupe:SetWidth(10)
loupe:SetHeight(10)
loupe:SetPoint("LEFT", champ, "LEFT", 1, -1)
local invite = champ:CreateFontString(nil, "ARTWORK", "GameFontDisableSmall")
invite:SetPoint("LEFT", champ, "LEFT", 16, 0)
invite:SetText(SEARCH)
local effacer = CreateFrame("Button", nil, champ)
effacer:SetWidth(17)
effacer:SetHeight(17)
effacer:SetPoint("RIGHT", champ, "RIGHT", -3, 0)
effacer:Hide()
local croix = effacer:CreateTexture(nil, "ARTWORK")
ForeverUI.SetAtlas(croix, "common-search-clearbutton", true)
croix:SetWidth(10)
croix:SetHeight(10)
croix:SetPoint("TOPLEFT", effacer, "TOPLEFT", 3, -3)
croix:SetAlpha(0.5)
effacer:SetScript("OnEnter", function() croix:SetAlpha(1) end)
effacer:SetScript("OnLeave", function() croix:SetAlpha(0.5) end)
effacer:SetScript("OnClick", function()
champ:SetText("")
champ:ClearFocus()
end)
local function majChamp()
local texte = champ:GetText() or ""
if texte == "" and not champ:HasFocus() then
invite:Show()
else
invite:Hide()
end
if texte == "" then
effacer:Hide()
else
effacer:Show()
end
end
champ:SetScript("OnTextChanged", function(self)
majChamp()
Recherche.Set(self:GetText())
end)
champ:SetScript("OnEditFocusGained", majChamp)
champ:SetScript("OnEditFocusLost", majChamp)
champ:SetScript("OnEscapePressed", function(self) self:ClearFocus() end)
champ:SetScript("OnEnterPressed", function(self) self:ClearFocus() end)
-- RELEVE -- BagSearch_OnChar : quatre caracteres identiques a la suite et le
-- champ rend la main, pour ne pas bloquer un joueur qui martele une touche.
champ:SetScript("OnChar", function(self)
local texte = self:GetText() or ""
if string.len(texte) >= 4 then
local repete = true
for i = 1, 3 do
if string.sub(texte, -i, -i) ~= string.sub(texte, -1 - i, -1 - i) then
repete = false
break
end
end
if repete then
self:ClearFocus()
end
end
end)
local boutonTri = CreateFrame("Button", "ForeverUIBagSortButton", UIParent)
boutonTri:SetWidth(R.triLargeur)
boutonTri:SetHeight(R.triHauteur)
boutonTri:Hide()
-- PIEGE 3.3.5. SetNormalTexture et ses soeurs ne prennent qu'un CHEMIN de
-- fichier ; leur passer un objet texture, comme le fait le client moderne,
-- leve une erreur -- et une erreur au premier niveau d'un fichier abandonne
-- TOUT ce qui suit. On pose donc le chemin de la feuille, puis on regle
-- l'atlas sur la texture que le bouton vient de creer.
local function poserEtat(bouton, poser, obtenir, atlas, largeur, hauteur, add)
local e = ForeverUI.AtlasEntry(atlas)
if not e then
return nil
end
bouton[poser](bouton, e[1])
local texture = bouton[obtenir](bouton)
if not texture then
return nil
end
ForeverUI.SetAtlas(texture, atlas, true)
texture:ClearAllPoints()
if largeur then
texture:SetWidth(largeur)
texture:SetHeight(hauteur)
texture:SetPoint("CENTER")
else
texture:SetAllPoints(bouton)
end
if add then
texture:SetBlendMode("ADD")
end
return texture
end
poserEtat(boutonTri, "SetNormalTexture", "GetNormalTexture", "bags-button-autosort-up")
poserEtat(boutonTri, "SetPushedTexture", "GetPushedTexture", "bags-button-autosort-down")
-- Le survol est un fichier du client, pas un element d'atlas : 24 x 23 centre.
boutonTri:SetHighlightTexture(SURVOL_CARRE)
local triSurvol = boutonTri:GetHighlightTexture()
if triSurvol then
triSurvol:SetBlendMode("ADD")
triSurvol:ClearAllPoints()
triSurvol:SetWidth(24)
triSurvol:SetHeight(23)
triSurvol:SetPoint("CENTER")
end
boutonTri:SetScript("OnClick", function()
Tri.Lancer()
end)
boutonTri:SetScript("OnEnter", function(self)
GameTooltip:SetOwner(self, "ANCHOR_LEFT")
GameTooltip:SetText(RANGER, 1, 1, 1)
GameTooltip:AddLine(RANGER_AIDE, nil, nil, nil, true)
GameTooltip:Show()
end)
boutonTri:SetScript("OnLeave", function()
GameTooltip:Hide()
end)
-- ------------------------------------------------------------ les mesures
-- RELEVE -- ContainerFrameMixin:GetRows, CalculateWidth, CalculateHeight,
-- GetPaddingHeight, CalculateExtraHeight, et leurs surcharges du sac a dos.
-- C'est LE calcul qui fait suivre la fenetre a son contenu : la hauteur se
-- deduit du nombre de rangees, jamais d'une somme d'ancrages.
local function mesures(cadre)
local taille = cadre.size or 0
local m = { sacADos = cadre:GetID() == 0 }
m.rangees = math.ceil(taille / R.colonnes) -- GetRows()
m.grille = m.rangees * R.emplacement + (m.rangees - 1) * R.ecartCases
-- GetPaddingHeight() : le bas du premier bouton, plus le titre et le
-- comble ; le sac a dos ajoute la bande du champ de recherche.
m.comble = R.premierBoutonY + R.entete
if m.sacADos then
m.comble = m.comble + R.bandeRecherche
end
-- CalculateExtraHeight() : la bourse, sur le sac a dos seulement, plus
-- le segment des monnaies suivies quand il y en a.
m.jetons = m.sacADos and ForeverUI.BagsWatchedCount() or 0
m.extra = m.sacADos and R.bourseHauteur or 0
if m.jetons > 0 then
m.extra = m.extra + R.jetonHauteur + R.jetonEcart
end
m.hauteur = m.grille + m.comble + m.extra -- CalculateHeight()
m.largeur = R.largeur -- CalculateWidth()
return m
end
-- RELEVE -- ContainerFrameMixin:UpdateSearchBox : les deux ne se montrent que-- RELEVE -- ContainerFrameMixin:UpdateSearchBox : les deux ne se montrent que
-- sur le sac principal, le champ en TOPLEFT (42, -37) et le bouton en
-- TOPRIGHT (-9, -34).
local function poserOutils()
local hote
for _, cadre in ipairs(cadres) do
if cadre:IsShown() and cadre:GetID() == 0 then
hote = cadre
break
end
end
if not hote then
champ:Hide()
boutonTri:Hide()
-- BagSearch_OnHide : plus de barre visible, la recherche s'efface.
if champ:GetText() ~= "" then
champ:SetText("")
end
return
end
-- Les deux sont ancres en HAUT, comme dans la source : ils occupent le
-- comble, dont la hauteur est fixe. C'est la formule de CalculateHeight
-- qui adapte la fenetre, pas ces deux ancrages.
champ:SetParent(hote)
champ:ClearAllPoints()
champ:SetWidth(R.champLargeur)
champ:SetHeight(R.champHauteur)
champ:SetPoint("TOPLEFT", hote, "TOPLEFT", R.champX, R.champY)
champ:Show()
boutonTri:SetParent(hote)
boutonTri:ClearAllPoints()
boutonTri:SetWidth(R.triLargeur)
boutonTri:SetHeight(R.triHauteur)
boutonTri:SetPoint("TOPRIGHT", hote, "TOPRIGHT", R.triX, R.triY)
boutonTri:Show()
end
-- L'encadre de la bourse : deux bouts et un milieu tendu, 17 de haut.
local function habillerBourse(bourse)
if not bourse or bourse.foreverBorde then
return
end
bourse:SetHeight(R.bourseHauteur)
local gauche = bourse:CreateTexture(nil, "BACKGROUND")
if not ForeverUI.SetAtlas(gauche, "common-coinbox-left", true) then
gauche:Hide()
return
end
gauche:SetWidth(R.bourseCadre / 2)
gauche:SetHeight(R.bourseCadre)
gauche:SetPoint("LEFT", bourse, "LEFT", 0, 0)
local droite = bourse:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(droite, "common-coinbox-right", true)
droite:SetWidth(R.bourseCadre / 2)
droite:SetHeight(R.bourseCadre)
droite:SetPoint("RIGHT", bourse, "RIGHT", 0, 0)
local milieu = bourse:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(milieu, "_common-coinbox-center", true)
milieu:SetPoint("TOPLEFT", gauche, "TOPRIGHT")
milieu:SetPoint("BOTTOMRIGHT", droite, "BOTTOMLEFT")
bourse.foreverBorde = true
end
-- ------------------------------------------- les monnaies suivies, sous la bourse
--
-- CE QUE 3.3.5 DONNE. GetBackpackCurrencyInfo(i) rend nom, compte,
-- typeSpecial, icone et identifiant d'objet, pour i de 1 a
-- MAX_WATCHED_TOKENS -- trois. Le client s'en sert lui-meme dans
-- BackpackTokenFrame_Update.
--
-- DEUX MONNAIES ONT UNE ICONE A PART, la meme regle que l'onglet des
-- monnaies : typeSpecial 1 les points d'arene, 2 les points d'honneur, ceux-
-- ci suivant la faction et rognes a 0,03125 .. 0,59375.
--
-- LE SEGMENT DU CLIENT SE TAIT. BackpackTokenFrame a son propre art et, pire,
-- ManageBackpackTokenFrame REPOSE LA HAUTEUR du sac -- BACKPACK_HEIGHT plus
-- 22 -- ce qui defait la notre. On le masque, et on se greffe sur cette
-- fonction pour repasser derriere elle.
local JETON_ARENE = "Interface\\PVPFrame\\PVP-ArenaPoints-Icon"
local JETON_HONNEUR = "Interface\\TargetingFrame\\UI-PVP-%s"
local JETON_COIN, JETON_COTE = 0.03125, 0.59375
-- LA TAILLE DES CHIFFRES DU SEGMENT.
--
-- A LA DEMANDE, et non d'apres la source : camelot ecrit son compte en
-- GameFontHighlightSmall. Ici c'est GameFontHighlight, d'un cran au-dessus.
-- Celui de la BOURSE ne bouge pas : il appartient au cadre d'argent du
-- client, que nous ne touchons pas.
--
-- ET LE COMPTE SE CENTRE. camelot l'ancre par son TOPLEFT, ce qui convient a
-- une police plus petite que le jeton ; avec celle-ci, le texte depassait
-- vers le bas et ne s'alignait plus sur l'icone. Deux ancres horizontales --
-- LEFT et RIGHT -- le bornent comme avant ET le centrent en hauteur.
local JETON_POLICE = "GameFontHighlight"
local function lireJetons()
local liste = {}
local maximum = MAX_WATCHED_TOKENS or 3
for rang = 1, maximum do
local nom, compte, special, icone = nil, nil, nil, nil
if GetBackpackCurrencyInfo then
nom, compte, special, icone = GetBackpackCurrencyInfo(rang)
end
if nom then
liste[#liste + 1] = {
nom = nom, compte = compte or 0,
special = special, icone = icone,
}
end
end
return liste
end
-- COMBIEN DE MONNAIES SUIVIES. La hauteur de la fenetre en depend : elle se
-- calcule AVANT que le segment ne soit pose, donc depuis les donnees et non
-- depuis ce qui est a l'ecran.
function ForeverUI.BagsWatchedCount()
return #lireJetons()
end
local function poserIconeJeton(texture, donnees)
if donnees.special == 1 then
texture:SetTexture(JETON_ARENE)
texture:SetTexCoord(0, 1, 0, 1)
elseif donnees.special == 2 then
local faction = UnitFactionGroup and UnitFactionGroup("player")
if faction then
texture:SetTexture(string.format(JETON_HONNEUR, faction))
texture:SetTexCoord(JETON_COIN, JETON_COTE, JETON_COIN, JETON_COTE)
else
texture:SetTexture("")
texture:SetTexCoord(0, 1, 0, 1)
end
else
texture:SetTexture(donnees.icone or "")
texture:SetTexCoord(0, 1, 0, 1)
end
end
local function monterSegment(cadre)
if cadre.foreverSegment then
return cadre.foreverSegment
end
local segment = CreateFrame("Frame", "ForeverUIBagTokens", cadre)
segment:SetHeight(R.jetonHauteur)
-- L'ENCADRE : deux bouts de 8 x 17 et un milieu tendu, le meme
-- decoupage que la bourse.
local gauche = segment:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(gauche, "common-currencybox-left", true)
gauche:SetWidth(R.jetonCote)
gauche:SetHeight(R.jetonHauteur)
gauche:SetPoint("LEFT", segment, "LEFT", 0, 0)
local droite = segment:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(droite, "common-currencybox-right", true)
droite:SetWidth(R.jetonCote)
droite:SetHeight(R.jetonHauteur)
droite:SetPoint("RIGHT", segment, "RIGHT", 0, 0)
local milieu = segment:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(milieu, "_common-currencybox-center", true)
milieu:SetPoint("TOPLEFT", gauche, "TOPRIGHT")
milieu:SetPoint("BOTTOMRIGHT", droite, "BOTTOMLEFT")
segment.jetons = {}
for rang = 1, (MAX_WATCHED_TOKENS or 3) do
local jeton = CreateFrame("Button", "ForeverUIBagToken" .. rang, segment)
jeton:SetWidth(R.jetonLargeur)
jeton:SetHeight(R.jetonPiece)
local icone = jeton:CreateTexture(nil, "ARTWORK")
icone:SetWidth(R.jetonPiece)
icone:SetHeight(R.jetonPiece)
icone:SetPoint("RIGHT", jeton, "RIGHT", R.jetonIconeX, R.jetonIconeY)
jeton.icone = icone
local compte = jeton:CreateFontString(nil, "ARTWORK", JETON_POLICE)
compte:SetJustifyH("RIGHT")
compte:SetPoint("LEFT", jeton, "LEFT", 0, 0)
compte:SetPoint("RIGHT", icone, "LEFT", 0, 0)
jeton.compte = compte
-- Les jetons s'enchainent vers la GAUCHE depuis le bord droit.
if rang == 1 then
jeton:SetPoint("RIGHT", segment, "RIGHT", R.jetonDepartX, R.jetonDepartY)
else
jeton:SetPoint("RIGHT", segment.jetons[rang - 1], "LEFT", 0, 0)
end
jeton:Hide()
segment.jetons[rang] = jeton
end
cadre.foreverSegment = segment
return segment
end
-- LE SEGMENT SUIT LES DONNEES : rend le nombre de monnaies posees.
local function majSegment(cadre)
local segment = monterSegment(cadre)
local liste = lireJetons()
for rang, jeton in ipairs(segment.jetons) do
local donnees = liste[rang]
if donnees then
poserIconeJeton(jeton.icone, donnees)
jeton.compte:SetText(donnees.compte)
jeton:Show()
else
jeton:Hide()
end
end
if #liste > 0 then
segment:Show()
else
segment:Hide()
end
return #liste
end
-- RELEVE -- 3.3.5 ContainerFrame_GenerateFrame : le premier bouton porte le
-- coin BAS DROIT de la grille, les suivants s'enchainent vers la gauche puis
-- vers le haut, avec 4 px entre deux rangees. camelot en met 5 : on repose
-- donc aussi le premier bouton de chaque rangee.
-- RELEVE -- ContainerFrameMixin:UpdateFrameSize, GetInitialItemAnchor,
-- GetAnchorLayout (grille BottomRightToTopLeft) et, pour le sac a dos,
-- ContainerFrameBackpackMixin:GetInitialItemAnchor + UpdateCurrencyFrames.
-- RELEVE -- ContainerFrameMixin:UpdateName et UpdateMiscellaneousFrames.
local function majEntete(cadre)
-- UpdateName : le nom vient du SAC, il n'est jamais ecrit ici. Le
-- trousseau n'en a pas -- GetBagName(-2) ne rend rien -- et on reprend
-- alors celui que le client a pose sur son propre titre.
local titre = cadre.foreverTitre
if titre then
local nomDuSac = GetBagName and GetBagName(cadre:GetID())
if not nomDuSac or nomDuSac == "" then
local ancienTitre = _G[cadre:GetName() .. "Name"]
nomDuSac = ancienTitre and ancienTitre:GetText() or ""
end
titre:SetText(nomDuSac)
end
-- UpdateMiscellaneousFrames : sac a dos, trousseau, ou l'icone de l'objet
-- que le sac est.
local portrait = cadre.foreverPortrait
if portrait then
local id = cadre:GetID()
local texture
if id == 0 then
texture = PORTRAIT_SAC_A_DOS
elseif id == (KEYRING_CONTAINER or -2) then
texture = PORTRAIT_TROUSSEAU
elseif ContainerIDToInventoryID then
texture = GetInventoryItemTexture("player", ContainerIDToInventoryID(id))
end
portrait:SetTexture(texture)
-- LE ROGNAGE DU CLIENT. Une icone de WoW est un carre de 64 px
-- borde de 4 px sombres ; 4 / 64 = 0,0625. Le client retire cette
-- bordure partout ou il pose une icone dans un anneau, ce qui fait
-- remplir le disque par le dessin au lieu de son cadre.
portrait:SetTexCoord(BORDURE_ICONE, 1 - BORDURE_ICONE,
BORDURE_ICONE, 1 - BORDURE_ICONE)
end
end
local function poserGrille(cadre)
majEntete(cadre)
local taille = cadre.size or 0
if taille <= 1 then
return -- le cadeau a un seul emplacement garde sa forme
end
local nom = cadre:GetName()
local m = mesures(cadre)
local bourse = _G[nom .. "MoneyFrame"]
-- UpdateFrameSize() : la fenetre prend la mesure calculee.
cadre:SetScale(R.echelle)
cadre:SetWidth(m.largeur)
cadre:SetHeight(m.hauteur)
-- UpdateFrameSize appelle NineSliceUtil.UpdateCornerCropping juste apres
-- SetSize : sur une fenetre courte, les coins se chevauchent sinon.
if ForeverUI.UpdatePanelCorners then
ForeverUI.UpdatePanelCorners(cadre)
end
-- TEMOIN. On relit la hauteur DANS LA FOULEE. Deux cas se distinguent
-- ainsi, et un seul chiffre les separe :
-- la relecture ne rend pas ce qu'on vient de poser -> ce sont les
-- ancrages du cadre qui decident de sa hauteur, SetHeight est ignore
-- la relecture est bonne mais /fui sacs montre autre chose plus tard
-- -> quelqu'un repose la taille apres nous
cadre.foreverDemande = m.hauteur
cadre.foreverRelue = cadre:GetHeight()
cadre.foreverAncrages = cadre:GetNumPoints()
-- UpdateCurrencyFrames() : le segment des monnaies prend le bas, la
-- bourse monte au-dessus de lui, et la grille s'accroche a la bourse.
-- Sans monnaie suivie, la bourse reprend le bas et rien ne change.
if m.sacADos and bourse then
habillerBourse(bourse)
local poses = majSegment(cadre)
local segment = cadre.foreverSegment
bourse:ClearAllPoints()
if poses > 0 and segment then
segment:ClearAllPoints()
segment:SetPoint("BOTTOMLEFT", cadre, "BOTTOMLEFT",
R.jetonCote, R.bourseBas)
segment:SetPoint("BOTTOMRIGHT", cadre, "BOTTOMRIGHT",
-R.jetonCote, R.bourseBas)
bourse:SetPoint("BOTTOMLEFT", segment, "TOPLEFT", 0, R.jetonEcart)
bourse:SetPoint("BOTTOMRIGHT", segment, "TOPRIGHT", 0, R.jetonEcart)
else
bourse:SetPoint("BOTTOMLEFT", cadre, "BOTTOMLEFT",
R.bourseCote, R.bourseBas)
bourse:SetPoint("BOTTOMRIGHT", cadre, "BOTTOMRIGHT",
-R.bourseCote, R.bourseBas)
end
bourse:Show()
end
-- GetAnchorLayout() : BottomRightToTopLeft, 4 colonnes, 5 d'ecart. Toute
-- la grille est reposee, 3.3.5 employant ses propres ecarts.
for index = 1, taille do
local bouton = _G[nom .. "Item" .. index]
if bouton then
bouton:SetWidth(R.emplacement)
bouton:SetHeight(R.emplacement)
bouton:ClearAllPoints()
if index == 1 then
if m.sacADos and bourse then
-- ContainerFrameBackpackMixin:GetInitialItemAnchor()
bouton:SetPoint("BOTTOMRIGHT", bourse, "TOPRIGHT",
0, R.ecartBourseGrille)
else
-- ContainerFrameMixin:GetInitialItemAnchor()
bouton:SetPoint("BOTTOMRIGHT", cadre, "BOTTOMRIGHT",
R.premierBoutonX, R.premierBoutonY)
end
elseif math.fmod(index - 1, R.colonnes) == 0 then
bouton:SetPoint("BOTTOMRIGHT", _G[nom .. "Item" .. (index - R.colonnes)],
"TOPRIGHT", 0, R.ecartCases)
else
bouton:SetPoint("BOTTOMRIGHT", _G[nom .. "Item" .. (index - 1)],
"BOTTOMLEFT", -R.ecartCases, 0)
end
end
end
end
-- RELEVE -- UpdateContainerFrameAnchors (containerframe.lua, 1372-1401).
-- Les sacs s'empilent du bas vers le haut, CONTAINER_SPACING entre deux, et
-- passent en colonne a gauche quand l'ecran est plein.
local function largeurBarresDroite()
-- EditModeUtil:GetRightActionBarWidth() n'existe pas en 3.3.5 : ses
-- barres de droite sont MultiBarRight et MultiBarLeft.
local largeur = 0
for _, nomBarre in ipairs({ "MultiBarRight", "MultiBarLeft" }) do
local barre = _G[nomBarre]
if barre and barre:IsShown() then
largeur = largeur + barre:GetWidth()
end
end
return largeur
end
local function sacsOuverts()
-- Le client tient l'ordre d'empilement dans ContainerFrame1.bags, ce que
-- la source lit avec GetBagsShown.
local liste = {}
if ContainerFrame1 and ContainerFrame1.bags then
for _, nomCadre in ipairs(ContainerFrame1.bags) do
local cadre = _G[nomCadre]
if cadre and cadre:IsShown() then
table.insert(liste, cadre)
end
end
end
if #liste == 0 then
for _, cadre in ipairs(cadres) do
if cadre:IsShown() and (cadre.size or 0) > 0 then
table.insert(liste, cadre)
end
end
end
return liste
end
local function poserSacs()
local hauteurEcran = GetScreenHeight() / R.echelle
local decalageX = (largeurBarresDroite() + R.bordDroit) / R.echelle
local decalageY = R.bordBas / R.echelle
local libre = hauteurEcran - decalageY
local precedent, premierDeColonne
for index, cadre in ipairs(sacsOuverts()) do
cadre:SetScale(R.echelle)
cadre:ClearAllPoints()
if index == 1 then
cadre:SetPoint("BOTTOMRIGHT", cadre:GetParent(), "BOTTOMRIGHT",
-decalageX, decalageY)
premierDeColonne = cadre
elseif libre < cadre:GetHeight() then
libre = hauteurEcran - decalageY
cadre:SetPoint("BOTTOMRIGHT", premierDeColonne, "BOTTOMLEFT",
R.ecartColonnes, 0)
premierDeColonne = cadre
else
cadre:SetPoint("BOTTOMRIGHT", precedent, "TOPRIGHT", 0, R.ecartSacs)
end
precedent = cadre
libre = libre - cadre:GetHeight()
end
end
ForeverUI.BagsStack = poserSacs
-- LE RATTRAPAGE. 3.3.5 repose la taille de ses cadres de sac a des moments
-- que les accroches ne couvrent pas toutes (le client ouvre, ferme et
-- reagence ses treize cadres entre eux). Un seul passage a l'image suivante
-- relit la mesure et la repose si elle a bouge ; il ne se redemande que
-- depuis les accroches, jamais depuis lui-meme, donc il ne tourne pas en
-- boucle contre le client.
local rattrapage = CreateFrame("Frame", "ForeverUIBagsRecheck")
rattrapage:Hide()
rattrapage:SetScript("OnUpdate", function(self)
self:Hide()
local refaire = false
for _, cadre in ipairs(cadres) do
if cadre:IsShown() and (cadre.size or 0) > 1 then
local m = mesures(cadre)
if math.abs(cadre:GetHeight() - m.hauteur) > 0.5
or math.abs(cadre:GetWidth() - m.largeur) > 0.5 then
cadre.foreverDefaite = (cadre.foreverDefaite or 0) + 1
poserGrille(cadre)
refaire = true
end
end
end
if refaire then
-- Une hauteur a change : l'empilement en depend.
poserSacs()
end
end)
local function demanderRattrapage()
rattrapage:Show()
end
ForeverUI.BagsRecheck = demanderRattrapage
ForeverUI.BagsLayout = poserOutils
-- ------------------------------------------------------------- accroches
local function habillerTout()
for index = 1, NB_CADRES do
habillerCadre(_G["ContainerFrame" .. index])
end
end
habillerTout()
majChamp()
if hooksecurefunc then
-- Le client repose ses propres morceaux a chaque ouverture de sac.
hooksecurefunc("ContainerFrame_GenerateFrame", function(cadre)
habillerCadre(cadre)
poserGrille(cadre)
poserOutils()
Recherche.Tout()
demanderRattrapage()
end)
-- ContainerFrame_Update est rappele a chaque mise a jour de sac. Le
-- client y repose ses propres morceaux : la grille est reposee ensuite,
-- sinon la taille d'origine revient des le premier objet ramasse.
hooksecurefunc("ContainerFrame_Update", function(cadre)
poserGrille(cadre)
Recherche.Appliquer(cadre)
demanderRattrapage()
end)
hooksecurefunc("ContainerFrame_OnHide", function()
poserOutils()
end)
-- Le client remet son echelle et repose les cadres a chaque
-- reagencement : on repasse derriere lui, taille comprise.
hooksecurefunc("updateContainerFrameAnchors", function()
for _, cadre in ipairs(cadres) do
if cadre:GetScale() ~= R.echelle then
cadre:SetScale(R.echelle)
end
if cadre:IsShown() then
poserGrille(cadre)
end
end
-- Le client vient d'empiler ses sacs avec SES ecarts ; on repose
-- ceux de camelot par-dessus.
poserSacs()
demanderRattrapage()
end)
end
-- LE SEGMENT DU CLIENT SE TAIT, ET SA FONCTION DE TAILLE AUSSI.
--
-- ManageBackpackTokenFrame reparente BackpackTokenFrame dans le sac et
-- REPOSE LA HAUTEUR de celui-ci -- BACKPACK_HEIGHT plus 22 -- ce qui defait
-- la notre. On masque son segment et on repasse derriere elle.
local function etoufferSegmentDuClient()
local ancien = _G["BackpackTokenFrame"]
if ancien then
ancien:Hide()
if ancien.EnableMouse then
ancien:EnableMouse(false)
end
end
end
if hooksecurefunc and type(_G["ManageBackpackTokenFrame"]) == "function" then
hooksecurefunc("ManageBackpackTokenFrame", function()
etoufferSegmentDuClient()
for _, cadre in ipairs(cadres) do
if cadre:IsShown() and cadre:GetID() == 0 then
poserGrille(cadre)
end
end
end)
end
-- COCHER "Show on Backpack" PASSE PAR SetCurrencyBackpack, et par rien
-- d'autre : c'est ce que font les deux cases du volet droit de l'onglet des
-- monnaies, et le clic modifie de la liste du client. On s'y greffe pour
-- que le sac suive dans la foulee.
if hooksecurefunc and type(_G["SetCurrencyBackpack"]) == "function" then
hooksecurefunc("SetCurrencyBackpack", function()
for _, cadre in ipairs(cadres) do
if cadre:IsShown() and cadre:GetID() == 0 then
poserGrille(cadre)
end
end
end)
end
local veilleur = CreateFrame("Frame", "ForeverUIBagsWatcher")
veilleur:RegisterEvent("PLAYER_ENTERING_WORLD")
veilleur:RegisterEvent("BAG_UPDATE")
-- La quantite d'une monnaie suivie change : le segment la montre.
veilleur:RegisterEvent("CURRENCY_DISPLAY_UPDATE")
veilleur:SetScript("OnEvent", function()
habillerTout()
etoufferSegmentDuClient()
for _, cadre in ipairs(cadres) do
if cadre:IsShown() then
poserGrille(cadre)
end
end
poserOutils()
Recherche.Tout()
demanderRattrapage()
end)
-- Refaire toute la mise en page apres un changement de reglage.
function ForeverUI.BagsApply()
for _, cadre in ipairs(cadres) do
poserGrille(cadre)
end
poserOutils()
Recherche.Tout()
end
-- Le diagnostic ne dit que ce que le JEU montre : pour CHAQUE sac ouvert, la
-- hauteur que la formule donne et celle que le cadre porte vraiment. Si les
-- deux different, quelque chose repose la taille apres nous, et la ligne le
-- dit au lieu de le laisser deviner.
ForeverUI.BagsDebug = function()
local ouverts = 0
for _, cadre in ipairs(cadres) do
if cadre:IsShown() and (cadre.size or 0) > 0 then
ouverts = ouverts + 1
local nom = cadre:GetName()
local m = mesures(cadre)
local reelleL, reelleH = cadre:GetWidth(), cadre:GetHeight()
local accord = (math.abs(reelleH - m.hauteur) < 0.5)
and (math.abs(reelleL - m.largeur) < 0.5)
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"|cff66ccffForeverUI|r " .. L.BAGS_DEBUG_FRAME,
nom, cadre:GetID(), cadre.size or 0, m.rangees))
DEFAULT_CHAT_FRAME:AddMessage(string.format(
" " .. L.BAGS_DEBUG_SIZE,
m.largeur, m.hauteur, m.grille, m.comble, m.extra, reelleL, reelleH,
accord and ("|cff44ff44" .. L.BAGS_DEBUG_MATCH .. "|r") or ("|cffff4444" .. L.BAGS_DEBUG_MISMATCH .. "|r")))
if not accord then
-- Le temoin dit LEQUEL des deux cas on tient.
DEFAULT_CHAT_FRAME:AddMessage(string.format(
" " .. L.BAGS_DEBUG_WITNESS,
tostring(cadre.foreverDemande), tostring(cadre.foreverRelue),
tostring(cadre.foreverDefaite or 0),
(cadre.foreverRelue and cadre.foreverDemande
and math.abs(cadre.foreverRelue - cadre.foreverDemande) < 0.5)
and ("|cffff4444" .. L.BAGS_DEBUG_RESIZED_AFTER .. "|r")
or ("|cffff4444" .. L.BAGS_DEBUG_ANCHORS_FORCE .. "|r")))
local lignes = string.format(" " .. L.BAGS_DEBUG_ANCHORS, cadre:GetNumPoints())
for index = 1, cadre:GetNumPoints() do
local point, cible, pointCible, x, y = cadre:GetPoint(index)
lignes = lignes .. string.format(L.BAGS_DEBUG_ANCHOR,
tostring(point), tostring(pointCible),
cible and (cible.GetName and cible:GetName() or "?") or L.BAGS_DEBUG_SCREEN,
x or 0, y or 0)
end
DEFAULT_CHAT_FRAME:AddMessage(lignes)
end
end
end
if ouverts == 0 then
DEFAULT_CHAT_FRAME:AddMessage(
"|cff66ccffForeverUI|r " .. L.BAGS_DEBUG_NO_BAG_OPEN)
end
DEFAULT_CHAT_FRAME:AddMessage(string.format(
" " .. L.BAGS_DEBUG_SKINNED,
#cadres, R.emplacement, tostring(champ:IsShown())))
-- LE SEGMENT DES MONNAIES SUIVIES : ce que le client donne, et ce qu'on
-- en pose. Le segment du CLIENT doit etre muet.
local suivies = lireJetons()
local noms = {}
for _, jeton in ipairs(suivies) do
noms[#noms + 1] = string.format("%s=%s", jeton.nom, tostring(jeton.compte))
end
local segment = _G["ForeverUIBagTokens"]
local ancien = _G["BackpackTokenFrame"]
DEFAULT_CHAT_FRAME:AddMessage(string.format(
" " .. L.BAGS_DEBUG_TOKENS, #suivies, MAX_WATCHED_TOKENS or 3,
(#noms > 0) and table.concat(noms, ", ") or L.BAGS_DEBUG_NONE))
DEFAULT_CHAT_FRAME:AddMessage(string.format(
" " .. L.BAGS_DEBUG_SEGMENT,
tostring(segment and segment:IsShown()),
tostring(ancien and ancien:IsShown())))
-- Tous les reglages, par ordre alphabetique : la liste ne peut pas se
-- demoder quand un reglage apparait ou disparait.
local cles = {}
for cle in pairs(R) do
table.insert(cles, cle)
end
table.sort(cles)
local ligne = ""
for _, cle in ipairs(cles) do
ligne = ligne .. string.format("%s=%s ", cle, tostring(R[cle]))
if string.len(ligne) > 80 then
DEFAULT_CHAT_FRAME:AddMessage(" " .. ligne)
ligne = ""
end
end
if ligne ~= "" then
DEFAULT_CHAT_FRAME:AddMessage(" " .. ligne)
end
end
-- Changer un reglage en jeu, pour essayer avant de le figer dans le fichier.
function ForeverUI.BagsSet(cle, valeur)
if R[cle] == nil then
DEFAULT_CHAT_FRAME:AddMessage("|cff66ccffForeverUI|r " .. L.BAGS_UNKNOWN_SETTING .. tostring(cle))
return false
end
local nombre = tonumber(valeur)
if not nombre then
DEFAULT_CHAT_FRAME:AddMessage("|cff66ccffForeverUI|r " .. L.BAGS_NUMBER_EXPECTED)
return false
end
R[cle] = nombre
ForeverUI.BagsApply()
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"|cff66ccffForeverUI|r " .. L.BAGS_SET, cle, tostring(nombre)))
return true
end