Files
benilla-octo/crates/benilla-app/assets/ui/DressUpFrame.xml
T
SamandClaude Fable 5 b5bc569156 the addon arc: the AddOns folder opens
- benilla loads real 1.12 addons from Interface/AddOns — TOC, XML and Lua through one loader, with per-addon saved variables, Bindings.xml and the lifecycle events addons expect
- both AddOns screens are in: the character-select list with per-addon enable state, and the ESC menu's in-game list
- the API surface is measured, not guessed — a corpus harness runs hundreds of real 1.12 addons and ranks exactly which globals, templates and verbs they demand
- the Lua dialect is the client's own, read off the binary — 5.0 semantics with the 1.12 additions, and UI source files are bytes, not UTF-8

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 18:18:31 +07:00

319 lines
20 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<Ui>
<!--
The dressing room (decision 1060) — ref `DressUpFrame.xml` / `DressUpFrame.lua`, quoted.
CTRL-left-click an item anywhere (bags, the paper doll, a chat link, the bank) and it lands
on your own character here, over the gear you are actually wearing. The window's own
instruction line says exactly that, in Blizzard's words:
`DRESSUP_FRAME_INSTRUCTIONS` = "CTRL-Left Click additional items to display them on your
character" (ref-GlobalStrings.lua:944) — which is also where the modifier is settled: CTRL
previews, SHIFT posts the link to chat (`ContainerFrame.lua:565-571`).
THE MODEL PANE is a booth-texture stand-in, not the ref's live `<DressUpModel>` widget —
the settled doctrine (0105/0118) and exactly what the character window's own pane does
(`CharacterFrame.xml`'s header). So the ref's three model METHODS have no widget to live on
and become the engine globals `BenillaDressUpModel_Dress/TryOn/Close/SetFacing`
(`benilla-ui/src/script/dressup.rs` has the why); `DressUpItem`/`DressUpItemLink` keep their
real global names, because every ctrl-click site in the UI calls those.
Departures, each named:
- No drag (the ref is `movable` with StartMoving/StopMovingOrSizing): benilla has no
user-moved-frame affordance yet — the window sits in the left UIPanel slot, which the ref
registers it into too (`UIParent.lua:41`, transcribed in UiPanels.xml).
- `DressUpModel:SetUnit("player")` and `:Dress()` are ONE verb here
(`BenillaDressUpModel_Dress`): our model is always the player's, so "point it at the
player" and "put the player's own gear back on it" are the same effect.
- The ref's `scale="2.0"` on the model widget is a 3D-widget zoom with no meaning for a
baked pane — the booth frames the body from its own bounds (`framing::body_frame`), the
same law the paper doll is framed by. The bake is framed at THIS pane's 316×351 aspect,
not square, or the UI's stretch would leave every character 11% too tall (decision 1069).
- The pane ANIMATES, as the ref's live widget does — Stand looping (decision 1069; the look
call 0822 §5 left open, made by the director). Its booth is TRANSPARENT for the same
reason a widget is: the dark room behind the character is the `DressUpBackground-<Race>`
art below, and Reset/Close sit on that art *inside* this pane's rect (both anchor sets
transcribed byte-identically from the ref, which is why the overlap is the ref's too).
-->
<Script><![CDATA[
-- GlobalStrings this window uses (ref-GlobalStrings.lua, cited per line; the
-- `X = X or "..."` idiom is this codebase's convention — CharacterFrame.xml/InspectFrame.xml).
DRESSUP_FRAME = DRESSUP_FRAME or "Dressing Room" -- :943
DRESSUP_FRAME_INSTRUCTIONS = DRESSUP_FRAME_INSTRUCTIONS
or "CTRL-Left Click additional items to display them on your character" -- :944
RESET = RESET or "Reset" -- :3340
CLOSE = CLOSE or "Close" -- :760
-- ref DressUpFrame.lua:2-8. The `not IsVisible()` gate is the reference's own state reset:
-- opening the room fresh re-dresses the model in the player's real gear, and only then
-- tries the clicked item on. Both halves ride ONE ordered intent queue app-side, so the
-- reset can never overtake the try-on (dressup.rs's module doc).
function DressUpItem(item)
if not DressUpFrame:IsVisible() then
ShowUIPanel(DressUpFrame)
BenillaDressUpModel_Dress() -- ref: DressUpModel:SetUnit("player")
end
BenillaDressUpModel_TryOn(item) -- ref: DressUpModel:TryOn(item)
end
-- ref DressUpFrame.lua:10-16, verbatim: pull the bare item id out of the `|Hitem:<id>:…`
-- payload and hand THAT to TryOn. Every ctrl-click site in the UI calls this one function,
-- which is why it keeps the reference's global name.
function DressUpItemLink(link)
if not link then
return
end
local item = gsub(link, ".*item:(%d+).*", "%1", 1)
DressUpItem(tonumber(item))
end
-- ref DressUpFrame.lua:18-32 including its own "-- HACK!!!" comment: gnomes borrow the
-- dwarf backdrop and trolls the orc one (there is no Gnome/Troll art), and an unknown race
-- falls back to Orc. Quoted, not corrected.
function DressUpTexturePath()
local race, fileName = UnitRace("player")
if fileName == "Gnome" or fileName == "GNOME" then
fileName = "Dwarf"
elseif fileName == "Troll" or fileName == "TROLL" then
fileName = "Orc"
end
if not fileName then
fileName = "Orc"
end
return "Interface\\DressUpFrame\\DressUpBackground-" .. fileName
end
-- ref DressUpFrame.lua:34-40. Called on OnShow as well as OnLoad: at load time the app has
-- usually not pushed the player's unit snapshot yet, so `UnitRace` answers nil and the ref's
-- own fallback would freeze an Orc backdrop onto every character (the same one-tick nil the
-- character window's level line documents).
function SetDressUpBackground()
local texture = DressUpTexturePath()
DressUpBackgroundTopLeft:SetTexture(texture .. 1)
DressUpBackgroundTopRight:SetTexture(texture .. 2)
DressUpBackgroundBotLeft:SetTexture(texture .. 3)
DressUpBackgroundBotRight:SetTexture(texture .. 4)
end
-- The pane's facing — the paper doll's pair of handlers, over this room's own booth yaw
-- (ref Model_OnLoad/Model_RotateLeft/Model_RotateRight/Model_OnUpdate, UIParent.lua:1421-1462;
-- CharacterFrame.xml carries the full note on the ref's sign quirk, quoted here too).
function BenillaDressUpModel_OnLoad(frame)
frame.facing = 0.61 -- ref UIParent.lua:1422, the literal default facing
BenillaDressUpModel_SetFacing(frame.facing)
end
function BenillaDressUpModel_RotateLeft(frame)
frame.facing = frame.facing - 0.03
BenillaDressUpModel_SetFacing(frame.facing)
PlaySound("igInventoryRotateCharacter")
end
function BenillaDressUpModel_RotateRight(frame)
frame.facing = frame.facing + 0.03
BenillaDressUpModel_SetFacing(frame.facing)
PlaySound("igInventoryRotateCharacter")
end
-- The held-button spin (ref Model_OnUpdate) — half a turn per second while a rotate button
-- reads PUSHED, the ref's own opposite-sign held branches included.
function BenillaDressUpModel_OnUpdate(frame, elapsed)
local step = elapsed * 2 * PI * ROTATIONS_PER_SECOND
if getglobal(frame:GetName() .. "RotateLeftButton"):GetButtonState() == "PUSHED" then
frame.facing = frame.facing + step
if frame.facing < 0 then
frame.facing = frame.facing + (2 * PI)
end
BenillaDressUpModel_SetFacing(frame.facing)
end
if getglobal(frame:GetName() .. "RotateRightButton"):GetButtonState() == "PUSHED" then
frame.facing = frame.facing - step
if frame.facing > (2 * PI) then
frame.facing = frame.facing - (2 * PI)
end
BenillaDressUpModel_SetFacing(frame.facing)
end
end
]]></Script>
<!-- A panel action button (Reset/Close): the ref's UIPanelButtonTemplate face, locally
reconstructed — this house dialect ships no such template, and each window keeps its own
copy (CraftFrame.xml's identical note). -->
<Button name="BenillaDressUpPanelButtonTemplate" virtual="true">
<Size><AbsDimension x="80" y="22"/></Size>
<NormalFont inherits="GameFontNormal"/>
<HighlightFont inherits="GameFontHighlight"/>
<DisabledFont inherits="GameFontDisable"/>
<NormalTexture file="Interface\Buttons\UI-Panel-Button-Up"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></NormalTexture>
<PushedTexture file="Interface\Buttons\UI-Panel-Button-Down"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></PushedTexture>
<DisabledTexture file="Interface\Buttons\UI-Panel-Button-Disabled"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></DisabledTexture>
<HighlightTexture file="Interface\Buttons\UI-Panel-Button-Highlight" alphaMode="ADD"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></HighlightTexture>
</Button>
<Frame name="DressUpFrame" toplevel="true" hidden="true">
<!-- The ref's 384×512 window. No <Anchors>: ShowUIPanel seats it in the left slot, this
house's convention for every panel window (MerchantFrame.xml/CraftFrame.xml). -->
<Size><AbsDimension x="384" y="512"/></Size>
<HitRectInsets><AbsInset left="0" right="30" top="0" bottom="45"/></HitRectInsets>
<Layers>
<!-- The "player" portrait, 60×60 at TOPLEFT (7,-6) (ref l.20-33) — BACKGROUND so the
window art's ring frames it; bound in OnShow, like every other portrait here. -->
<Layer level="BACKGROUND">
<Texture name="DressUpFramePortrait">
<Size><AbsDimension x="60" y="60"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-6"/></Offset></Anchor></Anchors>
</Texture>
</Layer>
<!-- The window art: the general-panel top pair + the skill-frame bottom pair, the ref's
own four slabs (l.34-72). -->
<Layer level="ARTWORK">
<Texture file="Interface\PaperDollInfoFrame\UI-Character-General-TopLeft">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
</Texture>
<Texture file="Interface\PaperDollInfoFrame\UI-Character-General-TopRight">
<Size><AbsDimension x="128" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="256" y="0"/></Offset></Anchor></Anchors>
</Texture>
<Texture file="Interface\PaperDollInfoFrame\SkillFrame-BotLeft">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-256"/></Offset></Anchor></Anchors>
</Texture>
<Texture file="Interface\PaperDollInfoFrame\SkillFrame-BotRight">
<Size><AbsDimension x="128" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="256" y="-256"/></Offset></Anchor></Anchors>
</Texture>
<FontString name="DressUpFrameTitleText" inherits="GameFontHighlight" text="DRESSUP_FRAME">
<Size><AbsDimension x="300" y="14"/></Size>
<Anchors><Anchor point="TOP"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="DressUpFrameDescriptionText" inherits="GameFontNormalSmall" text="DRESSUP_FRAME_INSTRUCTIONS">
<Size><AbsDimension x="260" y="36"/></Size>
<Anchors>
<Anchor point="CENTER" relativeTo="DressUpFrameTitleText" relativePoint="BOTTOM"><Offset><AbsDimension x="10" y="-22"/></Offset></Anchor>
</Anchors>
</FontString>
</Layer>
<!-- The race backdrop behind the model, four quadrants (ref l.73-118); the files are
chosen per race by SetDressUpBackground above. -->
<Layer level="OVERLAY">
<Texture name="DressUpBackgroundTopLeft">
<Size><AbsDimension x="256" y="255"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="22" y="-76"/></Offset></Anchor></Anchors>
</Texture>
<Texture name="DressUpBackgroundTopRight">
<Size><AbsDimension x="62" y="255"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="DressUpBackgroundTopLeft" relativePoint="TOPRIGHT"/></Anchors>
</Texture>
<Texture name="DressUpBackgroundBotLeft">
<Size><AbsDimension x="256" y="128"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="DressUpBackgroundTopLeft" relativePoint="BOTTOMLEFT"/></Anchors>
</Texture>
<Texture name="DressUpBackgroundBotRight">
<Size><AbsDimension x="62" y="128"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="DressUpBackgroundTopLeft" relativePoint="BOTTOMRIGHT"/></Anchors>
</Texture>
</Layer>
</Layers>
<Frames>
<!-- The corner close X: the ref's UIPanelCloseButton seat (CENTER of TOPRIGHT, -46,-25 —
ref l.120-127) on the minimize-button art every close button in this house uses. -->
<Button name="DressUpFrameCloseButton">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors><Anchor point="CENTER" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-46" y="-25"/></Offset></Anchor></Anchors>
<NormalTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Up"/>
<PushedTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Down"/>
<HighlightTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Highlight" alphaMode="ADD"/>
<Scripts>
<OnClick>HideUIPanel(DressUpFrame)</OnClick>
</Scripts>
</Button>
<!-- Close (ref l.128-146) and Reset (l.147-165, chained off Close's LEFT). Reset is the
ref's `DressUpModel:Dress()` + its gsTitleOptionOK click. -->
<Button name="DressUpFrameCancelButton" inherits="BenillaDressUpPanelButtonTemplate" text="CLOSE">
<Anchors><Anchor point="CENTER" relativePoint="TOPLEFT"><Offset><AbsDimension x="305" y="-422"/></Offset></Anchor></Anchors>
<Scripts>
<OnClick>HideUIPanel(DressUpFrame)</OnClick>
</Scripts>
</Button>
<Button name="DressUpFrameResetButton" inherits="BenillaDressUpPanelButtonTemplate" text="RESET">
<Anchors><Anchor point="RIGHT" relativeTo="DressUpFrameCancelButton" relativePoint="LEFT"/></Anchors>
<Scripts>
<OnClick>
BenillaDressUpModel_Dress()
PlaySound("gsTitleOptionOK")
</OnClick>
</Scripts>
</Button>
<!-- The model pane (ref the <DressUpModel> widget, l.166-207): 316×351 anchored BOTTOM
(-11,+85). A plain Frame sampling the "dressup" booth bake (header). -->
<Frame name="DressUpModelFrame">
<Size><AbsDimension x="316" y="351"/></Size>
<Anchors><Anchor point="BOTTOM"><Offset><AbsDimension x="-11" y="85"/></Offset></Anchor></Anchors>
<Layers>
<!-- BACKGROUND, not ARTWORK — the booth quad is an OPAQUE stand-in for a widget
the reference renders outside the layer batches, so it must sit as low as a
region goes or it swallows same-level siblings (decision 1070; the pet
page's diet icon is where that bit). This window is the one where the
change is visible: the CLOSE/RESET buttons are declared BEFORE the pane and
overlap its lower edge, so at ARTWORK the pane was painting over them. -->
<Layer level="BACKGROUND">
<Texture name="$parentTexture">
<Size><AbsDimension x="316" y="351"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
</Texture>
</Layer>
</Layers>
<Frames>
<!-- Rotate buttons (ref l.183-206): 35×35 at the WINDOW's TOPLEFT (21,-75), left
then right chained off its TOPRIGHT. Both edges registered, so one tap fires
OnClick twice and holding hands the spin to the pane's OnUpdate — the ref's
own tap-and-hold (decision 0638 §3). -->
<Button name="DressUpModelFrameRotateLeftButton">
<Size><AbsDimension x="35" y="35"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="DressUpFrame" relativePoint="TOPLEFT"><Offset><AbsDimension x="21" y="-75"/></Offset></Anchor></Anchors>
<NormalTexture file="Interface\Buttons\UI-RotationLeft-Button-Up"/>
<PushedTexture file="Interface\Buttons\UI-RotationLeft-Button-Down"/>
<HighlightTexture file="Interface\Buttons\ButtonHilight-Round" alphaMode="ADD"/>
<Scripts>
<OnLoad>self:RegisterForClicks("LeftButtonDown", "LeftButtonUp")</OnLoad>
<OnClick>BenillaDressUpModel_RotateLeft(getglobal("DressUpModelFrame"))</OnClick>
</Scripts>
</Button>
<Button name="DressUpModelFrameRotateRightButton">
<Size><AbsDimension x="35" y="35"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="DressUpModelFrameRotateLeftButton" relativePoint="TOPRIGHT"/></Anchors>
<NormalTexture file="Interface\Buttons\UI-RotationRight-Button-Up"/>
<PushedTexture file="Interface\Buttons\UI-RotationRight-Button-Down"/>
<HighlightTexture file="Interface\Buttons\ButtonHilight-Round" alphaMode="ADD"/>
<Scripts>
<OnLoad>self:RegisterForClicks("LeftButtonDown", "LeftButtonUp")</OnLoad>
<OnClick>BenillaDressUpModel_RotateRight(getglobal("DressUpModelFrame"))</OnClick>
</Scripts>
</Button>
</Frames>
<Scripts>
<OnLoad>
BenillaDressUpModel_OnLoad(self)
BenillaSetBoothTexture(getglobal(self:GetName() .. "Texture"), "dressup")
</OnLoad>
<OnUpdate>BenillaDressUpModel_OnUpdate(self, arg1)</OnUpdate>
</Scripts>
</Frame>
</Frames>
<Scripts>
<OnLoad>SetDressUpBackground()</OnLoad>
<OnShow>
SetDressUpBackground()
SetPortraitTexture(DressUpFramePortrait, "player")
PlaySound("igCharacterInfoOpen")
</OnShow>
<!-- Closing empties the booth (dressup.rs's `Close` intent): the ref's widget keeps its
state while hidden, but its next DressUpItem re-issues SetUnit("player") *because*
the frame wasn't visible, so that state is never observable. -->
<OnHide>
BenillaDressUpModel_Close()
PlaySound("igCharacterInfoClose")
</OnHide>
</Scripts>
</Frame>
</Ui>