Updated design and functionality of ItemGrid for Movies

This commit is contained in:
Neil Burrows 2020-06-10 17:43:32 +01:00
parent ba05444f44
commit 68e322b1ca
15 changed files with 351 additions and 70 deletions

View File

@ -0,0 +1,40 @@
sub init()
m.itemPoster = m.top.findNode("itemPoster")
m.itemText = m.top.findNode("itemText")
end sub
sub itemContentChanged()
itemData = m.top.itemContent
if itemData = invalid then return
itemPoster = m.top.findNode("itemPoster")
if itemData.type = "Movie" then
itemPoster.uri = itemData.PosterUrl
m.itemText.text = itemData.Title
return
end if
print "Unhandled Item Type: " + itemData.type
end sub
'
'Resize Poster and Title Vivbility on focus change
sub focusChanged()
if m.top.itemHasFocus = true then
m.itemPoster.width = 295
m.itemPoster.height = 440
m.itemPoster.translation = [0,0]
m.itemText.visible = true
else
m.itemPoster.width = 250
m.itemPoster.height = 375
m.itemPoster.translation = [21,35]
m.itemText.visible = false
end if
end sub

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="GridItem" extends="Group">
<children>
<Poster id="itemPoster" width="250" height="375" translation="[21,35]" loadDisplayMode="scaleToZoom" loadingBitmapUri="pkg:/images/PosterLoading.png" failedBitmapUri="pkg:/images/PosterFailed.png" />
<ScrollingLabel id="itemText" horizAlign="center" font="font:SmallSystemFont" maxWidth="295" translation="[0,442]" repeatCount="-1" visible="false" />
</children>
<interface>
<field id="itemContent" type="node" onChange="itemContentChanged" />
<field id="itemHasFocus" type="boolean" onChange="focusChanged" />
</interface>
<script type="text/brightscript" uri="GridItem.brs" />
</component>

View File

@ -0,0 +1,133 @@
sub init()
m.itemGrid = m.top.findNode("itemGrid")
m.backdrop = m.top.findNode("backdrop")
m.newBackdrop = m.top.findNode("backdropTransition")
m.swapAnimation = m.top.findNode("backroundSwapAnimation")
m.swapAnimation.observeField("state", "swapDone")
m.loadedRows = 0
m.loadedItems = 0
m.data = CreateObject("roSGNode", "ContentNode")
m.itemGrid.content = m.data
m.itemGrid.setFocus(true)
m.itemGrid.observeField("itemFocused", "onItemFocused")
m.itemGrid.observeField("itemSelected", "onItemSelected")
m.newBackdrop.observeField("loadStatus", "newBGLoaded")
'Background Image Queued for loading
m.queuedBGUri = ""
m.loadItemsTask = createObject("roSGNode", "LoadItemsTask2")
end sub
'
'Load initial set of Data
sub loadInitialItems()
m.loadItemsTask.itemId = m.top.itemId
m.loadItemsTask.observeField("content", "ItemDataLoaded")
m.loadItemsTask.itemType = "Movie"
m.loadItemsTask.control = "RUN"
end sub
'
'Handle loaded data, and add to Grid
sub ItemDataLoaded(msg)
itemData = msg.GetData()
data = msg.getField()
if itemData = invalid then
m.Loading = false
return
end if
for each item in itemData
m.data.appendChild(item)
end for
'Update the stored counts
m.loadedItems = m.itemGrid.content.getChildCount()
m.loadedRows = m.loadedItems / m.itemGrid.numColumns
m.Loading = false
end sub
'
'Set Background Image
sub SetBackground(backgroundUri as string)
'If a new image is being loaded, or transitioned to, store URL to load next
if m.swapAnimation.state <> "stopped" or m.newBackdrop.loadStatus = "loading" then
m.queuedBGUri = backgroundUri
return
end if
m.newBackdrop.uri = backgroundUri
end sub
'
'Handle new item being focused
sub onItemFocused()
focusedRow = CInt(m.itemGrid.itemFocused / m.itemGrid.numColumns) + 1
' Set Background
itemInt = m.itemGrid.itemFocused
SetBackground(m.itemGrid.content.getChild(m.itemGrid.itemFocused).backdropUrl)
' Load more data if focus is within last 3 rows, and there are more items to load
if focusedRow >= m.loadedRows - 3 and m.loadeditems < m.loadItemsTask.totalRecordCount then
loadMoreData()
end if
end sub
'
'When Image Loading Status changes
sub newBGLoaded()
'If image load was sucessful, start the fade swap
if m.newBackdrop.loadStatus = "ready"
m.swapAnimation.control = "start"
end if
end sub
'
'Swap Complete
sub swapDone()
if m.swapAnimation.state = "stopped" then
'Set main BG node image and hide transitioning node
m.backdrop.uri = m.newBackdrop.uri
m.backdrop.opacity = 0.25
m.newBackdrop.opacity = 0
'If there is another one to load
if m.newBackdrop.uri <> m.queuedBGUri and m.queuedBGUri <> "" then
SetBackground(m.queuedBGUri)
m.queuedBGUri = ""
end if
end if
end sub
'
'Load next set of items
sub loadMoreData()
if m.Loading = true then return
m.Loading = true
m.loadItemsTask.startIndex = m.loadedItems
m.loadItemsTask.control = "RUN"
end sub
'
'Item Selected
sub onItemSelected()
m.top.selectedItem = m.itemGrid.content.getChild(m.itemGrid.itemSelected)
end sub

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="ItemGrid2" extends="JFGroup">
<children>
<poster id="backdrop"
loadDisplayMode="scaleToFill"
width="1920"
height="1080"
opacity="0.25"
/>
<poster id="backdropTransition"
loadDisplayMode="scaleToFill"
width="1920"
height="1080"
opacity="0.25"
/>
<MarkupGrid
id = "itemGrid"
translation = "[ 96, 160 ]"
itemComponentName = "GridItem"
numColumns = "6"
numRows = "5"
vertFocusAnimationStyle = "fixed"
itemSize = "[ 290, 425 ]"
itemSpacing = "[ 0, 45 ]"
drawFocusFeedback = "false" />
<OptionsSlider id="options" />
<Animation id="backroundSwapAnimation" duration="1" repeat="false" easeFunction="linear" >
<FloatFieldInterpolator id = "fadeinLoading" key="[0.0, 1.0]" keyValue="[ 0.00, 0.25 ]" fieldToInterp="backdropTransition.opacity" />
<FloatFieldInterpolator id = "fadeoutLoaded" key="[0.0, 1.0]" keyValue="[ 0.25, 0.00 ]" fieldToInterp="backdrop.opacity" />
</Animation>
</children>
<interface>
<field id="itemId" type="string" onChange="loadInitialItems" />
<field id="selectedItem" type="node" alwaysNotify="true" />
</interface>
<script type="text/brightscript" uri="ItemGrid2.brs" />
</component>

View File

@ -0,0 +1,54 @@
sub init()
m.top.functionName = "loadItems"
end sub
sub loadItems()
results = []
sort_order = get_user_setting("movie_sort_order", "Ascending")
sort_field = get_user_setting("movie_sort_field", "SortName")
params = {
limit: m.top.limit,
StartIndex: m.top.startIndex,
parentid: m.top.itemId,
SortBy: sort_field,
SortOrder: sort_order,
recursive: false
}
if m.top.ItemType <> "" then
params.append({ IncludeItemTypes: m.top.ItemType})
end if
url = Substitute("Users/{0}/Items/", get_setting("active_user"))
resp = APIRequest(url, params)
data = getJson(resp)
if data.TotalRecordCount <> invalid then
m.top.totalRecordCount = data.TotalRecordCount
end if
for each item in data.Items
tmp = invalid
if item.Type = "Movie" then
tmp = CreateObject("roSGNode", "MovieData")
else
print "Unknown Type: " item.Type
end if
if tmp <> invalid then
tmp.json = item
results.push(tmp)
end if
end for
m.top.content = results
end sub

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="LoadItemsTask2" extends="Task">
<interface>
<field id="itemId" type="string" />
<field id="startIndex" type="integer" value="0" />
<field id="itemType" type="string" value="" />
<field id="limit" type="integer" value="36" />
<field id="metadata" type="associativearray" />
<!-- Total records available from server-->
<field id="totalRecordCount" type="int" value="-1" />
<field id="content" type="array" />
</interface>
<script type="text/brightscript" uri="LoadItemsTask2.brs" />
<script type="text/brightscript" uri="pkg:/source/api/Items.brs" />
<script type="text/brightscript" uri="pkg:/source/api/baserequest.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />
</component>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="JFContentItem" extends="ContentNode">
<interface>
<field id="favorite" type="boolean" />
<field id="watched" type="boolean" />
<field id="posterUrl" type="string" />
<field id="backdropUrl" type="string" />
<field id="SubTitle" type="string" value="" />
<field id="Type" type="string" value="" />
<field id="json" type="associativearray" onChange="setFields" />
</interface>
</component>

View File

@ -1,11 +1,27 @@
sub setFields()
' print "Setting Fields in MovieData - " m.top.json.name
json = m.top.json
m.top.id = json.id
m.top.title = json.name
m.top.overview = json.overview
m.top.Title = json.name
m.top.Description = json.overview
m.top.favorite = json.UserData.isFavorite
m.top.watched = json.UserData.played
m.top.Type = "Movie"
if json.ProductionYear <> invalid then
m.top.SubTitle = json.ProductionYear
end if
if json.OfficialRating <> invalid and json.OfficialRating <> "" then
m.top.Rating = json.OfficialRating
if m.top.SubTitle <> "" then
m.top.SubTitle = m.top.SubTitle + " - " + m.top.Rating
else
m.top.SubTitle = m.top.Rating
end if
end if
setPoster()
setContainer()
@ -15,9 +31,23 @@ sub setPoster()
if m.top.image <> invalid
m.top.posterURL = m.top.image.url
else
m.top.posterURL = ""
end if
if m.top.json.ImageTags.Primary <> invalid then
imgParams = { "maxHeight": 440, "maxWidth": 295, "Tag" : m.top.json.ImageTags.Primary }
m.top.posterURL = ImageURL(m.top.json.id, "Primary", imgParams)
else if m.top.json.BackdropImageTags <> invalid then
imgParams = { "maxHeight": 440, "Tag" : m.top.json.BackdropImageTags[0] }
m.top.posterURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
end if
' Add Backdrop Image
if m.top.json.BackdropImageTags <> invalid then
imgParams = { "maxHeight": 720, "maxWidth": 1280, "Tag" : m.top.json.BackdropImageTags[0] }
m.top.backdropURL = ImageURL(m.top.json.id, "Backdrop", imgParams)
end if
end if
end sub
sub setContainer()

View File

@ -1,16 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="MovieData" extends="ContentNode">
<component name="MovieData" extends="JFContentItem">
<interface>
<field id="title" type="string" />
<field id="image" type="node" onChange="setPoster" />
<field id="posterUrl" type="string" />
<field id="movieID" type="string" />
<field id="overview" type="string" />
<field id="favorite" type="boolean" />
<field id="watched" type="boolean" />
<field id="seasons" type="associativearray" />
<field id="container" type="string" />
<field id="json" type="associativearray" onChange="setFields" />
</interface>
<script type="text/brightscript" uri="MovieData.brs" />
<script type="text/brightscript" uri="pkg:/source/api/Image.brs" />
<script type="text/brightscript" uri="pkg:/source/api/baserequest.brs" />
<script type="text/brightscript" uri="pkg:/source/utils/config.brs" />
</component>

View File

@ -1,14 +0,0 @@
sub init()
end sub
function onKeyEvent(key as string, press as boolean) as boolean
if not press then return false
if key = "down"
m.top.lastFocus = m.top.focusedChild
m.top.findNode("paginator").setFocus(true)
end if
return false
end function

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<component name="Movies" extends="JFGroup">
<children>
<ItemGrid id="picker" visible="true" itemsPerRow="6" />
<OptionsSlider id="options" />
<Rectangle translation="[0,981]" width="1920" height="100" color="#101010" />
</children>
<interface>
<field id="movieSelected" alias="picker.itemSelected" />
<field id="objects" alias="picker.objects" />
<field id="pageNumber" type="integer" />
</interface>
<script type="text/brightscript" uri="Movies.brs" />
</component>

BIN
images/PosterFailed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
images/PosterLoading.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -235,9 +235,7 @@ sub Main()
else if isNodeEvent(msg, "pageSelected")
group.pageNumber = msg.getRoSGNode().pageSelected
collectionType = group.subType()
if collectionType = "Movies"
MovieLister(group, m.page_size)
else if collectionType = "Collections"
if collectionType = "Collections"
CollectionLister(group, m.page_size)
else if collectionType = "TVShows"
SeriesLister(group, m.page_size)

View File

@ -196,10 +196,10 @@ function CreateHomeGroup()
end function
function CreateMovieListGroup(libraryId)
group = CreateObject("roSGNode", "Movies")
group.id = libraryId
group = CreateObject("roSGNode", "ItemGrid2")
group.itemId = libraryId
group.observeField("movieSelected", m.port)
group.observeField("selectedItem", m.port)
sidepanel = group.findNode("options")
movie_options = [
@ -235,15 +235,7 @@ function CreateMovieListGroup(libraryId)
sidepanel.options = new_options
sidepanel.observeField("closeSidePanel", m.port)
p = CreatePaginator()
group.appendChild(p)
group.pageNumber = 1
p.currentPage = group.pageNumber
MovieLister(group, m.page_size)
return group
return group
end function
function CreateMovieDetailsGroup(movie)
@ -397,22 +389,6 @@ function CreateVideoPlayerGroup(video_id)
return video
end function
function MovieLister(group, page_size)
sort_order = get_user_setting("movie_sort_order", "Ascending")
sort_field = get_user_setting("movie_sort_field", "SortName")
item_list = ItemList(group.id, {"limit": page_size,
"StartIndex": page_size * (group.pageNumber - 1),
"SortBy": sort_field,
"SortOrder": sort_order,
"IncludeItemTypes": "Movie"
})
group.objects = item_list
p = group.findNode("paginator")
p.maxPages = div_ceiling(group.objects.TotalRecordCount, page_size)
end function
function SeriesLister(group, page_size)
sort_order = get_user_setting("series_sort_order", "Ascending")
sort_field = get_user_setting("series_sort_field", "SortName")