Skip to content

Commit b346a56

Browse files
chore(docs): update code docs
1 parent 99d7e98 commit b346a56

7 files changed

Lines changed: 111 additions & 6 deletions

docs/components_ItemGrid_BaseGridView.bs.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@
132132
' Clear global backdrop - we handle our own backdrops
133133
m.global.sceneManager.callFunc("setBackgroundImage", "")
134134

135+
' Re-activate voice capture — the voiceBox loses its active connection to the
136+
' Roku firmware when this screen is removed from the scene tree during pushScene.
137+
if isValid(m.voiceBox)
138+
m.voiceBox.active = true
139+
end if
140+
135141
if isValid(m.top.lastFocus)
136142
m.top.lastFocus.setFocus(true)
137143
else

docs/components_home_Home.bs.html

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
m.homeRows.observeField("selectedItem", "onRowItemSelected")
2424
m.homeRows.observeField("quickPlayNode", "onQuickPlayNode")
2525

26+
' Voice search setup — matches BaseGridView's pattern:
27+
' Set voiceEnabled once in init(), never set it to false.
28+
' Only toggle active on screen show/hide.
29+
initVoiceBox()
30+
2631
' Tab configuration
2732
m.top.overhangTabs = [
2833
{ title: tr("Home"), id: "home" },
@@ -86,8 +91,16 @@
8691
end if
8792
end if
8893

89-
' Set focus to the active content
90-
if isValid(m.top.lastFocus)
94+
' Re-activate voice capture — matching BaseGridView's OnScreenShown pattern.
95+
' Only toggle active; voiceEnabled was set once in init() and never changed.
96+
if isValid(m.voiceBox)
97+
m.voiceBox.active = true
98+
end if
99+
100+
' Set focus to the active content.
101+
' Skip voiceBox — SceneManager may save it as lastFocus when voice input
102+
' triggers a pushScene, but it's a hidden capture element, not a valid focus target.
103+
if isValid(m.top.lastFocus) and m.top.lastFocus.id <> "homeVoiceBox"
91104
m.top.lastFocus.setFocus(true)
92105
else if isValid(m.activeContent)
93106
m.activeContent.setFocus(true)
@@ -114,6 +127,12 @@
114127
sub OnScreenHidden()
115128
m.log.info("OnScreenHidden")
116129

130+
' Deactivate voice so other screens can claim the voice route.
131+
' Only toggle active — never set voiceEnabled=false (matches BaseGridView pattern).
132+
if isValid(m.voiceBox)
133+
m.voiceBox.active = false
134+
end if
135+
117136
scene = m.top.getScene()
118137
overhang = scene.findNode("overhang")
119138
if isValid(overhang)
@@ -122,6 +141,40 @@
122141
end if
123142
end sub
124143

144+
' ============================================
145+
' VOICE SEARCH
146+
' ============================================
147+
148+
' initVoiceBox: One-time voice setup in init() — matches BaseGridView's pattern.
149+
' Sets voiceEnabled=true ONCE and never sets it back to false. The firmware
150+
' silently manages voiceEnabled when other screens claim voice routing.
151+
' Only `active` is toggled on screen show/hide.
152+
sub initVoiceBox()
153+
m.voiceBox = m.top.findNode("homeVoiceBox")
154+
if not isValid(m.voiceBox)
155+
m.log.error("initVoiceBox: homeVoiceBox not found!")
156+
return
157+
end if
158+
159+
m.voiceBox.opacity = 0.0001
160+
m.voiceBox.hintText = tr("Use voice remote to search")
161+
m.voiceBox.voiceEnabled = true
162+
m.voiceBox.active = true
163+
m.voiceBox.observeField("text", "onVoiceSearch")
164+
end sub
165+
166+
' onVoiceSearch: Fires when voice input is captured by the hidden VoiceTextEditBox.
167+
' Signals Main.bs to open SearchResults with the spoken query pre-populated.
168+
sub onVoiceSearch()
169+
voiceText = m.voiceBox.text
170+
if voiceText = "" then return
171+
172+
' Reset for next voice search
173+
m.voiceBox.text = ""
174+
175+
m.top.voiceQuery = voiceText
176+
end sub
177+
125178
' ============================================
126179
' TAB HANDLING
127180
' ============================================
@@ -248,6 +301,13 @@
248301

249302
m.top.unobserveField("selectedTabId")
250303

304+
' Release voice box — match BaseGridView: never set voiceEnabled=false,
305+
' just unobserve and release the reference.
306+
if isValid(m.voiceBox)
307+
m.voiceBox.unobserveField("text")
308+
m.voiceBox = invalid
309+
end if
310+
251311
' Release tab bar observer
252312
if isValid(m.tabBar)
253313
m.tabBar.unobserveField("requestFocusReturn")

docs/components_search_SearchResults.bs.html

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,17 @@
3434

3535
' Set initial focus for scene navigation
3636
m.top.lastFocus = searchBox
37+
3738
end sub
3839

3940
sub OnScreenShown()
41+
if m.top.skipInitialFocus
42+
m.top.skipInitialFocus = false
43+
' Caller will set focus after pushScene — skip here to avoid a double-focus
44+
' race on the DynamicMiniKeyboard that prevents the voice prompt overlay.
45+
return
46+
end if
47+
4048
' Restore focus for scene navigation
4149
if isValid(m.top.lastFocus)
4250
m.top.lastFocus.setFocus(true)
@@ -156,8 +164,18 @@
156164
sub destroy()
157165
m.log.verbose("destroy")
158166

167+
' Fully release the voice route before teardown — the DynamicMiniKeyboard's
168+
' textEditBox claims the firmware's global voice route via voiceEnabled + active.
169+
' Both must be cleared: active=false stops input capture, voiceEnabled=false
170+
' releases the "only one voiceEnabled at a time" slot so the returning screen's
171+
' VoiceTextEditBox can reclaim it.
172+
if isValid(m.searchAlphabox) and isValid(m.searchAlphabox.textEditBox)
173+
m.searchAlphabox.textEditBox.voiceEnabled = false
174+
m.searchAlphabox.textEditBox.active = false
175+
end if
176+
159177
' Unobserve child node observers
160-
m.searchAlphabox.unobserveField("focusedChild")
178+
if isValid(m.searchAlphabox) then m.searchAlphabox.unobserveField("focusedChild")
161179
m.searchSelect.unobserveField("rowItemFocused")
162180

163181
' Stop and release task node (observer may already be cleared by loadResults())

docs/data/search.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

docs/module-Home.html

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

docs/source_Main.bs.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,26 @@
143143
end if
144144
end if
145145
end if
146+
else if isNodeEvent(msg, "voiceQuery")
147+
' Voice search from Home screen — open SearchResults with spoken query pre-populated
148+
voiceQuery = msg.getData()
149+
if isValidAndNotEmpty(voiceQuery)
150+
' Guard: skip if a SearchResults is already the active scene (prevents duplicate pushes
151+
' when voice system fires multiple text changes for a single input)
152+
activeScene = m.global.sceneManager.callFunc("getActiveScene")
153+
if isValid(activeScene) and activeScene.subtype() = "SearchResults"
154+
searchKey = activeScene.findNode("SearchBox").findNode("search_Key")
155+
searchKey.text = voiceQuery
156+
searchKey.textEditBox.setFocus(true)
157+
else
158+
group = CreateSearchPage()
159+
group.skipInitialFocus = true
160+
m.global.sceneManager.callFunc("pushScene", group)
161+
searchKey = group.findNode("SearchBox").findNode("search_Key")
162+
searchKey.text = voiceQuery
163+
searchKey.textEditBox.setFocus(true)
164+
end if
165+
end if
146166
else if isNodeEvent(msg, "output")
147167
task = msg.getRoSGNode()
148168
if isValid(task) and task.subtype() = "QuickPlayTask"

docs/source_ShowScenes.bs.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,7 @@
633633

634634
group.observeField("selectedItem", m.port)
635635
group.observeField("quickPlayNode", m.port)
636+
group.observeField("voiceQuery", m.port)
636637

637638
sidepanel = group.findNode("options")
638639
sidepanel.observeField("closeSidePanel", m.port)
@@ -708,7 +709,7 @@
708709

709710
function CreateSearchPage()
710711
' Search + Results Page
711-
group = CreateObject("roSGNode", "searchResults")
712+
group = CreateObject("roSGNode", "SearchResults")
712713
group.observeField("quickPlayNode", m.port)
713714
options = group.findNode("searchSelect")
714715
options.observeField("itemSelected", m.port)

0 commit comments

Comments
 (0)