From 75cf7e85db2ada3197a59e09902be5c5e57e49db Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Thu, 9 Jul 2026 09:57:12 -0600 Subject: [PATCH 01/18] add MakD media bar style --- components/home/Home.bs | 81 ++++++++++++++----- components/home/MediaBar.bs | 138 ++++++++++++++++++++++++++++++-- components/settings/settings.bs | 6 ++ settings/settings.json | 4 + source/utils/globals.bs | 3 +- 5 files changed, 204 insertions(+), 28 deletions(-) diff --git a/components/home/Home.bs b/components/home/Home.bs index 3cf451ba5..f78c3992e 100644 --- a/components/home/Home.bs +++ b/components/home/Home.bs @@ -124,12 +124,17 @@ sub init() m.detailsSection.visible = false end if if isValid(m.backdropTint) - m.backdropTint.opacity = 0 - end if - if isValid(m.backdrop) - m.backdrop.opacity = 1.0 + mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mediaBarMode = "makd" + m.backdropTint.opacity = 1.0 + m.backdrop.opacity = 0.3 + m.backdropTargetOpacity = 0.3 + else + m.backdropTint.opacity = 0 + m.backdrop.opacity = 1.0 + m.backdropTargetOpacity = 1.0 + end if end if - m.backdropTargetOpacity = 1.0 else ' Make sure media bar is hidden if disabled m.mediaBar.visible = false @@ -138,6 +143,8 @@ sub init() end if end if + m.global.observeFieldScoped("mediaBarModeChanged", "onMediaBarModeChanged") + scene = m.top.getScene() activeNav = getActiveNav(scene) if isValid(activeNav) @@ -190,6 +197,24 @@ sub onLibrarySelected(event as object) handleLibraryNavigation(libraryNode) end sub +' Handle media bar backdrop when style changes +sub onMediaBarModeChanged(_event as object) + if not isValid(m.mediaBar) or not m.mediaBar.visible then return + + if isValid(m.backdropTint) + mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mediaBarMode = "makd" + m.backdropTint.opacity = 1.0 + m.backdrop.opacity = 0.3 + m.backdropTargetOpacity = 0.3 + else + m.backdropTint.opacity = 0 + m.backdrop.opacity = 1.0 + m.backdropTargetOpacity = 1.0 + end if + end if +end sub + ' Handle media bar backdrop changes sub onMediaBarBackdropChanged(event as object) backdropUrl = event.getData() @@ -263,12 +288,17 @@ sub onHomeRowsContentLoaded() m.detailsSection.visible = false end if if isValid(m.backdropTint) - m.backdropTint.opacity = 0 - end if - if isValid(m.backdrop) - m.backdrop.opacity = 1.0 + mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mediaBarMode = "makd" + m.backdropTint.opacity = 1.0 + m.backdrop.opacity = 0.3 + m.backdropTargetOpacity = 0.3 + else + m.backdropTint.opacity = 0 + m.backdrop.opacity = 1.0 + m.backdropTargetOpacity = 1.0 + end if end if - m.backdropTargetOpacity = 1.0 ' Store focus state group = m.global.sceneManager.callFunc("getActiveScene") @@ -655,12 +685,17 @@ sub OnScreenShown(_isReturning = false as boolean) m.detailsSection.visible = false end if if isValid(m.backdropTint) - m.backdropTint.opacity = 0 - end if - if isValid(m.backdrop) - m.backdrop.opacity = 1.0 + mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mediaBarMode = "makd" + m.backdropTint.opacity = 1.0 + m.backdrop.opacity = 0.3 + m.backdropTargetOpacity = 0.3 + else + m.backdropTint.opacity = 0 + m.backdrop.opacity = 1.0 + m.backdropTargetOpacity = 1.0 + end if end if - m.backdropTargetOpacity = 1.0 else ' Default to home rows if isValid(m.homeRows) @@ -860,12 +895,18 @@ function onKeyEvent(key as string, press as boolean) as boolean m.detailsSection.visible = false end if if isValid(m.backdropTint) - m.backdropTint.opacity = 0 - end if - if isValid(m.backdrop) - m.backdrop.opacity = 1.0 + mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mediaBarMode = "makd" + m.backdropTint.opacity = 1.0 + m.backdrop.opacity = 0.3 + m.backdropTargetOpacity = 0.3 + else + m.backdropTint.opacity = 0 + m.backdrop.opacity = 1.0 + m.backdropTargetOpacity = 1.0 + end if end if - m.backdropTargetOpacity = 1.0 + return true end if end if diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index ef7e4bdae..ce8dd6ecf 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -35,6 +35,9 @@ sub init() m.ratingsRow = m.top.findNode("ratingsRow") + m.global.observeFieldScoped("mediaBarModeChanged", "onMediaBarModeChanged") + applyCurrentStyle() + ' Content fade animations m.contentFadeOut = m.top.findNode("contentFadeOut") m.contentFadeIn = m.top.findNode("contentFadeIn") @@ -72,6 +75,113 @@ sub init() loadMediaBarItems() end sub +sub onMediaBarModeChanged(_event as object) + applyCurrentStyle() + applyOverlaySettings() + refreshMediaBarBackdropUrls() + + if m.mediaBarItems.count() > 0 + showMediaBarItem(m.currentIndex) + end if +end sub + +sub applyCurrentStyle() + mode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mode = "off" or mode = "" then return + + if mode = "moonfin" + m.infoBottomAnchor = 720 + + ' Logo Shadow + m.logoShadow.width = "400" + m.logoShadow.height = "120" + m.logoShadow.translation = "[153, -12]" + ' Logo Image + m.logoImage.width = "400" + m.logoImage.height = "120" + m.logoImage.translation = "[150, -15]" + ' Title Text + m.titleText.translation = "[150, -15]" + m.titleText.horizAlign = "left" + ' Info Background + m.infoBackground.visible = true + ' Info Overlay Content + m.infoOverlayContent.translation = "[170, 475]" + m.infoOverlayContent.horizAlignment = "left" + + m.yearLabel.font = "font:SmallestBoldSystemFont" + m.runtimeLabel.font = "font:SmallestBoldSystemFont" + m.starLabel.font = "font:SmallestBoldSystemFont" + m.communityRatingLabel.font = "font:SmallestBoldSystemFont" + m.genresLabel.font = "font:SmallestBoldSystemFont" + m.ratingsRow.labelFont = "font:SmallBoldSystemFont" + m.overviewLabel.visible = true + ' Left Arrow (n/a) + ' Right Arrow (n/a) + ' Dots Background + m.dotsBackground.visible = true + ' Dots (n/a) + end if + + if mode = "makd" + m.infoBottomAnchor = 800 + + ' Logo Shadow + m.logoShadow.width = "500" + m.logoShadow.height = "220" + m.logoShadow.translation = "[710, 200]" + ' Logo Image + m.logoImage.width = "500" + m.logoImage.height = "220" + m.logoImage.translation = "[710, 200]" + ' Title Text + m.titleText.translation = "[560, 285]" + m.titleText.horizAlign = "center" + ' Info Background + m.infoBackground.visible = false + ' Info Overlay Content + m.infoOverlayContent.translation = "[960, 585]" + m.infoOverlayContent.horizAlignment = "center" + + m.yearLabel.font = "font:TinyBoldSystemFont" + m.runtimeLabel.font = "font:TinyBoldSystemFont" + m.starLabel.font = "font:TinyBoldSystemFont" + m.communityRatingLabel.font = "font:TinyBoldSystemFont" + m.genresLabel.font = "font:TinyBoldSystemFont" + m.ratingsRow.labelFont = "font:TinyBoldSystemFont" + m.overviewLabel.visible = false + ' Left Arrow (n/a) + ' Right Arrow (n/a) + ' Dots Background + m.dotsBackground.visible = false + ' Dots (n/a) + end if + + updateInfoBackgroundSize() + updateDotsBackgroundSize() +end sub + +sub refreshMediaBarBackdropUrls() + mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + useBlur = (mediaBarMode = "makd") + + for each item in m.mediaBarItems + if isValid(item.id) and isValidAndNotEmpty(item.backdropTag) + backdropParams = { + maxHeight: 1080, + maxWidth: 1920, + quality: 90, + Tag: item.backdropTag + } + if useBlur + backdropParams.blur = 15 + backdropParams.quality = 60 + end if + item.backdropUrl = ImageURL(item.id, "Backdrop", backdropParams) + end if + end for +end sub + ' Handle focus changes - ensure backdrop is set when component gets focus sub onFocusChanged(event as object) hasFocus = event.getData() @@ -216,6 +326,9 @@ end sub ' Process item data into display format function processItem(item as object) as object + mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + useBlur = (mediaBarMode = "makd") + processed = { id: item.id, title: item.name, @@ -230,17 +343,24 @@ function processItem(item as object) as object genres: [], genresDisplay: "", backdropUrl: "", + backdropTag: "", logoUrl: "", tmdbId: "" } if isChainValid(item, "backdropImageTags") and item.backdropImageTags.count() > 0 - processed.backdropUrl = ImageURL(item.id, "Backdrop", { + backdropParams = { maxHeight: 1080, maxWidth: 1920, - quality: 100, + quality: 90, Tag: item.backdropImageTags[0] - }) + } + if useBlur + backdropParams.blur = 15 + backdropParams.quality = 60 + end if + processed.backdropTag = item.backdropImageTags[0] + processed.backdropUrl = ImageURL(item.id, "Backdrop", backdropParams) end if if isChainValid(item, "imageTags.Logo") @@ -521,7 +641,10 @@ sub showMediaBarItem(index as integer) end if ' Overview (full text, no line limit) - if item.overview <> invalid + mode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mode = "makd" or mode = "off" + m.overviewLabel.text = "" + else if item.overview <> invalid m.overviewLabel.text = item.overview else m.overviewLabel.text = "" @@ -542,12 +665,13 @@ sub updateInfoBackgroundSize() if contentHeight <= 0 then return totalHeight = contentHeight + 30 - newBgY = 720 - totalHeight + anchor = m.infoBottomAnchor ?? 720 + newBgY = anchor - totalHeight m.infoBackground.height = totalHeight - m.infoBackground.translation = [150, newBgY] + m.infoBackground.translation = [m.infoBackground.translation[0], newBgY] - m.infoOverlayContent.translation = [170, newBgY + 15] + m.infoOverlayContent.translation = [m.infoOverlayContent.translation[0], newBgY + 15] end sub sub fetchMediaBarRatings(item as object) diff --git a/components/settings/settings.bs b/components/settings/settings.bs index 4d084e3a5..893346c21 100644 --- a/components/settings/settings.bs +++ b/components/settings/settings.bs @@ -2333,6 +2333,12 @@ sub radioSettingChanged() end if end if + if isStringEqual(selectedSetting.settingName, "ui.home.mediaBarMode") + if isValid(m.global) and m.global.hasField("mediaBarModeChanged") + m.global.mediaBarModeChanged = m.global.mediaBarModeChanged + 1 + end if + end if + if isStringEqual(selectedSetting.settingName, "navbar.position") selectedPosition = m.radioSetting.content.getChild(m.radioSetting.checkedItem).id scene = m.top.getScene() diff --git a/settings/settings.json b/settings/settings.json index 8d7eb0653..c7e01c1ec 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -205,6 +205,10 @@ "title": "Moonfin", "id": "moonfin" }, + { + "title": "MakD", + "id": "makd" + }, { "title": "Off", "id": "off" diff --git a/source/utils/globals.bs b/source/utils/globals.bs index d0548d404..d0a3cf983 100644 --- a/source/utils/globals.bs +++ b/source/utils/globals.bs @@ -24,7 +24,8 @@ sub setConstants() check_white: "pkg:/images/icons/check_white.png" } }, - overlaySettingsChanged: 0 + overlaySettingsChanged: 0, + mediaBarModeChanged: 0 }) end sub From ac8e0d6db3d057a1393bd6033ab7b05ce627f534 Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Thu, 9 Jul 2026 10:21:01 -0600 Subject: [PATCH 02/18] cache mode, use elseif, don't update dots bg when makd --- components/home/MediaBar.bs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index ce8dd6ecf..eb147ec54 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -87,6 +87,7 @@ end sub sub applyCurrentStyle() mode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + m.mediaBarMode = mode if mode = "off" or mode = "" then return if mode = "moonfin" @@ -121,9 +122,7 @@ sub applyCurrentStyle() ' Dots Background m.dotsBackground.visible = true ' Dots (n/a) - end if - - if mode = "makd" + else if mode = "makd" m.infoBottomAnchor = 800 ' Logo Shadow @@ -158,12 +157,13 @@ sub applyCurrentStyle() end if updateInfoBackgroundSize() - updateDotsBackgroundSize() + if m.mediaBarMode <> "makd" + updateDotsBackgroundSize() + end if end sub sub refreshMediaBarBackdropUrls() - mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" - useBlur = (mediaBarMode = "makd") + useBlur = (m.mediaBarMode = "makd") for each item in m.mediaBarItems if isValid(item.id) and isValidAndNotEmpty(item.backdropTag) @@ -326,8 +326,7 @@ end sub ' Process item data into display format function processItem(item as object) as object - mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" - useBlur = (mediaBarMode = "makd") + useBlur = (m.mediaBarMode = "makd") processed = { id: item.id, @@ -423,10 +422,8 @@ sub createIndicatorDots() ' Clear existing dots m.indicatorDots.removeChildrenIndex(m.indicatorDots.getChildCount(), 0) - totalItems = m.mediaBarItems.count() - ' Show one dot for each item - numDots = totalItems + numDots = m.mediaBarItems.count() ' Create dot for each visible indicator for i = 0 to numDots - 1 @@ -641,8 +638,7 @@ sub showMediaBarItem(index as integer) end if ' Overview (full text, no line limit) - mode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" - if mode = "makd" or mode = "off" + if m.mediaBarMode = "makd" or m.mediaBarMode = "off" m.overviewLabel.text = "" else if item.overview <> invalid m.overviewLabel.text = item.overview From 936695aff1fc72c47458b034e49d9e75acf51118 Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Thu, 9 Jul 2026 13:45:47 -0600 Subject: [PATCH 03/18] add banner media bar style --- components/JFOverhang.bs | 5 +- components/home/Home.bs | 262 +++++++++++++++--------------------- components/home/MediaBar.bs | 79 ++++++++--- settings/settings.json | 4 + 4 files changed, 177 insertions(+), 173 deletions(-) diff --git a/components/JFOverhang.bs b/components/JFOverhang.bs index 72bf1417a..1d801553d 100644 --- a/components/JFOverhang.bs +++ b/components/JFOverhang.bs @@ -690,7 +690,10 @@ function handleDownKey() as boolean mediaBar.visible = true mediaBar.hasFocus = true mediaBar.setFocus(true) - homeRows.visible = false + mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mediaBarMode <> "banner" + homeRows.visible = false + end if mediaBar.callFunc("refreshCurrentDisplay") return true end if diff --git a/components/home/Home.bs b/components/home/Home.bs index f78c3992e..8a203d04b 100644 --- a/components/home/Home.bs +++ b/components/home/Home.bs @@ -46,6 +46,7 @@ sub init() m.shuffleTask.observeField("selectedItem", "onShuffleComplete") m.homeRows = m.top.findNode("homeRows") + m.homeRowsDefaultY = 450 ' Initialize media bar enabled state using the shared helper in HomeRows m.isMediaBarEnabled = m.homeRows.callFunc("isFeaturedMediaEnabled") @@ -107,39 +108,19 @@ sub init() ' Setup media bar m.mediaBar = m.top.findNode("mediaBar") + m.mediaBarFocused = false if isValid(m.mediaBar) m.mediaBar.observeField("itemSelected", "onMediaBarItemSelected") m.mediaBar.observeField("currentBackdrop", "onMediaBarBackdropChanged") + m.mediaBar.observeFieldScoped("focusedChild", "onMediaBarFocusedChildChanged") ' Show MediaBar if enabled via toggle if m.isMediaBarEnabled - m.mediaBar.visible = true - m.homeRows.visible = false - ' Set focus to MediaBar - m.mediaBar.hasFocus = true + applyMediaBarLayout(true) m.mediaBar.setFocus(true) m.homeRows.setFocus(false) - ' Hide home rows and details when showing media bar - if isValid(m.detailsSection) - m.detailsSection.visible = false - end if - if isValid(m.backdropTint) - mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" - if mediaBarMode = "makd" - m.backdropTint.opacity = 1.0 - m.backdrop.opacity = 0.3 - m.backdropTargetOpacity = 0.3 - else - m.backdropTint.opacity = 0 - m.backdrop.opacity = 1.0 - m.backdropTargetOpacity = 1.0 - end if - end if else - ' Make sure media bar is hidden if disabled - m.mediaBar.visible = false - ' Make sure home rows are visible if media bar is not shown - m.homeRows.visible = true + applyMediaBarLayout(false) end if end if @@ -198,21 +179,15 @@ sub onLibrarySelected(event as object) end sub ' Handle media bar backdrop when style changes -sub onMediaBarModeChanged(_event as object) +sub onMediaBarModeChanged() if not isValid(m.mediaBar) or not m.mediaBar.visible then return - if isValid(m.backdropTint) - mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" - if mediaBarMode = "makd" - m.backdropTint.opacity = 1.0 - m.backdrop.opacity = 0.3 - m.backdropTargetOpacity = 0.3 - else - m.backdropTint.opacity = 0 - m.backdrop.opacity = 1.0 - m.backdropTargetOpacity = 1.0 - end if - end if + applyMediaBarLayout(true) +end sub + +' Track MediaBar focus state +sub onMediaBarFocusedChildChanged() + m.mediaBarFocused = isValid(m.mediaBar.focusedChild) end sub ' Handle media bar backdrop changes @@ -235,7 +210,7 @@ sub onMediaBarBackdropChanged(event as object) end if ' Only update if MediaBar has focus - if isValid(m.mediaBar) and m.mediaBar.hasFocus and m.mediaBar.visible + if isValid(m.mediaBar) and m.mediaBarFocused and m.mediaBar.visible if isValid(m.backdrop) ' Stop any running fade animations if isValid(m.backdropFadeIn) then m.backdropFadeIn.control = "stop" @@ -277,29 +252,9 @@ sub onHomeRowsContentLoaded() m.currentFocusedItemId = "" m.currentFocusedItemServerUrl = invalid - ' Show MediaBar and hide HomeRows only when setting initial focus - m.mediaBar.visible = true - m.homeRows.visible = false - m.mediaBar.hasFocus = true + applyMediaBarLayout(true) m.mediaBar.setFocus(true) - ' Update backdrop for media bar - if isValid(m.detailsSection) - m.detailsSection.visible = false - end if - if isValid(m.backdropTint) - mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" - if mediaBarMode = "makd" - m.backdropTint.opacity = 1.0 - m.backdrop.opacity = 0.3 - m.backdropTargetOpacity = 0.3 - else - m.backdropTint.opacity = 0 - m.backdrop.opacity = 1.0 - m.backdropTargetOpacity = 1.0 - end if - end if - ' Store focus state group = m.global.sceneManager.callFunc("getActiveScene") if isValid(group) then group.lastFocus = m.mediaBar @@ -308,9 +263,7 @@ sub onHomeRowsContentLoaded() m.shouldFocusMediaBarOnLoad = false end if else - ' Make sure media bar is hidden if disabled - m.mediaBar.visible = false - m.homeRows.visible = true + applyMediaBarLayout(false) end if end sub @@ -551,6 +504,86 @@ sub loadLibraries() m.homeRows.callFunc("loadLibraries") end sub +' Check if the current media bar mode is banner +function isBannerMode() as boolean + mode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + return (mode = "banner") +end function + +' Apply layout for media bar visibility (handles banner vs fullscreen vs hidden) +sub applyMediaBarLayout(mediaBarVisible as boolean) + if not isValid(m.mediaBar) or not isValid(m.homeRows) then return + + if mediaBarVisible + m.mediaBar.visible = true + if isBannerMode() + ' Banner media bar style + m.homeRows.visible = true + m.homeRows.translation = [111, 450] + if isValid(m.detailsSection) then m.detailsSection.visible = false + + m.backdrop.width = "1710" + m.backdrop.height = "300" + m.backdrop.translation = "[105, 135]" + m.backdrop.loadDisplayMode = "scaleToZoom" + + m.backdropPrev.width = "1710" + m.backdropPrev.height = "300" + m.backdropPrev.translation = "[105, 135]" + m.backdropPrev.loadDisplayMode = "scaleToZoom" + + if isValid(m.backdropTint) then m.backdropTint.opacity = 0 + if isValid(m.backdrop) then m.backdrop.opacity = 0.6 + m.backdropTargetOpacity = 0.6 + else + ' Fullscreen media bar style + m.homeRows.visible = false + + m.backdrop.width = "1920" + m.backdrop.height = "1080" + m.backdrop.translation = "[0, 0]" + m.backdrop.loadDisplayMode = "scaleToFill" + + m.backdropPrev.width = "1920" + m.backdropPrev.height = "1080" + m.backdropPrev.translation = "[0, 0]" + m.backdropPrev.loadDisplayMode = "scaleToFill" + + if isValid(m.detailsSection) then m.detailsSection.visible = false + mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mediaBarMode = "makd" + if isValid(m.backdropTint) then m.backdropTint.opacity = 1.0 + if isValid(m.backdrop) then m.backdrop.opacity = 0.3 + m.backdropTargetOpacity = 0.3 + else + if isValid(m.backdropTint) then m.backdropTint.opacity = 0 + if isValid(m.backdrop) then m.backdrop.opacity = 1.0 + m.backdropTargetOpacity = 1.0 + end if + end if + else + ' Media bar off, or focus on homeRows + m.mediaBar.visible = false + m.homeRows.visible = true + m.homeRows.translation = [111, m.homeRowsDefaultY] + + m.backdrop.width = "1920" + m.backdrop.height = "1080" + m.backdrop.translation = "[0, 0]" + m.backdrop.loadDisplayMode = "scaleToFill" + + m.backdropPrev.width = "1920" + m.backdropPrev.height = "1080" + m.backdropPrev.translation = "[0, 0]" + m.backdropPrev.loadDisplayMode = "scaleToFill" + + if isValid(m.detailsSection) then m.detailsSection.visible = true + if isValid(m.backdropTint) then m.backdropTint.opacity = 1.0 + if isValid(m.backdrop) then m.backdrop.opacity = 0.3 + m.backdropTargetOpacity = 0.3 + end if +end sub + ' JFScreen hook called when the screen is displayed by the screen manager sub OnScreenShown(_isReturning = false as boolean) m.homeRows.rowLabelColor = chainLookupReturn(m.global.session, "user.settings.colorHomeRowHeaders", ColorPalette.WHITE) @@ -587,16 +620,7 @@ sub OnScreenShown(_isReturning = false as boolean) m.homeRows.setFocus(true) focusRestored = true - ' Hide MediaBar and show HomeRows content when restoring to HomeRows - if isValid(m.mediaBar) - m.mediaBar.visible = false - end if - if isValid(m.homeRows) - m.homeRows.visible = true - end if - if isValid(m.detailsSection) - m.detailsSection.visible = true - end if + applyMediaBarLayout(false) ' Trigger backdrop update for current focused item if _isReturning @@ -607,20 +631,10 @@ sub OnScreenShown(_isReturning = false as boolean) m.currentFocusedItemId = "" m.currentFocusedItemServerUrl = invalid - m.mediaBar.hasFocus = true m.mediaBar.setFocus(true) focusRestored = true - ' Show MediaBar and hide HomeRows when restoring to MediaBar - if isValid(m.mediaBar) - m.mediaBar.visible = true - end if - if isValid(m.homeRows) - m.homeRows.visible = false - end if - if isValid(m.detailsSection) - m.detailsSection.visible = false - end if + applyMediaBarLayout(true) else ' lastFocus exists but isn't in homeRows or mediaBar (e.g., from settings/avatar) ' Default to homeRows since we're coming back to Home screen @@ -629,14 +643,7 @@ sub OnScreenShown(_isReturning = false as boolean) m.top.lastFocus = m.homeRows focusRestored = true - ' Show homeRows, hide mediaBar - if isValid(m.mediaBar) - m.mediaBar.visible = false - end if - m.homeRows.visible = true - if isValid(m.detailsSection) - m.detailsSection.visible = true - end if + applyMediaBarLayout(false) ' Trigger backdrop update for current focused item if _isReturning @@ -652,9 +659,8 @@ sub OnScreenShown(_isReturning = false as boolean) ' Home rows already have focus, keep it there m.homeRows.setFocus(true) focusRestored = true - else if isValid(m.mediaBar) and m.mediaBar.visible and m.mediaBar.hasFocus() + else if isValid(m.mediaBar) and m.mediaBar.visible and m.mediaBarFocused ' Media bar already has focus, keep it there - m.mediaBar.hasFocus = true m.mediaBar.setFocus(true) focusRestored = true end if @@ -676,40 +682,16 @@ sub OnScreenShown(_isReturning = false as boolean) m.currentFocusedItemId = "" m.currentFocusedItemServerUrl = invalid - m.mediaBar.visible = true - m.mediaBar.hasFocus = true + applyMediaBarLayout(true) m.mediaBar.setFocus(true) m.top.lastFocus = m.mediaBar - ' Hide details section and show full brightness backdrop for media bar - if isValid(m.detailsSection) - m.detailsSection.visible = false - end if - if isValid(m.backdropTint) - mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" - if mediaBarMode = "makd" - m.backdropTint.opacity = 1.0 - m.backdrop.opacity = 0.3 - m.backdropTargetOpacity = 0.3 - else - m.backdropTint.opacity = 0 - m.backdrop.opacity = 1.0 - m.backdropTargetOpacity = 1.0 - end if - end if else ' Default to home rows if isValid(m.homeRows) m.homeRows.setFocus(true) m.top.lastFocus = m.homeRows - ' Ensure homeRows is visible and mediaBar is hidden - m.homeRows.visible = true - if isValid(m.mediaBar) - m.mediaBar.visible = false - end if - if isValid(m.detailsSection) - m.detailsSection.visible = true - end if + applyMediaBarLayout(false) ' Trigger backdrop update if _isReturning @@ -883,30 +865,12 @@ function onKeyEvent(key as string, press as boolean) as boolean m.currentFocusedItemId = "" m.currentFocusedItemServerUrl = invalid - m.homeRows.visible = false m.homeRows.setFocus(false) - m.mediaBar.visible = true - m.mediaBar.hasFocus = true + applyMediaBarLayout(true) m.mediaBar.setFocus(true) m.shouldFocusMediaBarOnLoad = false m.mediaBar.callFunc("refreshCurrentDisplay") - if isValid(m.detailsSection) - m.detailsSection.visible = false - end if - if isValid(m.backdropTint) - mediaBarMode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" - if mediaBarMode = "makd" - m.backdropTint.opacity = 1.0 - m.backdrop.opacity = 0.3 - m.backdropTargetOpacity = 0.3 - else - m.backdropTint.opacity = 0 - m.backdrop.opacity = 1.0 - m.backdropTargetOpacity = 1.0 - end if - end if - return true end if end if @@ -920,11 +884,8 @@ function onKeyEvent(key as string, press as boolean) as boolean ' Handle navigation between MediaBar and HomeRows if isStringEqual(key, KeyCode.DOWN) - if isValid(m.mediaBar) and m.mediaBar.hasFocus and m.mediaBar.visible - ' Move focus from MediaBar to first row in HomeRows - m.mediaBar.visible = false - m.mediaBar.hasFocus = false - m.homeRows.visible = true + if isValid(m.mediaBar) and m.mediaBar.visible and m.mediaBarFocused + applyMediaBarLayout(false) ' Jump to first row (no placeholder to skip) if m.homeRows.content.getChildCount() > 0 @@ -932,28 +893,15 @@ function onKeyEvent(key as string, press as boolean) as boolean end if m.homeRows.setFocus(true) - - ' Restore details section and normal backdrop - if isValid(m.detailsSection) - m.detailsSection.visible = true - end if - if isValid(m.backdropTint) - m.backdropTint.opacity = 1.0 - end if - if isValid(m.backdrop) - m.backdrop.opacity = 0.3 - end if - m.backdropTargetOpacity = 0.3 return true end if end if if isStringEqual(key, KeyCode.UP) - if isValid(m.mediaBar) and m.mediaBar.hasFocus and m.mediaBar.visible + if isValid(m.mediaBar) and m.mediaBar.visible and m.mediaBarFocused scene = m.top.getScene() activeNav = getActiveNav(scene) if isValid(activeNav) - m.mediaBar.hasFocus = false activeNav.setFocus(true) return true end if diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index eb147ec54..0deea5278 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -117,11 +117,17 @@ sub applyCurrentStyle() m.genresLabel.font = "font:SmallestBoldSystemFont" m.ratingsRow.labelFont = "font:SmallBoldSystemFont" m.overviewLabel.visible = true - ' Left Arrow (n/a) - ' Right Arrow (n/a) + ' Left Arrow + m.leftArrow.visible = true + m.leftArrowBg.visible = true + ' Right Arrow + m.rightArrow.visible = true + m.rightArrowBg.visible = true ' Dots Background m.dotsBackground.visible = true - ' Dots (n/a) + ' Dots + m.dotsY = 810 + m.dotsBgY = 793 else if mode = "makd" m.infoBottomAnchor = 800 @@ -149,17 +155,59 @@ sub applyCurrentStyle() m.genresLabel.font = "font:TinyBoldSystemFont" m.ratingsRow.labelFont = "font:TinyBoldSystemFont" m.overviewLabel.visible = false - ' Left Arrow (n/a) - ' Right Arrow (n/a) + ' Left Arrow + m.leftArrow.visible = true + m.leftArrowBg.visible = true + ' Right Arrow + m.rightArrow.visible = true + m.rightArrowBg.visible = true ' Dots Background m.dotsBackground.visible = false - ' Dots (n/a) + ' Dots + m.dotsY = 810 + m.dotsBgY = 793 + else if mode = "banner" + m.infoBottomAnchor = 240 + + ' Logo Shadow + m.logoShadow.width = "400" + m.logoShadow.height = "120" + m.logoShadow.translation = "[153, -12]" + ' Logo Image + m.logoImage.width = "400" + m.logoImage.height = "120" + m.logoImage.translation = "[150, -15]" + ' Title Text + m.titleText.translation = "[150, -15]" + m.titleText.horizAlign = "left" + ' Info Background + m.infoBackground.visible = false + ' Info Overlay Content + m.infoOverlayContent.translation = "[153, 140]" + m.infoOverlayContent.horizAlignment = "left" + + m.yearLabel.font = "font:TinyBoldSystemFont" + m.runtimeLabel.font = "font:TinyBoldSystemFont" + m.starLabel.font = "font:TinyBoldSystemFont" + m.communityRatingLabel.font = "font:TinyBoldSystemFont" + m.genresLabel.font = "font:TinyBoldSystemFont" + m.ratingsRow.labelFont = "font:TinyBoldSystemFont" + m.overviewLabel.visible = false + ' Left Arrow + m.leftArrow.visible = false + m.leftArrowBg.visible = false + ' Right Arrow + m.rightArrow.visible = false + m.rightArrowBg.visible = false + ' Dots Background + m.dotsBackground.visible = false + ' Dots + m.dotsY = 190 + m.dotsBgY = 173 end if updateInfoBackgroundSize() - if m.mediaBarMode <> "makd" - updateDotsBackgroundSize() - end if + updateDotsBackgroundSize() end sub sub refreshMediaBarBackdropUrls() @@ -314,9 +362,10 @@ sub onMediaBarDataLoaded(event as object) sidebar = scene.findNode("sidebar") sidebarActive = isValid(sidebar) and sidebar.visible end if - m.leftArrow.visible = not sidebarActive - if isValid(m.leftArrowBg) then m.leftArrowBg.visible = not sidebarActive - m.rightArrow.visible = true + showArrows = (m.mediaBarMode <> "banner") and not sidebarActive + m.leftArrow.visible = showArrows + if isValid(m.leftArrowBg) then m.leftArrowBg.visible = showArrows + m.rightArrow.visible = showArrows startAutoAdvanceTimer() end if ' Display first item (single backdrop update) @@ -471,8 +520,8 @@ sub updateDotsBackgroundSize() ' Center the background and dots on screen ' Screen width is 1920, so center point is 960 screenCenter = 960 - m.indicatorDots.translation = [screenCenter - (totalDotsWidth + totalSpacingWidth) / 2, 809] - m.dotsBackground.translation = [screenCenter - calculatedWidth / 2, 792] + m.indicatorDots.translation = [screenCenter - (totalDotsWidth + totalSpacingWidth) / 2, m.dotsY] + m.dotsBackground.translation = [screenCenter - calculatedWidth / 2, m.dotsBgY] end sub ' Update indicator dots to show current slide @@ -638,7 +687,7 @@ sub showMediaBarItem(index as integer) end if ' Overview (full text, no line limit) - if m.mediaBarMode = "makd" or m.mediaBarMode = "off" + if m.mediaBarMode = "makd" or m.mediaBarMode = "banner" or m.mediaBarMode = "off" m.overviewLabel.text = "" else if item.overview <> invalid m.overviewLabel.text = item.overview diff --git a/settings/settings.json b/settings/settings.json index c7e01c1ec..a83966df4 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -209,6 +209,10 @@ "title": "MakD", "id": "makd" }, + { + "title": "Banner", + "id": "banner" + }, { "title": "Off", "id": "off" From cd7869b0066f92945f862fcf28838a49a5622092 Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Thu, 9 Jul 2026 14:30:02 -0600 Subject: [PATCH 04/18] fix backdrop, clean up some things in the code --- components/home/Home.bs | 5 ++--- components/home/MediaBar.bs | 7 +++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/components/home/Home.bs b/components/home/Home.bs index 8a203d04b..50d5c35dc 100644 --- a/components/home/Home.bs +++ b/components/home/Home.bs @@ -46,7 +46,6 @@ sub init() m.shuffleTask.observeField("selectedItem", "onShuffleComplete") m.homeRows = m.top.findNode("homeRows") - m.homeRowsDefaultY = 450 ' Initialize media bar enabled state using the shared helper in HomeRows m.isMediaBarEnabled = m.homeRows.callFunc("isFeaturedMediaEnabled") @@ -210,7 +209,7 @@ sub onMediaBarBackdropChanged(event as object) end if ' Only update if MediaBar has focus - if isValid(m.mediaBar) and m.mediaBarFocused and m.mediaBar.visible + if isValid(m.mediaBar) and m.mediaBar.visible if isValid(m.backdrop) ' Stop any running fade animations if isValid(m.backdropFadeIn) then m.backdropFadeIn.control = "stop" @@ -565,7 +564,7 @@ sub applyMediaBarLayout(mediaBarVisible as boolean) ' Media bar off, or focus on homeRows m.mediaBar.visible = false m.homeRows.visible = true - m.homeRows.translation = [111, m.homeRowsDefaultY] + m.homeRows.translation = [111, 450] m.backdrop.width = "1920" m.backdrop.height = "1080" diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index 0deea5278..dae856a0a 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -82,6 +82,7 @@ sub onMediaBarModeChanged(_event as object) if m.mediaBarItems.count() > 0 showMediaBarItem(m.currentIndex) + m.top.currentBackdrop = m.mediaBarItems[m.currentIndex].backdropUrl end if end sub @@ -90,6 +91,11 @@ sub applyCurrentStyle() m.mediaBarMode = mode if mode = "off" or mode = "" then return + if mode <> "moonfin" and mode <> "makd" and mode <> "banner" ' Only accept available styles + mode = "moonfin" + m.mediaBarMode = mode + end if + if mode = "moonfin" m.infoBottomAnchor = 720 @@ -896,6 +902,7 @@ end function ' Public function to refresh the current display from parent sub refreshCurrentDisplay() if m.mediaBarItems.count() > 0 + m.top.currentBackdrop = "" displayMediaBarItem(m.currentIndex) end if end sub From 2266ee27913694cff58e99f2655d16d10b210d35 Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Thu, 9 Jul 2026 19:01:12 -0600 Subject: [PATCH 05/18] add ((unfinished)) banner style, more tweaking needed --- components/home/GalleryItem.bs | 133 ++++++++++++++++ components/home/GalleryItem.xml | 100 ++++++++++++ components/home/Home.bs | 8 + components/home/MediaBar.bs | 261 ++++++++++++++++++++++++++++---- components/home/MediaBar.xml | 14 ++ settings/settings.json | 4 + 6 files changed, 494 insertions(+), 26 deletions(-) create mode 100644 components/home/GalleryItem.bs create mode 100644 components/home/GalleryItem.xml diff --git a/components/home/GalleryItem.bs b/components/home/GalleryItem.bs new file mode 100644 index 000000000..de0c9efcd --- /dev/null +++ b/components/home/GalleryItem.bs @@ -0,0 +1,133 @@ +import "pkg:/source/utils/misc.bs" + +sub init() + m.narrowContent = m.top.findNode("narrowContent") + m.wideContent = m.top.findNode("wideContent") + m.narrowTitle = m.top.findNode("narrowTitle") + m.narrowPoster = m.top.findNode("narrowPoster") + m.itemBackdrop = m.top.findNode("itemBackdrop") + m.itemScrim = m.top.findNode("itemScrim") + m.itemTitle = m.top.findNode("itemTitle") + m.gYear = m.top.findNode("gYear") + m.gSep1 = m.top.findNode("gSep1") + m.gRating = m.top.findNode("gRating") + m.gRatingBg = m.top.findNode("gRatingBg") + m.gRatingGroup = m.top.findNode("gRatingGroup") + m.gSep2 = m.top.findNode("gSep2") + m.gRuntime = m.top.findNode("gRuntime") + m.gSep3 = m.top.findNode("gSep3") + m.gStar = m.top.findNode("gStar") + m.gCommunityRating = m.top.findNode("gCommunityRating") + m.gSep4 = m.top.findNode("gSep4") + m.gGenres = m.top.findNode("gGenres") + m.gOverview = m.top.findNode("gOverview") + m.galleryMetaRow = m.top.findNode("galleryMetaRow") +end sub + +sub contentChanged() + data = m.top.itemContent + if not isValid(data) then return + + title = data.title + if title = invalid then title = "" + + ' Narrow content + m.narrowTitle.text = title + + posterUrl = data.backdropUrl + if posterUrl <> "" and posterUrl <> invalid + m.narrowPoster.uri = posterUrl + m.narrowPoster.visible = true + else + m.narrowPoster.uri = "" + m.narrowPoster.visible = false + end if + + ' Wide content + backdropUrl = data.backdropUrl + if backdropUrl <> "" and backdropUrl <> invalid + m.itemBackdrop.uri = backdropUrl + end if + + m.itemTitle.text = title + + ' Metadata + hasYear = false + hasRating = false + hasRuntime = false + hasStarRating = false + + year = data.year + if year <> invalid and year > 0 + m.gYear.text = year.toStr() + hasYear = true + else + m.gYear.text = "" + end if + + rating = data.rating + if rating <> invalid and rating <> "" + m.gRating.text = rating + hasRating = true + labelRect = m.gRating.boundingRect() + if isValid(labelRect) + paddingX = 16 + paddingY = 4 + m.gRatingBg.width = labelRect.width + paddingX + m.gRatingBg.height = labelRect.height + paddingY + m.gRatingBg.translation = [labelRect.x - paddingX / 2, labelRect.y - paddingY / 2 - 2] + m.gRatingBg.visible = true + end if + else + m.gRating.text = "" + m.gRatingBg.visible = false + end if + + runtimeDisplay = data.runtimeDisplay + itemType = data.itemType + if itemType = "Movie" and runtimeDisplay <> "" and runtimeDisplay <> invalid + m.gRuntime.text = runtimeDisplay + hasRuntime = true + else + m.gRuntime.text = "" + end if + + communityRatingDisplay = data.communityRatingDisplay + if communityRatingDisplay <> "" and communityRatingDisplay <> invalid + m.gStar.text = "★ " + m.gStar.visible = true + m.gCommunityRating.text = communityRatingDisplay + hasStarRating = true + else + m.gStar.text = "" + m.gStar.visible = false + m.gCommunityRating.text = "" + end if + + m.gSep1.text = (hasYear and hasRating) ? " • " : "" + m.gSep1.visible = (hasYear and hasRating) + m.gSep2.text = (hasRating) ? " • " : "" + m.gSep2.visible = hasRating + + m.gSep3.text = (hasRuntime and hasStarRating) ? " • " : "" + m.gSep3.visible = (hasRuntime and hasStarRating) + + genresDisplay = data.genresDisplay + hasGenres = (genresDisplay <> "" and genresDisplay <> invalid) + m.gGenres.text = genresDisplay + hasPreviousMeta = (hasYear or hasRating or hasRuntime or hasStarRating) + m.gSep4.text = (hasPreviousMeta and hasGenres) ? " • " : "" + m.gSep4.visible = (hasPreviousMeta and hasGenres) + + overview = data.overview + if overview <> invalid and overview <> "" + m.gOverview.text = overview + else + m.gOverview.text = "" + end if + + isActive = data.isActive + if isActive = invalid then isActive = false + m.wideContent.visible = isActive + m.narrowContent.visible = not isActive +end sub diff --git a/components/home/GalleryItem.xml b/components/home/GalleryItem.xml new file mode 100644 index 000000000..3ee2c513f --- /dev/null +++ b/components/home/GalleryItem.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/home/Home.bs b/components/home/Home.bs index 50d5c35dc..4a9eef0ff 100644 --- a/components/home/Home.bs +++ b/components/home/Home.bs @@ -196,6 +196,14 @@ sub onMediaBarBackdropChanged(event as object) return end if + mode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mode = "gallery" + if isValid(m.backdrop) then m.backdrop.uri = "" + if isValid(m.backdropPrev) then m.backdropPrev.uri = "" + m.backdropCrossfading = false + return + end if + ' Handle clearing the backdrop when MediaBar loses focus if backdropUrl = "" if isValid(m.backdrop) diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index dae856a0a..4c4b99f36 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -35,6 +35,9 @@ sub init() m.ratingsRow = m.top.findNode("ratingsRow") + m.galleryGroup = m.top.findNode("galleryGroup") + m.galleryGrid = m.top.findNode("galleryGrid") + m.global.observeFieldScoped("mediaBarModeChanged", "onMediaBarModeChanged") applyCurrentStyle() @@ -54,6 +57,12 @@ sub init() m.isPaused = false m.clearBackdrop = false + ' Gallery mode state + m.galleryPages = [] + m.galleryCurrentPage = 0 + m.galleryActiveIndex = 0 + m.galleryTotalPages = 0 + ' Make component focusable m.top.focusable = true @@ -81,8 +90,14 @@ sub onMediaBarModeChanged(_event as object) refreshMediaBarBackdropUrls() if m.mediaBarItems.count() > 0 - showMediaBarItem(m.currentIndex) - m.top.currentBackdrop = m.mediaBarItems[m.currentIndex].backdropUrl + if m.mediaBarMode = "gallery" + m.galleryCurrentPage = 0 + showGalleryPage(0) + m.top.currentBackdrop = "" + else + showMediaBarItem(m.currentIndex) + m.top.currentBackdrop = m.mediaBarItems[m.currentIndex].backdropUrl + end if end if end sub @@ -91,7 +106,7 @@ sub applyCurrentStyle() m.mediaBarMode = mode if mode = "off" or mode = "" then return - if mode <> "moonfin" and mode <> "makd" and mode <> "banner" ' Only accept available styles + if mode <> "moonfin" and mode <> "makd" and mode <> "gallery" and mode <> "banner" ' Only accept available styles mode = "moonfin" m.mediaBarMode = mode end if @@ -170,6 +185,25 @@ sub applyCurrentStyle() ' Dots Background m.dotsBackground.visible = false ' Dots + m.dotsY = 810 + m.dotsBgY = 793 + else if mode = "gallery" + m.infoBottomAnchor = 720 + + m.logoShadow.visible = false + m.logoImage.visible = false + m.titleText.visible = false + m.infoBackground.visible = false + m.infoOverlayContent.visible = false + m.leftArrow.visible = false + m.leftArrowBg.visible = false + m.rightArrow.visible = false + m.rightArrowBg.visible = false + m.dotsBackground.visible = false + m.overviewLabel.visible = false + + m.galleryGroup.visible = true + m.dotsY = 810 m.dotsBgY = 793 else if mode = "banner" @@ -242,7 +276,11 @@ sub onFocusChanged(event as object) if hasFocus and m.mediaBarItems.count() > 0 ' Immediately refresh display when focus is gained - displayMediaBarItem(m.currentIndex) + if m.mediaBarMode = "gallery" + showGalleryPage(m.galleryCurrentPage) + else + displayMediaBarItem(m.currentIndex) + end if ' Resume auto-advance timer if isValid(m.autoAdvanceTimer) m.autoAdvanceTimer.control = "start" @@ -265,8 +303,12 @@ sub onVisibilityChanged(event as object) isVisible = event.getData() if isVisible and m.mediaBarItems.count() > 0 - ' Refresh the current item display when component becomes visible - displayMediaBarItem(m.currentIndex) + if m.mediaBarMode = "gallery" + showGalleryPage(m.galleryCurrentPage) + else + ' Refresh the current item display when component becomes visible + displayMediaBarItem(m.currentIndex) + end if end if end sub @@ -360,22 +402,27 @@ sub onMediaBarDataLoaded(event as object) ' Initialize display if we have items if m.mediaBarItems.count() > 0 - createIndicatorDots() - if m.mediaBarItems.count() > 1 - scene = m.top.getScene() - sidebarActive = false - if isValid(scene) - sidebar = scene.findNode("sidebar") - sidebarActive = isValid(sidebar) and sidebar.visible + if m.mediaBarMode = "gallery" + setupGalleryPages() + showGalleryPage(0) + else + createIndicatorDots() + if m.mediaBarItems.count() > 1 + scene = m.top.getScene() + sidebarActive = false + if isValid(scene) + sidebar = scene.findNode("sidebar") + sidebarActive = isValid(sidebar) and sidebar.visible + end if + showArrows = (m.mediaBarMode <> "banner") and not sidebarActive + m.leftArrow.visible = showArrows + if isValid(m.leftArrowBg) then m.leftArrowBg.visible = showArrows + m.rightArrow.visible = showArrows + startAutoAdvanceTimer() end if - showArrows = (m.mediaBarMode <> "banner") and not sidebarActive - m.leftArrow.visible = showArrows - if isValid(m.leftArrowBg) then m.leftArrowBg.visible = showArrows - m.rightArrow.visible = showArrows - startAutoAdvanceTimer() + ' Display first item (single backdrop update) + displayMediaBarItem(0) end if - ' Display first item (single backdrop update) - displayMediaBarItem(0) end if end sub @@ -472,6 +519,138 @@ function processItem(item as object) as object return processed end function +' Split items into pages of 5 for gallery display +sub setupGalleryPages() + pageSize = 5 + m.galleryPages = [] + allItems = m.mediaBarItems + + pageIndex = 0 + while pageIndex * pageSize < allItems.count() + start = pageIndex * pageSize + finish = start + pageSize - 1 + if finish >= allItems.count() then finish = allItems.count() - 1 + page = [] + for i = start to finish + page.push(allItems[i]) + end for + m.galleryPages.push(page) + pageIndex++ + end while + + m.galleryTotalPages = m.galleryPages.count() + m.galleryCurrentPage = 0 + m.galleryActiveIndex = 0 +end sub + +' Show a specific gallery page +sub showGalleryPage(pageIndex as integer) + if pageIndex < 0 or pageIndex >= m.galleryTotalPages then return + + page = m.galleryPages[pageIndex] + + content = createObject("roSGNode", "ContentNode") + for i = 0 to page.count() - 1 + item = page[i] + child = createObject("roSGNode", "ContentNode") + child.addFields({ + title: item.title, + backdropUrl: item.backdropUrl, + logoUrl: item.logoUrl, + posterUrl: item.posterUrl, + overview: item.overview, + year: item.year, + rating: item.rating, + runtimeDisplay: item.runtimeDisplay, + communityRatingDisplay: item.communityRatingDisplay, + genresDisplay: item.genresDisplay, + itemType: item.itemType, + isActive: (i = m.galleryActiveIndex) + }) + content.appendChild(child) + end for + + m.galleryGrid.content = content + setGalleryColumnWidths(m.galleryActiveIndex) +end sub + +' Set column widths based on active index +sub setGalleryColumnWidths(activeIndex as integer) + widths = [110, 110, 110, 110, 110] + widths[activeIndex] = 1400 + m.galleryGrid.columnWidths = widths +end sub + +' Navigate to next item in gallery mode +sub galleryNextSlide() + if m.galleryTotalPages = 0 then return + + itemsOnPage = m.galleryPages[m.galleryCurrentPage].count() + if m.galleryActiveIndex < itemsOnPage - 1 + m.galleryActiveIndex++ + showGalleryPage(m.galleryCurrentPage) + + activeItem = m.galleryPages[m.galleryCurrentPage][m.galleryActiveIndex] + if isValid(activeItem) and activeItem.backdropUrl <> "" + m.top.currentBackdrop = activeItem.backdropUrl + end if + else + ' Advance to next page + if m.galleryCurrentPage < m.galleryTotalPages - 1 + m.galleryCurrentPage++ + m.galleryActiveIndex = 0 + else + ' Wrap to first page + m.galleryCurrentPage = 0 + m.galleryActiveIndex = 0 + end if + showGalleryPage(m.galleryCurrentPage) + activeItem = m.galleryPages[m.galleryCurrentPage][m.galleryActiveIndex] + if isValid(activeItem) and activeItem.backdropUrl <> "" + m.top.currentBackdrop = activeItem.backdropUrl + end if + end if +end sub + +' Navigate to previous item in gallery mode +sub galleryPreviousSlide() + if m.galleryTotalPages = 0 then return + + if m.galleryActiveIndex > 0 + ' Move within current page + m.galleryActiveIndex-- + showGalleryPage(m.galleryCurrentPage) + activeItem = m.galleryPages[m.galleryCurrentPage][m.galleryActiveIndex] + if isValid(activeItem) and activeItem.backdropUrl <> "" + m.top.currentBackdrop = activeItem.backdropUrl + end if + else + ' Go to previous page + if m.galleryCurrentPage > 0 + m.galleryCurrentPage-- + else + m.galleryCurrentPage = m.galleryTotalPages - 1 + end if + ' Set active index to last item on that page + m.galleryActiveIndex = m.galleryPages[m.galleryCurrentPage].count() - 1 + showGalleryPage(m.galleryCurrentPage) + activeItem = m.galleryPages[m.galleryCurrentPage][m.galleryActiveIndex] + if isValid(activeItem) and activeItem.backdropUrl <> "" + m.top.currentBackdrop = activeItem.backdropUrl + end if + end if +end sub + +' Get the currently active item in gallery mode +function getGalleryActiveItem() as object + if m.galleryTotalPages = 0 then return invalid + page = m.galleryPages[m.galleryCurrentPage] + if m.galleryActiveIndex < page.count() + return page[m.galleryActiveIndex] + end if + return invalid +end function + ' Create indicator dots for slides sub createIndicatorDots() ' Clear existing dots @@ -803,7 +982,11 @@ end function ' Auto-advance handler sub onAutoAdvance() if not m.isPaused and m.mediaBarItems.count() > 1 - nextSlide() + if m.mediaBarMode = "gallery" + galleryNextSlide() + else + nextSlide() + end if end if end sub @@ -852,7 +1035,11 @@ function onKeyEvent(key as string, press as boolean) as boolean return true end if end if - previousSlide() + if m.mediaBarMode = "gallery" + galleryPreviousSlide() + else + previousSlide() + end if ' Reset auto-advance timer if isValid(m.autoAdvanceTimer) m.autoAdvanceTimer.control = "stop" @@ -860,7 +1047,11 @@ function onKeyEvent(key as string, press as boolean) as boolean end if return true else if key = KeyCode.RIGHT - nextSlide() + if m.mediaBarMode = "gallery" + galleryNextSlide() + else + nextSlide() + end if ' Reset auto-advance timer if isValid(m.autoAdvanceTimer) m.autoAdvanceTimer.control = "stop" @@ -869,7 +1060,16 @@ function onKeyEvent(key as string, press as boolean) as boolean return true else if key = KeyCode.OK ' Navigate to item details - if m.mediaBarItems.count() > 0 and m.currentIndex >= 0 and m.currentIndex < m.mediaBarItems.count() + if m.mediaBarMode = "gallery" + activeItem = getGalleryActiveItem() + if isValid(activeItem) and isValid(activeItem.id) and isValid(activeItem.itemType) + itemNode = createObject("roSGNode", "ContentNode") + itemNode.id = activeItem.id + itemNode.title = activeItem.title + itemNode.addFields({ type: activeItem.itemType }) + m.top.itemSelected = itemNode + end if + else if m.mediaBarItems.count() > 0 and m.currentIndex >= 0 and m.currentIndex < m.mediaBarItems.count() currentItem = m.mediaBarItems[m.currentIndex] if isValid(currentItem) and isValid(currentItem.id) and isValid(currentItem.itemType) itemNode = createObject("roSGNode", "ContentNode") @@ -902,7 +1102,16 @@ end function ' Public function to refresh the current display from parent sub refreshCurrentDisplay() if m.mediaBarItems.count() > 0 - m.top.currentBackdrop = "" - displayMediaBarItem(m.currentIndex) + if m.mediaBarMode = "gallery" + m.top.currentBackdrop = "" + showGalleryPage(m.galleryCurrentPage) + activeItem = getGalleryActiveItem() + if isValid(activeItem) and activeItem.backdropUrl <> "" + m.top.currentBackdrop = activeItem.backdropUrl + end if + else + m.top.currentBackdrop = "" + displayMediaBarItem(m.currentIndex) + end if end if end sub diff --git a/components/home/MediaBar.xml b/components/home/MediaBar.xml index 995dae864..52415beaf 100644 --- a/components/home/MediaBar.xml +++ b/components/home/MediaBar.xml @@ -196,6 +196,20 @@ itemSpacings="[10]" /> + + + + + diff --git a/settings/settings.json b/settings/settings.json index a83966df4..35c6252f0 100644 --- a/settings/settings.json +++ b/settings/settings.json @@ -209,6 +209,10 @@ "title": "MakD", "id": "makd" }, + { + "title": "Gallery", + "id": "gallery" + }, { "title": "Banner", "id": "banner" From 0b9ea69a93004a554e203a12236b2ea389a74078 Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Thu, 9 Jul 2026 19:27:18 -0600 Subject: [PATCH 06/18] previous commit should say gallery not banner, add item number above narrow items --- components/home/GalleryItem.bs | 8 ++++++++ components/home/GalleryItem.xml | 31 ++++++++++++++++++++----------- components/home/MediaBar.bs | 3 ++- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/components/home/GalleryItem.bs b/components/home/GalleryItem.bs index de0c9efcd..1fa0039a1 100644 --- a/components/home/GalleryItem.bs +++ b/components/home/GalleryItem.bs @@ -1,6 +1,7 @@ import "pkg:/source/utils/misc.bs" sub init() + m.itemNumber = m.top.findNode("itemNumber") m.narrowContent = m.top.findNode("narrowContent") m.wideContent = m.top.findNode("wideContent") m.narrowTitle = m.top.findNode("narrowTitle") @@ -28,6 +29,13 @@ sub contentChanged() data = m.top.itemContent if not isValid(data) then return + itemNumber = data.itemNumber + if itemNumber <> invalid + m.itemNumber.text = right("0" + itemNumber.toStr(), 2) ' Zero-pad + else + m.itemNumber.text = "" + end if + title = data.title if title = invalid then title = "" diff --git a/components/home/GalleryItem.xml b/components/home/GalleryItem.xml index 3ee2c513f..e5c14bbe1 100644 --- a/components/home/GalleryItem.xml +++ b/components/home/GalleryItem.xml @@ -5,20 +5,29 @@ + \ No newline at end of file diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index 3b82811d3..fe882b0d6 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -446,7 +446,8 @@ function processItem(item as object) as object backdropUrl: "", backdropTag: "", logoUrl: "", - tmdbId: "" + tmdbId: "", + people: invalid } if isChainValid(item, "backdropImageTags") and item.backdropImageTags.count() > 0 @@ -516,6 +517,20 @@ function processItem(item as object) as object processed.genresDisplay = displayGenres.join(" • ") end if + if isChainValid(item, "People") and item.People.count() > 0 + peopleList = [] + for each person in item.People + personType = LCase(person.Type) + if personType <> "director" and personType <> "writer" + peopleList.push(person.Name) + if peopleList.count() >= 6 then exit for + end if + end for + if peopleList.count() > 0 + processed.people = peopleList.join(", ") + end if + end if + return processed end function @@ -559,6 +574,7 @@ sub showGalleryPage(pageIndex as integer) logoUrl: item.logoUrl, posterUrl: item.posterUrl, overview: item.overview, + starring: item.people, year: item.year, rating: item.rating, runtimeDisplay: item.runtimeDisplay, diff --git a/components/home/MediaBarTask.bs b/components/home/MediaBarTask.bs index 9755cf653..bf51544d7 100644 --- a/components/home/MediaBarTask.bs +++ b/components/home/MediaBarTask.bs @@ -44,7 +44,7 @@ sub getMediaBarData() sortBy: "Random", hasBackdrop: true, limit: 50, - fields: "Overview,Genres,CommunityRating,OfficialRating,ProductionYear,RunTimeTicks,ProviderIds" + fields: "Overview,People,Genres,CommunityRating,OfficialRating,ProductionYear,RunTimeTicks,ProviderIds" }) items = isChainValid(data, "items") ? data.items : [] From 216410e7f5c20fe4f1e36c45af4e727ad27e63c0 Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Thu, 9 Jul 2026 20:39:50 -0600 Subject: [PATCH 08/18] switch to and from gallery mode --- components/home/Home.bs | 7 +++++++ components/home/MediaBar.bs | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/components/home/Home.bs b/components/home/Home.bs index 4a9eef0ff..6e148e010 100644 --- a/components/home/Home.bs +++ b/components/home/Home.bs @@ -182,6 +182,13 @@ sub onMediaBarModeChanged() if not isValid(m.mediaBar) or not m.mediaBar.visible then return applyMediaBarLayout(true) + + mode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" + if mode = "gallery" + if isValid(m.backdrop) then m.backdrop.uri = "" + if isValid(m.backdropPrev) then m.backdropPrev.uri = "" + m.backdropCrossfading = false + end if end sub ' Track MediaBar focus state diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index fe882b0d6..030da085c 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -91,6 +91,7 @@ sub onMediaBarModeChanged(_event as object) if m.mediaBarItems.count() > 0 if m.mediaBarMode = "gallery" + setupGalleryPages() m.galleryCurrentPage = 0 showGalleryPage(0) m.top.currentBackdrop = "" @@ -114,6 +115,9 @@ sub applyCurrentStyle() if mode = "moonfin" m.infoBottomAnchor = 720 + m.galleryGroup.visible = false + m.infoOverlayContent.visible = true + ' Logo Shadow m.logoShadow.width = "400" m.logoShadow.height = "120" @@ -147,11 +151,15 @@ sub applyCurrentStyle() ' Dots Background m.dotsBackground.visible = true ' Dots + m.indicatorDots.visible = true m.dotsY = 810 m.dotsBgY = 793 else if mode = "makd" m.infoBottomAnchor = 800 + m.galleryGroup.visible = false + m.infoOverlayContent.visible = true + ' Logo Shadow m.logoShadow.width = "500" m.logoShadow.height = "220" @@ -185,11 +193,16 @@ sub applyCurrentStyle() ' Dots Background m.dotsBackground.visible = false ' Dots + m.indicatorDots.visible = true m.dotsY = 810 m.dotsBgY = 793 else if mode = "gallery" m.infoBottomAnchor = 720 + if isValid(m.contentFadeOut) then m.contentFadeOut.control = "stop" + if isValid(m.contentFadeIn) then m.contentFadeIn.control = "stop" + m.pendingDisplayIndex = -1 + m.logoShadow.visible = false m.logoImage.visible = false m.titleText.visible = false @@ -200,6 +213,7 @@ sub applyCurrentStyle() m.rightArrow.visible = false m.rightArrowBg.visible = false m.dotsBackground.visible = false + m.indicatorDots.visible = false m.overviewLabel.visible = false m.galleryGroup.visible = true @@ -209,6 +223,9 @@ sub applyCurrentStyle() else if mode = "banner" m.infoBottomAnchor = 240 + m.galleryGroup.visible = false + m.infoOverlayContent.visible = true + ' Logo Shadow m.logoShadow.width = "400" m.logoShadow.height = "120" @@ -242,6 +259,7 @@ sub applyCurrentStyle() ' Dots Background m.dotsBackground.visible = false ' Dots + m.indicatorDots.visible = true m.dotsY = 190 m.dotsBgY = 173 end if From 27dd73466b59c828f9032a1026d93912ea4969a9 Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Thu, 9 Jul 2026 22:01:27 -0600 Subject: [PATCH 09/18] cleanup galleryitem & mediabar --- components/home/GalleryItem.bs | 37 ++++---- components/home/GalleryItem.xml | 2 - components/home/MediaBar.bs | 152 +++++++++++++++----------------- 3 files changed, 92 insertions(+), 99 deletions(-) diff --git a/components/home/GalleryItem.bs b/components/home/GalleryItem.bs index 094c4e4bd..da01c1ea2 100644 --- a/components/home/GalleryItem.bs +++ b/components/home/GalleryItem.bs @@ -7,7 +7,6 @@ sub init() m.narrowTitle = m.top.findNode("narrowTitle") m.narrowPoster = m.top.findNode("narrowPoster") m.itemBackdrop = m.top.findNode("itemBackdrop") - m.itemScrim = m.top.findNode("itemScrim") m.itemTitle = m.top.findNode("itemTitle") m.gYear = m.top.findNode("gYear") m.gSep1 = m.top.findNode("gSep1") @@ -43,19 +42,24 @@ sub contentChanged() ' Narrow content m.narrowTitle.text = title + isActive = data.isActive + if isActive = invalid then isActive = false + posterUrl = data.backdropUrl - if posterUrl <> "" and posterUrl <> invalid + hasPoster = isValid(posterUrl) and posterUrl <> "" + + if isActive and hasPoster + m.itemBackdrop.uri = posterUrl + m.narrowPoster.uri = "" + m.narrowPoster.visible = false + else if hasPoster m.narrowPoster.uri = posterUrl m.narrowPoster.visible = true + m.itemBackdrop.uri = "" else m.narrowPoster.uri = "" m.narrowPoster.visible = false - end if - - ' Wide content - backdropUrl = data.backdropUrl - if backdropUrl <> "" and backdropUrl <> invalid - m.itemBackdrop.uri = backdropUrl + m.itemBackdrop.uri = "" end if m.itemTitle.text = title @@ -80,11 +84,9 @@ sub contentChanged() hasRating = true labelRect = m.gRating.boundingRect() if isValid(labelRect) - paddingX = 16 - paddingY = 4 - m.gRatingBg.width = labelRect.width + paddingX - m.gRatingBg.height = labelRect.height + paddingY - m.gRatingBg.translation = [labelRect.x - paddingX / 2, labelRect.y - paddingY / 2 - 2] + m.gRatingBg.width = labelRect.width + 16 + m.gRatingBg.height = labelRect.height + 4 + m.gRatingBg.translation = [labelRect.x - 16 / 2, labelRect.y - 4 / 2 - 2] m.gRatingBg.visible = true end if else @@ -102,7 +104,7 @@ sub contentChanged() end if communityRatingDisplay = data.communityRatingDisplay - if communityRatingDisplay <> "" and communityRatingDisplay <> invalid + if isValid(communityRatingDisplay) and communityRatingDisplay <> "" m.gStar.text = "★ " m.gStar.visible = true m.gCommunityRating.text = communityRatingDisplay @@ -122,11 +124,10 @@ sub contentChanged() m.gSep3.visible = (hasRuntime and hasStarRating) genresDisplay = data.genresDisplay - hasGenres = (genresDisplay <> "" and genresDisplay <> invalid) + hasGenres = isValid(genresDisplay) and genresDisplay <> "" m.gGenres.text = genresDisplay - hasPreviousMeta = (hasYear or hasRating or hasRuntime or hasStarRating) - m.gSep4.text = (hasPreviousMeta and hasGenres) ? " • " : "" - m.gSep4.visible = (hasPreviousMeta and hasGenres) + m.gSep4.text = ((hasYear or hasRating or hasRuntime or hasStarRating) and hasGenres) ? " • " : "" + m.gSep4.visible = ((hasYear or hasRating or hasRuntime or hasStarRating) and hasGenres) overview = data.overview if overview <> invalid and overview <> "" diff --git a/components/home/GalleryItem.xml b/components/home/GalleryItem.xml index 1b6e7aedc..b2931c1bd 100644 --- a/components/home/GalleryItem.xml +++ b/components/home/GalleryItem.xml @@ -68,8 +68,6 @@ height="70" font="font:ExtraLargeBoldSystemFont" color="0xFFFFFFFF" - wrap="false" - numLines="1" text="" /> diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index 030da085c..badbd1432 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -59,6 +59,7 @@ sub init() ' Gallery mode state m.galleryPages = [] + m.galleryPageContentNodes = [] m.galleryCurrentPage = 0 m.galleryActiveIndex = 0 m.galleryTotalPages = 0 @@ -92,9 +93,11 @@ sub onMediaBarModeChanged(_event as object) if m.mediaBarItems.count() > 0 if m.mediaBarMode = "gallery" setupGalleryPages() - m.galleryCurrentPage = 0 showGalleryPage(0) m.top.currentBackdrop = "" + if m.mediaBarItems.count() > 1 + startAutoAdvanceTimer() + end if else showMediaBarItem(m.currentIndex) m.top.currentBackdrop = m.mediaBarItems[m.currentIndex].backdropUrl @@ -273,17 +276,11 @@ sub refreshMediaBarBackdropUrls() for each item in m.mediaBarItems if isValid(item.id) and isValidAndNotEmpty(item.backdropTag) - backdropParams = { - maxHeight: 1080, - maxWidth: 1920, - quality: 90, - Tag: item.backdropTag - } if useBlur - backdropParams.blur = 15 - backdropParams.quality = 60 + item.backdropUrl = item.backdropUrlBlurred + else + item.backdropUrl = item.backdropUrlSharp end if - item.backdropUrl = ImageURL(item.id, "Backdrop", backdropParams) end if end for end sub @@ -446,8 +443,6 @@ end sub ' Process item data into display format function processItem(item as object) as object - useBlur = (m.mediaBarMode = "makd") - processed = { id: item.id, title: item.name, @@ -462,6 +457,8 @@ function processItem(item as object) as object genres: [], genresDisplay: "", backdropUrl: "", + backdropUrlSharp: "", + backdropUrlBlurred: "", backdropTag: "", logoUrl: "", tmdbId: "", @@ -469,18 +466,25 @@ function processItem(item as object) as object } if isChainValid(item, "backdropImageTags") and item.backdropImageTags.count() > 0 - backdropParams = { + processed.backdropTag = item.backdropImageTags[0] + processed.backdropUrlSharp = ImageURL(item.id, "Backdrop", { maxHeight: 1080, maxWidth: 1920, quality: 90, Tag: item.backdropImageTags[0] - } - if useBlur - backdropParams.blur = 15 - backdropParams.quality = 60 + }) + processed.backdropUrlBlurred = ImageURL(item.id, "Backdrop", { + maxHeight: 1080, + maxWidth: 1920, + quality: 60, + blur: 15, + Tag: item.backdropImageTags[0] + }) + if m.mediaBarMode = "makd" + processed.backdropUrl = processed.backdropUrlBlurred + else + processed.backdropUrl = processed.backdropUrlSharp end if - processed.backdropTag = item.backdropImageTags[0] - processed.backdropUrl = ImageURL(item.id, "Backdrop", backdropParams) end if if isChainValid(item, "imageTags.Logo") @@ -556,20 +560,38 @@ end function sub setupGalleryPages() pageSize = 5 m.galleryPages = [] + m.galleryPageContentNodes = [] allItems = m.mediaBarItems - pageIndex = 0 - while pageIndex * pageSize < allItems.count() - start = pageIndex * pageSize - finish = start + pageSize - 1 + for pageIndex = 0 to (allItems.count() - 1) step pageSize + finish = pageIndex + pageSize - 1 if finish >= allItems.count() then finish = allItems.count() - 1 page = [] - for i = start to finish - page.push(allItems[i]) + content = createObject("roSGNode", "ContentNode") + for i = pageIndex to finish + item = allItems[i] + page.push(item) + child = createObject("roSGNode", "ContentNode") + child.addFields({ + title: item.title, + backdropUrl: item.backdropUrl, + logoUrl: item.logoUrl, + overview: item.overview, + starring: item.people, + year: item.year, + rating: item.rating, + runtimeDisplay: item.runtimeDisplay, + communityRatingDisplay: item.communityRatingDisplay, + genresDisplay: item.genresDisplay, + itemType: item.itemType, + isActive: false, + itemNumber: pageIndex + i + 1 + }) + content.appendChild(child) end for m.galleryPages.push(page) - pageIndex++ - end while + m.galleryPageContentNodes.push(content) + end for m.galleryTotalPages = m.galleryPages.count() m.galleryCurrentPage = 0 @@ -580,32 +602,15 @@ end sub sub showGalleryPage(pageIndex as integer) if pageIndex < 0 or pageIndex >= m.galleryTotalPages then return - page = m.galleryPages[pageIndex] - - content = createObject("roSGNode", "ContentNode") - for i = 0 to page.count() - 1 - item = page[i] - child = createObject("roSGNode", "ContentNode") - child.addFields({ - title: item.title, - backdropUrl: item.backdropUrl, - logoUrl: item.logoUrl, - posterUrl: item.posterUrl, - overview: item.overview, - starring: item.people, - year: item.year, - rating: item.rating, - runtimeDisplay: item.runtimeDisplay, - communityRatingDisplay: item.communityRatingDisplay, - genresDisplay: item.genresDisplay, - itemType: item.itemType, - isActive: (i = m.galleryActiveIndex), - itemNumber: pageIndex * 5 + i + 1 - }) - content.appendChild(child) + pageContent = m.galleryPageContentNodes[pageIndex] + + for i = 0 to pageContent.getChildCount() - 1 + pageContent.getChild(i).isActive = false end for - m.galleryGrid.content = content + pageContent.getChild(m.galleryActiveIndex).isActive = true + + m.galleryGrid.content = pageContent setGalleryColumnWidths(m.galleryActiveIndex) end sub @@ -623,28 +628,17 @@ sub galleryNextSlide() itemsOnPage = m.galleryPages[m.galleryCurrentPage].count() if m.galleryActiveIndex < itemsOnPage - 1 m.galleryActiveIndex++ - showGalleryPage(m.galleryCurrentPage) - - activeItem = m.galleryPages[m.galleryCurrentPage][m.galleryActiveIndex] - if isValid(activeItem) and activeItem.backdropUrl <> "" - m.top.currentBackdrop = activeItem.backdropUrl - end if else - ' Advance to next page if m.galleryCurrentPage < m.galleryTotalPages - 1 m.galleryCurrentPage++ - m.galleryActiveIndex = 0 else - ' Wrap to first page m.galleryCurrentPage = 0 - m.galleryActiveIndex = 0 - end if - showGalleryPage(m.galleryCurrentPage) - activeItem = m.galleryPages[m.galleryCurrentPage][m.galleryActiveIndex] - if isValid(activeItem) and activeItem.backdropUrl <> "" - m.top.currentBackdrop = activeItem.backdropUrl end if + m.galleryActiveIndex = 0 end if + + showGalleryPage(m.galleryCurrentPage) + showGalleryActiveBackdrop() end sub ' Navigate to previous item in gallery mode @@ -652,27 +646,25 @@ sub galleryPreviousSlide() if m.galleryTotalPages = 0 then return if m.galleryActiveIndex > 0 - ' Move within current page m.galleryActiveIndex-- - showGalleryPage(m.galleryCurrentPage) - activeItem = m.galleryPages[m.galleryCurrentPage][m.galleryActiveIndex] - if isValid(activeItem) and activeItem.backdropUrl <> "" - m.top.currentBackdrop = activeItem.backdropUrl - end if else - ' Go to previous page if m.galleryCurrentPage > 0 m.galleryCurrentPage-- else m.galleryCurrentPage = m.galleryTotalPages - 1 end if - ' Set active index to last item on that page m.galleryActiveIndex = m.galleryPages[m.galleryCurrentPage].count() - 1 - showGalleryPage(m.galleryCurrentPage) - activeItem = m.galleryPages[m.galleryCurrentPage][m.galleryActiveIndex] - if isValid(activeItem) and activeItem.backdropUrl <> "" - m.top.currentBackdrop = activeItem.backdropUrl - end if + end if + + showGalleryPage(m.galleryCurrentPage) + showGalleryActiveBackdrop() +end sub + +' Set backdrop to the currently active gallery item +sub showGalleryActiveBackdrop() + activeItem = getGalleryActiveItem() + if isValid(activeItem) and activeItem.backdropUrl <> "" + m.top.currentBackdrop = activeItem.backdropUrl end if end sub @@ -719,6 +711,7 @@ end sub ' Update the background size for indicator dots sub updateDotsBackgroundSize() if not isValid(m.indicatorDots) or not isValid(m.dotsBackground) then return + if not m.dotsBackground.visible then return numDots = m.indicatorDots.getChildCount() if numDots = 0 then return @@ -925,6 +918,7 @@ end sub ' Update info background to match content height sub updateInfoBackgroundSize() if not isValid(m.infoOverlayContent) or not isValid(m.infoBackground) then return + if not m.infoBackground.visible then return contentHeight = m.infoOverlayContent.boundingRect().height if contentHeight <= 0 then return From 4ae9272f988d81b677ec90741db025f637b85637 Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Fri, 10 Jul 2026 00:24:25 -0600 Subject: [PATCH 10/18] loading optimization, temporarily remove People --- components/home/MediaBar.bs | 82 ++++++++++++--------------------- components/home/MediaBarTask.bs | 2 +- 2 files changed, 30 insertions(+), 54 deletions(-) diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index badbd1432..b66d28b44 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -272,14 +272,12 @@ sub applyCurrentStyle() end sub sub refreshMediaBarBackdropUrls() - useBlur = (m.mediaBarMode = "makd") - for each item in m.mediaBarItems if isValid(item.id) and isValidAndNotEmpty(item.backdropTag) - if useBlur - item.backdropUrl = item.backdropUrlBlurred + if m.mediaBarMode = "makd" + item.backdropUrl = ImageURL(item.id, "Backdrop", {maxHeight: 1080, maxWidth: 1920, quality: 60, blur: 15, Tag: item.backdropTag}) else - item.backdropUrl = item.backdropUrlSharp + item.backdropUrl = ImageURL(item.id, "Backdrop", {maxHeight: 1080, maxWidth: 1920, quality: 90, Tag: item.backdropTag}) end if end if end for @@ -415,6 +413,10 @@ sub onMediaBarDataLoaded(event as object) m.mediaBarItems = shuffled end if + for each item in m.mediaBarItems + buildItemUrls(item) + end for + ' Initialize display if we have items if m.mediaBarItems.count() > 0 if m.mediaBarMode = "gallery" @@ -447,65 +449,31 @@ function processItem(item as object) as object id: item.id, title: item.name, itemType: item.type, - overview: invalid, - year: invalid, - rating: invalid, + overview: item.overview, + year: item.productionYear, + rating: item.officialRating, runtime: invalid, runtimeDisplay: "", - communityRating: invalid, + communityRating: item.communityRating, communityRatingDisplay: "", genres: [], genresDisplay: "", - backdropUrl: "", - backdropUrlSharp: "", - backdropUrlBlurred: "", backdropTag: "", - logoUrl: "", + hasLogo: false, tmdbId: "", people: invalid } if isChainValid(item, "backdropImageTags") and item.backdropImageTags.count() > 0 processed.backdropTag = item.backdropImageTags[0] - processed.backdropUrlSharp = ImageURL(item.id, "Backdrop", { - maxHeight: 1080, - maxWidth: 1920, - quality: 90, - Tag: item.backdropImageTags[0] - }) - processed.backdropUrlBlurred = ImageURL(item.id, "Backdrop", { - maxHeight: 1080, - maxWidth: 1920, - quality: 60, - blur: 15, - Tag: item.backdropImageTags[0] - }) - if m.mediaBarMode = "makd" - processed.backdropUrl = processed.backdropUrlBlurred - else - processed.backdropUrl = processed.backdropUrlSharp - end if end if if isChainValid(item, "imageTags.Logo") - processed.logoUrl = ImageURL(item.id, "Logo", { - maxHeight: 150, - maxWidth: 500, - quality: 100 - }) + processed.hasLogo = true end if - ' Extract metadata - if isValid(item.overview) - processed.overview = item.overview - end if - - if isValid(item.productionYear) - processed.year = item.productionYear - end if - - if isValid(item.officialRating) - processed.rating = item.officialRating + if isChainValid(item, "providerIds") + processed.tmdbId = moonfinPlugin.GetTmdbId(item.providerIds) end if if isValid(item.runTimeTicks) @@ -521,22 +489,17 @@ function processItem(item as object) as object end if if isValid(item.communityRating) - processed.communityRating = item.communityRating processed.communityRatingDisplay = formatFloat(item.communityRating, 1) end if - if isChainValid(item, "providerIds") - processed.tmdbId = moonfinPlugin.GetTmdbId(item.providerIds) - end if - if isChainValid(item, "genres") and item.genres.count() > 0 - processed.genres = item.genres ' Cache genres display string (first 3 genres joined) displayGenres = item.genres if displayGenres.count() > 3 displayGenres = [displayGenres[0], displayGenres[1], displayGenres[2]] end if processed.genresDisplay = displayGenres.join(" • ") + processed.genres = item.genres end if if isChainValid(item, "People") and item.People.count() > 0 @@ -556,6 +519,19 @@ function processItem(item as object) as object return processed end function +sub buildItemUrls(item as object) + if isValid(item.backdropTag) and item.backdropTag <> "" + if m.mediaBarMode = "makd" + item.backdropUrl = ImageURL(item.id, "Backdrop", {maxHeight: 1080, maxWidth: 1920, quality: 60, blur: 15, Tag: item.backdropTag}) + else + item.backdropUrl = ImageURL(item.id, "Backdrop", {maxHeight: 1080, maxWidth: 1920, quality: 90, Tag: item.backdropTag}) + end if + end if + if item.hasLogo + item.logoUrl = ImageURL(item.id, "Logo", {maxHeight: 150, maxWidth: 500, quality: 100}) + end if +end sub + ' Split items into pages of 5 for gallery display sub setupGalleryPages() pageSize = 5 diff --git a/components/home/MediaBarTask.bs b/components/home/MediaBarTask.bs index bf51544d7..9755cf653 100644 --- a/components/home/MediaBarTask.bs +++ b/components/home/MediaBarTask.bs @@ -44,7 +44,7 @@ sub getMediaBarData() sortBy: "Random", hasBackdrop: true, limit: 50, - fields: "Overview,People,Genres,CommunityRating,OfficialRating,ProductionYear,RunTimeTicks,ProviderIds" + fields: "Overview,Genres,CommunityRating,OfficialRating,ProductionYear,RunTimeTicks,ProviderIds" }) items = isChainValid(data, "items") ? data.items : [] From 99f508c8671dc9737efd67edac0e70f88ca2fe1a Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Fri, 10 Jul 2026 01:41:36 -0600 Subject: [PATCH 11/18] fix gallery autoadvance, fix backdrop showing on gallery mode, fix itemNumber being incorrect --- components/home/Home.bs | 12 +++++++++--- components/home/MediaBar.bs | 9 ++++----- components/home/MediaBar.xml | 1 + 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/components/home/Home.bs b/components/home/Home.bs index 6e148e010..e82eee598 100644 --- a/components/home/Home.bs +++ b/components/home/Home.bs @@ -205,8 +205,14 @@ sub onMediaBarBackdropChanged(event as object) mode = m.global.session.user.settings["ui.home.mediaBarMode"] ?? "moonfin" if mode = "gallery" - if isValid(m.backdrop) then m.backdrop.uri = "" - if isValid(m.backdropPrev) then m.backdropPrev.uri = "" + if isValid(m.backdrop) + m.backdrop.uri = "" + m.backdrop.opacity = 0 + end if + if isValid(m.backdropPrev) + m.backdropPrev.uri = "" + m.backdropPrev.opacity = 0 + end if m.backdropCrossfading = false return end if @@ -1157,7 +1163,7 @@ sub onItemDetailsLoaded() isEpisode = isValid(json.Type) and json.Type = "Episode" - if isValid(m.backdrop) and not isValidAndNotEmpty(m.currentFocusedItemServerUrl) + if isValid(m.backdrop) backdropUri = "" if isEpisode and isValidAndNotEmpty(json.SeriesId) diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index b66d28b44..6a8f08276 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -422,6 +422,9 @@ sub onMediaBarDataLoaded(event as object) if m.mediaBarMode = "gallery" setupGalleryPages() showGalleryPage(0) + if m.mediaBarItems.count() > 1 + startAutoAdvanceTimer() + end if else createIndicatorDots() if m.mediaBarItems.count() > 1 @@ -561,7 +564,7 @@ sub setupGalleryPages() genresDisplay: item.genresDisplay, itemType: item.itemType, isActive: false, - itemNumber: pageIndex + i + 1 + itemNumber: i + 1 }) content.appendChild(child) end for @@ -1110,10 +1113,6 @@ sub refreshCurrentDisplay() if m.mediaBarMode = "gallery" m.top.currentBackdrop = "" showGalleryPage(m.galleryCurrentPage) - activeItem = getGalleryActiveItem() - if isValid(activeItem) and activeItem.backdropUrl <> "" - m.top.currentBackdrop = activeItem.backdropUrl - end if else m.top.currentBackdrop = "" displayMediaBarItem(m.currentIndex) diff --git a/components/home/MediaBar.xml b/components/home/MediaBar.xml index 52415beaf..abb931ff9 100644 --- a/components/home/MediaBar.xml +++ b/components/home/MediaBar.xml @@ -231,5 +231,6 @@ + \ No newline at end of file From 354b55bdda8432651ed45e69e88a44c16d0650cd Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Fri, 10 Jul 2026 12:13:06 -0600 Subject: [PATCH 12/18] parallelize item loading, move filtering logic to the task, exclude boxset/collectionfolder in api --- components/home/MediaBar.bs | 34 ++------------ components/home/MediaBarTask.bs | 78 ++++++++++++++++++++++++++++---- components/home/MediaBarTask.xml | 1 + 3 files changed, 74 insertions(+), 39 deletions(-) diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index 6a8f08276..a40a6d6df 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -357,6 +357,7 @@ sub loadMediaBarItems() m.mediaBarTask.contentType = m.global.session.user.settings["ui.home.mediaBarContentType"] ?? "both" m.mediaBarTask.sourceType = m.global.session.user.settings["ui.home.mediaBarSourceType"] ?? "library" m.mediaBarTask.selectedLibraryIds = m.global.session.user.settings["ui.home.mediaBarLibraryIds"] ?? "[]" + m.mediaBarTask.featuredCount = m.global.session.user.settings["ui.home.featuredMediaItems"] ?? 15 m.mediaBarTask.selectedCollectionIds = m.global.session.user.settings["ui.home.mediaBarCollectionIds"] ?? "[]" m.mediaBarTask.excludedGenreIds = m.global.session.user.settings["ui.home.mediaBarExcludedGenres"] ?? "[]" m.mediaBarTask.observeFieldScoped("mediaBarData", "onMediaBarDataLoaded") @@ -377,41 +378,12 @@ sub onMediaBarDataLoaded(event as object) allItems = isChainValid(data, "items") ? data.items : [] - ' Client-side type filter - API may not reliably exclude collection types itemsWithBackdrops = [] - seenMediaBarItems = {} for each item in allItems - itemType = LCase(item.LookupCI("Type") ?? "") - if itemType <> "boxset" and itemType <> "collectionfolder" and isChainValid(item, "backdropImageTags") and item.backdropImageTags.count() > 0 - processedItem = processItem(item) - - if isValidAndNotEmpty(processedItem.tmdbId) - dedupeKey = `${itemType}|tmdb|${LCase(processedItem.tmdbId)}` - else if isValidAndNotEmpty(processedItem.id) - dedupeKey = `${itemType}|id|${processedItem.id}` - else - nameKey = LCase(item.LookupCI("Name") ?? "") - yearKey = isValid(item.LookupCI("ProductionYear")) ? item.LookupCI("ProductionYear").toStr() : "" - dedupeKey = `${itemType}|name|${nameKey}|${yearKey}` - end if - - if not seenMediaBarItems.DoesExist(dedupeKey) - seenMediaBarItems[dedupeKey] = true - itemsWithBackdrops.push(processedItem) - end if - end if + itemsWithBackdrops.push(processItem(item)) end for - settingValue = m.global.session.user.settings["ui.home.featuredMediaItems"] ?? invalid - carouselItemCount = isValid(settingValue) ? settingValue.toInt() : 15 - - ' Shuffle and take configured number of items - shuffled = shuffleArray(itemsWithBackdrops) - if shuffled.count() > carouselItemCount - m.mediaBarItems = shuffled.slice(0, carouselItemCount) - else - m.mediaBarItems = shuffled - end if + m.mediaBarItems = itemsWithBackdrops for each item in m.mediaBarItems buildItemUrls(item) diff --git a/components/home/MediaBarTask.bs b/components/home/MediaBarTask.bs index 9755cf653..08890212d 100644 --- a/components/home/MediaBarTask.bs +++ b/components/home/MediaBarTask.bs @@ -1,5 +1,6 @@ import "pkg:/source/api/sdk.bs" import "pkg:/source/utils/misc.bs" +import "pkg:/source/api/baserequest.bs" sub init() m.top.functionName = "getMediaBarData" @@ -34,29 +35,90 @@ sub getMediaBarData() return end if - allItems = [] + fetchLimit = m.top.featuredCount + 5 + port = CreateObject("roMessagePort") + requests = [] for each parentId in parentIds - data = api.items.Get({ + req = APIRequest("/Items/", { userId: m.top.userId, parentId: parentId, includeItemTypes: includeTypes, + excludeItemTypes: "BoxSet,CollectionFolder", recursive: true, sortBy: "Random", hasBackdrop: true, - limit: 50, + imageTypes: "Backdrop", + limit: fetchLimit, fields: "Overview,Genres,CommunityRating,OfficialRating,ProductionYear,RunTimeTicks,ProviderIds" }) - - items = isChainValid(data, "items") ? data.items : [] - if excludedGenreNames.count() > 0 - items = filterItemsByExcludedGenres(items, excludedGenreNames) + if req <> invalid + req.setMessagePort(port) + if req.AsyncGetToString() + requests.push(req) + end if end if - allItems.append(items) end for + ' Parallelize + allItems = [] + completed = 0 + totalRequests = requests.count() + while completed < totalRequests + msg = wait(30000, port) + if type(msg) <> "roUrlEvent" then exit while + + if msg.GetResponseCode() = 200 + json = ParseJson(msg.GetString()) + items = isChainValid(json, "items") ? json.items : [] + if excludedGenreNames.count() > 0 + items = filterItemsByExcludedGenres(items, excludedGenreNames) + end if + allItems.append(items) + end if + completed++ + end while + + filteredItems = filterAndDedupeItems(allItems) + shuffled = shuffleArray(filteredItems) + featuredCount = m.top.featuredCount + if shuffled.count() > featuredCount + allItems = shuffled.slice(0, featuredCount) + else + allItems = shuffled + end if + m.top.mediaBarData = { items: allItems } end sub +function filterAndDedupeItems(items as object) as object + result = [] + seen = {} + for each item in items + itemType = LCase(item.LookupCI("Type") ?? "") + + tmdbId = "" + if isChainValid(item, "providerIds") + tmdbId = moonfinPlugin.GetTmdbId(item.providerIds) + end if + + if isValidAndNotEmpty(tmdbId) + dedupeKey = `${itemType}|tmdb|${LCase(tmdbId)}` + else if isValidAndNotEmpty(item.id) + dedupeKey = `${itemType}|id|${item.id}` + else + nameKey = LCase(item.LookupCI("Name") ?? "") + yearKey = isValid(item.LookupCI("ProductionYear")) ? item.LookupCI("ProductionYear").toStr() : "" + dedupeKey = `${itemType}|name|${nameKey}|${yearKey}` + end if + + if not seen.DoesExist(dedupeKey) + seen[dedupeKey] = true + result.push(item) + end if + end for + return result +end function + function getAllowedCollectionTypes() as object contentType = m.top.contentType if contentType = "movies" diff --git a/components/home/MediaBarTask.xml b/components/home/MediaBarTask.xml index f84a72a31..fc5fb992f 100644 --- a/components/home/MediaBarTask.xml +++ b/components/home/MediaBarTask.xml @@ -8,5 +8,6 @@ + \ No newline at end of file From d6e4478f845358a3b112c5512e63330280aa5b76 Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Fri, 10 Jul 2026 12:55:12 -0600 Subject: [PATCH 13/18] fix indicator dots not showing/translating when switching modes --- components/home/MediaBar.bs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index a40a6d6df..65eaf2c55 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -99,6 +99,9 @@ sub onMediaBarModeChanged(_event as object) startAutoAdvanceTimer() end if else + if m.indicatorDots.getChildCount() = 0 + createIndicatorDots() + end if showMediaBarItem(m.currentIndex) m.top.currentBackdrop = m.mediaBarItems[m.currentIndex].backdropUrl end if @@ -662,7 +665,6 @@ end sub ' Update the background size for indicator dots sub updateDotsBackgroundSize() if not isValid(m.indicatorDots) or not isValid(m.dotsBackground) then return - if not m.dotsBackground.visible then return numDots = m.indicatorDots.getChildCount() if numDots = 0 then return @@ -679,12 +681,17 @@ sub updateDotsBackgroundSize() totalDotsWidth = (numDots - 1) * inactiveDotSize + activeDotSize totalSpacingWidth = (numDots - 1) * spacing calculatedWidth = totalDotsWidth + totalSpacingWidth + padding - m.dotsBackground.width = calculatedWidth ' Center the background and dots on screen ' Screen width is 1920, so center point is 960 screenCenter = 960 + m.indicatorDots.translation = [screenCenter - (totalDotsWidth + totalSpacingWidth) / 2, m.dotsY] + + if not m.dotsBackground.visible then return + + calculatedWidth = totalDotsWidth + totalSpacingWidth + padding + m.dotsBackground.width = calculatedWidth m.dotsBackground.translation = [screenCenter - calculatedWidth / 2, m.dotsBgY] end sub From bdca4c3cba1466981c11cc14fc45e689c100846a Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Fri, 10 Jul 2026 13:37:44 -0600 Subject: [PATCH 14/18] fix logoUrl error, readd people for gallery starring --- components/home/MediaBar.bs | 1 + components/home/MediaBarTask.bs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/home/MediaBar.bs b/components/home/MediaBar.bs index 65eaf2c55..06bef9e21 100644 --- a/components/home/MediaBar.bs +++ b/components/home/MediaBar.bs @@ -437,6 +437,7 @@ function processItem(item as object) as object genres: [], genresDisplay: "", backdropTag: "", + logoUrl: "", hasLogo: false, tmdbId: "", people: invalid diff --git a/components/home/MediaBarTask.bs b/components/home/MediaBarTask.bs index 08890212d..f489891e5 100644 --- a/components/home/MediaBarTask.bs +++ b/components/home/MediaBarTask.bs @@ -49,7 +49,8 @@ sub getMediaBarData() hasBackdrop: true, imageTypes: "Backdrop", limit: fetchLimit, - fields: "Overview,Genres,CommunityRating,OfficialRating,ProductionYear,RunTimeTicks,ProviderIds" + fields: "Overview,People,Genres,CommunityRating,OfficialRating,ProductionYear,RunTimeTicks,ProviderIds", + personTypes: "Actor,GuestStar" }) if req <> invalid req.setMessagePort(port) From 82df160e56af1247662058501088aba872f8712f Mon Sep 17 00:00:00 2001 From: Harley Kradovill Date: Sat, 11 Jul 2026 01:22:14 -0600 Subject: [PATCH 15/18] add bookshelf style, lots of code cleanup --- components/home/BookshelfItem.bs | 33 ++++ components/home/BookshelfItem.xml | 56 ++++++ components/home/GalleryItem.bs | 27 +-- components/home/GalleryItem.xml | 2 +- components/home/Home.bs | 152 +++++---------- components/home/MediaBar.bs | 310 ++++++++++++++++++++++-------- components/home/MediaBar.xml | 15 +- images/bookSpine.png | Bin 0 -> 15741 bytes images/bookWide.png | Bin 0 -> 122402 bytes images/bookshelfBg.png | Bin 0 -> 133243 bytes settings/settings.json | 4 + 11 files changed, 388 insertions(+), 211 deletions(-) create mode 100644 components/home/BookshelfItem.bs create mode 100644 components/home/BookshelfItem.xml create mode 100644 images/bookSpine.png create mode 100644 images/bookWide.png create mode 100644 images/bookshelfBg.png diff --git a/components/home/BookshelfItem.bs b/components/home/BookshelfItem.bs new file mode 100644 index 000000000..a6d1459ec --- /dev/null +++ b/components/home/BookshelfItem.bs @@ -0,0 +1,33 @@ +import "pkg:/source/utils/misc.bs" + +sub init() + m.itemNumber = m.top.findNode("itemNumber") + m.narrowContent = m.top.findNode("narrowContent") + m.narrowTitle = m.top.findNode("narrowTitle") + m.wideContent = m.top.findNode("wideContent") + m.itemPoster = m.top.findNode("itemPoster") + m.narrowSpine = m.top.findNode("narrowSpine") + m.wideSpine = m.top.findNode("wideSpine") +end sub + +sub contentChanged() + data = m.top.itemContent + if not isValid(data) then return + + blendColor = data.blendColor <> invalid ? data.blendColor : "0x7A5334FF" + + m.narrowSpine.blendColor = blendColor + m.wideSpine.blendColor = blendColor + + isActive = data.isActive <> invalid ? data.isActive : false + title = data.title <> invalid ? data.title : "" + + m.itemNumber.text = data.itemNumber <> invalid ? right("0" + data.itemNumber.toStr(), 2) : "" + + m.narrowContent.visible = not isActive + m.wideContent.visible = isActive + + m.narrowTitle.text = title + + if isActive then m.itemPoster.uri = data.backdropUrl +end sub diff --git a/components/home/BookshelfItem.xml b/components/home/BookshelfItem.xml new file mode 100644 index 000000000..d71202d75 --- /dev/null +++ b/components/home/BookshelfItem.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/components/home/GalleryItem.bs b/components/home/GalleryItem.bs index da01c1ea2..5c80c16d2 100644 --- a/components/home/GalleryItem.bs +++ b/components/home/GalleryItem.bs @@ -29,42 +29,29 @@ sub contentChanged() data = m.top.itemContent if not isValid(data) then return - itemNumber = data.itemNumber - if itemNumber <> invalid - m.itemNumber.text = right("0" + itemNumber.toStr(), 2) ' Zero-pad - else - m.itemNumber.text = "" - end if + isActive = data.isActive <> invalid ? data.isActive : false + title = data.title <> invalid ? data.title : "" - title = data.title - if title = invalid then title = "" + m.itemNumber.text = data.itemNumber <> invalid ? right("0" + data.itemNumber.toStr(), 2) : "" ' Narrow content m.narrowTitle.text = title - isActive = data.isActive - if isActive = invalid then isActive = false - posterUrl = data.backdropUrl hasPoster = isValid(posterUrl) and posterUrl <> "" + m.narrowPoster.uri = "" + m.narrowPoster.visible = false + m.itemBackdrop.uri = "" if isActive and hasPoster m.itemBackdrop.uri = posterUrl - m.narrowPoster.uri = "" - m.narrowPoster.visible = false else if hasPoster m.narrowPoster.uri = posterUrl m.narrowPoster.visible = true - m.itemBackdrop.uri = "" - else - m.narrowPoster.uri = "" - m.narrowPoster.visible = false - m.itemBackdrop.uri = "" end if m.itemTitle.text = title - ' Metadata hasYear = false hasRating = false hasRuntime = false @@ -138,8 +125,6 @@ sub contentChanged() m.gStarring.text = data.starring - isActive = data.isActive - if isActive = invalid then isActive = false m.wideContent.visible = isActive m.narrowContent.visible = not isActive end sub diff --git a/components/home/GalleryItem.xml b/components/home/GalleryItem.xml index b2931c1bd..e884356d5 100644 --- a/components/home/GalleryItem.xml +++ b/components/home/GalleryItem.xml @@ -32,7 +32,7 @@ />