acdream/docs/research/2026-07-10-retained-widget-foundations-pseudocode.md

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, especially UIElement_Text::UIText_Flag, UIElement_Text, UIElement_Button, UIElementManager, ElementDesc, and Box2D.
  • Static x86 disassembly of the matching C:\Turbine\Asheron's Call\acclient.exe for the expressions that both Binary Ninja and Ghidra lost around x87 _ftol2 calls in UIElement::UpdateForParentSizeChange.

Cross-checks:

  • docs/research/2026-06-15-layoutdesc-format.md and the production LayoutDesc data it records. Its property observations agree with the named code, but its AnchorEdges reduction 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.md and docs/research/retail-ui/04-input-events.md, the older FUN_xxx research. These are useful as provenance but are superseded where they conflict with the PDB-named client. In particular, the named client contains the full UIElement* hierarchy and does not implement the inferred generic Device::RegisterTimerEvent queue 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

  1. Retail text interactivity is two orthogonal capabilities, not a three-value exclusive mode. Editable (0x16) and Selectable (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; Display means both are false.
  2. 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.
  3. 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 by UIElementManager::CheckTooltip. The unpumped UiRoot.RegisterTimerEvent list is not a faithful retail mechanism.
  4. Raw edge modes 3 and 4 cannot be reduced to AnchorEdges: 3 performs exact inclusive-pixel centering and 4 proportionally scales the individual edge coordinate with truncation toward zero.
  5. In the older LayoutDesc note, the exact text margins should be 0x23=left, 0x24=right, 0x25=upper, 0x26=lower, as shown by UIElement_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 hotClickingInProgress and unsubscribes from global 3;
  • changing bool property 0x0F to 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

  1. Text defaults: construct Type 12 with no interaction properties; assert editable=false, selectable=false, and no mouse claim absent tooltip/base role.
  2. 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.
  3. Focus teardown: disabling the last active text capability while focused relinquishes focus; disabling one while the other remains does not.
  4. 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.
  5. Hot click: immediate message 2, initial delay 0x10, repeat interval 0x11, pause/reset while pointer is outside, and cancellation on mouse-up, property disable, and destruction.
  6. 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.
  7. Time order: deletion/removal before global tick 3; tooltip check before tick 3; capture release restarts the tooltip idle deadline.
  8. 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 7 is the normal primary-button path in these methods. Alternate action 0x0A is accepted by UIElement_Button; its user-facing input name was not recovered in this bounded pass.
  • The high text selection-direction bit used in SetCursorPosition is outside UIText_Flag and remains unnamed.
  • This pass did not reconstruct every ICIDM input action name, only the exact routing and behavior needed to separate display/selectable/editable text.