Use slider controller

This commit is contained in:
1hitsong 2022-09-03 16:06:57 -04:00
parent fd5f988d6c
commit e2976b8ed2
12 changed files with 250 additions and 99 deletions

View File

@ -9,11 +9,11 @@ sub init()
m.top.observeField("height", "onHeightChanged")
m.top.observeField("width", "onWidthChanged")
m.top.observeField("padding", "onPaddingChanged")
m.top.observeField("focusedChild", "onFocusChanged")
m.top.observeField("focus", "onFocusChanged")
end sub
sub onFocusChanged()
if m.top.hasFocus()
if m.top.focus
m.buttonBackground.blendColor = m.top.focusBackground
else
m.buttonBackground.blendColor = m.top.background
@ -75,5 +75,13 @@ function onKeyEvent(key as string, press as boolean) as boolean
return true
end if
if key = "right" and m.top.hasFocus()
m.top.escape = "right"
end if
if key = "left" and m.top.hasFocus()
m.top.escape = "left"
end if
return false
end function

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="IconButton" extends="Group">
<children>
<Poster id="buttonBackground" uri="pkg:/images/white.9.png" />
<Poster id="buttonIcon" />
<Label id="buttonText" color="#aaaaaa" font="font:SmallestSystemFont" horizAlign="center" />
<Poster id="buttonBackground" uri="pkg:/images/white.9.png" />
<Poster id="buttonIcon" />
<Label id="buttonText" color="#aaaaaa" font="font:SmallestSystemFont" horizAlign="center" />
</children>
<interface>
<field id="background" type="color" value="" />
@ -14,6 +14,8 @@
<field id="width" type="integer" value="" />
<field id="icon" type="string" value="" />
<field id="selected" type="boolean" value="false" />
<field id="focus" type="boolean" />
<field id="escape" type="string" value="" />
</interface>
<script type="text/brightscript" uri="IconButton.brs" />
</component>

View File

@ -1,6 +1,5 @@
sub init()
getData()
m.top.infocus = false
end sub
function getData()
@ -35,7 +34,17 @@ function onKeyEvent(key as string, press as boolean) as boolean
if key = "up"
if m.top.itemFocused <= 4
m.top.infocus = false
m.top.escape = key
return true
end if
else if key = "left"
if m.top.itemFocused mod 5 = 0
m.top.escape = key
return true
end if
else if key = "right"
if m.top.itemFocused + 1 mod 5 = 0
m.top.escape = key
return true
end if
end if

View File

@ -2,7 +2,7 @@
<component name="AlbumGrid" extends="PosterGrid">
<interface>
<field id="MusicArtistAlbumData" type="assocarray" onChange="getData" />
<field id="infocus" type="boolean" />
<field id="escape" type="string" alwaysNotify="true" />
</interface>
<script type="text/brightscript" uri="AlbumGrid.brs" />
</component>

View File

@ -3,22 +3,23 @@ sub init()
setupMainNode()
setupButtons()
m.remoteButtonsActive = true
m.albumHeader = m.top.findNode("albumHeader")
m.albumHeader.text = tr("Albums")
m.albums = m.top.findNode("albums")
m.albums.observeField("infocus", "onAlbumFocusChange")
m.albums.observeField("escape", "onAlbumsEscape")
m.pageLoadAnimation = m.top.findNode("pageLoad")
m.pageLoadAnimation.control = "start"
m.showAlbumsAnimation = m.top.findNode("showAlbums")
m.hideAlbumsAnimation = m.top.findNode("hideAlbums")
m.sectionNavigation = m.top.findNode("sectionNavigation")
m.sectionNavigation.observeField("escape", "onSectionNavigationEscape")
m.sectionNavigation.observeField("selected", "onSectionNavigationSelected")
m.sectionScroller = m.top.findNode("sectionScroller")
' Load background image
m.LoadBackdropImageTask = CreateObject("roSGNode", "LoadItemsTask")
m.LoadBackdropImageTask.itemsToLoad = "backdropImage"
@ -30,6 +31,14 @@ sub init()
createDialogPallete()
end sub
sub onAlbumsEscape()
if m.albums.escape = "up"
m.sectionNavigation.selected = m.sectionScroller.displayedIndex - 1
else if m.albums.escape = "left"
m.sectionNavigation.setFocus(true)
end if
end sub
' Setup playback buttons, default to Play button selected
sub setupButtons()
m.buttonGrp = m.top.findNode("buttons")
@ -46,13 +55,13 @@ end sub
sub onButtonSelectedChange()
' Change previously selected button back to default image
if m.previouslySelectedButtonIndex > -1
selectedButton = m.buttonGrp.getChild(m.previouslySelectedButtonIndex)
selectedButton.setFocus(false)
previousSelectedButton = m.buttonGrp.getChild(m.previouslySelectedButtonIndex)
previousSelectedButton.focus = false
end if
' Change selected button image to selected image
selectedButton = m.buttonGrp.getChild(m.top.selectedButtonIndex)
selectedButton.setFocus(true)
selectedButton.focus = true
end sub
sub setupMainNode()
@ -119,36 +128,16 @@ sub onEllipsisChanged()
end if
end sub
sub onAlbumFocusChange()
if m.albums.infocus
m.albums.setFocus(true)
m.showAlbumsAnimation.control = "start"
return
end if
' Change selected button image to selected image
selectedButton = m.buttonGrp.getChild(m.top.selectedButtonIndex)
selectedButton.setFocus(true)
m.albums.setFocus(false)
m.hideAlbumsAnimation.control = "start"
end sub
sub onSectionNavigationEscape()
if m.sectionNavigation.escape = "right"
if m.albums.infocus
m.albums.setFocus(true)
return
end if
selectedButton = m.buttonGrp.getChild(0)
selectedButton.setFocus(true)
m.sectionNavigation.setFocus(false)
m.remoteButtonsActive = false
m.sectionScroller.focus = true
end if
end sub
sub onSectionNavigationSelected()
m.albums.infocus = (m.sectionNavigation.selected = 1)
m.sectionScroller.displayedIndex = m.sectionNavigation.selected
end sub
sub dscrShowFocus()
@ -191,9 +180,20 @@ sub createDialogPallete()
}
end sub
sub OnScreenShown()
m.sectionScroller.focus = true
if m.sectionScroller.displayedIndex = 0
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
m.top.selectedButtonIndex = 0
m.buttonGrp.setFocus(true)
end if
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if key = "left"
if m.buttonGrp.isInFocusChain()
if m.buttonGrp.isInFocusChain()
if key = "left"
if m.top.selectedButtonIndex > 0
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
m.top.selectedButtonIndex = m.top.selectedButtonIndex - 1
@ -201,7 +201,9 @@ function onKeyEvent(key as string, press as boolean) as boolean
end if
if press
m.buttonGrp.setFocus(false)
selectedButton = m.buttonGrp.getChild(m.top.selectedButtonIndex)
selectedButton.focus = false
m.sectionNavigation.setFocus(true)
return true
end if
@ -209,29 +211,27 @@ function onKeyEvent(key as string, press as boolean) as boolean
return false
end if
if m.albums.isInFocusChain()
if m.albums.itemFocused mod 5 = 0
m.sectionNavigation.setFocus(true)
if key = "right"
if m.top.pageContent.count() = 1 then return false
if m.buttonGrp.getChild(m.top.selectedButtonIndex).escape = "right"
m.buttonGrp.getChild(m.top.selectedButtonIndex).escape = ""
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
if m.top.selectedButtonIndex < m.buttonCount - 1
m.top.selectedButtonIndex = m.top.selectedButtonIndex + 1
end if
return true
end if
end if
end if
if m.buttonGrp.isInFocusChain()
if key = "down"
m.albums.infocus = true
return true
else if key = "right"
if m.sectionNavigation.escape = "right"
m.sectionNavigation.escape = ""
return true
end if
selectedButton = m.buttonGrp.getChild(m.top.selectedButtonIndex)
selectedButton.focus = false
if m.top.pageContent.count() = 1 then return false
m.previouslySelectedButtonIndex = m.top.selectedButtonIndex
if m.top.selectedButtonIndex < m.buttonCount - 1 then m.top.selectedButtonIndex = m.top.selectedButtonIndex + 1
return true
m.top.selectedButtonIndex = 0
m.sectionNavigation.selected = m.sectionScroller.displayedIndex + 1
end if
end if

View File

@ -1,22 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="ArtistView" extends="JFGroup">
<component name="ArtistView" extends="JFScreen">
<children>
<Poster id="backdrop" opacity=".4" loadDisplayMode="scaleToZoom" width="1920" height="1200" blendColor="#3f3f3f" />
<LayoutGroup id="toplevel" layoutDirection="vert" itemSpacings="[75]">
<LayoutGroup id="main_group" layoutDirection="horiz" itemSpacings="[125]">
<LayoutGroup layoutDirection="vert" itemSpacings="[75]">
<Label id="overview" wrap="true" lineSpacing="25" maxLines="6" width="1080" ellipsisText=" ... (Press * to read more)" />
<ButtonGroupHoriz id="buttons" itemSpacings="[20]">
<IconButton id="play" background="#070707" focusBackground="#00a4dc" padding="35" icon="pkg:/images/icons/play.png" text="Play" height="85" width="150" />
<IconButton id="instantMix" background="#070707" focusBackground="#00a4dc" padding="35" icon="pkg:/images/icons/instantMix.png" text="Instant Mix" height="85" width="150" />
</ButtonGroupHoriz>
<SectionScroller id="sectionScroller">
<Section id="slide-1" defaultFocusID="play">
<LayoutGroup id="toplevel" layoutDirection="vert" itemSpacings="[75]">
<LayoutGroup id="main_group" layoutDirection="horiz" itemSpacings="[125]">
<LayoutGroup layoutDirection="vert" itemSpacings="[75]">
<Label id="overview" wrap="true" lineSpacing="25" maxLines="6" width="1080" ellipsisText=" ... (Press * to read more)" />
<ButtonGroupHoriz id="buttons" itemSpacings="[20]">
<IconButton id="play" background="#070707" focusBackground="#00a4dc" padding="35" icon="pkg:/images/icons/play.png" text="Play" height="85" width="150" />
<IconButton id="instantMix" background="#070707" focusBackground="#00a4dc" padding="35" icon="pkg:/images/icons/instantMix.png" text="Instant Mix" height="85" width="150" />
</ButtonGroupHoriz>
</LayoutGroup>
<Poster id="artistImage" width="500" height="500" />
</LayoutGroup>
</LayoutGroup>
<Poster id="artistImage" width="500" height="500" />
</LayoutGroup>
</LayoutGroup>
<Rectangle id='albumRect' translation="[0, 1050]" width="1920" height="1080" color="#000000" opacity=".75" />
<Label id="albumHeader" translation="[120, 1100]" font="font:LargeSystemFont" />
<AlbumGrid id="albums" translation="[120, 1200]" vertFocusAnimationStyle="fixedFocus" basePosterSize="[300, 300]" numColumns="5" numRows="99" caption1NumLines="1" itemSpacing="[50, 50]" />
</Section>
<Section id="slide-2" translation="[0, 1050]" defaultFocusID="albums">
<Rectangle id='albumRect' translation="[0, 0]" width="1920" height="1080" color="#000000" opacity=".75" />
<Label id="albumHeader" translation="[120, 50]" font="font:LargeSystemFont" />
<AlbumGrid id="albums" translation="[120, 150]" vertFocusAnimationStyle="fixedFocus" basePosterSize="[300, 300]" numColumns="5" numRows="99" caption1NumLines="1" itemSpacing="[50, 50]" />
</Section>
</SectionScroller>
<bgv_ButtonGroupVert id="sectionNavigation" translation="[-100, 175]" itemSpacings="[10]">
<sob_SlideOutButton background="#070707" focusBackground="#00a4dc" highlightBackground="#555555" padding="20" icon="pkg:/images/icons/details.png" text="Details" height="50" width="60" />
@ -24,26 +32,10 @@
</bgv_ButtonGroupVert>
<Animation id="pageLoad" duration=".5" repeat="false">
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[0, 1050], [0, 750]]" fieldToInterp="albumRect.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[120, 1100], [120, 800]]" fieldToInterp="albumHeader.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[120, 1200], [120, 900]]" fieldToInterp="albums.translation" />
<Vector2DFieldInterpolator key="[0.0, .5]" keyValue="[[0, 1050], [0, 750]]" fieldToInterp="slide-2.translation" />
<Vector2DFieldInterpolator key="[0.5, 1.0]" keyValue="[[-100, 175], [40, 175]]" fieldToInterp="sectionNavigation.translation" />
</Animation>
<Animation id="showAlbums" duration="0.5" repeat="false">
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[0, 750], [0, 0]]" fieldToInterp="albumRect.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[120, 800], [120, 175]]" fieldToInterp="albumHeader.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[120, 900], [120, 275]]" fieldToInterp="albums.translation" />
<FloatFieldInterpolator key="[0.0, 1.0]" keyValue="[0.75, 0.95]" fieldToInterp="albumRect.opacity" />
</Animation>
<Animation id="hideAlbums" duration="0.5" repeat="false">
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[0, 0], [0, 750]]" fieldToInterp="albumRect.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[120, 175], [120, 800]]" fieldToInterp="albumHeader.translation" />
<Vector2DFieldInterpolator key="[0.0, 1.0]" keyValue="[[120, 275], [120, 900]]" fieldToInterp="albums.translation" />
<FloatFieldInterpolator key="[0.0, 1.0]" keyValue="[0.95, 0.75]" fieldToInterp="albumRect.opacity" />
</Animation>
</children>
<interface>
<field id="pageContent" type="node" onChange="pageContentChanged" />

View File

@ -0,0 +1,61 @@
sub init()
m.showFromBottomAnimation = m.top.findNode("showFromBottomAnimation")
m.showFromBottomPosition = m.top.findNode("showFromBottomPosition")
m.showFromBottomOpacity = m.top.findNode("showFromBottomOpacity")
m.showFromTopAnimation = m.top.findNode("showFromTopAnimation")
m.showFromTopPosition = m.top.findNode("showFromTopPosition")
m.showFromTopOpacity = m.top.findNode("showFromTopOpacity")
m.scrollOffTopAnimation = m.top.findNode("scrollOffTopAnimation")
m.scrollOffTopPosition = m.top.findNode("scrollOffTopPosition")
m.scrollOffTopOpacity = m.top.findNode("scrollOffTopOpacity")
m.scrollOffBottomAnimation = m.top.findNode("scrollOffBottomAnimation")
m.scrollOffBottomPosition = m.top.findNode("scrollOffBottomPosition")
m.scrollOffBottomOpacity = m.top.findNode("scrollOffBottomOpacity")
m.top.observeField("id", "onIDChange")
m.top.observeField("focusedChild", "onFocusChange")
end sub
sub onIDChange()
m.showFromBottomPosition.fieldToInterp = m.top.id + ".translation"
m.showFromBottomOpacity.fieldToInterp = m.top.id + ".opacity"
m.showFromTopPosition.fieldToInterp = m.top.id + ".translation"
m.showFromTopOpacity.fieldToInterp = m.top.id + ".opacity"
m.scrollOffTopPosition.fieldToInterp = m.top.id + ".translation"
m.scrollOffTopOpacity.fieldToInterp = m.top.id + ".opacity"
m.scrollOffBottomPosition.fieldToInterp = m.top.id + ".translation"
m.scrollOffBottomOpacity.fieldToInterp = m.top.id + ".opacity"
end sub
sub showFromTop()
m.showFromTopAnimation.control = "start"
end sub
sub showFromBottom()
m.showFromBottomAnimation.control = "start"
end sub
sub scrollOffBottom()
m.scrollOffBottomAnimation.control = "start"
end sub
sub scrollOffTop()
m.scrollOffTopAnimation.control = "start"
end sub
sub onFocusChange()
defaultFocusElement = m.top.findNode(m.top.defaultFocusID)
if isValid(defaultFocusElement)
defaultFocusElement.setFocus(m.top.isInFocusChain())
if isValid(defaultFocusElement.focus)
defaultFocusElement.focus = m.top.isInFocusChain()
end if
end if
end sub

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="Section" extends="JFGroup">
<children>
<Animation id="showFromBottomAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="showFromBottomPosition" key="[0.0, 1.0]" keyValue="[[0, 750], [0, 0]]" fieldToInterp="" />
<FloatFieldInterpolator id="showFromBottomOpacity" key="[0.0, 1.0]" keyValue="[0.75, 0.95]" fieldToInterp="" />
</Animation>
<Animation id="showFromTopAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="showFromTopPosition" key="[0.0, 1.0]" keyValue="[[0, -1080], [0, 0]]" fieldToInterp="" />
<FloatFieldInterpolator id="showFromTopOpacity" key="[0.0, 1.0]" keyValue="[0.75, 0.95]" fieldToInterp="" />
</Animation>
<Animation id="scrollOffBottomAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="scrollOffBottomPosition" key="[0.0, 1.0]" keyValue="[[0, 0], [0, 750]]" fieldToInterp="" />
<FloatFieldInterpolator id="scrollOffBottomOpacity" key="[0.0, 1.0]" keyValue="[0.95, 0.75]" fieldToInterp="" />
</Animation>
<Animation id="scrollOffTopAnimation" duration="0.5" repeat="false">
<Vector2DFieldInterpolator id="scrollOffTopPosition" key="[0.0, 1.0]" keyValue="[[0, 0], [0, -1080]]" fieldToInterp="" />
<FloatFieldInterpolator id="scrollOffTopOpacity" key="[0.0, 1.0]" keyValue="[0.95, 0.75]" fieldToInterp="" />
</Animation>
</children>
<interface>
<field id="defaultFocusID" type="string" />
<function name="showFromTop" />
<function name="showFromBottom" />
<function name="scrollOffTop" />
<function name="scrollOffBottom" />
</interface>
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
<script type="text/brightscript" uri="section.brs" />
</component>

View File

@ -0,0 +1,37 @@
sub init()
m.previouslyDisplayedSection = 0
end sub
sub onFocusChange()
if m.top.focus
m.top.getChild(m.top.displayedIndex).setFocus(true)
end if
end sub
sub displayedIndexChanged()
if not m.top.affectsFocus then return
if m.top.displayedIndex < 0
return
end if
if m.top.displayedIndex > (m.top.getChildCount() - 1)
return
end if
m.top.getChild(m.previouslyDisplayedSection).setFocus(false)
displayedSection = m.top.getChild(m.top.displayedIndex)
displayedSection.setFocus(true)
' Move sections either up or down depending on what index we're moving to
if m.top.displayedIndex > m.previouslyDisplayedSection
m.top.getChild(m.previouslyDisplayedSection).callFunc("scrollOffTop")
displayedSection.callFunc("showFromBottom")
else if m.top.displayedIndex < m.previouslyDisplayedSection
m.top.getChild(m.previouslyDisplayedSection).callFunc("scrollOffBottom")
displayedSection.callFunc("showFromTop")
end if
m.previouslyDisplayedSection = m.top.displayedIndex
end sub

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="SectionScroller" extends="JFGroup">
<children>
</children>
<interface>
<field id="focus" type="boolean" value="true" alwaysNotify="true" onChange="onFocusChange" />
<field id="affectsFocus" type="boolean" value="true" />
<field id="displayedIndex" type="integer" value="0" onChange="displayedIndexChanged" />
</interface>
<script type="text/brightscript" uri="pkg:/source/utils/misc.brs" />
<script type="text/brightscript" uri="sectionScroller.brs" />
</component>

14
package-lock.json generated
View File

@ -11,7 +11,7 @@
"license": "GPL-2.0",
"dependencies": {
"api": "npm:jellyfin-api-bs-client@^1.0.5",
"bgv": "npm:button-group-vert@^1.0.1",
"bgv": "npm:button-group-vert@^1.0.2",
"brighterscript-formatter": "^1.6.8",
"intKeyboard": "npm:integer-keyboard@^1.0.12",
"sob": "npm:slide-out-button@^1.0.1"
@ -784,9 +784,9 @@
},
"node_modules/bgv": {
"name": "button-group-vert",
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/button-group-vert/-/button-group-vert-1.0.1.tgz",
"integrity": "sha512-PtOAglZ7w1ebPR5PVxtPNfADydhwU9pJ8X4KKkaqvuDwNMOcD2LkwpgCH0nuGm/yzrws9Kqkqf86IC5mZh8xsQ=="
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/button-group-vert/-/button-group-vert-1.0.2.tgz",
"integrity": "sha512-pfrUYI/aFubtjhA8I08qNCtDluyIScksldR15icR7Pj24tNELYCYXE7M0jaU7xgdiFAhZJcYuB3aCXzyI1CoMw=="
},
"node_modules/binary-extensions": {
"version": "1.13.1",
@ -6927,9 +6927,9 @@
}
},
"bgv": {
"version": "npm:button-group-vert@1.0.1",
"resolved": "https://registry.npmjs.org/button-group-vert/-/button-group-vert-1.0.1.tgz",
"integrity": "sha512-PtOAglZ7w1ebPR5PVxtPNfADydhwU9pJ8X4KKkaqvuDwNMOcD2LkwpgCH0nuGm/yzrws9Kqkqf86IC5mZh8xsQ=="
"version": "npm:button-group-vert@1.0.2",
"resolved": "https://registry.npmjs.org/button-group-vert/-/button-group-vert-1.0.2.tgz",
"integrity": "sha512-pfrUYI/aFubtjhA8I08qNCtDluyIScksldR15icR7Pj24tNELYCYXE7M0jaU7xgdiFAhZJcYuB3aCXzyI1CoMw=="
},
"binary-extensions": {
"version": "1.13.1",

View File

@ -36,7 +36,7 @@
"homepage": "https://github.com/jellyfin/jellyfin-roku#readme",
"dependencies": {
"api": "npm:jellyfin-api-bs-client@^1.0.5",
"bgv": "npm:button-group-vert@^1.0.1",
"bgv": "npm:button-group-vert@^1.0.2",
"brighterscript-formatter": "^1.6.8",
"sob": "npm:slide-out-button@^1.0.1",
"intKeyboard": "npm:integer-keyboard@^1.0.12"