Files
Adrestie a7709279b8 ForeverUI : fenêtre de butin de camelot
La fenêtre de butin reprend le LootFrame de camelot : panneau plat
« Objets » de 220 de large et 290 de haut au plus, une carte par
emplacement (fond teinté par la qualité, contour, étiquette de rareté,
bouton d'objet de 37, nom), barre de défilement au-delà, place sous la
souris ou à (16, -116). Le LootFrame du client reste l'hôte, invisible :
événements, liste du butin de maître ancrée au bouton choisi, CloseLoot.
Feuille interface/lootframe/lootframe.blp ajoutée. Animations à venir.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 02:18:38 +02:00

636 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 }
ForeverUI.PanelBackground = PANEL_BACKGROUND
-- 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