14c29b6e11
Trois defauts vus sur la capture, trois causes. L'ICONE RESTAIT CARREE. A 36 ses coins tombent a 25,5 du centre, au-dela du metal de l'anneau qui s'arrete a 20,4 : ils depassaient. L'anneau est mesure au pixel a la taille ou il est dessine -- trou net jusqu'a 10,3, degrade jusqu'a 14,3, metal opaque de 14,3 a 20,4. Pour qu'un carre disparaisse il faut demi-cote >= 14,3 et diagonale <= 20,4, soit un cote entre 28,6 et 28,8. 28 est la seule taille qui tienne, et elle donne le meme rond visible que camelot, dont le disque mesure 28 sur la capture de reference. LE TROUSSEAU AVAIT UNE BARRE DOREE EN TRAVERS DE SON ANNEAU. C'est NineSliceUtil.UpdateCornerCropping qui manquait : sur une fenetre plus courte que ses deux coins empiles -- 94 de haut contre 100 + 95 -- les coins se chevauchent et le bord gauche, tendu entre eux, se dessine en travers. La formule de ClipNineSliceBottomCorner est recopiee et rejouee a chaque changement de hauteur, comme UpdateFrameSize le fait. LE TROUSSEAU N'AVAIT PAS DE NOM. GetBagName(-2) ne rend rien ; on reprend alors le titre que le client a pose lui-meme. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
380 lines
13 KiB
Lua
380 lines
13 KiB
Lua
-- ForeverUI : ce qu'on pose sur une feuille d'atlas.
|
|
--
|
|
-- Une feuille reste UNE texture : on ne decoupe rien en fichiers, on deplace
|
|
-- seulement le rectangle lu dedans. Un remplissage partiel (barre de vie a
|
|
-- 40 %) rogne ce rectangle au lieu d'etirer l'image, sinon l'art se deforme.
|
|
|
|
ForeverUI = ForeverUI or {}
|
|
|
|
local function entry(name)
|
|
if not UIAtlas or not UIAtlas.data then
|
|
return nil
|
|
end
|
|
return UIAtlas.data[name]
|
|
end
|
|
|
|
ForeverUI.AtlasEntry = entry
|
|
|
|
-- Regle une texture sur un element d'atlas. keepSize laisse la taille en place
|
|
-- (utile quand le cadre impose ses propres dimensions).
|
|
function ForeverUI.SetAtlas(texture, name, keepSize)
|
|
local e = entry(name)
|
|
if not e then
|
|
return false
|
|
end
|
|
|
|
texture:SetTexture(e[1])
|
|
texture:SetTexCoord(e[2], e[3], e[4], e[5])
|
|
|
|
if not keepSize then
|
|
texture:SetWidth(e[6])
|
|
texture:SetHeight(e[7])
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
-- Cree une texture deja reglee sur un element, a sa taille d'origine.
|
|
function ForeverUI.CreateAtlasTexture(parent, layer, name)
|
|
local texture = parent:CreateTexture(nil, layer)
|
|
if not ForeverUI.SetAtlas(texture, name) then
|
|
texture:Hide()
|
|
end
|
|
return texture
|
|
end
|
|
|
|
-- Remplissage de gauche a droite : le rectangle est rogne a la fraction voulue.
|
|
-- Une largeur nulle est interdite par le client, donc la texture est masquee.
|
|
function ForeverUI.SetAtlasFill(texture, name, fraction, fullWidth)
|
|
local e = entry(name)
|
|
if not e then
|
|
texture:Hide()
|
|
return false
|
|
end
|
|
|
|
if not fraction or fraction ~= fraction or fraction < 0 then
|
|
fraction = 0
|
|
elseif fraction > 1 then
|
|
fraction = 1
|
|
end
|
|
|
|
local width = (fullWidth or e[6]) * fraction
|
|
if width < 1 then
|
|
texture:Hide()
|
|
return true
|
|
end
|
|
|
|
texture:SetTexture(e[1])
|
|
texture:SetTexCoord(e[2], e[2] + (e[3] - e[2]) * fraction, e[4], e[5])
|
|
texture:SetWidth(width)
|
|
texture:SetHeight(e[7])
|
|
texture:Show()
|
|
return true
|
|
end
|
|
|
|
-- DECOUPE EN NEUF.
|
|
--
|
|
-- RELEVE -- mainline/MainActionBar.xml, camelot/MainMenuBarBagButtons.xml et
|
|
-- camelot/MainMenuBarMicroMenu.xml posent tous les trois la MEME image pour
|
|
-- encadrer leur groupe :
|
|
-- <Texture parentKey="BorderArt" atlas="UI-HUD-ActionBar-Frame">
|
|
-- <Anchor point="TOPLEFT" x="-6" y="6"/>
|
|
-- <Anchor point="BOTTOMRIGHT" x="4" y="-5"/>
|
|
-- Cette image fait 55 x 55 (110 x 110 sur une feuille 2x) : c'est un octogone
|
|
-- a bord de bronze sur fond noir. Le client moderne l'etire en neuf tranches ;
|
|
-- etiree d'une seule piece sur 560 px, ses biseaux deviendraient des rampes.
|
|
-- On la coupe donc ici, ce qui donne le meme resultat a l'ecran.
|
|
--
|
|
-- options.nom nom de l'element d'atlas
|
|
-- options.couche couche de dessin (BACKGROUND par defaut)
|
|
-- options.margeImage epaisseur du coin dans l'image, en pixels
|
|
-- options.tailleImage cote de l'image, en pixels
|
|
-- options.marge epaisseur du coin a l'ecran, en pixels
|
|
function ForeverUI.SetAtlasNineSlice(parent, options)
|
|
local e = entry(options.nom)
|
|
if not e then
|
|
return nil
|
|
end
|
|
|
|
local couche = options.couche or "BACKGROUND"
|
|
local marge = options.marge
|
|
local fraction = options.margeImage / options.tailleImage
|
|
local du = (e[3] - e[2]) * fraction
|
|
local dv = (e[5] - e[4]) * fraction
|
|
local u = { e[2], e[2] + du, e[3] - du, e[3] }
|
|
local v = { e[4], e[4] + dv, e[5] - dv, e[5] }
|
|
|
|
local function piece(cu1, cu2, cv1, cv2)
|
|
local t = parent:CreateTexture(nil, couche)
|
|
t:SetTexture(e[1])
|
|
t:SetTexCoord(u[cu1], u[cu2], v[cv1], v[cv2])
|
|
return t
|
|
end
|
|
|
|
local p = {}
|
|
|
|
p.coinHautGauche = piece(1, 2, 1, 2)
|
|
p.coinHautGauche:SetWidth(marge)
|
|
p.coinHautGauche:SetHeight(marge)
|
|
p.coinHautGauche:SetPoint("TOPLEFT", parent, "TOPLEFT")
|
|
|
|
p.coinHautDroit = piece(3, 4, 1, 2)
|
|
p.coinHautDroit:SetWidth(marge)
|
|
p.coinHautDroit:SetHeight(marge)
|
|
p.coinHautDroit:SetPoint("TOPRIGHT", parent, "TOPRIGHT")
|
|
|
|
p.coinBasGauche = piece(1, 2, 3, 4)
|
|
p.coinBasGauche:SetWidth(marge)
|
|
p.coinBasGauche:SetHeight(marge)
|
|
p.coinBasGauche:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT")
|
|
|
|
p.coinBasDroit = piece(3, 4, 3, 4)
|
|
p.coinBasDroit:SetWidth(marge)
|
|
p.coinBasDroit:SetHeight(marge)
|
|
p.coinBasDroit:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT")
|
|
|
|
-- Les bords et le centre sont definis par deux coins opposes : deux points
|
|
-- suffisent a fixer un rectangle, aucune taille a calculer.
|
|
p.bordHaut = piece(2, 3, 1, 2)
|
|
p.bordHaut:SetPoint("TOPLEFT", p.coinHautGauche, "TOPRIGHT")
|
|
p.bordHaut:SetPoint("BOTTOMRIGHT", p.coinHautDroit, "BOTTOMLEFT")
|
|
|
|
p.bordBas = piece(2, 3, 3, 4)
|
|
p.bordBas:SetPoint("TOPLEFT", p.coinBasGauche, "TOPRIGHT")
|
|
p.bordBas:SetPoint("BOTTOMRIGHT", p.coinBasDroit, "BOTTOMLEFT")
|
|
|
|
p.bordGauche = piece(1, 2, 2, 3)
|
|
p.bordGauche:SetPoint("TOPLEFT", p.coinHautGauche, "BOTTOMLEFT")
|
|
p.bordGauche:SetPoint("BOTTOMRIGHT", p.coinBasGauche, "TOPRIGHT")
|
|
|
|
p.bordDroit = piece(3, 4, 2, 3)
|
|
p.bordDroit:SetPoint("TOPLEFT", p.coinHautDroit, "BOTTOMLEFT")
|
|
p.bordDroit:SetPoint("BOTTOMRIGHT", p.coinBasDroit, "TOPRIGHT")
|
|
|
|
p.centre = piece(2, 3, 2, 3)
|
|
p.centre:SetPoint("TOPLEFT", p.coinHautGauche, "BOTTOMRIGHT")
|
|
p.centre:SetPoint("BOTTOMRIGHT", p.coinBasDroit, "TOPLEFT")
|
|
|
|
return p
|
|
end
|
|
|
|
-- L'encadrement commun aux trois groupes du bas de l'ecran.
|
|
--
|
|
-- MESURE SUR L'IMAGE. Le coin doit contenir TOUT le biseau, pas seulement son
|
|
-- arete exterieure. Le profil du bord haut de l'image ne devient constant qu'a
|
|
-- partir de la colonne 19 sur 110, et celui du bord gauche a partir de la
|
|
-- ligne 19 : en dessous, on coupe dans la diagonale, et le morceau de
|
|
-- diagonale restant part s'etirer sur toute la longueur de la barre -- c'est
|
|
-- ce qui deformait les coins. On prend donc 20, avec un pixel de marge, soit
|
|
-- 10 px a l'ecran puisque la feuille est en double densite.
|
|
function ForeverUI.SetBarFrameArt(frame, couche)
|
|
return ForeverUI.SetAtlasNineSlice(frame, {
|
|
nom = "ui-hud-actionbar-frame",
|
|
couche = couche or "BACKGROUND",
|
|
margeImage = 20,
|
|
tailleImage = 110,
|
|
marge = 10,
|
|
})
|
|
end
|
|
|
|
-- SEPARATEUR ENTRE DEUX EMPLACEMENTS.
|
|
--
|
|
-- RELEVE -- mainline/MainActionBar.xml : HorizontalDividerTemplate fait 12 de
|
|
-- large et se monte en TROIS tranches verticales sur le jeu d'images
|
|
-- ui-hud-actionbar-frame-divider. Les hauteurs tombent juste sur un bouton de
|
|
-- 45 : 14 en haut, 16 au centre, 15 en bas.
|
|
function ForeverUI.CreateDivider(parent, niveau)
|
|
local cadre = CreateFrame("Frame", nil, parent)
|
|
cadre:SetWidth(12)
|
|
cadre:SetFrameLevel(niveau or parent:GetFrameLevel())
|
|
|
|
local haut = cadre:CreateTexture(nil, "ARTWORK")
|
|
if not ForeverUI.SetAtlas(haut, "ui-hud-actionbar-frame-divider-threeslice-edgetop", true) then
|
|
cadre:Hide()
|
|
return cadre
|
|
end
|
|
haut:SetHeight(14)
|
|
haut:SetPoint("TOPLEFT", cadre, "TOPLEFT")
|
|
haut:SetPoint("TOPRIGHT", cadre, "TOPRIGHT")
|
|
|
|
local bas = cadre:CreateTexture(nil, "ARTWORK")
|
|
ForeverUI.SetAtlas(bas, "ui-hud-actionbar-frame-divider-threeslice-edgebottom", true)
|
|
bas:SetHeight(15)
|
|
bas:SetPoint("BOTTOMLEFT", cadre, "BOTTOMLEFT")
|
|
bas:SetPoint("BOTTOMRIGHT", cadre, "BOTTOMRIGHT")
|
|
|
|
local centre = cadre:CreateTexture(nil, "ARTWORK")
|
|
ForeverUI.SetAtlas(centre, "!ui-hud-actionbar-frame-divider-threeslice-center", true)
|
|
centre:SetPoint("TOPLEFT", haut, "BOTTOMLEFT")
|
|
centre:SetPoint("BOTTOMRIGHT", bas, "TOPRIGHT")
|
|
|
|
cadre.haut, cadre.centre, cadre.bas = haut, centre, bas
|
|
return cadre
|
|
end
|
|
|
|
-- LE PANNEAU.
|
|
--
|
|
-- RELEVE -- mainline/SharedUIPanelTemplates.xml : un panneau du client moderne
|
|
-- (PortraitFrameFlatTemplate) est fait de deux couches.
|
|
-- 1. le fond plat (FlatPanelBackgroundTemplate), pose entre TOPLEFT (2, -20)
|
|
-- et BOTTOMRIGHT (-2, 3) : deux coins arrondis de 16 x 16 en bas
|
|
-- (uiframebackground-nineslice-cornerbottom*), un bord entre eux, et tout
|
|
-- le reste en aplat, le tout teinte par PANEL_BACKGROUND_COLOR ;
|
|
-- 2. l'encadrement de metal en neuf tranches, jeu HeldBagLayout de
|
|
-- mainline/NineSliceLayouts.lua, avec les corrections de
|
|
-- camelot/NineSliceLayoutOverrides.lua (coin haut droit x -2, coins bas
|
|
-- y = -8).
|
|
--
|
|
-- PANEL_BACKGROUND_COLOR est une couleur du client, absente du code extrait.
|
|
-- Elle est MESUREE sur la capture du vrai client (docs/reference) : le fond
|
|
-- d'un panneau y vaut (16, 14, 12) et il est OPAQUE -- le decor ne passe pas
|
|
-- au travers. Un fond translucide fait virer au bleu les ecarts entre les
|
|
-- cases d'un sac, ce qui a ete le premier ecart visible.
|
|
--
|
|
-- Les bords sont ETIRES et non paves : une texture d'atlas en pavage etale la
|
|
-- feuille entiere.
|
|
local PANNEAU_FOND = { 16 / 255, 14 / 255, 12 / 255, 1 }
|
|
|
|
-- RELEVE -- HeldBagLayout (blizzard_sharedxml/mainline/nineslicelayouts.lua) :
|
|
-- les huit morceaux sont declares en OVERLAY, et chaque coin porte son
|
|
-- decalage. Tout est recopie ici tel quel.
|
|
--
|
|
-- Ce que cela impose : en OVERLAY, le metal couvre toute region du cadre
|
|
-- lui-meme. La source s'en accommode parce qu'elle range le titre et le
|
|
-- portrait dans des CADRES FILS (TitleContainer a frameLevel 510,
|
|
-- PortraitContainer) -- un cadre fils se dessine au-dessus des regions de
|
|
-- son parent, quel que soit leur calque. Les appelants doivent donc en
|
|
-- faire autant pour tout ce qui doit rester visible.
|
|
local PANNEAU_COUCHE = "OVERLAY"
|
|
|
|
local PANNEAU_COINS = {
|
|
{ cle = "coinHautGauche", nom = "ui-frame-portraitmetal-cornertopleftsmall",
|
|
point = "TOPLEFT", x = -13, y = 16 },
|
|
{ cle = "coinHautDroit", nom = "ui-frame-metal-cornertopright",
|
|
point = "TOPRIGHT", x = 4, y = 16 },
|
|
{ cle = "coinBasGauche", nom = "ui-frame-metal-cornerbottomleft",
|
|
point = "BOTTOMLEFT", x = -13, y = -3 },
|
|
{ cle = "coinBasDroit", nom = "ui-frame-metal-cornerbottomright",
|
|
point = "BOTTOMRIGHT", x = 4, y = -3 },
|
|
}
|
|
|
|
function ForeverUI.SetPanelArt(frame)
|
|
if frame.foreverPanel then
|
|
return frame.foreverPanel
|
|
end
|
|
|
|
local p = {}
|
|
|
|
-- 1. le fond plat
|
|
local r, v, b, a = PANNEAU_FOND[1], PANNEAU_FOND[2], PANNEAU_FOND[3], PANNEAU_FOND[4]
|
|
|
|
local basGauche = frame:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(basGauche, "uiframebackground-nineslice-cornerbottomleft")
|
|
basGauche:SetVertexColor(r, v, b, a)
|
|
basGauche:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 2, 3)
|
|
|
|
local basDroit = frame:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(basDroit, "uiframebackground-nineslice-cornerbottomright")
|
|
basDroit:SetVertexColor(r, v, b, a)
|
|
basDroit:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -2, 3)
|
|
|
|
local bordBas = frame:CreateTexture(nil, "BACKGROUND")
|
|
bordBas:SetTexture(r, v, b, a)
|
|
bordBas:SetPoint("TOPLEFT", basGauche, "TOPRIGHT")
|
|
bordBas:SetPoint("BOTTOMRIGHT", basDroit, "BOTTOMLEFT")
|
|
|
|
local corps = frame:CreateTexture(nil, "BACKGROUND")
|
|
corps:SetTexture(r, v, b, a)
|
|
corps:SetPoint("TOPLEFT", frame, "TOPLEFT", 2, -20)
|
|
corps:SetPoint("BOTTOMRIGHT", basDroit, "TOPRIGHT")
|
|
|
|
p.fond = { basGauche, basDroit, bordBas, corps }
|
|
|
|
-- 2. l'encadrement de metal
|
|
for _, coin in ipairs(PANNEAU_COINS) do
|
|
local texture = frame:CreateTexture(nil, PANNEAU_COUCHE)
|
|
if ForeverUI.SetAtlas(texture, coin.nom) then
|
|
texture:SetPoint(coin.point, frame, coin.point, coin.x, coin.y)
|
|
p[coin.cle] = texture
|
|
else
|
|
texture:Hide()
|
|
end
|
|
end
|
|
|
|
local function bord(nom, point1, cible1, relatif1, point2, cible2, relatif2)
|
|
local texture = frame:CreateTexture(nil, PANNEAU_COUCHE)
|
|
if not ForeverUI.SetAtlas(texture, nom) then
|
|
texture:Hide()
|
|
return nil
|
|
end
|
|
texture:SetPoint(point1, cible1, relatif1)
|
|
texture:SetPoint(point2, cible2, relatif2)
|
|
return texture
|
|
end
|
|
|
|
if p.coinHautGauche and p.coinHautDroit then
|
|
p.bordHaut = bord("_ui-frame-metal-edgetop",
|
|
"TOPLEFT", p.coinHautGauche, "TOPRIGHT",
|
|
"TOPRIGHT", p.coinHautDroit, "TOPLEFT")
|
|
p.bordGauche = bord("!ui-frame-metal-edgeleft",
|
|
"TOPLEFT", p.coinHautGauche, "BOTTOMLEFT",
|
|
"BOTTOMLEFT", p.coinBasGauche, "TOPLEFT")
|
|
p.bordDroit = bord("!ui-frame-metal-edgeright",
|
|
"TOPRIGHT", p.coinHautDroit, "BOTTOMRIGHT",
|
|
"BOTTOMRIGHT", p.coinBasDroit, "TOPRIGHT")
|
|
p.bordBas = bord("_ui-frame-metal-edgebottom",
|
|
"BOTTOMLEFT", p.coinBasGauche, "BOTTOMRIGHT",
|
|
"BOTTOMRIGHT", p.coinBasDroit, "BOTTOMLEFT")
|
|
end
|
|
|
|
frame.foreverPanel = p
|
|
ForeverUI.UpdatePanelCorners(frame)
|
|
return p
|
|
end
|
|
|
|
-- RELEVE -- NineSliceUtil.UpdateCornerCropping et ClipNineSliceBottomCorner
|
|
-- (blizzard_sharedxml/nineslice.lua).
|
|
--
|
|
-- debord = hauteurCoinHaut + hauteurCoinBas - hauteurCadre
|
|
-- - decalageHaut - (-decalageBas)
|
|
--
|
|
-- Quand une fenetre est plus courte que ses deux coins empiles, le coin du
|
|
-- BAS est rogne PAR LE HAUT de cet excedent : ses coordonnees de texture
|
|
-- remontent d'autant et sa hauteur diminue. Sans cela les deux coins se
|
|
-- chevauchent et le bord gauche, tendu entre eux, se retrouve dessine a
|
|
-- l'envers en travers du cadre -- ce qui barrait l'anneau du trousseau.
|
|
function ForeverUI.UpdatePanelCorners(frame)
|
|
local p = frame.foreverPanel
|
|
if not p then
|
|
return
|
|
end
|
|
|
|
local hautGauche, basGauche
|
|
for _, coin in ipairs(PANNEAU_COINS) do
|
|
if coin.cle == "coinHautGauche" then hautGauche = coin end
|
|
if coin.cle == "coinBasGauche" then basGauche = coin end
|
|
end
|
|
|
|
local eHaut = ForeverUI.AtlasEntry(hautGauche.nom)
|
|
local eBas = ForeverUI.AtlasEntry(basGauche.nom)
|
|
if not (eHaut and eBas) then
|
|
return
|
|
end
|
|
|
|
local debord = eHaut[7] + eBas[7] - frame:GetHeight() - hautGauche.y - (-basGauche.y)
|
|
|
|
for _, coin in ipairs(PANNEAU_COINS) do
|
|
if coin.point == "BOTTOMLEFT" or coin.point == "BOTTOMRIGHT" then
|
|
local texture = p[coin.cle]
|
|
local e = ForeverUI.AtlasEntry(coin.nom)
|
|
if texture and e then
|
|
local rogne = math.max(0, math.min(debord, e[7]))
|
|
local hauteurUV = e[5] - e[4]
|
|
texture:SetTexCoord(e[2], e[3], e[4] + (rogne / e[7]) * hauteurUV, e[5])
|
|
texture:SetWidth(e[6])
|
|
texture:SetHeight(e[7] - rogne)
|
|
end
|
|
end
|
|
end
|
|
end
|