Members

(static, constant) BUTTON_SPACING

Layout is DERIVED, not a stack of fixed offsets: every block is measured and the next one flows below it. Fixed offsets could not survive a title that wraps to two lines, a message of any length, or a button row whose width depends on the translation.

The flow itself (the order of the blocks and the gaps between them) lives in source/utils/dialogLayout.bs, shared with JRListDialog and OverviewDialog and unit-tested there. What stays here is what is specific to THIS dialog: a panel that grows to fit its button row, and the row that made it grow.

Default Value
  • 24

(static, constant) MAX_BUTTONS

Default Value
  • 3

(static, constant) PANEL_MAX_WIDTH

Default Value
  • 1200

(static, constant) PANEL_WIDTH

Width starts here and GROWS to fit the button row (long translations), up to PANEL_MAX_WIDTH. Past that the row stacks vertically instead of overflowing.

Both on the 6px scale so the panel's left and right edges land on the 720p output grid: (1920 - w) / 2 = 960 - w/2, so a width divisible by 6 gives a panelX divisible by 3. 760 did not, which put both vertical edges off it.

Default Value
  • 762

Methods

(static) applyButtonLayout(contentWidth) → {void}

Horizontal row normally; vertical, full-width buttons when they cannot fit.

Parameters:
NameTypeDescription
contentWidthinteger
Returns:
Type: 
void

(static) applyLayout() → {void}

Position the chrome. Called on any content change; safe to run repeatedly.

Everything is measured then flowed, so a two-line title, a long message and a wide button row each push what follows instead of colliding with it.

Returns:
Type: 
void

(static) buttonRowHeight() → {integer}

Rendered height of the button row, or a computed fallback before it lays out (localBoundingRect is 0 until the TextButtons size themselves).

Returns:
Type: 
integer

(static) cancelDialog() → {void}

Cancel this dialog from OUTSIDE: see cancelOpenDialog() in source/utils/dialogs.bs. Routed through resolveDialog so the once-only guard and the standard cancelled result both apply: a third party clearing the screen must be indistinguishable from the user pressing Back, or the owner waiting on the result is stranded.

Indistinguishable in what it DELIVERS, not in where it came from: the result also carries externallyCancelled, so an owner whose handler ACTS can decline to act on a close the user did not make. Everyone else reads cancelled and is unaffected. See source/utils/dialogResult.bs.

Returns:
Type: 
void

(static) centerButtonRow() → {void}

Center the row on the PANEL, not the screen: they only coincided while the panel was a fixed width centered on screen.

Returns:
Type: 
void

(static) closeDialog() → {void}

Dismiss this overlay. Appended directly to the scene by dialogs.bs, so closing is a removeChild from our parent + restoring focus to the opener (returnFocusTo). Mirrors OverviewDialog.closeDialog().

Returns:
Type: 
void

(static) focusButton(index) → {void}

Parameters:
NameTypeDescription
indexinteger
Returns:
Type: 
void

(static) init() → {void}

Returns:
Type: 
void

(static) moveButtonFocus(delta) → {void}

Step button focus, WRAPPING at the ends to match JRButtonGroup: the app's other horizontal button row wraps, so a dialog that dead-ends feels broken.

Narration of the newly focused button is handled by the focusedChild observer, so it covers remote-driven moves and programmatic ones alike.

Parameters:
NameTypeDescription
deltainteger
Returns:
Type: 
void

(static) naturalButtonRowWidth() → {integer}

Width the row wants at its natural size, before any panel growth.

Returns:
Type: 
integer

(static) onButtonFocusChanged() → {void}

Screen-reader announcement, driven by the focus event itself.

The platform announces the focused button the moment focus lands, and app speech and platform speech interrupt each other rather than queueing, so we have to speak second to be the one that survives. The focus change IS that moment, so reacting to it needs no guess about timing (an earlier version used a tuned delay; the value was a guess that would rot on different hardware).

First focus = the dialog opening: announce the whole thing, folding in the button so it is one utterance in the built-in order. Later focus changes announce only the button the user moved to: nothing else has changed.

Returns:
Type: 
void

(static) onButtonReady() → {void}

A button finished sizing itself. Re-run the whole layout, not only the centering: the panel's width and height and the row's position are all derived from the buttons' real measurements. Laying out once from an estimate left the bottom padding short, and left a stacked row positioned off the panel.

Returns:
Type: 
void

(static) onButtonsChanged() → {void}

Returns:
Type: 
void

(static) onKeyEvent(key, press) → {boolean}

The decision itself is buttonDialogKeyAction in source/utils/dialogKeys.bs, which is pure and unit-tested. Everything here is the doing.

Parameters:
NameTypeDescription
keystring
pressboolean
Returns:
Type: 
boolean

(static) onMessageChanged() → {void}

Returns:
Type: 
void

(static) onMessageRendered() → {void}

Returns:
Type: 
void

(static) onOpeningAnnouncementDue() → {void}

Announce the whole dialog, once the platform's own focus announcement is out of the way. Folds in the focused button so this is a single utterance in the built-in order: title, message, button.

Returns:
Type: 
void

(static) onSubheadingChanged() → {void}

SET THE TEXT FIELDS BEFORE PRESENTING, never after. renderTracking fires on the none -> full transition and never again (the same property that made the button row use TextButton's isReady instead), so once the dialog has rendered no later text change re-runs applyLayout, and the new text draws on top of the old layout. True of title and message too; the helpers in source/utils/dialogs.bs set every field before presentOverlayDialog for exactly this reason. The one exception is a one-line field whose height cannot change: PlayerHostView rewrites the still-watching countdown subheading every second.

Returns:
Type: 
void

(static) onSubheadingRendered() → {void}

Returns:
Type: 
void

(static) onTitleChanged() → {void}

Returns:
Type: 
void

(static) onTitleMeasured() → {void}

Returns:
Type: 
void

(static) resolveDialog(index, externallyCancelledopt) → {void}

Resolve the dialog exactly once, then close. index = -1 means cancelled (back). externallyCancelled is true only on the cancelDialog() path below.

Parameters:
NameTypeAttributesDefaultDescription
indexinteger
externallyCancelledboolean<optional>
false
Returns:
Type: 
void

(static) settleLayout() → {void}

Lay out NOW, called by presentOverlayDialog once this dialog is attached.

The dialog is hidden until this returns, so there is no renderTracking to wait on, and none is needed, because localBoundingRect() answers while hidden. See dialogReveal.bs.

Returns:
Type: 
void