2023-11-11 00:42:33 +00:00
|
|
|
import "pkg:/source/utils/misc.bs"
|
2023-11-06 19:05:21 +00:00
|
|
|
|
2024-01-25 02:54:04 +00:00
|
|
|
const LOGO_RIGHT_PADDING = 30
|
|
|
|
const OPTIONCONTROLS_TOP_PADDING = 50
|
|
|
|
|
2023-11-06 19:05:21 +00:00
|
|
|
sub init()
|
|
|
|
m.videoControls = m.top.findNode("videoControls")
|
|
|
|
m.optionControls = m.top.findNode("optionControls")
|
|
|
|
|
|
|
|
m.inactivityTimer = m.top.findNode("inactivityTimer")
|
2024-01-25 02:54:04 +00:00
|
|
|
m.videoInfo = m.top.findNode("videoInfo")
|
2023-11-06 19:05:21 +00:00
|
|
|
m.itemTitle = m.top.findNode("itemTitle")
|
|
|
|
m.videoPlayPause = m.top.findNode("videoPlayPause")
|
2023-11-11 22:30:06 +00:00
|
|
|
m.videoPositionTime = m.top.findNode("videoPositionTime")
|
|
|
|
m.videoRemainingTime = m.top.findNode("videoRemainingTime")
|
|
|
|
m.progressBar = m.top.findNode("progressBar")
|
|
|
|
m.progressBarBackground = m.top.findNode("progressBarBackground")
|
2023-11-06 19:05:21 +00:00
|
|
|
|
|
|
|
m.top.observeField("visible", "onVisibleChanged")
|
2023-11-07 01:46:18 +00:00
|
|
|
m.top.observeField("hasFocus", "onFocusChanged")
|
2023-11-11 22:30:06 +00:00
|
|
|
m.top.observeField("progressPercentage", "onProgressPercentageChanged")
|
2023-11-06 19:05:21 +00:00
|
|
|
m.top.observeField("playbackState", "onPlaybackStateChanged")
|
2024-01-25 02:54:04 +00:00
|
|
|
|
2023-11-06 19:05:21 +00:00
|
|
|
m.top.observeField("itemTitleText", "onItemTitleTextChanged")
|
2024-01-25 02:54:04 +00:00
|
|
|
m.top.observeField("seasonNumber", "onSeasonNumberChanged")
|
|
|
|
m.top.observeField("episodeNumber", "onEpisodeNumberChanged")
|
2024-01-29 02:17:51 +00:00
|
|
|
m.top.observeField("episodeNumberEnd", "onEpisodeNumberEndChanged")
|
2024-01-25 02:54:04 +00:00
|
|
|
m.top.observeField("logoImage", "onLogoImageChanged")
|
2023-11-06 19:05:21 +00:00
|
|
|
|
2023-12-02 17:45:42 +00:00
|
|
|
m.defaultButtonIndex = 2
|
|
|
|
m.focusedButtonIndex = 2
|
2024-01-25 02:54:04 +00:00
|
|
|
m.optionControlsMoved = false
|
2023-11-10 21:52:10 +00:00
|
|
|
|
|
|
|
m.videoControls.buttonFocused = m.defaultButtonIndex
|
|
|
|
m.optionControls.buttonFocused = m.optionControls.getChildCount() - 1
|
|
|
|
|
2023-11-06 19:05:21 +00:00
|
|
|
m.videoControls.getChild(m.defaultButtonIndex).focus = true
|
|
|
|
m.deviceInfo = CreateObject("roDeviceInfo")
|
|
|
|
end sub
|
|
|
|
|
2023-11-11 22:30:06 +00:00
|
|
|
' onProgressPercentageChanged: Handler for changes to m.top.progressPercentage param
|
|
|
|
'
|
|
|
|
sub onProgressPercentageChanged()
|
2023-11-12 17:30:53 +00:00
|
|
|
m.videoPositionTime.text = secondsToHuman(m.top.positionTime, true)
|
|
|
|
m.videoRemainingTime.text = secondsToHuman(m.top.remainingPositionTime, true)
|
2023-11-11 22:30:06 +00:00
|
|
|
m.progressBar.width = m.progressBarBackground.width * m.top.progressPercentage
|
|
|
|
end sub
|
|
|
|
|
2023-11-06 19:05:21 +00:00
|
|
|
' onPlaybackStateChanged: Handler for changes to m.top.playbackState param
|
|
|
|
'
|
|
|
|
sub onPlaybackStateChanged()
|
|
|
|
if LCase(m.top.playbackState) = "playing"
|
|
|
|
m.videoPlayPause.icon = "pkg:/images/icons/pause.png"
|
|
|
|
return
|
|
|
|
end if
|
|
|
|
|
|
|
|
m.videoPlayPause.icon = "pkg:/images/icons/play.png"
|
|
|
|
end sub
|
|
|
|
|
|
|
|
' onItemTitleTextChanged: Handler for changes to m.top.itemTitleText param.
|
|
|
|
'
|
|
|
|
sub onItemTitleTextChanged()
|
|
|
|
m.itemTitle.text = m.top.itemTitleText
|
|
|
|
end sub
|
|
|
|
|
2024-01-25 02:54:04 +00:00
|
|
|
' onSeasonNumberChanged: Handler for changes to m.top.seasonNumber param.
|
|
|
|
'
|
|
|
|
sub onSeasonNumberChanged()
|
|
|
|
m.top.unobserveField("seasonNumber")
|
|
|
|
itemSeason = m.top.findNode("itemSeason")
|
|
|
|
itemSeason.font.size = 32
|
|
|
|
itemSeason.text = `S${m.top.seasonNumber}`
|
|
|
|
|
|
|
|
' Move the option controls down to give room for season number
|
|
|
|
if not m.optionControlsMoved
|
|
|
|
moveOptionControls(0, OPTIONCONTROLS_TOP_PADDING)
|
|
|
|
m.optionControlsMoved = true
|
|
|
|
end if
|
|
|
|
end sub
|
|
|
|
|
|
|
|
' onEpisodeNumberChanged: Handler for changes to m.top.episodeNumber param.
|
|
|
|
'
|
|
|
|
sub onEpisodeNumberChanged()
|
|
|
|
m.top.unobserveField("episodeNumber")
|
|
|
|
itemEpisode = m.top.findNode("itemEpisode")
|
|
|
|
itemEpisode.font.size = 32
|
|
|
|
itemEpisode.text = `E${m.top.episodeNumber}`
|
|
|
|
|
|
|
|
' Move the option controls down to give room for episode number
|
|
|
|
if not m.optionControlsMoved
|
|
|
|
moveOptionControls(0, OPTIONCONTROLS_TOP_PADDING)
|
|
|
|
m.optionControlsMoved = true
|
|
|
|
end if
|
|
|
|
end sub
|
|
|
|
|
2024-01-29 02:17:51 +00:00
|
|
|
' onEpisodeNumberEndChanged: Handler for changes to m.top.episodeNumberEnd param.
|
|
|
|
'
|
|
|
|
sub onEpisodeNumberEndChanged()
|
|
|
|
m.top.unobserveField("episodeNumberEnd")
|
|
|
|
itemEpisodeEnd = m.top.findNode("itemEpisodeEnd")
|
|
|
|
itemEpisodeEnd.font.size = 32
|
|
|
|
itemEpisodeEnd.text = `-${m.top.episodeNumberEnd}`
|
|
|
|
|
|
|
|
' Move the option controls down to give room for episode number
|
|
|
|
if not m.optionControlsMoved
|
|
|
|
moveOptionControls(0, OPTIONCONTROLS_TOP_PADDING)
|
|
|
|
m.optionControlsMoved = true
|
|
|
|
end if
|
|
|
|
end sub
|
|
|
|
|
2024-01-25 02:54:04 +00:00
|
|
|
' moveOptionControls: Moves option controls node based on passed pixel values
|
|
|
|
'
|
|
|
|
' @param {integer} horizontalPixels - Number of horizontal pixels to move option controls
|
|
|
|
' @param {integer} verticalPixels - Number of vertical pixels to move option controls
|
|
|
|
sub moveOptionControls(horizontalPixels as integer, verticalPixels as integer)
|
|
|
|
m.optionControls.translation = `[${m.optionControls.translation[0] + horizontalPixels}, ${m.optionControls.translation[1] + verticalPixels}]`
|
|
|
|
end sub
|
|
|
|
|
|
|
|
' onLogoLoadStatusChanged: Handler for changes to logo image's status.
|
|
|
|
'
|
|
|
|
' @param {dynamic} event - field change event
|
|
|
|
sub onLogoLoadStatusChanged(event)
|
|
|
|
if LCase(event.GetData()) = "ready"
|
|
|
|
logoImage = event.getRoSGNode()
|
|
|
|
logoImage.unobserveField("loadStatus")
|
|
|
|
|
|
|
|
' Move video info to the right based on the logo width
|
|
|
|
m.videoInfo.translation = `[${m.videoInfo.translation[0] + logoImage.bitmapWidth + LOGO_RIGHT_PADDING}, ${m.videoInfo.translation[1]}]`
|
|
|
|
m.itemTitle.maxWidth = m.itemTitle.maxWidth - (logoImage.bitmapWidth + LOGO_RIGHT_PADDING)
|
|
|
|
|
|
|
|
' Move the option controls down based on the logo height
|
|
|
|
if not m.optionControlsMoved
|
|
|
|
moveOptionControls(0, OPTIONCONTROLS_TOP_PADDING)
|
|
|
|
m.optionControlsMoved = true
|
|
|
|
end if
|
|
|
|
end if
|
|
|
|
end sub
|
|
|
|
|
|
|
|
' onLogoImageChanged: Handler for changes to m.top.logoImage param.
|
|
|
|
'
|
|
|
|
sub onLogoImageChanged()
|
|
|
|
if isValidAndNotEmpty(m.top.logoImage)
|
|
|
|
logoImage = createObject("roSGNode", "Poster")
|
|
|
|
logoImage.Id = "logoImage"
|
|
|
|
logoImage.observeField("loadStatus", "onLogoLoadStatusChanged")
|
|
|
|
logoImage.uri = m.top.logoImage
|
|
|
|
logoImage.translation = [103, 61]
|
|
|
|
m.top.appendChild(logoImage)
|
|
|
|
end if
|
|
|
|
end sub
|
|
|
|
|
2023-11-06 19:05:21 +00:00
|
|
|
' resetFocusToDefaultButton: Reset focus back to the default button
|
|
|
|
'
|
|
|
|
sub resetFocusToDefaultButton()
|
|
|
|
' Remove focus from previously selected button
|
|
|
|
for each child in m.videoControls.getChildren(-1, 0)
|
|
|
|
if isValid(child.focus)
|
|
|
|
child.focus = false
|
|
|
|
end if
|
|
|
|
end for
|
|
|
|
|
|
|
|
for each child in m.optionControls.getChildren(-1, 0)
|
|
|
|
if isValid(child.focus)
|
|
|
|
child.focus = false
|
|
|
|
end if
|
|
|
|
end for
|
|
|
|
|
|
|
|
m.optionControls.setFocus(false)
|
|
|
|
|
|
|
|
' Set focus back to the default button
|
|
|
|
m.videoControls.setFocus(true)
|
|
|
|
m.focusedButtonIndex = m.defaultButtonIndex
|
|
|
|
m.videoControls.getChild(m.defaultButtonIndex).focus = true
|
2023-12-02 17:45:42 +00:00
|
|
|
m.videoControls.buttonFocused = m.defaultButtonIndex
|
2023-11-10 21:52:10 +00:00
|
|
|
m.optionControls.buttonFocused = m.optionControls.getChildCount() - 1
|
2023-11-06 19:05:21 +00:00
|
|
|
end sub
|
|
|
|
|
2023-11-16 14:26:20 +00:00
|
|
|
' onVisibleChanged: Handler for changes to the visibility of this menu.
|
2023-11-06 19:05:21 +00:00
|
|
|
'
|
|
|
|
sub onVisibleChanged()
|
|
|
|
if m.top.visible
|
|
|
|
resetFocusToDefaultButton()
|
|
|
|
m.inactivityTimer.observeField("fire", "inactiveCheck")
|
|
|
|
m.inactivityTimer.control = "start"
|
|
|
|
return
|
|
|
|
end if
|
|
|
|
|
|
|
|
m.inactivityTimer.unobserveField("fire")
|
|
|
|
m.inactivityTimer.control = "stop"
|
|
|
|
end sub
|
|
|
|
|
2023-11-16 14:26:20 +00:00
|
|
|
' onFocusChanged: Handler for changes to the focus of this menu.
|
2023-11-07 01:46:18 +00:00
|
|
|
'
|
|
|
|
sub onFocusChanged()
|
|
|
|
if m.top.hasfocus
|
|
|
|
focusedButton = m.optionControls.getChild(m.focusedButtonIndex)
|
|
|
|
if focusedButton.focus
|
|
|
|
m.optionControls.setFocus(true)
|
|
|
|
return
|
|
|
|
end if
|
|
|
|
|
|
|
|
m.videoControls.setFocus(true)
|
|
|
|
end if
|
|
|
|
end sub
|
|
|
|
|
2023-11-16 14:26:20 +00:00
|
|
|
' inactiveCheck: Checks if the time since last keypress is greater than or equal to the allowed inactive time of the menu.
|
2023-11-06 19:05:21 +00:00
|
|
|
'
|
|
|
|
sub inactiveCheck()
|
2023-11-19 00:43:05 +00:00
|
|
|
' If user is currently seeing a dialog box, ignore inactive check
|
|
|
|
if m.global.sceneManager.callFunc("isDialogOpen")
|
|
|
|
return
|
|
|
|
end if
|
|
|
|
|
2023-11-06 19:05:21 +00:00
|
|
|
if m.deviceInfo.timeSinceLastKeypress() >= m.top.inactiveTimeout
|
|
|
|
m.top.action = "hide"
|
|
|
|
end if
|
|
|
|
end sub
|
|
|
|
|
2023-11-16 14:26:20 +00:00
|
|
|
' onButtonSelected: Handler for selection of buttons from the menu.
|
2023-11-06 19:05:21 +00:00
|
|
|
'
|
|
|
|
sub onButtonSelected()
|
|
|
|
if m.optionControls.isInFocusChain()
|
|
|
|
buttonGroup = m.optionControls
|
|
|
|
else
|
|
|
|
buttonGroup = m.videoControls
|
|
|
|
end if
|
|
|
|
|
|
|
|
selectedButton = buttonGroup.getChild(m.focusedButtonIndex)
|
|
|
|
|
|
|
|
if LCase(selectedButton.id) = "chapterlist"
|
|
|
|
m.top.showChapterList = not m.top.showChapterList
|
|
|
|
end if
|
|
|
|
|
|
|
|
m.top.action = selectedButton.id
|
|
|
|
end sub
|
|
|
|
|
|
|
|
function onKeyEvent(key as string, press as boolean) as boolean
|
|
|
|
if not press then return false
|
|
|
|
|
|
|
|
if key = "OK"
|
|
|
|
onButtonSelected()
|
|
|
|
return true
|
|
|
|
end if
|
|
|
|
|
|
|
|
if key = "play"
|
|
|
|
m.top.action = "videoplaypause"
|
|
|
|
return true
|
|
|
|
end if
|
|
|
|
|
|
|
|
if key = "right"
|
|
|
|
if m.optionControls.isInFocusChain()
|
|
|
|
buttonGroup = m.optionControls
|
|
|
|
else
|
|
|
|
buttonGroup = m.videoControls
|
|
|
|
end if
|
|
|
|
|
|
|
|
if m.focusedButtonIndex + 1 >= buttonGroup.getChildCount()
|
|
|
|
return true
|
|
|
|
end if
|
|
|
|
|
|
|
|
focusedButton = buttonGroup.getChild(m.focusedButtonIndex)
|
|
|
|
focusedButton.focus = false
|
|
|
|
|
|
|
|
' Skip spacer elements until next button is found
|
|
|
|
for i = m.focusedButtonIndex + 1 to buttonGroup.getChildCount()
|
|
|
|
m.focusedButtonIndex = i
|
|
|
|
focusedButton = buttonGroup.getChild(m.focusedButtonIndex)
|
|
|
|
|
|
|
|
if isValid(focusedButton.focus)
|
2023-11-10 21:52:10 +00:00
|
|
|
buttonGroup.buttonFocused = m.focusedButtonIndex
|
2023-11-06 19:05:21 +00:00
|
|
|
focusedButton.focus = true
|
|
|
|
exit for
|
|
|
|
end if
|
|
|
|
end for
|
|
|
|
|
|
|
|
return true
|
|
|
|
end if
|
|
|
|
|
|
|
|
if key = "left"
|
|
|
|
if m.focusedButtonIndex = 0
|
|
|
|
return true
|
|
|
|
end if
|
|
|
|
|
|
|
|
if m.optionControls.isInFocusChain()
|
|
|
|
buttonGroup = m.optionControls
|
|
|
|
else
|
|
|
|
buttonGroup = m.videoControls
|
|
|
|
end if
|
|
|
|
|
|
|
|
focusedButton = buttonGroup.getChild(m.focusedButtonIndex)
|
|
|
|
focusedButton.focus = false
|
|
|
|
|
|
|
|
' Skip spacer elements until next button is found
|
|
|
|
for i = m.focusedButtonIndex - 1 to 0 step -1
|
|
|
|
m.focusedButtonIndex = i
|
|
|
|
focusedButton = buttonGroup.getChild(m.focusedButtonIndex)
|
|
|
|
|
|
|
|
if isValid(focusedButton.focus)
|
2023-11-10 21:52:10 +00:00
|
|
|
buttonGroup.buttonFocused = m.focusedButtonIndex
|
2023-11-06 19:05:21 +00:00
|
|
|
focusedButton.focus = true
|
|
|
|
exit for
|
|
|
|
end if
|
|
|
|
end for
|
|
|
|
|
|
|
|
return true
|
|
|
|
end if
|
|
|
|
|
|
|
|
if key = "up"
|
|
|
|
if m.videoControls.isInFocusChain()
|
|
|
|
focusedButton = m.videoControls.getChild(m.focusedButtonIndex)
|
|
|
|
focusedButton.focus = false
|
|
|
|
m.videoControls.setFocus(false)
|
|
|
|
|
2023-11-10 21:52:10 +00:00
|
|
|
m.focusedButtonIndex = m.optionControls.buttonFocused
|
2023-11-06 19:05:21 +00:00
|
|
|
focusedButton = m.optionControls.getChild(m.focusedButtonIndex)
|
|
|
|
focusedButton.focus = true
|
|
|
|
m.optionControls.setFocus(true)
|
|
|
|
end if
|
|
|
|
|
|
|
|
return true
|
|
|
|
end if
|
|
|
|
|
|
|
|
if key = "down"
|
|
|
|
if m.optionControls.isInFocusChain()
|
|
|
|
focusedButton = m.optionControls.getChild(m.focusedButtonIndex)
|
|
|
|
focusedButton.focus = false
|
|
|
|
m.optionControls.setFocus(false)
|
|
|
|
|
2023-11-10 21:52:10 +00:00
|
|
|
m.focusedButtonIndex = m.videoControls.buttonFocused
|
|
|
|
focusedButton = m.videoControls.getChild(m.focusedButtonIndex)
|
2023-11-06 19:05:21 +00:00
|
|
|
focusedButton.focus = true
|
|
|
|
m.videoControls.setFocus(true)
|
|
|
|
end if
|
|
|
|
|
|
|
|
return true
|
|
|
|
end if
|
|
|
|
|
|
|
|
' All other keys hide the menu
|
|
|
|
m.top.action = "hide"
|
|
|
|
return true
|
|
|
|
end function
|