Members

(static, constant) BUTTON_ROW_MAX_WIDTH

A deliberately CONSERVATIVE cap, not a mirror of any panel dimension.

JRDialog flows its button row INSIDE the panel and grows the panel to fit, up to PANEL_MAX_WIDTH (1200) — so 1104px of row would actually render. This stops well short of that: past ~900 a three-button row reads as a wall of chrome, and the scrollable list is the better presentation for that many words regardless of whether it would fit.

Erring low only routes to the list dialog, which is the safe direction. Three buttons at TextButton's 200 minWidth plus spacing is 648, so the cap bites only on genuinely long labels.

(This previously claimed to mirror JRDialog's PANEL_WIDTH and to describe a row centered UNDER the panel. Neither was true: PANEL_WIDTH is 762, and the row moved inside the panel in #757.)

Default Value
  • 900

(static, constant) MAX_BUTTON_ROW_CHOICES

Mirrors JRDialog's MAX_BUTTONS design cap. Kept as its own constant because BrighterScript consts are file-scoped and the component's is not visible here.

Default Value
  • 3

(static, constant) OVERLAY_DIALOG_ID

The id every overlay dialog is stamped with. One definition, because three things depend on being able to FIND the open dialog by it: isOverlayDialogOpen, cancelOpenDialog, and presentOverlayDialog's supersede below. It identifies exactly one node because presentOverlayDialog keeps it that way.

Default Value
  • jrDialog

Methods

(static) abandonDialog(dialog) → {void}

Abandon a dialog this screen opened, delivering no result. Call from onDestroy for any dialog node you are still holding.

Overlay dialogs are appended to the SCENE, not to the screen that opened them, so they outlive their opener: a routed view destroyed while one is open (deep link, session expiry, server switch — anything that navigates without the user pressing back) would leave a modal stranded over the incoming screen, with a scoped observer pointing at a torn-down scope and returnFocusTo pointing at a dead node. Safe to call with invalid / an already-closed dialog.

Parameters:
NameTypeDescription
dialogobject
Returns:
Type: 
void

(static) cancelOpenDialog() → {void}

Close whatever dialog is open, delivering a CANCELLED result to whoever opened it. The counterpart to abandonDialog(): use this when a THIRD PARTY needs the screen clear while the dialog's owner is still alive and waiting on a result.

The distinction matters. abandonDialog() delivers NOTHING, which is right in onDestroy — the scope that would receive the result is being torn down. Here the owner is a main-thread flow that holds state until its dialog answers (the deep-link server switch holds the pending server and the stashed link), so closing without an answer strands it. A cancelled result is the same thing the user pressing Back would have produced.

Covers BOTH channels, like isDialogOpen(): the scene-appended overlays cancel through their own once-only resolve guard, and Roku's modal channel resolves as cancelled on close (see JRKeyboardDialog.onWasClosed).

Returns:
Type: 
void

(static) choicesFitAsButtons(choices) → {boolean}

A choice set renders as JRDialog's horizontal button row only if it's short (<= 3) AND the measured row fits the screen with comfortable margins. Translations that overflow (e.g. long German labels) route to the list dialog.

Measured with the system font at the size the button label actually renders at (constants.fontSizeMedium, applied by LabelMedium), NOT roFontRegistry's default size — those differ, so the old estimate was wrong for every label. Still an estimate: a user on the downloaded fallback font (uiFontFallback) has slightly different metrics, but fontScaleFactor normalizes those toward the system font's, so the error stays small. Erring wide merely routes to the list dialog, which is the safe direction.

Parameters:
NameTypeDescription
choicesobject
Returns:
Type: 
boolean

(static) isOverlayDialogOpen() → {boolean}

Is a scene-appended overlay dialog currently on screen?

The JRDialog family does NOT use Roku's modal channel (m.scene.dialog), so anything asking "is a dialog open?" via that channel alone has been answering false for every dialog in the app since the family moved to scene overlays. SceneManager.isDialogOpen() folds this in; prefer that if you have it, because it covers the keyboard dialogs on the modal channel too.

Returns:
Type: 
boolean

(static) presentOverlayDialog(dialog, onResult, returnFocusToopt) → {object}

Append an overlay dialog to the scene, capture focus, wire the caller's result observer. onResult is resolved in the CALLER's scope because these helpers execute inside the importing component's script scope.

Parameters:
NameTypeAttributesDefaultDescription
dialogobject
onResultstring
returnFocusToobject<optional>
invalid
Returns:
Type: 
object

(static) showAlertDialog(title, message, onResultopt, subheadingopt, secondaryButtonTextopt) → {object}

Simple message + OK button, plus an OPTIONAL secondary action beside it.

result.buttonIndex is 0 for OK, 1 for the secondary action, and the result is cancelled on back. A caller with no secondary state to unwind should treat cancelled exactly as it treats OK — back on a one-button alert has nothing to cancel back to.

subheading is an optional BOLD lead line above the message, for a message that genuinely arrives in two parts — a heading and its text. Use it only when the two parts come from different places (the cast notice: JellyRock owns the provenance title, the sender owns the heading and the body). A message you wrote yourself belongs in one string.

secondaryButtonText adds ONE extra button to the right of OK, for an action that does not leave the alert's subject — "Details" on an error, not a second answer to a question. Two answers to a question are showConfirmDialog.

NOT showChoiceDialog, which is the near miss a reader will reach for: it falls back to JRListDialog when the labels are wide (long translations), which would turn an error message into a scrollable list, and its contract says NOTHING is current yet — wrong for an alert whose primary action is already the default.

Parameters:
NameTypeAttributesDefaultDescription
titlestring
messagestring
onResultstring<optional>
""
subheadingstring<optional>
""
secondaryButtonTextstring<optional>
""
Returns:
Type: 
object

(static) showChoiceDialog(title, message, choices, onResultopt, defaultIndexopt) → {object}

2+ mutually exclusive options. Up to 3 short options render as the horizontal button row; more (or wider-than-panel labels, e.g. long translations) fall back to the scrollable list dialog. Either way the selection lands in result.optionIndex (-1 = cancelled), so callers don't care which route ran.

Parameters:
NameTypeAttributesDefaultDescription
titlestring
messagestring
choicesobject
onResultstring<optional>
""
defaultIndexinteger<optional>
0
Returns:
Type: 
object

(static) showConfirmDialog(title, message, onResultopt, confirmTextopt, cancelTextopt) → {object}

Yes/No-style confirmation. Cancel is LEFT, confirm is RIGHT; back = cancelled. Read result.confirmed — true only when the confirm button was selected.

Parameters:
NameTypeAttributesDefaultDescription
titlestring
messagestring
onResultstring<optional>
""
confirmTextstring<optional>
""
cancelTextstring<optional>
""
Returns:
Type: 
object

(static) showInfoDialog(title, overview, taglineopt, returnFocusToopt) → {object}

Read-only long-form text (descriptions, biographies). Self-closing on OK/back; no result to observe. Wraps the existing OverviewDialog chrome.

returnFocusTo names the node to restore focus to on close. Pass it when the OPENER is the right answer and you already hold it — a focusable element that expands into this dialog. Omitted, focus returns to whatever was focused when the dialog opened, which is the same node in most cases but is DERIVED rather than known.

Parameters:
NameTypeAttributesDefaultDescription
titlestring
overviewstring
taglinestring<optional>
""
returnFocusToobject<optional>
invalid
Returns:
Type: 
object

(static) showKeyboardDialog(title, onResultopt, defaultTextopt, secureopt, keyboardDomainopt) → {object}

Text entry via the OS keyboard (voice-capable, certification-compliant). Entered text lands in result.value; result.cancelled distinguishes cancel from an intentionally empty entry. secure = true masks input for passwords.

keyboardDomain drives the keyboard's VOICE dictation mode, so it must match the kind of text being entered ("email" / "numeric" / "alphanumeric" / "password" / "generic"). secure = true implies "password" unless the caller names a domain — otherwise a masked field would still dictate as free-form words.

Parameters:
NameTypeAttributesDefaultDescription
titlestring
onResultstring<optional>
""
defaultTextstring<optional>
""
secureboolean<optional>
false
keyboardDomainstring<optional>
""
Returns:
Type: 
object

(static) showListDialog(title, items, onResultopt, defaultIndexopt, selectedIndexopt) → {object}

Select one item from a longer list (audio/subtitle tracks, video sources). result.optionIndex is the selected index, -1 on cancel.

defaultIndex is which row to FOCUS. selectedIndex is which option is already CURRENT — it gets the check, the surface, and the "currently selected" announcement — and defaults to -1, meaning none is. A picker passes both (they normally coincide); anything offering a fresh choice passes only the first.

Parameters:
NameTypeAttributesDefaultDescription
titlestring
itemsobject
onResultstring<optional>
""
defaultIndexinteger<optional>
0
selectedIndexinteger<optional>
-1
Returns:
Type: 
object

(static) showQuickConnectDialog(title, instruction, code, onResultopt) → {object}

Show a Quick Connect code and wait for the user to approve it elsewhere.

A VIEW ONLY: it displays code with a Cancel button and resolves cancelled if the user backs out. It does not poll and does not sign anyone in — the caller owns the flow (initiate, poll, exchange) because that is a chain of fetchAsync promises and the promise registry lives on the CALLER's m. UserSelect.startQuickConnect is the reference and the only caller.

The caller closes it on the success path with abandonDialog(): approval comes from the server, not from the user, so there is nothing to deliver back through result.

Parameters:
NameTypeAttributesDefaultDescription
titlestring
instructionstring
codestring
onResultstring<optional>
""
Returns:
Type: 
object

(static) showReportDialog(title, status, sections, returnFocusToopt) → {object}

Show a structured read-only report — the same overlay as showInfoDialog, with a body of label/value rows instead of a paragraph.

Returns the dialog so the caller can keep reporting into it: assigning sections again RECONCILES rather than rebuilds, which is how the playback report refreshes its live figures without disturbing the scroll position. The caller owns stopping that, and owns abandonDialog() in its onDestroy.

status is the one-line verdict above the sections ("Transcoding"), carried in the tagline slot because that slot is already defined as a bold lead line and already read first by the opening announcement.

Parameters:
NameTypeAttributesDefaultDescription
titlestring

dialog title

statusstring

a short lead line, or ""

sectionsobject

[ { id, heading, wideLabels, rows: [ { id, label, value } ] } ]

returnFocusToobject<optional>
invalid

node to restore focus to on close

Returns:
Type: 
object