Files
moon-well-ui/data/addon/ForeverUI/AtlasUtil.lua
T

717 lines
26 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 },
}
-- OPTIONS.
-- coinHautGauche : l'atlas du coin haut gauche. Les deux mises en page
-- de la source ne different que par lui -- HeldBagLayout prend
-- ...CornerTopLeftSmall, PortraitFrameTemplate prend ...CornerTopLeft,
-- un anneau plus large pour un portrait de 62.
-- coins : { cle = { x = , y = } } -- remplace le decalage d'un coin. La
-- source elle-meme s'en sert : camelot/NineSliceLayoutOverrides.lua
-- repasse sur TOUTES les mises en page apres les avoir definies, parce
-- que son art ne fait pas la meme taille que celui du jeu moderne.
-- niveau : quand il est donne, LE METAL se pose dans un CADRE FILS de ce
-- niveau au-dessus du cadre. La source fait de meme -- son NineSlice
-- est un cadre fils -- et il le faut des que le cadre porte d'autres
-- cadres fils : ceux-ci se dessinent au-dessus de toute region de leur
-- parent, et recouvriraient le metal.
--
-- LE FOND, LUI, RESTE SUR LE CADRE. Il doit passer DERRIERE tout le
-- reste : monte avec le metal, il recouvrait les volets de la feuille
-- du personnage. Une region du cadre est sous tous ses cadres fils,
-- c'est exactement la place qu'il lui faut.
function ForeverUI.SetPanelArt(frame, options)
if frame.foreverPanel then
return frame.foreverPanel
end
options = options or {}
-- hote : ou va le METAL. Le fond reste sur le cadre, au dernier plan.
local hote = frame
if options.niveau then
hote = CreateFrame("Frame", nil, frame)
hote:SetAllPoints(frame)
hote:SetFrameLevel(frame:GetFrameLevel() + options.niveau)
frame.foreverHabillage = hote
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 = hote:CreateTexture(nil, PANNEAU_COUCHE)
local nomAtlas = coin.nom
if coin.cle == "coinHautGauche" and options.coinHautGauche then
nomAtlas = options.coinHautGauche
end
if ForeverUI.SetAtlas(texture, nomAtlas) then
local x, y = coin.x, coin.y
local reglage = options.coins and options.coins[coin.cle]
if reglage then
x = reglage.x or x
y = reglage.y or y
end
texture:SetPoint(coin.point, hote, coin.point, x, y)
p[coin.cle] = texture
p[coin.cle .. "Atlas"] = nomAtlas
else
texture:Hide()
end
end
local function bord(nom, point1, cible1, relatif1, point2, cible2, relatif2)
local texture = hote: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(p.coinHautGaucheAtlas or 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
-- UN SEPARATEUR VERTICAL EN TROIS TRANCHES. common-framedivider fait 11 x 50
-- et porte un EMBOUT a chaque extremite : l'etirer sur la hauteur d'un volet
-- les etale sur des dizaines de pixels. On decoupe donc l'element en trois
-- bandes par ses coordonnees de texture -- embout haut, milieu tire, embout
-- bas -- comme le ferait un neuf-tranches.
function ForeverUI.CreateVerticalDivider(parent, atlas, embout, niveau)
local e = ForeverUI.AtlasEntry(atlas)
local cadre = CreateFrame("Frame", nil, parent)
if not e then
cadre:Hide()
return cadre
end
embout = embout or 4
cadre:SetWidth(e[6])
cadre:SetFrameLevel(niveau or parent:GetFrameLevel())
local u1, u2, v1, v2, hauteur = e[2], e[3], e[4], e[5], e[7]
local partV = (v2 - v1) * (embout / hauteur)
local haut = cadre:CreateTexture(nil, "OVERLAY")
haut:SetTexture(e[1])
haut:SetTexCoord(u1, u2, v1, v1 + partV)
haut:SetHeight(embout)
haut:SetPoint("TOPLEFT", cadre, "TOPLEFT")
haut:SetPoint("TOPRIGHT", cadre, "TOPRIGHT")
local bas = cadre:CreateTexture(nil, "OVERLAY")
bas:SetTexture(e[1])
bas:SetTexCoord(u1, u2, v2 - partV, v2)
bas:SetHeight(embout)
bas:SetPoint("BOTTOMLEFT", cadre, "BOTTOMLEFT")
bas:SetPoint("BOTTOMRIGHT", cadre, "BOTTOMRIGHT")
local milieu = cadre:CreateTexture(nil, "OVERLAY")
milieu:SetTexture(e[1])
milieu:SetTexCoord(u1, u2, v1 + partV, v2 - partV)
milieu:SetPoint("TOPLEFT", haut, "BOTTOMLEFT")
milieu:SetPoint("BOTTOMRIGHT", bas, "TOPRIGHT")
cadre.haut, cadre.milieu, cadre.bas = haut, milieu, bas
return cadre
end
-- NEUF TRANCHES DECOUPEES DANS UNE SEULE IMAGE.
--
-- POURQUOI. Une image de panneau porte une ombre et des coins arrondis de
-- taille fixe. L'etirer d'un bord a l'autre multiplie cette ombre par le
-- facteur d'echelle : sur une liste trois fois plus large que l'image, le
-- filet du bord rentre de vingt pixels et les angles se deforment. Les
-- coins doivent donc garder leur taille, les bords ne s'etirer que dans un
-- sens, et le centre seul dans les deux.
--
-- marges = { gauche, haut, droite, bas } : de combien le rectangle de
-- l'image deborde du cadre. C'est ainsi qu'on fait tomber le filet de
-- l'image exactement sur le bord du cadre : on donne l'epaisseur de
-- l'ombre, mesuree sur l'image.
function ForeverUI.CreateNineSlice(parent, atlas, coin, marges, niveau)
local e = ForeverUI.AtlasEntry(atlas)
if not e then
return nil
end
local chemin, u1, u2, v1, v2, largeur, hauteur = e[1], e[2], e[3], e[4], e[5], e[6], e[7]
local du = (u2 - u1) * coin / largeur
local dv = (v2 - v1) * coin / hauteur
local us = { u1, u1 + du, u2 - du, u2 }
local vs = { v1, v1 + dv, v2 - dv, v2 }
local tranches = {}
local function tranche(colonne, ligne)
local t = parent:CreateTexture(nil, niveau or "BACKGROUND")
t:SetTexture(chemin)
t:SetTexCoord(us[colonne], us[colonne + 1], vs[ligne], vs[ligne + 1])
tranches[#tranches + 1] = t
return t
end
local hg, hd = tranche(1, 1), tranche(3, 1)
local bg, bd = tranche(1, 3), tranche(3, 3)
local haut, bas = tranche(2, 1), tranche(2, 3)
local gauche, droite = tranche(1, 2), tranche(3, 2)
local centre = tranche(2, 2)
for _, c in ipairs({ hg, hd, bg, bd }) do
c:SetWidth(coin)
c:SetHeight(coin)
end
haut:SetHeight(coin)
bas:SetHeight(coin)
gauche:SetWidth(coin)
droite:SetWidth(coin)
local G, H, D, B = marges[1], marges[2], marges[3], marges[4]
hg:SetPoint("TOPLEFT", parent, "TOPLEFT", -G, H)
hd:SetPoint("TOPRIGHT", parent, "TOPRIGHT", D, H)
bg:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", -G, -B)
bd:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", D, -B)
haut:SetPoint("TOPLEFT", hg, "TOPRIGHT")
haut:SetPoint("TOPRIGHT", hd, "TOPLEFT")
bas:SetPoint("BOTTOMLEFT", bg, "BOTTOMRIGHT")
bas:SetPoint("BOTTOMRIGHT", bd, "BOTTOMLEFT")
gauche:SetPoint("TOPLEFT", hg, "BOTTOMLEFT")
gauche:SetPoint("BOTTOMRIGHT", bg, "TOPRIGHT")
droite:SetPoint("TOPLEFT", hd, "BOTTOMLEFT")
droite:SetPoint("BOTTOMRIGHT", bd, "TOPRIGHT")
centre:SetPoint("TOPLEFT", hg, "BOTTOMRIGHT")
centre:SetPoint("BOTTOMRIGHT", bd, "TOPLEFT")
return tranches
end
-- LE BOUTON TERTIAIRE, EN DEUX ETATS.
--
-- common-button-tertiary-normal et ...-pressed, 46 x 34 chacun, sur la
-- feuille commonbuttontertiaryc60. Mesure sur l'art : a partir de x = 11 le
-- profil d'une colonne ne change plus -- l'about arrondi fait 11 px, d'ou un
-- coin de 11 sur les deux axes (11 + 24 + 11 en largeur, 11 + 12 + 11 en
-- hauteur). Etire au lieu d'etre decoupe, il ecraserait ses angles des qu'il
-- depasse 46 de large.
--
-- Le bouton garde ses propres textures, simplement effacees : elles portent
-- encore son etat pour le client, et certaines fonctions les lisent.
--
-- auto : l'etat presse suit le bouton de la souris. Sans lui, c'est a
-- l'appelant de commander, par bouton.foreverPresser(vrai ou faux) -- ce que
-- font les selecteurs de statistiques, dont l'etat tient tant que leur liste
-- est ouverte.
local TERTIAIRE_NORMAL = "common-button-tertiary-normal"
local TERTIAIRE_PRESSE = "common-button-tertiary-pressed"
local TERTIAIRE_COIN = 11
local TERTIAIRE_MARGES = { 0, 0, 0, 0 }
function ForeverUI.SkinTertiaryButton(bouton, auto)
if bouton.foreverPresser then
return bouton
end
for _, methode in ipairs({ "GetNormalTexture", "GetPushedTexture",
"GetHighlightTexture", "GetDisabledTexture" }) do
local texture = bouton[methode] and bouton[methode](bouton)
if texture then
texture:SetAlpha(0)
end
end
bouton.foreverNormal = ForeverUI.CreateNineSlice(bouton, TERTIAIRE_NORMAL,
TERTIAIRE_COIN, TERTIAIRE_MARGES, "BACKGROUND")
bouton.foreverPresse = ForeverUI.CreateNineSlice(bouton, TERTIAIRE_PRESSE,
TERTIAIRE_COIN, TERTIAIRE_MARGES, "BACKGROUND")
bouton.foreverPresser = function(etat)
for _, tranche in ipairs(bouton.foreverPresse or {}) do
if etat then tranche:Show() else tranche:Hide() end
end
for _, tranche in ipairs(bouton.foreverNormal or {}) do
if etat then tranche:Hide() else tranche:Show() end
end
end
bouton.foreverPresser(false)
if auto then
bouton:HookScript("OnMouseDown", function() bouton.foreverPresser(true) end)
bouton:HookScript("OnMouseUp", function() bouton.foreverPresser(false) end)
end
return bouton
end
-- L'ENCADRE DE CAMELOT -- InsetFrameTemplate (shareduipaneltemplates.xml et
-- nineslicelayouts.lua) : fond UI-Background-Marble en mosaique ; lisere
-- UI-Frame-InnerTopLeft / TopRight / BotLeftCorner / BotRight (6 x 6, les
-- deux du bas a y = -1) et _UI-Frame-InnerTopTile / BotTile,
-- !UI-Frame-InnerLeftTile / RightTile (3 d'epaisseur) entre eux.
-- Mis en commun le 2026-09-26 (volet des champs de bataille, fenetre Social).
local MARBRE = "interface" .. string.char(92) .. "ForeverUI" .. string.char(92)
.. "framegeneral" .. string.char(92) .. "ui-background-marble"
function ForeverUI.CreateInset(parent, nom)
return ForeverUI.DecorateInset(CreateFrame("Frame", nom, parent))
end
-- Le meme habillage, pose sur un cadre qui existe deja (le cadre des droits
-- de banque de la fenetre de controle de guilde, par exemple) : ses regions
-- a lui, sous ses cadres fils.
function ForeverUI.DecorateInset(e)
local fond = e:CreateTexture(nil, "BACKGROUND")
fond:SetTexture(MARBRE, true)
if fond.SetHorizTile then
fond:SetHorizTile(true)
fond:SetVertTile(true)
end
fond:SetAllPoints(e)
e.fond = fond
local function coin(atlas, point, y)
local t = e:CreateTexture(nil, "BORDER")
ForeverUI.SetAtlas(t, atlas)
t:SetPoint(point, e, point, 0, y or 0)
return t
end
local hg = coin("ui-frame-innertopleft", "TOPLEFT")
local hd = coin("ui-frame-innertopright", "TOPRIGHT")
local bg = coin("ui-frame-innerbotleftcorner", "BOTTOMLEFT", -1)
local bd = coin("ui-frame-innerbotright", "BOTTOMRIGHT", -1)
-- l'epaisseur vient de l'element (3), la longueur des coins
local function bord(atlas, a1, c1, r1, a2, c2, r2)
local t = e:CreateTexture(nil, "BORDER")
ForeverUI.SetAtlas(t, atlas, true)
t:SetPoint(a1, c1, r1)
t:SetPoint(a2, c2, r2)
return t
end
local haut = bord("_ui-frame-innertoptile", "TOPLEFT", hg, "TOPRIGHT", "TOPRIGHT", hd, "TOPLEFT")
haut:SetHeight(3)
local bas = bord("_ui-frame-innerbottile", "BOTTOMLEFT", bg, "BOTTOMRIGHT", "BOTTOMRIGHT", bd, "BOTTOMLEFT")
bas:SetHeight(3)
local gauche = bord("!ui-frame-innerlefttile", "TOPLEFT", hg, "BOTTOMLEFT", "BOTTOMLEFT", bg, "TOPLEFT")
gauche:SetWidth(3)
local droite = bord("!ui-frame-innerrighttile", "TOPRIGHT", hd, "BOTTOMRIGHT", "BOTTOMRIGHT", bd, "TOPRIGHT")
droite:SetWidth(3)
e.lisere = { hg, hd, bg, bd, haut, bas, gauche, droite }
return e
end
-- LE BOUTON DE PANNEAU DE CAMELOT -- UIPanelButtonTemplate : trois morceaux
-- de UI-Panel-Button-Up / -Down / -Disabled (gauche 12, milieu etire,
-- droite 12 ; 0,6875 de haut), surbrillance -Highlight en ADD.
-- Mis en commun le 2026-09-26 (equipes d'arene, champs de bataille, Social).
-- Activer(oui) : UIPanelButton_OnEnable / _OnDisable, l'image -Disabled et
-- la police grisee.
local BOUTON_PANNEAU = "Interface" .. string.char(92) .. "Buttons" .. string.char(92) .. "UI-Panel-Button-"
function ForeverUI.CreatePanelButton(parent, texteBouton, largeur, hauteur, nom, police, gabarit)
local b = CreateFrame("Button", nom, parent, gabarit)
b:SetWidth(largeur)
b:SetHeight(hauteur)
local function morceau(u1, u2)
local t = b:CreateTexture(nil, "BACKGROUND")
t:SetTexCoord(u1, u2, 0, 0.6875)
return t
end
local g = morceau(0, 0.09375)
g:SetWidth(12)
g:SetPoint("TOPLEFT", b, "TOPLEFT", 0, 0)
g:SetPoint("BOTTOMLEFT", b, "BOTTOMLEFT", 0, 0)
local d = morceau(0.53125, 0.625)
d:SetWidth(12)
d:SetPoint("TOPRIGHT", b, "TOPRIGHT", 0, 0)
d:SetPoint("BOTTOMRIGHT", b, "BOTTOMRIGHT", 0, 0)
local m = morceau(0.09375, 0.53125)
m:SetPoint("TOPLEFT", g, "TOPRIGHT", 0, 0)
m:SetPoint("BOTTOMRIGHT", d, "BOTTOMLEFT", 0, 0)
local morceaux = { g, m, d }
local function etat(suffixe)
for _, t in ipairs(morceaux) do
t:SetTexture(BOUTON_PANNEAU .. suffixe)
end
end
etat("Up")
local normale = police and _G[police .. ""] or GameFontNormalSmall
local survol = police and _G[(police == "GameFontNormal") and "GameFontHighlight" or "GameFontHighlightSmall"]
or GameFontHighlightSmall
local fs = b:CreateFontString(nil, "ARTWORK")
fs:SetFontObject(normale)
fs:SetPoint("CENTER", b, "CENTER", 0, 0)
b:SetFontString(fs)
b:SetNormalFontObject(normale)
b:SetHighlightFontObject(survol)
if b.SetDisabledFontObject then
b:SetDisabledFontObject((police == "GameFontNormal") and GameFontDisable or GameFontDisableSmall)
end
b:SetText(texteBouton)
b:SetHighlightTexture(BOUTON_PANNEAU .. "Highlight")
local s = b:GetHighlightTexture()
if s then
s:SetTexCoord(0, 0.625, 0, 0.6875)
s:SetBlendMode("ADD")
end
b.actif = true
b:SetScript("OnMouseDown", function(self) if self.actif then etat("Down") end end)
b:SetScript("OnMouseUp", function(self) if self.actif then etat("Up") end end)
function b:Activer(oui)
self.actif = oui and true or false
if oui then
self:Enable()
etat("Up")
else
self:Disable()
etat("Disabled")
end
end
return b
end