- 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>
1276 lines
73 KiB
XML
1276 lines
73 KiB
XML
<Ui>
|
||
<!--
|
||
The chat window, rebuilt on the reference's own structure (decision 0288 P3): the docked
|
||
General + Combat Log pair sharing ChatFrame1's rect, the hover-revealed tabs and
|
||
background (the classic transparent chat — chat-cache.txt ships window alpha 0, hover
|
||
fades to DEFAULT_CHATFRAME_ALPHA 0.25), the outside-left scroll column with the
|
||
blinking bottom button, and the reference edit box (three-piece border art + header)
|
||
below the frame. Geometry, art paths, timing constants, and behaviors are QUOTED from the
|
||
extracted patch-chain FloatingChatFrame.xml/.lua + ChatFrame.xml/.lua (the pin in 0288);
|
||
window management (drag/undock/resize/rename, the tab right-click options dropdown) is
|
||
chat SETTINGS — deliberately out (0288 §2).
|
||
|
||
The inbound feed (ui_chat::feed) AddMessages into ChatFrame1; the composer/router phase
|
||
(0288 P4) fans CHAT_MSG groups across both windows per the chat-cache defaults. The edit
|
||
box submits through SubmitChatInput → ui_chat::input's grammar; its live header machine
|
||
(ChatEdit_UpdateHeader) is 0288 P5 — until then the header shows the sticky default
|
||
"Say:" with static insets.
|
||
-->
|
||
<Script><![CDATA[
|
||
-- ── NUM_CHAT_WINDOWS — how many chat windows the UI declares ─────────────────────────
|
||
-- `NUM_CHAT_WINDOWS = 7` is ChatFrame.lua line 5, verbatim. It is a FrameXML constant, not
|
||
-- an engine one, and the number is NOT the engine's: the chat-cache and the per-window
|
||
-- struct array `0xb4fe50` (wow-re `chat-color-table.md`, stride 0x98) both hold **10**
|
||
-- windows, and FloatingChatFrame.xml duly declares ChatFrame1TabDockRegion..10 — but only
|
||
-- seven ChatFrames. 7 is the count an addon iterates.
|
||
--
|
||
-- Measured demand: **5 corpus addons, all genuinely distinct** (no replicated library —
|
||
-- the one clean count in this area). Two idioms, and they need different things from us:
|
||
-- getglobal("ChatFrame"..i) Outfitter, _LazyPig — _LazyPig indexes the result
|
||
-- UNGUARDED (`ChatFrame:IsVisible()`), so it dies at i=3
|
||
-- unless ChatFrame3..7 are real frames. They are, below.
|
||
-- GetChatWindowInfo(i) EnhTooltip, MikScrollingBattleText, Enchantrix — the
|
||
-- engine getter, `benilla_ui::script::chat_window`.
|
||
NUM_CHAT_WINDOWS = 7
|
||
|
||
-- ── SlashCmdList — the addon slash-command registry (decision 1195) ────────────────────
|
||
-- Declared HERE, not in Rust, because the 1.12 client declares it in ChatFrame.lua and the
|
||
-- captured `_G` attributes it `table framexml`. An addon's registration is three lines at
|
||
-- FILE SCOPE:
|
||
-- SLASH_MYADDON1 = "/myaddon"
|
||
-- SlashCmdList["MYADDON"] = function(msg) ... end
|
||
-- so a missing table is not a missing feature, it is `attempt to index global
|
||
-- 'SlashCmdList'` on the addon's very first chunk — 26 of a 218-addon corpus died there.
|
||
-- The dispatch walk is `benilla_ui::script::slash`, transcribing ChatEdit_ParseText's.
|
||
--
|
||
-- It starts EMPTY: benilla's own commands are dispatched host-side (decision 0881's table)
|
||
-- where the reference registers them here. That divergence is recorded in `slash.rs`.
|
||
SlashCmdList = {}
|
||
|
||
-- ── ChatTypeGroup / OtherMenuChatTypeGroups — declared, and deliberately EMPTY ────────
|
||
-- The reference builds `ChatTypeGroup` at ChatFrame.lua l.115-348: 68 entries mapping a
|
||
-- chat TYPE to the list of `CHAT_MSG_*` events that belong to it, which its
|
||
-- `ChatFrame_RegisterForMessages` walks to register events on a chat window.
|
||
--
|
||
-- Declared here for the same reason `SlashCmdList` above is: an addon REGISTERS ITS OWN
|
||
-- chat type at file scope, and a missing table is not a missing feature — it is
|
||
-- `attempt to index global 'ChatTypeGroup'` on the addon's very first chunk. CT_RaidAssist
|
||
-- does exactly that three lines apart (`CT_RaidAssist.lua:59` assigns
|
||
-- `ChatTypeGroup["CTRAID"]`, `:63` tinserts into `OtherMenuChatTypeGroups`), and it dies
|
||
-- there — taking `CT_RA_OnEvent` with it, which is why FOUR MORE corpus addons
|
||
-- (CT_RABossMods, FixCTGroups, FuBar_CTRaid, SmartRes) die on that function being nil.
|
||
-- One missing table, five addons.
|
||
--
|
||
-- **EMPTY, not populated, and that is the honest shape rather than a shortcut.** Two
|
||
-- reasons, in order of weight:
|
||
-- · The 68 entries are 233 lines of the reference's own Lua. We RUN the reference's
|
||
-- files where we can (1234) and write our own where we cannot; what we do not do is
|
||
-- paste its source into a repo that mirrors publicly.
|
||
-- · Nothing would read them. Measured across the 218-addon corpus with `grep -a`:
|
||
-- CT_RaidAssist is the ONLY addon that touches either table, and it only WRITES. Not
|
||
-- one addon reads a group out.
|
||
--
|
||
-- The divergence that leaves, stated rather than discovered: our chat frames do not
|
||
-- register through this table at all — `ui_chat` routes host-side, the same split
|
||
-- `SlashCmdList` records above. So an addon that adds a group gets no routing from it.
|
||
-- That is the `ChatFrame_OnEvent` architecture question, not this table's.
|
||
ChatTypeGroup = {}
|
||
OtherMenuChatTypeGroups = {}
|
||
|
||
-- ── ChatTypeInfo — the per-chat-type table addons read colors out of ──────────────────
|
||
-- Declared HERE because the 1.12 client declares it at the top of ChatFrame.lua, and it
|
||
-- is an ADDON-FACING table before it is ours: `local info = ChatTypeInfo["SAY"]` then
|
||
-- `frame:AddMessage(text, info.r, info.g, info.b, info.id)` is the standard way an addon
|
||
-- prints a colored line.
|
||
--
|
||
-- Measured, and the raw count is misleading enough to be worth writing down: 234 corpus
|
||
-- sites, but **192 of them are one line** — AceEvent-2.0's
|
||
-- `ChatTypeInfo and ChatTypeInfo.WHISPER and ChatTypeInfo.WHISPER.r`, a GUARDED readiness
|
||
-- probe that copy-pastes into 64 addons and cannot raise. The reads that actually index
|
||
-- the table are 42 sites in 6 addons (ChatLog, oRA2/Participant's RaidWarn, Glory-2.0's
|
||
-- two FuBar hosts, FuBar_HeyFu) — those are `attempt to index global 'ChatTypeInfo'`
|
||
-- without it, and they are the reason this exists.
|
||
--
|
||
-- Three columns, three sources:
|
||
-- sticky the reference's own literals (ChatFrame.lua l.10-114) — the five sticky types
|
||
-- are SAY/PARTY/RAID/GUILD/BATTLEGROUND, which `ui_chat::edit` already quotes.
|
||
-- id GetChatTypeIndex's 1-based index into the engine's 94-entry color registry
|
||
-- (wow-re `chat-color-table.md`, static table 0x804710) — the ref sets it in
|
||
-- ChatFrame_OnLoad, and AddMessage's 5th argument is what consumes it.
|
||
-- r,g,b that registry's byte defaults, /255 — the engine hands Lua 0..1 floats
|
||
-- (it re-normalizes with the f32 1/255 at 0x8026c8).
|
||
--
|
||
-- ONE DIVERGENCE, and it is a shortcut through a two-step: the reference's OnLoad writes
|
||
-- 1.0/1.0/1.0 into every entry and waits for the engine's UPDATE_CHAT_COLOR storm to fill
|
||
-- the real values in (ChatFrame.lua l.1349 is the only writer in the whole default UI).
|
||
-- benilla has no chat-color settings surface and nothing that fires that event, so the
|
||
-- values are seeded at their END STATE directly. `ui_chat::event::default_color` is the
|
||
-- same wow-re table rendered host-side for our own feed, and
|
||
-- `chat_type_info_matches_the_host_color_table` fails if the two ever drift.
|
||
--
|
||
-- REPLY and COMBAT_ERROR are the two keys FrameXML declares that the engine's registry
|
||
-- does NOT contain, so GetChatTypeIndex answers 0 for both and no UPDATE_CHAT_COLOR is
|
||
-- ever fired for either name. They do NOT share a fate, and the first version of this
|
||
-- block got that wrong by generalising:
|
||
-- COMBAT_ERROR nothing writes it, ever — id 0, white, on the real client too.
|
||
-- REPLY the UPDATE_CHAT_COLOR handler MIRRORS WHISPER INTO IT by hand
|
||
-- (ChatFrame.lua l.1357-1365: `if strupper(arg1) == "WHISPER" then
|
||
-- info = ChatTypeInfo["REPLY"] ... end`). So its end state is WHISPER's
|
||
-- FF80FF, which is what the /r header shows, and seeding it white was a
|
||
-- bug the comment above it was busy justifying.
|
||
-- CHANNEL1..CHANNEL10 are the boot-seeded extras (indices 95-104), each colored from the
|
||
-- live CHANNEL entry — FFC0C0.
|
||
ChatTypeInfo = {}
|
||
local function chatType(name, sticky, id, r, g, b)
|
||
ChatTypeInfo[name] = { sticky = sticky, id = id, r = r / 255, g = g / 255, b = b / 255 }
|
||
end
|
||
chatType("SAY", 1, 1, 255, 255, 255)
|
||
chatType("PARTY", 1, 2, 170, 170, 255)
|
||
chatType("RAID", 1, 3, 255, 127, 0)
|
||
chatType("GUILD", 1, 4, 64, 255, 64)
|
||
chatType("OFFICER", 0, 5, 64, 192, 64)
|
||
chatType("YELL", 0, 6, 255, 64, 64)
|
||
chatType("WHISPER", 0, 7, 255, 128, 255)
|
||
chatType("WHISPER_INFORM", 0, 8, 255, 128, 255)
|
||
chatType("REPLY", 0, 0, 255, 128, 255)
|
||
chatType("EMOTE", 0, 9, 255, 128, 64)
|
||
chatType("TEXT_EMOTE", 0, 10, 255, 128, 64)
|
||
chatType("SYSTEM", 0, 11, 255, 255, 0)
|
||
chatType("MONSTER_WHISPER", 0, 27, 179, 179, 179)
|
||
chatType("MONSTER_SAY", 0, 12, 255, 255, 159)
|
||
chatType("MONSTER_YELL", 0, 13, 255, 64, 64)
|
||
chatType("MONSTER_EMOTE", 0, 14, 255, 128, 64)
|
||
chatType("CHANNEL", 0, 15, 255, 192, 192)
|
||
chatType("CHANNEL_JOIN", 0, 16, 192, 128, 128)
|
||
chatType("CHANNEL_LEAVE", 0, 17, 192, 128, 128)
|
||
chatType("CHANNEL_LIST", 0, 18, 192, 128, 128)
|
||
chatType("CHANNEL_NOTICE", 0, 19, 192, 192, 192)
|
||
chatType("CHANNEL_NOTICE_USER", 0, 20, 192, 192, 192)
|
||
chatType("AFK", 0, 21, 255, 128, 255)
|
||
chatType("DND", 0, 22, 255, 128, 255)
|
||
chatType("IGNORED", 0, 23, 255, 0, 0)
|
||
chatType("SKILL", 0, 24, 85, 85, 255)
|
||
chatType("LOOT", 0, 25, 0, 170, 0)
|
||
chatType("COMBAT_ERROR", 0, 0, 255, 255, 255)
|
||
chatType("COMBAT_MISC_INFO", 0, 26, 128, 128, 255)
|
||
chatType("CHANNEL1", 0, 95, 255, 192, 192)
|
||
chatType("CHANNEL2", 0, 96, 255, 192, 192)
|
||
chatType("CHANNEL3", 0, 97, 255, 192, 192)
|
||
chatType("CHANNEL4", 0, 98, 255, 192, 192)
|
||
chatType("CHANNEL5", 0, 99, 255, 192, 192)
|
||
chatType("CHANNEL6", 0, 100, 255, 192, 192)
|
||
chatType("CHANNEL7", 0, 101, 255, 192, 192)
|
||
chatType("CHANNEL8", 0, 102, 255, 192, 192)
|
||
chatType("CHANNEL9", 0, 103, 255, 192, 192)
|
||
chatType("CHANNEL10", 0, 104, 255, 192, 192)
|
||
chatType("COMBAT_SELF_HITS", 0, 28, 255, 255, 255)
|
||
chatType("COMBAT_SELF_MISSES", 0, 29, 255, 255, 255)
|
||
chatType("COMBAT_PET_HITS", 0, 30, 255, 255, 255)
|
||
chatType("COMBAT_PET_MISSES", 0, 31, 255, 255, 255)
|
||
chatType("COMBAT_PARTY_HITS", 0, 32, 255, 255, 255)
|
||
chatType("COMBAT_PARTY_MISSES", 0, 33, 255, 255, 255)
|
||
chatType("COMBAT_FRIENDLYPLAYER_HITS", 0, 34, 255, 255, 255)
|
||
chatType("COMBAT_FRIENDLYPLAYER_MISSES", 0, 35, 255, 255, 255)
|
||
chatType("COMBAT_HOSTILEPLAYER_HITS", 0, 36, 255, 255, 255)
|
||
chatType("COMBAT_HOSTILEPLAYER_MISSES", 0, 37, 255, 255, 255)
|
||
chatType("COMBAT_CREATURE_VS_SELF_HITS", 0, 38, 255, 47, 47)
|
||
chatType("COMBAT_CREATURE_VS_SELF_MISSES", 0, 39, 255, 47, 47)
|
||
chatType("COMBAT_CREATURE_VS_PARTY_HITS", 0, 40, 255, 255, 255)
|
||
chatType("COMBAT_CREATURE_VS_PARTY_MISSES", 0, 41, 255, 255, 255)
|
||
chatType("COMBAT_CREATURE_VS_CREATURE_HITS", 0, 42, 255, 255, 255)
|
||
chatType("COMBAT_CREATURE_VS_CREATURE_MISSES", 0, 43, 255, 255, 255)
|
||
chatType("COMBAT_FRIENDLY_DEATH", 0, 44, 255, 255, 255)
|
||
chatType("COMBAT_HOSTILE_DEATH", 0, 45, 255, 255, 255)
|
||
chatType("COMBAT_XP_GAIN", 0, 46, 111, 111, 255)
|
||
chatType("COMBAT_HONOR_GAIN", 0, 82, 224, 202, 10)
|
||
chatType("SPELL_SELF_DAMAGE", 0, 47, 255, 255, 0)
|
||
chatType("SPELL_SELF_BUFF", 0, 48, 255, 255, 0)
|
||
chatType("SPELL_PET_DAMAGE", 0, 49, 255, 255, 255)
|
||
chatType("SPELL_PET_BUFF", 0, 50, 255, 255, 255)
|
||
chatType("SPELL_PARTY_DAMAGE", 0, 51, 255, 255, 255)
|
||
chatType("SPELL_PARTY_BUFF", 0, 52, 255, 255, 255)
|
||
chatType("SPELL_FRIENDLYPLAYER_DAMAGE", 0, 53, 255, 255, 255)
|
||
chatType("SPELL_FRIENDLYPLAYER_BUFF", 0, 54, 255, 255, 255)
|
||
chatType("SPELL_HOSTILEPLAYER_DAMAGE", 0, 55, 255, 255, 255)
|
||
chatType("SPELL_HOSTILEPLAYER_BUFF", 0, 56, 255, 255, 255)
|
||
chatType("SPELL_CREATURE_VS_SELF_DAMAGE", 0, 57, 202, 76, 217)
|
||
chatType("SPELL_CREATURE_VS_SELF_BUFF", 0, 58, 255, 255, 255)
|
||
chatType("SPELL_CREATURE_VS_PARTY_DAMAGE", 0, 59, 255, 255, 255)
|
||
chatType("SPELL_CREATURE_VS_PARTY_BUFF", 0, 60, 255, 255, 255)
|
||
chatType("SPELL_CREATURE_VS_CREATURE_DAMAGE", 0, 61, 255, 255, 255)
|
||
chatType("SPELL_CREATURE_VS_CREATURE_BUFF", 0, 62, 255, 255, 255)
|
||
chatType("SPELL_TRADESKILLS", 0, 63, 255, 255, 255)
|
||
chatType("SPELL_DAMAGESHIELDS_ON_SELF", 0, 64, 255, 255, 255)
|
||
chatType("SPELL_DAMAGESHIELDS_ON_OTHERS", 0, 65, 255, 255, 255)
|
||
chatType("SPELL_AURA_GONE_SELF", 0, 66, 255, 255, 255)
|
||
chatType("SPELL_AURA_GONE_PARTY", 0, 67, 255, 255, 255)
|
||
chatType("SPELL_AURA_GONE_OTHER", 0, 68, 255, 255, 255)
|
||
chatType("SPELL_ITEM_ENCHANTMENTS", 0, 69, 255, 255, 255)
|
||
chatType("SPELL_BREAK_AURA", 0, 70, 255, 255, 255)
|
||
chatType("SPELL_PERIODIC_SELF_DAMAGE", 0, 71, 255, 255, 255)
|
||
chatType("SPELL_PERIODIC_SELF_BUFFS", 0, 72, 255, 255, 255)
|
||
chatType("SPELL_PERIODIC_PARTY_DAMAGE", 0, 73, 255, 255, 255)
|
||
chatType("SPELL_PERIODIC_PARTY_BUFFS", 0, 74, 255, 255, 255)
|
||
chatType("SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE", 0, 75, 255, 255, 255)
|
||
chatType("SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS", 0, 76, 255, 255, 255)
|
||
chatType("SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE", 0, 77, 255, 255, 255)
|
||
chatType("SPELL_PERIODIC_HOSTILEPLAYER_BUFFS", 0, 78, 255, 255, 255)
|
||
chatType("SPELL_PERIODIC_CREATURE_DAMAGE", 0, 79, 255, 255, 255)
|
||
chatType("SPELL_PERIODIC_CREATURE_BUFFS", 0, 80, 255, 255, 255)
|
||
chatType("SPELL_FAILED_LOCALPLAYER", 0, 81, 255, 255, 255)
|
||
chatType("BG_SYSTEM_NEUTRAL", 0, 83, 255, 120, 10)
|
||
chatType("BG_SYSTEM_ALLIANCE", 0, 84, 0, 174, 239)
|
||
chatType("BG_SYSTEM_HORDE", 0, 85, 255, 0, 0)
|
||
chatType("COMBAT_FACTION_CHANGE", 0, 86, 128, 128, 255)
|
||
chatType("MONEY", 0, 87, 255, 255, 0)
|
||
chatType("RAID_LEADER", 0, 88, 255, 219, 183)
|
||
chatType("RAID_WARNING", 0, 89, 255, 219, 183)
|
||
chatType("RAID_BOSS_EMOTE", 0, 91, 255, 219, 183)
|
||
chatType("FILTERED", 0, 92, 255, 0, 0)
|
||
chatType("BATTLEGROUND", 1, 93, 255, 127, 0)
|
||
chatType("BATTLEGROUND_LEADER", 0, 94, 255, 219, 183)
|
||
|
||
-- ── BenillaFCF: the FloatingChatFrame behaviors, re-expressed (ref FloatingChatFrame.lua)
|
||
-- Quoted constants: CHAT_TAB_SHOW_DELAY=0.2, CHAT_FRAME_FADE_TIME=0.15,
|
||
-- DEFAULT_CHATFRAME_ALPHA=0.25, CHAT_BUTTON_FLASH_TIME=0.5,
|
||
-- MESSAGE_SCROLLBUTTON_INITIAL_DELAY=0, MESSAGE_SCROLLBUTTON_SCROLL_DELAY=0.05.
|
||
BenillaFCF = {
|
||
selected = 1, -- SELECTED_DOCK_FRAME (1 = General, 2 = Combat Log)
|
||
hover = false, -- cursor inside the dock's padded rect
|
||
hoverTime = 0, -- stationary-cursor accumulator (ref: moving resets it)
|
||
oldX = 0, oldY = 0,
|
||
reveal = 0, -- 0..1 master fade the tab/texture alphas scale by
|
||
revealTarget = 0,
|
||
flashTab = 0, -- tab id whose whisper flash runs (0 = none), 0288 P4 wires it
|
||
flashLeft = 0, -- remaining flash seconds (ref: 60s, blink 0.25/0.25)
|
||
}
|
||
|
||
-- The per-window texture sets the hover fade drives (ref CHAT_FRAME_TEXTURES: the
|
||
-- background + the 8 border pieces; benilla authors the borders as plain textures —
|
||
-- resizing is settings, the hover LOOK is not). Global: the load-time tint below needs it.
|
||
function BenillaFCF_Textures(id)
|
||
local n = "ChatFrame" .. id
|
||
local list = {}
|
||
for _, suffix in ipairs({
|
||
"Background",
|
||
"BorderTopLeft", "BorderTopRight", "BorderBottomLeft", "BorderBottomRight",
|
||
"BorderTop", "BorderBottom", "BorderLeft", "BorderRight",
|
||
}) do
|
||
local t = getglobal(n .. suffix)
|
||
if t then table.insert(list, t) end
|
||
end
|
||
return list
|
||
end
|
||
|
||
-- FCF_SelectDockFrame: show the selected frame, hide the other; selected tab full alpha,
|
||
-- unselected half (both still scaled by the reveal fade below). Stops the selected tab's
|
||
-- whisper flash (ref FCF_SelectDockFrame → UIFrameFlashRemoveFrame). Tab getglobals are
|
||
-- guarded: window 2's tab is deliberately unbuilt (see the note at the tab section).
|
||
function BenillaFCF_SelectDock(id)
|
||
BenillaFCF.selected = id
|
||
for i = 1, 2 do
|
||
local frame = getglobal("ChatFrame" .. i)
|
||
if i == id then frame:Show() else frame:Hide() end
|
||
end
|
||
if BenillaFCF.flashTab == id then
|
||
BenillaFCF.flashTab = 0
|
||
local flash = getglobal("ChatFrame" .. id .. "TabFlash")
|
||
if flash then flash:Hide() end
|
||
end
|
||
end
|
||
|
||
-- FCF_SelectDockFrame(frame) — the reference's own name for the operation above, taking a
|
||
-- FRAME where ours takes a dock id. Both corpus callers pass DEFAULT_CHAT_FRAME:
|
||
--
|
||
-- if not DEFAULT_CHAT_FRAME:IsVisible() then
|
||
-- FCF_SelectDockFrame(DEFAULT_CHAT_FRAME)
|
||
-- end
|
||
-- DEFAULT_CHAT_FRAME:AddMessage(out)
|
||
--
|
||
-- verbatim in both CustomNameplates (`CustomNameplates.lua:69`) and Roid-Macros
|
||
-- (`Utility.lua:40`) — "un-hide the chat window before printing into it". Two addons,
|
||
-- two independent files, not a replicated library.
|
||
--
|
||
-- Ours does the dock half and NOT the rest of ref FCF_SelectDockFrame l.1204-1217: the ref
|
||
-- also assigns `SELECTED_DOCK_FRAME` (a global no corpus addon reads — 0 hits) and calls
|
||
-- `FCF_DockUpdate()` (tab geometry, which FCF_OnUpdate below re-derives every frame).
|
||
--
|
||
-- An UNDOCKED frame RAISES rather than quietly doing nothing. ChatFrame3..7 exist as real
|
||
-- frames but benilla has no undock/dock model at all (0288 §2 — window management is chat
|
||
-- settings, deliberately out), so "select ChatFrame5" has no meaning here; answering it
|
||
-- with silence would leave the caller's next AddMessage landing in a window that is still
|
||
-- hidden, which is the bug the guard was written to prevent.
|
||
function FCF_SelectDockFrame(frame)
|
||
if not frame then
|
||
error("FCF_SelectDockFrame: nil frame", 2)
|
||
end
|
||
local id = frame:GetID()
|
||
if id ~= 1 and id ~= 2 then
|
||
error("FCF_SelectDockFrame: " .. (frame:GetName() or "?")
|
||
.. " is not docked — benilla docks only ChatFrame1/ChatFrame2 (decision 0288 §2)", 2)
|
||
end
|
||
BenillaFCF_SelectDock(id)
|
||
end
|
||
|
||
function BenillaFCF_TabClick(id)
|
||
-- ref FCF_Tab_OnClick (docked, left click): select; UChatScrollButton per click.
|
||
BenillaFCF_SelectDock(id)
|
||
PlaySound("UChatScrollButton")
|
||
end
|
||
|
||
-- The whisper tab flash (ref FCF_FlashTab: never the selected tab; 0.25s on/off for 60s,
|
||
-- and a flashing tab holds the dock tabs visible). Fired by the app on a received
|
||
-- whisper landing in an unselected window (0288 P4). No-tab windows can't flash.
|
||
function BenillaFCF_FlashTab(id)
|
||
if id == BenillaFCF.selected then return end
|
||
if not getglobal("ChatFrame" .. id .. "TabFlash") then return end
|
||
BenillaFCF.flashTab = id
|
||
BenillaFCF.flashLeft = 60
|
||
end
|
||
|
||
-- PanelTemplates_TabResize(5, tab), re-expressed: the ref sizes every docked tab to its
|
||
-- label (FCF_DockUpdate) — middle slice = text width + 5, tab = middle + the two 16px
|
||
-- caps; the flash/highlight bands follow the Left/Right pieces by anchor. The label
|
||
-- width is host-measured (GetStringWidth), so early frames may see 0 — the OnUpdate
|
||
-- below re-applies until it settles, then the change-guard makes this a no-op.
|
||
function BenillaFCF_TabResize(tab)
|
||
local w = getglobal(tab:GetName() .. "Text"):GetStringWidth()
|
||
if w == 0 or tab.benillaTabTextW == w then return end
|
||
tab.benillaTabTextW = w
|
||
getglobal(tab:GetName() .. "Middle"):SetWidth(w + 5)
|
||
tab:SetWidth(w + 5 + 32)
|
||
end
|
||
|
||
-- The hover rect: the dock's rect expanded by the ref's MouseIsOver pads
|
||
-- (FCF_OnUpdate: MouseIsOver(chatFrame, 45, -10, -5, 5) — 45 up into the tab zone,
|
||
-- 10 down past the edit box seam, 5 out each side).
|
||
local function overDock(x, y)
|
||
local f = ChatFrame1
|
||
local left, right = f:GetLeft(), f:GetRight()
|
||
local top, bottom = f:GetTop(), f:GetBottom()
|
||
if not left then return false end
|
||
return x >= left - 5 and x <= right + 5 and y >= bottom - 10 and y <= top + 45
|
||
end
|
||
|
||
-- FCF_OnUpdate transcription (the parts that survive with window management out):
|
||
-- reveal after a 0.2s STATIONARY hover (a moving cursor keeps resetting the delay),
|
||
-- fade over 0.15s, conceal on leave; a flashing tab forces the tabs visible; the
|
||
-- selected window's textures ride the same fade toward alpha 0.25 (the chat-cache
|
||
-- default window alpha is 0 — the box only exists on hover).
|
||
function FCF_OnUpdate(elapsed)
|
||
local s = BenillaFCF
|
||
local x, y = GetCursorPosition()
|
||
if overDock(x, y) then
|
||
if not s.hover then
|
||
s.hover = true
|
||
s.hoverTime = 0
|
||
s.oldX, s.oldY = x, y
|
||
elseif s.oldX == x and s.oldY == y then
|
||
s.hoverTime = s.hoverTime + elapsed
|
||
else
|
||
s.hoverTime = 0
|
||
s.oldX, s.oldY = x, y
|
||
end
|
||
if s.hoverTime > 0.2 then s.revealTarget = 1 end
|
||
else
|
||
s.hover = false
|
||
s.hoverTime = 0
|
||
s.revealTarget = 0
|
||
end
|
||
if s.flashTab ~= 0 then s.revealTarget = 1 end
|
||
|
||
-- The 0.15s linear fade toward the target (ref UIFrameFadeIn/Out over
|
||
-- CHAT_FRAME_FADE_TIME).
|
||
if s.reveal < s.revealTarget then
|
||
s.reveal = math.min(s.revealTarget, s.reveal + elapsed / 0.15)
|
||
elseif s.reveal > s.revealTarget then
|
||
s.reveal = math.max(s.revealTarget, s.reveal - elapsed / 0.15)
|
||
end
|
||
|
||
-- Apply: tabs at 1.0 (selected) / 0.5 (unselected) × reveal; the selected window's
|
||
-- background+borders at 0.25 × reveal (ref: fade oldAlpha↔DEFAULT_CHATFRAME_ALPHA).
|
||
for i = 1, 2 do
|
||
local tab = getglobal("ChatFrame" .. i .. "Tab")
|
||
if tab then
|
||
local a = (i == BenillaFCF.selected) and 1.0 or 0.5
|
||
tab:SetAlpha(a * s.reveal)
|
||
BenillaFCF_TabResize(tab)
|
||
end
|
||
for _, t in ipairs(BenillaFCF_Textures(i)) do
|
||
t:SetAlpha(0.25 * s.reveal)
|
||
end
|
||
end
|
||
|
||
-- The whisper flash blink (0.25 on / 0.25 off, 60s — ref UIFrameFlash args).
|
||
if s.flashTab ~= 0 then
|
||
s.flashLeft = s.flashLeft - elapsed
|
||
local flash = getglobal("ChatFrame" .. s.flashTab .. "TabFlash")
|
||
if s.flashLeft <= 0 then
|
||
s.flashTab = 0
|
||
flash:Hide()
|
||
else
|
||
local phase = mod(s.flashLeft, 0.5)
|
||
if phase > 0.25 then flash:Show() else flash:Hide() end
|
||
end
|
||
end
|
||
end
|
||
|
||
-- ChatFrame_OnUpdate transcription: the bottom button's blink while scrolled up
|
||
-- (CHAT_BUTTON_FLASH_TIME = 0.5s toggle; hidden the moment the view is AtBottom).
|
||
function ChatFrame_OnUpdate(elapsed)
|
||
local frame = this
|
||
local flash = getglobal(frame:GetName() .. "BottomButtonFlash")
|
||
if not flash then return end
|
||
if frame:AtBottom() then
|
||
flash:Hide()
|
||
frame.flashTimer = 0
|
||
return
|
||
end
|
||
frame.flashTimer = (frame.flashTimer or 0) + elapsed
|
||
while frame.flashTimer >= 0.5 do
|
||
frame.flashTimer = frame.flashTimer - 0.5
|
||
if flash:IsVisible() then flash:Hide() else flash:Show() end
|
||
end
|
||
end
|
||
|
||
-- A completed left click on the chat body dismisses a held SPELL/ACTION payload
|
||
-- (decision 0843, the director's call — no ref counterpart: the reference keeps a
|
||
-- non-item payload on any frame click). Items are deliberately excluded: an item
|
||
-- dismissal is a destroy, which only the world-drop popup flow may offer.
|
||
function BenillaChatFrame_DismissCursor(button)
|
||
if button ~= "LeftButton" then return end
|
||
local kind = GetCursorInfo()
|
||
if kind == "spell" or kind == "action" then
|
||
ClearCursor()
|
||
end
|
||
end
|
||
|
||
-- MessageFrameScrollButton_OnLoad/_OnUpdate + the ref OnClick pair: a press arms the
|
||
-- hold-repeat (initial delay 0), each 0.05s while held scrolls WITHOUT the sound; the
|
||
-- release click scrolls once WITH the sound (ref FloatingChatFrame.xml's DownButton/
|
||
-- UpButton scripts — sound only on the non-pushed branch).
|
||
function BenillaChatScrollButton_OnClick(btn, down, dir, sound)
|
||
if down then
|
||
btn.clickDelay = 0
|
||
else
|
||
PlaySound(sound)
|
||
local frame = getglobal("ChatFrame" .. BenillaFCF.selected)
|
||
if dir == "up" then frame:ScrollUp() else frame:ScrollDown() end
|
||
end
|
||
end
|
||
|
||
function BenillaChatScrollButton_OnUpdate(btn, elapsed, dir)
|
||
if btn:GetButtonState() == "PUSHED" then
|
||
btn.clickDelay = (btn.clickDelay or 0) - elapsed
|
||
if btn.clickDelay < 0 then
|
||
local frame = getglobal("ChatFrame" .. BenillaFCF.selected)
|
||
if dir == "up" then frame:ScrollUp() else frame:ScrollDown() end
|
||
btn.clickDelay = 0.05
|
||
end
|
||
end
|
||
end
|
||
|
||
-- ── BenillaUIMenu: the 1.12 UIMenu kit, re-expressed (ref UIMenu.lua — the chat menu's
|
||
-- row engine: fixed 16px rows in a tooltip-backdrop box, a 2 s no-hover timeout that
|
||
-- walks up parentMenu chains, nested submenus opened on row hover). Constants quoted.
|
||
-- Scoped here because the chat menu is its only consumer; a second consumer graduates
|
||
-- it to its own file.
|
||
BENILLA_UIMENU_BUTTON_HEIGHT = 16
|
||
BENILLA_UIMENU_BUTTON_WIDTH = 104
|
||
BENILLA_UIMENU_BORDER_HEIGHT = 12
|
||
BENILLA_UIMENU_BORDER_WIDTH = 12
|
||
BENILLA_UIMENU_NUMBUTTONS = 32
|
||
BENILLA_UIMENU_TIMEOUT = 2.0
|
||
|
||
function UIMenu_Initialize()
|
||
local menu = this
|
||
menu.numButtons = 0
|
||
menu.subMenu = ""
|
||
local name = menu:GetName()
|
||
for i = 1, BENILLA_UIMENU_NUMBUTTONS do
|
||
local button = getglobal(name .. "Button" .. i)
|
||
button:SetWidth(BENILLA_UIMENU_BUTTON_WIDTH)
|
||
button:SetHeight(BENILLA_UIMENU_BUTTON_HEIGHT)
|
||
button:Hide()
|
||
getglobal(button:GetName() .. "ShortcutText"):Hide()
|
||
end
|
||
menu:SetWidth(BENILLA_UIMENU_BUTTON_WIDTH + BENILLA_UIMENU_BORDER_WIDTH * 2)
|
||
menu:SetHeight(BENILLA_UIMENU_BORDER_HEIGHT * 2)
|
||
end
|
||
|
||
-- Rows past the button pool are dropped (the ref message()s; nothing we author overflows).
|
||
function UIMenu_AddButton(text, shortcut, func, nested)
|
||
local menu = this
|
||
local id = menu.numButtons + 1
|
||
if id > BENILLA_UIMENU_NUMBUTTONS then return end
|
||
menu.numButtons = id
|
||
local button = getglobal(menu:GetName() .. "Button" .. id)
|
||
if text then button:SetText(text) end
|
||
button.func = func
|
||
button.nested = nested
|
||
button:Show()
|
||
if shortcut then
|
||
local s = getglobal(button:GetName() .. "ShortcutText")
|
||
s:SetText(shortcut)
|
||
s:Show()
|
||
end
|
||
menu:SetHeight(id * BENILLA_UIMENU_BUTTON_HEIGHT + BENILLA_UIMENU_BORDER_HEIGHT * 2)
|
||
end
|
||
|
||
function UIMenu_OnShow()
|
||
this.timeleft = BENILLA_UIMENU_TIMEOUT
|
||
this.counting = 1
|
||
end
|
||
|
||
function UIMenu_OnUpdate(elapsed)
|
||
if this.counting == 1 then
|
||
local timeleft = (this.timeleft or BENILLA_UIMENU_TIMEOUT) - elapsed
|
||
if timeleft <= 0 then
|
||
this:Hide()
|
||
return
|
||
end
|
||
this.timeleft = timeleft
|
||
end
|
||
end
|
||
|
||
function UIMenu_StartCounting(menu)
|
||
menu.counting = 1
|
||
if menu.parentMenu then UIMenu_StartCounting(getglobal(menu.parentMenu)) end
|
||
end
|
||
|
||
function UIMenu_StopCounting(menu)
|
||
menu.counting = 0
|
||
menu.timeleft = BENILLA_UIMENU_TIMEOUT
|
||
if menu.parentMenu then UIMenu_StopCounting(getglobal(menu.parentMenu)) end
|
||
end
|
||
|
||
function UIMenuButton_OnLoad()
|
||
this:SetPoint("TOP", this:GetParent():GetName(), "TOP", 0,
|
||
-((this:GetID() - 1) * BENILLA_UIMENU_BUTTON_HEIGHT) - BENILLA_UIMENU_BORDER_HEIGHT)
|
||
end
|
||
|
||
function UIMenuButton_OnClick()
|
||
if this.func then this.func() end
|
||
this:GetParent():Hide()
|
||
PlaySound("UChatScrollButton")
|
||
end
|
||
|
||
function UIMenuButton_OnEnter()
|
||
local nested = this.nested
|
||
if nested then
|
||
local menu = getglobal(nested)
|
||
if not menu:IsVisible() then
|
||
local oldMenu = getglobal(this:GetParent().subMenu or "")
|
||
if oldMenu then oldMenu:Hide() end
|
||
this:GetParent().subMenu = nested
|
||
menu:SetPoint("BOTTOMLEFT", this:GetName(), "BOTTOMRIGHT", 10, -12)
|
||
menu:Show()
|
||
end
|
||
UIMenu_StopCounting(menu)
|
||
else
|
||
UIMenu_StopCounting(this:GetParent())
|
||
end
|
||
end
|
||
|
||
function UIMenuButton_OnLeave()
|
||
UIMenu_StartCounting(this:GetParent())
|
||
if this.nested then UIMenu_StartCounting(getglobal(this.nested)) end
|
||
end
|
||
|
||
-- ── The chat menu rows (ref ChatMenu_* in ChatFrame.lua, adapted): every type row
|
||
-- prefixes the box with the type's slash and lets the LIVE PARSE convert — the ref's
|
||
-- own Whisper row does literally `SetText("/w "..GetText())`, and routing all rows the
|
||
-- same way means the menu can never drift from the slash grammar the edit machine
|
||
-- owns. Labels/shortcuts quoted from GlobalStrings. Language (benilla has no language
|
||
-- model yet) and Macro (no macro frame) are deliberately out; this OnLoad is their
|
||
-- return path.
|
||
function BenillaChatMenu_Type(prefix)
|
||
local eb = ChatFrameEditBox
|
||
eb:Show()
|
||
eb:SetFocus()
|
||
eb:SetText(prefix .. (eb:GetText() or ""))
|
||
end
|
||
|
||
function ChatMenu_OnLoad()
|
||
UIMenu_Initialize()
|
||
UIMenu_AddButton("Say", "/s", function() BenillaChatMenu_Type("/s ") end)
|
||
UIMenu_AddButton("Party Chat", "/p", function() BenillaChatMenu_Type("/p ") end)
|
||
UIMenu_AddButton("Guild Chat", "/g", function() BenillaChatMenu_Type("/g ") end)
|
||
UIMenu_AddButton("Yell", "/y", function() BenillaChatMenu_Type("/y ") end)
|
||
UIMenu_AddButton("Whisper", "/w", function() BenillaChatMenu_Type("/w ") end)
|
||
UIMenu_AddButton("Emote", "/e", function() BenillaChatMenu_Type("/e ") end, "EmoteMenu")
|
||
UIMenu_AddButton("Reply", "/r", function() BenillaChatMenu_Type("/r ") end)
|
||
UIMenu_AddButton("Voice Emote", nil, nil, "VoiceMacroMenu")
|
||
end
|
||
|
||
-- An emote row submits its /command through the REAL chat pipeline (SubmitChatInput →
|
||
-- the router's EmotesText resolve + the send-side posture gate) — the ref's DoEmote by
|
||
-- token, reached through our one grammar. The lists are the ref's EmoteList /
|
||
-- TextEmoteSpeechList tokens as OUR canonical /commands (the EmotesText names the typed
|
||
-- grammar resolves), CMD1-sorted like the ref menu. Three ref labels differ
|
||
-- (/attacktarget /followme /silly are GlobalStrings CMD aliases the typed grammar
|
||
-- doesn't carry yet) — each row shows the command it actually runs.
|
||
BenillaEmoteMenuList = {
|
||
"/applaud", "/beg", "/bow", "/chicken", "/cry", "/dance", "/eat", "/flex",
|
||
"/kiss", "/kneel", "/laugh", "/point", "/roar", "/rude", "/salute", "/shy",
|
||
"/sit", "/sleep", "/stand", "/talk", "/wave",
|
||
}
|
||
BenillaVoiceMacroList = {
|
||
"/attackmytarget", "/bye", "/charge", "/cheer", "/congratulate", "/flee",
|
||
"/flirt", "/follow", "/healme", "/hello", "/helpme", "/incoming", "/joke",
|
||
"/no", "/nod", "/oom", "/openfire", "/rasp", "/thank", "/train", "/wait",
|
||
"/welcome",
|
||
}
|
||
|
||
function BenillaEmoteMenu_Click(cmd)
|
||
SubmitChatInput(cmd)
|
||
ChatMenu:Hide()
|
||
end
|
||
|
||
function BenillaOnEmoteMenuLoad(list)
|
||
UIMenu_Initialize()
|
||
this.parentMenu = "ChatMenu"
|
||
for _, cmd in ipairs(list) do
|
||
local c = cmd
|
||
UIMenu_AddButton(cmd, nil, function() BenillaEmoteMenu_Click(c) end)
|
||
end
|
||
end
|
||
]]></Script>
|
||
|
||
<!-- ── The dock: ChatFrame1 (General) + ChatFrame2 (Combat Log) sharing its rect ─────── -->
|
||
<!--
|
||
ChatFrame1: 430×120 at BOTTOMLEFT (32, 85) — the managed-position base
|
||
(UIPARENT_MANAGED_FRAME_POSITIONS["ChatFrame1"]: baseY 85 + the bottom-stack offsets,
|
||
applied by UIParent_ManageFramePositions per decision 0272); displayDuration 120 /
|
||
maxLines 128 (ref ChatFrameTemplate). The background stretches -2/+2 x and +3/-6 y past
|
||
the frame (ref $parentBackground anchors), the 8 border slices ring it (the ref draws
|
||
them through its resize buttons' textures; benilla authors them as plain textures — the
|
||
hover LOOK without the resize mechanics). Everything starts alpha 0; BenillaFCF fades.
|
||
-->
|
||
<ScrollingMessageFrame name="ChatFrame1" frameStrata="BACKGROUND" displayDuration="120.0" maxLines="128" id="1">
|
||
<Size><AbsDimension x="430" y="120"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOMLEFT" relativePoint="BOTTOMLEFT">
|
||
<Offset><AbsDimension x="32" y="85"/></Offset>
|
||
</Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentBackground" file="Interface\ChatFrame\ChatFrameBackground" alpha="0">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="-2" y="3"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="2" y="-6"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Texture>
|
||
<!-- The ChatFrameBorder ring (ref ChatFrameBorder slices; texcoords quoted from
|
||
the ref resize buttons). Corners 16×16, edges stretched between them. -->
|
||
<Texture name="$parentBorderTopLeft" file="Interface\ChatFrame\ChatFrameBorder" alpha="0">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentBackground"><Offset><AbsDimension x="-2" y="2"/></Offset></Anchor></Anchors>
|
||
<TexCoords left="0" right="0.25" top="0" bottom="0.125"/>
|
||
</Texture>
|
||
<Texture name="$parentBorderTopRight" file="Interface\ChatFrame\ChatFrameBorder" alpha="0">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors><Anchor point="TOPRIGHT" relativeTo="$parentBackground"><Offset><AbsDimension x="2" y="2"/></Offset></Anchor></Anchors>
|
||
<TexCoords left="0.75" right="1.0" top="0" bottom="0.125"/>
|
||
</Texture>
|
||
<Texture name="$parentBorderBottomLeft" file="Interface\ChatFrame\ChatFrameBorder" alpha="0">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors><Anchor point="BOTTOMLEFT" relativeTo="$parentBackground"><Offset><AbsDimension x="-2" y="-3"/></Offset></Anchor></Anchors>
|
||
<TexCoords left="0" right="0.25" top="0.7265625" bottom="0.8515625"/>
|
||
</Texture>
|
||
<Texture name="$parentBorderBottomRight" file="Interface\ChatFrame\ChatFrameBorder" alpha="0">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors><Anchor point="BOTTOMRIGHT" relativeTo="$parentBackground"><Offset><AbsDimension x="2" y="-3"/></Offset></Anchor></Anchors>
|
||
<TexCoords left="0.75" right="1.0" top="0.7265625" bottom="0.8515625"/>
|
||
</Texture>
|
||
<Texture name="$parentBorderTop" file="Interface\ChatFrame\ChatFrameBorder" alpha="0">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativeTo="$parentBorderTopLeft" relativePoint="RIGHT"/>
|
||
<Anchor point="RIGHT" relativeTo="$parentBorderTopRight" relativePoint="LEFT"/>
|
||
</Anchors>
|
||
<TexCoords left="0.25" right="0.75" top="0" bottom="0.125"/>
|
||
</Texture>
|
||
<Texture name="$parentBorderBottom" file="Interface\ChatFrame\ChatFrameBorder" alpha="0">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativeTo="$parentBorderBottomLeft" relativePoint="RIGHT"/>
|
||
<Anchor point="RIGHT" relativeTo="$parentBorderBottomRight" relativePoint="LEFT"/>
|
||
</Anchors>
|
||
<TexCoords left="0.25" right="0.75" top="0.7265625" bottom="0.8515625"/>
|
||
</Texture>
|
||
<Texture name="$parentBorderLeft" file="Interface\ChatFrame\ChatFrameBorder" alpha="0">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOP" relativeTo="$parentBorderTopLeft" relativePoint="BOTTOM"/>
|
||
<Anchor point="BOTTOM" relativeTo="$parentBorderBottomLeft" relativePoint="TOP"/>
|
||
</Anchors>
|
||
<TexCoords left="0" right="0.25" top="0.125" bottom="0.7265625"/>
|
||
</Texture>
|
||
<Texture name="$parentBorderRight" file="Interface\ChatFrame\ChatFrameBorder" alpha="0">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOP" relativeTo="$parentBorderTopRight" relativePoint="BOTTOM"/>
|
||
<Anchor point="BOTTOM" relativeTo="$parentBorderBottomRight" relativePoint="TOP"/>
|
||
</Anchors>
|
||
<TexCoords left="0.75" right="1.0" top="0.125" bottom="0.7265625"/>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- The scroll column, outside bottom-left (ref: BottomButton at (-32,-4), Down and
|
||
Up stacked above it, 2px overlaps). Sounds + hold-repeat per the ref scripts. -->
|
||
<Button name="$parentBottomButton">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOMLEFT" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="-32" y="-4"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="OVERLAY">
|
||
<Texture name="$parentFlash" file="Interface\ChatFrame\UI-ChatIcon-BlinkHilight" hidden="true" setAllPoints="true"/>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnClick>
|
||
PlaySound("igChatBottom")
|
||
getglobal("ChatFrame" .. BenillaFCF.selected):ScrollToBottom()
|
||
</OnClick>
|
||
</Scripts>
|
||
<NormalTexture file="Interface\ChatFrame\UI-ChatIcon-ScrollEnd-Up"/>
|
||
<PushedTexture file="Interface\ChatFrame\UI-ChatIcon-ScrollEnd-Down"/>
|
||
<DisabledTexture file="Interface\ChatFrame\UI-ChatIcon-ScrollEnd-Disabled"/>
|
||
<HighlightTexture file="Interface\Buttons\UI-Common-MouseHilight" alphaMode="ADD"/>
|
||
</Button>
|
||
<Button name="$parentDownButton">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOM" relativeTo="$parentBottomButton" relativePoint="TOP"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts>
|
||
<OnLoad>
|
||
<!-- All four transitions, ref-verbatim (MessageFrameScrollButton_OnLoad,
|
||
ChatFrame.lua:1583): the OnClick body reads no button, so a right-click
|
||
scrolls exactly like a left one, and the DOWN halves are what arm the
|
||
hold-repeat. -->
|
||
this:RegisterForClicks("LeftButtonDown", "LeftButtonUp", "RightButtonUp", "RightButtonDown")
|
||
</OnLoad>
|
||
<OnClick>
|
||
BenillaChatScrollButton_OnClick(this, arg2, "down", "igChatScrollDown")
|
||
</OnClick>
|
||
<OnUpdate>
|
||
BenillaChatScrollButton_OnUpdate(this, arg1, "down")
|
||
</OnUpdate>
|
||
</Scripts>
|
||
<NormalTexture file="Interface\ChatFrame\UI-ChatIcon-ScrollDown-Up"/>
|
||
<PushedTexture file="Interface\ChatFrame\UI-ChatIcon-ScrollDown-Down"/>
|
||
<DisabledTexture file="Interface\ChatFrame\UI-ChatIcon-ScrollDown-Disabled"/>
|
||
<HighlightTexture file="Interface\Buttons\UI-Common-MouseHilight" alphaMode="ADD"/>
|
||
</Button>
|
||
<Button name="$parentUpButton">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOM" relativeTo="$parentDownButton" relativePoint="TOP"/>
|
||
</Anchors>
|
||
<Scripts>
|
||
<OnLoad>
|
||
<!-- All four, ref-verbatim — see the down button's note. -->
|
||
this:RegisterForClicks("LeftButtonDown", "LeftButtonUp", "RightButtonUp", "RightButtonDown")
|
||
</OnLoad>
|
||
<OnClick>
|
||
BenillaChatScrollButton_OnClick(this, arg2, "up", "igChatScrollUp")
|
||
</OnClick>
|
||
<OnUpdate>
|
||
BenillaChatScrollButton_OnUpdate(this, arg1, "up")
|
||
</OnUpdate>
|
||
</Scripts>
|
||
<NormalTexture file="Interface\ChatFrame\UI-ChatIcon-ScrollUp-Up"/>
|
||
<PushedTexture file="Interface\ChatFrame\UI-ChatIcon-ScrollUp-Down"/>
|
||
<DisabledTexture file="Interface\ChatFrame\UI-ChatIcon-ScrollUp-Disabled"/>
|
||
<HighlightTexture file="Interface\Buttons\UI-Common-MouseHilight" alphaMode="ADD"/>
|
||
</Button>
|
||
</Frames>
|
||
<Scripts>
|
||
<!-- EXECUTE_CHAT_LINE — how a MACRO line reaches the chat grammar (0996, wow-re
|
||
`macro-execution-law.md` §4, VERIFIED). The engine's macro runner (`0x4f14e0`)
|
||
names no Lua function and walks no command table: per body line it fires
|
||
`FrameScript_SignalEvent(0x188, "%s", line)`, and `0x188` is EXECUTE_CHAT_LINE
|
||
(the registry slot `0xbe17b8` is written once, at `0x51b4ff`, with `0x852470`).
|
||
ChatFrame.lua:1343 is the handler — `editBox:SetText(arg1)` → `ChatEdit_SendText`
|
||
→ `ChatEdit_OnEscapePressed` — and ChatFrame1's OnLoad
|
||
(FloatingChatFrame.xml:700) is its ONLY registrant in the whole default UI, so
|
||
every macro line runs through THIS frame's box whichever one has focus. benilla's
|
||
SubmitChatInput stands in for ChatEdit_SendText: the grammar behind it is Rust
|
||
(0881), which is the only part of that chain we replace. -->
|
||
<OnLoad>
|
||
this:RegisterEvent("EXECUTE_CHAT_LINE")
|
||
</OnLoad>
|
||
<OnEvent>
|
||
if event == "EXECUTE_CHAT_LINE" then SubmitChatInput(arg1) end
|
||
</OnEvent>
|
||
<OnUpdate>
|
||
FCF_OnUpdate(arg1)
|
||
ChatFrame_OnUpdate(arg1)
|
||
</OnUpdate>
|
||
<OnMouseWheel>
|
||
-- WoW wheel convention: +1 per notch up, -1 down. Up = older messages.
|
||
if arg1 > 0 then this:ScrollUp() else this:ScrollDown() end
|
||
</OnMouseWheel>
|
||
<OnClick>
|
||
BenillaChatFrame_DismissCursor(arg1)
|
||
</OnClick>
|
||
<OnHyperlinkClick>
|
||
SetItemRef(arg1, arg2, arg3)
|
||
</OnHyperlinkClick>
|
||
</Scripts>
|
||
<FontString inherits="ChatFontNormal" justifyH="LEFT"/>
|
||
</ScrollingMessageFrame>
|
||
|
||
<!-- ChatFrame2 "Combat Log": docked — shares ChatFrame1's rect (ref FCF_DockUpdate anchors),
|
||
hidden until its tab selects it. Its message-group wiring (the chat-cache combat set)
|
||
lands with the router (0288 P4); until then it renders what it's given: nothing. -->
|
||
<ScrollingMessageFrame name="ChatFrame2" frameStrata="BACKGROUND" displayDuration="120.0" maxLines="128" hidden="true" id="2">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="ChatFrame1" relativePoint="TOPLEFT"/>
|
||
<Anchor point="BOTTOMRIGHT" relativeTo="ChatFrame1" relativePoint="BOTTOMRIGHT"/>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentBackground" file="Interface\ChatFrame\ChatFrameBackground" alpha="0">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="-2" y="3"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="2" y="-6"/></Offset></Anchor>
|
||
</Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnMouseWheel>
|
||
if arg1 > 0 then this:ScrollUp() else this:ScrollDown() end
|
||
</OnMouseWheel>
|
||
<OnClick>
|
||
BenillaChatFrame_DismissCursor(arg1)
|
||
</OnClick>
|
||
<OnHyperlinkClick>
|
||
SetItemRef(arg1, arg2, arg3)
|
||
</OnHyperlinkClick>
|
||
</Scripts>
|
||
<FontString inherits="ChatFontNormal" justifyH="LEFT"/>
|
||
</ScrollingMessageFrame>
|
||
|
||
<!-- ── ChatFrame3..ChatFrame7: the five undocked windows ────────────────────────────────
|
||
The reference declares seven ScrollingMessageFrames (FloatingChatFrame.xml l.822-832,
|
||
`inherits="FloatingChatFrameTemplate"`), of which 3..7 ship HIDDEN and UNDOCKED — the
|
||
shipped chat-cache says so in as many words, and it is the state that matters here:
|
||
|
||
WINDOW 3 … 10: SIZE 0 / COLOR 0 0 0 0 / LOCKED 1 / DOCKED 0 / SHOWN 0
|
||
MESSAGES <empty> CHANNELS <empty> ZONECHANNELS 0
|
||
|
||
(`wow-5875-re/WoW/WTF/.../chat-cache.txt`, a real 1.12 character). So these are not
|
||
placeholders standing in for something richer — hidden, empty, unsubscribed IS what
|
||
ChatFrame3 is on a stock client, and an `AddMessage` into one lands in a ring nobody is
|
||
looking at, exactly as it does there.
|
||
|
||
**They are load-bearing for two different reasons, both measured:**
|
||
|
||
1. `_LazyPig/LazyPig.lua:1992` walks `for i = 1, NUM_CHAT_WINDOWS` and calls
|
||
`getglobal("ChatFrame"..i):IsVisible()` with NO nil guard — it dies at i=3 the moment
|
||
NUM_CHAT_WINDOWS exists without the frames behind it. Declaring the constant alone
|
||
would have *created* that crash.
|
||
2. `ChatFrame3` and `ChatFrame5` are named directly as debug sinks by five distinct
|
||
addons: IgniteStatus (7 unguarded `ChatFrame3:AddMessage` sites), TipBuddy, Radar
|
||
(the one guarded read), and FuBar_CorkFu + FuBar_oRA2CooldownFu, which store the frame
|
||
as AceDebug's `debugFrame`. A sixth "consumer", RosterLib-2.0's
|
||
`local function print(text) ChatFrame3:AddMessage(text) end`, is dead code replicated
|
||
into four folders — the count that looks like 12 files is 7 real sites.
|
||
|
||
No tabs (`ChatFrame3Tab`..`ChatFrame7Tab`) and no dock regions. The reference builds
|
||
both; here they would be furniture for a dock model we do not have (0288 §2), and
|
||
nothing reads them while these frames stay hidden and undocked. Outfitter.lua:3099 is
|
||
the one addon that would touch a tab — `local vTab = getglobal("ChatFrame"..i.."Tab")`
|
||
then `vTab:GetText()`, unguarded — but only INSIDE `if vChatFrame:IsVisible() or
|
||
vChatFrame.isDocked`, so a hidden, `isDocked`-less window never reaches it. That is also
|
||
precisely why `isDocked` is not set on ChatFrame2 either: our Combat Log tab is
|
||
deliberately unbuilt (see the tab section below), and marking window 2 docked — which
|
||
the reference does — would walk Outfitter straight into a nil tab.
|
||
|
||
No background/border art: the ref's own defaults put every one of these at window alpha
|
||
0 (COLOR's fourth byte), and BenillaFCF only fades the dock's two. A shown ChatFrame5
|
||
renders its text over the world with no box — which is what the reference gives you. ── -->
|
||
<ScrollingMessageFrame name="ChatFrame3" frameStrata="BACKGROUND" displayDuration="120.0" maxLines="128" hidden="true" id="3">
|
||
<Size><AbsDimension x="430" y="128"/></Size>
|
||
<Anchors><Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="100" y="100"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnMouseWheel>
|
||
if arg1 > 0 then this:ScrollUp() else this:ScrollDown() end
|
||
</OnMouseWheel>
|
||
<OnHyperlinkClick>
|
||
SetItemRef(arg1, arg2, arg3)
|
||
</OnHyperlinkClick>
|
||
</Scripts>
|
||
<FontString inherits="ChatFontNormal" justifyH="LEFT"/>
|
||
</ScrollingMessageFrame>
|
||
<ScrollingMessageFrame name="ChatFrame4" frameStrata="BACKGROUND" displayDuration="120.0" maxLines="128" hidden="true" id="4">
|
||
<Size><AbsDimension x="430" y="128"/></Size>
|
||
<Anchors><Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="100" y="100"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnMouseWheel>
|
||
if arg1 > 0 then this:ScrollUp() else this:ScrollDown() end
|
||
</OnMouseWheel>
|
||
<OnHyperlinkClick>
|
||
SetItemRef(arg1, arg2, arg3)
|
||
</OnHyperlinkClick>
|
||
</Scripts>
|
||
<FontString inherits="ChatFontNormal" justifyH="LEFT"/>
|
||
</ScrollingMessageFrame>
|
||
<ScrollingMessageFrame name="ChatFrame5" frameStrata="BACKGROUND" displayDuration="120.0" maxLines="128" hidden="true" id="5">
|
||
<Size><AbsDimension x="430" y="128"/></Size>
|
||
<Anchors><Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="100" y="100"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnMouseWheel>
|
||
if arg1 > 0 then this:ScrollUp() else this:ScrollDown() end
|
||
</OnMouseWheel>
|
||
<OnHyperlinkClick>
|
||
SetItemRef(arg1, arg2, arg3)
|
||
</OnHyperlinkClick>
|
||
</Scripts>
|
||
<FontString inherits="ChatFontNormal" justifyH="LEFT"/>
|
||
</ScrollingMessageFrame>
|
||
<ScrollingMessageFrame name="ChatFrame6" frameStrata="BACKGROUND" displayDuration="120.0" maxLines="128" hidden="true" id="6">
|
||
<Size><AbsDimension x="430" y="128"/></Size>
|
||
<Anchors><Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="100" y="100"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnMouseWheel>
|
||
if arg1 > 0 then this:ScrollUp() else this:ScrollDown() end
|
||
</OnMouseWheel>
|
||
<OnHyperlinkClick>
|
||
SetItemRef(arg1, arg2, arg3)
|
||
</OnHyperlinkClick>
|
||
</Scripts>
|
||
<FontString inherits="ChatFontNormal" justifyH="LEFT"/>
|
||
</ScrollingMessageFrame>
|
||
<ScrollingMessageFrame name="ChatFrame7" frameStrata="BACKGROUND" displayDuration="120.0" maxLines="128" hidden="true" id="7">
|
||
<Size><AbsDimension x="430" y="128"/></Size>
|
||
<Anchors><Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="100" y="100"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnMouseWheel>
|
||
if arg1 > 0 then this:ScrollUp() else this:ScrollDown() end
|
||
</OnMouseWheel>
|
||
<OnHyperlinkClick>
|
||
SetItemRef(arg1, arg2, arg3)
|
||
</OnHyperlinkClick>
|
||
</Scripts>
|
||
<FontString inherits="ChatFontNormal" justifyH="LEFT"/>
|
||
</ScrollingMessageFrame>
|
||
|
||
<!-- ── The tabs (ref ChatTabTemplate: 64×32, ChatFrameTab 3-slice quarters 16/44/16, label
|
||
at the left slice's right edge y −5, GameFontNormalSmall; the flash band at y −7; the
|
||
ref's ButtonText also carries a vestigial 50×8 Size that TabResize immediately
|
||
shrink-wraps — omitted). The middle slice hugs the label live (BenillaFCF_TabResize —
|
||
the ref's PanelTemplates_TabResize(5, tab) from FCF_DockUpdate). Hidden by alpha
|
||
(hover reveals).
|
||
|
||
ChatFrame2Tab ("Combat Log") is deliberately NOT built: the combat log renders nothing
|
||
real yet (window 2 gets its chat-cache groups from the router, but the combat-event →
|
||
line pipeline doesn't exist), so the tab just selected an empty window. ChatFrame2
|
||
itself stays above (the router keeps feeding it) so the tab can return whole with the
|
||
combat-log arc — director's call, 2026-07-11. The FCF loops guard the missing tab. ── -->
|
||
<Button name="ChatFrame1Tab" frameStrata="LOW" alpha="0">
|
||
<Size><AbsDimension x="70" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOMLEFT" relativeTo="ChatFrame1Background" relativePoint="TOPLEFT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentLeft" file="Interface\ChatFrame\ChatFrameTab">
|
||
<Size><AbsDimension x="16" y="32"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||
<TexCoords left="0.0" right="0.25" top="0.0" bottom="1.0"/>
|
||
</Texture>
|
||
<Texture name="$parentMiddle" file="Interface\ChatFrame\ChatFrameTab">
|
||
<Size><AbsDimension x="38" y="32"/></Size>
|
||
<Anchors><Anchor point="LEFT" relativeTo="$parentLeft" relativePoint="RIGHT"/></Anchors>
|
||
<TexCoords left="0.25" right="0.75" top="0.0" bottom="1.0"/>
|
||
</Texture>
|
||
<Texture name="$parentRight" file="Interface\ChatFrame\ChatFrameTab">
|
||
<Size><AbsDimension x="16" y="32"/></Size>
|
||
<Anchors><Anchor point="LEFT" relativeTo="$parentMiddle" relativePoint="RIGHT"/></Anchors>
|
||
<TexCoords left="0.75" right="1.0" top="0.0" bottom="1.0"/>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<Frame name="$parentFlash" hidden="true">
|
||
<Size><AbsDimension x="5" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativeTo="$parentLeft"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor>
|
||
<Anchor point="RIGHT" relativeTo="$parentRight"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture file="Interface\PaperDollInfoFrame\UI-Character-Tab-Highlight" alphaMode="ADD" setAllPoints="true"/>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnClick>
|
||
BenillaFCF_TabClick(1)
|
||
</OnClick>
|
||
</Scripts>
|
||
<ButtonText name="$parentText" text="General">
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativeTo="$parentLeft" relativePoint="RIGHT"><Offset><AbsDimension x="0" y="-5"/></Offset></Anchor>
|
||
</Anchors>
|
||
</ButtonText>
|
||
<NormalFont inherits="GameFontNormalSmall"/>
|
||
<!-- The ref leaves this texture unsized and leans on the BLP's NATURAL height (32) to
|
||
seat the glow at the anchors' y −7 — centered on the tab's visible body, which sits
|
||
in the BOTTOM 22 rows of the ChatFrameTab canvas. benilla's v1 region model has no
|
||
natural-size law (an unpinned axis inherits the owner's edges, decision 0068), which
|
||
parked the glow 7 units high; the explicit height states what the ref reads from the
|
||
file. -->
|
||
<HighlightTexture file="Interface\PaperDollInfoFrame\UI-Character-Tab-Highlight" alphaMode="ADD">
|
||
<Size><AbsDimension x="0" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativeTo="$parentLeft"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor>
|
||
<Anchor point="RIGHT" relativeTo="$parentRight"><Offset><AbsDimension x="0" y="-7"/></Offset></Anchor>
|
||
</Anchors>
|
||
</HighlightTexture>
|
||
</Button>
|
||
|
||
<!-- ── The UIMenu kit (ref UIMenu.xml, names kept for the addon surface): a row button
|
||
with a right-aligned shortcut string, and the tooltip-backdrop menu holding the ref's
|
||
fixed pool of 32 rows (hidden until AddButton). The label is authored LEFT-anchored:
|
||
the ref leans on the 1.12 ButtonText default rect spanning the button, which our lazy
|
||
label slot doesn't reproduce. ─────────────────────────────────────────────────── -->
|
||
<Button name="UIMenuButtonTemplate" virtual="true">
|
||
<Size><AbsDimension x="128" y="16"/></Size>
|
||
<Scripts>
|
||
<OnLoad>
|
||
UIMenuButton_OnLoad()
|
||
</OnLoad>
|
||
<OnClick>
|
||
UIMenuButton_OnClick()
|
||
</OnClick>
|
||
<OnEnter>
|
||
UIMenuButton_OnEnter()
|
||
</OnEnter>
|
||
<OnLeave>
|
||
UIMenuButton_OnLeave()
|
||
</OnLeave>
|
||
</Scripts>
|
||
<Layers>
|
||
<Layer level="ARTWORK">
|
||
<FontString name="$parentShortcutText" inherits="GameFontHighlight" hidden="true">
|
||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<ButtonText name="$parentText">
|
||
<Anchors><Anchor point="LEFT"/></Anchors>
|
||
</ButtonText>
|
||
<NormalFont inherits="GameFontNormal" justifyH="LEFT"/>
|
||
<HighlightFont inherits="GameFontHighlight" justifyH="LEFT"/>
|
||
<DisabledFont inherits="GameFontDisable" justifyH="LEFT"/>
|
||
</Button>
|
||
<Frame name="UIMenuTemplate" frameStrata="DIALOG" virtual="true">
|
||
<Backdrop bgFile="Interface\Tooltips\UI-Tooltip-Background" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
|
||
<BackgroundInsets><AbsInset left="4" right="4" top="4" bottom="4"/></BackgroundInsets>
|
||
<TileSize><AbsValue val="32"/></TileSize>
|
||
<EdgeSize><AbsValue val="13"/></EdgeSize>
|
||
</Backdrop>
|
||
<Frames>
|
||
<Button name="$parentButton1" inherits="UIMenuButtonTemplate" id="1"/>
|
||
<Button name="$parentButton2" inherits="UIMenuButtonTemplate" id="2"/>
|
||
<Button name="$parentButton3" inherits="UIMenuButtonTemplate" id="3"/>
|
||
<Button name="$parentButton4" inherits="UIMenuButtonTemplate" id="4"/>
|
||
<Button name="$parentButton5" inherits="UIMenuButtonTemplate" id="5"/>
|
||
<Button name="$parentButton6" inherits="UIMenuButtonTemplate" id="6"/>
|
||
<Button name="$parentButton7" inherits="UIMenuButtonTemplate" id="7"/>
|
||
<Button name="$parentButton8" inherits="UIMenuButtonTemplate" id="8"/>
|
||
<Button name="$parentButton9" inherits="UIMenuButtonTemplate" id="9"/>
|
||
<Button name="$parentButton10" inherits="UIMenuButtonTemplate" id="10"/>
|
||
<Button name="$parentButton11" inherits="UIMenuButtonTemplate" id="11"/>
|
||
<Button name="$parentButton12" inherits="UIMenuButtonTemplate" id="12"/>
|
||
<Button name="$parentButton13" inherits="UIMenuButtonTemplate" id="13"/>
|
||
<Button name="$parentButton14" inherits="UIMenuButtonTemplate" id="14"/>
|
||
<Button name="$parentButton15" inherits="UIMenuButtonTemplate" id="15"/>
|
||
<Button name="$parentButton16" inherits="UIMenuButtonTemplate" id="16"/>
|
||
<Button name="$parentButton17" inherits="UIMenuButtonTemplate" id="17"/>
|
||
<Button name="$parentButton18" inherits="UIMenuButtonTemplate" id="18"/>
|
||
<Button name="$parentButton19" inherits="UIMenuButtonTemplate" id="19"/>
|
||
<Button name="$parentButton20" inherits="UIMenuButtonTemplate" id="20"/>
|
||
<Button name="$parentButton21" inherits="UIMenuButtonTemplate" id="21"/>
|
||
<Button name="$parentButton22" inherits="UIMenuButtonTemplate" id="22"/>
|
||
<Button name="$parentButton23" inherits="UIMenuButtonTemplate" id="23"/>
|
||
<Button name="$parentButton24" inherits="UIMenuButtonTemplate" id="24"/>
|
||
<Button name="$parentButton25" inherits="UIMenuButtonTemplate" id="25"/>
|
||
<Button name="$parentButton26" inherits="UIMenuButtonTemplate" id="26"/>
|
||
<Button name="$parentButton27" inherits="UIMenuButtonTemplate" id="27"/>
|
||
<Button name="$parentButton28" inherits="UIMenuButtonTemplate" id="28"/>
|
||
<Button name="$parentButton29" inherits="UIMenuButtonTemplate" id="29"/>
|
||
<Button name="$parentButton30" inherits="UIMenuButtonTemplate" id="30"/>
|
||
<Button name="$parentButton31" inherits="UIMenuButtonTemplate" id="31"/>
|
||
<Button name="$parentButton32" inherits="UIMenuButtonTemplate" id="32"/>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnShow>
|
||
UIMenu_OnShow()
|
||
PlaySound("UChatScrollButton")
|
||
this:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
|
||
this:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
|
||
</OnShow>
|
||
<OnUpdate>
|
||
UIMenu_OnUpdate(arg1)
|
||
</OnUpdate>
|
||
</Scripts>
|
||
</Frame>
|
||
|
||
<!-- ── The chat menu button (ref ChatFrameMenuButton): the bubble icon atop the scroll
|
||
column; click opens ChatMenu (ref ChatFrame_OpenMenu). ──────────────────────────── -->
|
||
<Button name="ChatFrameMenuButton" frameStrata="LOW">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOM" relativeTo="ChatFrame1UpButton" relativePoint="TOP"/>
|
||
</Anchors>
|
||
<Scripts>
|
||
<OnClick>
|
||
PlaySound("igChatEmoteButton")
|
||
ChatMenu:Show()
|
||
</OnClick>
|
||
</Scripts>
|
||
<NormalTexture file="Interface\ChatFrame\UI-ChatIcon-Chat-Up"/>
|
||
<PushedTexture file="Interface\ChatFrame\UI-ChatIcon-Chat-Down"/>
|
||
<DisabledTexture file="Interface\ChatFrame\UI-ChatIcon-Chat-Disabled"/>
|
||
<HighlightTexture file="Interface\Buttons\UI-Common-MouseHilight" alphaMode="ADD"/>
|
||
</Button>
|
||
|
||
<!-- ChatMenu + its two nested menus (ref FloatingChatFrame.xml; the ref's own OnShow
|
||
hides only EmoteMenu — hiding both submenus is the same intent without the ref's
|
||
Voice-menu-left-open corner). The submenus are authored as children (the ref's
|
||
parent="ChatMenu" attr — this engine attaches by nesting, CharacterFrame.xml's note),
|
||
so hiding ChatMenu hides them with it. -->
|
||
<Frame name="ChatMenu" inherits="UIMenuTemplate" hidden="true">
|
||
<Anchors>
|
||
<Anchor point="BOTTOMLEFT" relativeTo="ChatFrameMenuButton" relativePoint="TOPRIGHT"/>
|
||
</Anchors>
|
||
<Frames>
|
||
<Frame name="EmoteMenu" inherits="UIMenuTemplate" hidden="true">
|
||
<Scripts>
|
||
<OnLoad>
|
||
BenillaOnEmoteMenuLoad(BenillaEmoteMenuList)
|
||
</OnLoad>
|
||
</Scripts>
|
||
</Frame>
|
||
<Frame name="VoiceMacroMenu" inherits="UIMenuTemplate" hidden="true">
|
||
<Scripts>
|
||
<OnLoad>
|
||
BenillaOnEmoteMenuLoad(BenillaVoiceMacroList)
|
||
</OnLoad>
|
||
</Scripts>
|
||
</Frame>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnLoad>
|
||
ChatMenu_OnLoad()
|
||
</OnLoad>
|
||
<OnShow>
|
||
UIMenu_OnShow()
|
||
EmoteMenu:Hide()
|
||
VoiceMacroMenu:Hide()
|
||
this:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
|
||
this:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
|
||
</OnShow>
|
||
</Scripts>
|
||
</Frame>
|
||
|
||
<!-- ── The edit box (ref ChatFrameEditBox): below ChatFrame1, -5/+5 wide, 32 tall, DIALOG
|
||
strata, hidden until ENTER opens it (ui_chat::input). The three-piece
|
||
UI-ChatInputBorder art; the header ("Say:" until the P5 edit machine drives it) at
|
||
LEFT+13; typed text starts past it (static insets, P5 makes them live). ─────────── -->
|
||
<!-- The ref template ships ignoreArrows="true": plain arrows fall through to CHARACTER
|
||
MOVEMENT while typing (Ctrl bypasses), because 1.12 lets you keep arrow-steering with
|
||
the box open. benilla has no typing-while-moving arrow scheme, so the attr would leave
|
||
the keys dead; the director's call is the modern law — plain arrows move the caret,
|
||
Up/Down recall history (decision 0301). The engine still honors the attr faithfully
|
||
for any box that sets it. -->
|
||
<!-- historyLines: the ref template ships 32; 64 at the director's call ("at least 50",
|
||
2026-07-26) — the recall walk is the modern law already (decision 0301). -->
|
||
<EditBox name="ChatFrameEditBox" frameStrata="DIALOG" hidden="true" historyLines="64" letters="255">
|
||
<Size><AbsDimension x="0" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="ChatFrame1" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="-5" y="-2"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT" relativeTo="ChatFrame1" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="5" y="-2"/></Offset></Anchor>
|
||
</Anchors>
|
||
<TextInsets><AbsInset left="47" right="13" top="0" bottom="0"/></TextInsets>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentLeft" file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||
<Size><AbsDimension x="256" y="32"/></Size>
|
||
<Anchors><Anchor point="LEFT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentRight" file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||
<Size><AbsDimension x="16" y="32"/></Size>
|
||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||
<TexCoords left="0.9375" right="1.0" top="0" bottom="1.0"/>
|
||
</Texture>
|
||
<Texture name="$parentMid" file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||
<Size><AbsDimension x="1" y="32"/></Size>
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativeTo="$parentLeft" relativePoint="RIGHT"/>
|
||
<Anchor point="RIGHT" relativeTo="$parentRight" relativePoint="LEFT"/>
|
||
</Anchors>
|
||
<TexCoords left="0" right="0.9375" top="0" bottom="1.0"/>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="ARTWORK">
|
||
<FontString name="$parentHeader" inherits="ChatFontNormal" text="Say:">
|
||
<Anchors>
|
||
<Anchor point="LEFT"><Offset><AbsDimension x="13" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnEnterPressed>
|
||
SubmitChatInput(this:GetText())
|
||
this:SetText("")
|
||
this:ClearFocus()
|
||
this:Hide()
|
||
</OnEnterPressed>
|
||
<OnEscapePressed>
|
||
this:SetText("")
|
||
this:ClearFocus()
|
||
this:Hide()
|
||
</OnEscapePressed>
|
||
<OnTabPressed>
|
||
BenillaChatTabPressed()
|
||
</OnTabPressed>
|
||
</Scripts>
|
||
<FontString inherits="ChatFontNormal" justifyH="LEFT"/>
|
||
</EditBox>
|
||
|
||
<!-- The load-order hack, like the ref's own trailing FCF_DockFrame(ChatFrame1, 1): select
|
||
the General tab once everything above exists — and tint the window chrome BLACK
|
||
(FCF_SetWindowColor: the ref vertex-colors every CHAT_FRAME_TEXTURES piece with the
|
||
chat-cache window COLOR, which ships 0 0 0; the ChatFrameBackground art itself is
|
||
white, so untinted it reads as a white wash over the world). -->
|
||
<Script><![CDATA[
|
||
-- `DEFAULT_CHAT_FRAME = ChatFrame1` — the reference's own line (ChatFrame.lua), and the
|
||
-- name half of FrameXML actually prints through. It sits here rather than in the header
|
||
-- script because a <Script> block runs where it stands: at the top of this file
|
||
-- ChatFrame1 does not exist yet. Measured demand: 3 of the 218 vanilla-corpus addons
|
||
-- die on `attempt to index global 'DEFAULT_CHAT_FRAME'` without it, and many more reach
|
||
-- it on a later path.
|
||
DEFAULT_CHAT_FRAME = ChatFrame1
|
||
|
||
BenillaFCF_SelectDock(1)
|
||
for i = 1, 2 do
|
||
for _, t in ipairs(BenillaFCF_Textures(i)) do
|
||
t:SetVertexColor(0, 0, 0)
|
||
end
|
||
end
|
||
]]></Script>
|
||
</Ui>
|