Files
moon-well-ui/data/addon/ForeverUI/GroupFinder.lua
T
Adrestie 2c94ea6bc9 PNJ etapes 1-2, barres selon le contenu, liste des donjons, tabard
- PNJ (NpcDialog.lua, nouveau) : dialogue et quete, livres et lettres,
  petition, registre de guilde a la DA de camelot (parchemin
  QuestBG-Parchment, portrait, croix, places de camelot, contours de
  qualite des objets).
- Barres de defilement : chacune cachee quand rien ne defile, et sans
  elle le contenu laisse a droite la marge qu'il a a gauche (courrier et
  papeterie, banque de guilde, hotel des ventes, reputation, competences,
  monnaies, contacts, guilde, macros, reglages, chercheur, dialogues des
  PNJ) ; ecrans d'accueil : selection des personnages (cartes centrees),
  royaumes, AddOns, creation.
- Chercheur de donjon : la liste des donjons reprend la liste a
  categories des raids (en-tetes de la feuille, cases de camelot),
  l'en-tete gardant sa case "tout cocher".
- Tabard : camera 1 et modele dans la moitie gauche de l'encadre, cadrage
  independant de la race (gnome).
- Cabine : portrait a 48 (1, 1.5) ; README a jour (fenetres secondaires,
  commerce).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 17:48:43 +02:00

1822 lines
68 KiB
Lua

-- ForeverUI : le chercheur de donjon de WotLK, a la DA du chercheur de groupe
-- de camelot, dans une fenetre deplacable (demande de l'utilisateur,
-- 2026-09-26 ; memoire foreverui-chercheur).
--
-- CAMELOT N'A PAS DE CHERCHEUR DE DONJON : blizzard_groupfinder.toc porte
-- ExcludeLoadGameType camelot. Il charge a la place
-- Blizzard_GroupFinder_VanillaStyle (LFGParentFrame), qui liste des groupes.
-- DECISION DE L'UTILISATEUR : la file de WotLK et ses donnees, presentees
-- comme le LFGParentFrame de camelot ; le navigateur de raid en onglet
-- lateral de la meme fenetre (etape 2).
--
-- RELEVE -- blizzard_groupfinder_vanillastyle/mainline (charge par [Family]) :
-- blizzard_lfgvanilla_parentframe.xml, blizzard_lfgvanilla_listing.xml/.lua,
-- lfgvanilla_constants.lua ; blizzard_sharedxml (PortraitFrameTemplate,
-- InsetFrameTemplate, LargeSideTabButtonTemplate, camelot/nineslicelayout
-- overrides) ; sharedutils.lua (GetTexCoordsForRole,
-- GetBackgroundTexCoordsForRole) :
-- fenetre 458 x 535 ; roche (2,-21 / -2,2), stries (6,-21 / -2,-21),
-- metal du cadre a portrait (coins -13,16 / 2,16 / -13,-8 /
-- 2,-8) ; portrait groupfinder-eye-frame 62 x 62 a (-5, 7) ;
-- titre LFG_TITLE, bande (58,-1 / -24,-1), texte TOP (0,-5) ;
-- croix TOPRIGHT (0, 0)
-- roles bandeau UI-LFG-BlueBG de (2,-25) au TOPRIGHT (248,-169),
-- etire, sans TexCoords ; boutons 64 x 64 a y -41 : tank
-- x 70, soigneur x 174, degats x 278, le quatrieme (celui du
-- nouveau venu, ici le CHEF de WotLK) TOPRIGHT (-20,-41) ; fond
-- UI-LFG-ICONS-ROLEBACKGROUNDS 80 x 80 centre (alpha 0,6 / 0,4
-- / 0,6), icone UI-LFG-ICON-ROLES, voile (0,5) quand le role
-- est refuse ; case UI-CheckBox 24 x 24 a BOTTOMLEFT (-5,-5)
-- encadre (4,-118 / -4,37), sans marbre ; groupfinder-background de
-- (3,-3) a (-3,3), TexCoords top 0,093
-- categories (8,-124 / -8,40) ; boutons 380 x 60, le premier a TOP
-- (0,-20), pas de 68 [RESSERRES, voir plus bas] ; image
-- groupfinder-button-* (5,-8 /
-- -5,5), couvercle groupfinder-button-cover, selection et
-- surbrillance PvPMegaQueue 366 x 48 en ADD, texte
-- GameFontNormalLarge a LEFT (20, 0)
-- liste (7,-125 / -7,40), marge 4, lignes de 22 ; barre a droite
-- (liste a -28 avec, 0 sans) ; ligne : +/- 16 x 16 a LEFT,
-- case 30 x 30 a sa droite (+4), nom, niveau 60 x 22 a RIGHT ;
-- en-tete gris 0,7, entree or, trop facile gris 0,5, trop dur
-- orange (1, 0,5, 0,25)
-- boutons Back 111 x 28 BOTTOMLEFT (4,6), Post 109 x 28 BOTTOMRIGHT
-- (-4,6)
-- onglets LargeSideTabButtonTemplate 55 x 55 a TOPLEFT sur TOPRIGHT
-- (0,-60), les suivants 2 plus bas ; icone INV_Helmet_08 pour
-- le premier
--
-- CE QUI VIENT DE WotLK (LFDFrame.lua, LFGFrame.lua) : TOUT ce qui est donnee
-- ou action. Le panneau LFDParentFrame reste celui du client -- le
-- micro-bouton, /lfd, la touche, l'oeil de la minimap, Echap et la
-- conversation d'un PNJ l'ouvrent et le ferment comme avant. Son contenu
-- (LFDQueueFrame) reste affiche mais transparent, SOUS notre fenetre : toute
-- sa logique tourne donc comme prevu (demandes de verrous a l'ouverture,
-- listes filtrees, bouton de recherche, attente, reprise de groupe). Notre
-- fenetre en lit l'etat apres chacune de ses mises a jour, et agit par ses
-- fonctions : LFDQueueFrame_SetType, les cases et les +/- de la liste, les
-- cases de role, le bouton de recherche.
--
-- CORRESPONDANCES (camelot -> WotLK) :
-- categories le menu "Type" de WotLK : un bouton par donjon aleatoire
-- affichable, puis "Specific Dungeons" (meme ordre, memes
-- refus et meme infobulle) ; le bouton du type choisi porte
-- la selection
-- liste la liste des donjons specifiques (en-tetes, cases a trois
-- etats, verrous) -- depuis le 28/09, la liste a categories
-- de celle des raids (F.creerListeCat), l'en-tete gardant sa
-- case
-- details le texte et les recompenses du donjon aleatoire choisi
-- (texte, recompenses, argent, experience) -- camelot n'en a
-- pas : sous les categories, aux polices de camelot
-- Back retour aux categories, visible dans la liste seulement ; Post -> le bouton
-- de recherche du client (Find Group / Join as Party / Leave
-- Queue)
-- voiles l'attente et la desertion, la reprise d'un groupe, "pas de
-- donjon pendant le raid" : ceux de WotLK, sur ce qui suit le
-- choix du type, comme chez WotLK ou le menu "Type" restait
-- accessible -- les details, ou toute la liste
--
-- DEMANDE DE L'UTILISATEUR (2026-09-26, apres la premiere livraison) : les
-- boutons resserres vers le haut (premier a 6 sous le haut de la zone, pas
-- de 62 au lieu de 68), un separateur dessous (le filet de camelot,
-- shop-list-rule), et sous le separateur le texte et les recompenses du
-- bouton choisi. Choisir un donjon aleatoire reste sur la page ; seul
-- "Specific Dungeons" ouvre la liste.
-- PUIS (meme jour) : boutons encore resserres et amincis -- 380 x 40 au
-- lieu de 60, colles (pas de 40), le premier a 4 sous le haut de la zone ;
-- l'image, la selection et la surbrillance suivent la meme proportion
-- (40 / 60) ; filet et details rapproches, ecarts du texte reduits : le
-- but est de loger le texte et les recompenses sans barre. La barre reste,
-- pour le cas qui depasse encore.
-- PUIS : le filet DESCEND sur le contenu. Les details sont poses en bas de
-- l'encadre, a leur hauteur, et le filet juste au-dessus ; il ne monte
-- jamais plus haut que sous le dernier bouton (6 dessous), ou la barre
-- prend le relais.
-- PUIS ("un peu trop descendu, le scroller est reapparu") : trois causes
-- corrigees -- la barre comptait en crans arrondis (contenu vers le haut,
-- zone vers le bas) et apparaissait des qu'une hauteur n'etait pas un
-- multiple de 20 ; la zone visible etait lue par GetHeight juste apres le
-- reancrage, ou le client peut encore rendre l'ancienne ; la hauteur d'un
-- texte replie peut etre sous-estimee a la premiere mesure. Desormais : la
-- zone visible se calcule d'apres nos propres ecarts, 6 d'air au-dessus du
-- contenu, une tolerance de 2, et une seconde mesure a l'image suivante (du
-- haut du contenu au bas de sa derniere piece) qui replace le filet si la
-- premiere etait courte.
-- PUIS : les boutons s'epaississent LEGEREMENT, de 40 a 46 -- la hauteur
-- propre de groupfinder-button-cover, sans etirement vertical -- toujours
-- colles (pas de 46) ; image, selection et surbrillance au prorata (46 /
-- 60). Le filet et les details ne bougent pas : poses sur le contenu.
-- PUIS : les details prennent le decalage des boutons -- 380 de large,
-- centres dans la zone de 442 qui part a 8 : leur bord gauche tombe a
-- 8 + (442 - 380) / 2 = 39, le droit a 39 du bord droit. La barre, si elle
-- vient, se pose 5 a droite.
-- PUIS ("un peu plus") : le bord gauche avance de 6, a 45 ; le droit reste.
-- PUIS : l'argent devient une case comme les objets -- l'icone de pieces
-- de WotLK (inv_misc_coin_02, celle de sa fenetre "donjon pret") et le
-- montant dans le cadre de nom, a la place suivante de la grille.
-- PUIS : filet et details remontes de 5 -- le bas des details passe de 45
-- a 50 au-dessus du bas de la fenetre.
-- PUIS : boutons epaissis de 2, a 48 (pas de 48, selection 38 = 48 x 48 / 60).
--
-- ETAPE 2 (2026-09-26) : le navigateur de raid, dans GroupFinderRaid.lua. Deux
-- onglets lateraux (demande de l'utilisateur, apres un premier essai a trois) :
-- donjons (INV_Helmet_08) et "Raid Browser" (Achievement_General_StayClassy),
-- une seule page a deux sections retractables. Les deux
-- panneaux du client restent les portes d'entree : LFDParentFrame pour le
-- premier onglet, LFRParentFrame pour le second. Notre fenetre -- la notre, pas un cadre du client -- passe de
-- l'un a l'autre (F.attacher) ; un seul des deux est ouvert a la fois. Les
-- lignes de liste, le bandeau des roles et l'encadre sont communs aux pages.
-- PUIS ("encore") : 2 de plus, a 50 (pas de 50, selection 40).
--
-- ECARTS SIGNALES : pas de bouton d'options (camelot en a un, rien a y
-- mettre) ; pas de commentaire ni de filet shop-list-rule sous la liste des
-- donjons (WotLK n'a pas de commentaire pour les donjons : la liste descend
-- jusqu'en bas) ; le verrou de WotLK (UI-LFG-ICON-LOCK) prend la place de la
-- case d'un donjon interdit ; pas d'icone heroique sur les en-tetes (leur
-- nom le dit).
local ForeverUI = ForeverUI or {}
_G.ForeverUI = ForeverUI
local F = {}
ForeverUI.GroupFinder = F
local SEP = string.char(92)
local L = ForeverUI.L
local LFG = "Interface" .. SEP .. "LFGFrame" .. SEP
local BOUTONS = "Interface" .. SEP .. "Buttons" .. SEP
-- une table : Lua 5.1 limite a 60 les valeurs capturees par une fonction
local G = {
largeur = 458, hauteur = 535,
roche = "interface" .. SEP .. "ForeverUI" .. SEP .. "framegeneral" .. SEP .. "ui-background-rock",
portraitCote = 62, portraitX = -5, portraitY = 7,
titreX1 = 58, titreX2 = -24, titreY = -1, titreH = 20, titreTexteY = -5,
croix = 24, croixX = 0, croixY = 0,
bleu = "interface" .. SEP .. "ForeverUI" .. SEP .. "lfgframe" .. SEP .. "ui-lfg-bluebg",
bleuX1 = 2, bleuY1 = -25, bleuX2 = 248, bleuY2 = -169,
role = 64, roleY = -41, roleFond = 80, caseRole = 24, caseRoleX = -5, caseRoleY = -5,
roleIcones = LFG .. "UI-LFG-ICON-ROLES",
roleFonds = LFG .. "UI-LFG-ICONS-ROLEBACKGROUNDS",
encadreX1 = 4, encadreY1 = -118, encadreX2 = -4, encadreY2 = 37,
fondHaut = 0.093,
catX1 = 8, catY1 = -124, catX2 = -8, catY2 = 40, catPremier = -4, catPas = 50,
-- le filet sous les boutons, puis les details
regleEcart = 6, regleH = 16, detailsEcart = 6, detailsAir = 6, tolerance = 2, detailsX1 = 45, detailsX2 = -39, detailsBas = 50,
cran = 20,
catL = 380, catH = 50, catSelL = 366, catSelH = 40,
-- l'image dans le bouton : (5,-8 / -5,5) de camelot, ramenes a 50 de haut
catImageHaut = -6, catImageBas = 4,
mega = "interface" .. SEP .. "ForeverUI" .. SEP .. "pvpframe" .. SEP .. "pvpmegaqueue",
pieces = "Interface" .. SEP .. "Icons" .. SEP .. "inv_misc_coin_02",
listeX1 = 7, listeY1 = -125, listeX2 = -7, listeY2 = 40, marge = 4, barreDroite = -28,
ligne = 22, plus = 16, case = 30, niveauL = 60,
backL = 111, postL = 109, boutonH = 28, boutonBas = 6, boutonCote = 4,
onglet = 55, ongletY = -60, ongletEcart = -2, ongletIcone = 50, ongletIconeX = -3, rognage = 0.03125,
voileL = 330, voileH = 257,
}
-- les coordonnees de sharedutils.lua (GetTexCoordsForRole : 256 x 256,
-- cases de 67 ; GetBackgroundTexCoordsForRole : 256 x 128, cases de 75)
local ROLES = {
{ cle = "tank", client = "LFDQueueFrameRoleButtonTank", id = 2, x = 70, alpha = 0.6,
icone = { 0, 0.26171875, 0.26171875, 0.5234375 }, fond = { 0.29296875, 0.5859375, 0, 0.5859375 } },
{ cle = "soin", client = "LFDQueueFrameRoleButtonHealer", id = 3, x = 174, alpha = 0.4,
icone = { 0.26171875, 0.5234375, 0, 0.26171875 }, fond = { 0, 0.29296875, 0, 0.5859375 } },
{ cle = "degats", client = "LFDQueueFrameRoleButtonDPS", id = 1, x = 278, alpha = 0.6,
icone = { 0.26171875, 0.5234375, 0.26171875, 0.5234375 }, fond = { 0.5859375, 0.87890625, 0, 0.5859375 } },
-- le quatrieme emplacement de camelot (TOPRIGHT -20,-41), sans fond
{ cle = "chef", client = "LFDQueueFrameRoleButtonLeader", id = 4, droite = -20,
icone = { 0, 0.26171875, 0, 0.26171875 } },
}
local VOILE_ROLE = { 0, 0.2617, 0.5234, 0.7851 }
-- les polices de fontstyles.xml (LFGActivityHeader, LFGActivityEntry...)
local COULEURS = {
entete = { 0.7, 0.7, 0.7 },
entree = { 1.0, 0.82, 0 },
facile = { 0.5, 0.5, 0.5 },
dur = { 1.0, 0.5, 0.25 },
}
-- l'image d'un bouton de categorie
local ART = {
aleatoire = "groupfinder-button-dungeons",
fete = "groupfinder-button-questing",
specifique = "groupfinder-button-custom-pve",
}
local function txt(cle)
return _G[cle] or cle
end
-- 3.3.5 rend 1 ou nil (et le banc 0 ou 1) : on compare
local function actif(b)
return b ~= nil and b.IsEnabled ~= nil and b:IsEnabled() == 1
end
-- visible pour de bon : le cadre et ses parents jusqu'au panneau
local function affiche(c)
while c do
if not c:IsShown() then return false end
if c == LFDParentFrame then return true end
c = c:GetParent()
end
return false
end
-- ------------------------------------------------------------------ le cadre
local METAL = {
{ cle = "hg", nom = "ui-frame-portraitmetal-cornertopleft", point = "TOPLEFT", x = -13, y = 16 },
{ cle = "hd", nom = "ui-frame-metal-cornertopright", point = "TOPRIGHT", x = 2, y = 16 },
{ cle = "bg", nom = "ui-frame-metal-cornerbottomleft", point = "BOTTOMLEFT", x = -13, y = -8 },
{ cle = "bd", nom = "ui-frame-metal-cornerbottomright", point = "BOTTOMRIGHT", x = 2, y = -8 },
}
local function croixRouge(b)
for _, etat in ipairs({
{ "SetNormalTexture", "GetNormalTexture", "redbutton-exit" },
{ "SetPushedTexture", "GetPushedTexture", "redbutton-exit-pressed" },
{ "SetHighlightTexture", "GetHighlightTexture", "redbutton-highlight" },
}) do
local e = ForeverUI.AtlasEntry(etat[3])
b[etat[1]](b, e and e[1] or "")
local t = b[etat[2]](b)
if t then
ForeverUI.SetAtlas(t, etat[3], true)
t:ClearAllPoints()
t:SetAllPoints(b)
if etat[3] == "redbutton-highlight" then t:SetBlendMode("ADD") end
end
end
end
local function construireCadre(f)
local roche = f:CreateTexture(nil, "BACKGROUND")
roche:SetTexture(G.roche, true)
if roche.SetHorizTile then roche:SetHorizTile(true) roche:SetVertTile(true) end
roche:SetPoint("TOPLEFT", f, "TOPLEFT", 2, -21)
roche:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -2, 2)
local stries = f:CreateTexture(nil, "BORDER")
ForeverUI.SetAtlas(stries, "_ui-frame-toptilestreaks", true)
stries:SetHeight(43)
stries:SetPoint("TOPLEFT", f, "TOPLEFT", 6, -21)
stries:SetPoint("TOPRIGHT", f, "TOPRIGHT", -2, -21)
local metal = CreateFrame("Frame", nil, f)
metal:SetAllPoints(f)
metal:SetFrameLevel(f:GetFrameLevel() + 20)
local p = {}
for _, coin in ipairs(METAL) do
local t = metal:CreateTexture(nil, "OVERLAY")
ForeverUI.SetAtlas(t, coin.nom)
t:SetPoint(coin.point, metal, coin.point, coin.x, coin.y)
p[coin.cle] = t
end
local function bord(nom, a1, c1, r1, a2, c2, r2)
local t = metal:CreateTexture(nil, "OVERLAY")
ForeverUI.SetAtlas(t, nom)
t:SetPoint(a1, c1, r1)
t:SetPoint(a2, c2, r2)
end
bord("_ui-frame-metal-edgetop", "TOPLEFT", p.hg, "TOPRIGHT", "TOPRIGHT", p.hd, "TOPLEFT")
bord("_ui-frame-metal-edgebottom", "BOTTOMLEFT", p.bg, "BOTTOMRIGHT", "BOTTOMRIGHT", p.bd, "BOTTOMLEFT")
bord("!ui-frame-metal-edgeleft", "TOPLEFT", p.hg, "BOTTOMLEFT", "BOTTOMLEFT", p.bg, "TOPLEFT")
bord("!ui-frame-metal-edgeright", "TOPRIGHT", p.hd, "BOTTOMRIGHT", "BOTTOMRIGHT", p.bd, "TOPRIGHT")
-- le portrait : l'oeil de camelot (SetPortraitAtlasRaw, 62 x 62)
local cadrePortrait = CreateFrame("Frame", nil, f)
cadrePortrait:SetAllPoints(f)
cadrePortrait:SetFrameLevel(f:GetFrameLevel() + 19)
local portrait = cadrePortrait:CreateTexture(nil, "OVERLAY")
ForeverUI.SetAtlas(portrait, "groupfinder-eye-frame", true)
portrait:SetWidth(G.portraitCote)
portrait:SetHeight(G.portraitCote)
portrait:SetPoint("TOPLEFT", f, "TOPLEFT", G.portraitX, G.portraitY)
f.portrait = portrait
local bandeau = CreateFrame("Frame", nil, f)
bandeau:SetFrameLevel(f:GetFrameLevel() + 21)
bandeau:SetHeight(G.titreH)
bandeau:SetPoint("TOPLEFT", f, "TOPLEFT", G.titreX1, G.titreY)
bandeau:SetPoint("TOPRIGHT", f, "TOPRIGHT", G.titreX2, G.titreY)
f.titre = bandeau:CreateFontString(nil, "OVERLAY", "GameFontNormal")
f.titre:SetPoint("TOP", bandeau, "TOP", 0, G.titreTexteY)
f.titre:SetText(txt("LFG_TITLE"))
f.bandeau = bandeau
-- LA CROIX : elle ferme le panneau du client qui porte la fenetre, comme
-- la sienne
local croix = CreateFrame("Button", "ForeverUIGroupFinderCloseButton", f)
croix:SetWidth(G.croix)
croix:SetHeight(G.croix)
croix:SetFrameLevel(f:GetFrameLevel() + 22)
croix:SetPoint("TOPRIGHT", f, "TOPRIGHT", G.croixX, G.croixY)
croixRouge(croix)
croix:SetScript("OnClick", function() HideUIPanel(F.cadre:GetParent()) end)
f.croix = croix
end
-- ------------------------------------------------------------ les onglets
-- LargeSideTabButtonTemplate, comme ceux de la feuille (CharacterFrame.lua) :
-- fond common-sidetab, icone cuite par tools/cuire_masque.py a 50 x 50 en
-- (-3, 0) rognee de 0,03125, marqueur common-sidetab-selected, survol
-- common-sidetab-hover.
local ICONES = "Interface" .. SEP .. "ForeverUI" .. SEP .. "tabicons" .. SEP
local ONGLETS = {
{ cle = "donjons", texte = "LOOKING_FOR_DUNGEON", icone = ICONES .. "inv_helmet_08" },
-- un seul onglet pour le navigateur de raid (demande du 2026-09-26), a
-- l'icone de l'ancien onglet de parcours
{ cle = "raid", texte = "LOOKING_FOR_RAID", raid = true,
icone = ICONES .. "achievement_general_stayclassy" },
}
local function creerOnglet(f, def, n)
local o = CreateFrame("Button", "ForeverUIGroupFinderTab" .. n, f)
o:SetWidth(G.onglet)
o:SetHeight(G.onglet)
local fond = o:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(fond, "common-sidetab", true)
fond:SetAllPoints(o)
local icone = o:CreateTexture(nil, "ARTWORK")
icone:SetWidth(G.ongletIcone)
icone:SetHeight(G.ongletIcone)
icone:SetPoint("CENTER", o, "CENTER", G.ongletIconeX, 0)
icone:SetTexCoord(G.rognage, 1 - G.rognage, G.rognage, 1 - G.rognage)
icone:SetTexture(def.icone)
o.icone = icone
local choisi = o:CreateTexture(nil, "OVERLAY")
ForeverUI.SetAtlas(choisi, "common-sidetab-selected", true)
choisi:SetAllPoints(o)
choisi:Hide()
o.choisi = choisi
local survol = o:CreateTexture(nil, "HIGHLIGHT")
ForeverUI.SetAtlas(survol, "common-sidetab-hover", true)
survol:SetAllPoints(o)
o:SetScript("OnEnter", function(self)
GameTooltip:SetOwner(self, "ANCHOR_RIGHT", -4, -4)
GameTooltip:SetText(txt(def.texte))
if def.detail then GameTooltip:AddLine(txt(def.detail), 1, 1, 1) end
GameTooltip:Show()
end)
o:SetScript("OnLeave", function() GameTooltip:Hide() end)
o:SetScript("OnClick", function()
PlaySound("igCharacterInfoTab")
F.allerA(def.cle)
end)
o.cle = def.cle
return o
end
function F.choisirOnglet(cle)
F.onglet = cle
for _, o in ipairs(F.onglets or {}) do
if o.cle == cle then o.choisi:Show() else o.choisi:Hide() end
end
end
-- ------------------------------------------------------------ les roles
local function creerRole(parent, r)
local b = CreateFrame("Button", "ForeverUIGroupFinderRole" .. (r.page or "") .. r.cle, parent)
b:SetWidth(G.role)
b:SetHeight(G.role)
if r.droite then
b:SetPoint("TOPRIGHT", F.cadre, "TOPRIGHT", r.droite, G.roleY)
else
b:SetPoint("TOPLEFT", F.cadre, "TOPLEFT", r.x, G.roleY)
end
if r.fond then
local fond = b:CreateTexture(nil, "BACKGROUND")
fond:SetTexture(G.roleFonds)
fond:SetTexCoord(r.fond[1], r.fond[2], r.fond[3], r.fond[4])
fond:SetWidth(G.roleFond)
fond:SetHeight(G.roleFond)
fond:SetPoint("CENTER", b, "CENTER", 0, 0)
fond:SetAlpha(r.alpha)
b.fond = fond
end
local icone = b:CreateTexture(nil, "ARTWORK")
icone:SetTexture(G.roleIcones)
icone:SetTexCoord(r.icone[1], r.icone[2], r.icone[3], r.icone[4])
icone:SetAllPoints(b)
b.icone = icone
local voile = b:CreateTexture(nil, "OVERLAY")
voile:SetTexture(G.roleIcones)
voile:SetTexCoord(VOILE_ROLE[1], VOILE_ROLE[2], VOILE_ROLE[3], VOILE_ROLE[4])
voile:SetAllPoints(b)
voile:SetAlpha(0.5)
voile:Hide()
b.voile = voile
local c = CreateFrame("CheckButton", "ForeverUIGroupFinderRole" .. (r.page or "") .. r.cle .. "Check", b)
c:SetWidth(G.caseRole)
c:SetHeight(G.caseRole)
c:SetPoint("BOTTOMLEFT", b, "BOTTOMLEFT", G.caseRoleX, G.caseRoleY)
c:SetNormalTexture(BOUTONS .. "UI-CheckBox-Up")
c:SetPushedTexture(BOUTONS .. "UI-CheckBox-Down")
c:SetHighlightTexture(BOUTONS .. "UI-CheckBox-Highlight")
local s = c:GetHighlightTexture()
if s then s:SetBlendMode("ADD") end
c:SetCheckedTexture(BOUTONS .. "UI-CheckBox-Check")
c:SetDisabledCheckedTexture(BOUTONS .. "UI-CheckBox-Check-Disabled")
b.case = c
-- LA CASE DU CLIENT FAIT LE TRAVAIL : son Click la bascule et joue son
-- OnClick (son, LFDQueueFrame_SetRoles). La notre suit ensuite.
c:SetScript("OnClick", function(self)
local client = _G[r.client]
self:SetChecked(not self:GetChecked())
if client and client.checkButton and actif(client.checkButton) then
client.checkButton:Click()
end
F.majRoles()
end)
b:SetScript("OnClick", function()
if actif(c) then c:Click() end
end)
b:SetScript("OnEnter", function(self)
local client = _G[r.client]
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
if r.cle == "chef" then
GameTooltip:SetText(txt("GUIDE_TOOLTIP"), nil, nil, nil, nil, 1)
else
GameTooltip:SetText(txt("ROLE_DESCRIPTION" .. r.id), nil, nil, nil, nil, 1)
if client and client.permDisabled then
local rouge = RED_FONT_COLOR or { r = 1, g = 0.1, b = 0.1 }
GameTooltip:AddLine(txt("YOUR_CLASS_MAY_NOT_PERFORM_ROLE"), rouge.r, rouge.g, rouge.b, 1)
end
end
GameTooltip:Show()
if actif(c) then c:LockHighlight() end
end)
b:SetScript("OnLeave", function()
GameTooltip:Hide()
c:UnlockHighlight()
end)
b.def = r
return b
end
F.creerRole = creerRole
-- l'etat de chaque bouton de role du client, recopie (ceux de toutes les
-- pages)
function F.majRoles()
for _, b in ipairs(F.tousRoles or {}) do
local client = _G[b.def.client]
if client and client.checkButton then
b.case:SetChecked(client.checkButton:GetChecked())
if client.checkButton:IsShown() then b.case:Show() else b.case:Hide() end
if actif(client.checkButton) then b.case:Enable() else b.case:Disable() end
if client.cover and client.cover:IsShown() then
b.voile:SetAlpha(client.cover:GetAlpha())
b.voile:Show()
else
b.voile:Hide()
end
if b.fond then
if client.background and client.background:IsShown() then b.fond:Show() else b.fond:Hide() end
end
b.icone:SetDesaturated(client.permDisabled and true or false)
end
end
end
-- ------------------------------------------------------------ les categories
-- isRandomDungeonDisplayable de LFDFrame.lua (locale au client)
local function aleatoireAffichable(id)
local _, _, minimum, maximum, _, _, _, extension = GetLFGDungeonInfo(id)
local niveau = UnitLevel("player")
local ext = (GetExpansionLevel and GetExpansionLevel()) or 2
return minimum and niveau >= minimum and niveau <= maximum and ext >= (extension or 0)
end
-- LFDQueueFrameTypeDropDown_Initialize : "specific" d'abord, puis chaque
-- aleatoire affichable, rejoignable ou non
function F.categories()
local l = { { valeur = "specific", texte = txt("SPECIFIC_DUNGEONS"), art = ART.specifique, dispo = true } }
for i = 1, (GetNumRandomDungeons and GetNumRandomDungeons() or 0) do
local id, nom = GetLFGRandomDungeonInfo(i)
if id and aleatoireAffichable(id) then
local fete = select(14, GetLFGDungeonInfo(id))
table.insert(l, { valeur = id, texte = nom, art = fete and ART.fete or ART.aleatoire,
dispo = IsLFGDungeonJoinable(id) and true or false })
end
end
return l
end
local function creerCategorie(ligne)
local b = CreateFrame("Button", nil, ligne)
b:SetWidth(G.catL)
b:SetHeight(G.catH)
b:SetPoint("TOP", ligne, "TOP", 0, 0)
local image = b:CreateTexture(nil, "BACKGROUND")
image:SetPoint("TOPLEFT", b, "TOPLEFT", 5, G.catImageHaut)
image:SetPoint("BOTTOMRIGHT", b, "BOTTOMRIGHT", -5, G.catImageBas)
b.image = image
local couvercle = b:CreateTexture(nil, "ARTWORK")
ForeverUI.SetAtlas(couvercle, "groupfinder-button-cover", true)
couvercle:SetAllPoints(b)
local choix = b:CreateTexture(nil, "OVERLAY")
choix:SetTexture(G.mega)
choix:SetTexCoord(0.00195313, 0.63867188, 0.76953125, 0.83007813)
choix:SetBlendMode("ADD")
choix:SetWidth(G.catSelL)
choix:SetHeight(G.catSelH)
choix:SetPoint("CENTER", b, "CENTER", 0, 0)
choix:Hide()
b.choix = choix
b:SetHighlightTexture(G.mega)
local s = b:GetHighlightTexture()
if s then
s:SetTexCoord(0.00195313, 0.63867188, 0.70703125, 0.76757813)
s:SetBlendMode("ADD")
s:ClearAllPoints()
s:SetWidth(G.catSelL)
s:SetHeight(G.catSelH)
s:SetPoint("CENTER", b, "CENTER", 0, 0)
end
local texte = b:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
texte:SetPoint("LEFT", b, "LEFT", 20, 0)
texte:SetJustifyH("LEFT")
b.texte = texte
b:SetScript("OnClick", function(self)
local c = self.categorie
if not c or not c.dispo then return end
PlaySound("igMainMenuOptionCheckBoxOn")
LFDQueueFrame_SetType(c.valeur)
F.vue = (c.valeur == "specific") and "liste" or "categories"
F.maj()
end)
b:SetScript("OnEnter", function(self)
local c = self.categorie
if c and not c.dispo then
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
GameTooltip:SetText(txt("YOU_MAY_NOT_QUEUE_FOR_THIS"), 1, 1, 1)
local raison = LFDConstructDeclinedMessage and LFDConstructDeclinedMessage(c.valeur)
if raison then GameTooltip:AddLine(raison, nil, nil, nil, 1) end
GameTooltip:Show()
end
end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
ligne.bouton = b
end
local function remplirCategorie(ligne, index)
local c = F.liste_categories[index]
local b = ligne.bouton
b.categorie = c
ForeverUI.SetAtlas(b.image, c.art, true)
b.image:SetDesaturated(not c.dispo)
b.texte:SetText(c.texte)
if c.dispo then
b.texte:SetTextColor(1, 0.82, 0)
else
b.texte:SetTextColor(0.5, 0.5, 0.5)
end
if LFDQueueFrame.type == c.valeur then b.choix:Show() else b.choix:Hide() end
end
-- ------------------------------------------------------------ la liste
-- Les lignes des donjons et celles des raids sont les memes ; ce qui
-- differe -- la liste, les fonctions du client, les cases rondes d'un
-- groupe pour les raids -- vient de la SOURCE de la liste (zone.src).
-- LFGSpecificChoiceEnableButton_SetIsRadio (LFGFrame.lua) : en groupe, un
-- raid se choisit par un bouton rond, UI-RadioButton en quatre quarts, 17 x
-- 17 ; seul, par une case.
-- LA CASE DE CAMELOT (source de style "camelot" : la liste des raids,
-- demande du 2026-09-26) : checkbox-minimal / checkmark-minimal, comme les
-- cases de la feuille et de Social (S.habillerCase) ; en groupe,
-- common-radiobutton-circle / -dot. 20 x 20 dans une ligne de 22 (camelot
-- pose sa case a l'echelle 0,7 sur 30 x 29), le rond a 16.
local CASE_CAMELOT, ROND_CAMELOT = 20, 16
local function caseCamelot(c, radio)
if not radio then
ForeverUI.Social.habillerCase(c)
return
end
local function poser(set, get, atlas, grise)
local e = ForeverUI.AtlasEntry(atlas)
c[set](c, e and e[1] or "")
local t = c[get](c)
if t then
ForeverUI.SetAtlas(t, atlas, true)
t:ClearAllPoints()
t:SetWidth(ROND_CAMELOT)
t:SetHeight(ROND_CAMELOT)
t:SetPoint("CENTER", c, "CENTER", 0, 0)
if grise then t:SetDesaturated(true) end
end
end
poser("SetNormalTexture", "GetNormalTexture", "common-radiobutton-circle")
poser("SetPushedTexture", "GetPushedTexture", "common-radiobutton-circle")
poser("SetCheckedTexture", "GetCheckedTexture", "common-radiobutton-dot")
poser("SetDisabledCheckedTexture", "GetDisabledCheckedTexture", "common-radiobutton-dot", true)
c:SetHighlightTexture("")
end
local function caseRadio(c, radio)
if c.radio == radio then return end
c.radio = radio
if c.style == "camelot" then
caseCamelot(c, radio)
return
end
local etats = {
{ "SetNormalTexture", "GetNormalTexture", "UI-CheckBox-Up", 0, 0.25 },
{ "SetPushedTexture", "GetPushedTexture", "UI-CheckBox-Down", 0, 0.25 },
{ "SetHighlightTexture", "GetHighlightTexture", "UI-CheckBox-Highlight", 0.5, 0.75 },
{ "SetCheckedTexture", "GetCheckedTexture", "UI-CheckBox-Check", 0.25, 0.5 },
}
for _, e in ipairs(etats) do
c[e[1]](c, BOUTONS .. (radio and "UI-RadioButton" or e[3]))
local t = c[e[2]](c)
if t then
t:ClearAllPoints()
if radio then
t:SetTexCoord(e[4], e[5], 0, 1)
t:SetWidth(17)
t:SetHeight(17)
t:SetPoint("CENTER", c, "CENTER", 0, 0)
else
t:SetTexCoord(0, 1, 0, 1)
t:SetAllPoints(c)
end
if e[1] == "SetHighlightTexture" then t:SetBlendMode("ADD") end
end
end
end
-- la source des donjons (LFDFrame.lua) ; les cases de camelot et l'en-tete
-- a case (choix de l'utilisateur, 28/09 : la liste des donjons comme celle
-- des raids, l'en-tete gardant sa case « tout cocher »)
local SOURCE_LFD = {
style = "camelot",
caseEntete = true,
replier = function(id, replie) LFDList_SetHeaderCollapsed(id, replie) end,
liste = function() return LFDDungeonList end,
habilite = function() return LFD_IsEmpowered() end,
plus = function(b) LFDQueueFrameExpandOrCollapseButton_OnClick(b) end,
case = function(c) LFDQueueFrameDungeonChoiceEnableButton_OnClick(c) end,
plusieurs = function() return true end,
verrou = function(id) return LFGLockList[id] end,
etat = function(id, mode)
if mode == "queued" or mode == "listed" then return LFGQueuedForList[id] end
return LFGEnabledList[id]
end,
}
F.SOURCE_LFD = SOURCE_LFD
local function creerLigne(ligne)
ligne.src = ligne:GetParent().src
local plus = CreateFrame("Button", nil, ligne)
plus:SetWidth(G.plus)
plus:SetHeight(G.plus)
plus:SetPoint("LEFT", ligne, "LEFT", 0, 0)
plus:SetHitRectInsets(1, -4, -2, -2)
plus:SetNormalTexture(BOUTONS .. "UI-MinusButton-UP")
local n = plus:GetNormalTexture()
if n then
n:ClearAllPoints()
n:SetWidth(20)
n:SetHeight(20)
n:SetPoint("LEFT", plus, "LEFT", 3, 0)
end
plus:SetHighlightTexture(BOUTONS .. "UI-PlusButton-Hilight")
local s = plus:GetHighlightTexture()
if s then
s:SetBlendMode("ADD")
s:ClearAllPoints()
s:SetWidth(20)
s:SetHeight(20)
s:SetPoint("LEFT", plus, "LEFT", 3, 0)
end
-- le +/- du client : LFDQueueFrameExpandOrCollapseButton_OnClick lit
-- self:GetParent().id et .isCollapsed
plus:SetScript("OnClick", function(self)
PlaySound("igMainMenuOptionCheckBoxOn")
ligne.src.plus(self)
end)
ligne.plus = plus
local c = CreateFrame("CheckButton", nil, ligne)
c.style = ligne.src.style
local cote = (c.style == "camelot") and CASE_CAMELOT or G.case
c:SetWidth(cote)
c:SetHeight(cote)
c:SetPoint("LEFT", plus, "RIGHT", 4, 0)
c:SetNormalTexture(BOUTONS .. "UI-CheckBox-Up")
c:SetPushedTexture(BOUTONS .. "UI-CheckBox-Down")
c:SetHighlightTexture(BOUTONS .. "UI-CheckBox-Highlight")
local h = c:GetHighlightTexture()
if h then h:SetBlendMode("ADD") end
c:SetCheckedTexture(BOUTONS .. "UI-CheckBox-Check")
c:SetDisabledCheckedTexture(BOUTONS .. "UI-CheckBox-Check-Disabled")
-- la case du client : LFDQueueFrameDungeonChoiceEnableButton_OnClick lit
-- self:GetParent().id et self:GetChecked()
c:SetScript("OnClick", function(self)
ligne.src.case(self)
end)
ligne.case = c
-- le verrou de WotLK, a la place de la case
local verrou = ligne:CreateTexture(nil, "ARTWORK")
verrou:SetTexture(LFG .. "UI-LFG-ICON-LOCK")
verrou:SetTexCoord(0, 0.71875, 0, 0.875)
verrou:SetWidth(12)
verrou:SetHeight(14)
verrou:SetPoint("CENTER", c, "CENTER", 0, 0)
verrou:Hide()
ligne.lockedIndicator = verrou
local niveau = ligne:CreateFontString(nil, "ARTWORK", "GameFontNormal")
niveau:SetWidth(G.niveauL)
niveau:SetHeight(G.ligne)
niveau:SetPoint("RIGHT", ligne, "RIGHT", 0, 0)
niveau:SetJustifyH("LEFT")
ligne.niveau = niveau
local nom = ligne:CreateFontString(nil, "ARTWORK", "GameFontNormal")
nom:SetHeight(G.ligne)
nom:SetPoint("LEFT", c, "RIGHT", 0, 0)
nom:SetPoint("RIGHT", niveau, "LEFT", -4, 0)
nom:SetJustifyH("LEFT")
ligne.nom = nom
-- le nom clique la case, et la survole (NameButton de camelot) ;
-- l'infobulle des verrous est celle du client
ligne:EnableMouse(true)
ligne:SetScript("OnEnter", function(self)
if actif(self.case) and self.case:IsShown() then self.case:LockHighlight() end
LFDQueueFrameDungeonListButton_OnEnter(self)
end)
ligne:SetScript("OnLeave", function(self)
self.case:UnlockHighlight()
GameTooltip:Hide()
end)
ligne:SetScript("OnClick", function(self)
if self.case:IsShown() and actif(self.case) then self.case:Click() end
end)
end
local function couleur(fs, c)
fs:SetTextColor(c[1], c[2], c[3])
end
-- LFDQueueFrameSpecificListButton_SetDungeon (et celle des raids), a la
-- maniere de camelot
local function remplirLigne(ligne, index)
local src = ligne.src
local id = src.liste()[index]
local info = LFGDungeonInfo and LFGDungeonInfo[id] or {}
local mode = GetLFGMode()
local fige = mode == "rolecheck" or mode == "queued" or mode == "listed" or not src.habilite()
ligne.id = id
ligne.nom:SetText(info[1] or "")
if id < 0 then
ligne.plus:Show()
ligne.isCollapsed = LFGCollapseList[id]
ligne.plus:SetNormalTexture(BOUTONS .. (ligne.isCollapsed and "UI-PlusButton-UP" or "UI-MinusButton-UP"))
local n = ligne.plus:GetNormalTexture()
if n then
n:ClearAllPoints()
n:SetWidth(20)
n:SetHeight(20)
n:SetPoint("LEFT", ligne.plus, "LEFT", 3, 0)
end
ligne.niveau:Hide()
couleur(ligne.nom, COULEURS.entete)
else
ligne.plus:Hide()
ligne.isCollapsed = false
local minimum, maximum = info[3] or 0, info[4] or 0
if minimum == maximum then
ligne.niveau:SetText(format(txt("LFD_LEVEL_FORMAT_SINGLE"), minimum))
else
ligne.niveau:SetText(format(txt("LFD_LEVEL_FORMAT_RANGE"), minimum, maximum))
end
ligne.niveau:Show()
local joueur = UnitLevel("player")
local c = COULEURS.entree
if maximum ~= 0 and maximum < joueur then
c = COULEURS.facile
elseif minimum ~= 0 and minimum > joueur then
c = COULEURS.dur
end
couleur(ligne.niveau, c)
couleur(ligne.nom, fige and COULEURS.entete or c)
end
local plusieurs = src.plusieurs()
if src.verrou(id) then
ligne.case:Hide()
ligne.lockedIndicator:Show()
else
-- en groupe, un en-tete de raid n'a pas de bouton
if plusieurs or id >= 0 then ligne.case:Show() else ligne.case:Hide() end
ligne.lockedIndicator:Hide()
end
caseRadio(ligne.case, not plusieurs)
local etat = src.etat(id, mode)
if plusieurs and ligne.case.style == "camelot" then
ligne.case:SetChecked(etat and etat ~= 0)
elseif plusieurs then
if etat == 1 then
ligne.case:SetCheckedTexture(BOUTONS .. "UI-MultiCheck-Up")
ligne.case:SetDisabledCheckedTexture(BOUTONS .. "UI-MultiCheck-Disabled")
else
ligne.case:SetCheckedTexture(BOUTONS .. "UI-CheckBox-Check")
ligne.case:SetDisabledCheckedTexture(BOUTONS .. "UI-CheckBox-Check-Disabled")
end
ligne.case:SetChecked(etat and etat ~= 0)
else
ligne.case:SetChecked(etat)
end
if fige then ligne.case:Disable() else ligne.case:Enable() end
end
F.creerLigne = creerLigne
F.remplirLigne = remplirLigne
-- ------------------------------------------------------------ la liste a categories
-- LA LISTE A CATEGORIES : celle des raids (GroupFinderRaid.lua, VALIDEE le
-- 26/09), commune depuis que la liste des donjons specifiques la reprend
-- (demande du 28/09 : « la liste des donjons n'a pas l'en-tete de
-- camelot »). En-tetes de la feuille de personnage (TokensTab.lua,
-- TokenHeaderTemplate) : 26 de haut, common-button-list-collapseExpand en
-- neuf tranches (coin 12), nom GameFontNormalLeft a LEFT (10) sur 15, fleche
-- common-button-list-minus / -plus a RIGHT (-8, -1), survol : la plaque en
-- ADD a 0,3 ; entrees de 22 en retrait de 2, cases de camelot, le rectangle
-- de la feuille (charactercreate-customize-dropdown-linemouseover en trois
-- tranches, cotes de 6, le droit retourne) a 0,10 au survol et 0,20 coche ;
-- 3 entre deux lignes, 4 de marge en haut. Un clic sur l'en-tete replie ou
-- deplie (src.replier, la fonction du client). Des lignes de deux hauteurs :
-- on les empile a la main, et le decalage compte des lignes.
-- ECART, pour les donjons (choix de l'utilisateur, 28/09) : l'en-tete garde
-- la case « tout cocher » de WotLK (src.caseEntete), a la place du debut du
-- nom (LEFT 10), le nom 2 a sa droite ; cochee en partie (LFGEnabledList a
-- 1), sa coche grisee, comme la liste des AddOns ; un en-tete verrouille
-- montre le verrou de WotLK a la place de la case, et son infobulle.
local LC = {
entete = 26, entree = 22, ecart = 3, retrait = 2, marge = 4, coin = 12, nomX = 10, nomH = 15,
flecheX = -8, flecheY = -1, flechePlace = 16, caseEcart = 2, cote = 6, choisie = 0.20, survol = 0.10,
plaque = "common-button-list-collapseexpand",
survolCote = "charactercreate-customize-dropdown-linemouseover-side",
survolMilieu = "charactercreate-customize-dropdown-linemouseover-middle",
}
F.LC = LC
local function creerEnteteCat(zone, n)
local b = CreateFrame("Button", zone.prefixe .. "Header" .. n, zone)
b:SetHeight(LC.entete)
ForeverUI.CreateNineSlice(b, LC.plaque, LC.coin, { 0, 0, 0, 0 }, "BACKGROUND")
for _, t in ipairs(ForeverUI.CreateNineSlice(b, LC.plaque, LC.coin, { 0, 0, 0, 0 }, "HIGHLIGHT") or {}) do
t:SetBlendMode("ADD")
t:SetAlpha(0.3)
end
local nom = b:CreateFontString(nil, "OVERLAY", "GameFontNormalLeft")
nom:SetHeight(LC.nomH)
nom:SetJustifyH("LEFT")
nom:SetPoint("RIGHT", b, "RIGHT", -LC.flechePlace, 0)
b.nom = nom
local fleche = b:CreateTexture(nil, "OVERLAY")
fleche:SetPoint("RIGHT", b, "RIGHT", LC.flecheX, LC.flecheY)
b.fleche = fleche
if zone.src.caseEntete then
-- la case du client : LFDQueueFrameDungeonChoiceEnableButton_OnClick lit
-- self:GetParent().id et self:GetChecked()
local c = CreateFrame("CheckButton", nil, b)
c.style = "camelot"
c:SetWidth(CASE_CAMELOT)
c:SetHeight(CASE_CAMELOT)
c:SetPoint("LEFT", b, "LEFT", LC.nomX, 0)
caseCamelot(c, false)
c:SetScript("OnClick", function(self) zone.src.case(self) end)
b.case = c
local verrou = b:CreateTexture(nil, "OVERLAY")
verrou:SetTexture(LFG .. "UI-LFG-ICON-LOCK")
verrou:SetTexCoord(0, 0.71875, 0, 0.875)
verrou:SetWidth(12)
verrou:SetHeight(14)
verrou:SetPoint("CENTER", c, "CENTER", 0, 0)
verrou:Hide()
b.lockedIndicator = verrou
nom:SetPoint("LEFT", c, "RIGHT", LC.caseEcart, 0)
b:SetScript("OnEnter", function(self) LFDQueueFrameDungeonListButton_OnEnter(self) end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
else
nom:SetPoint("LEFT", b, "LEFT", LC.nomX, 0)
end
b:SetScript("OnClick", function(self)
PlaySound("igMainMenuOptionCheckBoxOn")
zone.src.replier(self.id, not LFGCollapseList[self.id])
F.demander()
end)
return b
end
local function remplirEnteteCat(b, id, src)
local info = LFGDungeonInfo and LFGDungeonInfo[id] or {}
b.id = id
b.nom:SetText(info[1] or "")
ForeverUI.SetAtlas(b.fleche, LFGCollapseList[id] and "common-button-list-plus" or "common-button-list-minus", false)
local c = b.case
if not c then return end
local mode = GetLFGMode()
local fige = mode == "rolecheck" or mode == "queued" or mode == "listed" or not src.habilite()
if src.verrou(id) then
c:Hide()
b.lockedIndicator:Show()
else
c:Show()
b.lockedIndicator:Hide()
end
local etat = src.etat(id, mode)
c:SetChecked(etat and etat ~= 0)
local coche = c:GetCheckedTexture()
if coche then coche:SetDesaturated(etat == 1) end
if fige then c:Disable() else c:Enable() end
end
local function creerEntreeCat(zone, n)
local l = CreateFrame("Button", zone.prefixe .. "Row" .. n, zone)
l:SetHeight(LC.entree)
-- le rectangle de la feuille, sous la ligne
local survol = CreateFrame("Frame", nil, l)
survol:SetAllPoints(l)
survol:SetAlpha(0)
local g = survol:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(g, LC.survolCote, true)
g:SetWidth(LC.cote)
g:SetPoint("TOPLEFT", survol, "TOPLEFT", 0, 0)
g:SetPoint("BOTTOMLEFT", survol, "BOTTOMLEFT", 0, 0)
local d = survol:CreateTexture(nil, "BACKGROUND")
if ForeverUI.SetAtlas(d, LC.survolCote, true) then
local e = ForeverUI.AtlasEntry(LC.survolCote)
d:SetTexCoord(e[3], e[2], e[4], e[5])
end
d:SetWidth(LC.cote)
d:SetPoint("TOPRIGHT", survol, "TOPRIGHT", 0, 0)
d:SetPoint("BOTTOMRIGHT", survol, "BOTTOMRIGHT", 0, 0)
local m = survol:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(m, LC.survolMilieu, true)
m:SetPoint("TOPLEFT", g, "TOPRIGHT", 0, 0)
m:SetPoint("BOTTOMRIGHT", d, "BOTTOMLEFT", 0, 0)
l.survol = survol
creerLigne(l)
return l
end
-- l'opacite du rectangle : cochee 0,20 ; au survol 0,10 ; au repos 0
local function poserSurvolCat(l)
local coche = l.case:IsShown() and l.case:GetChecked()
l.survol:SetAlpha((coche and LC.choisie) or (l:IsMouseOver() and LC.survol) or 0)
end
-- combien de lignes tiennent depuis la i-eme
local function tiennentCat(l, depuis, haut)
local y, n = LC.marge, 0
for i = depuis, #l do
local h = (l[i] < 0) and LC.entete or LC.entree
if y + h > haut then break end
y = y + h + LC.ecart
n = n + 1
end
return n
end
-- la zone : du haut de la liste (G.listeY1, marge comprise) a zone.bas au-dessus
-- du bas de la fenetre ; sa barre lui laisse sa place (barreDroite), sinon
-- elle va a la marge
local function poserZoneCat(zone)
local f = F.cadre
zone:ClearAllPoints()
zone:SetPoint("TOPLEFT", f, "TOPLEFT", G.listeX1 + G.marge, G.listeY1 - G.marge)
zone:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", zone.avecBarre and (G.listeX2 + G.barreDroite - G.marge)
or (G.listeX2 - G.marge), zone.bas)
end
function F.majListeCat(zone)
local l = zone.src.liste() or {}
local total = #l
local haut = G.hauteur + (G.listeY1 - G.marge) - zone.bas
-- le plus grand decalage : celui d'ou la fin de la liste tient
local maxi = 0
for d = 0, total do
if tiennentCat(l, d + 1, haut) >= total - d then
maxi = d
break
end
end
zone.maxi = maxi
zone.decalage = math.max(0, math.min(zone.decalage or 0, maxi))
local avecBarre = maxi > 0
if avecBarre ~= zone.avecBarre then
zone.avecBarre = avecBarre
poserZoneCat(zone)
end
local y = LC.marge
local ne, nn = 0, 0
for i = zone.decalage + 1, total do
local id = l[i]
local h = (id < 0) and LC.entete or LC.entree
if y + h > haut then break end
local ligne
if id < 0 then
ne = ne + 1
ligne = zone.entetes[ne] or creerEnteteCat(zone, ne)
zone.entetes[ne] = ligne
remplirEnteteCat(ligne, id, zone.src)
ligne:ClearAllPoints()
ligne:SetPoint("TOPLEFT", zone, "TOPLEFT", 0, -y)
ligne:SetPoint("TOPRIGHT", zone, "TOPRIGHT", 0, -y)
else
nn = nn + 1
ligne = zone.entrees[nn] or creerEntreeCat(zone, nn)
zone.entrees[nn] = ligne
remplirLigne(ligne, i)
poserSurvolCat(ligne)
ligne:ClearAllPoints()
ligne:SetPoint("TOPLEFT", zone, "TOPLEFT", LC.retrait, -y)
ligne:SetPoint("TOPRIGHT", zone, "TOPRIGHT", 0, -y)
end
ligne:Show()
y = y + h + LC.ecart
end
for i = ne + 1, #zone.entetes do zone.entetes[i]:Hide() end
for i = nn + 1, #zone.entrees do zone.entrees[i]:Hide() end
zone.barre:Regler(maxi + 1, 1, zone.decalage)
end
-- une liste a categories dans la page p : nom de la zone, prefixe des
-- lignes, source, bas de la zone (au-dessus du bas de la fenetre), encadre
function F.creerListeCat(p, nom, prefixe, src, bas, encadre)
local zone = CreateFrame("Frame", nom, p)
zone.src = src
zone.prefixe = prefixe
zone.entetes, zone.entrees = {}, {}
zone.decalage = 0
zone.bas = bas
zone:SetFrameLevel(encadre:GetFrameLevel() + 1)
zone.avecBarre = false
poserZoneCat(zone)
local barre = ForeverUI.CreateScrollBar(nom .. "ScrollBar", p, zone)
barre:SetFrameLevel(encadre:GetFrameLevel() + 2)
barre:ClearAllPoints()
barre:SetPoint("TOPLEFT", zone, "TOPRIGHT", 13 + G.marge, 0)
barre:SetPoint("BOTTOMLEFT", zone, "BOTTOMRIGHT", 13 + G.marge, -2)
barre.surDefilement = function(nouveau)
zone.decalage = nouveau
F.majListeCat(zone)
end
zone.barre = barre
zone:EnableMouseWheel(true)
zone:SetScript("OnMouseWheel", function(self, sens)
self.decalage = math.max(0, math.min((self.decalage or 0) - sens, self.maxi or 0))
F.majListeCat(self)
end)
-- le survol suivi a chaque image, comme la feuille (suivreSurvol)
zone:SetScript("OnUpdate", function(self)
for _, l in ipairs(self.entrees) do
if l:IsShown() then poserSurvolCat(l) end
end
end)
return zone
end
-- ------------------------------------------------------------ les recompenses
local OBJETS = "LFDQueueFrameRandomScrollFrameChildFrameItem"
local function creerObjet(parent, i)
local b = CreateFrame("Button", "ForeverUIGroupFinderReward" .. i, parent)
b:SetWidth(147)
b:SetHeight(41)
local icone = b:CreateTexture(nil, "BORDER")
icone:SetWidth(39)
icone:SetHeight(39)
icone:SetPoint("TOPLEFT", b, "TOPLEFT", 0, 0)
b.icone = icone
local cadreNom = b:CreateTexture(nil, "BACKGROUND")
cadreNom:SetTexture("Interface" .. SEP .. "QuestFrame" .. SEP .. "UI-QuestItemNameFrame")
cadreNom:SetWidth(128)
cadreNom:SetHeight(64)
cadreNom:SetPoint("LEFT", icone, "RIGHT", -10, 0)
local nom = b:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
nom:SetWidth(90)
nom:SetHeight(36)
nom:SetPoint("LEFT", icone, "RIGHT", 8, 0)
nom:SetJustifyH("LEFT")
b.nom = nom
local nombre = b:CreateFontString(nil, "OVERLAY", "NumberFontNormal")
nombre:SetPoint("BOTTOMRIGHT", icone, "BOTTOMRIGHT", -5, 2)
b.nombre = nombre
b:SetID(i)
-- LFDRandomDungeonLootTemplate
b:SetScript("OnEnter", function(self)
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
GameTooltip:SetLFGDungeonReward(LFDQueueFrame.type, self:GetID())
GameTooltip:Show()
end)
b:SetScript("OnLeave", function() GameTooltip:Hide() end)
b:SetScript("OnClick", function(self)
HandleModifiedItemClick(GetLFGDungeonRewardLink(LFDQueueFrame.type, self:GetID()))
end)
return b
end
-- LES DETAILS : une zone qui defile sous le filet. Chaque piece est posee
-- a sa hauteur depuis le haut du contenu (et non d'une piece a l'autre) :
-- la hauteur totale en sort, et la barre la suit.
local function construireRecompenses(p)
local vue = CreateFrame("ScrollFrame", "ForeverUIGroupFinderDetails", p)
vue.decalage = 0
local r = CreateFrame("Frame", "ForeverUIGroupFinderRewards", vue)
r:SetWidth(G.largeur + G.detailsX2 - G.detailsX1)
r:SetHeight(1)
vue:SetScrollChild(r)
local largeur = G.largeur + G.detailsX2 - G.detailsX1
local function texte(police)
local fs = r:CreateFontString(nil, "ARTWORK", police)
fs:SetWidth(largeur)
fs:SetJustifyH("LEFT")
return fs
end
r.description = texte("GameFontHighlight")
r.etiquette = texte("GameFontNormalLarge")
r.etiquette:SetText(txt("LFD_REWARDS"))
r.explication = texte("GameFontHighlight")
r.pug = texte("GameFontHighlight")
-- l'argent : une case d'objet, sans infobulle ni lien
local argent = creerObjet(r, 0)
argent:SetScript("OnEnter", nil)
argent:SetScript("OnLeave", nil)
argent:SetScript("OnClick", nil)
argent.icone:SetTexture(G.pieces)
argent.nombre:Hide()
r.argent = argent
r.xp = texte("GameFontNormal")
r.objets = {}
F.recompenses = r
local barre = ForeverUI.CreateScrollBar("ForeverUIGroupFinderDetailsScrollBar", p, vue)
barre.surDefilement = function(nouveau)
vue.decalage = nouveau
vue:SetVerticalScroll(math.min(nouveau * G.cran, math.max(0, (vue.contenu or 0) - (vue.visible or 0))))
end
vue:EnableMouseWheel(true)
vue:SetScript("OnMouseWheel", function(self, sens)
barre:Deplacer(self.decalage - sens)
end)
vue.barre = barre
F.details = vue
-- LA SECONDE MESURE, a l'image suivante : du haut du contenu au bas de
-- sa derniere piece. Plus haute que la premiere, elle replace tout.
local remesure = CreateFrame("Frame")
remesure:Hide()
remesure:SetScript("OnUpdate", function(self)
self:Hide()
local piece = F.dernierePiece
if not (vue:IsShown() and piece and piece:IsShown()) then return end
local haut, bas = r:GetTop(), piece:GetBottom()
if not (haut and bas) then return end
local reel = haut - bas
if reel > (vue.contenu or 0) + 0.5 then F.poserContenu(reel) end
end)
F.remesure = remesure
end
-- LE FILET ET LES DETAILS. Les details finissent en bas de l'encadre ; le
-- filet se pose au-dessus de leur contenu, sans monter plus haut que
-- F.filetMax (6 sous le dernier bouton). Sans contenu, il reste la-haut.
local function placerFilet(contenu)
local y = F.filetMax or 0
if contenu and contenu > 0 then
local voulu = -(G.hauteur - G.detailsBas) + contenu + G.detailsAir + G.detailsEcart + G.regleH / 2
if voulu < y then y = voulu end
end
-- la hauteur visible des details, d'apres nos ecarts et non GetHeight
F.details.visible = (y - G.regleH / 2 - G.detailsEcart) + (G.hauteur - G.detailsBas)
F.regle:ClearAllPoints()
F.regle:SetPoint("LEFT", F.cadre, "TOPLEFT", G.listeX1, y)
F.regle:SetPoint("RIGHT", F.cadre, "TOPRIGHT", G.listeX2, y)
F.details:ClearAllPoints()
F.details:SetPoint("TOPLEFT", F.cadre, "TOPLEFT", G.detailsX1, y - G.regleH / 2 - G.detailsEcart)
F.details:SetPoint("BOTTOMRIGHT", F.cadre, "BOTTOMRIGHT", G.detailsX2, G.detailsBas)
-- les voiles des details partent du filet
F.hautDetails = y - G.regleH / 2 - G.encadreY1
end
-- la hauteur d'un texte replie sur sa largeur : GetHeight, comme
-- QuestInfo.lua du client
local function hauteur(fs)
local h = fs:GetHeight() or 0
if h <= 0 and fs.GetStringHeight then h = fs:GetStringHeight() or 0 end
return h
end
-- LFDQueueFrameRandom_UpdateFrame a pose les textes sur les regions du
-- client : on les reprend. Le titre n'est pas repris : le bouton choisi le
-- porte deja.
local function majRecompenses()
local r = F.recompenses
local vue = F.details
local client = LFDQueueFrameRandomScrollFrameChildFrame
local id = LFDQueueFrame.type
if not client or type(id) ~= "number" then
placerFilet(nil)
vue:Hide()
vue.barre:Hide()
return
end
vue:Show()
local y = 0
local dernier
local function poser(piece, x, ecart)
piece:ClearAllPoints()
piece:SetPoint("TOPLEFT", r, "TOPLEFT", x or 0, -(y + (ecart or 0)))
piece:Show()
y = y + (ecart or 0) + hauteur(piece)
dernier = piece
end
r.description:SetText(client.description:GetText() or "")
poser(r.description)
if client.rewardsLabel:IsShown() then
r.explication:SetText(client.rewardsDescription:GetText() or "")
poser(r.etiquette, 0, 10)
poser(r.explication, 0, 6)
else
r.etiquette:Hide()
r.explication:Hide()
end
local _, argentBase, argentVar, xpBase, xpVar, nombre = GetLFGDungeonRewards(id)
nombre = nombre or 0
-- l'argent et l'experience : le calcul de LFDQueueFrameRandom_UpdateFrame
local aleas = 4 - GetNumPartyMembers()
local argent = (argentBase or 0) + (argentVar or 0) * aleas
local xp = (xpBase or 0) + (xpVar or 0) * aleas
local haut = y + 8
local function case(b, i)
local rang, colonne = math.floor((i - 1) / 2), (i - 1) % 2
b:ClearAllPoints()
b:SetPoint("TOPLEFT", r, "TOPLEFT", colonne * 157, -(haut + rang * 44))
b:Show()
y = haut + rang * 44 + 41
dernier = b
end
for i = 1, nombre do
local b = r.objets[i] or creerObjet(r, i)
r.objets[i] = b
local nom, image, quantite = GetLFGDungeonRewardInfo(id, i)
b.nom:SetText(nom or "")
b.icone:SetTexture(image)
if quantite and quantite > 1 then b.nombre:SetText(quantite) b.nombre:Show() else b.nombre:Hide() end
case(b, i)
end
for i = nombre + 1, #r.objets do r.objets[i]:Hide() end
if argent > 0 then
r.argent.nom:SetText(GetCoinTextureString(argent))
case(r.argent, nombre + 1)
else
r.argent:Hide()
end
if client.pugDescription:IsShown() then
r.pug:SetText(client.pugDescription:GetText() or "")
poser(r.pug, 0, 5)
else
r.pug:Hide()
end
if xp > 0 then
r.xp:SetText(txt("EXPERIENCE_COLON") .. " |cffffffff" .. xp .. "|r")
poser(r.xp, 0, 8)
else
r.xp:Hide()
end
-- le filet descend sur le contenu ; puis la barre
F.dernierePiece = dernier
F.poserContenu(y)
-- la seconde mesure, une fois les textes poses par le client
F.remesure:Show()
end
function F.poserContenu(y)
local vue = F.details
F.recompenses:SetHeight(math.max(1, y))
vue.contenu = y
placerFilet(y)
local visible = vue.visible or 0
if visible <= 0 or y <= visible + G.tolerance then
vue.decalage = 0
vue:SetVerticalScroll(0)
vue.barre:Regler(0, 0, 0)
else
-- en crans de 20 : le dernier cran montre la fin du contenu
local total, visibles = math.ceil((y - visible) / G.cran) + 1, 1
vue.decalage = math.min(vue.decalage, total - visibles)
vue.barre:Regler(total, visibles, vue.decalage)
vue.barre.surDefilement(vue.decalage)
end
end
-- ------------------------------------------------------------ les voiles
-- Ceux de WotLK (330 x 257, noir a 0,93), poses sur l'encadre et dans le
-- meme ordre : l'attente (11), la reprise d'un groupe (14), "pas de donjon
-- pendant le raid" (16).
local function creerVoile(nom, niveau, page, encadre)
local v = CreateFrame("Frame", nom, page or F.pageDonjons)
v:SetFrameLevel((encadre or F.encadre):GetFrameLevel() + niveau)
v:EnableMouse(true)
local noir = v:CreateTexture(nil, "BACKGROUND")
noir:SetTexture(0, 0, 0, 0.93)
noir:SetAllPoints(v)
v.description = v:CreateFontString(nil, "ARTWORK", "GameFontNormal")
v.description:SetWidth(300)
v.description:SetPoint("TOP", v, "TOP", 0, -70)
v:Hide()
return v
end
F.creerVoile = creerVoile
local function construireVoiles()
local attente = creerVoile("ForeverUIGroupFinderCooldown", 11)
attente.temps = attente:CreateFontString(nil, "ARTWORK", "GameFontHighlightLarge")
attente.temps:SetPoint("TOP", attente.description, "BOTTOM", 0, -10)
attente.noms, attente.etats = {}, {}
for i = 1, 4 do
local n = attente:CreateFontString(nil, "ARTWORK", "GameFontNormal")
n:SetWidth(120)
n:SetJustifyH("LEFT")
if i == 1 then
n:SetPoint("TOPLEFT", attente.description, "BOTTOMLEFT", 25, -60)
else
n:SetPoint("TOP", attente.noms[i - 1], "BOTTOM", 0, -5)
end
local e = attente:CreateFontString(nil, "ARTWORK", "GameFontNormal")
e:SetWidth(110)
e:SetJustifyH("RIGHT")
e:SetPoint("TOPLEFT", n, "TOPRIGHT", 0, 0)
attente.noms[i], attente.etats[i] = n, e
end
-- le temps restant change a chaque image chez le client
attente:SetScript("OnUpdate", function(self)
local c = LFDQueueFrameCooldownFrame
if c and c.time then
self.temps:SetText(c.time:GetText() or "")
end
end)
F.attente = attente
local reprise = creerVoile("ForeverUIGroupFinderBackfill", 14)
reprise.oui = ForeverUI.CreatePanelButton(reprise, txt("YES"), 153, 22, nil, "GameFontNormal")
reprise.oui:SetPoint("TOPRIGHT", reprise.description, "BOTTOM", -5, -10)
reprise.oui:SetScript("OnClick", function()
if LFDQueueFramePartyBackfillBackfillButton then LFDQueueFramePartyBackfillBackfillButton:Click() end
end)
reprise.non = ForeverUI.CreatePanelButton(reprise, txt("HIDE"), 153, 22, nil, "GameFontNormal")
reprise.non:SetPoint("TOPLEFT", reprise.description, "BOTTOM", 5, -10)
reprise.non:SetScript("OnClick", function()
if LFDQueueFramePartyBackfillNoBackfillButton then LFDQueueFramePartyBackfillNoBackfillButton:Click() end
end)
F.reprise = reprise
local raid = creerVoile("ForeverUIGroupFinderNoLFDWhileLFR", 16)
raid.quitter = ForeverUI.CreatePanelButton(raid, "", 153, 22, nil, "GameFontNormal")
raid.quitter:SetPoint("TOP", raid.description, "BOTTOM", 0, -10)
raid.quitter:SetScript("OnClick", function()
if LFDQueueFrameNoLFDWhileLFRLeaveQueueButton then LFDQueueFrameNoLFDWhileLFRLeaveQueueButton:Click() end
end)
F.raid = raid
end
local function texteDe(nom)
local r = _G[nom]
return r and r.GetText and r:GetText() or ""
end
-- ce qu'ils couvrent : les details sous le filet, ou toute la liste
local function placerVoiles()
for _, v in ipairs({ F.attente, F.reprise, F.raid }) do
v:ClearAllPoints()
if F.vue == "liste" then
v:SetAllPoints(F.encadre)
else
v:SetPoint("TOPLEFT", F.encadre, "TOPLEFT", 0, F.hautDetails)
v:SetPoint("BOTTOMRIGHT", F.encadre, "BOTTOMRIGHT", 0, 0)
end
end
end
local function majVoiles()
placerVoiles()
-- l'attente : visible chez le client (son parent change selon la
-- desertion), textes recopies
local c = LFDQueueFrameCooldownFrame
if c and affiche(c) then
F.attente.description:SetText(c.description and c.description:GetText() or "")
F.attente.description:ClearAllPoints()
F.attente.description:SetPoint("TOP", F.attente, "TOP", 0, GetNumPartyMembers() == 0 and -85 or -30)
F.attente.temps:SetText(c.time and c.time:GetText() or "")
if c.time and c.time:IsShown() then F.attente.temps:Show() else F.attente.temps:Hide() end
for i = 1, 4 do
local n, e = _G["LFDQueueFrameCooldownFrameName" .. i], _G["LFDQueueFrameCooldownFrameStatus" .. i]
if n and n:IsShown() then
F.attente.noms[i]:SetText(n:GetText() or "")
F.attente.etats[i]:SetText(e and e:GetText() or "")
F.attente.noms[i]:Show()
F.attente.etats[i]:Show()
else
F.attente.noms[i]:Hide()
F.attente.etats[i]:Hide()
end
end
F.attente:Show()
else
F.attente:Hide()
end
local b = LFDQueueFramePartyBackfill
if b and affiche(b) then
F.reprise.description:SetText(texteDe("LFDQueueFramePartyBackfillDescription"))
F.reprise.oui:Activer(actif(LFDQueueFramePartyBackfillBackfillButton))
F.reprise:Show()
else
F.reprise:Hide()
end
local l = LFDQueueFrameNoLFDWhileLFR
if l and affiche(l) then
F.raid.description:SetText(texteDe("LFDQueueFrameNoLFDWhileLFRDescription"))
F.raid.quitter:SetText(texteDe("LFDQueueFrameNoLFDWhileLFRLeaveQueueButton"))
F.raid.quitter:Activer(actif(LFDQueueFrameNoLFDWhileLFRLeaveQueueButton))
F.raid:Show()
else
F.raid:Hide()
end
end
-- ------------------------------------------------------------ la page
-- LE BANDEAU DES ROLES d'une page : un cadre fils, au-dessus des stries
-- (camelot le pose en cadre fils, RolesSection), le bleu et les boutons
function F.construireBandeau(p, roles)
local f = F.cadre
local bandeau = CreateFrame("Frame", nil, p)
bandeau:SetAllPoints(f)
bandeau:SetFrameLevel(f:GetFrameLevel() + 1)
local bleu = bandeau:CreateTexture(nil, "BACKGROUND")
bleu:SetTexture(G.bleu)
bleu:SetPoint("TOPLEFT", f, "TOPLEFT", G.bleuX1, G.bleuY1)
bleu:SetPoint("BOTTOMRIGHT", f, "TOPRIGHT", G.bleuX2, G.bleuY2)
local l = {}
F.tousRoles = F.tousRoles or {}
for _, r in ipairs(roles) do
local b = creerRole(bandeau, r)
table.insert(l, b)
table.insert(F.tousRoles, b)
end
return bleu, l
end
-- L'ENCADRE d'une page, au-dessus du bandeau : sans marbre, le fond de
-- camelot a 3 du bord ; rogne en haut (0,093) sous un bandeau de roles, entier
-- sans (le parcours)
function F.construireEncadre(p, nom, haut, rogne, bas)
local f = F.cadre
local encadre = ForeverUI.CreateInset(p, nom)
encadre:SetFrameLevel(f:GetFrameLevel() + 2)
encadre:SetPoint("TOPLEFT", f, "TOPLEFT", G.encadreX1, haut)
encadre:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", G.encadreX2, bas or G.encadreY2)
encadre.fond:Hide()
local fond = encadre:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(fond, "groupfinder-background", true)
local e = ForeverUI.AtlasEntry("groupfinder-background")
if e and rogne then
fond:SetTexCoord(e[2], e[3], e[4] + (e[5] - e[4]) * G.fondHaut, e[5])
end
fond:SetPoint("TOPLEFT", encadre, "TOPLEFT", 3, -3)
fond:SetPoint("BOTTOMRIGHT", encadre, "BOTTOMRIGHT", -3, 3)
return encadre
end
local function construirePage(f)
local p = CreateFrame("Frame", "ForeverUIGroupFinderDungeons", f)
p:SetAllPoints(f)
F.pageDonjons = p
F.bleu, F.roles = F.construireBandeau(p, ROLES)
-- son fond de camelot couvre le bas du bleu comme dans la source
local encadre = F.construireEncadre(p, "ForeverUIGroupFinderInset", G.encadreY1, true)
F.encadre = encadre
local S = ForeverUI.Social
-- les categories : empilees depuis le haut, sans defilement ; le filet
-- et les details suivent le dernier bouton
local cats = CreateFrame("Frame", "ForeverUIGroupFinderCategories", p)
cats:SetFrameLevel(encadre:GetFrameLevel() + 1)
cats:SetPoint("TOPLEFT", f, "TOPLEFT", G.catX1, G.catY1)
cats:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", G.catX2, G.catY2)
cats.lignes = {}
F.categoriesVue = cats
local regle = cats:CreateTexture(nil, "OVERLAY")
ForeverUI.SetAtlas(regle, "shop-list-rule", true)
regle:SetHeight(G.regleH)
F.regle = regle
-- la liste des donjons specifiques : la liste a categories des raids
-- (demande du 28/09), jusqu'a la marge du bas de l'encadre
F.listeVue = F.creerListeCat(p, "ForeverUIGroupFinderList", "ForeverUIGroupFinder", SOURCE_LFD,
G.listeY2 + G.marge, encadre)
construireRecompenses(p)
F.details:SetFrameLevel(encadre:GetFrameLevel() + 1)
F.details.barre:SetFrameLevel(encadre:GetFrameLevel() + 2)
construireVoiles()
-- les boutons du bas
local retour = ForeverUI.CreatePanelButton(p, txt("BACK"), G.backL, G.boutonH, "ForeverUIGroupFinderBackButton", "GameFontNormal")
retour:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", G.boutonCote, G.boutonBas)
retour:SetScript("OnClick", function()
PlaySound("igMainMenuOptionCheckBoxOn")
F.vue = "categories"
F.maj()
end)
F.retour = retour
local chercher = ForeverUI.CreatePanelButton(p, txt("FIND_A_GROUP"), G.postL, G.boutonH, "ForeverUIGroupFinderFindButton", "GameFontNormal")
chercher:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -G.boutonCote, G.boutonBas)
-- le bouton du client : son OnClick choisit entre LeaveLFG et
-- LFDQueueFrame_Join
chercher:SetScript("OnClick", function()
if LFDQueueFrameFindGroupButton and actif(LFDQueueFrameFindGroupButton) then
LFDQueueFrameFindGroupButton:Click()
end
end)
F.chercher = chercher
end
-- ------------------------------------------------------------ la mise a jour
local function modeEnFile()
local mode = GetLFGMode()
return mode == "queued" or mode == "rolecheck" or mode == "proposal"
end
-- les boutons de categorie, le filet sous le dernier, les details dessous
local function poserCategories()
local cats = F.categoriesVue
local n = #F.liste_categories
for i = 1, n do
local l = cats.lignes[i]
if not l then
l = CreateFrame("Frame", nil, cats)
l:SetHeight(G.catH)
creerCategorie(l)
cats.lignes[i] = l
end
l:ClearAllPoints()
l:SetPoint("TOPLEFT", cats, "TOPLEFT", 0, G.catPremier - (i - 1) * G.catPas)
l:SetPoint("TOPRIGHT", cats, "TOPRIGHT", 0, G.catPremier - (i - 1) * G.catPas)
remplirCategorie(l, i)
l:Show()
end
for i = n + 1, #cats.lignes do cats.lignes[i]:Hide() end
-- le bas du dernier bouton, depuis le haut de la fenetre
local bas = G.catY1 + G.catPremier - (n - 1) * G.catPas - G.catH
F.filetMax = bas - G.regleEcart
placerFilet(nil)
end
local function majDonjons()
local vue = F.vue or "categories"
if vue == "liste" and LFDQueueFrame.type ~= "specific" then vue = "categories" end
F.vue = vue
F.liste_categories = F.categories()
poserCategories()
if vue == "categories" then
F.categoriesVue:Show()
majRecompenses()
else
F.categoriesVue:Hide()
F.details:Hide()
F.details.barre:Hide()
end
if vue == "liste" then
F.listeVue:Show()
F.majListeCat(F.listeVue)
else
F.listeVue:Hide()
F.listeVue.barre:Hide()
end
majVoiles()
-- Back n'existe que dans la liste de "Specific Dungeons" (demande du
-- 2026-09-26)
if vue == "liste" then F.retour:Show() else F.retour:Hide() end
local client = LFDQueueFrameFindGroupButton
if client then
F.chercher:SetText(client:GetText() or txt("FIND_A_GROUP"))
F.chercher:Activer(actif(client))
end
end
-- LES PAGES : une par onglet ; celle des donjons ici, celles du raid dans
-- GroupFinderRaid.lua (F.inscrirePage)
F.pages = {}
function F.inscrirePage(cle, cadre, maj)
F.pages[cle] = { cadre = cadre, maj = maj }
end
function F.maj()
if not F.cadre then return end
F.majRoles()
local pg = F.pages[F.onglet or "donjons"]
if pg then pg.maj() end
end
-- montrer une page : son onglet choisi, les autres pages cachees
function F.afficher(cle)
F.choisirOnglet(cle)
for k, pg in pairs(F.pages) do
if k == cle then pg.cadre:Show() else pg.cadre:Hide() end
end
F.maj()
end
-- plusieurs mises a jour du client dans la meme image n'en font qu'une chez
-- nous
local attendre = CreateFrame("Frame")
attendre:Hide()
attendre:SetScript("OnUpdate", function(self)
self:Hide()
F.maj()
end)
F.attendre = attendre
F.G, F.txt, F.actif, F.couleur, F.COULEURS, F.ROLES = G, txt, actif, couleur, COULEURS, ROLES
function F.demander()
if F.cadre and F.cadre:IsShown() then attendre:Show() end
end
-- ------------------------------------------------------------ le panneau
-- Le panneau du client se tait : ses regions, ses cadres fils sauf son
-- contenu et notre fenetre ; il n'attrape plus la souris. Son contenu reste
-- affiche -- sa logique en depend -- mais transparent, et pose SOUS notre
-- fenetre : ses boutons invisibles ne peuvent rien attraper.
local function etoufferClient()
local p = LFDParentFrame
for _, r in ipairs({ p:GetRegions() }) do r:Hide() end
for _, c in ipairs({ p:GetChildren() }) do
if c ~= F.cadre and c ~= LFDQueueFrame then c:Hide() end
end
p:EnableMouse(false)
LFDQueueFrame:SetAlpha(0)
LFDQueueFrame:ClearAllPoints()
LFDQueueFrame:SetPoint("TOPLEFT", F.cadre, "TOPLEFT", 0, 0)
LFDQueueFrame:SetWidth(p:GetWidth())
LFDQueueFrame:SetHeight(p:GetHeight())
end
-- NOTRE FENETRE PASSE D'UN PANNEAU A L'AUTRE : fille de celui qui est
-- ouvert, au-dessus de son contenu ; a sa place retenue si on l'a deplacee,
-- sinon a celle du panneau.
function F.attacher(panneau)
local f = F.cadre
if f:GetParent() ~= panneau then f:SetParent(panneau) end
f:SetFrameLevel(panneau:GetFrameLevel() + 30)
local pos = ForeverUIDB and ForeverUIDB.positions and ForeverUIDB.positions.chercheur
if not pos then
f:ClearAllPoints()
f:SetPoint("TOPLEFT", panneau, "TOPLEFT", 0, 0)
end
end
-- UN ONGLET : son panneau du client s'ouvre, l'autre se ferme. Les donjons
-- restent fermes sous le niveau du client (SHOW_LFD_LEVEL, comme le
-- micro-bouton).
function F.allerA(cle)
if cle == "donjons" then
if UnitLevel("player") < (SHOW_LFD_LEVEL or 15) then return end
if LFRParentFrame and LFRParentFrame:IsShown() then HideUIPanel(LFRParentFrame) end
if not LFDParentFrame:IsShown() then ShowUIPanel(LFDParentFrame) end
elseif LFRParentFrame then
if LFDParentFrame:IsShown() then HideUIPanel(LFDParentFrame) end
if LFRParentFrame:IsShown() then F.afficher(cle) else ShowUIPanel(LFRParentFrame) end
end
end
local function ouvrir()
if LFRParentFrame and LFRParentFrame:IsShown() then HideUIPanel(LFRParentFrame) end
F.attacher(LFDParentFrame)
etoufferClient()
-- a l'ouverture, les categories ; en file pour des donjons choisis, leur
-- liste
if modeEnFile() and LFDQueueFrame.type == "specific" then
F.vue = "liste"
else
F.vue = "categories"
end
F.cadre:Show()
F.afficher("donjons")
end
local function construire()
if F.cadre or not LFDParentFrame or not LFDQueueFrame then return end
local f = CreateFrame("Frame", "ForeverUIGroupFinderFrame", LFDParentFrame)
f:SetWidth(G.largeur)
f:SetHeight(G.hauteur)
f:SetPoint("TOPLEFT", LFDParentFrame, "TOPLEFT", 0, 0)
-- au-dessus de tout le contenu du client (ses voiles montent a 16)
f:SetFrameLevel(LFDParentFrame:GetFrameLevel() + 30)
f:EnableMouse(true)
F.cadre = f
construireCadre(f)
construirePage(f)
F.inscrirePage("donjons", F.pageDonjons, majDonjons)
F.onglets = {}
local precedent
for n, def in ipairs(ONGLETS) do
if def.raid and not LFRParentFrame then break end
local o = creerOnglet(f, def, n)
if precedent then
o:SetPoint("TOPLEFT", precedent, "BOTTOMLEFT", 0, G.ongletEcart)
else
o:SetPoint("TOPLEFT", f, "TOPRIGHT", 0, G.ongletY)
end
F.onglets[n] = o
precedent = o
end
f:Hide()
end
construire()
if F.cadre then
LFDParentFrame:HookScript("OnShow", ouvrir)
LFDParentFrame:HookScript("OnHide", function()
if F.cadre:GetParent() == LFDParentFrame then F.cadre:Hide() end
end)
-- chaque mise a jour du client, et le changement de type (le PNJ d'une
-- conversation l'appelle aussi) : la vue suit le type choisi
for _, nom in ipairs({ "LFDQueueFrameSpecificList_Update", "LFDQueueFrameRandom_UpdateFrame",
"LFDQueueFrameFindGroupButton_Update", "LFG_UpdateRolesChangeable", "LFG_UpdateRoleCheckboxes",
"LFG_UpdateLockedOutPanels", "LFDFrame_UpdateBackfill", "LFDQueueFrameRandomCooldownFrame_Update" }) do
if _G[nom] then hooksecurefunc(nom, F.demander) end
end
if LFDQueueFrame_SetType then
hooksecurefunc("LFDQueueFrame_SetType", function(valeur)
F.vue = (valeur == "specific") and "liste" or "categories"
F.demander()
end)
end
local veilleur = CreateFrame("Frame")
for _, ev in ipairs({ "LFG_UPDATE", "LFG_ROLE_UPDATE", "LFG_LOCK_INFO_RECEIVED",
"LFG_UPDATE_RANDOM_INFO", "PARTY_MEMBERS_CHANGED" }) do
veilleur:RegisterEvent(ev)
end
veilleur:SetScript("OnEvent", F.demander)
-- deplacable par son titre ; devant quand on l'ouvre ou qu'on la clique
ForeverUI.Superposition.deplacable(F.cadre, F.cadre.bandeau, "chercheur")
ForeverUI.Superposition.inscrire("chercheur", LFDParentFrame, function()
local z = { F.cadre }
for _, o in ipairs(F.onglets) do z[#z + 1] = o end
return z
end)
end
-- TEMOIN -- /fui chercheur
function ForeverUI.GroupFinderDebug()
local dire = function(t) DEFAULT_CHAT_FRAME:AddMessage("|cff66ccffForeverUI|r " .. t) end
if not F.cadre then
dire(L.GROUPFINDER_DEBUG_NOT_BUILT)
return
end
dire(string.format(L.GROUPFINDER_DEBUG_STATE,
tostring(F.vue), tostring(LFDQueueFrame.type), tostring(GetLFGMode()),
LFDDungeonList and #LFDDungeonList or 0, F.cadre:IsShown() and L.GROUPFINDER_DEBUG_SHOWN or L.GROUPFINDER_DEBUG_HIDDEN,
F.cadre:GetFrameLevel(), tostring(LFDQueueFrame:GetAlpha()), LFDQueueFrame:GetFrameLevel()))
-- le navigateur de raid : la recherche, et ce qui a ete releve pour
-- chacun de ses raids
local R = ForeverUI.GroupFinderRaid
if R and R.cache then
local r = R.recherche
dire(string.format(L.GROUPFINDER_DEBUG_RAID,
r and r.nom or L.GROUPFINDER_DEBUG_NONE, tostring(SearchLFGGetJoinedID and SearchLFGGetJoinedID())))
for _, id in ipairs(r and r.ids or {}) do
local info = LFGGetDungeonInfoByID and LFGGetDungeonInfoByID(id)
dire(string.format(L.GROUPFINDER_DEBUG_RAID_ENTRY, info and info[1] or "?", id,
R.cache[id] and #R.cache[id] or L.GROUPFINDER_DEBUG_NOTHING))
end
end
end