Files
moon-well-ui/data/addon/ForeverUI/AtlasUtil.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

635 lines
23 KiB
Lua

-- ForeverUI: drawing parts of atlas sheets.
-- A sheet stays one texture: only the rectangle read from it moves. A partial fill
-- (health bar at 40 %) crops that rectangle instead of stretching the image, which would
-- distort the art.
ForeverUI = ForeverUI or {}
-- Atlas entry { file, left, right, top, bottom, width, height }, or nil if unknown.
local function entry(name)
if not UIAtlas or not UIAtlas.data then
return nil
end
return UIAtlas.data[name]
end
ForeverUI.AtlasEntry = entry
-- Sets a texture to an atlas element. keepSize keeps the current size (when the frame
-- sets its own dimensions).
function ForeverUI.SetAtlas(texture, name, keepSize)
local e = entry(name)
if not e then
return false
end
texture:SetTexture(e[1])
texture:SetTexCoord(e[2], e[3], e[4], e[5])
if not keepSize then
texture:SetWidth(e[6])
texture:SetHeight(e[7])
end
return true
end
-- Left-to-right fill: the rectangle is cropped to the fraction. fullWidth: width at 100 %.
-- The client rejects a zero width, so the texture is hidden instead.
function ForeverUI.SetAtlasFill(texture, name, fraction, fullWidth)
local e = entry(name)
if not e then
texture:Hide()
return false
end
if not fraction or fraction ~= fraction or fraction < 0 then
fraction = 0
elseif fraction > 1 then
fraction = 1
end
local width = (fullWidth or e[6]) * fraction
if width < 1 then
texture:Hide()
return true
end
texture:SetTexture(e[1])
texture:SetTexCoord(e[2], e[2] + (e[3] - e[2]) * fraction, e[4], e[5])
texture:SetWidth(width)
texture:SetHeight(e[7])
texture:Show()
return true
end
-- Nine-slice cut of one atlas element.
-- mainline/MainActionBar.xml, camelot/MainMenuBarBagButtons.xml and MainMenuBarMicroMenu.xml
-- all frame their group with UI-HUD-ActionBar-Frame (TOPLEFT -6, 6; BOTTOMRIGHT 4, -5):
-- a 55 x 55 octagon (110 x 110 on a 2x sheet) with a bronze edge. The modern client
-- nine-slices it; stretched in one piece over 560 px, its bevels would become ramps.
-- options.name: atlas element; options.layer: draw layer (BACKGROUND by default);
-- options.imageMargin: corner size in the image, px; options.imageSize: image side, px;
-- options.margin: corner size on screen, px.
function ForeverUI.SetAtlasNineSlice(parent, options)
local e = entry(options.name)
if not e then
return nil
end
local layer = options.layer or "BACKGROUND"
local margin = options.margin
local fraction = options.imageMargin / options.imageSize
local du = (e[3] - e[2]) * fraction
local dv = (e[5] - e[4]) * fraction
local u = { e[2], e[2] + du, e[3] - du, e[3] }
local v = { e[4], e[4] + dv, e[5] - dv, e[5] }
local function piece(cu1, cu2, cv1, cv2)
local t = parent:CreateTexture(nil, layer)
t:SetTexture(e[1])
t:SetTexCoord(u[cu1], u[cu2], v[cv1], v[cv2])
return t
end
local p = {}
p.topLeftCorner = piece(1, 2, 1, 2)
p.topLeftCorner:SetWidth(margin)
p.topLeftCorner:SetHeight(margin)
p.topLeftCorner:SetPoint("TOPLEFT", parent, "TOPLEFT")
p.topRightCorner = piece(3, 4, 1, 2)
p.topRightCorner:SetWidth(margin)
p.topRightCorner:SetHeight(margin)
p.topRightCorner:SetPoint("TOPRIGHT", parent, "TOPRIGHT")
p.bottomLeftCorner = piece(1, 2, 3, 4)
p.bottomLeftCorner:SetWidth(margin)
p.bottomLeftCorner:SetHeight(margin)
p.bottomLeftCorner:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT")
p.bottomRightCorner = piece(3, 4, 3, 4)
p.bottomRightCorner:SetWidth(margin)
p.bottomRightCorner:SetHeight(margin)
p.bottomRightCorner:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT")
-- Edges and center are set by two opposite corners: no size to compute.
p.topEdge = piece(2, 3, 1, 2)
p.topEdge:SetPoint("TOPLEFT", p.topLeftCorner, "TOPRIGHT")
p.topEdge:SetPoint("BOTTOMRIGHT", p.topRightCorner, "BOTTOMLEFT")
p.bottomEdge = piece(2, 3, 3, 4)
p.bottomEdge:SetPoint("TOPLEFT", p.bottomLeftCorner, "TOPRIGHT")
p.bottomEdge:SetPoint("BOTTOMRIGHT", p.bottomRightCorner, "BOTTOMLEFT")
p.leftEdge = piece(1, 2, 2, 3)
p.leftEdge:SetPoint("TOPLEFT", p.topLeftCorner, "BOTTOMLEFT")
p.leftEdge:SetPoint("BOTTOMRIGHT", p.bottomLeftCorner, "TOPRIGHT")
p.rightEdge = piece(3, 4, 2, 3)
p.rightEdge:SetPoint("TOPLEFT", p.topRightCorner, "BOTTOMLEFT")
p.rightEdge:SetPoint("BOTTOMRIGHT", p.bottomRightCorner, "TOPRIGHT")
p.center = piece(2, 3, 2, 3)
p.center:SetPoint("TOPLEFT", p.topLeftCorner, "BOTTOMRIGHT")
p.center:SetPoint("BOTTOMRIGHT", p.bottomRightCorner, "TOPLEFT")
return p
end
-- Frame art shared by the three groups at the bottom of the screen.
-- The corner must hold the whole bevel: the image's top and left profiles only become
-- constant from pixel 19 of 110. A smaller cut stretches part of the diagonal along the
-- bar. So 20 (one pixel of margin), which is 10 px on screen on this 2x sheet.
function ForeverUI.SetBarFrameArt(frame, layer)
return ForeverUI.SetAtlasNineSlice(frame, {
name = "ui-hud-actionbar-frame",
layer = layer or "BACKGROUND",
imageMargin = 20,
imageSize = 110,
margin = 10,
})
end
-- Divider between two slots. mainline/MainActionBar.xml HorizontalDividerTemplate: 12 wide,
-- three vertical slices of ui-hud-actionbar-frame-divider. Heights 14 top, 16 center,
-- 15 bottom fit a 45 button.
function ForeverUI.CreateDivider(parent, level)
local frame = CreateFrame("Frame", nil, parent)
frame:SetWidth(12)
frame:SetFrameLevel(level or parent:GetFrameLevel())
local top = frame:CreateTexture(nil, "ARTWORK")
if not ForeverUI.SetAtlas(top, "ui-hud-actionbar-frame-divider-threeslice-edgetop", true) then
frame:Hide()
return frame
end
top:SetHeight(14)
top:SetPoint("TOPLEFT", frame, "TOPLEFT")
top:SetPoint("TOPRIGHT", frame, "TOPRIGHT")
local down = frame:CreateTexture(nil, "ARTWORK")
ForeverUI.SetAtlas(down, "ui-hud-actionbar-frame-divider-threeslice-edgebottom", true)
down:SetHeight(15)
down:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT")
down:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT")
local center = frame:CreateTexture(nil, "ARTWORK")
ForeverUI.SetAtlas(center, "!ui-hud-actionbar-frame-divider-threeslice-center", true)
center:SetPoint("TOPLEFT", top, "BOTTOMLEFT")
center:SetPoint("BOTTOMRIGHT", down, "TOPRIGHT")
frame.top, frame.center, frame.down = top, center, down
return frame
end
-- Panel art: mainline/SharedUIPanelTemplates.xml PortraitFrameFlatTemplate, two layers.
-- 1. Flat background (FlatPanelBackgroundTemplate), TOPLEFT (2, -20) to BOTTOMRIGHT (-2, 3):
-- two 16 x 16 rounded bottom corners, an edge between them, the rest flat, all tinted
-- by PANEL_BACKGROUND_COLOR.
-- 2. Metal nine-slice frame: HeldBagLayout (mainline/NineSliceLayouts.lua) with the fixes
-- of camelot/NineSliceLayoutOverrides.lua (top right corner x -2, bottom corners y -8).
-- PANEL_BACKGROUND_COLOR is not in the extracted code; measured on a real client capture
-- (docs/reference): (16, 14, 12), opaque. A translucent one turns bag slot gaps blue.
-- Edges are stretched, not tiled: a tiled atlas texture spreads the whole sheet.
local PANEL_BACKGROUND = { 16 / 255, 14 / 255, 12 / 255, 1 }
-- HeldBagLayout (blizzard_sharedxml/mainline/nineslicelayouts.lua): the eight pieces are
-- OVERLAY and each corner has its offset, copied as is. In OVERLAY the metal covers every
-- region of the frame itself. The source puts title and portrait in child frames
-- (TitleContainer at frameLevel 510, PortraitContainer), which draw above their parent's
-- regions; callers must do the same for anything that must stay visible.
local PANEL_LAYER = "OVERLAY"
local PANEL_CORNERS = {
{ key = "topLeftCorner", name = "ui-frame-portraitmetal-cornertopleftsmall",
point = "TOPLEFT", x = -13, y = 16 },
{ key = "topRightCorner", name = "ui-frame-metal-cornertopright",
point = "TOPRIGHT", x = 4, y = 16 },
{ key = "bottomLeftCorner", name = "ui-frame-metal-cornerbottomleft",
point = "BOTTOMLEFT", x = -13, y = -3 },
{ key = "bottomRightCorner", name = "ui-frame-metal-cornerbottomright",
point = "BOTTOMRIGHT", x = 4, y = -3 },
}
-- options.topLeftCorner: top left corner atlas. HeldBagLayout uses ...CornerTopLeftSmall,
-- PortraitFrameTemplate ...CornerTopLeft (a wider ring for a 62 portrait).
-- options.corners: { key = { x =, y = } } overrides a corner offset, as
-- camelot/NineSliceLayoutOverrides.lua does on every layout (its art has other sizes).
-- options.level: puts the metal in a child frame this many levels above the frame. Needed
-- when the frame has other child frames, which would draw over the metal. The background
-- stays on the frame itself, behind all its child frames.
function ForeverUI.SetPanelArt(frame, options)
if frame.foreverPanel then
return frame.foreverPanel
end
options = options or {}
-- host: where the metal goes. The background stays on the frame, at the back.
local host = frame
if options.level then
host = CreateFrame("Frame", nil, frame)
host:SetAllPoints(frame)
host:SetFrameLevel(frame:GetFrameLevel() + options.level)
frame.foreverSkinLayer = host
end
local p = {}
-- 1. Flat background
local r, v, b, a = PANEL_BACKGROUND[1], PANEL_BACKGROUND[2], PANEL_BACKGROUND[3], PANEL_BACKGROUND[4]
local bottomLeft = frame:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(bottomLeft, "uiframebackground-nineslice-cornerbottomleft")
bottomLeft:SetVertexColor(r, v, b, a)
bottomLeft:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 2, 3)
local bottomRight = frame:CreateTexture(nil, "BACKGROUND")
ForeverUI.SetAtlas(bottomRight, "uiframebackground-nineslice-cornerbottomright")
bottomRight:SetVertexColor(r, v, b, a)
bottomRight:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -2, 3)
local bottomEdge = frame:CreateTexture(nil, "BACKGROUND")
bottomEdge:SetTexture(r, v, b, a)
bottomEdge:SetPoint("TOPLEFT", bottomLeft, "TOPRIGHT")
bottomEdge:SetPoint("BOTTOMRIGHT", bottomRight, "BOTTOMLEFT")
local body = frame:CreateTexture(nil, "BACKGROUND")
body:SetTexture(r, v, b, a)
body:SetPoint("TOPLEFT", frame, "TOPLEFT", 2, -20)
body:SetPoint("BOTTOMRIGHT", bottomRight, "TOPRIGHT")
p.background = { bottomLeft, bottomRight, bottomEdge, body }
-- 2. Metal frame
for _, corner in ipairs(PANEL_CORNERS) do
local texture = host:CreateTexture(nil, PANEL_LAYER)
local atlasName = corner.name
if corner.key == "topLeftCorner" and options.topLeftCorner then
atlasName = options.topLeftCorner
end
if ForeverUI.SetAtlas(texture, atlasName) then
local x, y = corner.x, corner.y
local setting = options.corners and options.corners[corner.key]
if setting then
x = setting.x or x
y = setting.y or y
end
texture:SetPoint(corner.point, host, corner.point, x, y)
p[corner.key] = texture
p[corner.key .. "Atlas"] = atlasName
else
texture:Hide()
end
end
local function edge(name, point1, target1, relative1, point2, target2, relative2)
local texture = host:CreateTexture(nil, PANEL_LAYER)
if not ForeverUI.SetAtlas(texture, name) then
texture:Hide()
return nil
end
texture:SetPoint(point1, target1, relative1)
texture:SetPoint(point2, target2, relative2)
return texture
end
if p.topLeftCorner and p.topRightCorner then
p.topEdge = edge("_ui-frame-metal-edgetop",
"TOPLEFT", p.topLeftCorner, "TOPRIGHT",
"TOPRIGHT", p.topRightCorner, "TOPLEFT")
p.leftEdge = edge("!ui-frame-metal-edgeleft",
"TOPLEFT", p.topLeftCorner, "BOTTOMLEFT",
"BOTTOMLEFT", p.bottomLeftCorner, "TOPLEFT")
p.rightEdge = edge("!ui-frame-metal-edgeright",
"TOPRIGHT", p.topRightCorner, "BOTTOMRIGHT",
"BOTTOMRIGHT", p.bottomRightCorner, "TOPRIGHT")
p.bottomEdge = edge("_ui-frame-metal-edgebottom",
"BOTTOMLEFT", p.bottomLeftCorner, "BOTTOMRIGHT",
"BOTTOMRIGHT", p.bottomRightCorner, "BOTTOMLEFT")
end
frame.foreverPanel = p
ForeverUI.UpdatePanelCorners(frame)
return p
end
-- NineSliceUtil.UpdateCornerCropping and ClipNineSliceBottomCorner
-- (blizzard_sharedxml/nineslice.lua):
-- overhang = topCornerHeight + bottomCornerHeight - frameHeight
-- - topOffset - (-bottomOffset)
-- When a frame is shorter than its two corners stacked, the bottom corners are cropped from
-- the top by that overhang (texture coordinates and height). Otherwise the corners overlap
-- and the left edge between them is drawn upside down across the frame.
function ForeverUI.UpdatePanelCorners(frame)
local p = frame.foreverPanel
if not p then
return
end
local topLeft, bottomLeft
for _, corner in ipairs(PANEL_CORNERS) do
if corner.key == "topLeftCorner" then topLeft = corner end
if corner.key == "bottomLeftCorner" then bottomLeft = corner end
end
local topEntry = ForeverUI.AtlasEntry(p.topLeftCornerAtlas or topLeft.name)
local bottomEntry = ForeverUI.AtlasEntry(bottomLeft.name)
if not (topEntry and bottomEntry) then
return
end
local overhang = topEntry[7] + bottomEntry[7] - frame:GetHeight() - topLeft.y - (-bottomLeft.y)
for _, corner in ipairs(PANEL_CORNERS) do
if corner.point == "BOTTOMLEFT" or corner.point == "BOTTOMRIGHT" then
local texture = p[corner.key]
local e = ForeverUI.AtlasEntry(corner.name)
if texture and e then
local trim = math.max(0, math.min(overhang, e[7]))
local uvHeight = e[5] - e[4]
texture:SetTexCoord(e[2], e[3], e[4] + (trim / e[7]) * uvHeight, e[5])
texture:SetWidth(e[6])
texture:SetHeight(e[7] - trim)
end
end
end
end
-- Vertical divider in three slices. common-framedivider (11 x 50) has a cap at each end;
-- stretched over a panel's height, the caps smear over dozens of pixels. So it is cut by
-- texture coordinates into top cap, stretched middle and bottom cap.
-- atlas: element; endCap: cap height in px (4 by default); level: frame level.
function ForeverUI.CreateVerticalDivider(parent, atlas, endCap, level)
local e = ForeverUI.AtlasEntry(atlas)
local frame = CreateFrame("Frame", nil, parent)
if not e then
frame:Hide()
return frame
end
endCap = endCap or 4
frame:SetWidth(e[6])
frame:SetFrameLevel(level or parent:GetFrameLevel())
local u1, u2, v1, v2, height = e[2], e[3], e[4], e[5], e[7]
local partV = (v2 - v1) * (endCap / height)
local top = frame:CreateTexture(nil, "OVERLAY")
top:SetTexture(e[1])
top:SetTexCoord(u1, u2, v1, v1 + partV)
top:SetHeight(endCap)
top:SetPoint("TOPLEFT", frame, "TOPLEFT")
top:SetPoint("TOPRIGHT", frame, "TOPRIGHT")
local down = frame:CreateTexture(nil, "OVERLAY")
down:SetTexture(e[1])
down:SetTexCoord(u1, u2, v2 - partV, v2)
down:SetHeight(endCap)
down:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT")
down:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT")
local middle = frame:CreateTexture(nil, "OVERLAY")
middle:SetTexture(e[1])
middle:SetTexCoord(u1, u2, v1 + partV, v2 - partV)
middle:SetPoint("TOPLEFT", top, "BOTTOMLEFT")
middle:SetPoint("BOTTOMRIGHT", down, "TOPRIGHT")
frame.top, frame.middle, frame.down = top, middle, down
return frame
end
-- Nine slices cut from one image. A panel image has a shadow and fixed-size rounded
-- corners; stretched whole, the shadow scales with it and the corners distort. Corners keep
-- their size, edges stretch one way, the center both.
-- corner: corner size in px; margins = { left, top, right, bottom }: how far the image
-- overhangs the frame (the shadow thickness measured on the image), so the image's rim
-- falls on the frame edge; level: draw layer (BACKGROUND by default).
function ForeverUI.CreateNineSlice(parent, atlas, corner, margins, level)
local e = ForeverUI.AtlasEntry(atlas)
if not e then
return nil
end
local path, u1, u2, v1, v2, width, height = e[1], e[2], e[3], e[4], e[5], e[6], e[7]
local du = (u2 - u1) * corner / width
local dv = (v2 - v1) * corner / height
local us = { u1, u1 + du, u2 - du, u2 }
local vs = { v1, v1 + dv, v2 - dv, v2 }
local slices = {}
local function slice(column, row)
local t = parent:CreateTexture(nil, level or "BACKGROUND")
t:SetTexture(path)
t:SetTexCoord(us[column], us[column + 1], vs[row], vs[row + 1])
slices[#slices + 1] = t
return t
end
local topLeft, topRight = slice(1, 1), slice(3, 1)
local bottomLeft, bottomRight = slice(1, 3), slice(3, 3)
local top, down = slice(2, 1), slice(2, 3)
local left, right = slice(1, 2), slice(3, 2)
local center = slice(2, 2)
for _, c in ipairs({ topLeft, topRight, bottomLeft, bottomRight }) do
c:SetWidth(corner)
c:SetHeight(corner)
end
top:SetHeight(corner)
down:SetHeight(corner)
left:SetWidth(corner)
right:SetWidth(corner)
local G, H, D, B = margins[1], margins[2], margins[3], margins[4]
topLeft:SetPoint("TOPLEFT", parent, "TOPLEFT", -G, H)
topRight:SetPoint("TOPRIGHT", parent, "TOPRIGHT", D, H)
bottomLeft:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", -G, -B)
bottomRight:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", D, -B)
top:SetPoint("TOPLEFT", topLeft, "TOPRIGHT")
top:SetPoint("TOPRIGHT", topRight, "TOPLEFT")
down:SetPoint("BOTTOMLEFT", bottomLeft, "BOTTOMRIGHT")
down:SetPoint("BOTTOMRIGHT", bottomRight, "BOTTOMLEFT")
left:SetPoint("TOPLEFT", topLeft, "BOTTOMLEFT")
left:SetPoint("BOTTOMRIGHT", bottomLeft, "TOPRIGHT")
right:SetPoint("TOPLEFT", topRight, "BOTTOMLEFT")
right:SetPoint("BOTTOMRIGHT", bottomRight, "TOPRIGHT")
center:SetPoint("TOPLEFT", topLeft, "BOTTOMRIGHT")
center:SetPoint("BOTTOMRIGHT", bottomRight, "TOPLEFT")
return slices
end
-- Tertiary button, two states: common-button-tertiary-normal / -pressed, 46 x 34 each, sheet
-- commonbuttontertiaryc60. The rounded end is 11 px (column profile constant from x = 11),
-- so the corner is 11 on both axes. Stretched instead of sliced, it squashes past 46 wide.
-- The button keeps its own textures at alpha 0: they still hold its state for the client,
-- and some functions read them.
-- auto: the pressed state follows the mouse button. Otherwise the caller drives it with
-- button.foreverPress(true or false), e.g. statistic selectors, pressed while their list
-- is open.
local TERTIARY_NORMAL = "common-button-tertiary-normal"
local TERTIARY_PRESSED = "common-button-tertiary-pressed"
local TERTIARY_CORNER = 11
local TERTIARY_MARGINS = { 0, 0, 0, 0 }
function ForeverUI.SkinTertiaryButton(button, auto)
if button.foreverPress then
return button
end
for _, method in ipairs({ "GetNormalTexture", "GetPushedTexture",
"GetHighlightTexture", "GetDisabledTexture" }) do
local texture = button[method] and button[method](button)
if texture then
texture:SetAlpha(0)
end
end
button.foreverNormal = ForeverUI.CreateNineSlice(button, TERTIARY_NORMAL,
TERTIARY_CORNER, TERTIARY_MARGINS, "BACKGROUND")
button.foreverPressed = ForeverUI.CreateNineSlice(button, TERTIARY_PRESSED,
TERTIARY_CORNER, TERTIARY_MARGINS, "BACKGROUND")
button.foreverPress = function(state)
for _, slice in ipairs(button.foreverPressed or {}) do
if state then slice:Show() else slice:Hide() end
end
for _, slice in ipairs(button.foreverNormal or {}) do
if state then slice:Hide() else slice:Show() end
end
end
button.foreverPress(false)
if auto then
button:HookScript("OnMouseDown", function() button.foreverPress(true) end)
button:HookScript("OnMouseUp", function() button.foreverPress(false) end)
end
return button
end
-- Camelot inset: InsetFrameTemplate (shareduipaneltemplates.xml, nineslicelayouts.lua).
-- Tiled UI-Background-Marble background; UI-Frame-InnerTopLeft / TopRight / BotLeftCorner /
-- BotRight corners (6 x 6, bottom ones at y = -1) joined by _UI-Frame-InnerTopTile / BotTile
-- and !UI-Frame-InnerLeftTile / RightTile (3 thick).
local MARBLE = "interface" .. string.char(92) .. "ForeverUI" .. string.char(92)
.. "framegeneral" .. string.char(92) .. "ui-background-marble"
function ForeverUI.CreateInset(parent, name)
return ForeverUI.DecorateInset(CreateFrame("Frame", name, parent))
end
-- Same art on an existing frame (e.g. the bank rights frame of the guild control window):
-- regions of its own, below its child frames.
function ForeverUI.DecorateInset(e)
local background = e:CreateTexture(nil, "BACKGROUND")
background:SetTexture(MARBLE, true)
if background.SetHorizTile then
background:SetHorizTile(true)
background:SetVertTile(true)
end
background:SetAllPoints(e)
e.background = background
local function corner(atlas, point, y)
local t = e:CreateTexture(nil, "BORDER")
ForeverUI.SetAtlas(t, atlas)
t:SetPoint(point, e, point, 0, y or 0)
return t
end
local topLeft = corner("ui-frame-innertopleft", "TOPLEFT")
local topRight = corner("ui-frame-innertopright", "TOPRIGHT")
local bottomLeft = corner("ui-frame-innerbotleftcorner", "BOTTOMLEFT", -1)
local bottomRight = corner("ui-frame-innerbotright", "BOTTOMRIGHT", -1)
-- thickness from the element (3), length from the corners
local function edge(atlas, a1, c1, r1, a2, c2, r2)
local t = e:CreateTexture(nil, "BORDER")
ForeverUI.SetAtlas(t, atlas, true)
t:SetPoint(a1, c1, r1)
t:SetPoint(a2, c2, r2)
return t
end
local top = edge("_ui-frame-innertoptile", "TOPLEFT", topLeft, "TOPRIGHT", "TOPRIGHT", topRight, "TOPLEFT")
top:SetHeight(3)
local down = edge("_ui-frame-innerbottile", "BOTTOMLEFT", bottomLeft, "BOTTOMRIGHT", "BOTTOMRIGHT", bottomRight, "BOTTOMLEFT")
down:SetHeight(3)
local left = edge("!ui-frame-innerlefttile", "TOPLEFT", topLeft, "BOTTOMLEFT", "BOTTOMLEFT", bottomLeft, "TOPLEFT")
left:SetWidth(3)
local right = edge("!ui-frame-innerrighttile", "TOPRIGHT", topRight, "BOTTOMRIGHT", "BOTTOMRIGHT", bottomRight, "TOPRIGHT")
right:SetWidth(3)
e.trim = { topLeft, topRight, bottomLeft, bottomRight, top, down, left, right }
return e
end
-- Camelot panel button, UIPanelButtonTemplate: three pieces of UI-Panel-Button-Up / -Down /
-- -Disabled (left 12, stretched middle, right 12; 0.6875 high), -Highlight in ADD.
-- b:Activate(yes): UIPanelButton_OnEnable / _OnDisable, -Disabled image and grey font.
-- font: font object name (GameFontNormalSmall by default); template: optional frame template.
local PANEL_BUTTON = "Interface" .. string.char(92) .. "Buttons" .. string.char(92) .. "UI-Panel-Button-"
function ForeverUI.CreatePanelButton(parent, buttonText, width, height, name, font, template)
local b = CreateFrame("Button", name, parent, template)
b:SetWidth(width)
b:SetHeight(height)
local function piece(u1, u2)
local t = b:CreateTexture(nil, "BACKGROUND")
t:SetTexCoord(u1, u2, 0, 0.6875)
return t
end
local g = piece(0, 0.09375)
g:SetWidth(12)
g:SetPoint("TOPLEFT", b, "TOPLEFT", 0, 0)
g:SetPoint("BOTTOMLEFT", b, "BOTTOMLEFT", 0, 0)
local d = piece(0.53125, 0.625)
d:SetWidth(12)
d:SetPoint("TOPRIGHT", b, "TOPRIGHT", 0, 0)
d:SetPoint("BOTTOMRIGHT", b, "BOTTOMRIGHT", 0, 0)
local m = piece(0.09375, 0.53125)
m:SetPoint("TOPLEFT", g, "TOPRIGHT", 0, 0)
m:SetPoint("BOTTOMRIGHT", d, "BOTTOMLEFT", 0, 0)
local pieces = { g, m, d }
local function state(suffix)
for _, t in ipairs(pieces) do
t:SetTexture(PANEL_BUTTON .. suffix)
end
end
state("Up")
local normalFont = font and _G[font .. ""] or GameFontNormalSmall
local hover = font and _G[(font == "GameFontNormal") and "GameFontHighlight" or "GameFontHighlightSmall"]
or GameFontHighlightSmall
local fs = b:CreateFontString(nil, "ARTWORK")
fs:SetFontObject(normalFont)
fs:SetPoint("CENTER", b, "CENTER", 0, 0)
b:SetFontString(fs)
b:SetNormalFontObject(normalFont)
b:SetHighlightFontObject(hover)
if b.SetDisabledFontObject then
b:SetDisabledFontObject((font == "GameFontNormal") and GameFontDisable or GameFontDisableSmall)
end
b:SetText(buttonText)
b:SetHighlightTexture(PANEL_BUTTON .. "Highlight")
local s = b:GetHighlightTexture()
if s then
s:SetTexCoord(0, 0.625, 0, 0.6875)
s:SetBlendMode("ADD")
end
b.active = true
b:SetScript("OnMouseDown", function(self) if self.active then state("Down") end end)
b:SetScript("OnMouseUp", function(self) if self.active then state("Up") end end)
function b:Activate(yes)
self.active = yes and true or false
if yes then
self:Enable()
state("Up")
else
self:Disable()
state("Disabled")
end
end
return b
end