diff --git a/src/components/emby-slider/emby-slider.css b/src/components/emby-slider/emby-slider.css index bd258d3bc5..b173f5c511 100644 --- a/src/components/emby-slider/emby-slider.css +++ b/src/components/emby-slider/emby-slider.css @@ -87,6 +87,10 @@ _:-ms-input-placeholder { transform: scale(1.6); } +.mdl-slider.show-focus:focus::-webkit-slider-thumb { + transform: scale(1.6); +} + .slider-no-webkit-thumb::-webkit-slider-thumb { opacity: 0 !important; } diff --git a/src/components/emby-slider/emby-slider.js b/src/components/emby-slider/emby-slider.js index 1ca1448ea1..c340e79359 100644 --- a/src/components/emby-slider/emby-slider.js +++ b/src/components/emby-slider/emby-slider.js @@ -19,6 +19,11 @@ define(['browser', 'dom', 'layoutManager', 'css!./emby-slider', 'registerElement function updateValues() { + // Do not update values when dragging with keyboard to keep current progress for reference + if (!!this.keyboardDragging) { + return; + } + var range = this; var value = range.value; @@ -82,6 +87,9 @@ define(['browser', 'dom', 'layoutManager', 'css!./emby-slider', 'registerElement if (!layoutManager.mobile) { this.classList.add('mdl-slider-hoverthumb'); } + if (layoutManager.tv) { + this.classList.add('show-focus'); + } var containerElement = this.parentNode; containerElement.classList.add('mdl-slider-container'); @@ -177,6 +185,108 @@ define(['browser', 'dom', 'layoutManager', 'css!./emby-slider', 'registerElement } }; + /** + * Keyboard dragging timeout. + * After this delay "change" event will be fired. + */ + var KeyboardDraggingTimeout = 1000; + + /** + * Keyboard dragging timer. + */ + var keyboardDraggingTimer; + + /** + * Start keyboard dragging. + * + * @param {Object} elem slider itself + */ + function startKeyboardDragging(elem) { + elem.keyboardDragging = true; + + clearTimeout(keyboardDraggingTimer); + keyboardDraggingTimer = setTimeout(function () { + finishKeyboardDragging(elem); + }, KeyboardDraggingTimeout); + } + + /** + * Finish keyboard dragging. + * + * @param {Object} elem slider itself + */ + function finishKeyboardDragging(elem) { + clearTimeout(keyboardDraggingTimer); + keyboardDraggingTimer = undefined; + + elem.keyboardDragging = false; + + var event = new Event('change', { + bubbles: true, + cancelable: false + }); + elem.dispatchEvent(event); + } + + /** + * Do step by delta. + * + * @param {Object} elem slider itself + * @param {number} delta step amount + */ + function stepKeyboard(elem, delta) { + startKeyboardDragging(elem); + + elem.value = Math.max(elem.min, Math.min(elem.max, parseFloat(elem.value) + delta)); + + var event = new Event('input', { + bubbles: true, + cancelable: false + }); + elem.dispatchEvent(event); + } + + /** + * Handle KeyDown event + */ + function onKeyDown(e) { + switch (e.key) { + case 'ArrowLeft': + case 'Left': + stepKeyboard(this, -this.keyboardStepDown || -1); + e.preventDefault(); + e.stopPropagation(); + break; + case 'ArrowRight': + case 'Right': + stepKeyboard(this, this.keyboardStepUp || 1); + e.preventDefault(); + e.stopPropagation(); + break; + } + } + + /** + * Enable keyboard dragging. + */ + EmbySliderPrototype.enableKeyboardDragging = function () { + if (!this.keyboardDraggingEnabled) { + this.addEventListener('keydown', onKeyDown); + this.keyboardDraggingEnabled = true; + } + } + + /** + * Set steps for keyboard input. + * + * @param {number} stepDown step to reduce + * @param {number} stepUp step to increase + */ + EmbySliderPrototype.setKeyboardSteps = function (stepDown, stepUp) { + this.keyboardStepDown = stepDown || stepUp || 1; + this.keyboardStepUp = stepUp || stepDown || 1; + } + function setRange(elem, startPercent, endPercent) { var style = elem.style; diff --git a/src/controllers/videoosd.js b/src/controllers/videoosd.js index b610886963..6232c1f2e0 100644 --- a/src/controllers/videoosd.js +++ b/src/controllers/videoosd.js @@ -770,6 +770,11 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med var isProgressClear = state.MediaSource && null == state.MediaSource.RunTimeTicks; nowPlayingPositionSlider.setIsClear(isProgressClear); + if (nowPlayingItem.RunTimeTicks) { + nowPlayingPositionSlider.setKeyboardSteps(userSettings.skipBackLength() * 1000000 / nowPlayingItem.RunTimeTicks, + userSettings.skipForwardLength() * 1000000 / nowPlayingItem.RunTimeTicks); + } + if (-1 === supportedCommands.indexOf("ToggleFullscreen") || player.isLocalPlayer && layoutManager.tv && playbackManager.isFullscreen(player)) { view.querySelector(".btnFullscreen").classList.add("hide"); } else { @@ -1070,10 +1075,21 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med } } + /** + * Keys used for keyboard navigation. + */ + var NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"]; + function onWindowKeyDown(e) { if (!currentVisibleMenu && 32 === e.keyCode) { playbackManager.playPause(currentPlayer); - return void showOsd(); + showOsd(); + return; + } + + if (layoutManager.tv && NavigationKeys.indexOf(e.key) != -1) { + showOsd(); + return; } switch (e.key) { @@ -1237,6 +1253,12 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med var transitionEndEventName = dom.whichTransitionEvent(); var headerElement = document.querySelector(".skinHeader"); var osdBottomElement = document.querySelector(".videoOsdBottom-maincontrols"); + + if (layoutManager.tv) { + nowPlayingPositionSlider.classList.add("focusable"); + nowPlayingPositionSlider.enableKeyboardDragging(); + } + view.addEventListener("viewbeforeshow", function (e) { headerElement.classList.add("osdHeader"); Emby.Page.setTransparency("full"); diff --git a/src/videoosd.html b/src/videoosd.html index 0f4be75916..34c73a3019 100644 --- a/src/videoosd.html +++ b/src/videoosd.html @@ -31,7 +31,7 @@
-