diff --git a/build/types/ui b/build/types/ui index dc9938345..d398ef1ad 100644 --- a/build/types/ui +++ b/build/types/ui @@ -7,7 +7,6 @@ +../../ui/externs/ui.js +../../ui/externs/watermark.js +../../ui/play_button.js -+../../ui/big_play_button.js +../../ui/airplay_button.js +../../ui/cast_button.js +../../ui/chapter_selection.js @@ -40,7 +39,6 @@ +../../ui/save_video_frame_button.js +../../ui/seek_bar.js +../../ui/settings_menu.js -+../../ui/small_play_button.js +../../ui/skip_ad_button.js +../../ui/skip_next_button.js +../../ui/skip_previous_button.js diff --git a/docs/tutorials/ui-customization.md b/docs/tutorials/ui-customization.md index 14e789366..24ac2d48c 100644 --- a/docs/tutorials/ui-customization.md +++ b/docs/tutorials/ui-customization.md @@ -84,6 +84,10 @@ The following elements can be added to the UI bar using this configuration value The button is visible only if the content has at least one text track. * captions-size: adds a button that controls the size of the captions. The button is visible only if the content has at least one text track. +* skip_next: adds a button to skip to next element in the queue. The button + is visible only if there is next. +* skip_previous: adds a button to skip to previous element in the queue. The button + is visible only if there is previous. [Document Picture-in-Picture API]: https://developer.chrome.com/docs/web-platform/document-picture-in-picture/ @@ -133,6 +137,31 @@ ui.configure(config); An important note: the 'overflow_menu' button needs to be part of the 'controlPanelElements' layout for the overflow menu to be available to the user. +The following elements can be added as big buttons using this configuration value: +* play_pause: adds a button that plays/pauses the video on click. +* mute: adds a button that mutes/unmutes the video on click. +* fullscreen: adds a button that toggles full screen mode on click. +* rewind: adds a button that rewinds the presentation on click; that is, it starts playing + the presentation backwards. +* fast_forward: adds a button that fast forwards the presentation on click; that is, it + starts playing the presentation at an increased speed +* picture_in_picture: adds a button that enables/disables picture-in-picture mode on browsers +* remote: adds a button that opens a Remote Playback dialog. The button is visible only if the + browser supports Remote Playback API. +* loop: adds a button that controls if the currently selected video is played in a loop. +* skip_next: adds a button to skip to next element in the queue. The button + is visible only if there is next. +* skip_previous: adds a button to skip to previous element in the queue. The button + is visible only if there is previous. + +Example: +```js +const config = { + 'bigButtons' : ['play_pause'] +} +ui.configure(config); +``` + #### Adding tooltips to control panel buttons Tooltips can be enabled to display the function of every button in the control panel. Where applicable, they will also contain the current selection in parenthesis. @@ -198,7 +227,7 @@ ui.configure(config); ``` The presence of the seek bar and the big play button in the center of the video element can be -customized with `addSeekBar` and `addBigPlayButton` booleans in the config. +customized with `addSeekBar` boolean and `bigButtons` in the config. UI layout can be reconfigured at any point after it's been created. Please note that custom layouts might need CSS adjustments to look good. diff --git a/docs/tutorials/upgrade.md b/docs/tutorials/upgrade.md index 2c0e30d37..b5594b739 100644 --- a/docs/tutorials/upgrade.md +++ b/docs/tutorials/upgrade.md @@ -131,6 +131,7 @@ application: - `doubleClickForFullscreen` enabled by default for mobile. - `preferDocumentPictureInPicture` has been renamed to `documentPictureInPicture.enabled`. - `customContextMenu` enabled by default for desktop browsers. + - `addBigPlayButton` has been removed. Similar feature on `bigButtons` config. - Plugin changes: - `TextDisplayer` plugins must implement the `configure()` method. diff --git a/shaka-player.uncompiled.js b/shaka-player.uncompiled.js index 5c199d225..fe39aea84 100644 --- a/shaka-player.uncompiled.js +++ b/shaka-player.uncompiled.js @@ -96,7 +96,6 @@ goog.require('shaka.ui.AudioLanguageSelection'); goog.require('shaka.ui.AdInfo'); goog.require('shaka.ui.AdStatisticsButton'); goog.require('shaka.ui.AirPlayButton'); -goog.require('shaka.ui.BigPlayButton'); goog.require('shaka.ui.CastButton'); goog.require('shaka.ui.ChapterSelection'); goog.require('shaka.ui.ContentTitle'); @@ -113,6 +112,7 @@ goog.require('shaka.ui.MatrixQuaternion'); goog.require('shaka.ui.MuteButton'); goog.require('shaka.ui.Overlay'); goog.require('shaka.ui.PipButton'); +goog.require('shaka.ui.PlayButton'); goog.require('shaka.ui.PlaybackRateSelection'); goog.require('shaka.ui.PresentationTimeTracker'); goog.require('shaka.ui.RecenterVRButton'); @@ -123,7 +123,6 @@ goog.require('shaka.ui.SaveVideoFrameButton'); goog.require('shaka.ui.SkipAdButton'); goog.require('shaka.ui.SkipNextButton'); goog.require('shaka.ui.SkipPreviousButton'); -goog.require('shaka.ui.SmallPlayButton'); goog.require('shaka.ui.Spacer'); goog.require('shaka.ui.StatisticsButton'); goog.require('shaka.ui.TextPosition'); diff --git a/test/ui/ui_customization_unit.js b/test/ui/ui_customization_unit.js index f47e213d0..60332c8af 100644 --- a/test/ui/ui_customization_unit.js +++ b/test/ui/ui_customization_unit.js @@ -77,17 +77,19 @@ describe('UI Customization', () => { UiUtils.confirmElementFound(container, 'shaka-seek-bar'); }); - it('big play button only created when configured', async () => { + it('big buttons only created when configured', async () => { + const config = { + bigButtons: [], + }; const ui = await UiUtils.createUIThroughAPI( - container, video, {addBigPlayButton: false}, canvas); - UiUtils.confirmElementMissing(container, 'shaka-play-button-container'); - UiUtils.confirmElementMissing(container, 'shaka-play-button'); + container, video, config, canvas); + UiUtils.confirmElementMissing(container, 'shaka-big-buttons-container'); await ui.destroy(); + config.bigButtons.push('play_pause'); await UiUtils.createUIThroughAPI( - container, video, {addBigPlayButton: true}, canvas); - UiUtils.confirmElementFound(container, 'shaka-play-button-container'); - UiUtils.confirmElementFound(container, 'shaka-play-button'); + container, video, config, canvas); + UiUtils.confirmElementFound(container, 'shaka-big-buttons-container'); }); it('controls are created in specified order', async () => { diff --git a/test/ui/ui_unit.js b/test/ui/ui_unit.js index 5bbcce6e7..d7f6663ea 100644 --- a/test/ui/ui_unit.js +++ b/test/ui/ui_unit.js @@ -418,14 +418,12 @@ describe('UI', () => { // Overflow button UiUtils.confirmElementFound(controlsButtonPanel, 'shaka-overflow-menu-button'); - // Big play button + // Big buttons UiUtils.confirmElementFound(videoContainer, - 'shaka-play-button-container'); - UiUtils.confirmElementFound(videoContainer, - 'shaka-play-button'); + 'shaka-big-buttons-container'); // Small play button UiUtils.confirmElementMissing(videoContainer, - 'shaka-small-play-button'); + 'shaka-play-button'); // Volume bar UiUtils.confirmElementMissing(controlsButtonPanel, 'shaka-volume-bar'); @@ -439,14 +437,12 @@ describe('UI', () => { // Overflow button UiUtils.confirmElementMissing(controlsButtonPanel, 'shaka-overflow-menu-button'); - // Big play button + // Big buttons UiUtils.confirmElementMissing(videoContainer, - 'shaka-play-button-container'); - UiUtils.confirmElementMissing(videoContainer, - 'shaka-play-button'); + 'shaka-big-buttons-container'); // Small play button UiUtils.confirmElementFound(videoContainer, - 'shaka-small-play-button'); + 'shaka-play-button'); // Volume bar UiUtils.confirmElementMissing(controlsButtonPanel, 'shaka-volume-bar'); @@ -460,14 +456,12 @@ describe('UI', () => { // Overflow button UiUtils.confirmElementFound(controlsButtonPanel, 'shaka-overflow-menu-button'); - // Big play button + // Big buttons UiUtils.confirmElementMissing(videoContainer, - 'shaka-play-button-container'); - UiUtils.confirmElementMissing(videoContainer, - 'shaka-play-button'); + 'shaka-big-buttons-container'); // Small play button UiUtils.confirmElementFound(videoContainer, - 'shaka-small-play-button'); + 'shaka-play-button'); // Volume bar UiUtils.confirmElementFound(controlsButtonPanel, 'shaka-volume-bar'); diff --git a/ui/big_play_button.js b/ui/big_play_button.js deleted file mode 100644 index bddd03ae0..000000000 --- a/ui/big_play_button.js +++ /dev/null @@ -1,30 +0,0 @@ -/*! @license - * Shaka Player - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - - -goog.provide('shaka.ui.BigPlayButton'); - -goog.require('shaka.ui.PlayButton'); -goog.requireType('shaka.ui.Controls'); - - -/** - * @extends {shaka.ui.PlayButton} - * @final - * @export - */ -shaka.ui.BigPlayButton = class extends shaka.ui.PlayButton { - /** - * @param {!HTMLElement} parent - * @param {!shaka.ui.Controls} controls - */ - constructor(parent, controls) { - super(parent, controls); - - this.button.classList.add('shaka-play-button'); - this.button.classList.add('shaka-no-propagation'); - } -}; diff --git a/ui/controls.js b/ui/controls.js index cb814bf18..bc6529f25 100644 --- a/ui/controls.js +++ b/ui/controls.js @@ -15,7 +15,6 @@ goog.require('shaka.device.DeviceFactory'); goog.require('shaka.device.IDevice'); goog.require('shaka.log'); goog.require('shaka.ui.AdInfo'); -goog.require('shaka.ui.BigPlayButton'); goog.require('shaka.ui.ContextMenu'); goog.require('shaka.ui.HiddenFastForwardButton'); goog.require('shaka.ui.HiddenRewindButton'); @@ -510,6 +509,15 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { shaka.ui.ControlsPanel.elementNamesToFactories_.set(name, factory); } + /** + * @param {string} name + * @param {!shaka.extern.IUIElement.Factory} factory + * @export + */ + static registerBigElement(name, factory) { + shaka.ui.ControlsPanel.bigElementNamesToFactories_.set(name, factory); + } + /** * @param {!shaka.extern.IUISeekBar.Factory} factory * @export @@ -556,10 +564,6 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { this.seekBar_ = null; } - if (this.playButton_) { - this.playButton_ = null; - } - if (this.contextMenu_) { this.contextMenu_ = null; } @@ -1245,8 +1249,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { this.addScrimContainer_(); - if (this.config_.addBigPlayButton) { - this.addPlayButton_(); + if (this.config_.bigButtons.length) { + this.addBigButtons_(); } if (!this.spinnerContainer_) { @@ -1305,15 +1309,24 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { } /** @private */ - addPlayButton_() { - const playButtonContainer = shaka.util.Dom.createHTMLElement('div'); - playButtonContainer.classList.add('shaka-play-button-container'); - this.controlsContainer_.appendChild(playButtonContainer); + addBigButtons_() { + const bigButtonsContainer = shaka.util.Dom.createHTMLElement('div'); + bigButtonsContainer.classList.add('shaka-big-buttons-container'); + this.controlsContainer_.appendChild(bigButtonsContainer); - /** @private {shaka.ui.BigPlayButton} */ - this.playButton_ = - new shaka.ui.BigPlayButton(playButtonContainer, this); - this.elements_.push(this.playButton_); + const elementNamesToFactories = + shaka.ui.ControlsPanel.bigElementNamesToFactories_; + + for (const name of this.config_.bigButtons) { + if (elementNamesToFactories.has(name)) { + const factory = elementNamesToFactories.get(name); + const element = factory.create(bigButtonsContainer, this); + this.elements_.push(element); + } else { + shaka.log.alwaysWarn( + 'Unrecognized big button element requested:', name); + } + } } /** @private */ @@ -1431,7 +1444,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { // Create the elements specified by topControlPanelElements for (const name of this.config_.topControlPanelElements) { - if (shaka.ui.ControlsPanel.elementNamesToFactories_.get(name)) { + if (shaka.ui.ControlsPanel.elementNamesToFactories_.has(name)) { const factory = shaka.ui.ControlsPanel.elementNamesToFactories_.get(name); const element = factory.create(this.topControlsButtonPanel_, this); @@ -1481,7 +1494,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { // Create the elements specified by controlPanelElements for (const name of this.config_.controlPanelElements) { - if (shaka.ui.ControlsPanel.elementNamesToFactories_.get(name)) { + if (shaka.ui.ControlsPanel.elementNamesToFactories_.has(name)) { const factory = shaka.ui.ControlsPanel.elementNamesToFactories_.get(name); const element = factory.create(this.controlsButtonPanel_, this); @@ -2774,5 +2787,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { /** @private {!Map} */ shaka.ui.ControlsPanel.elementNamesToFactories_ = new Map(); +/** @private {!Map} */ +shaka.ui.ControlsPanel.bigElementNamesToFactories_ = new Map(); + /** @private {?shaka.extern.IUISeekBar.Factory} */ shaka.ui.ControlsPanel.seekBarFactory_ = new shaka.ui.SeekBar.Factory(); diff --git a/ui/externs/ui.js b/ui/externs/ui.js index 27ac89746..fb8d34840 100644 --- a/ui/externs/ui.js +++ b/ui/externs/ui.js @@ -254,6 +254,7 @@ shaka.extern.UIDocumentPictureInPicture; * @typedef {{ * controlPanelElements: !Array, * topControlPanelElements: !Array, + * bigButtons: !Array, * overflowMenuButtons: !Array, * contextMenuElements: !Array, * statisticsList: !Array, @@ -262,7 +263,6 @@ shaka.extern.UIDocumentPictureInPicture; * fastForwardRates: !Array, * rewindRates: !Array, * addSeekBar: boolean, - * addBigPlayButton: boolean, * customContextMenu: boolean, * castReceiverAppId: string, * castAndroidReceiverCompatible: boolean, @@ -313,6 +313,8 @@ shaka.extern.UIDocumentPictureInPicture; * The ordered list of control panel elements of the UI. * @property {!Array} topControlPanelElements * The ordered list of top control panel elements of the UI. + * @property {!Array} bigButtons + * The ordered list of big buttons elements of the UI. * @property {!Array} overflowMenuButtons * The ordered list of the overflow menu buttons. * @property {!Array} contextMenuElements @@ -337,12 +339,6 @@ shaka.extern.UIDocumentPictureInPicture; * Whether or not a seek bar should be part of the UI. *
* Defaults to true. - * @property {boolean} addBigPlayButton - * Whether or not a big play button in the center of the video - * should be part of the UI. - *
- * Defaults to false except on mobile where the default value - * is true * @property {boolean} customContextMenu * Whether or not a custom context menu replaces the default. *
@@ -853,30 +849,6 @@ shaka.extern.IUISeekBar.Factory = class { create(rootElement, controls) {} }; -/** - * @interface - * @exportDoc - */ -shaka.extern.IUIPlayButton = class { - /** - * @param {!HTMLElement} parent - * @param {!shaka.ui.Controls} controls - */ - constructor(parent, controls) { - /** - * @protected {!HTMLButtonElement} - * @exportDoc - */ - this.button; - } - - /** @return {boolean} */ - isPaused() {} - - /** @return {boolean} */ - isEnded() {} -}; - /** * @typedef {{ * path: ?(string | Array), diff --git a/ui/fast_forward_button.js b/ui/fast_forward_button.js index e714ab37f..ee7999b9c 100644 --- a/ui/fast_forward_button.js +++ b/ui/fast_forward_button.js @@ -33,6 +33,7 @@ shaka.ui.FastForwardButton = class extends shaka.ui.Element { this.button_ = shaka.util.Dom.createButton(); this.button_.classList.add('shaka-fast-forward-button'); this.button_.classList.add('shaka-tooltip-status'); + this.button_.classList.add('shaka-no-propagation'); this.button_.setAttribute('shaka-status', '1x'); new shaka.ui.Icon(this.button_).use( @@ -118,3 +119,6 @@ shaka.ui.FastForwardButton.Factory = class { shaka.ui.Controls.registerElement( 'fast_forward', new shaka.ui.FastForwardButton.Factory()); + +shaka.ui.Controls.registerBigElement( + 'fast_forward', new shaka.ui.FastForwardButton.Factory()); diff --git a/ui/fullscreen_button.js b/ui/fullscreen_button.js index f08f6ac39..929266e96 100644 --- a/ui/fullscreen_button.js +++ b/ui/fullscreen_button.js @@ -37,6 +37,7 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element { this.button_ = shaka.util.Dom.createButton(); this.button_.classList.add('shaka-fullscreen-button'); this.button_.classList.add('shaka-tooltip'); + this.button_.classList.add('shaka-no-propagation'); /** @private {shaka.ui.Icon} */ this.icon_ = new shaka.ui.Icon(this.button_, @@ -135,3 +136,5 @@ shaka.ui.FullscreenButton.Factory = class { shaka.ui.Controls.registerElement( 'fullscreen', new shaka.ui.FullscreenButton.Factory()); +shaka.ui.Controls.registerBigElement( + 'fullscreen', new shaka.ui.FullscreenButton.Factory()); diff --git a/ui/less/buttons.less b/ui/less/buttons.less index 3a6142936..9e7a668d7 100644 --- a/ui/less/buttons.less +++ b/ui/less/buttons.less @@ -6,8 +6,6 @@ /* The main buttons in the UI controls. */ -@play-button-size-percentage: 15%; - .disabled-button() { /* Set the background and the color, otherwise it might be overwritten by * the css styles in demo. */ @@ -16,45 +14,6 @@ cursor: default; } -/* The giant play button, which sits inside .shaka-player-button-container. */ -.shaka-play-button { - width: @play-button-size-percentage; - aspect-ratio: 1 / 1; - border-radius: 50%; - border: none; - margin: 0; - padding: 0; - - .center-children(); - - color: @general-font-color; - background-color: @general-background-color; - box-shadow: 0 8px 24px rgba(0, 0, 0, 35%); - cursor: pointer; - - transition: transform 0.18s ease-out, background-color 0.18s ease-out; - - .shaka-ui-icon { - width: 75%; - height: 75%; - } - - &:hover { - transform: scale(1.06); - background-color: @general-background-color-hover; - } - - &:active { - transform: scale(0.96); - } - - @media (prefers-reduced-motion: reduce) { - transition: none; - } - - .show-when-controls-shown(); -} - /* This button contains the current time and duration of the video. * It's only clickable when the content is live, and current time is behind live * edge. Otherwise, the button is disabled. diff --git a/ui/less/containers.less b/ui/less/containers.less index 8017e8888..f9bf7df99 100644 --- a/ui/less/containers.less +++ b/ui/less/containers.less @@ -82,9 +82,9 @@ * the size of the container for some applications. */ } -/* A container for all controls, including the giant play button, seek bar, etc. +/* A container for all controls, including the giant buttons, seek bar, etc. * Sits inside .shaka-video-container, on top of (Z axis) .shaka-video, and - * below (Y axis) .shaka-play-button-container. */ + * below (Y axis) .shaka-big-buttons-container. */ .shaka-controls-container { .overlay-child(); @@ -123,7 +123,7 @@ /* A container for all canvas for LCEVC decoding * Sits inside .shaka-video-container, on top of (Z axis) .shaka-video, and - * below (Y axis) .shaka-play-button-container. */ + * below (Y axis) .shaka-big-buttons-container. */ .shaka-canvas-container { .overlay-child(); @@ -134,7 +134,7 @@ /* A container for VR * Sits inside .shaka-video-container, on top of (Z axis) .shaka-video, and - * below (Y axis) .shaka-play-button-container. */ + * below (Y axis) .shaka-big-buttons-container. */ .shaka-vr-canvas-container { .overlay-child(); @@ -174,8 +174,75 @@ z-index: 1; } +.shaka-context-menu { + button { + .shaka-current-selection-span { + display: none; + } + } +} + +/* The container for the giant buttons. Sits above (Y axis) the + * other video controls and seek bar, in the middle of the video frame, on top + * of (Z axis) the video. */ +.shaka-big-buttons-container { + /* Take up as much space as possible, but shrink (vertically) to accomodate + * the controls at the bottom. */ + margin: 0; + .fill-container(); + .shrinkable(); + .absolute-position(); + + /* Keep the play button in the middle of this container. */ + .center-children(); + + gap: 2.5%; + + z-index: 1; + + @big-button-size-percentage: 15%; + + .shaka-ui-icon { + width: 75%; + height: 75%; + } + + button { + width: @big-button-size-percentage; + aspect-ratio: 1 / 1; + border-radius: 50%; + border: none; + margin: 0; + padding: 0; + + .center-children(); + + color: @general-font-color; + background-color: @general-background-color; + box-shadow: 0 8px 24px rgba(0, 0, 0, 35%); + cursor: pointer; + + transition: transform 0.18s ease-out, background-color 0.18s ease-out; + + &:hover { + transform: scale(1.06); + background-color: @general-background-color-hover; + } + + &:active { + transform: scale(0.96); + } + + @media (prefers-reduced-motion: reduce) { + transition: none; + } + + .show-when-controls-shown(); + } +} + /* This is the container for the horizontal row of controls above the seek bar. - * It sits above (Y axis) the seek bar, and below (Y axis) the giant play button + * It sits above (Y axis) the seek bar, and below (Y axis) the giant buttons * in the middle. */ .shaka-controls-button-panel, .shaka-controls-top-button-panel { @@ -235,7 +302,7 @@ /* Use a bit larger icons for some buttons */ &.shaka-fast-forward-button, &.shaka-rewind-button, - &.shaka-small-play-button, + &.shaka-play-button, &.shaka-skip-previous-button, &.shaka-skip-next-button { .shaka-ui-icon { @@ -265,29 +332,11 @@ /* Buttons hide certain items if they are found inside the control panel */ .shaka-controls-button-panel .shaka-overflow-menu-only, -.shaka-controls-top-button-panel .shaka-overflow-menu-only { +.shaka-controls-top-button-panel .shaka-overflow-menu-only, +.shaka-big-buttons-container .shaka-overflow-menu-only { display: none; } -/* The container for the giant play button. Sits above (Y axis) the - * other video controls and seek bar, in the middle of the video frame, on top - * of (Z axis) the video. */ -.shaka-play-button-container { - /* Take up as much space as possible, but shrink (vertically) to accomodate - * the controls at the bottom. */ - margin: 0; - .fill-container(); - .shrinkable(); - .absolute-position(); - - /* Keep the play button in the middle of this container. */ - display: flex; - justify-content: center; - align-items: center; - - z-index: 1; -} - .shaka-statistics-container { overflow-x: hidden; overflow-y: auto; @@ -362,14 +411,6 @@ } } -.shaka-context-menu { - button { - .shaka-current-selection-span { - display: none; - } - } -} - .shaka-scrim-container { margin: 0; width: 100%; diff --git a/ui/loop_button.js b/ui/loop_button.js index f5b502c1d..111e5cda5 100644 --- a/ui/loop_button.js +++ b/ui/loop_button.js @@ -38,6 +38,7 @@ shaka.ui.LoopButton = class extends shaka.ui.Element { this.button_ = shaka.util.Dom.createButton(); this.button_.classList.add('shaka-loop-button'); this.button_.classList.add('shaka-tooltip'); + this.button_.classList.add('shaka-no-propagation'); /** @private {!shaka.ui.Icon} */ this.icon_ = new shaka.ui.Icon(this.button_, @@ -210,3 +211,6 @@ shaka.ui.OverflowMenu.registerElement( shaka.ui.Controls.registerElement( 'loop', new shaka.ui.LoopButton.Factory()); + +shaka.ui.Controls.registerBigElement( + 'loop', new shaka.ui.LoopButton.Factory()); diff --git a/ui/mute_button.js b/ui/mute_button.js index 7826fa37b..f8ad895a1 100644 --- a/ui/mute_button.js +++ b/ui/mute_button.js @@ -37,6 +37,7 @@ shaka.ui.MuteButton = class extends shaka.ui.Element { this.button_ = shaka.util.Dom.createButton(); this.button_.classList.add('shaka-mute-button'); this.button_.classList.add('shaka-tooltip'); + this.button_.classList.add('shaka-no-propagation'); /** @private {!shaka.ui.Icon} */ this.icon_ = new shaka.ui.Icon(this.button_, @@ -206,3 +207,6 @@ shaka.ui.OverflowMenu.registerElement( shaka.ui.Controls.registerElement( 'mute', new shaka.ui.MuteButton.Factory()); + +shaka.ui.Controls.registerBigElement( + 'mute', new shaka.ui.MuteButton.Factory()); diff --git a/ui/pip_button.js b/ui/pip_button.js index 03811d30d..273984678 100644 --- a/ui/pip_button.js +++ b/ui/pip_button.js @@ -43,6 +43,7 @@ shaka.ui.PipButton = class extends shaka.ui.Element { this.pipButton_ = shaka.util.Dom.createButton(); this.pipButton_.classList.add('shaka-pip-button'); this.pipButton_.classList.add('shaka-tooltip'); + this.pipButton_.classList.add('shaka-no-propagation'); /** @private {!shaka.ui.Icon} */ this.pipIcon_ = new shaka.ui.Icon(this.pipButton_, @@ -219,3 +220,6 @@ shaka.ui.OverflowMenu.registerElement( shaka.ui.Controls.registerElement( 'picture_in_picture', new shaka.ui.PipButton.Factory()); + +shaka.ui.Controls.registerBigElement( + 'picture_in_picture', new shaka.ui.PipButton.Factory()); diff --git a/ui/play_button.js b/ui/play_button.js index 89bf2d648..7cd79b791 100644 --- a/ui/play_button.js +++ b/ui/play_button.js @@ -8,18 +8,18 @@ goog.provide('shaka.ui.PlayButton'); goog.require('shaka.ads.Utils'); +goog.require('shaka.ui.Controls'); goog.require('shaka.ui.Element'); goog.require('shaka.ui.Enums'); goog.require('shaka.ui.Icon'); goog.require('shaka.ui.Locales'); goog.require('shaka.ui.Localization'); goog.require('shaka.util.Dom'); -goog.requireType('shaka.ui.Controls'); /** * @extends {shaka.ui.Element} - * @implements {shaka.extern.IUIPlayButton} + * @final * @export */ shaka.ui.PlayButton = class extends shaka.ui.Element { @@ -31,79 +31,81 @@ shaka.ui.PlayButton = class extends shaka.ui.Element { super(parent, controls); /** @protected {!HTMLButtonElement} */ - this.button = shaka.util.Dom.createButton(); - this.parent.appendChild(this.button); + this.button_ = shaka.util.Dom.createButton(); + this.button_.classList.add('shaka-play-button'); + this.button_.classList.add('shaka-tooltip'); + this.button_.classList.add('shaka-no-propagation'); + this.parent.appendChild(this.button_); /** @private {!shaka.ui.Icon} */ - this.icon_ = new shaka.ui.Icon(this.button); + this.icon_ = new shaka.ui.Icon(this.button_); const LOCALE_UPDATED = shaka.ui.Localization.LOCALE_UPDATED; this.eventManager.listen(this.localization, LOCALE_UPDATED, () => { - this.updateAriaLabel(); + this.updateAriaLabel_(); }); const LOCALE_CHANGED = shaka.ui.Localization.LOCALE_CHANGED; this.eventManager.listen(this.localization, LOCALE_CHANGED, () => { - this.updateAriaLabel(); + this.updateAriaLabel_(); }); this.eventManager.listen(this.video, 'play', () => { - this.updateAriaLabel(); - this.updateIcon(); + this.updateAriaLabel_(); + this.updateIcon_(); }); this.eventManager.listen(this.video, 'pause', () => { - this.updateAriaLabel(); - this.updateIcon(); + this.updateAriaLabel_(); + this.updateIcon_(); }); this.eventManager.listen(this.video, 'seeking', () => { - this.updateAriaLabel(); - this.updateIcon(); + this.updateAriaLabel_(); + this.updateIcon_(); }); this.eventManager.listen(this.player, 'loaded', () => { - this.updateAriaLabel(); - this.updateIcon(); + this.updateAriaLabel_(); + this.updateIcon_(); }); this.eventManager.listen(this.adManager, shaka.ads.Utils.AD_PAUSED, () => { - this.updateAriaLabel(); - this.updateIcon(); + this.updateAriaLabel_(); + this.updateIcon_(); }); this.eventManager.listen(this.adManager, shaka.ads.Utils.AD_RESUMED, () => { - this.updateAriaLabel(); - this.updateIcon(); + this.updateAriaLabel_(); + this.updateIcon_(); }); this.eventManager.listen(this.adManager, shaka.ads.Utils.AD_STARTED, () => { - this.updateAriaLabel(); - this.updateIcon(); + this.updateAriaLabel_(); + this.updateIcon_(); }); this.eventManager.listen(this.adManager, shaka.ads.Utils.AD_STOPPED, () => { - this.updateAriaLabel(); - this.updateIcon(); + this.updateAriaLabel_(); + this.updateIcon_(); }); - this.eventManager.listen(this.button, 'click', () => { + this.eventManager.listen(this.button_, 'click', () => { if (!this.controls.isOpaque()) { return; } this.controls.playPausePresentation(); }); - this.updateAriaLabel(); - this.updateIcon(); + this.updateAriaLabel_(); + this.updateIcon_(); } /** * @return {boolean} - * @protected - * @override + * @private */ - isPaused() { + isPaused_() { if (this.ad && this.ad.isLinear()) { return this.ad.isPaused(); } @@ -113,10 +115,9 @@ shaka.ui.PlayButton = class extends shaka.ui.Element { /** * @return {boolean} - * @protected - * @override + * @private */ - isEnded() { + isEnded_() { if (this.ad && this.ad.isLinear()) { return false; } @@ -125,29 +126,46 @@ shaka.ui.PlayButton = class extends shaka.ui.Element { } /** - * Called when the button's aria label needs to change. - * To be overridden by subclasses, if necessary + * @private */ - updateAriaLabel() { + updateAriaLabel_() { const LocIds = shaka.ui.Locales.Ids; - if (this.isEnded() && this.video.duration) { - this.button.ariaLabel = this.localization.resolve(LocIds.REPLAY); + if (this.isEnded_() && this.video.duration) { + this.button_.ariaLabel = this.localization.resolve(LocIds.REPLAY); } else { - const label = this.isPaused() ? LocIds.PLAY : LocIds.PAUSE; - this.button.ariaLabel = this.localization.resolve(label); + const label = this.isPaused_() ? LocIds.PLAY : LocIds.PAUSE; + this.button_.ariaLabel = this.localization.resolve(label); } } + /** - * Called when the button's icon needs to change. - * To be overridden by subclasses. + * @private */ - updateIcon() { + updateIcon_() { const Icons = shaka.ui.Enums.MaterialDesignSVGIcons; - if (this.isEnded() && this.video.duration) { + if (this.isEnded_() && this.video.duration) { this.icon_.use(Icons['REPLAY']); } else { - this.icon_.use(this.isPaused() ? Icons['PLAY'] : Icons['PAUSE']); + this.icon_.use(this.isPaused_() ? Icons['PLAY'] : Icons['PAUSE']); } } }; + + +/** + * @implements {shaka.extern.IUIElement.Factory} + * @final + */ +shaka.ui.PlayButton.Factory = class { + /** @override */ + create(rootElement, controls) { + return new shaka.ui.PlayButton(rootElement, controls); + } +}; + +shaka.ui.Controls.registerElement( + 'play_pause', new shaka.ui.PlayButton.Factory()); + +shaka.ui.Controls.registerBigElement( + 'play_pause', new shaka.ui.PlayButton.Factory()); diff --git a/ui/remote_button.js b/ui/remote_button.js index b8338384a..d70043608 100644 --- a/ui/remote_button.js +++ b/ui/remote_button.js @@ -43,6 +43,7 @@ shaka.ui.RemoteButton = class extends shaka.ui.Element { this.remoteButton_ = shaka.util.Dom.createButton(); this.remoteButton_.classList.add('shaka-remote-button'); this.remoteButton_.classList.add('shaka-tooltip'); + this.remoteButton_.classList.add('shaka-no-propagation'); this.remoteButton_.ariaPressed = 'false'; /** @private {!shaka.ui.Icon} */ @@ -245,3 +246,6 @@ shaka.ui.OverflowMenu.registerElement( shaka.ui.Controls.registerElement( 'remote', new shaka.ui.RemoteButton.Factory()); + +shaka.ui.Controls.registerBigElement( + 'remote', new shaka.ui.RemoteButton.Factory()); diff --git a/ui/rewind_button.js b/ui/rewind_button.js index acebbaf2f..bc97d79f4 100644 --- a/ui/rewind_button.js +++ b/ui/rewind_button.js @@ -33,6 +33,7 @@ shaka.ui.RewindButton = class extends shaka.ui.Element { this.button_ = shaka.util.Dom.createButton(); this.button_.classList.add('shaka-rewind-button'); this.button_.classList.add('shaka-tooltip-status'); + this.button_.classList.add('shaka-no-propagation'); this.button_.setAttribute('shaka-status', this.localization.resolve(shaka.ui.Locales.Ids.OFF)); @@ -120,3 +121,5 @@ shaka.ui.RewindButton.Factory = class { shaka.ui.Controls.registerElement( 'rewind', new shaka.ui.RewindButton.Factory()); +shaka.ui.Controls.registerBigElement( + 'rewind', new shaka.ui.RewindButton.Factory()); diff --git a/ui/skip_next_button.js b/ui/skip_next_button.js index ad3093a24..9e2019483 100644 --- a/ui/skip_next_button.js +++ b/ui/skip_next_button.js @@ -40,6 +40,7 @@ shaka.ui.SkipNextButton = class extends shaka.ui.Element { this.button_ = shaka.util.Dom.createButton(); this.button_.classList.add('shaka-skip-next-button'); this.button_.classList.add('shaka-tooltip'); + this.button_.classList.add('shaka-no-propagation'); new shaka.ui.Icon(this.button_).use( shaka.ui.Enums.MaterialDesignSVGIcons['SKIP_NEXT']); this.parent.appendChild(this.button_); @@ -112,3 +113,6 @@ shaka.ui.SkipNextButton.Factory = class { shaka.ui.Controls.registerElement( 'skip_next', new shaka.ui.SkipNextButton.Factory()); + +shaka.ui.Controls.registerBigElement( + 'skip_next', new shaka.ui.SkipNextButton.Factory()); diff --git a/ui/skip_previous_button.js b/ui/skip_previous_button.js index 0d76327f9..244037896 100644 --- a/ui/skip_previous_button.js +++ b/ui/skip_previous_button.js @@ -40,6 +40,7 @@ shaka.ui.SkipPreviousButton = class extends shaka.ui.Element { this.button_ = shaka.util.Dom.createButton(); this.button_.classList.add('shaka-skip-previous-button'); this.button_.classList.add('shaka-tooltip'); + this.button_.classList.add('shaka-no-propagation'); new shaka.ui.Icon(this.button_).use( shaka.ui.Enums.MaterialDesignSVGIcons['SKIP_PREVIOUS']); this.parent.appendChild(this.button_); @@ -111,3 +112,6 @@ shaka.ui.SkipPreviousButton.Factory = class { shaka.ui.Controls.registerElement( 'skip_previous', new shaka.ui.SkipPreviousButton.Factory()); + +shaka.ui.Controls.registerBigElement( + 'skip_previous', new shaka.ui.SkipPreviousButton.Factory()); diff --git a/ui/small_play_button.js b/ui/small_play_button.js deleted file mode 100644 index 769de6cb1..000000000 --- a/ui/small_play_button.js +++ /dev/null @@ -1,45 +0,0 @@ -/*! @license - * Shaka Player - * Copyright 2016 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - - -goog.provide('shaka.ui.SmallPlayButton'); - -goog.require('shaka.ui.Controls'); -goog.require('shaka.ui.PlayButton'); - - -/** - * @extends {shaka.ui.PlayButton} - * @final - * @export - */ -shaka.ui.SmallPlayButton = class extends shaka.ui.PlayButton { - /** - * @param {!HTMLElement} parent - * @param {!shaka.ui.Controls} controls - */ - constructor(parent, controls) { - super(parent, controls); - - this.button.classList.add('shaka-small-play-button'); - this.button.classList.add('shaka-tooltip'); - } -}; - - -/** - * @implements {shaka.extern.IUIElement.Factory} - * @final - */ -shaka.ui.SmallPlayButton.Factory = class { - /** @override */ - create(rootElement, controls) { - return new shaka.ui.SmallPlayButton(rootElement, controls); - } -}; - -shaka.ui.Controls.registerElement( - 'play_pause', new shaka.ui.SmallPlayButton.Factory()); diff --git a/ui/ui.js b/ui/ui.js index e7ad525c8..a3da81ac8 100644 --- a/ui/ui.js +++ b/ui/ui.js @@ -308,6 +308,7 @@ shaka.ui.Overlay = class { 'content_title', 'spacer', ], + bigButtons: [], overflowMenuButtons: [ 'captions', 'captions-position', @@ -366,7 +367,6 @@ shaka.ui.Overlay = class { fastForwardRates: [2, 4, 8, 1], rewindRates: [-1, -2, -4, -8], addSeekBar: true, - addBigPlayButton: false, customContextMenu: true, castReceiverAppId: '', castAndroidReceiverCompatible: false, @@ -463,7 +463,11 @@ shaka.ui.Overlay = class { // button and show the big play/pause button in the center. // This is in line with default styles in Chrome. if (this.isMobile()) { - config.addBigPlayButton = true; + config.bigButtons = [ + 'skip_previous', + 'play_pause', + 'skip_next', + ]; config.customContextMenu = false; config.singleClickForPlayAndPause = false; config.seekOnTaps = true; @@ -496,7 +500,9 @@ shaka.ui.Overlay = class { 'spacer', ]; } else if (this.isSmartTV()) { - config.addBigPlayButton = true; + config.bigButtons = [ + 'play_pause', + ]; config.customContextMenu = false; config.singleClickForPlayAndPause = false; config.enableTooltips = false;