28 KiB
Retained widget foundations -- named-retail pseudocode
Date: 2026-07-10
Scope: Wave 0 oracle for UIElement_Text, UIElement_Button, raw edge
layout, UI time delivery, tooltip cancellation, and focus ordering. This note
changes no runtime behavior.
Sources and confidence
Primary evidence:
docs/research/named-retail/acclient_2013_pseudo_c.txt, the Sept 2013 EoR PDB-named client decompilation.docs/research/named-retail/acclient.h, especiallyUIElement_Text::UIText_Flag,UIElement_Text,UIElement_Button,UIElementManager,ElementDesc, andBox2D.- Static x86 disassembly of the matching
C:\Turbine\Asheron's Call\acclient.exefor the expressions that both Binary Ninja and Ghidra lost around x87_ftol2calls inUIElement::UpdateForParentSizeChange.
Cross-checks:
docs/research/2026-06-15-layoutdesc-format.mdand the production LayoutDesc data it records. Its property observations agree with the named code, but itsAnchorEdgesreduction is intentionally lossy and its text-property table has two margin fields transposed; the corrections are listed below.docs/research/retail-ui/02-class-hierarchy.mdanddocs/research/retail-ui/04-input-events.md, the olderFUN_xxxresearch. These are useful as provenance but are superseded where they conflict with the PDB-named client. In particular, the named client contains the fullUIElement*hierarchy and does not implement the inferred genericDevice::RegisterTimerEventqueue described there.- The only vendored reference tree present in this worktree is WorldBuilder; it does not implement the retail retained-widget system. No independent ACE, AC2D, ACViewer, or holtburger UI implementation was available here to corroborate these client-internal mechanics.
Confidence is high for every branch below unless it is explicitly marked
uncertain. Addresses were checked against symbols.json or the named function
header in the pseudo-C.
Corrections that affect the implementation plan
- Retail text interactivity is two orthogonal capabilities, not a
three-value exclusive mode.
Editable(0x16) andSelectable(0x27) can be independently false/true. An editable, non-selectable field accepts edits but does not begin drag selection. The modern representation should therefore be flags or two booleans;Displaymeans both are false. - A newly constructed Type-12 text starts with
m_bitField = 0x300(DIRTY | CURSOR_VISIBLE), not editable and not selectable. Imported labels must not be made interactive merely because their resolved type is 12. - There is no evidence for the old generic Device timer queue in this build.
Retail time-driven UI is a per-frame global message (
3) plus explicit deadline polling byUIElementManager::CheckTooltip. The unpumpedUiRoot.RegisterTimerEventlist is not a faithful retail mechanism. - Raw edge modes
3and4cannot be reduced toAnchorEdges:3performs exact inclusive-pixel centering and4proportionally scales the individual edge coordinate with truncation toward zero. - In the older LayoutDesc note, the exact text margins should be
0x23=left,0x24=right,0x25=upper,0x26=lower, as shown byUIElement_Text::OnSetAttribute @ 0x0046A640.
1. UIElement_Text
1.1 Flag model and defaults
acclient.h defines the exact flags:
| Flag | Value | Meaning |
|---|---|---|
UITF_EDITABLE |
0x0001 |
Editing/input capability |
UITF_ONE_LINE |
0x0002 |
One-line layout |
UITF_SELECTABLE |
0x0004 |
Selection/copy capability |
UITF_NO_IME |
0x0008 |
Do not enable IME |
UITF_OUTLINE |
0x0010 |
Outline glyphs |
UITF_DROPSHADOW |
0x0020 |
Drop shadow glyphs |
UITF_MOUSE_SELECTING |
0x0040 |
Pointer drag is active |
UITF_SELECTING |
0x0080 |
Selection anchor/end are active |
UITF_DIRTY |
0x0100 |
Glyph/layout cache dirty |
UITF_CURSOR_VISIBLE |
0x0200 |
Caret blink phase visible |
UITF_FIT_TO_TEXT |
0x0400 |
Resize element to paper |
UITF_TRUNCATE_TEXT_TO_FIT |
0x0800 |
Trailer/truncation path |
UITF_LOSE_FOCUS_ON_ESCAPE |
0x1000 |
Escape relinquishes focus |
UITF_LOSE_FOCUS_ON_ACCEPT_INPUT |
0x2000 |
Accept-input action relinquishes focus |
UIElement_Text::UIElement_Text @ 0x00468570 initializes the flag word to
0x300, selection/cursor positions to zero, horizontal justification to 2,
vertical justification to 4, margins to zero, and cursor/flash timestamps to
-1.0. Thus the retail default is a display-only label.
1.2 Interaction-relevant property IDs
UIElement_Text::OnSetAttribute @ 0x0046A640 first delegates to
UIElement_Scrollable::OnSetAttribute, then handles these properties:
| Property | Value kind | Exact effect |
|---|---|---|
0x14 |
enum | Horizontal justification |
0x15 |
enum | Vertical justification |
0x16 |
bool | SetEditable; then recompute mouse visibility |
0x17 |
StringInfo |
Set contents, or clear when invalid |
0x1A |
array/data-id | Re-resolve current font |
0x1B |
array/color | Re-resolve current font color |
0x1D |
array/color | Re-resolve tag font color |
0x1E |
integer | Maximum characters |
0x1F |
bool | SetNoIme |
0x20 |
bool | SetOneLine |
0x21 |
bool | SetOutline |
0x22 |
color | Outline color; then font reset |
0x23 |
integer | Left margin |
0x24 |
integer | Right margin |
0x25 |
integer | Upper margin |
0x26 |
integer | Lower margin |
0x27 |
bool | SetSelectable; then recompute mouse visibility |
0x28 |
bool | Glyph list TrimFromTop |
0x29 |
bool | SetFitToText |
0xC7 |
StringInfo |
Enable/define truncation trailer; invalid disables it |
0xCB |
bool | Lose focus on Escape |
0xCC |
bool | Lose focus on accept-input |
1.3 Capability setters
Sources: SetEditable @ 0x004680D0, SetOneLine @ 0x00468170, and
SetSelectable @ 0x004681C0.
SetEditable(value):
if value == current Editable: return
if value is false:
if this has global focus AND Selectable is false:
RelinquishFocus()
else if TruncateTextToFit is true:
clear TruncateTextToFit
mark dirty and dirty the root
set Editable = value
SetSelectable(value):
if value == current Selectable: return
if value is false:
if this has global focus AND Editable is false:
RelinquishFocus()
else if TruncateTextToFit is true:
clear TruncateTextToFit
mark dirty and dirty the root
set Selectable = value
if the internal selection-direction marker is active:
clear selecting/mouse-selecting and zero both selection endpoints
SetOneLine(value):
if value differs:
set OneLine = value
mark dirty and dirty the root
The high internal selection-direction bit checked by SetSelectable and
SetCursorPosition is not named in UIText_Flag; it is deliberately not given
a speculative public name here.
SetTruncateTextToFit @ 0x00467480 refuses to enable truncation whenever
either Editable or Selectable is set.
1.4 Mouse visibility
Sources: UIElement_Text::GetShouldBeMouseVisible @ 0x00467460 and
UIElement::GetShouldBeMouseVisible @ 0x004601B0.
Text.GetShouldBeMouseVisible():
if Editable OR Selectable:
return true
return base.GetShouldBeMouseVisible()
Base.GetShouldBeMouseVisible():
return explicit mouse-visible/interaction flag OR valid tooltip text
Consequently, a plain imported label does not claim the mouse unless another base property (for example a tooltip/explicit mouse role) says it should.
1.5 Mouse down, selection, and activation
Sources: MouseDown @ 0x00469370, MouseUp @ 0x004694F0, and
MouseMove @ 0x004695F0.
MouseDown(screenX, screenY, mouseAction):
remember whether this already had focus
call Scrollable.MouseDown(...)
if hidden/blocked base flags reject input: return
if mouseAction is not one of the accepted text actions (5, 6, 7): return
if the base action-enable bit is false: return
local = screen point - screen origin - margins
if mouseAction == 7 AND (Editable OR Selectable):
read bool property 0xD1
if this did not previously have focus AND property 0xD1 is true:
SelectAll()
return
hitPos = DeterminePositionFromXY(local)
previousAnchor = selectionStart when the internal selection direction
is active, otherwise current cursor
SetCursorPosition(hitPos, DontSelectText)
Activate() // activate the owning root
if Selectable:
set Selecting and MouseSelecting
if Shift is down:
selectionStart = previousAnchor
selectionEnd = cursor
else:
selectionStart = cursor
MouseMove(screenX, screenY):
if not MouseSelecting:
call base MouseMove and return
local = screen point - screen origin - margins
SetCursorPosition(DeterminePositionFromXY(local), Default)
MouseUp(screenX, screenY, mouseAction):
remember whether a glyph callback is registered for mouseAction
call base MouseUp(...)
if MouseSelecting:
set cursor from release point using Default
clear MouseSelecting
if a glyph callback was registered:
glyph = glyph at release point
if glyph has a callback: invoke it with mouseAction
Important boundary: Editable alone allows caret placement and root
activation, but only Selectable starts pointer selection. The method calls
Activate, not TakeFocus, so the focus transition remains governed by the
root's remembered focus element and explicit TakeFocus requests.
1.6 Focus registration and key capabilities
Sources: RegisterInputMaps @ 0x00467350, UnregisterInputMaps @ 0x004673D0, ListenToElementMessage @ 0x004687C0, and OnAction @ 0x0046A260.
RegisterInputMaps(priority):
unregister this callback first
result = Scrollable.RegisterInputMaps(priority)
if Editable:
register input map 1 at priority - 10
register input map 7 at priority
if Editable OR Selectable:
register input map 8 at priority
return OR of registration results
On focus message 0x2F for this element:
if neither Editable nor Selectable: delegate to base
if Editable:
on gain:
register as ICIDM input handler
turn IME on unless disabled
ask Keystone to lose its external focus
on loss:
unregister ICIDM input handler
on gain:
ICIDM.SetTextMode(true)
subscribe to global message 3
on loss:
Deselect()
ICIDM.SetTextMode(false)
unsubscribe from global message 3
delegate to Scrollable.ListenToElementMessage
Map 8 exposes cursor movement, line/document home/end, up/down/page movement,
and Copy for both editable and selectable text. Editable-only maps add character
editing. Cut, Paste, and deletion helpers independently guard on
UITF_EDITABLE, so merely selectable text cannot mutate its contents.
The action at 0x25 relinquishes focus when 0xCC is set; the action at
0x27 relinquishes focus when 0xCB is set. Actions 0x26 and 0x28 are
forward-delete and backspace behavior respectively, deleting the active
selection first.
1.7 Per-frame caret work
UIElement_Text::Global_Loop @ 0x00469FC0 runs on global message 3.
It checks asynchronous string downloads, then, only while
(Editable || Selectable) && HasFocus, uses GetCaretBlinkTime() converted
from milliseconds to seconds to flip UITF_CURSOR_VISIBLE and dirty the last
caret rectangle. This is subscription-driven; it is not a registered timer
object.
2. UIElement_Button
2.1 State inputs and numeric states
Source: UIElement_Button::UpdateState_ @ 0x00471CF0, verified against
the matching x86 instructions.
The method reads these bool properties:
0x0D: ghosted/disabled role;0x0E: selected/toggled role;0x13: allow rollover-state change role.
It combines them with mousePressedOnButton and the inherited
mouseOverTop bit. The standard state IDs are:
| Visual phase | Unselected | Selected |
|---|---|---|
| Normal | 1 |
6 |
| Rollover / pressed outside | 2 |
7 |
| Pressed while pointer is over | 3 |
8 |
| Ghosted | 13 |
13 |
UpdateState_():
disabled = bool property 0x0D
if disabled:
requested = 13
else:
selected = bool property 0x0E
rolloverEnabled = bool property 0x13
requested = selected ? 6 : 1
if mousePressedOnButton OR (rolloverEnabled AND mouseOverTop):
requested = selected ? 7 : 2
if mousePressedOnButton AND mouseOverTop:
requested = selected ? 8 : 3
if ElementDesc contains requested state:
virtual SetState(requested)
else:
leave the current state unchanged
That last test is the exact fallback rule. There is no search for a nearest standard state. A custom semantic state survives only when the newly requested standard state is absent from the element's DAT state table.
2.2 SetState synchronization
Source: UIElement_Button::SetState @ 0x00471DA0.
SetState(requested):
if bool property 0x0B is true: // toggle behavior
selected = bool property 0x0E
if requested == 6 AND not selected:
set property 0x0E = true
return true
if requested == 1 AND selected:
set property 0x0E = false
return true
wantsDisabled = requested == 13
disabled = bool property 0x0D
if wantsDisabled != disabled:
set property 0x0D = wantsDisabled
return true
return UIElement.SetState(requested)
Thus programmatic SetState(1/6) is also the selected-property API for a
toggle button, and SetState(13) is the disabled-property API. Property
changes call UpdateState_ through OnSetAttribute.
2.3 Mouse state machine
Sources: MouseDown @ 0x00471FF0, MouseUp @ 0x004720E0, and
MouseOverTop @ 0x004721F0.
MouseOverTop(isOver):
base.MouseOverTop(isOver)
UpdateState_()
MouseDown(x, y, mouseAction):
Text.MouseDown(...)
if inherited visibility/input gates reject: return
if mouseAction is neither 7 nor accepted alternate action 0x0A: return
mousePressedOnButton = true
UpdateState_()
disabled = bool property 0x0D
allowHotClickWhileDisabled = bool property 0x0C
if disabled AND not allowHotClickWhileDisabled: return
if bool property 0x0F: // hot-click/auto-repeat
BroadcastElementMessage(2, mouseAction, 0) // immediate activation
hotClickingInProgress = true
subscribe to global message 3
nextHotClickTime = Timer.cur_time + float property 0x10
MouseUp(x, y, mouseAction):
Text.MouseUp(...)
disabled = bool property 0x0D
if mousePressedOnButton AND mouseAction is 7 or 0x0A:
emitClick = false
if mouseOverTop AND not disabled:
if bool property 0x0B: // toggle
property 0x0E = !property 0x0E
if not hotClickingInProgress:
emitClick = true
mousePressedOnButton = false
if hotClickingInProgress:
hotClickingInProgress = false
unsubscribe from global message 3
UpdateState_()
if emitClick:
BroadcastElementMessage(1, 7, 0)
Release outside never emits message 1. During a captured press, moving outside
changes state 3/8 -> 2/7; moving back inside restores 3/8. A hot-click press
emits message 2 immediately and suppresses the ordinary release click.
2.4 Hot-click deadline delivery and cancellation
Sources: ListenToGlobalMessage @ 0x00471C60, OnSetAttribute @ 0x00471F40, and the button destructor at 0x00471BC0.
On global message 3 while hotClickingInProgress:
now = Timer.cur_time
if not mouseOverTop AND now >= nextHotClickTime:
nextHotClickTime = now
if mouseOverTop AND now >= nextHotClickTime:
BroadcastElementMessage(2, 0, 0)
nextHotClickTime += float property 0x11
Cancellation is explicit and idempotent at all three owner transitions:
- mouse up clears
hotClickingInProgressand unsubscribes from global3; - changing bool property
0x0Fto false does the same; - destruction does the same if a hot click is active.
OnSetAttribute recalculates visual state for 0x0D, 0x0E, and 0x13;
0x0D also recomputes mouse visibility.
The descriptive names for button properties 0x0B..0x13 are not present in
the recovered headers. Their behavioral roles above are exact; do not bake the
descriptive labels into a wire/DAT enum until the master property-name table is
recovered.
3. UIElement::UpdateForParentSizeChange
Oracle: UIElement::UpdateForParentSizeChange @ 0x00462640.
Box2D is inclusive: width is x1 - x0 + 1, height is
y1 - y0 + 1. For a root/no-parent element (or the relevant root-sizing flag),
the original parent box is [0,0,layout.displayWidth-1,layout.displayHeight-1]
and the current parent box is [0,0,displayWidth-1,displayHeight-1].
Otherwise, both boxes come from the parent.
orig = this.GetOriginalPosition()
origParent = parent.GetOriginalPosition() or layout display box
curParent = parent.GetCurrentPosition() or current display box
origParentWidth = origParent.x1 - origParent.x0 + 1
origParentHeight = origParent.y1 - origParent.y0 + 1
curParentWidth = curParent.x1 - curParent.x0 + 1
curParentHeight = curParent.y1 - curParent.y0 + 1
deltaX = curParentWidth - origParentWidth
deltaY = curParentHeight - origParentHeight
scaleX = origParentWidth != 0 ? curParentWidth / origParentWidth : 0.0
scaleY = origParentHeight != 0 ? curParentHeight / origParentHeight : 0.0
All ratios are floating point. Mode-4 conversion uses MSVC _ftol2, i.e.
truncate toward zero, not floor and not round-to-nearest.
Define halfTowardZero(n) = truncate(n / 2) for signed integers.
newX0 = orig.x0
switch LeftEdge:
2: newX0 = orig.x0 + deltaX
3: newX0 = halfTowardZero(curParentWidth)
- halfTowardZero(orig.width)
4: newX0 = truncateTowardZero(orig.x0 * scaleX)
0,1: leave orig.x0 for now
newX1 = orig.x1
switch RightEdge:
1: newX1 = orig.x1 + deltaX
3: newX1 = halfTowardZero(curParentWidth)
+ halfTowardZero(orig.width) - 1
4: newX1 = truncateTowardZero(orig.x1 * scaleX)
0,2: leave orig.x1 for now
newY0 = orig.y0
switch TopEdge:
2: newY0 = orig.y0 + deltaY
3: newY0 = halfTowardZero(curParentHeight)
- halfTowardZero(orig.height)
4: newY0 = truncateTowardZero(orig.y0 * scaleY)
0,1: leave orig.y0 for now
newY1 = orig.y1
switch BottomEdge:
1: newY1 = orig.y1 + deltaY
3: newY1 = halfTowardZero(curParentHeight)
+ halfTowardZero(orig.height) - 1
4: newY1 = truncateTowardZero(orig.y1 * scaleY)
0,2: leave orig.y1 for now
Then retail applies mode 0 as a preservation policy:
if current width != 0 OR current height != 0 OR preserve-current flag is set:
if LeftEdge == 0: newX0 = current.x0
if TopEdge == 0: newY0 = current.y0
if RightEdge == 0: newX1 = current.x1
if BottomEdge == 0: newY1 = current.y1
Finally:
MoveTo(newX0, newY0)
ResizeTo(newX1 - newX0 + 1, newY1 - newY0 + 1)
if original z-level differs from current z-level:
restore original z-level
ask parent to remove/reinsert this child in z-order
return true
Raw-mode summary
| Raw mode | Near edge (Left/Top) |
Far edge (Right/Bottom) |
|---|---|---|
0 |
Preserve current edge after initialization | Preserve current edge after initialization |
1 |
Keep original near edge | Add parent size delta (stretch) |
2 |
Add parent size delta (move with far side) | Keep original far edge |
3 |
Recompute centered near edge | Recompute centered far edge |
4 |
Proportionally scale original coordinate | Proportionally scale original coordinate |
Mode 4 does not mean “both anchors.” That description in the earlier
LayoutDesc note was only an approximation of the resulting resize for common
assets and must not survive in the imported layout policy.
4. Time delivery, tooltip cancellation, and focus ordering
4.1 No generic Device timer queue in the named client
The old 04-input-events.md inferred a Device vtable timer API from unnamed
calls. The recovered UIElementManager fields in acclient.h contain tooltip
timestamps but no timer collection, and the named execution path is explicit.
The exact per-frame method is UIElementManager::UseTime @ 0x0045CFD0:
UseTime():
CleanDeleteQueue()
ProcessUIMessageRemovalData()
if mouse hit-test requested:
DoMouseUpdate()
CheckTooltip()
BroadcastGlobalMessage(3, 0)
if ICIDM exists:
ICIDM.UseTime()
DrawDirtyRegions()
Global message 3 is the shared time pulse used by text caret/download work and
button hot-click repeat. Subscription and unsubscription are the cancellation
mechanism. Deletions and deferred listener removals are processed before the
time pulse.
4.2 Tooltip deadlines
Sources: manager constructor 0x0045F5D0, CheckTooltip @ 0x0045B6E0,
StartHover @ 0x00459250, StopHover @ 0x00459280, SwitchMouseOver @ 0x0045B560, MouseMoveHandler @ 0x0045E710, and ReleaseMouseCapture @ 0x0045D2B0.
Defaults are 0.25 seconds delay and 10 seconds duration. The delay is a
user preference (Misc.TooltipDelay, allowed range 0..10 seconds); per-element
float property 0x50 overrides it.
MouseMoveHandler(...):
lastMouseMoveTime = Timer.local_time
refresh the element under the pointer and call SwitchMouseOver
continue drag processing
ReleaseMouseCapture(owner):
if owner holds capture:
lastMouseMoveTime = Timer.local_time
decrement/release capture
CheckTooltip():
now = Timer.local_time
if hover has not started AND mouse is inside the window:
if no element has mouse capture AND lastEntered exists:
delay = lastEntered.floatProperty(0x50) or manager default
if now >= lastMouseMoveTime + delay:
StartHover(currentMouseX, currentMouseY)
if tooltip element exists:
if now - tooltipStart >= tooltipDuration:
queue tooltip element for deletion
tooltipElement = null
SwitchMouseOver(null)
StartHover(x, y):
if hover has not started:
hoverStarted = lastEntered.MouseHover(x, y)
StopHover():
if hoverStarted:
hoverStarted = false
lastEntered?.MouseUnhover()
if tooltipElement exists:
queue it for deletion
tooltipElement = null
SwitchMouseOver(old -> new) calls StopHover before sending
old.MouseOverTop(false), then sends new.MouseOverTop(true). Capture prevents
a new tooltip deadline from starting. UIElementManager::DeletingElement @ 0x0045E520 also clears capture/hover/drag/tooltip ownership and removes queued
element messages that target the deleted element.
There is therefore no “remove a due timer before callback” retail rule to port for tooltips. The faithful rule is owner-state cancellation plus subscription teardown before the global time pulse.
4.3 Focus transfer order
Sources: UIElementManager::SetFocusElement @ 0x0045B890,
UIElement::TakeFocus @ 0x004601E0, UIElement::RelinquishFocus @ 0x00461DE0, and UIElementManager::BroadcastElementMessage @ 0x0045AB40.
SetFocusElement(newFocus):
oldFocus = focusElement
if oldFocus == newFocus: return
if oldFocus exists:
BroadcastElementMessage(oldFocus, 0x2F, param1=0, param2=0)
oldFocus.UnregisterInputMaps()
focusElement = newFocus
if newFocus exists:
BroadcastElementMessage(newFocus, 0x2F, param1=1, param2=0)
newFocus.RegisterInputMaps(priority=0x0BB8) // 3000
TakeFocus(element):
root = element.GetRootElement()
if root is null: return false
set element's owns-focus marker
if root is active:
SetFocusElement(element)
becameGlobal = true
root.rememberedFocusDescendant = element
return becameGlobal
RelinquishFocus(element):
root = element.GetRootElement()
if root exists:
root.rememberedFocusDescendant = null
clear element's owns-focus marker
if manager.focusElement == element:
SetFocusElement(null)
return true
Focus messages are synchronous. If a handler broadcasts another element message
while one is already broadcasting, BroadcastElementMessage appends it to a
FIFO list and drains that list before clearing the broadcasting flag. This
prevents recursive listener traversal while retaining deterministic order.
Active-root changes are separate from text focus. UIElement::Activate @ 0x00461C80 activates/registers the root and restores its remembered focus
descendant through SetFocusElement; Deactivate @ 0x00461D50 broadcasts root
deactivation and clears global focus when the root owned it.
4.4 Keyboard routing relative to focus
UIElementManager::KeyPressEvent @ 0x0045C300 uses this order when the debug
console is not intercepting input:
target = focusElement if present else activeElement
if target exists:
consumed = target.KeyDown(action, extent)
if not consumed:
BroadcastGlobalMessage(1, action)
else:
BroadcastGlobalMessage(1, action)
DoVisibilityToggleAction(action) // still runs after the route above
That final visibility-toggle call is unconditional within the normal input path;
it is not gated by whether the focus/active target consumed KeyDown.
5. Conformance vectors to carry into Wave 1
- Text defaults: construct Type 12 with no interaction properties; assert editable=false, selectable=false, and no mouse claim absent tooltip/base role.
- Orthogonal capabilities: cover all four
(editable, selectable)pairs. Editable-only places the caret but does not drag-select; selectable-only selects/copies but cut/paste/delete do not mutate. - Focus teardown: disabling the last active text capability while focused relinquishes focus; disabling one while the other remains does not.
- Button states: golden table for states
1/2/3/6/7/8/13, captured release outside, re-entry, toggle on/off, and missing-state fallback. - Hot click: immediate message
2, initial delay0x10, repeat interval0x11, pause/reset while pointer is outside, and cancellation on mouse-up, property disable, and destruction. - Layout: one vector per raw mode on each individual edge; include odd and even parent/child sizes, negative intermediate centered values, a zero-sized original parent, mode-0 preservation after initialization, and nested parents.
- Time order: deletion/removal before global tick
3; tooltip check before tick3; capture release restarts the tooltip idle deadline. - Focus order: old loss message -> old map removal -> pointer assignment -> new gain message -> new map registration; nested focus messages drain FIFO.
Remaining uncertainties
- The recovered symbols do not name button properties
0x0B..0x13; this note records exact observed behavior but intentionally does not assert canonical property names for all of them. - Mouse action
7is the normal primary-button path in these methods. Alternate action0x0Ais accepted byUIElement_Button; its user-facing input name was not recovered in this bounded pass. - The high text selection-direction bit used in
SetCursorPositionis outsideUIText_Flagand remains unnamed. - This pass did not reconstruct every
ICIDMinput action name, only the exact routing and behavior needed to separate display/selectable/editable text.