Files
moon-well-ui/data/addon/ForeverUI/Panes.lua
T
Adrestie eb681aa9cd Aucun texte en dur : anglais en base, francais, langues extensibles
- socle : Textes.lua (ForeverUI.L) et ForeverUIGlueTextes.lua (G.L) ; les
  textes dans Textes_enUS / Textes_frFR (325 cles en jeu) et
  ForeverUIGlueTextes_enUS / _frFR (41 cles aux ecrans d'accueil) ; une cle
  absente prend l'anglais, une langue sans fichier lit l'anglais ; ajouter
  une langue = un fichier de plus
- les textes que le client 3.3.5 connait se lisent dans ses globales
  (GlobalStrings, GlueStrings en glue) ; les textes de secours sont retires
- tous les fichiers repris : libelles, infobulles, mode edition, messages et
  aide de /fui, erreurs internes ; .toc : Notes + Notes-frFR
- selection des personnages : race et classe traduites ramenees a leur jeton
  par les tables de langue (GetAvailableRaces / GetAvailableClasses font
  planter le client hors de la creation)
- tools/cartes_instances.py : noms des instances dans toutes les langues de
  la LibBabble-Zone de WDM (portails de la carte sur tout client)
- banc : toute cle employee existe en anglais et en francais, memes cles dans
  les deux, bascule frFR / deDE essayee ; chaines 3.3.5 ajoutees au faux client
- README : etat des sections mis a jour, langues, glue, deploiement --glue

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 03:22:30 +02:00

295 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 = {}
local L = ForeverUI.L
-- --------------------------------------------------------------- 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.L.PANES_ERROR_UNKNOWN_HOST .. 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(L.PANES_REPORT_PAGE, id,
#contenu.cadres, contenu.bati and "" or L.PANES_REPORT_NEVER_BUILT)
end
end
lignes[#lignes + 1] = string.format(
L.PANES_REPORT_LINE,
nom, tostring(hote.groupe), tostring(hote.actuel),
tostring(hote.montre), table.concat(pages, ", "))
end
return lignes
end