e3b4e3360d
La source le dit : UpdateRightPaneHeader ne masque que le StoneBg -- "the stone header backs the PaperDoll sidebar tabs, so it should not render on tabs that have none" -- et RightPaneHost demeure. Chaque onglet aura ses propres informations a y mettre ; le volet leur est reserve des maintenant, vide en attendant. Ce qui s'en va d'un onglet a l'autre n'est donc pas le volet mais le MOBILIER du groupe : bande de pierre, onglets de page et ligne de niveau pour le personnage -- les trois choses exactes que la source masque. Le mobilier est un troisieme niveau dans Panes.lua, parce que deux ne suffisaient pas : la pierre et les onglets de page appartiennent au groupe et non a l'une de ses deux pages, ou passer de l'une a l'autre les aurait emportes. Le repli, lui, traverse les onglets : replie sur le personnage, il l'est encore sur les competences, et se defait depuis n'importe lequel. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
294 lines
9.0 KiB
Lua
294 lines
9.0 KiB
Lua
-- ForeverUI : les hotes et leurs contenus.
|
|
--
|
|
-- CE QUE C'EST, ET POURQUOI.
|
|
--
|
|
-- Une fenetre moderne n'est pas un bloc : c'est un ou deux HOTES -- des
|
|
-- surfaces fixes -- dans lesquels des CONTENUS se remplacent. camelot le dit
|
|
-- ainsi dans CharacterFrame.xml : LeftPaneHost et RightPaneHost, et une
|
|
-- SidePane par onglet. Changer d'onglet n'y recalcule rien : un contenu se
|
|
-- masque, un autre se montre.
|
|
--
|
|
-- LE DEFAUT QUE CELA CORRIGE. La feuille de personnage decidait sa visibilite
|
|
-- a cinq endroits -- l'habillage, les deux onglets du volet, le repli, et la
|
|
-- fonction des statistiques -- et reposait TOUTE sa geometrie a chaque
|
|
-- evenement. D'ou deux fautes que rien ne pouvait prevenir :
|
|
-- * le panneau du gestionnaire d'equipement restait a l'ecran quand on
|
|
-- changeait d'onglet lateral, parce qu'il appartient a NOTRE volet et
|
|
-- non au PaperDollFrame du client, que lui seul masque ;
|
|
-- * les barres de reputation traversaient le volet droit, parce que le
|
|
-- cadre du client garde la taille de la fenetre d'origine et que
|
|
-- personne ne le bornait.
|
|
-- Tant que la visibilite se decide partout, chaque nouvel ecran rouvre les
|
|
-- memes plaies. Elle se decide donc ICI, et nulle part ailleurs.
|
|
--
|
|
-- LE MODELE, en trois mots :
|
|
--
|
|
-- HOTE une surface qui accueille : un volet, une colonne.
|
|
-- GROUPE ce qu'un onglet ouvre. Un groupe touche TOUS les hotes a la
|
|
-- fois : c'est un ecran entier.
|
|
-- PAGE un contenu dans un hote pour un groupe donne. Plusieurs pages
|
|
-- dans le meme hote et le meme groupe se remplacent entre elles
|
|
-- -- ce sont des boutons, pas des onglets.
|
|
-- MOBILIER ce qu'un hote porte pour un groupe QUELLE QUE SOIT la page : la
|
|
-- bande de pierre, les onglets qui choisissent la page. camelot le
|
|
-- traite ainsi -- UpdateRightPaneHeader ne masque que le StoneBg,
|
|
-- l'hote, lui, reste en place.
|
|
--
|
|
-- Un contenu se CONSTRUIT une seule fois, a sa premiere ouverture, et pose sa
|
|
-- geometrie a ce moment-la. Ensuite il ne fait plus que paraitre et
|
|
-- disparaitre. Rien n'est recalcule.
|
|
--
|
|
-- CE QU'UN CONTENU DECLARE POSSEDER. Notre racine est un cadre a nous : ses
|
|
-- fils la suivent. Mais un ecran de 3.3.5 est fait de cadres DU CLIENT, que
|
|
-- l'on ne reparente pas -- leur niveau et leur strate s'y perdraient, et des
|
|
-- reglages deja valides avec. Un contenu declare donc aussi la LISTE des
|
|
-- cadres du client qu'il possede, et la bibliotheque les montre et les masque
|
|
-- avec sa racine. Une seule boucle, un seul endroit.
|
|
|
|
local ForeverUI = ForeverUI or {}
|
|
_G.ForeverUI = ForeverUI
|
|
|
|
local Panes = {}
|
|
ForeverUI.Panes = Panes
|
|
|
|
local hotes = {} -- nom -> { cadre, contenus, groupe, page, montre }
|
|
local ordreHotes = {}
|
|
|
|
-- --------------------------------------------------------------- les hotes
|
|
|
|
-- Declarer un hote. Le cadre existe deja : la bibliotheque ne le cree pas,
|
|
-- elle ne fait que savoir ou les contenus vont.
|
|
function Panes.NewHost(nom, cadre)
|
|
local hote = hotes[nom]
|
|
if not hote then
|
|
hote = { nom = nom, contenus = {}, ordre = {}, pages = {},
|
|
mobilier = {}, montre = true }
|
|
hotes[nom] = hote
|
|
ordreHotes[#ordreHotes + 1] = nom
|
|
end
|
|
hote.cadre = cadre
|
|
return hote
|
|
end
|
|
|
|
function Panes.Host(nom)
|
|
local hote = hotes[nom]
|
|
return hote and hote.cadre
|
|
end
|
|
|
|
-- ---------------------------------------------------------- les contenus
|
|
|
|
-- def = { hote, groupe, id, construire }
|
|
--
|
|
-- construire(hoteCadre) est appelee UNE fois, a la premiere ouverture. Elle
|
|
-- rend, dans l'ordre : la racine -- un cadre a nous, ou nil -- et la liste
|
|
-- des cadres du client que ce contenu possede.
|
|
function Panes.Register(def)
|
|
local hote = hotes[def.hote]
|
|
if not hote then
|
|
error("ForeverUI.Panes : hote inconnu -- " .. tostring(def.hote))
|
|
end
|
|
|
|
local contenu = {
|
|
id = def.id,
|
|
groupe = def.groupe,
|
|
construire = def.construire,
|
|
hote = hote,
|
|
bati = false,
|
|
cadres = {},
|
|
}
|
|
hote.contenus[def.id] = contenu
|
|
hote.ordre[#hote.ordre + 1] = def.id
|
|
|
|
-- La premiere page declaree pour un groupe en devient la page par defaut.
|
|
if hote.pages[def.groupe] == nil then
|
|
hote.pages[def.groupe] = def.id
|
|
end
|
|
return contenu
|
|
end
|
|
|
|
local function batir(contenu)
|
|
if contenu.bati then
|
|
return
|
|
end
|
|
contenu.bati = true -- avant l'appel : pas de boucle
|
|
|
|
if contenu.construire then
|
|
local racine, cadres = contenu.construire(contenu.hote.cadre)
|
|
contenu.racine = racine
|
|
contenu.cadres = cadres or {}
|
|
end
|
|
end
|
|
|
|
-- Un contenu possede aussi des cadres du client, declares apres coup : un
|
|
-- ecran peut en decouvrir a l'usage, quand le client les cree tard.
|
|
function Panes.Own(hote, id, cadre)
|
|
local h = hotes[hote]
|
|
local contenu = h and h.contenus[id]
|
|
if not contenu or not cadre then
|
|
return
|
|
end
|
|
for _, connu in ipairs(contenu.cadres) do
|
|
if connu == cadre then
|
|
return
|
|
end
|
|
end
|
|
contenu.cadres[#contenu.cadres + 1] = cadre
|
|
end
|
|
|
|
-- Le mobilier d'un groupe. Appelable plusieurs fois : les cadres s'ajoutent.
|
|
function Panes.Furniture(nom, groupe, cadres)
|
|
local hote = hotes[nom]
|
|
if not hote then
|
|
return
|
|
end
|
|
local liste = hote.mobilier[groupe] or {}
|
|
hote.mobilier[groupe] = liste
|
|
for _, cadre in ipairs(cadres or {}) do
|
|
if cadre then
|
|
liste[#liste + 1] = cadre
|
|
end
|
|
end
|
|
end
|
|
|
|
local function poser(contenu, visible)
|
|
if visible then
|
|
batir(contenu)
|
|
elseif not contenu.bati then
|
|
-- Jamais ouvert, donc rien a masquer : on ne le batit pas pour cela.
|
|
return
|
|
end
|
|
|
|
if contenu.racine then
|
|
if visible then contenu.racine:Show() else contenu.racine:Hide() end
|
|
end
|
|
for _, cadre in ipairs(contenu.cadres) do
|
|
if cadre and cadre.Show then
|
|
if visible then cadre:Show() else cadre:Hide() end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- ------------------------------------------------------------ l'affichage
|
|
|
|
local function appliquer(hote)
|
|
local voulu = hote.montre and hote.pages[hote.groupe or ""] or nil
|
|
|
|
for _, id in ipairs(hote.ordre) do
|
|
poser(hote.contenus[id], id == voulu)
|
|
end
|
|
hote.actuel = voulu
|
|
|
|
-- LE MOBILIER. On masque tout ce qui est declare, puis on remontre celui
|
|
-- du groupe courant : un meuble partage par deux groupes ne depend ainsi
|
|
-- d'aucun ordre de parcours.
|
|
local actifs = {}
|
|
local sien = hote.mobilier[hote.groupe or ""]
|
|
if hote.montre and voulu and sien then
|
|
for _, cadre in ipairs(sien) do
|
|
actifs[cadre] = true
|
|
end
|
|
end
|
|
for _, liste in pairs(hote.mobilier) do
|
|
for _, cadre in ipairs(liste) do
|
|
if cadre and cadre.Hide and not actifs[cadre] then
|
|
cadre:Hide()
|
|
end
|
|
end
|
|
end
|
|
for cadre in pairs(actifs) do
|
|
if cadre.Show then
|
|
cadre:Show()
|
|
end
|
|
end
|
|
|
|
-- UN HOTE SANS CONTENU NE S'AFFICHE PAS. C'est ce qui empeche un volet
|
|
-- droit vide -- et son fond, et sa bande de pierre -- de rester a
|
|
-- l'ecran sur un onglet qui n'a rien a y mettre.
|
|
if hote.cadre then
|
|
local utile = hote.montre and voulu ~= nil
|
|
if utile then hote.cadre:Show() else hote.cadre:Hide() end
|
|
end
|
|
end
|
|
|
|
-- Ouvrir un groupe : un ecran entier, tous les hotes a la fois.
|
|
function Panes.ShowGroup(groupe)
|
|
for _, nom in ipairs(ordreHotes) do
|
|
local hote = hotes[nom]
|
|
hote.groupe = groupe
|
|
appliquer(hote)
|
|
end
|
|
Panes.groupe = groupe
|
|
end
|
|
|
|
function Panes.CurrentGroup()
|
|
return Panes.groupe
|
|
end
|
|
|
|
-- Changer de page dans un hote, sans toucher au groupe : c'est ce que font
|
|
-- deux boutons qui se partagent la meme surface.
|
|
function Panes.ShowPage(nom, id)
|
|
local hote = hotes[nom]
|
|
if not hote or not hote.contenus[id] then
|
|
return
|
|
end
|
|
hote.pages[hote.groupe or ""] = id
|
|
appliquer(hote)
|
|
end
|
|
|
|
function Panes.CurrentPage(nom)
|
|
local hote = hotes[nom]
|
|
return hote and hote.actuel
|
|
end
|
|
|
|
-- Masquer un hote entier -- le repli d'un volet. Le groupe et la page
|
|
-- choisie sont conserves : deplier les retrouve.
|
|
function Panes.SetHostShown(nom, etat)
|
|
local hote = hotes[nom]
|
|
if not hote then
|
|
return
|
|
end
|
|
hote.montre = etat and true or false
|
|
appliquer(hote)
|
|
end
|
|
|
|
function Panes.IsHostShown(nom)
|
|
local hote = hotes[nom]
|
|
return hote ~= nil and hote.montre and hote.actuel ~= nil
|
|
end
|
|
|
|
-- Un hote a-t-il quelque chose a montrer pour ce groupe ? Repond sans rien
|
|
-- batir : c'est ce qui decide, par exemple, si le bouton de repli a un sens.
|
|
function Panes.HasContent(nom, groupe)
|
|
local hote = hotes[nom]
|
|
return hote ~= nil and hote.pages[groupe or hote.groupe or ""] ~= nil
|
|
end
|
|
|
|
-- Reposer l'etat courant. A appeler quand un contenu vient d'etre declare
|
|
-- apres l'ouverture du groupe, jamais dans une boucle d'affichage.
|
|
function Panes.Refresh()
|
|
for _, nom in ipairs(ordreHotes) do
|
|
appliquer(hotes[nom])
|
|
end
|
|
end
|
|
|
|
-- TEMOIN. Ce que chaque hote montre, et ce qu'il possede.
|
|
function Panes.Report()
|
|
local lignes = {}
|
|
for _, nom in ipairs(ordreHotes) do
|
|
local hote = hotes[nom]
|
|
local pages = {}
|
|
for _, id in ipairs(hote.ordre) do
|
|
local contenu = hote.contenus[id]
|
|
if contenu.groupe == hote.groupe then
|
|
pages[#pages + 1] = string.format("%s(%d cadres%s)", id,
|
|
#contenu.cadres, contenu.bati and "" or ", jamais bati")
|
|
end
|
|
end
|
|
lignes[#lignes + 1] = string.format(
|
|
"%s : groupe=%s page=%s visible=%s | pages du groupe : %s",
|
|
nom, tostring(hote.groupe), tostring(hote.actuel),
|
|
tostring(hote.montre), table.concat(pages, ", "))
|
|
end
|
|
return lignes
|
|
end
|