Files
benilla-octo/crates/benilla-app/assets/ui/ItemTextFrame.xml
T
SamandClaude Fable 5 a683d5524f the interface code is ours: the transcription debt is paid
- forty files that carried the reference's literal Lua are rewritten as our own expression of the behaviour — worst identical run 61 lines down to 7 — and the README now says exactly that
- the overlap gate measures the code itself across all four reference corpora, and ratchets: the number may only go down
- a lost session ends at the login screen instead of silently re-authenticating and stealing the account back
- a building's skybox is a real model: Caverns of Time's white ceiling becomes its painted nebula and additive stars

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-14 19:21:03 +07:00

396 lines
22 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 item-text reader — letters, books, plaques (the mail arc's "permanent copy" lands
here). The Lua below is OURS; the reference ItemTextFrame.xml (354 l) + ItemTextFrame.lua
(104 l) are the SPECIFICATION it satisfies — frame/region names, the event flow, the
material art paths and the geometry/art are the reference's and are quoted as such.
Driven by the engine's item-text seam (benilla-ui/src/script/item_text.rs) +
crate::ui_item_text's feed/drain: the app fires the reference event flow
ITEM_TEXT_BEGIN → ITEM_TEXT_READY → ITEM_TEXT_CLOSED.
ADAPTATIONS (the MailFrame.xml precedents):
· The body scroll (ItemTextScrollFrame, UIPanelScrollFrameTemplate 280×355 at -66,-76) is
a plain Frame at that exact rect with the page FontString INSIDE it (no live scrollbar);
a very long page clips. The ref's own flanking scrollbar art (UI-Character-ScrollBar
top/bottom + the black middle strip) is quoted verbatim as inert layers, so the right
edge reads exactly like the reference at rest.
· ItemTextPageText is SimpleHTML in the ref — drawn here as a FontString at the same
rect (our letters/pages are plain text; an HTML page renders as its markup, accepted).
· The ScrollFrame-only verbs the reference's OnLoad/READY use (UpdateScrollChildRect,
ScrollBar SetValue/Hide, scrollBarHideable) are dropped with the live scrollbar.
· The two material colour TABLES are the reference's data (its Fonts.xml), quoted
verbatim — the numbers ARE the look. They live here Benilla-scoped (the MoneyInput-shim
precedent) because our own Fonts.xml deliberately doesn't ship the reference's two
globals; the lookup around them is ours, under the reference's published name.
· GlobalStrings inlined: PREV "Prev", NEXT "Next", ITEM_TEXT_FROM "From," (the reference
string really is comma'd).
· ITEM_TEXT_TRANSLATION (the foreign-language progress bar) is implemented but never
fires — no translation mechanic on our letters; the StatusBar stays hidden.
· <HitRectInsets> quoted verbatim for fidelity but currently inert (the loader ignores
the element).
-->
<Script><![CDATA[
-- ── The material palette ─────────────────────────────────────────────────────────────
-- The six materials a page can be printed on, and the ink each one takes. The COLOURS are
-- the reference's own data (its Fonts.xml l.27-42) and are quoted verbatim: a different
-- number is a different look. Benilla-scoped names — see the header.
BENILLA_MATERIAL_TEXT_COLOR_TABLE = {
["Default"] = {0.18, 0.12, 0.06},
["Stone"] = {1.0, 1.0, 1.0},
["Parchment"] = {0.18, 0.12, 0.06},
["Marble"] = {0, 0, 0},
["Silver"] = {0.12, 0.12, 0.12},
["Bronze"] = {0.18, 0.12, 0.06}
}
BENILLA_MATERIAL_TITLETEXT_COLOR_TABLE = {
["Default"] = {0, 0, 0},
["Stone"] = {0.93, 0.82, 0},
["Parchment"] = {0, 0, 0},
["Marble"] = {0.93, 0.82, 0},
["Silver"] = {0.93, 0.82, 0},
["Bronze"] = {0.93, 0.82, 0}
}
-- The page ink and the title ink for a material, under the reference's published name
-- (its UIParent.lua l.1880-1888). Falls back to "Default" unless BOTH halves are known, so
-- an unrecognised material never gets one table's colour and the other's default.
function GetMaterialTextColors(material)
local textColor = BENILLA_MATERIAL_TEXT_COLOR_TABLE[material]
local titleColor = BENILLA_MATERIAL_TITLETEXT_COLOR_TABLE[material]
if not (textColor and titleColor) then
textColor = BENILLA_MATERIAL_TEXT_COLOR_TABLE["Default"]
titleColor = BENILLA_MATERIAL_TITLETEXT_COLOR_TABLE["Default"]
end
return textColor, titleColor
end
-- ── The reader's regions, by role ────────────────────────────────────────────────────
-- The four material corners are one SET: a page is either bare parchment (all four off)
-- or dressed in a material, in which case each corner wears that material's own art.
-- Naming the suffixes once is also what makes the art path a single expression.
local MATERIAL_CORNERS = { "TopLeft", "TopRight", "BotLeft", "BotRight" }
-- Everything a fresh ITEM_TEXT_BEGIN clears away. At that point only the title is known,
-- so only the title stays on screen — the page and all of its paging chrome go.
local PAGE_CHROME = {
"ItemTextScrollFrame", "ItemTextCurrentPage", "ItemTextStatusBar",
"ItemTextPrevPageButton", "ItemTextNextPageButton",
}
local function ShowOrHide(region, wanted)
if wanted then
region:Show()
else
region:Hide()
end
end
-- A page always HAS a material: the server may name none, and the frame's own art is
-- already parchment, so that is what "none" means.
local function PageMaterial()
return ItemTextGetMaterial() or "Parchment"
end
-- Dress the page's four corners in its material. Parchment IS the frame's own art, so
-- parchment leaves the corners off and lets the bare frame show through; anything else
-- lights all four and points each at its own slice of that material's art set.
local function DressPage(material)
if material == "Parchment" then
for _, corner in ipairs(MATERIAL_CORNERS) do
getglobal("ItemTextMaterial" .. corner):Hide()
end
return
end
local art = "Interface\\ItemTextFrame\\ItemText-" .. material .. "-"
for _, corner in ipairs(MATERIAL_CORNERS) do
local layer = getglobal("ItemTextMaterial" .. corner)
layer:Show()
layer:SetTexture(art .. corner)
end
end
-- Put the reader on screen. The panel manager is allowed to refuse (something
-- native-center is holding the screen) — and then we are not reading after all, so the
-- server has to be told, or the session stays open behind a window that never appeared.
-- The reference makes the same check, on both of the paths that open this window.
local function OpenReader(frame)
ShowUIPanel(frame)
if not frame:IsVisible() then
CloseItemText()
end
end
-- ── The event flow ───────────────────────────────────────────────────────────────────
-- One function per event of the reference's flow: ItemTextFrame_OnLoad registers FLOW in
-- order and ItemTextFrame_OnEvent dispatches into Handlers by name. (The reference states
-- the same pairing twice — four RegisterEvent calls, then a four-arm `if event == …`
-- chain — which is how a fifth event gets added to one of them and not the other.)
local FLOW = {
"ITEM_TEXT_BEGIN", "ITEM_TEXT_TRANSLATION", "ITEM_TEXT_READY", "ITEM_TEXT_CLOSED",
}
local Handlers = {}
-- A text has been opened. Only its title is known yet, so the page is cleared away and
-- the ink is set for the material the pages are going to arrive on.
function Handlers.ITEM_TEXT_BEGIN(frame)
ItemTextTitleText:SetText(ItemTextGetItem())
for _, name in ipairs(PAGE_CHROME) do
getglobal(name):Hide()
end
local ink = GetMaterialTextColors(PageMaterial())
ItemTextPageText:SetTextColor(ink[1], ink[2], ink[3])
end
-- A foreign-language page is being translated: the turn buttons stand down and the
-- progress bar takes over, ranged across the arg1 seconds the server quoted (OnUpdate
-- fills it). Never fires on our letters — see the header.
function Handlers.ITEM_TEXT_TRANSLATION(frame)
ItemTextPrevPageButton:Hide()
ItemTextNextPageButton:Hide()
frame.translationElapsed = 0
ItemTextStatusBar:SetMinMaxValues(0, arg1)
ItemTextStatusBar:Show()
OpenReader(frame)
end
-- A page has arrived. A letter signs off with its sender (ITEM_TEXT_FROM — the reference
-- string really is comma'd); anything else just gets its trailing blank line. The paging
-- row appears only when there is another page to be on, and then each button shows only
-- if there is something in its direction.
function Handlers.ITEM_TEXT_READY(frame)
local creator = ItemTextGetCreator()
local body = "\n" .. ItemTextGetText()
if creator then
body = body .. "\n\n" .. "From," .. "\n" .. creator .. "\n\n"
else
body = body .. "\n"
end
ItemTextPageText:SetText(body)
ItemTextScrollFrame:Show()
local pageNumber = ItemTextGetPage()
local hasNext = ItemTextHasNextPage()
DressPage(PageMaterial())
if pageNumber > 1 or hasNext then
ItemTextCurrentPage:SetText(pageNumber)
ItemTextCurrentPage:Show()
ShowOrHide(ItemTextPrevPageButton, pageNumber > 1)
ShowOrHide(ItemTextNextPageButton, hasNext)
end
ItemTextStatusBar:Hide()
OpenReader(frame)
end
function Handlers.ITEM_TEXT_CLOSED(frame)
HideUIPanel(frame)
end
function ItemTextFrame_OnLoad()
for _, event in ipairs(FLOW) do
this:RegisterEvent(event)
end
end
function ItemTextFrame_OnEvent(event)
local handler = Handlers[event]
if handler then
handler(this)
end
end
-- The translation bar's clock: while the bar is up it fills with the seconds spent so far.
function ItemTextFrame_OnUpdate(elapsed)
if not ItemTextStatusBar:IsVisible() then
return
end
local spent = this.translationElapsed + elapsed
ItemTextStatusBar:SetValue(spent)
this.translationElapsed = spent
end
-- Both page buttons are one gesture in two directions: the paging click, then the
-- engine's own page request. `turnPage` is ItemTextPrevPage or ItemTextNextPage.
function ItemTextFrame_TurnPage(turnPage)
PlaySound("igMainMenuOptionCheckBoxOn")
turnPage()
end
]]></Script>
<Frame name="ItemTextFrame" toplevel="true" movable="true" enableMouse="true" hidden="true" parent="UIParent">
<Size><AbsDimension x="384" y="512"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-104"/></Offset></Anchor></Anchors>
<HitRectInsets><AbsInset left="0" right="30" top="0" bottom="70"/></HitRectInsets>
<Layers>
<Layer level="BACKGROUND">
<Texture file="Interface\Spellbook\Spellbook-Icon">
<Size><AbsDimension x="58" y="58"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="10" y="-8"/></Offset></Anchor></Anchors>
</Texture>
</Layer>
<Layer level="BORDER">
<Texture file="Interface\ItemTextFrame\UI-ItemText-TopLeft">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
</Texture>
<Texture file="Interface\Spellbook\UI-SpellbookPanel-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\ItemTextFrame\UI-ItemText-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\Spellbook\UI-SpellbookPanel-BotRight">
<Size><AbsDimension x="128" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="256" y="-256"/></Offset></Anchor></Anchors>
</Texture>
</Layer>
<Layer level="ARTWORK">
<!-- The non-parchment material corners (stone/marble/…): textures swapped in by
ITEM_TEXT_READY; hidden entirely for parchment (ref l.56-70). -->
<Texture name="ItemTextMaterialTopLeft" hidden="true">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="21" y="-75"/></Offset></Anchor></Anchors>
</Texture>
<Texture name="ItemTextMaterialTopRight" hidden="true">
<Size><AbsDimension x="64" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="ItemTextMaterialTopLeft" relativePoint="TOPRIGHT"/></Anchors>
</Texture>
<Texture name="ItemTextMaterialBotLeft" hidden="true">
<Size><AbsDimension x="256" y="128"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="ItemTextMaterialTopLeft" relativePoint="BOTTOMLEFT"/></Anchors>
</Texture>
<Texture name="ItemTextMaterialBotRight" hidden="true">
<Size><AbsDimension x="64" y="128"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="ItemTextMaterialTopLeft" relativePoint="BOTTOMRIGHT"/></Anchors>
</Texture>
</Layer>
<Layer level="OVERLAY">
<FontString name="ItemTextTitleText" inherits="GameFontNormal">
<Size><AbsDimension x="224" y="14"/></Size>
<Anchors><Anchor point="CENTER"><Offset><AbsDimension x="6" y="230"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="ItemTextCurrentPage" inherits="GameFontNormal">
<Size><AbsDimension x="192" y="0"/></Size>
<Anchors><Anchor point="TOP"><Offset><AbsDimension x="10" y="-50"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
<Frames>
<!-- ref ItemTextScrollFrame l.145-219: the plain-Frame stand-in (see the header) with
the ref's own flanking scrollbar art kept verbatim. -->
<Frame name="ItemTextScrollFrame" hidden="true">
<Size><AbsDimension x="280" y="355"/></Size>
<Anchors><Anchor point="TOPRIGHT"><Offset><AbsDimension x="-66" y="-76"/></Offset></Anchor></Anchors>
<Layers>
<!-- The ref declares ARTWORK before BACKGROUND here, and the order is
load-bearing: Middle anchors to Top by name, and anchors resolve at
SetPoint time — a target declared later falls back to the parent (and
the black track lands on the parchment). -->
<Layer level="ARTWORK">
<Texture name="ItemTextScrollFrameTop" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
<Size><AbsDimension x="28" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="0" y="5"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="0.484375" top="0" bottom="1.0"/>
</Texture>
<Texture name="ItemTextScrollFrameBottom" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
<Size><AbsDimension x="28" y="107"/></Size>
<Anchors><Anchor point="BOTTOMLEFT" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-3"/></Offset></Anchor></Anchors>
<TexCoords left="0.515625" right="1.0" top="0" bottom="0.4140625"/>
</Texture>
</Layer>
<Layer level="BACKGROUND">
<Texture name="ItemTextScrollFrameMiddle">
<Size><AbsDimension x="25" y="365"/></Size>
<Anchors><Anchor point="TOPRIGHT" relativeTo="ItemTextScrollFrameTop"/></Anchors>
<Color r="0" g="0" b="0" a="0.8"/>
</Texture>
</Layer>
<Layer level="OVERLAY">
<!-- ref ItemTextPageText (SimpleHTML 270×304 at 0,-15 in the scroll child)
as a FontString at the same rect. -->
<FontString name="ItemTextPageText" inherits="ItemTextFontNormal" justifyH="LEFT" justifyV="TOP">
<Size><AbsDimension x="270" y="304"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-15"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
</Frame>
<!-- ref ItemTextStatusBar l.220-251 — the ITEM_TEXT_TRANSLATION progress bar; never
fires on our letters, kept for fidelity. -->
<StatusBar name="ItemTextStatusBar" hidden="true">
<Size><AbsDimension x="250" y="21"/></Size>
<Anchors><Anchor point="BOTTOM" relativeTo="ItemTextFrame"><Offset><AbsDimension x="-5" y="128"/></Offset></Anchor></Anchors>
<Layers>
<Layer level="OVERLAY">
<Texture file="Interface\PaperDollInfoFrame\UI-Character-Skills-BarBorder">
<Size><AbsDimension x="256" y="23"/></Size>
<Anchors><Anchor point="CENTER"/></Anchors>
<TexCoords left="0" right="1.0" top="0.0625" bottom="0.75"/>
</Texture>
</Layer>
</Layers>
<Scripts>
<OnLoad>this:SetFrameLevel(this:GetFrameLevel()+1)</OnLoad>
</Scripts>
<BarTexture file="Interface\TargetingFrame\UI-TargetingFrame-BarFill"/>
<BarColor r="0.25" g="0.75" b="0.25"/>
</StatusBar>
<Button name="ItemTextPrevPageButton" hidden="true">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors><Anchor point="CENTER" relativePoint="TOPLEFT"><Offset><AbsDimension x="90" y="-56"/></Offset></Anchor></Anchors>
<Layers>
<Layer level="ARTWORK">
<FontString inherits="GameFontNormal" justifyH="LEFT" text="Prev"><!-- PREV -->
<Anchors><Anchor point="LEFT" relativePoint="RIGHT"/></Anchors>
</FontString>
</Layer>
</Layers>
<Scripts>
<OnClick>ItemTextFrame_TurnPage(ItemTextPrevPage)</OnClick>
</Scripts>
<NormalTexture file="Interface\Buttons\UI-SpellbookIcon-PrevPage-Up"/>
<PushedTexture file="Interface\Buttons\UI-SpellbookIcon-PrevPage-Down"/>
<DisabledTexture file="Interface\Buttons\UI-SpellbookIcon-PrevPage-Disabled"/>
<HighlightTexture file="Interface\Buttons\UI-Common-MouseHilight" alphaMode="ADD"/>
</Button>
<Button name="ItemTextNextPageButton" hidden="true">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors><Anchor point="CENTER" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-55" y="-56"/></Offset></Anchor></Anchors>
<Layers>
<Layer level="ARTWORK">
<FontString inherits="GameFontNormal" justifyH="RIGHT" text="Next"><!-- NEXT -->
<Anchors><Anchor point="RIGHT" relativePoint="LEFT"/></Anchors>
</FontString>
</Layer>
</Layers>
<Scripts>
<OnClick>ItemTextFrame_TurnPage(ItemTextNextPage)</OnClick>
</Scripts>
<NormalTexture file="Interface\Buttons\UI-SpellbookIcon-NextPage-Up"/>
<PushedTexture file="Interface\Buttons\UI-SpellbookIcon-NextPage-Down"/>
<DisabledTexture file="Interface\Buttons\UI-SpellbookIcon-NextPage-Disabled"/>
<HighlightTexture file="Interface\Buttons\UI-Common-MouseHilight" alphaMode="ADD"/>
</Button>
<Button name="ItemTextCloseButton">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors><Anchor point="CENTER" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-45" y="-26"/></Offset></Anchor></Anchors>
<Scripts>
<OnClick>HideUIPanel(ItemTextFrame)</OnClick>
</Scripts>
<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"/>
</Button>
</Frames>
<Scripts>
<OnLoad>ItemTextFrame_OnLoad()</OnLoad>
<OnEvent>ItemTextFrame_OnEvent(event)</OnEvent>
<OnUpdate>ItemTextFrame_OnUpdate(arg1)</OnUpdate>
<OnShow>PlaySound("igMainMenuOpen")</OnShow>
<OnHide>
CloseItemText()
PlaySound("igMainMenuClose")
</OnHide>
</Scripts>
</Frame>
</Ui>