The sheet is read against the world and nothing else #54

Manually merged
mdoorn merged 6 commits from feat/054-the-sheet-is-read-against-the-world-and-nothin into master 2026-08-07 14:54:49 +02:00
Owner

The sheet is read against the world and nothing else

Along the way:

  • A pin is ours, and the client's five slots are left alone
  • A tracker of our own, off the log we already read
  • The options window stops counting its own characters
  • The player and target blocks get out from under the sheet
  • The pin is drawn where you can see it

Touches 28 files: docs/CHANGELOG.md, scripts/harness/client/12-questlog.lua, scripts/harness/runner.lua, scripts/harness/sections/16-options-window.lua, scripts/harness/sections/47-quest-log.lua, scripts/harness/sections/47-quest-pin.lua and 22 more.

The sheet is read against the world and nothing else Along the way: - A pin is ours, and the client's five slots are left alone - A tracker of our own, off the log we already read - The options window stops counting its own characters - The player and target blocks get out from under the sheet - The pin is drawn where you can see it Touches 28 files: `docs/CHANGELOG.md`, `scripts/harness/client/12-questlog.lua`, `scripts/harness/runner.lua`, `scripts/harness/sections/16-options-window.lua`, `scripts/harness/sections/47-quest-log.lua`, `scripts/harness/sections/47-quest-pin.lua` and 22 more.
jlindqvist approved these changes 2026-08-06 09:00:34 +02:00
jlindqvist left a comment

I mostly read this one to learn the secure handler pattern. One question, which may be naive.

I mostly read this one to learn the secure handler pattern. One question, which may be naive.
@ -0,0 +110,4 @@
local function Guard()
if not guard then
guard = CreateFrame("Frame", "WarriorKitHushGuard", UIParent,
"SecureHandlerAttributeTemplate")
Owner

A named global frame on a secure attribute template, made once and kept. Does the name have to be global for the snippet to find it, or is that only for the harness?

A named global frame on a secure attribute template, made once and kept. Does the name have to be global for the snippet to find it, or is that only for the harness?
Author
Owner

Not naive. The name is for the harness and for anyone typing /framestack; the snippet reaches it through the attribute rather than by name. Said so above the call.

Not naive. The name is for the harness and for anyone typing `/framestack`; the snippet reaches it through the attribute rather than by name. Said so above the call.
The character sheet had two things drawn over it and neither was noticed
because both are the addon's own. A screen window sat at BACKGROUND, which
was right while it was a backdrop over the whole monitor and wrong the moment
it became half of one: every rectangle this addon puts on the world sits at
MEDIUM or HIGH, so the buff nag, the cooldowns, the swing bars, the meters,
the standing row and both feeds drew over nineteen item names and a column of
numbers. The wash UI/Window.lua lays behind the sheet could do nothing about
any of them, because a cooldown row is a frame over the world rather than part
of it.

Two answers, because a screen window needs both. A screen window is HIGH now,
over everything the addon draws on the world and under DIALOG, which is every
window the player opens; toplevel stays off, so a click on a gear square still
cannot lift the sheet over the bags. That covers what nothing else owns,
including the client's own frames.

UI/Hush.lua is the other half. The seven rows are re-parented into one frame
at build, and the frame is shut for as long as a screen window is up. Attic's
mechanism for Attic's reason: every one of those rows is driven by a ticker
that shows and hides it as the fight goes, so a Hide from outside is undone on
the next tick and a hidden parent is not. The row's own flag is never written,
which is what keeps /wk status honest and what puts the HUD back in the state
it went away in. The tickers all live on separate event frames already, so
nothing in there goes stale. Nothing secure is registered: re-parenting is
refused mid fight on a protected frame, and the sheet is opened in a fight by a
snippet.

Off is a tick box on the character page, next to the wash and shaped like it:
UI/Window.lua takes a getter rather than a setting name, and Window:Quiet
reaches a sheet that is already up so the box answers on the click. Off still
leaves the sheet over all seven, because the strata answer holds on its own.

52-screen-hush.lua reads the four things that fail silently: every named row
that a run built is in the room, the room shuts and opens with the sheet, the
rows' own shown flags survive it, and a raw Hide on the frame, which is what
Escape and the key do, brings them back. Seven rows on a warrior.

52-stats-page.lua is the miss maths out of 52-character.lua, which has been 35
lines over its own ceiling since the sheet was resized and was failing the
harness budget on master. The window and the arithmetic are two subjects: none
of those numbers moves when the sheet is dragged, opened in a fight or closed,
and the three helpers the block used are read nowhere else. That leaves the
file at 727 lines, under the 800 every section shares, so the allow-list entry
is gone rather than lowered.
The quest log's track button wrote AddQuestWatch, which stops at five. Questie
replaces GetNumQuestWatches outright to get out from under that cap, and a
second addon fighting the client over the same five slots is not a fight worth
entering for a list this addon can keep itself. So the pin is ours now: shift
left click a row of the left column pins the quest, the footer button says pin
and unpin, and both go through ns.QuestLog.Pin.

The store is ns.dbc.questPins, this character's, uncapped, a list of quest keys
in the order they were pinned. Keyed on the quest rather than on its index for
the reason at the head of Quests/Log.lua: a turn-in moves every row under it,
and a pin held as a position comes back on the neighbour. A pin whose quest has
left the log is dropped, on a read that found a log and never on the empty
answer a client gives mid-loading-screen. Client.Watch and Client.Watched stay
on Quests/Client.lua unused, so writing the client's list again is one line.

The gesture is UI.List's Press, which fires on the row already selected, so it
works on the quest you are reading. It reads which button and which modifier,
both nil on every move the code makes, so no repaint can pin anything. The
harness holds that with shift held down across two paints.

The read side is ns.QuestLog.Pins(): the pinned quests that are in the log,
oldest pin first. Item 96's group above the zones and item 91's tracker both
draw that and neither needs the saved table. Nothing draws a pin yet, and the
row field is quest.pinned where it was quest.watched.

The panel says what the trade costs, on the reading of your log, which now
counts the pins: Questie's map icons can be filtered to tracked quests and a
pin does not reach that filter. The hint fits inside the options window's
31,000 character prose budget at 30,992, so no ceiling moved.

47-quest-log.lua is at its allow-listed line ceiling, so the pin has a section
of its own under it. It asserts the gesture through the row's own button, the
pin surviving a turn-in that moves every index, six pins where the client takes
five, the order they come back in, and that AddQuestWatch has not been called
once in the whole run.
There was one quest tracker on the screen and it belonged to Questie, so the box
that switched it off left nothing in its place. Quests/Column.lua is the column
that goes there: a placeable frame over the world with the quests the client
filed under the place you are standing in on it, each name with its objectives
under it and a gold bar down the left of the ones you have pinned. Clicking a
row is Quests/Tracker.lua's existing swap arriving from this addon's own frame
instead of Questie's, which is one call and no second path into the window.

It draws from ns.QuestLog's zones and nothing else. Log.Zones is the one door
and Log.Objectives is the one thing added beside it, which asks for a quest's
objective lines without the text and the rewards Detail reads for the window's
other two columns. A tracker with a reading of the client's log of its own would
be the sheet and the loot feed again: two pictures of one thing, drifting apart
in the details nobody looks at until they are side by side. The read is taken
here as well, because the window reads only while it is showing and the tracker
is up while the window is shut, but both drawings come off the zones that one
read leaves behind.

The scope is zone.name against ns.QuestHere.Now().name. It is the only name
comparison in the addon and it is safe for one reason: both strings are this
client's own, in the player's language, one off the log header and one off
C_Map. Nothing else is decided on a name and Core/Here.lua argues at length why.
The header is a sort category rather than a place, which item 93 is about and
this is not; nothing here calls Where.Nearest, which item 99 gates at its one
caller.

One switch and one tracker. ns.QuestTrackerOff.Wanted is what this follows
rather than questsHideBlizz, so the box that takes Questie's tracker off the
screen is the box that puts this one up and there is never a moment with two.
The only setting it owns is where it sits. Nothing runs on a ticker: the log
changes on the client's own events and this redraws on those, and Questie's
quest updates are deliberately not a second source here, because nothing this
column draws is Questie's answer.

For the items after it: item 96's pinned group appends in Column.Quests, which
is where the zone's quests are gathered, and the mark it draws is already there,
read through the one-line Pinned. Item 98's line goes on the foot of the stack
in Fill, after the quests and before the two Spare calls that put unused rows
away. Item 95 renames the field Pinned reads and touches nothing else here.

Section 85-quest-column walks you through Westfall, Elwynn Forest and Stormwind
City, empties the client's log and puts it back, and counts the rows off the
frames rather than off the model, because a scope that answered correctly and a
paint that drew the last zone's rows would pass every assertion made against the
list. The options window's prose budget does not move: no page arrived, the
tracker rides the switch that was already there, and what it cost is twenty two
characters of clause on that hint, to 30,830 against 31,000.
The harness added up every lede and hint in the options window and failed the
build when the total passed a number. It was 16,000 across forty-two sections
when it went in and 31,000 when it came out, raised eight times, and every raise
in the comment above it ends with the same phrase: that measurement plus a
hint's worth of room again. A ceiling redefined as slightly more than whatever
was just written is not a gate. It is a log of what the window weighed on eight
particular days, kept in a place that fails builds.

The comment block admitted the counter was wrong twice over. Once at the zoom
rows, where twenty three hints replaced twenty three permanent readings: what a
player reads went down and the counted number went up, because a hint is read
one at a time on the row you hovered and a reading is on the screen whether you
wanted it or not. And once about itself, saying that separating the two is a
change to what the budget means and should not be made in the commit that would
benefit from it. Nobody made it in any other commit either.

The two caps that survive bound something a person meets: a lede at 160
characters and a hint at 200, each measured against the one string on the one
row. Those stay exactly as they were. What goes is the window-wide sum, the
dedupe table behind it, the field on the summary print, and the two hundred and
sixty lines of comment arguing about where the number should sit.

Each menu page gets tuned on its own when the addon is done, which is a reading
of one page at a time and not an arithmetic total across all of them.
The sheet is pinned to an edge of the monitor and the two frames most
likely to be under it were the two the hush could not touch. Each one is
an anchor of ours with a secure unit button inside it: it cannot be
reparented into the room the rows go into, and it cannot be hidden from
Lua in a fight, which is the fight the sheet is most often opened in.

So they go by snippet. UI/Hush.lua grows a guard built from
SecureHandlerAttributeTemplate, holding a frame reference per block; the
Lua side writes the flag and bumps a turn, and the snippet hides or shows
what it was handed. That is the same route UI/Placeable.lua takes to move
the sheet itself while you are being hit. The anchor is what the snippet
touches and never the button, so the client's unit watch keeps its own
answer the way a row keeps its own shown flag.

One switch still, not two: the tick box that puts the addon's rows away
puts these away with them.
A pin was a fact the addon kept and nowhere a player could read. The store, the
gesture and the tracker's gold bar all landed with item 95 and item 91, and the
quest window's left column drew none of it: the only way to find out which
quests you had pinned was to click each one and read the footer button.

Two drawings now, both off ns.QuestLog.Pins(). Window.Rows puts the pinned
quests in a group headed Pinned above the zones, in pin order, and draws the
group only where something is in it. The same quests still draw under their own
zones, so pinning one does not move it, and the duplicate rows carry the same
id, which is what keeps both copies of the quest you are reading lit. On the
row, Mark and MarkTint answer a gold "o" for a pinned quest, third behind the
tick and the exclamation, because one glyph column cannot say three things and
the group says the pin for every pinned quest whatever its state.

Column.Quests appends the pins that are not already under your feet, after the
zone's own quests rather than before them, which is the exception to the
tracker's zone scope that the pin exists to buy. Column.Describe counts the two
halves separately and adds ", and one pinned elsewhere", because one number over
both would say three quests in Westfall about a column with one Westfall quest
on it. Item 98's line still goes at the foot of Fill and the pools it takes a
row from have not moved. quests.WatchCalls() is still 0.
mdoorn manually merged commit c51908185c into master 2026-08-07 14:54:49 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
northgale/warriorkit!54
No description provided.