Files
moon-well-ui/data/addon/ForeverUI/ScrollBar.lua
T
Adrestie dc4932127c Reputation et competences : la barre de defilement de camelot
ScrollBar.lua refait MinimalScrollBar -- 3.3.5 n a ni ce gabarit ni le
ScrollBox qui le pilote. La barre ne connait pas la liste : on lui donne
combien de lignes existent, combien tiennent et ou l on en est, et elle
rend le nouveau decalage. Elle s efface quand tout tient.

Ancrages de camelot : TOPLEFT sur le TOPRIGHT du ScrollBox (5, -2),
BOTTOMLEFT sur son BOTTOMRIGHT (5, 4).

LES TRANCHES SONT MESUREES, PAS DEDUITES DE LEUR NOM. Celle que la
source appelle thumb-bottom fait 8 x 36 et n est PAS un embout : son
alpha monte de 1 a 255, donc son haut est transparent, et posee au bas
du curseur elle le faisait fondre dans le noir de la glissiere. Les deux
bouts du curseur sont donc le MEME morceau, thumb-top, le second
retourne : le curseur est plein jusqu a ses extremites.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-23 16:21:03 +02:00

257 lines
9.2 KiB
Lua

-- ForeverUI : la barre de defilement de camelot, MinimalScrollBar.
--
-- RELEVE -- camelot/reputationframe.xml et blizzard_tokenui/camelot :
-- <EventFrame parentKey="ScrollBar" inherits="MinimalScrollBar">
-- TOPLEFT sur le TOPRIGHT du ScrollBox (5, -2)
-- BOTTOMLEFT sur le BOTTOMRIGHT du ScrollBox (5, 4)
--
-- MESURE SUR L'ART, et non d'apres les noms -- la regle vaut ici plus
-- qu'ailleurs, les noms de ces atlas ne disant pas ce qu'ils portent :
--
-- minimal-scrollbar-track-top / -bottom 8 x 8, noir a 60 % d'alpha,
-- le texel du bout transparent :
-- ce sont les deux embouts
-- arrondis de la glissiere
-- !minimal-scrollbar-track-middle 8 x 1, faite pour s'etirer
-- minimal-scrollbar-thumb-top 8 x 8, brun : l'embout du haut
-- minimal-scrollbar-thumb-middle 8 x 515, brun, etirable
-- minimal-scrollbar-thumb-bottom 8 x 36 -- et ce N'EST PAS un
-- embout : son alpha monte de 1 a
-- 255 sur les 36 pixels, donc son
-- HAUT est transparent. Posee au
-- bas du curseur, elle le faisait
-- fondre dans le noir de la
-- glissiere. ELLE N'EST PAS
-- EMPLOYEE.
-- minimal-scrollbar-arrow-top / -bottom 17 x 11, plus larges que la
-- barre : elles la debordent de
-- 4,5 de chaque cote
--
-- LES DEUX BOUTS DU CURSEUR SONT LE MEME MORCEAU, le second RETOURNE.
-- thumb-top est opaque d'un bord a l'autre, avec un filet clair sur son
-- premier texel : c'est un embout fini. On le pose en haut tel quel et en
-- bas retourne, et le milieu s'etire entre les deux -- le curseur est alors
-- plein jusqu'a ses extremites, ce que la demande veut. Ecart assume, et
-- mesure : garder le morceau nomme "bottom" donnait un degrade.
--
-- CE QUI DIFFERE, ET POURQUOI. 3.3.5 n'a pas MinimalScrollBar, ni le
-- ScrollBox qui le pilote : la barre est refaite en entier, et c'est
-- l'appelant qui lui dit combien de lignes existent, combien tiennent, et
-- ou il en est. Elle lui rend le nouveau decalage, rien de plus -- elle ne
-- touche a aucune liste.
local ForeverUI = ForeverUI or {}
_G.ForeverUI = ForeverUI
local LARGEUR = 8
local FLECHE_L, FLECHE_H = 17, 11
local CURSEUR_BOUT = 8 -- minimal-scrollbar-thumb-top
local CURSEUR_MIN = 2 * CURSEUR_BOUT -- ses deux bouts, sans milieu
local TRACK_BOUT = 8
local ATLAS = {
trackHaut = "minimal-scrollbar-track-top-c60",
trackMilieu = "!minimal-scrollbar-track-middle-c60",
trackBas = "minimal-scrollbar-track-bottom-c60",
curseurBout = "minimal-scrollbar-thumb-top-c60",
curseurMilieu = "minimal-scrollbar-thumb-middle-c60",
flecheHaut = "minimal-scrollbar-arrow-top-c60",
flecheHautSurvol = "minimal-scrollbar-arrow-top-over-c60",
flecheBas = "minimal-scrollbar-arrow-bottom-c60",
flecheBasSurvol = "minimal-scrollbar-arrow-bottom-over-c60",
}
-- OU EN EST LA SOURIS, dans le repere d'un cadre. GetCursorPosition rend des
-- coordonnees d'ECRAN : il faut les ramener a l'echelle du cadre.
local function souris(cadre)
local _, y = GetCursorPosition()
return y / (cadre:GetEffectiveScale() or 1)
end
local function creerFleche(barre, nom, atlas, atlasSurvol, pas)
local bouton = CreateFrame("Button", nom, barre)
bouton:SetWidth(FLECHE_L)
bouton:SetHeight(FLECHE_H)
local image = bouton:CreateTexture(nil, "ARTWORK")
ForeverUI.SetAtlas(image, atlas, true)
image:SetAllPoints(bouton)
bouton.image = image
bouton:SetScript("OnEnter", function(self)
ForeverUI.SetAtlas(self.image, atlasSurvol, true)
end)
bouton:SetScript("OnLeave", function(self)
ForeverUI.SetAtlas(self.image, atlas, true)
end)
bouton:SetScript("OnClick", function()
barre:Deplacer(barre.decalage + pas)
end)
return bouton
end
-- LA BARRE. `parent` la porte, `liste` est ce qu'elle fait defiler : on
-- s'ancre sur lui comme camelot le fait sur son ScrollBox.
function ForeverUI.CreateScrollBar(nom, parent, liste)
local barre = CreateFrame("Frame", nom, parent)
barre:SetWidth(LARGEUR)
barre:SetPoint("TOPLEFT", liste, "TOPRIGHT", 5, -2)
barre:SetPoint("BOTTOMLEFT", liste, "BOTTOMRIGHT", 5, 4)
barre.total = 0
barre.visibles = 0
barre.decalage = 0
local haut = creerFleche(barre, nom .. "Up", ATLAS.flecheHaut,
ATLAS.flecheHautSurvol, -1)
haut:SetPoint("TOP", barre, "TOP", 0, 0)
barre.flecheHaut = haut
local bas = creerFleche(barre, nom .. "Down", ATLAS.flecheBas,
ATLAS.flecheBasSurvol, 1)
bas:SetPoint("BOTTOM", barre, "BOTTOM", 0, 0)
barre.flecheBas = bas
-- LA GLISSIERE, entre les deux fleches : deux embouts et un milieu tendu.
local piste = CreateFrame("Frame", nil, barre)
piste:SetPoint("TOPLEFT", haut, "BOTTOMLEFT", 0, 0)
piste:SetPoint("BOTTOMRIGHT", bas, "TOPRIGHT", 0, 0)
piste:SetWidth(LARGEUR)
barre.piste = piste
local function tranche(cadre, atlas, couche)
local t = cadre:CreateTexture(nil, couche or "BACKGROUND")
ForeverUI.SetAtlas(t, atlas, true)
t:SetWidth(LARGEUR)
return t
end
local pisteHaut = tranche(piste, ATLAS.trackHaut)
pisteHaut:SetHeight(TRACK_BOUT)
pisteHaut:SetPoint("TOP", piste, "TOP", 0, 0)
local pisteBas = tranche(piste, ATLAS.trackBas)
pisteBas:SetHeight(TRACK_BOUT)
pisteBas:SetPoint("BOTTOM", piste, "BOTTOM", 0, 0)
local pisteMilieu = tranche(piste, ATLAS.trackMilieu)
pisteMilieu:SetPoint("TOPLEFT", pisteHaut, "BOTTOMLEFT", 0, 0)
pisteMilieu:SetPoint("BOTTOMRIGHT", pisteBas, "TOPRIGHT", 0, 0)
-- LE CURSEUR : ses trois tranches, dans un cadre qu'on deplace.
local curseur = CreateFrame("Frame", nom .. "Thumb", piste)
curseur:SetWidth(LARGEUR)
curseur:SetHeight(CURSEUR_MIN)
curseur:EnableMouse(true)
barre.curseur = curseur
local cHaut = tranche(curseur, ATLAS.curseurBout, "ARTWORK")
cHaut:SetHeight(CURSEUR_BOUT)
cHaut:SetPoint("TOP", curseur, "TOP", 0, 0)
-- LE MEME MORCEAU, RETOURNE : on echange le haut et le bas de son
-- rectangle d'atlas.
local cBas = tranche(curseur, ATLAS.curseurBout, "ARTWORK")
local e = ForeverUI.AtlasEntry(ATLAS.curseurBout)
if e then
cBas:SetTexCoord(e[2], e[3], e[5], e[4])
end
cBas:SetHeight(CURSEUR_BOUT)
cBas:SetPoint("BOTTOM", curseur, "BOTTOM", 0, 0)
local cMilieu = tranche(curseur, ATLAS.curseurMilieu, "ARTWORK")
cMilieu:SetPoint("TOPLEFT", cHaut, "BOTTOMLEFT", 0, 0)
cMilieu:SetPoint("BOTTOMRIGHT", cBas, "TOPRIGHT", 0, 0)
-- CE QUE LA BARRE SAIT FAIRE.
-- Deplacer d'un cran, en bornant : elle ne connait pas la liste, elle
-- ne connait que ses trois nombres.
function barre:Deplacer(vers)
local maximum = math.max(0, self.total - self.visibles)
vers = math.max(0, math.min(math.floor(vers + 0.5), maximum))
if vers == self.decalage then
return
end
self.decalage = vers
self:Repositionner()
if self.surDefilement then
self.surDefilement(vers)
end
end
-- Poser le curseur : sa hauteur dit la part visible, sa place le
-- decalage.
function barre:Repositionner()
local hauteur = self.piste:GetHeight() or 0
local maximum = math.max(0, self.total - self.visibles)
if hauteur <= 0 or maximum <= 0 then
self.curseur:Hide()
return
end
local part = self.visibles / self.total
local taille = math.max(CURSEUR_MIN, math.floor(hauteur * part + 0.5))
if taille > hauteur then
taille = hauteur
end
local course = hauteur - taille
self.curseur:SetHeight(taille)
self.curseur:ClearAllPoints()
self.curseur:SetPoint("TOP", self.piste, "TOP", 0,
-course * (self.decalage / maximum))
self.curseur:Show()
end
-- CE QUE L'APPELANT LUI DIT : combien de lignes, combien tiennent, ou il
-- en est. La barre s'efface quand tout tient.
function barre:Regler(total, visibles, decalage)
self.total = total or 0
self.visibles = visibles or 0
self.decalage = decalage or 0
if self.total <= self.visibles then
self:Hide()
return
end
self:Show()
self:Repositionner()
end
-- GLISSER LE CURSEUR. Un OnUpdate le suit tant que le bouton est tenu :
-- 3.3.5 n'a pas de suivi de souris sur un cadre.
curseur:SetScript("OnMouseDown", function(self)
self.prise = souris(self)
self.priseDecalage = barre.decalage
self:SetScript("OnUpdate", function(soi)
local hauteur = barre.piste:GetHeight() or 0
local course = hauteur - (soi:GetHeight() or 0)
local maximum = math.max(0, barre.total - barre.visibles)
if course <= 0 or maximum <= 0 then
return
end
local parcouru = soi.prise - souris(soi)
barre:Deplacer(soi.priseDecalage + parcouru / course * maximum)
end)
end)
curseur:SetScript("OnMouseUp", function(self)
self:SetScript("OnUpdate", nil)
end)
-- CLIQUER LA GLISSIERE saute d'une page, du cote ou l'on a clique.
piste:EnableMouse(true)
piste:SetScript("OnMouseDown", function(self)
local y = souris(self)
local dessus = barre.curseur:GetTop() or 0
local dessous = barre.curseur:GetBottom() or 0
if y > dessus then
barre:Deplacer(barre.decalage - barre.visibles)
elseif y < dessous then
barre:Deplacer(barre.decalage + barre.visibles)
end
end)
barre:Hide()
return barre
end