Files
moon-well-ui/data/addon/ForeverUI/Bags.lua
T
Adrestie 7a72cf649c mod-forever-ui: three differences the two captures gave up
Comparing my own bag with camelot's, side by side and pixel by pixel, named
what was wrong.

The panel let the world through. Camelot's interior is (16,14,12) and opaque:
the gaps between slots went blue on mine. The colour is now the measured one,
at full opacity.

The title and the bag icon were missing. The metal nine-slice sat in OVERLAY,
where the modern client can afford it because its portrait and title live in
child frames at high level; 3.3.5 keeps them as plain regions of the frame,
in BACKGROUND and ARTWORK, so the metal covered both. The nine-slice moves to
BORDER.

Every slot in camelot wears a frame, full or empty, dark grey: edges between
25 and 49, corners near 100, which is WhiteIconFrame at a tint of 0.39. Mine
showed it white and only on full slots. It is now on every slot at that tint,
and the quality colour takes over only from uncommon up -- the common items
of the capture keep the grey.

Both captures are kept in docs/reference/.

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

820 lines
26 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.
local NB_CADRES = NUM_CONTAINER_FRAMES or 13
local SACS = { 0, 1, 2, 3, 4 } -- sac a dos et les quatre sacs portes
local EMPLACEMENT = 37 -- taille d'un bouton d'objet
local RECHERCHE_W, RECHERCHE_H = 96, 18
local TRI_W, TRI_H = 28, 26
-- 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"
-- LA MISE EN PAGE EST REFAITE ICI.
--
-- 3.3.5 taille sa fenetre sur ses propres images de fond et colle la grille a
-- droite (premier bouton a -12 du bord). Ces images ont disparu avec
-- l'habillage : la fenetre est donc mesuree a partir de ce qu'elle contient,
-- et la grille centree.
-- entete la bande de titre, plus la bande du champ de recherche pour le
-- sac a dos (le champ est pose de -37 a -55)
-- grille quatre colonnes, pas de 42 x 41 pour des boutons de 37
-- bourse sous la grille, dans la fenetre
local COLONNES = 4
local ECART = 5 -- ITEM_SPACING_X et _Y
local LARGEUR_CADRE = 178 -- CONTAINER_WIDTH de camelot (192 en 3.3.5)
local REMPLISSAGE = 9 + 48 -- GetPaddingHeight
local REMPLISSAGE_RECHERCHE = 30
local MARGE_BAS = 9 -- GetFirstButtonOffsetY
local BOURSE_H = 13
local BOURSE_BAS, BOURSE_COTE = 8, 8
-- MESURE SUR LA CAPTURE DU VRAI CLIENT (docs/reference). A l'echelle 4/3 de
-- cette capture, la grille fait 217 px pour les 163 unites de camelot, un
-- emplacement 49 px pour 37, le pas 56 px pour 42, et la fenetre 411 px pour
-- les 305 unites que donne la formule sur cinq rangees. Les chiffres du code
-- de camelot sont donc les bons : rien a ajouter.
-- L'encadre de la bourse depasse d'elle de deux pixels en haut comme en bas
-- (17 de haut pour une bourse de 13). La derniere rangee se pose 1 px
-- au-dessus de cet encadre.
local BORDURE_BOURSE_H, BORDURE_BOURSE_BOUT = 17, 8
local BORDURE_DEPASSE = (BORDURE_BOURSE_H - BOURSE_H) / 2
local AIR_GRILLE_BOURSE = 1
local CADRE_QUALITE = "Interface" .. SEP .. "ForeverUI" .. SEP .. "common" .. SEP .. "whiteiconframe"
-- 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 -- les objets communs
-- de la capture gardent le cadre gris.
local CADRE_GRIS = 0.39
local QUALITE_TEINTEE = 2
local RANGER = BAG_CLEANUP_BAGS or "Ranger les sacs"
local RANGER_AIDE = BAG_CLEANUP_BAGS_DESCRIPTION
or "Reunit les piles et remet les objets en ordre."
ForeverUI = ForeverUI or {}
-- ------------------------------------------------------------ les cadres
local cadres = {}
local function habillerBouton(bouton)
if not bouton or bouton.foreverSkinned then
return
end
local nom = bouton:GetName()
-- Le cadre dore de 3.3.5 laisse la place a l'emplacement moderne, qui est
-- le fond d'une case vide chez camelot (emptyBackgroundAtlas).
local normale = bouton:GetNormalTexture()
if normale then
ForeverUI.SetAtlas(normale, "bags-item-slot64", true)
normale:ClearAllPoints()
normale:SetAllPoints(bouton)
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)
-- Le portrait se pose dans l'anneau du coin haut gauche. L'anneau est
-- centre a 13,5 px du bord gauche et 14 px sous le haut (mesure sur
-- l'image du coin), son trou fait 36 de diametre. 3.3.5 n'a pas de
-- masque : l'icone est rognee pour tenir dans le rond.
local portrait = _G[nom .. "Portrait"]
if portrait then
portrait:ClearAllPoints()
portrait:SetWidth(34)
portrait:SetHeight(34)
portrait:SetPoint("CENTER", cadre, "TOPLEFT", 13.5, -14)
portrait:SetTexCoord(0.08, 0.92, 0.08, 0.92)
portrait:SetDrawLayer("OVERLAY")
end
-- Le titre : centre dans la bande du haut, entre 58 et la largeur moins 24.
local titre = _G[nom .. "Name"]
if titre then
titre:ClearAllPoints()
titre:SetPoint("TOPLEFT", cadre, "TOPLEFT", 58, -6)
titre:SetPoint("TOPRIGHT", cadre, "TOPRIGHT", -24, -6)
titre:SetJustifyH("CENTER")
end
-- 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(24)
fermer:SetHeight(24)
fermer:ClearAllPoints()
fermer:SetPoint("TOPRIGHT", cadre, "TOPRIGHT", 1, 0)
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.
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)
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")
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(RECHERCHE_W)
champ:SetHeight(RECHERCHE_H)
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 or "Rechercher")
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(TRI_W)
boutonTri:SetHeight(TRI_H)
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)
-- 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
champ:SetParent(hote)
champ:ClearAllPoints()
champ:SetPoint("TOPLEFT", hote, "TOPLEFT", 42, -37)
champ:SetWidth(RECHERCHE_W)
champ:Show()
boutonTri:SetParent(hote)
boutonTri:ClearAllPoints()
boutonTri:SetPoint("TOPRIGHT", hote, "TOPRIGHT", -9, -34)
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(BOURSE_H)
local gauche = bourse:CreateTexture(nil, "BACKGROUND")
if not ForeverUI.SetAtlas(gauche, "common-coinbox-left", true) then
gauche:Hide()
return
end
gauche:SetWidth(BORDURE_BOURSE_BOUT)
gauche:SetHeight(BORDURE_BOURSE_H)
gauche:SetPoint("LEFT", bourse, "LEFT", 0, 0)
local droite = bourse:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(droite, "common-coinbox-right", true)
droite:SetWidth(BORDURE_BOURSE_BOUT)
droite:SetHeight(BORDURE_BOURSE_H)
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
-- 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.
local function poserGrille(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 rangees = math.ceil(taille / COLONNES)
local hauteurGrille = rangees * EMPLACEMENT + (rangees - 1) * ECART
local sacADos = cadre:GetID() == 0
cadre:SetWidth(LARGEUR_CADRE)
local bourse = _G[nom .. "MoneyFrame"]
local hauteur = hauteurGrille + REMPLISSAGE
if sacADos then
hauteur = hauteur + REMPLISSAGE_RECHERCHE + BOURSE_H
end
cadre:SetHeight(hauteur)
if sacADos and bourse then
habillerBourse(bourse)
bourse:ClearAllPoints()
bourse:SetPoint("BOTTOMLEFT", cadre, "BOTTOMLEFT", BOURSE_COTE, BOURSE_BAS)
bourse:SetPoint("BOTTOMRIGHT", cadre, "BOTTOMRIGHT", -BOURSE_COTE, BOURSE_BAS)
bourse:Show()
end
local premier = _G[nom .. "Item1"]
if premier then
premier:ClearAllPoints()
if sacADos and bourse then
premier:SetPoint("BOTTOMRIGHT", bourse, "TOPRIGHT", 0,
BORDURE_DEPASSE + AIR_GRILLE_BOURSE)
else
premier:SetPoint("BOTTOMRIGHT", cadre, "BOTTOMRIGHT", -7, MARGE_BAS)
end
end
-- 3.3.5 empile les rangees avec 4 px ; camelot en met 5.
for index = COLONNES + 1, taille, COLONNES do
local bouton = _G[nom .. "Item" .. index]
local dessous = _G[nom .. "Item" .. (index - COLONNES)]
if bouton and dessous then
bouton:ClearAllPoints()
bouton:SetPoint("BOTTOMRIGHT", dessous, "TOPRIGHT", 0, ECART)
end
end
end
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()
end)
hooksecurefunc("ContainerFrame_Update", function(cadre)
Recherche.Appliquer(cadre)
end)
hooksecurefunc("ContainerFrame_OnHide", function()
poserOutils()
end)
end
local veilleur = CreateFrame("Frame")
veilleur:RegisterEvent("PLAYER_ENTERING_WORLD")
veilleur:RegisterEvent("BAG_UPDATE")
veilleur:SetScript("OnEvent", function()
habillerTout()
poserOutils()
Recherche.Tout()
end)
ForeverUI.BagsDebug = function()
local ouverts = 0
for _, cadre in ipairs(cadres) do
if cadre:IsShown() then
ouverts = ouverts + 1
end
end
DEFAULT_CHAT_FRAME:AddMessage(string.format(
"|cff66ccffForeverUI|r sacs : %d cadres habilles, %d ouverts | recherche \"%s\" | champ visible=%s | tri actif=%s",
#cadres, ouverts, Recherche.texte, tostring(champ:IsShown()), tostring(Tri.actif)))
end