Add play/pause, auto-hide, subtitle, video info, adjust pause logic
This commit is contained in:
parent
4c394dc7e7
commit
7fdac1b489
39
components/Clock.bs
Normal file
39
components/Clock.bs
Normal file
|
@ -0,0 +1,39 @@
|
|||
import "pkg:/source/utils/misc.brs"
|
||||
|
||||
sub init()
|
||||
|
||||
' If hideclick setting is checked, exit without setting any variables
|
||||
if m.global.session.user.settings["ui.design.hideclock"]
|
||||
return
|
||||
end if
|
||||
|
||||
m.clockTime = m.top.findNode("clockTime")
|
||||
|
||||
m.currentTimeTimer = m.top.findNode("currentTimeTimer")
|
||||
m.dateTimeObject = CreateObject("roDateTime")
|
||||
|
||||
m.currentTimeTimer.observeField("fire", "onCurrentTimeTimerFire")
|
||||
m.currentTimeTimer.control = "start"
|
||||
|
||||
' Default to 12 hour clock
|
||||
m.format = "short-h12"
|
||||
|
||||
' If user has selected a 24 hour clock, update date display format
|
||||
if LCase(m.global.device.clockFormat) = "24h"
|
||||
m.format = "short-h24"
|
||||
end if
|
||||
end sub
|
||||
|
||||
|
||||
' onCurrentTimeTimerFire: Code that runs every time the currentTimeTimer fires
|
||||
'
|
||||
sub onCurrentTimeTimerFire()
|
||||
' Refresh time variable
|
||||
m.dateTimeObject.Mark()
|
||||
|
||||
' Convert to local time zone
|
||||
m.dateTimeObject.ToLocalTime()
|
||||
|
||||
' Format time as requested
|
||||
m.clockTime.text = m.dateTimeObject.asTimeStringLoc(m.format)
|
||||
end sub
|
9
components/Clock.xml
Normal file
9
components/Clock.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<component name="Clock" extends="Group" initialFocus="chapterNext">
|
||||
<children>
|
||||
<Label id="clockTime" font="font:SmallSystemFont" horizAlign="right" vertAlign="center" height="64" width="200" />
|
||||
<Timer id="currentTimeTimer" repeat="true" duration="1" />
|
||||
</children>
|
||||
<interface>
|
||||
</interface>
|
||||
</component>
|
|
@ -1,63 +0,0 @@
|
|||
import "pkg:/source/utils/misc.brs"
|
||||
|
||||
sub init()
|
||||
m.chapterNavigation = m.top.findNode("chapterNavigation")
|
||||
|
||||
m.selectedButtonIndex = 0
|
||||
m.chapterNavigation.getChild(m.selectedButtonIndex).focus = true
|
||||
end sub
|
||||
|
||||
sub onButtonSelected()
|
||||
selectedButton = m.chapterNavigation.getChild(m.selectedButtonIndex)
|
||||
|
||||
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 = "right"
|
||||
if m.selectedButtonIndex + 1 >= m.chapterNavigation.getChildCount()
|
||||
return true
|
||||
end if
|
||||
|
||||
selectedButton = m.chapterNavigation.getChild(m.selectedButtonIndex)
|
||||
selectedButton.focus = false
|
||||
|
||||
m.selectedButtonIndex++
|
||||
|
||||
selectedButton = m.chapterNavigation.getChild(m.selectedButtonIndex)
|
||||
selectedButton.focus = true
|
||||
|
||||
return true
|
||||
end if
|
||||
|
||||
if key = "left"
|
||||
if m.selectedButtonIndex = 0
|
||||
return true
|
||||
end if
|
||||
|
||||
selectedButton = m.chapterNavigation.getChild(m.selectedButtonIndex)
|
||||
selectedButton.focus = false
|
||||
|
||||
m.selectedButtonIndex--
|
||||
|
||||
selectedButton = m.chapterNavigation.getChild(m.selectedButtonIndex)
|
||||
selectedButton.focus = true
|
||||
|
||||
return true
|
||||
end if
|
||||
|
||||
' All other keys hide the menu
|
||||
m.top.action = "hide"
|
||||
return true
|
||||
end function
|
206
components/video/PauseMenu.bs
Normal file
206
components/video/PauseMenu.bs
Normal file
|
@ -0,0 +1,206 @@
|
|||
import "pkg:/source/utils/misc.brs"
|
||||
|
||||
sub init()
|
||||
m.videoControls = m.top.findNode("videoControls")
|
||||
m.optionControls = m.top.findNode("optionControls")
|
||||
|
||||
m.inactivityTimer = m.top.findNode("inactivityTimer")
|
||||
m.itemTitle = m.top.findNode("itemTitle")
|
||||
m.videoPlayPause = m.top.findNode("videoPlayPause")
|
||||
|
||||
m.top.observeField("visible", "onVisibleChanged")
|
||||
m.top.observeField("playbackState", "onPlaybackStateChanged")
|
||||
m.top.observeField("itemTitleText", "onItemTitleTextChanged")
|
||||
|
||||
m.defaultButtonIndex = 1
|
||||
m.focusedButtonIndex = 1
|
||||
m.videoControls.getChild(m.defaultButtonIndex).focus = true
|
||||
m.deviceInfo = CreateObject("roDeviceInfo")
|
||||
end sub
|
||||
|
||||
' 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
|
||||
|
||||
' 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
|
||||
end sub
|
||||
|
||||
' onVisibleChanged: Handler for changes to the visibility of this pause menu.
|
||||
'
|
||||
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
|
||||
|
||||
' inactiveCheck: Checks if the time since last keypress is greater than or equal to the allowed inactive time of the pause menu.
|
||||
'
|
||||
sub inactiveCheck()
|
||||
if LCase(m.top.playbackState) <> "playing" then return
|
||||
|
||||
if m.deviceInfo.timeSinceLastKeypress() >= m.top.inactiveTimeout
|
||||
m.top.action = "hide"
|
||||
end if
|
||||
end sub
|
||||
|
||||
' onButtonSelected: Handler for selection of buttons from the pause menu.
|
||||
'
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
|
||||
m.focusedButtonIndex = 2
|
||||
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)
|
||||
|
||||
m.focusedButtonIndex = m.defaultButtonIndex
|
||||
focusedButton = m.videoControls.getChild(m.defaultButtonIndex)
|
||||
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
|
|
@ -1,13 +1,27 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<component name="PauseMenu" extends="Group" initialFocus="chapterNext">
|
||||
<children>
|
||||
<ButtonGroup id="chapterNavigation" itemSpacings="[20]" layoutDirection="horiz">
|
||||
<IconButton id="chapterList" background="#070707" focusBackground="#00a4dc" padding="35" icon="pkg:/images/icons/numberList.png" height="65" width="100" />
|
||||
<IconButton id="chapterBack" background="#070707" focusBackground="#00a4dc" padding="35" icon="pkg:/images/icons/previous.png" height="65" width="100" />
|
||||
<IconButton id="chapterNext" background="#070707" focusBackground="#00a4dc" padding="35" icon="pkg:/images/icons/next.png" height="65" width="100" />
|
||||
<Label id="itemTitle" font="font:LargeBoldSystemFont" translation="[103,61]" />
|
||||
<Clock id="clock" translation="[1618, 46]" />
|
||||
|
||||
<ButtonGroup id="optionControls" itemSpacings="[20]" layoutDirection="horiz" horizAlignment="left" translation="[103,120]">
|
||||
<IconButton id="showVideoInfoPopup" background="#070707" focusBackground="#00a4dc" padding="16" icon="pkg:/images/icons/videoInfo.png" height="65" width="100" />
|
||||
<IconButton id="chapterList" background="#070707" focusBackground="#00a4dc" padding="16" icon="pkg:/images/icons/numberList.png" height="65" width="100" />
|
||||
<IconButton id="showSubtitleMenu" background="#070707" focusBackground="#00a4dc" padding="0" icon="pkg:/images/icons/subtitle.png" height="65" width="100" />
|
||||
</ButtonGroup>
|
||||
|
||||
<ButtonGroup id="videoControls" itemSpacings="[20]" layoutDirection="horiz" horizAlignment="center" translation="[960,950]">
|
||||
<IconButton id="chapterBack" background="#070707" focusBackground="#00a4dc" padding="16" icon="pkg:/images/icons/previousChapter.png" height="65" width="100" />
|
||||
<IconButton id="videoPlayPause" background="#070707" focusBackground="#00a4dc" padding="35" icon="pkg:/images/icons/play.png" height="65" width="100" />
|
||||
<IconButton id="chapterNext" background="#070707" focusBackground="#00a4dc" padding="16" icon="pkg:/images/icons/nextChapter.png" height="65" width="100" />
|
||||
</ButtonGroup>
|
||||
|
||||
<Timer id="inactivityTimer" duration="1" repeat="true" />
|
||||
</children>
|
||||
<interface>
|
||||
<field id="itemTitleText" type="string" />
|
||||
<field id="inactiveTimeout" type="integer" />
|
||||
<field id="playbackState" type="string" alwaysNotify="true" />
|
||||
<field id="action" type="string" alwaysNotify="true" />
|
||||
<field id="showChapterList" type="boolean" alwaysNotify="true" />
|
||||
</interface>
|
||||
|
|
|
@ -92,16 +92,21 @@ end sub
|
|||
|
||||
' handleHideAction: Handles action to hide pause menu
|
||||
'
|
||||
sub handleHideAction()
|
||||
' @param {boolean} resume - controls whether or not to resume video playback when sub is called
|
||||
'
|
||||
sub handleHideAction(resume as boolean)
|
||||
m.pauseMenu.visible = false
|
||||
m.chapterList.visible = false
|
||||
m.pauseMenu.showChapterList = false
|
||||
m.chapterList.setFocus(false)
|
||||
m.pauseMenu.setFocus(false)
|
||||
m.top.setFocus(true)
|
||||
m.top.control = "resume"
|
||||
if resume
|
||||
m.top.control = "resume"
|
||||
end if
|
||||
end sub
|
||||
|
||||
' handleHideAction: Handles action to show chapter list
|
||||
' handleChapterListAction: Handles action to show chapter list
|
||||
'
|
||||
sub handleChapterListAction()
|
||||
m.chapterList.visible = m.pauseMenu.showChapterList
|
||||
|
@ -136,13 +141,45 @@ function getCurrentChapterIndex() as integer
|
|||
return currentChapter
|
||||
end function
|
||||
|
||||
' handleVideoPlayPauseAction: Handles action to either play or pause the video content
|
||||
'
|
||||
sub handleVideoPlayPauseAction()
|
||||
' If video is paused, resume it
|
||||
if m.top.state = "paused"
|
||||
m.top.control = "resume"
|
||||
return
|
||||
end if
|
||||
|
||||
' Pause video
|
||||
m.top.control = "pause"
|
||||
end sub
|
||||
|
||||
' handleShowSubtitleMenuAction: Handles action to show subtitle selection menu
|
||||
'
|
||||
sub handleShowSubtitleMenuAction()
|
||||
m.top.selectSubtitlePressed = true
|
||||
handleHideAction(false)
|
||||
end sub
|
||||
|
||||
' handleShowVideoInfoPopupAction: Handles action to show video info popup
|
||||
'
|
||||
sub handleShowVideoInfoPopupAction()
|
||||
m.top.selectPlaybackInfoPressed = true
|
||||
handleHideAction(false)
|
||||
end sub
|
||||
|
||||
' onPauseMenuAction: Process action events from pause menu to their respective handlers
|
||||
'
|
||||
sub onPauseMenuAction()
|
||||
action = LCase(m.pauseMenu.action)
|
||||
|
||||
if action = "hide"
|
||||
handleHideAction()
|
||||
handleHideAction(false)
|
||||
return
|
||||
end if
|
||||
|
||||
if action = "play"
|
||||
handleHideAction(true)
|
||||
return
|
||||
end if
|
||||
|
||||
|
@ -155,6 +192,21 @@ sub onPauseMenuAction()
|
|||
handleChapterListAction()
|
||||
return
|
||||
end if
|
||||
|
||||
if action = "videoplaypause"
|
||||
handleVideoPlayPauseAction()
|
||||
return
|
||||
end if
|
||||
|
||||
if action = "showsubtitlemenu"
|
||||
handleShowSubtitleMenuAction()
|
||||
return
|
||||
end if
|
||||
|
||||
if action = "showvideoinfopopup"
|
||||
handleShowVideoInfoPopupAction()
|
||||
return
|
||||
end if
|
||||
end sub
|
||||
|
||||
' Only setup caption items if captions are allowed
|
||||
|
@ -265,6 +317,8 @@ sub onVideoContentLoaded()
|
|||
m.top.transcodeParams = videoContent[0].transcodeparams
|
||||
m.chapters = videoContent[0].chapters
|
||||
|
||||
m.pauseMenu.itemTitleText = m.top.content.title
|
||||
|
||||
populateChapterMenu()
|
||||
|
||||
if m.LoadMetaDataTask.isIntro
|
||||
|
@ -381,6 +435,9 @@ sub onState(msg)
|
|||
m.captionTask.playerState = m.top.state + m.top.globalCaptionMode
|
||||
end if
|
||||
|
||||
' Pass video state into pause menu
|
||||
m.pauseMenu.playbackState = m.top.state
|
||||
|
||||
' When buffering, start timer to monitor buffering process
|
||||
if m.top.state = "buffering" and m.bufferCheckTimer <> invalid
|
||||
|
||||
|
@ -488,7 +545,7 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
if m.chapterMenu.hasFocus()
|
||||
if not press then return false
|
||||
|
||||
if key = "OK" or key = "play"
|
||||
if key = "OK"
|
||||
focusedChapter = m.chapterMenu.itemFocused
|
||||
selectedChapter = m.chapterMenu.content.getChild(focusedChapter)
|
||||
seekTime = selectedChapter.playstart
|
||||
|
@ -508,6 +565,10 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
return true
|
||||
end if
|
||||
|
||||
if key = "play"
|
||||
handleVideoPlayPauseAction()
|
||||
end if
|
||||
|
||||
return true
|
||||
end if
|
||||
|
||||
|
@ -543,31 +604,27 @@ function onKeyEvent(key as string, press as boolean) as boolean
|
|||
return true
|
||||
end if
|
||||
|
||||
else if key = "OK"
|
||||
' OK will play/pause depending on current state
|
||||
' return false to allow selection during seeking
|
||||
if m.top.state = "paused"
|
||||
m.top.control = "resume"
|
||||
return false
|
||||
else if m.top.state = "playing"
|
||||
m.top.control = "pause"
|
||||
' Disable pause menu for intro videos
|
||||
if not m.LoadMetaDataTask.isIntro
|
||||
' Show pause menu
|
||||
m.top.control = "pause"
|
||||
m.pauseMenu.visible = true
|
||||
m.pauseMenu.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
|
||||
return false
|
||||
else if key = "OK" and not m.top.trickPlayBar.visible
|
||||
if not m.LoadMetaDataTask.isIntro
|
||||
' Show pause menu, but don't pause video
|
||||
m.pauseMenu.visible = true
|
||||
m.pauseMenu.setFocus(true)
|
||||
return true
|
||||
end if
|
||||
|
||||
return false
|
||||
end if
|
||||
|
||||
' Disable pause menu for intro videos
|
||||
if not m.LoadMetaDataTask.isIntro
|
||||
if key = "play"
|
||||
' Show pause menu
|
||||
if key = "play" and not m.top.trickPlayBar.visible
|
||||
' If video is paused, resume it and don't show pause menu
|
||||
if m.top.state = "paused"
|
||||
m.top.control = "resume"
|
||||
return true
|
||||
end if
|
||||
|
||||
' Pause video and show pause menu
|
||||
m.top.control = "pause"
|
||||
m.pauseMenu.visible = true
|
||||
m.pauseMenu.setFocus(true)
|
||||
|
|
|
@ -30,10 +30,10 @@
|
|||
<Group id="captionGroup" translation="[960,1020]" />
|
||||
<timer id="playbackTimer" repeat="true" duration="30" />
|
||||
<timer id="bufferCheckTimer" repeat="true" />
|
||||
<PauseMenu id="pauseMenu" visible="false" translation="[50,950]" />
|
||||
<PauseMenu id="pauseMenu" visible="false" inactiveTimeout="5" />
|
||||
|
||||
<Rectangle id="chapterList" visible="false" color="0x00000098" width="460" height="380" translation="[50,530]">
|
||||
<LabelList id="chaptermenu" itemSpacing="[0,20]" numRows="5" font="font:SmallSystemFont" itemSize="[365,40]" translation="[50,20]">
|
||||
<Rectangle id="chapterList" visible="false" color="0x00000098" width="400" height="380" translation="[103,210]">
|
||||
<LabelList id="chaptermenu" itemSpacing="[0,20]" numRows="5" font="font:SmallSystemFont" itemSize="[315,40]" translation="[40,20]">
|
||||
<ContentNode id="chapterContent" role="content" />
|
||||
</LabelList>
|
||||
</Rectangle>
|
||||
|
|
BIN
images/icons/nextChapter.png
Normal file
BIN
images/icons/nextChapter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
BIN
images/icons/pause.png
Normal file
BIN
images/icons/pause.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 734 B |
BIN
images/icons/previousChapter.png
Normal file
BIN
images/icons/previousChapter.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.0 KiB |
BIN
images/icons/subtitle.png
Normal file
BIN
images/icons/subtitle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
BIN
images/icons/videoInfo.png
Normal file
BIN
images/icons/videoInfo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.6 KiB |
Loading…
Reference in New Issue
Block a user