- a size-less `<Model>` pane's rect is its file's box in layout units: the reference's geometry overrides (`0x76d080`/`0x76d0d0`) answer the bbox extent whenever no size is authored, `bboxExtent · 768·√(a²+1) · layoutScale` FrameXML units (`1280 · extent` at 4:3), written into the pane's layout input when the file's facts land, re-derived on an aspect change, and ended for good by any authored size; `SetPosition` is the same unit, which is how `0x4a7b20` centres the arrow at `½·GetWidth, ½·GetHeight` through the override — so the map arrow's sprite arm and its hand-sized 33.6 px footprint go (its rect is `0.0262 × 0.0263` layout units, 33.5 × 33.7 FrameXML units at 4:3, 41.0 × 41.2 at 16:9), and the stock `ItemAnim` pane plays the item push unadapted (`ReplaceIconTexture` on the type-14 batch, the 1000 ms clamp, the stock `OnAnimFinished`), `ContainerFrameAdapters.xml` losing 0887's Lua card and its hand-measured 42.41 × 124.72 rect — the implicit one at 16:9 — and keeping the keyring fork and the three bag verbs (2015); and the cooldown is the reference's own model: the texture-transform law `uv' = R_q((uv + t − p) ⊙ s) + p`, `p = (½, ½)`, the raw quaternion component-lerped and never normalised, is one function (`uv_transform`) with the shader's UV fold as its twin — rotation and scale tracks bake per file sequence slot beside the translation, the mat-anim table gains an affine row `[cos − 1, sin, sx − 1, sy − 1]` on `anim_slots.z` whose identity is the zero row so every static batch reads exactly what it did, and a UI tile clones the twin material per animated batch and writes both rows every frame off the pane's play head, never the world clock; the stock `Cooldown.xml` runs off the chain on 2007's two handlers, the file's four quadrant quads turned by their own rotation tracks (`0 → −90°` over each quadrant's 250 ms window, clockwise from 12 o'clock) and its alpha tracks revealing the star for the flash; retired: the native `Cooldown` widget and `SetCooldown` (an Era API 1.12 never had — `CreateFrame("Cooldown")` is the reference's unknown-type refusal now), the loader's `Model → Cooldown` substitution, the app's hand-built pie and star, and our `Cooldown.xml`; `assets/ui` is eight files (2019)
- `UpdateMapHighlight` gets its zone-level exits (B360): at zone zoom ours returned the nil tail for every cursor position, so hovering Deadwind Pass from Duskwood showed no label where 1.12 shows "Deadwind Pass"; the reference's `0x4a7fa0` runs two lookups at zone level and answers a NAME only — first the current zone's exploration-gated overlay list (the builder at `0x4a6ad9` gating on the explored byte), each row's HitRect scaled by 1/1002 and 1/668 against the normalized cursor, edges inclusive, the first hit naming its Area1, then the continent grid re-windowed through the displayed zone's own rect, naming the neighbouring zone or city and nothing for the displayed zone itself — and only the name-only tail is reachable from there, fileName nil and the six coords zero, so the stock frame hides the highlight quad, as the director's own 1.12 shot shows; and `SetVerticalScroll` stores the offset as given (B370, B363): the reference's `0x786db0` compares the new offset against the old and writes it to `[+0x328]` verbatim, `0x787100` re-anchors the scroll child off it, and the range at `[+0x320]` is never read on that path — every clamp the reference exhibits is FrameXML's, through the scroll bar's `[min, max]`; ours clamped into `[0, GetVerticalScrollRange()]`, the child's overflow past the FRAME, which is 40 px short of where `FauxScrollFrame_Update` sends the bar on the 220-tall skills list (three rows lost) and 15 px short on the 287-tall who list (one); the setter fires `OnVerticalScroll` only on a change, as the bytes do, `BenillaScroll_Step` bounds its wheel in Lua where the reference's handler does, and the ScrollFrame kind's prose stops calling its offset unpinned (2017)
- overdraw's cheap half, the sky first: since 0588 every sky element wrote `frag_depth = 0.0`, which costs a pipeline its early-Z, so on an outdoor frame every full-screen dome fragment under the ground and the buildings was shaded in full (the gradient's `asin`, `atan2` and eight table lookups at dawn and dusk) and thrown away; one shared `sky_vertex.wgsl` — bevy's mesh vertex verbatim plus `out.position.z = 0.0` — is now the vertex stage of the gradient dome, the cloud dome, the star patches and the celestial discs and glares, and the model lane's `WOW_SKY_DEPTH` branch moves from its fragment to its vertex, so no sky fragment declares `frag_depth` and the rasterizer rejects the covered screen before the shader runs; every sky `specialize` zeroes the rasterizer bias constant the `sky_order` rung had been leaking into `DepthBiasState::constant`, harmless only while the fragment overwrote its depth, and the prose beside the pin follows it; then the retained `static_gx` pass draws BEFORE bevy's opaque pass (`StartMainPass → static_gx → MainOpaquePass`) — buildings and doodads are the frame's best occluders and a terrain fragment its dearest, six samples a pixel — so walls reject the terrain behind them instead of the reverse, the pass taking the first-use clear with it and the dev depth probe ordered after it; measured on 16 capture scenarios: 14 bit-identical, `northshire-dusk` 1 px and `water-night` 11 px, attributed to exact depth ties on static/terrain silhouettes and last-bit interpolation noise; a depth prepass and near-to-far bins are declined until a journal says where the time goes (2016)
- the grass cut goes per pixel because of a quarter-mip: `0x6813d0` sets `D3DSAMP_MIPMAPLODBIAS = +0.25` on stage 0 (`0x6813f4`) once per frame before the detail-doodad pass — capture-confirmed on all 29 doodad batches, and outside the `[0x6b1000, 0x6b3200)` scan 2004 had called complete; it decides the whole look because the `clutter_cut` probe finds 6 of 31 shipped detail atlases carry a BINARY alpha pyramid (mip 0 has DXT3's 16 levels, every level below exactly two, both starting zones among them), so `a × ramp` degenerates to a global on/off and every texel of a leaf crosses at the same depth — Elwynn's survivor coverage is flat at 47.6 % across the whole 52.5–61.1 yd band, then 0.0 %, while the probe's box-filtered control erodes 47.7 → 20.9 over the same band, so the art has the gradient the shipped pyramid threw away, and `clutter_size` puts the median tuft at 0.92 yd tall, 1.03 yd deep, a full yard for the boundary plane to sweep; the escape is bilinear: full alpha needs a 2×2 all-opaque texel neighbourhood, and those run out between mip 2 and 3 (179/56/10/0/0/0 on the dominant Elwynn cell), so the reference's λ ≈ 2.9–3.3 sits past the cliff and the tuft erodes continuously — 2.2 % of a tuft's pixels leave in the last half-yard with the bias, 11.6 % without, 24.2 % one mip sharper; wgpu has no sampler LOD bias, so the clutter lane re-samples base colour at `view.mip_bias + 0.25`, keeping the sampler every other model batch shares at the reference's 0; ruled out on the way: our BLP decoder (all nine uploaded levels byte-identical against the archive's 52 793 BLP2s), mip regeneration, the `has_mipmaps` byte's value 2 (a plain boolean, an authoring marker), anisotropy and geometry; what it does not fix is resolution-dependent, and so is the reference's own look — λ with the bias is 3.22 at the capture's 648p but 2.48 at 1080p and 1.48 at 2160p, so at a modern resolution the reference itself pops leaf by leaf, and scaling the bias with the viewport is one reversible line left for a look call (2021)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
610 lines
41 KiB
XML
610 lines
41 KiB
XML
<Ui>
|
||
<!--
|
||
benilla's reusable scroll kits — TWO of them, sharing one scrollbar, one pair of arrow
|
||
helpers and one optional TROUGH (`BenillaScrollTroughTemplate`, at the foot: the recessed
|
||
channel a bar rides in — 1.12 authors that art per window, so an owner declares one beside
|
||
its bar and the kit shows and hides the two together).
|
||
The **faux-scroll** kit (this header) re-binds a fixed set of rows to a moving data
|
||
offset; the **real-scroll** kit (second half of the script block) pans and CLIPS a
|
||
variable-height child through the engine's `<ScrollFrame>` (decision 0112) — the one a gossip
|
||
menu of wrapped options needs, since its content height isn't known until it is laid out.
|
||
Only their OnValueChanged differs, and an instance overrides the template's (the loader
|
||
SetScripts every `<Scripts>` block in order, so the instance's handler wins).
|
||
|
||
The faux kit (decisions 0247/0250/0251): a draggable scroll bar over
|
||
the `Slider` primitive (0250) plus the fixed-row "faux scroll" Lua layer — the reference's
|
||
`FauxScrollFrame`, which never really scrolls a child: it re-binds a fixed set of row buttons to
|
||
a moving data offset. First customer: the class trainer's skill-line list (TrainerFrame.xml).
|
||
The quest log's chunk-local faux-scroll upvalue is a later migration onto this.
|
||
|
||
DESIGN — collapsed from the real client's three-part decomposition (0250: FauxScrollFrame =
|
||
ScrollFrame + Slider + Lua). The real `FauxScrollFrameTemplate` routes the scrollbar's value
|
||
through its parent `ScrollFrame` (`OnValueChanged → parent:SetVerticalScroll(v)`; the
|
||
ScrollFrame's `OnVerticalScroll` then derives `offset = floor(v/itemHeight + 0.5)`). That
|
||
ScrollFrame adds nothing to the value on its way through: 0251 believed it clamped to the
|
||
scroll range and called the clamp a liability, and decision 2017 read the bytes — the
|
||
reference's `SetVerticalScroll` stores what the bar hands it. So the relay is a pure
|
||
pass-through, and benilla's faux frame is a plain `<Frame>` + a `Slider`: fewer moving parts
|
||
and the identical on-screen result (a smooth, pixel-granularity thumb whose position maps
|
||
1:1 to the scroll offset). See 0251 for the full rationale + the divergence from 0250's
|
||
described (real-client) shape.
|
||
|
||
VALUE MODEL. The scrollbar's value is in PIXELS: `[0, (numItems − numToDisplay) * itemHeight]`,
|
||
`valueStep = itemHeight`. The row offset is `floor(value / itemHeight + 0.5)` — sub-pixel
|
||
rounding, so the thumb (drawn at the raw value fraction) and the list (drawn at the rounded
|
||
offset) never visibly disagree. The frame carries `frame.offset` (the top visible row, 0-based)
|
||
and `frame.updateFunc` (the owner's list-repaint, set by the owner's OnLoad).
|
||
-->
|
||
<Script><![CDATA[
|
||
-- Step the bar one row (the slider's valueStep) — the sensible step for an item list (the
|
||
-- generic ref scrollbar steps half its height; a discrete row list wants one). Silent by
|
||
-- design: the ref plays UChatScrollButton only on the arrow *buttons'* OnClick, never on the
|
||
-- wheel (ScrollFrameTemplate_OnMouseWheel is soundless). Both the wheel and the arrows call
|
||
-- this, so the click lives on the arrow OnClick, keeping wheel-scroll silent.
|
||
function BenillaScrollBar_Step(scrollBar, dir)
|
||
local step = scrollBar:GetValueStep()
|
||
if step <= 0 then step = 1 end
|
||
scrollBar:SetValue(scrollBar:GetValue() + dir * step)
|
||
end
|
||
|
||
-- Grey the up arrow at the top and the down arrow at the bottom (ref
|
||
-- ScrollFrame_OnScrollRangeChanged's arrow handling), from the slider's own value/range.
|
||
function BenillaScrollBar_UpdateArrows(scrollBar)
|
||
local minV, maxV = scrollBar:GetMinMaxValues()
|
||
local val = scrollBar:GetValue()
|
||
local up = getglobal(scrollBar:GetName() .. "ScrollUpButton")
|
||
local down = getglobal(scrollBar:GetName() .. "ScrollDownButton")
|
||
if val <= minV then up:Disable() else up:Enable() end
|
||
if val >= maxV then down:Disable() else down:Enable() end
|
||
end
|
||
|
||
-- The scrollbar's OnValueChanged: derive the row offset from the (pixel) value and, only when
|
||
-- it actually moved, repaint the owner's list. The change-gate is load-bearing: the owner's
|
||
-- repaint calls _Update below, which SetValue()s the thumb back to offset*itemHeight — a value
|
||
-- that maps to the SAME offset, so this handler no-ops and the cycle stops after one hop (the
|
||
-- Slider's own SetValue change-gate stops the value side, decision 0250).
|
||
--
|
||
-- THE SECOND HALF IS FOR ADDONS, and it is the reference's own wiring. The ref's
|
||
-- `UIPanelScrollBarTemplate` routes its value through the ScrollFrame it rides
|
||
-- (`<OnValueChanged>this:GetParent():SetVerticalScroll(arg1)</OnValueChanged>`,
|
||
-- ref-UIPanelTemplates.xml l.156-158), and that frame's `<OnVerticalScroll>` is where an
|
||
-- addon puts `FauxScrollFrame_OnVerticalScroll(itemHeight, MyUpdate)`. Our OWN faux frames
|
||
-- are plain `<Frame>`s (the 0251 divergence in the header above), so they have no
|
||
-- SetVerticalScroll and this line never fires for them — but an addon's is declared
|
||
-- `<ScrollFrame ... inherits="FauxScrollFrameTemplate">`, and the instance's own tag wins
|
||
-- over the template's on inherit (framexml.rs `merge`), so it really is one. Duck-typed for
|
||
-- exactly that reason.
|
||
--
|
||
-- The engine's SetVerticalScroll takes the bar's value as given (decision 2017 — the
|
||
-- reference's never reads the scroll range), so an addon frame's `<OnVerticalScroll>`
|
||
-- fires with this very value and its `FauxScrollFrame_OnVerticalScroll` derives the offset
|
||
-- from it, the reference's path end to end. The offset our own kit computes above is the
|
||
-- same number by construction — it comes straight off the Slider, which is the whole point
|
||
-- of 0251.
|
||
function BenillaFauxScrollBar_OnValueChanged(scrollBar, value)
|
||
local frame = scrollBar:GetParent()
|
||
local step = scrollBar:GetValueStep()
|
||
if step <= 0 then step = 1 end
|
||
local newOffset = floor(value / step + 0.5)
|
||
if newOffset ~= frame.offset then
|
||
frame.offset = newOffset
|
||
if frame.updateFunc then frame.updateFunc() end
|
||
end
|
||
BenillaScrollBar_UpdateArrows(scrollBar)
|
||
if frame.SetVerticalScroll then frame:SetVerticalScroll(value) end
|
||
end
|
||
|
||
-- Seat a trough ON ITS BAR. THE SOCKETS, and why this is not a free choice: the channel art
|
||
-- is not a plain tube — each end carries a 16-tall SOCKET that the arrow button drops into,
|
||
-- bounded by the frame's outer cap and an inner crossbar. Measured texel-exact off
|
||
-- UI-Character-ScrollBar (64x256):
|
||
-- top piece (left column) : rows 0-3 CAP · rows 4-19 SOCKET · rows 20-21 crossbar
|
||
-- bottom piece (right column): rows 85-86 crossbar · rows 87-102 SOCKET · rows 104-106 CAP
|
||
-- Both pieces draw 1:1 anchored to their own end of the trough, and the arrow buttons sit 16
|
||
-- outside the slider's ends — so a socket lands on its arrow only when the trough OVERHANGS
|
||
-- the bar by 21 above and 20 below (4 rows of cap at the top against 3+1 at the bottom; that
|
||
-- 1 is the whole asymmetry). Those are the ref's own numbers, reached from the other side:
|
||
-- ReputationFrame hangs its trough at the scroll frame's TOPRIGHT (-2,+5) / BOTTOMRIGHT
|
||
-- (-2,-4) against a bar seated (6,-16)/(6,+16) — 21, 20, and the 8-left hang
|
||
-- (ref-ReputationFrame.xml l.573-599 against ref-UIPanelTemplates.xml l.166-181). Miss it by
|
||
-- even 4 and the arrows ride OUT of their sockets onto the caps with a sliver of bare socket
|
||
-- showing beside them, which is exactly what B224 reported.
|
||
--
|
||
-- Seated HERE, off the bar, rather than restated in each owner's XML: it is one law, and an
|
||
-- owner made to re-derive it in offsets is an owner that can get it wrong — which is how
|
||
-- B224 happened. So a trough carries NO anchors of its own; declare it before the bar (so it
|
||
-- draws beneath) and name it `<barname>Trough`, and that is the whole contract. Lazy,
|
||
-- because the bar is declared after the trough and does not exist at the trough's OnLoad.
|
||
function BenillaScrollTrough_Seat(trough, scrollBar)
|
||
if trough.seated then return end
|
||
trough.seated = true
|
||
trough:ClearAllPoints()
|
||
trough:SetPoint("TOPLEFT", scrollBar, "TOPLEFT", -8, 21)
|
||
trough:SetPoint("BOTTOMRIGHT", scrollBar, "BOTTOMRIGHT", 7, -20)
|
||
end
|
||
|
||
-- Show/hide a bar TOGETHER WITH ITS TROUGH — the recessed channel it rides in, if the
|
||
-- owner authored one beside it (a `<barname>Trough` frame on BenillaScrollTroughTemplate
|
||
-- below). Every path that shows or hides a bar goes through here, so a window that fits
|
||
-- shows neither and one that overflows shows both — and so a trough is seated on its bar
|
||
-- before it can ever be visible, whichever way the first call goes.
|
||
--
|
||
-- Why the trough is OPT-IN rather than part of the bar template: 1.12 authors trough art
|
||
-- PER WINDOW, never in the shared scroll kit — `UIPanelScrollBarTemplate` carries only the
|
||
-- two arrow buttons and the knob, and the ref's FrameXML then hangs loose trough textures
|
||
-- beside 29 individual bars, in two different skins (UI-Character-ScrollBar for the
|
||
-- dialog/paperdoll family, UI-ClassTrainer-ScrollBar for the trainer family). The era's own
|
||
-- MinimalScrollBar went the other way and made its Track part of the bar; this pair keeps
|
||
-- the era's COUPLING (track lives and dies with the bar) on 1.12's per-window SKIN.
|
||
function BenillaScrollBar_SetShown(scrollBar, shown)
|
||
local trough = getglobal(scrollBar:GetName() .. "Trough")
|
||
if trough then BenillaScrollTrough_Seat(trough, scrollBar) end
|
||
if shown then
|
||
scrollBar:Show()
|
||
if trough then trough:Show() end
|
||
else
|
||
scrollBar:Hide()
|
||
if trough then trough:Hide() end
|
||
end
|
||
end
|
||
|
||
-- **Our trough, following the REFERENCE's bar.** `BenillaScrollBar_SetShown` above is the
|
||
-- Benilla kit's own pairing and nothing in the reference's faux kit calls it — the
|
||
-- reference has no trough at all, which is exactly why this template stays ours (1837's
|
||
-- manifest note). Since the faux frames took the chain's `FauxScrollFrameTemplate` their
|
||
-- bar is `UIPanelScrollBarTemplate`, so an owner that wants its trough asks for it here,
|
||
-- right after its `FauxScrollFrame_Update`. Seating is idempotent and the visibility
|
||
-- simply mirrors the bar's, which the reference's `_Update` has already decided by
|
||
-- showing or hiding the scroll FRAME.
|
||
--
|
||
-- Decision 1860. Named after the bar rather than the frame because the trough is declared
|
||
-- `<barname>Trough` — one law, stated once, exactly as BenillaScrollTrough_Seat wants it.
|
||
function BenillaScrollTrough_Follow(scrollFrameName)
|
||
local sf = getglobal(scrollFrameName)
|
||
if not sf then return end
|
||
local bar = getglobal(scrollFrameName .. "ScrollBar")
|
||
if not bar then return end
|
||
local trough = getglobal(bar:GetName() .. "Trough")
|
||
if not trough then return end
|
||
BenillaScrollTrough_Seat(trough, bar)
|
||
if sf:IsShown() then trough:Show() else trough:Hide() end
|
||
end
|
||
|
||
-- A wheel spin over the list → one row (WoW convention: +1 up, −1 down). Called from the
|
||
-- owner window's OnMouseWheel (the engine bubbles the spin up the parent chain, so the handler
|
||
-- lives on the window, not a child — TrainerFrame.xml's own note).
|
||
--
|
||
-- KEEPS the `Benilla` prefix on purpose: the reference has no faux-scroll wheel function to
|
||
-- take the name of. Its `ScrollFrameTemplate_OnMouseWheel` is the REAL-scroll one and moves
|
||
-- by half the bar's height; its faux owners each write a wheel body of their own inline.
|
||
function BenillaFauxScrollFrame_OnMouseWheel(frame, delta)
|
||
local scrollBar = getglobal(frame:GetName() .. "ScrollBar")
|
||
BenillaScrollBar_Step(scrollBar, -delta)
|
||
end
|
||
|
||
-- ══════════════════════════ The REAL-scroll kit ══════════════════════════════════════════
|
||
--
|
||
-- The other half of the file: a true `<ScrollFrame>` (engine mechanism, decision 0112) that
|
||
-- pans and CLIPS a variable-height child, as against the faux kit above (fixed rows re-bound
|
||
-- to a data offset). Windows whose content height is unknown until it is laid out — a gossip
|
||
-- menu of wrapped options, a quest's body text — need this one; a uniform list wants the faux.
|
||
--
|
||
-- SHAPE, from the reference `UIPanelScrollFrameTemplate` (Interface\FrameXML\
|
||
-- UIPanelTemplates.xml l.166-211, extracted via mpqx): the scrollbar is a SLIDER that rides
|
||
-- its ScrollFrame as a child, so `bar:GetParent()` IS the frame it scrolls — no name
|
||
-- derivation, and `$parentBar` gives SyncBar its handle. The ref's OnVerticalScroll drives
|
||
-- the bar's value and greys the two arrows at the ends; ours does the same through the
|
||
-- shared arrow helper above.
|
||
BENILLA_SCROLL_STEP = 20 -- px per arrow click / wheel notch
|
||
BENILLA_SCROLL_PAD = 20 -- breathing room under the last element in the child
|
||
|
||
-- The bar moved (drag, arrow, or SyncBar): pan the frame it rides. The loop this could form
|
||
-- (bar → scroll → OnVerticalScroll → SyncBar → bar) dead-ends in one hop — SyncBar writes the
|
||
-- value the bar already holds, and the Slider's SetValue change-gate (decision 0250) drops it.
|
||
function BenillaScrollBar_OnValueChanged(bar, value)
|
||
bar:GetParent():SetVerticalScroll(value)
|
||
BenillaScrollBar_UpdateArrows(bar)
|
||
end
|
||
|
||
-- Reconcile a ScrollFrame's bar to its live range (ref ScrollFrame_OnScrollRangeChanged):
|
||
-- the bar spans [0, range] in pixels, seats at the current scroll, and hides entirely when
|
||
-- the content fits. Wired to both OnScrollRangeChanged and OnVerticalScroll by the owner.
|
||
function BenillaScroll_SyncBar(sf)
|
||
local bar = getglobal(sf:GetName() .. "Bar")
|
||
if not bar then return end
|
||
local range = sf:GetVerticalScrollRange()
|
||
bar:SetValueStep(BENILLA_SCROLL_STEP)
|
||
bar:SetMinMaxValues(0, range)
|
||
bar:SetValue(sf:GetVerticalScroll())
|
||
BenillaScrollBar_UpdateArrows(bar)
|
||
BenillaScrollBar_SetShown(bar, range > 0)
|
||
end
|
||
|
||
-- Pan by `delta` px, clamped to [0, range] HERE: the engine's SetVerticalScroll stores
|
||
-- what it is given (decision 2017 — the reference's never reads the range), and the
|
||
-- reference's own wheel handler bounds itself the same way, through its bar's [min, max]
|
||
-- (ScrollFrameTemplate_OnMouseWheel → scrollBar:SetValue).
|
||
function BenillaScroll_Step(sf, delta)
|
||
local target = sf:GetVerticalScroll() + delta
|
||
local range = sf:GetVerticalScrollRange()
|
||
if target > range then target = range end
|
||
if target < 0 then target = 0 end
|
||
sf:SetVerticalScroll(target)
|
||
BenillaScroll_SyncBar(sf)
|
||
end
|
||
|
||
-- WoW wheel convention: +1 per notch up, -1 down — up lifts the content.
|
||
function BenillaScroll_OnMouseWheel(sf, delta)
|
||
BenillaScroll_Step(sf, -delta * BENILLA_SCROLL_STEP)
|
||
end
|
||
|
||
-- A widget's resolved bottom edge for the content sum — nil when it isn't contributing
|
||
-- (hidden frame / blank FontString / not yet resolved this frame).
|
||
function BenillaScroll_WidgetBottom(w)
|
||
if not w then return nil end
|
||
if w.IsShown then
|
||
if not w:IsShown() then return nil end
|
||
elseif w.GetText then
|
||
local t = w:GetText()
|
||
if not t or t == "" then return nil end
|
||
end
|
||
return w:GetBottom()
|
||
end
|
||
|
||
-- Size a scroll child to its MEASURED content — the resolved bottom of the lowest visible
|
||
-- widget, plus padding — instead of a fixed budget, and tell the frame (which fires
|
||
-- OnScrollRangeChanged → the owner's SyncBar). Frame-late by nature: rects resolve after Lua
|
||
-- runs, so owners re-run this from an OnUpdate until it stops changing. Returns true when it
|
||
-- actually resized (only a real change writes: SetHeight invalidates layout, and an
|
||
-- every-frame write would fight the resolver).
|
||
function BenillaScroll_ResizeChild(scrollName, widgets)
|
||
local sf = getglobal(scrollName)
|
||
-- ASK THE FRAME, don't guess a name. This used to be
|
||
-- `getglobal(scrollName .. "Child")`, which only ever worked because every caller wore
|
||
-- a private name in that exact shape — the reference's own convention is
|
||
-- `<X>ScrollFrame` / `<X>ScrollChildFrame`, not a suffix, so the derivation broke the
|
||
-- moment QuestFrame took the reference's names. A ScrollFrame knows its own child.
|
||
local child = sf:GetScrollChild()
|
||
if not child then return false end
|
||
local top = child:GetTop()
|
||
if not top then return false end
|
||
local bottom = nil
|
||
for i = 1, table.getn(widgets) do
|
||
local b = BenillaScroll_WidgetBottom(getglobal(widgets[i]))
|
||
if b and (not bottom or b < bottom) then bottom = b end
|
||
end
|
||
local height = 1
|
||
if bottom then height = (top - bottom) + BENILLA_SCROLL_PAD end
|
||
if height < 1 then height = 1 end
|
||
if math.abs(child:GetHeight() - height) < 1 then return false end
|
||
child:SetHeight(height)
|
||
sf:UpdateScrollChildRect()
|
||
return true
|
||
end
|
||
]]></Script>
|
||
|
||
<!--
|
||
The scroll bar: a `Slider` (0250) 16px wide, its up/down step buttons pinned above/below the
|
||
track, and the knob thumb. The value is in pixels (see the header); the thumb draws at the
|
||
value fraction along the track (engine-side, decision 0250 §4), draggable (§5). The step-button
|
||
+ knob art is the stock 1.12 scrollbar kit, each texture cropped to its centred 16px (the
|
||
`0.25..0.75` TexCoords the ref's `UIPanelScrollBarButton` applies).
|
||
-->
|
||
<Slider name="BenillaScrollBarTemplate" virtual="true">
|
||
<Size><AbsDimension x="16" y="0"/></Size>
|
||
<Frames>
|
||
<Button name="$parentScrollUpButton">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors><Anchor point="BOTTOM" relativePoint="TOP"/></Anchors>
|
||
<NormalTexture file="Interface\Buttons\UI-ScrollBar-ScrollUpButton-Up"><TexCoords left="0.25" right="0.75" top="0.25" bottom="0.75"/></NormalTexture>
|
||
<PushedTexture file="Interface\Buttons\UI-ScrollBar-ScrollUpButton-Down"><TexCoords left="0.25" right="0.75" top="0.25" bottom="0.75"/></PushedTexture>
|
||
<DisabledTexture file="Interface\Buttons\UI-ScrollBar-ScrollUpButton-Disabled"><TexCoords left="0.25" right="0.75" top="0.25" bottom="0.75"/></DisabledTexture>
|
||
<HighlightTexture file="Interface\Buttons\UI-ScrollBar-ScrollUpButton-Highlight" alphaMode="ADD"><TexCoords left="0.25" right="0.75" top="0.25" bottom="0.75"/></HighlightTexture>
|
||
<Scripts><OnClick>BenillaScrollBar_Step(this:GetParent(), -1); PlaySound("UChatScrollButton")</OnClick></Scripts>
|
||
</Button>
|
||
<Button name="$parentScrollDownButton">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors><Anchor point="TOP" relativePoint="BOTTOM"/></Anchors>
|
||
<NormalTexture file="Interface\Buttons\UI-ScrollBar-ScrollDownButton-Up"><TexCoords left="0.25" right="0.75" top="0.25" bottom="0.75"/></NormalTexture>
|
||
<PushedTexture file="Interface\Buttons\UI-ScrollBar-ScrollDownButton-Down"><TexCoords left="0.25" right="0.75" top="0.25" bottom="0.75"/></PushedTexture>
|
||
<DisabledTexture file="Interface\Buttons\UI-ScrollBar-ScrollDownButton-Disabled"><TexCoords left="0.25" right="0.75" top="0.25" bottom="0.75"/></DisabledTexture>
|
||
<HighlightTexture file="Interface\Buttons\UI-ScrollBar-ScrollDownButton-Highlight" alphaMode="ADD"><TexCoords left="0.25" right="0.75" top="0.25" bottom="0.75"/></HighlightTexture>
|
||
<Scripts><OnClick>BenillaScrollBar_Step(this:GetParent(), 1); PlaySound("UChatScrollButton")</OnClick></Scripts>
|
||
</Button>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnValueChanged>BenillaFauxScrollBar_OnValueChanged(self, arg1)</OnValueChanged>
|
||
</Scripts>
|
||
<ThumbTexture name="$parentThumbTexture" file="Interface\Buttons\UI-ScrollBar-Knob">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<TexCoords left="0.25" right="0.75" top="0.25" bottom="0.75"/>
|
||
</ThumbTexture>
|
||
</Slider>
|
||
|
||
<!--
|
||
The scroll bar's TROUGH — the recessed channel the bar rides in: the "background and
|
||
border" a bare arrows+knob bar is missing. An owner declares one of these BESIDE its bar,
|
||
named `<barname>Trough` and WITH NO ANCHORS — it seats itself on the bar the first time
|
||
BenillaScrollBar_SetShown touches it (BenillaScrollTrough_Seat above, where the socket law
|
||
and the ref citations live). Declare it BEFORE the bar so it draws beneath.
|
||
|
||
THE ART, and why it is sliced this way. `Interface\PaperDollInfoFrame\UI-Character-ScrollBar`
|
||
is a 64x256 sheet holding TWO 31-wide channel pieces, measured texel-exact from the file —
|
||
rails at x 2-5 and 25-30 with a 19-wide interior between them, and each piece bearing a
|
||
16-tall SOCKET for its arrow button:
|
||
· LEFT column (u 0 .. 31/64): rows 0-3 the solid TOP cap, 4-19 the up-arrow socket,
|
||
20-21 the crossbar that closes it, then plain channel from row 22 to the bottom edge.
|
||
· RIGHT column (u 33/64 .. 1): plain channel, then rows 85-86 the crossbar, 87-102 the
|
||
down-arrow socket, 104-106 the solid BOTTOM cap (nothing below 107).
|
||
The ref composes a trough from exactly two quads — the whole left column plus the right
|
||
column's bottom 108 rows — which reaches 362 units and no further, and only works because
|
||
its scroll frames are short enough for the two to OVERLAP. Ours must span a 563-tall page,
|
||
so the same art is cut as a THREE-SLICE instead: the two socket ends at their natural
|
||
heights and a plain-channel slice stretched between them. The middle is constant down its
|
||
whole length, so stretching it is exact — there is no seam to find.
|
||
|
||
The black `Fill` is the ref's own trough backing, not an invention: ItemTextFrame draws a
|
||
0.8-alpha black quad behind its channel art for exactly this (ref-ItemTextFrame.xml's
|
||
`Middle`). It sits in BACKGROUND and the channel art in BORDER — two layers, because within
|
||
ONE layer the client re-sorts overlapping quads by texture handle and their order is not
|
||
ours to depend on (order.rs). The bar's own furniture stays clear above both: button state
|
||
textures are ARTWORK, the knob is OVERLAY.
|
||
-->
|
||
<Frame name="BenillaScrollTroughTemplate" virtual="true" hidden="true">
|
||
<Size><AbsDimension x="31"/></Size>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<!-- The channel interior: x 6..24 of the 31-wide piece, i.e. inset 6 from each edge. -->
|
||
<Texture name="$parentFill">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-6" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Color r="0" g="0" b="0" a="0.8"/>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="BORDER">
|
||
<!-- Cap + up-arrow socket + crossbar: left column, rows 0..22 (v 0 .. 23/256). -->
|
||
<Texture name="$parentTop" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||
<Size><AbsDimension y="23"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"/>
|
||
<Anchor point="TOPRIGHT"/>
|
||
</Anchors>
|
||
<TexCoords left="0" right="0.484375" top="0" bottom="0.08984375"/>
|
||
</Texture>
|
||
<!-- Crossbar + down-arrow socket + cap: right column, rows 82..107
|
||
(v 82/256 .. 108/256) — the mirror of the top's 23, cut to put row 107 on the
|
||
trough's bottom edge exactly as ReputationFrame's own 108/0.421875 does, which
|
||
is what lands the socket on the arrow (BenillaScrollTrough_Seat's law). -->
|
||
<Texture name="$parentBottom" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||
<Size><AbsDimension y="26"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOMLEFT"/>
|
||
<Anchor point="BOTTOMRIGHT"/>
|
||
</Anchors>
|
||
<TexCoords left="0.515625" right="1.0" top="0.3203125" bottom="0.421875"/>
|
||
</Texture>
|
||
<!-- The stretched run between them: left column rows 30..70, plain rails only.
|
||
Declared LAST because it anchors to the two above by name, and an anchor whose
|
||
target is declared later falls back to the parent (ItemTextFrame's own note). -->
|
||
<Texture name="$parentMiddle" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentTop" relativePoint="BOTTOMLEFT"/>
|
||
<Anchor point="BOTTOMRIGHT" relativeTo="$parentBottom" relativePoint="TOPRIGHT"/>
|
||
</Anchors>
|
||
<TexCoords left="0" right="0.484375" top="0.1171875" bottom="0.2734375"/>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
|
||
<!-- ══ Carried here from UiPanels.xml when that file went stock (decision 1988) ══
|
||
Three things our UIParent.lua counterpart held that are not the reference's, seated in
|
||
the earliest file of ours every consumer loads after: the placeholder icon, the tab
|
||
settle, and the window tab template the settle drives. -->
|
||
<Script><![CDATA[
|
||
-- The placeholder icon for an item/spell whose texture has not arrived yet (Craft, Quest,
|
||
-- QuestLog, Trainer and TradeSkill read it). Was ActionBar.xml's (stock since 1938), then
|
||
-- UiPanels.xml's (stock since 1988); this file is the earliest of ours every reader loads
|
||
-- after. A benilla name, so nothing stock reads it.
|
||
BENILLA_FALLBACK_ICON = "Interface\\Icons\\INV_Misc_QuestionMark"
|
||
|
||
-- The tab kit — `PanelTemplates_*` — is the REFERENCE's, off the player's chain
|
||
-- (`Interface\FrameXML\UIPanelTemplates.lua`, seated below this file in the manifest).
|
||
-- Moved here from our UIParent.lua counterpart with 1988, beside the template that calls it.
|
||
-- Our eleven transcribed copies stood here dead from the day that entry landed, and one
|
||
-- of them was carrying a benilla-only return the settle below read (1855/1860). What
|
||
-- stays is that settle, which is ours because our text measure is a frame late and the
|
||
-- reference's is not.
|
||
|
||
-- The tab template's per-frame settle: the ref does TabResize(0) once, in OnShow
|
||
-- (CharacterFrameTemplates.xml l.77-80), trusting a synchronous text measure. Our
|
||
-- GetStringWidth has a frame of latency, so the template's OnUpdate calls this until the
|
||
-- measured width settles (then it's a no-op compare per frame). The ref sizes the hover
|
||
-- highlight alongside it; this kit does not — both tab templates anchor their highlight to
|
||
-- their own side edges, so it is the tab by construction and no settle can desync it
|
||
-- (decision 1004).
|
||
--
|
||
-- The fit PARAMETERS live on the tab (`benillaTabPadding`/`benillaTabMaxWidth`, defaulting
|
||
-- to the +0 fit every window tab has always used) rather than at the call site, because
|
||
-- there is exactly one call site and it is this settle loop: an instance that ran its own
|
||
-- TabResize in OnLoad — as the reference's macro tabs do — would just be overwritten on the
|
||
-- next frame. Setting the two fields IS how an instance asks for the reference's own fit.
|
||
-- THE STRUCTURAL CLAMP — a tab may never grow past its window's drawn right edge, whatever
|
||
-- the instance asked for (decision 1002; the director's "fix this properly to prevent it
|
||
-- ever again"). The reference's own caps are numbers hand-tuned to ITS font: the macro
|
||
-- window's 150 is right for the enUS FRIZQT__ the 1.12 client rasterizes and for nothing
|
||
-- else in particular, and the label it guards carries a CHARACTER NAME — the one string
|
||
-- whose width no window author can know. So the instance's cap is a preference and the
|
||
-- window's edge is the law.
|
||
--
|
||
-- `benillaTabRightInset` is how a window declares how far its DRAWN plate stops short of
|
||
-- its frame rect, because no kit can introspect an art margin. The macro window's is 40:
|
||
-- its 384-unit frame is painted by a 256-wide slice plus a 128-wide one that carries a
|
||
-- transparent tail. Clamping to the frame rect instead — the first attempt — clamped to a
|
||
-- boundary the player cannot see, and the tab still walked out over the world.
|
||
--
|
||
-- The clamp is applied to the RESULT, through TabResize's own `absoluteSize` arm, rather
|
||
-- than folded into `maxWidth`: that parameter is compared against `text + padding` but
|
||
-- assigned as `maxWidth + padding`, so a room budget expressed through it is off by the
|
||
-- padding and silently fails to bind. `absoluteSize` sets the tab width outright, which is
|
||
-- exactly what a room budget is.
|
||
function BenillaTab_Room(tab)
|
||
local parent = tab:GetParent()
|
||
if not parent then return nil end
|
||
local left, right = tab:GetLeft(), parent:GetRight()
|
||
if not left or not right then return nil end
|
||
local room = (right - (tab.benillaTabRightInset or 0)) - left
|
||
if room < 1 then room = 1 end
|
||
return room
|
||
end
|
||
|
||
function BenillaTabButton_OnUpdate(tab)
|
||
local name = tab:GetName()
|
||
local text = getglobal(name .. "Text")
|
||
local w = (text and text:GetStringWidth()) or 0
|
||
if w <= 0 then return end
|
||
local pad = tab.benillaTabPadding or 0
|
||
local room = BenillaTab_Room(tab)
|
||
if tab.benillaFitWidth == w and tab.benillaFitRoom == room then return end
|
||
tab.benillaFitWidth, tab.benillaFitRoom = w, room
|
||
PanelTemplates_TabResize(pad, tab, nil, tab.benillaTabMaxWidth)
|
||
-- THE CLAMP READS THE FIT BACK OFF THE ART, not off a return value and not off the
|
||
-- button. `PanelTemplates_TabResize` is the REFERENCE's now (1837 put
|
||
-- `UIPanelTemplates.lua` on the chain below this file, so its definition wins) and it
|
||
-- returns nothing; our own copy's benilla-only `return tabWidth` had already stopped
|
||
-- running, which is what took 1002's clamp off the air silently. `tab:GetWidth()`
|
||
-- cannot serve either — it reads the last RESOLVED rect, this frame's stale input
|
||
-- inside a settle, which is the whole reason 1002 added that return.
|
||
--
|
||
-- `$parentMiddle` can. `PanelTemplates_TabResize` sets it on its way out, a Texture's
|
||
-- width is its AUTHORED size, and a region's GetWidth reports the authored value
|
||
-- without consulting a resolved rect (region::virtual_span) — so it is the number the
|
||
-- verb just computed, same tick. The tab is that plus its two end caps, which is the
|
||
-- reference's own `tabWidth = width + sideWidths`.
|
||
if room then
|
||
local middle = getglobal(name .. "Middle")
|
||
local left = getglobal(name .. "Left")
|
||
local fitted = (middle and middle:GetWidth() or 0)
|
||
+ 2 * (left and left:GetWidth() or 0)
|
||
if fitted > room then
|
||
PanelTemplates_TabResize(pad, tab, room)
|
||
end
|
||
end
|
||
-- No highlight sizing here on purpose — BOTH tab templates anchor their highlight to
|
||
-- their own two side edges, so it IS the tab by construction (decision 1004).
|
||
end
|
||
]]></Script>
|
||
|
||
<!--
|
||
The window tab button (ref CharacterFrameTabButtonTemplate — Interface\FrameXML\
|
||
CharacterFrameTemplates.xml in patch.MPQ, NOT the base FrameXML.toc set; extracted via
|
||
mpqx, transcribed VERBATIM: 115x32, the Active/InActive three-slice pair with the ref's
|
||
exact TexCoords, text CENTER +2, the ADD highlight inset 10px). "Disabled" IS "selected"
|
||
here — PanelTemplates_SelectTab (script kit above) disables the button and shows the
|
||
*Disabled (= Active) slices. The ref's OnShow text-fit (PanelTemplates_TabResize(0)) runs
|
||
from OnUpdate here instead — BenillaTabButton_OnUpdate settles once the async text measure
|
||
lands, then no-ops (the ref's C measure is synchronous; the 115px XML width is just the
|
||
pre-fit state). Its OnShow *also* sets the highlight width, which is dead code in the
|
||
reference and dropped here: this highlight is anchored LEFT/RIGHT, and two opposing anchors
|
||
resolve the size from the span without consulting the width at all (wow-re layout.md,
|
||
VERIFIED). The small tab now says the same thing the same way (1004).
|
||
|
||
**The OnLoad raise and the OnClick are the reference's own, and both were dropped here
|
||
until decision 1810.** The note that stood in their place said the raise was unnecessary —
|
||
"the tabs sit below the window art's hit-rect so nothing overlaps them" — and that "OnClick
|
||
is the instance's to define". Both were true of our own CharacterFrame.xml, and both are
|
||
false of the reference's, which is the file that runs now:
|
||
|
||
- the reference's `PaperDollFrame` is `setAllPoints enableMouse="true"` with a
|
||
`bottom=45` hit inset, and the reference's tabs are anchored INSIDE the window (CENTER
|
||
to BOTTOMLEFT +60,+62), so the page's hit rect covers the whole row. Frame levels come
|
||
out CharacterFrame 1 / PaperDollFrame 2 / tabs 2, later-created winning — the page eats
|
||
every tab click. `SetFrameLevel(+4)` is what lifts the row out, and is why the
|
||
reference has it.
|
||
- stock `CharacterFrame.xml` gives its five tabs only `<OnEnter>`/`<OnLeave>`; the
|
||
dispatch lives HERE. Without it `CharacterFrameTab4:GetScript("OnClick")` is nil and
|
||
the character window's tabs do nothing at all.
|
||
|
||
The body hardcodes `CharacterFrame`, which reads odd on a shared template until you check
|
||
the other consumers: the reference's `MerchantFrameTab1/2` and every one of ours
|
||
(Auction/Friends/Inspect/Mail/Talent) declares its own `<OnClick>`, and an instance's
|
||
handler REPLACES the inherited one rather than adding to it. So this reaches exactly the
|
||
window it names, which is the reference's own arrangement.
|
||
|
||
Still ours (moved here from UiPanels.xml with 1988 — that file is the reference's
|
||
UIParent.lua now), and still why this template is not simply sourced off the chain:
|
||
`Interface\FrameXML\CharacterFrameTemplates.xml` fits its text in `<OnShow>` through
|
||
`PanelTemplates_TabResize(0)`, which needs the reference's SYNCHRONOUS string measure. Ours
|
||
lands a frame late, so the fit runs from `BenillaTabButton_OnUpdate` instead and an
|
||
OnShow-only fit would leave every tab at its pre-fit 115px. That divergence is 1004's, it
|
||
spans six windows, and it is what this template's own migration waits on.
|
||
-->
|
||
<Button name="CharacterFrameTabButtonTemplate" virtual="true">
|
||
<Size><AbsDimension x="115" y="32"/></Size>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentLeftDisabled" file="Interface\PaperDollInfoFrame\UI-Character-ActiveTab">
|
||
<Size><AbsDimension x="20" y="32"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="5"/></Offset></Anchor></Anchors>
|
||
<TexCoords left="0" right="0.15625" top="0" bottom="1.0"/>
|
||
</Texture>
|
||
<Texture name="$parentMiddleDisabled" file="Interface\PaperDollInfoFrame\UI-Character-ActiveTab">
|
||
<Size><AbsDimension x="88" y="32"/></Size>
|
||
<Anchors><Anchor point="LEFT" relativeTo="$parentLeftDisabled" relativePoint="RIGHT"/></Anchors>
|
||
<TexCoords left="0.15625" right="0.84375" top="0" bottom="1.0"/>
|
||
</Texture>
|
||
<Texture name="$parentRightDisabled" file="Interface\PaperDollInfoFrame\UI-Character-ActiveTab">
|
||
<Size><AbsDimension x="20" y="32"/></Size>
|
||
<Anchors><Anchor point="LEFT" relativeTo="$parentMiddleDisabled" relativePoint="RIGHT"/></Anchors>
|
||
<TexCoords left="0.84375" right="1.0" top="0" bottom="1.0"/>
|
||
</Texture>
|
||
<Texture name="$parentLeft" file="Interface\PaperDollInfoFrame\UI-Character-InActiveTab">
|
||
<Size><AbsDimension x="20" y="32"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||
<TexCoords left="0" right="0.15625" top="0" bottom="1.0"/>
|
||
</Texture>
|
||
<Texture name="$parentMiddle" file="Interface\PaperDollInfoFrame\UI-Character-InActiveTab">
|
||
<Size><AbsDimension x="88" y="32"/></Size>
|
||
<Anchors><Anchor point="LEFT" relativeTo="$parentLeft" relativePoint="RIGHT"/></Anchors>
|
||
<TexCoords left="0.15625" right="0.84375" top="0" bottom="1.0"/>
|
||
</Texture>
|
||
<Texture name="$parentRight" file="Interface\PaperDollInfoFrame\UI-Character-InActiveTab">
|
||
<Size><AbsDimension x="20" y="32"/></Size>
|
||
<Anchors><Anchor point="LEFT" relativeTo="$parentMiddle" relativePoint="RIGHT"/></Anchors>
|
||
<TexCoords left="0.84375" right="1.0" top="0" bottom="1.0"/>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<ButtonText name="$parentText">
|
||
<Anchors><Anchor point="CENTER"><Offset><AbsDimension x="0" y="2"/></Offset></Anchor></Anchors>
|
||
</ButtonText>
|
||
<NormalFont inherits="GameFontNormalSmall"/>
|
||
<HighlightFont inherits="GameFontHighlightSmall"/>
|
||
<DisabledFont inherits="GameFontHighlightSmall"/>
|
||
<HighlightTexture name="$parentHighlightTexture" file="Interface\PaperDollInfoFrame\UI-Character-Tab-Highlight" alphaMode="ADD">
|
||
<Anchors>
|
||
<Anchor point="LEFT"><Offset><AbsDimension x="10" y="2"/></Offset></Anchor>
|
||
<Anchor point="RIGHT"><Offset><AbsDimension x="-10" y="2"/></Offset></Anchor>
|
||
</Anchors>
|
||
</HighlightTexture>
|
||
<Scripts>
|
||
<!-- ref CharacterFrameTemplates.xml:69-81, verbatim. -->
|
||
<OnLoad>this:SetFrameLevel(this:GetFrameLevel() + 4)</OnLoad>
|
||
<OnClick>
|
||
PanelTemplates_Tab_OnClick(CharacterFrame)
|
||
CharacterFrameTab_OnClick()
|
||
</OnClick>
|
||
<OnUpdate>BenillaTabButton_OnUpdate(self)</OnUpdate>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<!-- `FauxScrollFrameTemplate` and the `FauxScrollFrame_*`/`ScrollFrame_*`/`ScrollingEdit_*`
|
||
API are the REFERENCE's, off the player's chain (`Interface\FrameXML\UIPanelTemplates`,
|
||
seated below this file — so its declarations win). Our transcriptions stood here dead
|
||
from the day that entry landed and are gone (1860). What stays is
|
||
`BenillaScrollBarTemplate` + `BenillaScrollTroughTemplate` and their helpers, which are
|
||
ours: six windows inherit the bar directly and the reference has no trough at all. -->
|
||
</Ui>
|