diff --git a/src/assets/css/fonts.css b/src/assets/css/fonts.css index cb0da0f80..6e87f11d9 100644 --- a/src/assets/css/fonts.css +++ b/src/assets/css/fonts.css @@ -1,7 +1,5 @@ html { font-family: "Noto Sans", sans-serif; - font-size: 93%; - -webkit-text-size-adjust: 100%; text-size-adjust: 100%; -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility; @@ -29,7 +27,9 @@ h3 { } .layout-tv { - font-size: 130%; + /* Per WebOS and Tizen guidelines, fonts must be 20px minimum. + This takes the 16px baseline and multiplies it by 1.25 to get 20px. */ + font-size: 125%; } .layout-mobile { diff --git a/src/assets/css/librarybrowser.css b/src/assets/css/librarybrowser.css index 643fb9ca9..c9ee82c8a 100644 --- a/src/assets/css/librarybrowser.css +++ b/src/assets/css/librarybrowser.css @@ -28,6 +28,10 @@ padding-top: 0 !important; } +.layout-tv .itemDetailPage { + padding-top: 4.2em !important; +} + .standalonePage { padding-top: 4.5em !important; } @@ -163,6 +167,12 @@ transition: background ease-in-out 0.5s; } +.layout-tv .skinHeader { + /* In TV layout, it makes more sense to keep the top bar at the top of the page + Having it follow the view only makes us lose vertical space, while not being focusable */ + position: relative; +} + .hiddenViewMenuBar .skinHeader { display: none; } @@ -447,8 +457,7 @@ height: 26.5vh; } -.layout-desktop .itemBackdrop::after, -.layout-tv .itemBackdrop::after { +.layout-desktop .itemBackdrop::after { content: ""; width: 100%; height: 100%; @@ -456,8 +465,8 @@ display: block; } -.layout-desktop .noBackdrop .itemBackdrop, -.layout-tv .noBackdrop .itemBackdrop { +.layout-tv .itemBackdrop, +.layout-desktop .noBackdrop .itemBackdrop { display: none; } @@ -624,6 +633,10 @@ z-index: 2; } +.layout-tv .detailPagePrimaryContainer { + display: block; +} + .layout-mobile .detailPagePrimaryContainer { display: block; position: relative; @@ -637,12 +650,16 @@ padding-left: 32.45vw; } -.layout-desktop .detailRibbon, -.layout-tv .detailRibbon { +.layout-desktop .detailRibbon { margin-top: -7.2em; height: 7.2em; } +.layout-tv .detailRibbon { + margin-top: 0; + height: inherit; +} + .layout-desktop .noBackdrop .detailRibbon, .layout-tv .noBackdrop .detailRibbon { margin-top: 0; @@ -748,8 +765,7 @@ div.itemDetailGalleryLink.defaultCardBackground { position: relative; } - .layout-desktop .itemBackdrop, - .layout-tv .itemBackdrop { + .layout-desktop .itemBackdrop { height: 40vh; } @@ -775,13 +791,8 @@ div.itemDetailGalleryLink.defaultCardBackground { } .emby-button.detailFloatingButton { - position: absolute; - background-color: rgba(0, 0, 0, 0.5); - z-index: 3; - top: 100%; - left: 90%; - margin: -2.2em 0 0 -2.2em; - padding: 0.4em; + font-size: 1.4em; + margin-right: 0.5em !important; color: rgba(255, 255, 255, 0.76); } @@ -844,7 +855,7 @@ div.itemDetailGalleryLink.defaultCardBackground { -webkit-align-items: center; align-items: center; margin: 0 !important; - padding: 0.5em 0.7em !important; + padding: 0.7em 0.7em !important; } @media all and (min-width: 29em) { @@ -913,10 +924,6 @@ div.itemDetailGalleryLink.defaultCardBackground { } @media all and (min-width: 100em) { - .detailFloatingButton { - display: none !important; - } - .personBackdrop { display: none !important; } @@ -925,6 +932,11 @@ div.itemDetailGalleryLink.defaultCardBackground { font-size: 108%; margin: 1.25em 0; } + + .layout-tv .mainDetailButtons { + font-size: 108%; + margin: 1em 0 1.25em; + } } @media all and (max-width: 50em) { @@ -1140,13 +1152,13 @@ div:not(.sectionTitleContainer-cards) > .sectionTitle-cards { } .layout-tv .padded-top-focusscale { - padding-top: 1em; - margin-top: -1em; + padding-top: 1.5em; + margin-top: -1.5em; } .layout-tv .padded-bottom-focusscale { - padding-bottom: 1em; - margin-bottom: -1em; + padding-bottom: 1.5em; + margin-bottom: -1.5em; } @media all and (min-height: 31.25em) { diff --git a/src/components/cardbuilder/card.css b/src/components/cardbuilder/card.css index 74c376e85..4c046ce98 100644 --- a/src/components/cardbuilder/card.css +++ b/src/components/cardbuilder/card.css @@ -209,6 +209,10 @@ button::-moz-focus-inner { contain: strict; } +.defaultCardBackground { + display: flex; +} + .cardContent:not(.defaultCardBackground) { background-color: transparent; } diff --git a/src/controllers/itemDetails/index.html b/src/controllers/itemDetails/index.html index a69d91787..3c808e0b7 100644 --- a/src/controllers/itemDetails/index.html +++ b/src/controllers/itemDetails/index.html @@ -7,11 +7,11 @@
-
-
+
+
-
+
-
+

${Schedule}

diff --git a/src/controllers/itemDetails/index.js b/src/controllers/itemDetails/index.js index c1ec022e5..56ecdb11f 100644 --- a/src/controllers/itemDetails/index.js +++ b/src/controllers/itemDetails/index.js @@ -414,7 +414,7 @@ function renderName(item, container, context) { }, { context: context }); - parentNameHtml.push('' + item.SeriesName + ''); + parentNameHtml.push('' + item.SeriesName + ''); } else if (item.IsSeries || item.EpisodeTitle) { parentNameHtml.push(item.Name); } @@ -429,7 +429,7 @@ function renderName(item, container, context) { }, { context: context }); - parentNameHtml.push('' + item.SeriesName + ''); + parentNameHtml.push('' + item.SeriesName + ''); } else if (item.ParentIndexNumber != null && item.Type === 'Episode') { parentRoute = appRouter.getRouteUrl({ Id: item.SeasonId, @@ -440,7 +440,7 @@ function renderName(item, container, context) { }, { context: context }); - parentNameHtml.push('' + item.SeasonName + ''); + parentNameHtml.push('' + item.SeasonName + ''); } else if (item.ParentIndexNumber != null && item.IsSeries) { parentNameHtml.push(item.SeasonName || 'S' + item.ParentIndexNumber); } else if (item.Album && item.AlbumId && (item.Type === 'MusicVideo' || item.Type === 'Audio')) { @@ -453,7 +453,7 @@ function renderName(item, container, context) { }, { context: context }); - parentNameHtml.push('' + item.Album + ''); + parentNameHtml.push('' + item.Album + ''); } else if (item.Album) { parentNameHtml.push(item.Album); } @@ -570,9 +570,12 @@ function reloadFromItem(instance, page, params, item, user) { // Start rendering the artwork first renderImage(page, item); - renderLogo(page, item, apiClient); + // Save some screen real estate in TV mode + if (!layoutManager.tv) { + renderLogo(page, item, apiClient); + renderDetailPageBackdrop(page, item, apiClient); + } renderBackdrop(item); - renderDetailPageBackdrop(page, item, apiClient); // Render the main information for the item page.querySelector('.detailPagePrimaryContainer').classList.add('detailRibbon'); @@ -764,6 +767,9 @@ function renderDetailImage(elem, item, imageLoader) { elem.innerHTML = cardHtml; imageLoader.lazyChildren(elem); + + // Avoid breaking the design by preventing focus of the poster using the keyboard. + elem.querySelector('button').tabIndex = -1; } function renderImage(page, item) { @@ -1059,7 +1065,12 @@ function renderDetails(page, item, apiClient, context, isStatic) { renderOverview(page, item); renderMiscInfo(page, item); reloadUserDataButtons(page, item); - renderLinks(page, item); + + // Don't allow redirection to other websites from the TV layout + if (!layoutManager.tv) { + renderLinks(page, item); + } + renderTags(page, item); renderSeriesAirTime(page, item, isStatic); } @@ -1990,6 +2001,17 @@ export default function (view, params) { let currentItem; const self = this; const apiClient = params.serverId ? connectionManager.getApiClient(params.serverId) : ApiClient; + + const btnResume = view.querySelector('.mainDetailButtons .btnResume'); + const btnPlay = view.querySelector('.mainDetailButtons .btnPlay'); + if (layoutManager.tv && !btnResume.classList.contains('hide')) { + btnResume.classList.add('fab'); + btnResume.classList.add('detailFloatingButton'); + } else if (layoutManager.tv && btnResume.classList.contains('hide')) { + btnPlay.classList.add('fab'); + btnPlay.classList.add('detailFloatingButton'); + } + view.querySelectorAll('.btnPlay'); bindAll(view, '.btnPlay', 'click', onPlayClick); bindAll(view, '.btnResume', 'click', onPlayClick); diff --git a/src/controllers/user/menu/index.html b/src/controllers/user/menu/index.html index 4d91f1206..1c131919f 100644 --- a/src/controllers/user/menu/index.html +++ b/src/controllers/user/menu/index.html @@ -57,7 +57,7 @@
-
+

${HeaderAdmin}

diff --git a/src/controllers/user/menu/index.js b/src/controllers/user/menu/index.js index f44a193a5..7a8d619bb 100644 --- a/src/controllers/user/menu/index.js +++ b/src/controllers/user/menu/index.js @@ -1,4 +1,5 @@ import appHost from 'apphost'; +import layoutManager from 'layoutManager'; import 'listViewStyle'; import 'emby-button'; @@ -38,19 +39,19 @@ export default function (view, params) { page.querySelector('.selectServer').classList.add('hide'); } - // hide the actions if user preferences are being edited for a different user + ApiClient.getUser(userId).then(function (user) { + page.querySelector('.headerUsername').innerHTML = user.Name; + if (user.Policy.IsAdministrator && !layoutManager.tv) { + page.querySelector('.adminSection').classList.remove('hide'); + } + }); + + // Hide the actions if user preferences are being edited for a different user if (params.userId && params.userId !== Dashboard.getCurrentUserId) { page.querySelector('.userSection').classList.add('hide'); page.querySelector('.adminSection').classList.add('hide'); } - ApiClient.getUser(userId).then(function (user) { - page.querySelector('.headerUsername').innerHTML = user.Name; - if (!user.Policy.IsAdministrator) { - page.querySelector('.adminSection').classList.add('hide'); - } - }); - import('autoFocuser').then(({default: autoFocuser}) => { autoFocuser.autoFocus(view); }); diff --git a/src/libraries/scroller.js b/src/libraries/scroller.js index c460ec5b2..dbb3de16e 100644 --- a/src/libraries/scroller.js +++ b/src/libraries/scroller.js @@ -256,7 +256,11 @@ var scrollerFactory = function (frame, options) { ensureSizeInfo(); var pos = self._pos; - newPos = within(newPos, pos.start, pos.end); + if (layoutManager.tv) { + newPos = within(newPos, pos.start); + } else { + newPos = within(newPos, pos.start, pos.end); + } if (!transform) { nativeScrollTo(nativeScrollElement, newPos, immediate); diff --git a/src/themes/appletv/theme.css b/src/themes/appletv/theme.css index 41540ce0d..ad18bf24c 100644 --- a/src/themes/appletv/theme.css +++ b/src/themes/appletv/theme.css @@ -23,21 +23,25 @@ html { .skinHeader-withBackground { color: rgba(0, 0, 0, 0.7); background: #303030; - background: -webkit-gradient(linear, left top, right top, from(#bcbcbc), color-stop(#a7b4b7), color-stop(#beb5a5), color-stop(#adbec2), to(#b9c7cb)); - background: -webkit-linear-gradient(left, #bcbcbc, #a7b4b7, #beb5a5, #adbec2, #b9c7cb); - background: -o-linear-gradient(left, #bcbcbc, #a7b4b7, #beb5a5, #adbec2, #b9c7cb); background: linear-gradient(to right, #bcbcbc, #a7b4b7, #beb5a5, #adbec2, #b9c7cb); } .skinHeader.semiTransparent { - -webkit-backdrop-filter: none !important; backdrop-filter: none !important; } +.layout-tv .skinHeader.semiTransparent { + background: none; +} + .pageTitleWithDefaultLogo { background-image: url(../../assets/img/banner-dark.png); } +.layout-tv .pageTitleWithDefaultLogo { + background-image: url(../../assets/img/icon-transparent.png); +} + html { background: #d5e9f2; } @@ -234,12 +238,13 @@ html { .detailRibbon { background: #303030; - background: -webkit-gradient(linear, left top, right top, from(#bcbcbc), color-stop(#a7b4b7), color-stop(#beb5a5), color-stop(#adbec2), to(#b9c7cb)); - background: -webkit-linear-gradient(left, #bcbcbc, #a7b4b7, #beb5a5, #adbec2, #b9c7cb); - background: -o-linear-gradient(left, #bcbcbc, #a7b4b7, #beb5a5, #adbec2, #b9c7cb); background: linear-gradient(to right, #bcbcbc, #a7b4b7, #beb5a5, #adbec2, #b9c7cb); } +.layout-tv .detailRibbon { + background: none; +} + .detailTableBodyRow-shaded:nth-child(even) { background: #f8f8f8; background: rgba(0, 0, 0, 0.1); diff --git a/src/themes/blueradiance/theme.css b/src/themes/blueradiance/theme.css index 7ecc4a74a..85795c90c 100644 --- a/src/themes/blueradiance/theme.css +++ b/src/themes/blueradiance/theme.css @@ -21,26 +21,28 @@ html { .skinHeader-withBackground { background: #303030; - background: -webkit-gradient(linear, left top, right top, from(#291a31), color-stop(#033664), color-stop(#011432), color-stop(#141a3a), to(#291a31)); - background: -webkit-linear-gradient(left, #291a31, #033664, #011432, #141a3a, #291a31); - background: -o-linear-gradient(left, #291a31, #033664, #011432, #141a3a, #291a31); background: linear-gradient(to right, #291a31, #033664, #011432, #141a3a, #291a31); } .skinHeader.semiTransparent { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; - background: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.6)), to(rgba(0, 0, 0, 0))); - background: -webkit-linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0)); - background: -o-linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0)); background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0)); background-color: rgba(0, 0, 0, 0.3); } +.layout-tv .skinHeader.semiTransparent { + background: none; +} + .pageTitleWithDefaultLogo { background-image: url(../../assets/img/banner-light.png); } +.layout-tv .pageTitleWithDefaultLogo { + background-image: url(../../assets/img/icon-transparent.png); +} + .dialog, .nowPlayingPlaylist, .nowPlayingContextMenu, @@ -231,6 +233,10 @@ html { background: linear-gradient(to right, #291a31, #033664, #011432, #141a3a, #291a31); } +.layout-tv .detailRibbon { + background: none; +} + .detailTableBodyRow-shaded:nth-child(even) { background: #1c1c1c; background: rgba(30, 30, 30, 0.9); diff --git a/src/themes/dark/theme.css b/src/themes/dark/theme.css index be2b9269a..1024770c7 100644 --- a/src/themes/dark/theme.css +++ b/src/themes/dark/theme.css @@ -30,10 +30,18 @@ html { background-color: rgba(0, 0, 0, 0.4); } +.layout-tv .skinHeader.semiTransparent { + background: none; +} + .pageTitleWithDefaultLogo { background-image: url(../../assets/img/banner-light.png); } +.layout-tv .pageTitleWithDefaultLogo { + background-image: url(../../assets/img/icon-transparent.png); +} + .backgroundContainer, .dialog, .nowPlayingPlaylist, @@ -207,6 +215,10 @@ html { background: rgba(32, 32, 32, 0.8); } +.layout-tv .detailRibbon { + background: none; +} + .noBackdrop .detailRibbon { background: #202020; } @@ -454,3 +466,8 @@ html { background-color: #00a4dc; color: #fff; } + +.layout-tv .emby-button.detailFloatingButton:focus { + background-color: #f2f2f2; + color: #00a4dc; +} diff --git a/src/themes/light/theme.css b/src/themes/light/theme.css index c5161985c..ec618138c 100644 --- a/src/themes/light/theme.css +++ b/src/themes/light/theme.css @@ -38,10 +38,18 @@ html { background-color: rgba(0, 0, 0, 0.4); } +.layout-tv .skinHeader.semiTransparent { + background: none; +} + .pageTitleWithDefaultLogo { background-image: url(../../assets/img/banner-light.png); } +.layout-tv .pageTitleWithDefaultLogo { + background-image: url(../../assets/img/icon-transparent.png); +} + .backgroundContainer, html { background-color: #f2f2f2; @@ -233,6 +241,10 @@ html { box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37); } +.layout-tv .detailRibbon { + background: none; +} + .detailTableBodyRow-shaded:nth-child(even) { background: #f8f8f8; } diff --git a/src/themes/purplehaze/theme.css b/src/themes/purplehaze/theme.css index 1d82afc23..4b2451cfe 100644 --- a/src/themes/purplehaze/theme.css +++ b/src/themes/purplehaze/theme.css @@ -17,25 +17,28 @@ html { .skinHeader-withBackground { background: #000420; - background: -moz-linear-gradient(left, #000420 0%, #06256f 18%, #2b052b 38%, #2b052b 68%, #06256f 81%, #000420 100%); - background: -webkit-linear-gradient(left, #000420 0%, #06256f 18%, #2b052b 38%, #2b052b 68%, #06256f 81%, #000420 100%); background: linear-gradient(to right, #000420 0%, #06256f 18%, #2b052b 38%, #2b052b 68%, #06256f 81%, #000420 100%); } .skinHeader.semiTransparent { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; - background: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.6)), to(rgba(0, 0, 0, 0))); - background: -webkit-linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0)); - background: -o-linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0)); background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0)); background-color: rgba(0, 0, 0, 0.3); } +.layout-tv .skinHeader.semiTransparent { + background: none; +} + .pageTitleWithDefaultLogo { background-image: url(../../assets/img/banner-light.png); } +.layout-tv .pageTitleWithDefaultLogo { + background-image: url(../../assets/img/icon-transparent.png); +} + .dialog, .nowPlayingPlaylist, .nowPlayingContextMenu, @@ -318,6 +321,10 @@ a[data-role=button] { background: linear-gradient(to right, #000420 0%, #06256f 18%, #2b052b 38%, #2b052b 68%, #06256f 81%, #000420 100%); } +.layout-tv .detailRibbon { + background: none; +} + .detailTableBodyRow-shaded:nth-child(even) { background: #1c1c1c; background: rgba(30, 30, 30, 0.9); diff --git a/src/themes/wmc/theme.css b/src/themes/wmc/theme.css index 4a7375a12..a0db08962 100644 --- a/src/themes/wmc/theme.css +++ b/src/themes/wmc/theme.css @@ -26,9 +26,6 @@ html { .formDialogHeader:not(.formDialogHeader-clear), .skinHeader-withBackground { - background: -webkit-gradient(linear, left top, left bottom, from(#0c2450), to(#081b3b)); - background: -webkit-linear-gradient(top, #0c2450, #081b3b); - background: -o-linear-gradient(top, #0c2450, #081b3b); background: linear-gradient(to bottom, #0c2450, #081b3b); background-color: #0c2450; } @@ -36,17 +33,22 @@ html { .skinHeader.semiTransparent { -webkit-backdrop-filter: none !important; backdrop-filter: none !important; - background: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.6)), to(rgba(0, 0, 0, 0))); - background: -webkit-linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0)); - background: -o-linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0)); background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0)); background-color: rgba(0, 0, 0, 0.3); } +.layout-tv .skinHeader.semiTransparent { + background: none; +} + .pageTitleWithDefaultLogo { background-image: url(../../assets/img/banner-light.png); } +.layout-tv .pageTitleWithDefaultLogo { + background-image: url(../../assets/img/icon-transparent.png); +} + .backgroundContainer, .dialog, .nowPlayingPlaylist, @@ -213,6 +215,10 @@ html { background-color: #081b3b; } +.layout-tv .detailRibbon { + background: none; +} + .detailTableBodyRow-shaded:nth-child(even) { background: #1c1c1c; background: rgba(0, 0, 0, 0.3);