Files
moon-well-ui/data/addon/ForeverUI/IconPicker.lua
T
Adrestie 5842eb7a2b ForeverUI : code en anglais, commentaires resserres, code mort retire
Traduction
- Identifiants en anglais dans tout le module : addon, ecrans d'accueil,
  outils Python, banc d'essai et patcheur C#.
- Fichiers au nom francais renommes (Gabarits.lua -> Templates.lua,
  Textes_*.lua -> Texts_*.lua, deployer.py -> deploy.py,
  tools/patcheur -> tools/patcher, etc.).
- Commandes /fui et options de deploy.py en anglais (--check, --server,
  --remove-dbc).
- Cles des variables sauvegardees et de data/dbc/statistics.json
  traduites, sans reprise des anciennes cles.
- API de la micro-barre : ForeverUI.AddMicroButton(def) avec les champs
  name, atlasSet, after, tooltip, onClick, ready, et
  ForeverUI.UpdateMicro(name, isOpen).
- Commentaires reecrits en anglais, courts, sans dates ni historique ;
  les fonctions au nom peu parlant et leurs parametres sont expliques.

Code mort
- Exports, fonctions et variables jamais lus retires.
- Retires a la demande des modules : ForeverUI.PvPJauge, les exports
  FondEntete / TuilerEntete / Polices de TradeSkill, l'art legacy-up-c60,
  honorfill(half)blue et inv_misc_book_09, leurs entrees d'atlas et les
  parties d'outils correspondantes.

Verifications : liaisons des noms comparees avant et apres, banc vert,
chemins de textures inchanges, generateurs reproduisant les fichiers,
patcheur identique octet pour octet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:15:52 +02:00

498 lines
16 KiB
Lua

-- Icon picker of equipment sets (GearManagerDialogPopup) in the layout of camelot
-- IconSelectorPopupFrameTemplate (SharedUIPanelTemplates.xml) and ScrollBoxSelectorMixin.
-- The client fills a grid of 5 per row through the NUM_GEARSET_ICON* globals. Writing them
-- from the addon would taint every client function that reads them, so the client fills its
-- grid and we redo it after it in rows of 10 (populate, realign).
ForeverUI = ForeverUI or {}
local L = ForeverUI.L
-- Positions from camelot IconSelectorPopupFrameTemplate. The window takes the character
-- sheet's height instead of camelot's 495; from the grid (-97) to the button base, seven
-- rows fit.
local POPUP_W, POPUP_H = 525, 484
local HEADER_X, HEADER_Y = 24, -21
local FIELD_X, FIELD_Y = 29, -35
local CHOOSE_X, CHOOSE_Y = 24, -79
local ZONE_W, ZONE_H = 275, 45
local ZONE_X, ZONE_Y = -13, -13
local CHOICE_ICON = 36
local CHOICE_X, CHOICE_Y = -4.5, -3.5
-- camelot ScrollBoxSelectorMixin: stride 10, button 36, padding 5, spacing 10
local GRID_X, GRID_Y = 21, -97
local ICON = 36
local PER_ROW = 10
local ROWS = 7
local GAP = 10
local MARGIN = 5
local STEP = ICON + GAP
-- camelot SelectionFrameTemplate buttons: Cancel 78 x 22 at BOTTOMRIGHT (-11, 13), Okay at
-- its left (x = -2). They fall exactly in the two slots of the bottom right corner art.
local BOTTOM_BUTTON_W, BOTTOM_BUTTON_H = 78, 22
local BOTTOM_BUTTON_X, BOTTOM_BUTTON_Y = -11, 13
local BOTTOM_BUTTON_GAP = -2
local RIGHT_EDGE = 17 -- width of !macropopup-right
-- Skin sizes (background, frame, bar). Declared here because placeWindow uses them to
-- center the bar in the remaining space.
local BACKGROUND_ALPHA = 0.8
local BACKGROUND_MARGIN = 7
local BAR_W = 8
local ARROW_W, ARROW_H = 17, 11
local CURSOR_H = 36 -- minimal-scrollbar-thumb-bottom
local built = false
local SHOWN_COUNT = PER_ROW * ROWS
-- Creates the missing buttons on the client's template (the client makes only fifteen, at
-- load), then lays out the whole grid in rows of ten.
local function layoutGrid(popup)
for index = #popup.buttons + 1, SHOWN_COUNT do
local button = CreateFrame("CheckButton",
"GearManagerDialogPopupButton" .. index, popup,
"GearSetPopupButtonTemplate")
button:SetID(index)
table.insert(popup.buttons, button)
end
for index, button in ipairs(popup.buttons) do
button:SetWidth(ICON)
button:SetHeight(ICON)
button:ClearAllPoints()
if index == 1 then
button:SetPoint("TOPLEFT", popup, "TOPLEFT",
GRID_X + MARGIN, GRID_Y - MARGIN)
elseif math.fmod(index - 1, PER_ROW) == 0 then
button:SetPoint("TOPLEFT", popup.buttons[index - PER_ROW],
"BOTTOMLEFT", 0, -GAP)
else
button:SetPoint("TOPLEFT", popup.buttons[index - 1], "TOPRIGHT",
GAP, 0)
end
end
end
-- Current choice zone. camelot shows the selected icon and a click scrolls the list to it;
-- 3.3.5 does this jump with RecalculateGearManagerDialogPopup.
local function placeCurrentChoice(popup)
if popup.foreverChoice then
return
end
local zone = CreateFrame("Frame", "ForeverUIIconChoice", popup)
zone:SetWidth(ZONE_W)
zone:SetHeight(ZONE_H)
zone:SetPoint("TOPRIGHT", popup, "TOPRIGHT", ZONE_X, ZONE_Y)
local button = CreateFrame("Button", "ForeverUIIconChoiceButton", zone)
button:SetWidth(CHOICE_ICON)
button:SetHeight(CHOICE_ICON)
button:SetPoint("TOPRIGHT", zone, "TOPRIGHT", CHOICE_X, CHOICE_Y)
local icon = button:CreateTexture(nil, "ARTWORK")
icon:SetAllPoints(button)
button.icon = icon
local highlight = button:CreateTexture(nil, "HIGHLIGHT")
highlight:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
highlight:SetBlendMode("ADD")
highlight:SetAllPoints(button)
-- From the text table: ICON_SELECTION_TITLE_CURRENT and its description are missing from
-- this client.
local title = zone:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
title:SetPoint("TOPRIGHT", button, "TOPLEFT", -6, -2)
title:SetText(L.ICONPICKER_CURRENTLY_SELECTED)
local help = zone:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
help:SetPoint("TOPRIGHT", title, "BOTTOMRIGHT", 0, -2)
help:SetText(L.ICONPICKER_CLICK_TO_VIEW)
button:SetScript("OnClick", function()
if RecalculateGearManagerDialogPopup then
RecalculateGearManagerDialogPopup()
end
end)
popup.foreverChoice = button
popup.foreverZone = zone
end
-- The icon shown in the zone follows the client's selection.
local function updateCurrentChoice()
local popup = _G["GearManagerDialogPopup"]
if not popup or not popup.foreverChoice then
return
end
local texture = popup.selectedTexture
if not texture and popup.selectedIcon and GetEquipmentSetIconInfo then
texture = GetEquipmentSetIconInfo(popup.selectedIcon)
end
popup.foreverChoice.icon:SetTexture(texture or "")
end
-- Sizes the window and places the client's name field, labels, scroll frame and buttons
local function placeWindow(popup)
local sheet = _G["CharacterFrame"]
popup:SetWidth(POPUP_W)
popup:SetHeight((sheet and sheet:GetHeight()) or POPUP_H)
local field = _G["GearManagerDialogPopupEditBox"]
if field then
field:ClearAllPoints()
field:SetPoint("TOPLEFT", popup, "TOPLEFT", FIELD_X, FIELD_Y)
end
-- The two client labels are unnamed regions, found by their text (GEARSETS_POPUP_TEXT and
-- MACRO_POPUP_CHOOSE_ICON).
local regions = { popup:GetRegions() }
for _, region in ipairs(regions) do
if region.GetObjectType and region:GetObjectType() == "FontString" then
local text = region:GetText()
region:ClearAllPoints()
if text == MACRO_POPUP_CHOOSE_ICON then
region:SetPoint("TOPLEFT", popup, "TOPLEFT", CHOOSE_X, CHOOSE_Y)
else
region:SetPoint("TOPLEFT", popup, "TOPLEFT", HEADER_X, HEADER_Y)
end
end
end
local scrolling = _G["GearManagerDialogPopupScrollFrame"]
if scrolling then
scrolling:SetWidth(PER_ROW * STEP - GAP + 2 * MARGIN)
scrolling:SetHeight(ROWS * STEP - GAP + 2 * MARGIN)
scrolling:ClearAllPoints()
scrolling:SetPoint("TOPLEFT", popup, "TOPLEFT", GRID_X, GRID_Y)
end
-- Same space on both sides of the bar: between the last icon column and the bar, and
-- between the bar and the window edge. It is computed, so it follows the grid and window.
local bar = _G["GearManagerDialogPopupScrollFrameScrollBar"]
if bar then
local iconsRight = GRID_X + MARGIN + PER_ROW * STEP - GAP
local free = (POPUP_W - RIGHT_EDGE) - iconsRight
local gap = (free - BAR_W) / 2
bar:ClearAllPoints()
bar:SetPoint("TOPRIGHT", popup, "TOPRIGHT",
-(RIGHT_EDGE + gap), GRID_Y - MARGIN)
bar:SetPoint("BOTTOMRIGHT", popup, "TOPRIGHT",
-(RIGHT_EDGE + gap), GRID_Y - MARGIN - (ROWS * STEP - GAP))
end
local okay = _G["GearManagerDialogPopupOkay"]
local cancel = _G["GearManagerDialogPopupCancel"]
if cancel then
cancel:SetWidth(BOTTOM_BUTTON_W)
cancel:SetHeight(BOTTOM_BUTTON_H)
cancel:ClearAllPoints()
cancel:SetPoint("BOTTOMRIGHT", popup, "BOTTOMRIGHT",
BOTTOM_BUTTON_X, BOTTOM_BUTTON_Y)
end
if okay and cancel then
okay:SetWidth(BOTTOM_BUTTON_W)
okay:SetHeight(BOTTOM_BUTTON_H)
okay:ClearAllPoints()
okay:SetPoint("RIGHT", cancel, "LEFT", BOTTOM_BUTTON_GAP, 0)
end
end
-- ------------------------------------------------------------ filling
-- Equipped items with an icon, like RefreshEquipmentSetIconInfo
local function countEquippedItems()
local n = 0
for i = INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED do
if GetInventoryItemTexture("player", i) then n = n + 1 end
end
return n
end
-- The client's count (_TotalItems): items, macro icons, and the special icon when
-- RecalculateGearManagerDialogPopup has set one
local function tally()
local base = countEquippedItems() + GetNumMacroIcons()
if GetEquipmentSetIconInfo(base + 1) then
base = base + 1
end
return base
end
local inProgress = false
-- After GearManagerDialogPopup_Update: refill as a grid of ten. Each button's ID is set so
-- that GearSetPopupButton_OnClick (offset x 5 + ID) lands on the icon it shows. Scrolling
-- counts rows of ten with the client's GEARSET_ICON_ROW_HEIGHT (read only).
local function populate()
local popup = _G["GearManagerDialogPopup"]
local scrolling = _G["GearManagerDialogPopupScrollFrame"]
if inProgress or not (built and popup and scrolling and popup.buttons) then
return
end
inProgress = true
local offset = FauxScrollFrame_GetOffset(scrolling) or 0
local total = tally()
local clientPerRow = NUM_GEARSET_ICONS_PER_ROW or 5
for i, button in ipairs(popup.buttons) do
local index = offset * PER_ROW + i
button:SetID(offset * (PER_ROW - clientPerRow) + i)
if i <= SHOWN_COUNT and index <= total then
local texture = GetEquipmentSetIconInfo(index)
button.icon:SetTexture(texture)
button:Show()
if index == popup.selectedIcon then
button:SetChecked(1)
elseif texture and texture == popup.selectedTexture then
button:SetChecked(1)
popup:SetSelection(false, index)
else
button:SetChecked(nil)
end
else
button.icon:SetTexture("")
button:Hide()
end
end
FauxScrollFrame_Update(scrolling, math.ceil(total / PER_ROW), ROWS, GEARSET_ICON_ROW_HEIGHT)
inProgress = false
end
-- After RecalculateGearManagerDialogPopup: scroll the selected icon into view with the
-- client's rule (at least ROWS rows shown, no move if it is on the first page)
local function realign()
local popup = _G["GearManagerDialogPopup"]
local scrolling = _G["GearManagerDialogPopupScrollFrame"]
if not (built and popup and scrolling) then
return
end
local total = tally()
local match = popup.selectedIcon
if not match and popup.selectedTexture then
for index = 1, total do
if GetEquipmentSetIconInfo(index) == popup.selectedTexture then
match = index
break
end
end
end
if match then
local last = math.floor((total - 1) / PER_ROW)
local rowLine = math.floor((match - 1) / PER_ROW)
rowLine = rowLine + math.min(ROWS - 1, last - rowLine) - (ROWS - 1)
if match <= SHOWN_COUNT then
rowLine = 0
end
FauxScrollFrame_OnVerticalScroll(scrolling, rowLine * GEARSET_ICON_ROW_HEIGHT, GEARSET_ICON_ROW_HEIGHT, nil)
end
populate()
end
-- Builds the grid and choice zone once, then applies the skin; skipped in combat
local function applySkin()
local popup = _G["GearManagerDialogPopup"]
if not popup or not popup.buttons or InCombatLockdown() then
return
end
if not built then
layoutGrid(popup)
placeCurrentChoice(popup)
placeWindow(popup)
built = true
end
if ForeverUI.IconPickerSkin then
ForeverUI.IconPickerSkin()
end
updateCurrentChoice()
end
ForeverUI.IconPicker = { Apply = applySkin }
-- ============================================================ skin
--
-- camelot: black background at 80 %, inset 7; SelectionFrameTemplate frame, a nine-slice
-- of macropopup-* atlases (the bottom right corner is 174 wide: it holds the button base);
-- MinimalScrollBar, 8 wide, three-piece track and thumb, 17 x 11 arrows.
-- Replaces the 3.3.5 window art with the camelot background and frame
local function skinFrame(popup)
if popup.foreverFrame then
return
end
-- Hide the 3.3.5 window art.
local regions = { popup:GetRegions() }
for _, region in ipairs(regions) do
if region.GetObjectType and region:GetObjectType() == "Texture" then
region:SetAlpha(0)
end
end
local background = popup:CreateTexture(nil, "BACKGROUND")
background:SetTexture(0, 0, 0, BACKGROUND_ALPHA)
background:SetPoint("TOPLEFT", popup, "TOPLEFT", BACKGROUND_MARGIN, -BACKGROUND_MARGIN)
background:SetPoint("BOTTOMRIGHT", popup, "BOTTOMRIGHT", -BACKGROUND_MARGIN, BACKGROUND_MARGIN)
local function piece(atlas, point)
local t = popup:CreateTexture(nil, "BORDER")
if not ForeverUI.SetAtlas(t, atlas) then
t:Hide()
end
if point then
t:SetPoint(point, popup, point, 0, 0)
end
return t
end
local topLeft = piece("macropopup-topleft-c60", "TOPLEFT")
local topRight = piece("macropopup-topright-c60", "TOPRIGHT")
local bottomLeft = piece("macropopup-bottomleft-c60", "BOTTOMLEFT")
local bottomRight = piece("macropopup-bottomright-c60", "BOTTOMRIGHT")
local top = piece("_macropopup-top-c60")
top:SetPoint("TOPLEFT", topLeft, "TOPRIGHT")
top:SetPoint("TOPRIGHT", topRight, "TOPLEFT")
local down = piece("_macropopup-bottom-c60")
down:SetPoint("BOTTOMLEFT", bottomLeft, "BOTTOMRIGHT")
down:SetPoint("BOTTOMRIGHT", bottomRight, "BOTTOMLEFT")
-- Side strips keep their atlas width: anchoring them by opposite corners would stretch them
-- to the next corner, and the bottom right corner is 174 wide. Two points on the same side
-- are enough.
local left = piece("!macropopup-left-c60")
left:SetPoint("TOPLEFT", topLeft, "BOTTOMLEFT")
left:SetPoint("BOTTOMLEFT", bottomLeft, "TOPLEFT")
local right = piece("!macropopup-right-c60")
right:SetPoint("TOPRIGHT", topRight, "BOTTOMRIGHT")
right:SetPoint("BOTTOMRIGHT", bottomRight, "TOPRIGHT")
popup.foreverFrame = { topLeft, topRight, bottomLeft, bottomRight, top, down, left, right }
popup.foreverBackground = background
end
-- Scroll bar: the FauxScrollFrame bar keeps its behavior; only its textures change.
local function skinBar()
local bar = _G["GearManagerDialogPopupScrollFrameScrollBar"]
if not bar or bar.foreverBar then
return
end
-- The bar's old trim is not on the bar: the client's scroll frame holds two 30-wide
-- textures (the track outline), so hide them too.
local frame = _G["GearManagerDialogPopupScrollFrame"]
if frame then
for _, region in ipairs({ frame:GetRegions() }) do
if region.GetObjectType and region:GetObjectType() == "Texture" then
region:SetAlpha(0)
end
end
end
bar:SetWidth(BAR_W)
-- The thumb is a region of the bar: keep it out of this loop, or it stays invisible after
-- being retextured.
local cursor = _G["GearManagerDialogPopupScrollFrameScrollBarThumbTexture"]
for _, region in ipairs({ bar:GetRegions() }) do
if region ~= cursor and region.GetObjectType
and region:GetObjectType() == "Texture" then
region:SetAlpha(0)
end
end
local function slice(atlas, layer)
local t = bar:CreateTexture(nil, layer or "BACKGROUND")
if not ForeverUI.SetAtlas(t, atlas) then
t:Hide()
end
t:SetWidth(BAR_W)
return t
end
local trackTop = slice("minimal-scrollbar-track-top-c60")
trackTop:SetPoint("TOP", bar, "TOP", 0, 0)
local bottomLeft = slice("minimal-scrollbar-track-bottom-c60")
bottomLeft:SetPoint("BOTTOM", bar, "BOTTOM", 0, 0)
local trackMiddle = slice("!minimal-scrollbar-track-middle-c60")
trackMiddle:SetPoint("TOPLEFT", trackTop, "BOTTOMLEFT")
trackMiddle:SetPoint("BOTTOMRIGHT", bottomLeft, "TOPRIGHT")
-- The client has one thumb texture where camelot has three: use the stretchable middle
-- piece at the size of camelot's shortest thumb (8 x 36).
if cursor then
ForeverUI.SetAtlas(cursor, "minimal-scrollbar-thumb-middle-c60", true)
cursor:SetWidth(BAR_W)
cursor:SetHeight(CURSOR_H)
cursor:SetAlpha(1)
end
for name, atlas in pairs({
["GearManagerDialogPopupScrollFrameScrollBarScrollUpButton"] =
"minimal-scrollbar-arrow-top-c60",
["GearManagerDialogPopupScrollFrameScrollBarScrollDownButton"] =
"minimal-scrollbar-arrow-bottom-c60",
}) do
local button = _G[name]
if button then
button:SetWidth(ARROW_W)
button:SetHeight(ARROW_H)
for _, method in ipairs({ "GetNormalTexture", "GetPushedTexture",
"GetDisabledTexture", "GetHighlightTexture" }) do
local texture = button[method] and button[method](button)
if texture then
texture:SetAlpha(0)
end
end
local arrow = button:CreateTexture(nil, "ARTWORK")
ForeverUI.SetAtlas(arrow, atlas)
arrow:SetPoint("CENTER", button, "CENTER", 0, 0)
end
end
bar.foreverBar = true
end
-- Right of the character sheet: camelot anchors its TOPLEFT to the TOPRIGHT of the frame it
-- accompanies. The 3.3.5 anchor, the gear manager, is no longer a window here.
local function placeBeside(popup)
local sheet = _G["CharacterFrame"]
if not sheet then
return
end
popup:ClearAllPoints()
popup:SetPoint("TOPLEFT", sheet, "TOPRIGHT", 0, 0)
end
ForeverUI.IconPickerSkin = function()
local popup = _G["GearManagerDialogPopup"]
if not popup then
return
end
skinFrame(popup)
skinBar()
placeBeside(popup)
end
-- Run at load, and last: the client fills its grid on first open and needs its eighty
-- buttons then; IconPickerSkin must already be defined when this runs.
applySkin()
if hooksecurefunc then
for _, name in ipairs({ "GearManagerDialogPopup_OnShow", "GearManagerDialogPopup_Update" }) do
if type(_G[name]) == "function" then
hooksecurefunc(name, function() applySkin() end)
end
end
if type(GearManagerDialogPopup_Update) == "function" then
hooksecurefunc("GearManagerDialogPopup_Update", populate)
end
if type(RecalculateGearManagerDialogPopup) == "function" then
hooksecurefunc("RecalculateGearManagerDialogPopup", realign)
end
end