diff --git a/Makefile b/Makefile
index e036d4121..a6e4a2e2a 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
# If you want to get_images, you'll also need convert from ImageMagick
##########################################################################
-VERSION := 2.2.2
+VERSION := 2.2.5
## usage
diff --git a/components/ItemGrid/LoadVideoContentTask.bs b/components/ItemGrid/LoadVideoContentTask.bs
index 69a5cce0c..30840e341 100644
--- a/components/ItemGrid/LoadVideoContentTask.bs
+++ b/components/ItemGrid/LoadVideoContentTask.bs
@@ -54,6 +54,7 @@ sub loadItems()
forceTranscoding = false
m.top.content = [LoadItems_VideoPlayer(id, mediaSourceId, audio_stream_idx, forceTranscoding)]
+ m.top.forceMp3 = false
end sub
function LoadItems_VideoPlayer(id as string, mediaSourceId = invalid as dynamic, audio_stream_idx = 1 as integer, forceTranscoding = false as boolean) as dynamic
@@ -115,6 +116,21 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s
end if
end if
+ if videotype = "tvchannel" and isValid(meta.json) and isValid(meta.json.CurrentProgram)
+ if isValid(meta.json.CurrentProgram.Name)
+ meta.title = `${meta.title}: ${meta.json.CurrentProgram.Name}`
+ end if
+ if isValid(meta.json.CurrentProgram.ParentIndexNumber)
+ video.seasonNumber = meta.json.CurrentProgram.ParentIndexNumber
+ end if
+ if isValid(meta.json.CurrentProgram.IndexNumber)
+ video.episodeNumber = meta.json.CurrentProgram.IndexNumber
+ end if
+ if isValid(meta.json.CurrentProgram.IndexNumberEnd)
+ video.episodeNumberEnd = meta.json.CurrentProgram.IndexNumberEnd
+ end if
+ end if
+
video.chapters = meta.json.Chapters
video.content.title = meta.title
video.showID = meta.showID
@@ -157,7 +173,7 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s
if not isValid(mediaSourceId) then mediaSourceId = video.id
if meta.live then mediaSourceId = ""
- m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
+ m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition, m.top.forceMp3)
if not isValid(m.playbackInfo)
video.errorMsg = "Error loading playback info"
video.content = invalid
@@ -174,7 +190,7 @@ sub LoadItems_AddVideoContent(video as object, mediaSourceId as dynamic, audio_s
video.SelectedSubtitle = defaultSubtitleIndex
subtitle_idx = defaultSubtitleIndex
- m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
+ m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition, m.top.forceMp3)
if not isValid(m.playbackInfo)
video.errorMsg = "Error loading playback info"
video.content = invalid
diff --git a/components/ItemGrid/LoadVideoContentTask.xml b/components/ItemGrid/LoadVideoContentTask.xml
index f935c3e33..20a6317ac 100644
--- a/components/ItemGrid/LoadVideoContentTask.xml
+++ b/components/ItemGrid/LoadVideoContentTask.xml
@@ -18,6 +18,7 @@
+
diff --git a/components/home/HomeItem.bs b/components/home/HomeItem.bs
index 70a5918e4..046ee081e 100644
--- a/components/home/HomeItem.bs
+++ b/components/home/HomeItem.bs
@@ -62,10 +62,15 @@ sub itemContentChanged()
if isValid(m.unplayedCount) then m.unplayedCount.visible = false
itemData = m.top.itemContent
if itemData = invalid then return
- userSettings = m.global.session.user.settings
itemData.Title = itemData.name ' Temporarily required while we move from "HomeItem" to "JFContentItem"
+ ' validate to prevent crash
+ userSettings = invalid
+ if isValid(m.global) and isValid(m.global.session) and isValid(m.global.session.user) and isValid(m.global.session.user.settings)
+ userSettings = m.global.session.user.settings
+ end if
+
' validate to prevent crash
if not isValid(m.itemPoster) then initItemPoster()
if not isValid(m.itemText) then initItemText()
@@ -164,17 +169,22 @@ sub itemContentChanged()
drawProgressBar(itemData)
end if
- if userSettings["ui.general.episodeimagesnextup"] = "webclient"
- tmpSetting = m.global.session.user.Configuration.useEpisodeImagesInNextUpAndResume
- if isValid(tmpSetting) and tmpSetting
- m.itemPoster.uri = itemData.thumbnailURL
- else
+ if isValid(userSettings)
+ if userSettings["ui.general.episodeimagesnextup"] = "webclient"
+ tmpSetting = m.global.session.user.Configuration.useEpisodeImagesInNextUpAndResume
+ if isValid(tmpSetting) and tmpSetting
+ m.itemPoster.uri = itemData.thumbnailURL
+ else
+ m.itemPoster.uri = itemData.widePosterURL
+ end if
+ else if userSettings["ui.general.episodeimagesnextup"] = "show"
m.itemPoster.uri = itemData.widePosterURL
+ else if userSettings["ui.general.episodeimagesnextup"] = "episode"
+ m.itemPoster.uri = itemData.thumbnailURL
end if
- else if userSettings["ui.general.episodeimagesnextup"] = "show"
+ else
+ ' use show image if user settings are invalid for some reason
m.itemPoster.uri = itemData.widePosterURL
- else if userSettings["ui.general.episodeimagesnextup"] = "episode"
- m.itemPoster.uri = itemData.thumbnailURL
end if
' Set Series and Episode Number for Extra Text
diff --git a/components/home/HomeRows.bs b/components/home/HomeRows.bs
index 9afdc4f0f..ec2cf69c5 100644
--- a/components/home/HomeRows.bs
+++ b/components/home/HomeRows.bs
@@ -51,20 +51,30 @@ sub loadLibraries()
end sub
sub updateSize()
- m.top.translation = [111, 180]
- itemHeight = 330
-
- 'Set width of Rows to cut off at edge of Safe Zone
- m.top.itemSize = [1703, itemHeight]
+ uiRowLayout = m.global.session.user.settings["ui.row.layout"]
+
+ if isValid(uiRowLayout)
+ if uiRowLayout = "fullwidth"
+ m.top.translation = [0, 180]
+ ' rows take up full width of the screen
+ m.top.itemSize = [1920, 330]
+ ' align with edge of "action" safe zone
+ m.top.focusXOffset = [96]
+ m.top.rowLabelOffset = [96, 20]
+ else
+ ' original layout
+ m.top.translation = [111, 180]
+ m.top.itemSize = [1703, 330]
+ ' reset to defaults
+ m.top.focusXOffset = []
+ m.top.rowLabelOffset = [0, 20]
+ end if
+ end if
' spacing between rows
m.top.itemSpacing = [0, 105]
-
' spacing between items in a row
- m.top.rowItemSpacing = [20, 0]
-
- ' Default size to wide poster, the most used size
- m.top.rowItemSize = homeRowItemSizes.WIDE_POSTER
+ m.top.rowItemSpacing = [21, 0]
m.top.visible = true
end sub
@@ -411,15 +421,6 @@ end sub
' createContinueWatchingRow: Creates a row displaying items the user can continue watching
'
sub createContinueWatchingRow()
- sectionName = tr("Continue Watching")
-
- if not sectionExists(sectionName)
- nextUpRow = m.top.content.CreateChild("HomeRow")
- nextUpRow.title = sectionName
- nextUpRow.imageWidth = homeRowItemSizes.WIDE_POSTER[0]
- nextUpRow.cursorSize = homeRowItemSizes.WIDE_POSTER
- end if
-
' Load the Continue Watching Data
m.LoadContinueWatchingTask.observeField("content", "updateContinueWatchingItems")
m.LoadContinueWatchingTask.control = "RUN"
@@ -455,6 +456,8 @@ end sub
sub updateHomeRows()
' Hide the row counter to prevent flicker. We'll show it once loading timer fires
m.top.showRowCounter = [false]
+ m.top.visible = false
+ updateSize()
processUserSections()
end sub
diff --git a/components/settings/settings.bs b/components/settings/settings.bs
index 55239d77d..200cf2bff 100644
--- a/components/settings/settings.bs
+++ b/components/settings/settings.bs
@@ -251,5 +251,19 @@ function onKeyEvent(key as string, press as boolean) as boolean
settingSelected()
end if
+ if key = "up" and m.settingsMenu.focusedChild <> invalid and m.settingsMenu.itemFocused = 0
+ m.settingsMenu.jumpToItem = m.settingsMenu.content.getChildCount() - 1
+
+ return true
+ end if
+
+ if key = "down" and m.settingsMenu.focusedChild <> invalid
+ if m.settingsMenu.itemFocused = m.settingsMenu.content.getChildCount() - 1
+ m.settingsMenu.jumpToItem = 0
+
+ return true
+ end if
+ end if
+
return false
end function
diff --git a/components/tvshows/TVListDetails.bs b/components/tvshows/TVListDetails.bs
index 38e27702f..9d17dbe08 100644
--- a/components/tvshows/TVListDetails.bs
+++ b/components/tvshows/TVListDetails.bs
@@ -108,7 +108,12 @@ sub itemContentChanged()
if isValid(itemData.MediaSources)
for i = 0 to itemData.MediaSources.Count() - 1
if item.selectedVideoStreamId = itemData.MediaSources[i].id and isValid(itemData.MediaSources[i].MediaStreams[0])
- m.videoCodec.text = tr("Video") + ": " + itemData.MediaSources[i].MediaStreams[0].DisplayTitle
+ m.videoCodec.text = tr("Video") + ": "
+ if isValid(itemData.MediaSources[i].MediaStreams[0].DisplayTitle)
+ m.videoCodec.text = m.videoCodec.text + itemData.MediaSources[i].MediaStreams[0].DisplayTitle
+ else
+ m.videoCodec.text = m.videoCodec.text + tr("N/A")
+ end if
SetupAudioDisplay(itemData.MediaSources[i].MediaStreams, item.selectedAudioStreamIndex)
exit for
end if
diff --git a/components/video/VideoPlayerView.bs b/components/video/VideoPlayerView.bs
index 109a5569c..6340cd9b0 100644
--- a/components/video/VideoPlayerView.bs
+++ b/components/video/VideoPlayerView.bs
@@ -34,7 +34,6 @@ sub init()
m.playbackTimer = m.top.findNode("playbackTimer")
m.bufferCheckTimer = m.top.findNode("bufferCheckTimer")
- m.top.observeField("state", "onState")
m.top.observeField("content", "onContentChange")
m.top.observeField("selectedSubtitle", "onSubtitleChange")
m.top.observeField("audioIndex", "onAudioIndexChange")
@@ -397,6 +396,8 @@ sub onVideoContentLoaded()
return
end if
+ m.top.observeField("state", "onState")
+
m.top.content = videoContent[0].content
m.top.PlaySessionId = videoContent[0].PlaySessionId
m.top.videoId = videoContent[0].id
@@ -412,7 +413,7 @@ sub onVideoContentLoaded()
m.osd.itemTitleText = m.top.content.title
' If video is an episode, attempt to add season and episode numbers to OSD
- if m.top.content.contenttype = 4
+ if m.top.content.contenttype = 4 or m.top.content.live
if isValid(videoContent[0].seasonNumber)
m.osd.seasonNumber = videoContent[0].seasonNumber
end if
@@ -438,13 +439,9 @@ sub onVideoContentLoaded()
populateChapterMenu()
- if m.LoadMetaDataTask.isIntro
- ' Disable trackplay bar for intro videos
- m.top.enableTrickPlay = false
- else
- ' Allow custom captions for non intro videos
- m.top.allowCaptions = true
- end if
+
+ ' Allow custom captions for all videos including intro videos
+ m.top.allowCaptions = true
' Allow default subtitles
m.top.unobserveField("selectedSubtitle")
@@ -466,8 +463,8 @@ sub onVideoContentLoaded()
' If IsForced, make sure to remember the Roku global setting so we
' can set it back when the video is done playing.
m.originalClosedCaptionState = m.top.globalCaptionMode
- m.top.globalCaptionMode = "On"
end if
+ m.top.globalCaptionMode = "On"
m.top.subtitleTrack = m.top.availableSubtitleTracks[availableSubtitleTrackIndex].TrackName
end if
end if
@@ -639,6 +636,17 @@ sub onState(msg)
if not m.playReported and m.top.transcodeAvailable
m.top.retryWithTranscoding = true ' If playback was not reported, retry with transcoding
+ else if m.top.errorStr = "decoder:pump:Unsupported AAC stream."
+ m.log.info("retrying video with mp3 audio stream", m.currentItem.id, m.top.SelectedSubtitle, m.top.audioIndex)
+
+ m.top.unobserveField("state")
+ m.LoadMetaDataTask.forceMp3 = true
+ m.LoadMetaDataTask.selectedSubtitleIndex = m.top.SelectedSubtitle
+ m.LoadMetaDataTask.selectedAudioStreamIndex = m.top.audioIndex
+ m.LoadMetaDataTask.itemId = m.currentItem.id
+ m.LoadMetaDataTask.observeField("content", "onVideoContentLoaded")
+
+ m.LoadMetaDataTask.control = "RUN"
else
' If an error was encountered, Display dialog
showPlaybackErrorDialog(tr("Error During Playback"))
@@ -825,62 +833,52 @@ function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
if key = "down" and not m.top.trickPlayBar.visible
- if not m.LoadMetaDataTask.isIntro
- ' Don't allow user to open menu prior to video loading
- if not stateAllowsOSD() then return true
+ ' Don't allow user to open menu prior to video loading
+ if not stateAllowsOSD() then return true
- m.osd.visible = true
- m.osd.hasFocus = true
- m.osd.setFocus(true)
- return true
- end if
+ m.osd.visible = true
+ m.osd.hasFocus = true
+ m.osd.setFocus(true)
+ return true
else if key = "up" and not m.top.trickPlayBar.visible
- if not m.LoadMetaDataTask.isIntro
- ' Don't allow user to open menu prior to video loading
- if not stateAllowsOSD() then return true
+ ' Don't allow user to open menu prior to video loading
+ if not stateAllowsOSD() then return true
- m.osd.visible = true
- m.osd.hasFocus = true
- m.osd.setFocus(true)
- return true
- end if
+ m.osd.visible = true
+ m.osd.hasFocus = true
+ m.osd.setFocus(true)
+ return true
else if key = "OK" and not m.top.trickPlayBar.visible
- if not m.LoadMetaDataTask.isIntro
- ' Don't allow user to open menu prior to video loading
- if not stateAllowsOSD() then return true
-
- ' Show OSD, but don't pause video
- m.osd.visible = true
- m.osd.hasFocus = true
- m.osd.setFocus(true)
- return true
- end if
+ ' Don't allow user to open menu prior to video loading
+ if not stateAllowsOSD() then return true
- return false
+ ' Show OSD, but don't pause video
+ m.osd.visible = true
+ m.osd.hasFocus = true
+ m.osd.setFocus(true)
+ return true
end if
' Disable OSD for intro videos
- if not m.LoadMetaDataTask.isIntro
- if key = "play" and not m.top.trickPlayBar.visible
+ if key = "play" and not m.top.trickPlayBar.visible
- ' Don't allow user to open menu prior to video loading
- if not stateAllowsOSD() then return true
-
- ' If video is paused, resume it and don't show OSD
- if m.top.state = "paused"
- m.top.control = "resume"
- return true
- end if
+ ' Don't allow user to open menu prior to video loading
+ if not stateAllowsOSD() then return true
- ' Pause video and show OSD
- m.top.control = "pause"
- m.osd.visible = true
- m.osd.hasFocus = true
- m.osd.setFocus(true)
+ ' If video is paused, resume it and don't show OSD
+ if m.top.state = "paused"
+ m.top.control = "resume"
return true
end if
+
+ ' Pause video and show OSD
+ m.top.control = "pause"
+ m.osd.visible = true
+ m.osd.hasFocus = true
+ m.osd.setFocus(true)
+ return true
end if
if key = "back"
diff --git a/components/video/VideoPlayerView.xml b/components/video/VideoPlayerView.xml
index 2f7b67fe4..d66e9126c 100644
--- a/components/video/VideoPlayerView.xml
+++ b/components/video/VideoPlayerView.xml
@@ -20,6 +20,7 @@
+
diff --git a/docs/api/components_ItemGrid_LoadVideoContentTask.bs.html b/docs/api/components_ItemGrid_LoadVideoContentTask.bs.html
index 001b828b4..bcc45e19a 100644
--- a/docs/api/components_ItemGrid_LoadVideoContentTask.bs.html
+++ b/docs/api/components_ItemGrid_LoadVideoContentTask.bs.html
@@ -56,6 +56,7 @@
forceTranscoding = false
m.top.content = [LoadItems_VideoPlayer(id, mediaSourceId, audio_stream_idx, forceTranscoding)]
+ m.top.forceMp3 = false
end sub
function LoadItems_VideoPlayer(id as string, mediaSourceId = invalid as dynamic, audio_stream_idx = 1 as integer, forceTranscoding = false as boolean) as dynamic
@@ -117,6 +118,21 @@
end if
end if
+ if videotype = "tvchannel" and isValid(meta.json) and isValid(meta.json.CurrentProgram)
+ if isValid(meta.json.CurrentProgram.Name)
+ meta.title = `${meta.title}: ${meta.json.CurrentProgram.Name}`
+ end if
+ if isValid(meta.json.CurrentProgram.ParentIndexNumber)
+ video.seasonNumber = meta.json.CurrentProgram.ParentIndexNumber
+ end if
+ if isValid(meta.json.CurrentProgram.IndexNumber)
+ video.episodeNumber = meta.json.CurrentProgram.IndexNumber
+ end if
+ if isValid(meta.json.CurrentProgram.IndexNumberEnd)
+ video.episodeNumberEnd = meta.json.CurrentProgram.IndexNumberEnd
+ end if
+ end if
+
video.chapters = meta.json.Chapters
video.content.title = meta.title
video.showID = meta.showID
@@ -159,7 +175,7 @@
if not isValid(mediaSourceId) then mediaSourceId = video.id
if meta.live then mediaSourceId = ""
- m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
+ m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition, m.top.forceMp3)
if not isValid(m.playbackInfo)
video.errorMsg = "Error loading playback info"
video.content = invalid
@@ -176,7 +192,7 @@
video.SelectedSubtitle = defaultSubtitleIndex
subtitle_idx = defaultSubtitleIndex
- m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition)
+ m.playbackInfo = ItemPostPlaybackInfo(video.id, mediaSourceId, audio_stream_idx, subtitle_idx, playbackPosition, m.top.forceMp3)
if not isValid(m.playbackInfo)
video.errorMsg = "Error loading playback info"
video.content = invalid
diff --git a/docs/api/components_home_HomeItem.bs.html b/docs/api/components_home_HomeItem.bs.html
index 6f28722ee..0dfb4452f 100644
--- a/docs/api/components_home_HomeItem.bs.html
+++ b/docs/api/components_home_HomeItem.bs.html
@@ -64,10 +64,15 @@
if isValid(m.unplayedCount) then m.unplayedCount.visible = false
itemData = m.top.itemContent
if itemData = invalid then return
- userSettings = m.global.session.user.settings
itemData.Title = itemData.name ' Temporarily required while we move from "HomeItem" to "JFContentItem"
+ ' validate to prevent crash
+ userSettings = invalid
+ if isValid(m.global) and isValid(m.global.session) and isValid(m.global.session.user) and isValid(m.global.session.user.settings)
+ userSettings = m.global.session.user.settings
+ end if
+
' validate to prevent crash
if not isValid(m.itemPoster) then initItemPoster()
if not isValid(m.itemText) then initItemText()
@@ -166,17 +171,22 @@
drawProgressBar(itemData)
end if
- if userSettings["ui.general.episodeimagesnextup"] = "webclient"
- tmpSetting = m.global.session.user.Configuration.useEpisodeImagesInNextUpAndResume
- if isValid(tmpSetting) and tmpSetting
- m.itemPoster.uri = itemData.thumbnailURL
- else
+ if isValid(userSettings)
+ if userSettings["ui.general.episodeimagesnextup"] = "webclient"
+ tmpSetting = m.global.session.user.Configuration.useEpisodeImagesInNextUpAndResume
+ if isValid(tmpSetting) and tmpSetting
+ m.itemPoster.uri = itemData.thumbnailURL
+ else
+ m.itemPoster.uri = itemData.widePosterURL
+ end if
+ else if userSettings["ui.general.episodeimagesnextup"] = "show"
m.itemPoster.uri = itemData.widePosterURL
+ else if userSettings["ui.general.episodeimagesnextup"] = "episode"
+ m.itemPoster.uri = itemData.thumbnailURL
end if
- else if userSettings["ui.general.episodeimagesnextup"] = "show"
+ else
+ ' use show image if user settings are invalid for some reason
m.itemPoster.uri = itemData.widePosterURL
- else if userSettings["ui.general.episodeimagesnextup"] = "episode"
- m.itemPoster.uri = itemData.thumbnailURL
end if
' Set Series and Episode Number for Extra Text
diff --git a/docs/api/components_home_HomeRows.bs.html b/docs/api/components_home_HomeRows.bs.html
index abae358e9..70d322dca 100644
--- a/docs/api/components_home_HomeRows.bs.html
+++ b/docs/api/components_home_HomeRows.bs.html
@@ -53,20 +53,30 @@
end sub
sub updateSize()
- m.top.translation = [111, 180]
- itemHeight = 330
-
- 'Set width of Rows to cut off at edge of Safe Zone
- m.top.itemSize = [1703, itemHeight]
+ uiRowLayout = m.global.session.user.settings["ui.row.layout"]
+
+ if isValid(uiRowLayout)
+ if uiRowLayout = "fullwidth"
+ m.top.translation = [0, 180]
+ ' rows take up full width of the screen
+ m.top.itemSize = [1920, 330]
+ ' align with edge of "action" safe zone
+ m.top.focusXOffset = [96]
+ m.top.rowLabelOffset = [96, 20]
+ else
+ ' original layout
+ m.top.translation = [111, 180]
+ m.top.itemSize = [1703, 330]
+ ' reset to defaults
+ m.top.focusXOffset = []
+ m.top.rowLabelOffset = [0, 20]
+ end if
+ end if
' spacing between rows
m.top.itemSpacing = [0, 105]
-
' spacing between items in a row
- m.top.rowItemSpacing = [20, 0]
-
- ' Default size to wide poster, the most used size
- m.top.rowItemSize = homeRowItemSizes.WIDE_POSTER
+ m.top.rowItemSpacing = [21, 0]
m.top.visible = true
end sub
@@ -413,15 +423,6 @@
' createContinueWatchingRow: Creates a row displaying items the user can continue watching
'
sub createContinueWatchingRow()
- sectionName = tr("Continue Watching")
-
- if not sectionExists(sectionName)
- nextUpRow = m.top.content.CreateChild("HomeRow")
- nextUpRow.title = sectionName
- nextUpRow.imageWidth = homeRowItemSizes.WIDE_POSTER[0]
- nextUpRow.cursorSize = homeRowItemSizes.WIDE_POSTER
- end if
-
' Load the Continue Watching Data
m.LoadContinueWatchingTask.observeField("content", "updateContinueWatchingItems")
m.LoadContinueWatchingTask.control = "RUN"
@@ -457,6 +458,8 @@
sub updateHomeRows()
' Hide the row counter to prevent flicker. We'll show it once loading timer fires
m.top.showRowCounter = [false]
+ m.top.visible = false
+ updateSize()
processUserSections()
end sub
diff --git a/docs/api/components_settings_settings.bs.html b/docs/api/components_settings_settings.bs.html
index 363d8d16f..554f03621 100644
--- a/docs/api/components_settings_settings.bs.html
+++ b/docs/api/components_settings_settings.bs.html
@@ -253,6 +253,20 @@
settingSelected()
end if
+ if key = "up" and m.settingsMenu.focusedChild <> invalid and m.settingsMenu.itemFocused = 0
+ m.settingsMenu.jumpToItem = m.settingsMenu.content.getChildCount() - 1
+
+ return true
+ end if
+
+ if key = "down" and m.settingsMenu.focusedChild <> invalid
+ if m.settingsMenu.itemFocused = m.settingsMenu.content.getChildCount() - 1
+ m.settingsMenu.jumpToItem = 0
+
+ return true
+ end if
+ end if
+
return false
end function