Skip to content

Commit

Permalink
UI: Dynamic layout construction.
Browse files Browse the repository at this point in the history
Fixes shaka-project#1674

Change-Id: I338917bbd43a6ddc837388fdc8beea9e3f7b0778
  • Loading branch information
ismena committed Apr 29, 2019
1 parent 87694d0 commit 7a15c1e
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 40 deletions.
25 changes: 25 additions & 0 deletions lib/util/dom_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ shaka.util.Dom = class {
}


/**
* Cast a Node/Element to an HTMLMediaElement
*
* @param {!Node|!Element} original
* @return {!HTMLMediaElement}
* @export
*/
static asHTMLMediaElement(original) {
return /** @type {!HTMLMediaElement}*/ (original);
}


/**
* Returns the element with a given class name.
* Assumes the class name to be unique for a given parent.
Expand All @@ -81,4 +93,17 @@ shaka.util.Dom = class {

return shaka.util.Dom.asHTMLElement(elements[0]);
}


/**
* Remove all of the child nodes of an element.
* @param {!Element} element
* @export
*/
static removeAllChildren(element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
}
};

4 changes: 2 additions & 2 deletions test/ui/ui_integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ describe('UI', () => {
// TODO: Cast receiver id to test chromecast integration
};

const ui =
new compiledShaka.ui.Overlay(player, videoContainer, video, config);
const ui = new compiledShaka.ui.Overlay(player, videoContainer, video);
ui.configure(config);

// Grab event manager from the uncompiled library:
eventManager = new shaka.util.EventManager();
Expand Down
8 changes: 2 additions & 6 deletions test/ui/ui_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -546,12 +546,8 @@ describe('UI', function() {
player = new shaka.Player(video);
// Create UI
config = config || {};
const ui = new shaka.ui.Overlay(player, videoContainer, video, config);

// The tests we have at the moment will pass without this, but compiler
// complained about not using the ui var, and I(ismena) didn't know
// any better.
ui.setEnabled(true);
const ui = new shaka.ui.Overlay(player, videoContainer, video);
ui.configure(config);
}

/**
Expand Down
2 changes: 2 additions & 0 deletions ui/audio_language_selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.Element {

// Set up all the strings in the user's preferred language.
this.updateLocalizedStrings_();

this.updateAudioLanguages_();
}


Expand Down
10 changes: 10 additions & 0 deletions ui/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,16 @@ shaka.ui.Controls.prototype.destroy = function() {
*/


/**
* @event shaka.Controls.UIUpdatedEvent
* @description Fired after a call to ui.configure() once the UI has finished
* updating.
* @property {string} type
* 'uiupdated'
* @exportDoc
*/


/**
* @param {string} name
* @param {!shaka.extern.IUIElement.Factory} factory
Expand Down
2 changes: 2 additions & 0 deletions ui/text_selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ shaka.ui.TextSelection = class extends shaka.ui.Element {

// Set up all the strings in the user's preferred language.
this.updateLocalizedStrings_();

this.updateTextLanguages_();
}


Expand Down
116 changes: 84 additions & 32 deletions ui/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,35 +28,22 @@ goog.require('shaka.ui.TextDisplayer');
* @param {!shaka.Player} player
* @param {!HTMLElement} videoContainer
* @param {!HTMLMediaElement} video
* @param {!Object=} config This should follow the form of
* {@link shaka.extern.UIConfiguration}, but you may omit
* any field you do not wish to change.
* @implements {shaka.util.IDestroyable}
* @constructor
* @export
*/
shaka.ui.Overlay = function(player, videoContainer, video, config) {
shaka.ui.Overlay = function(player, videoContainer, video) {
/** @private {shaka.Player} */
this.player_ = player;

/** @private {!shaka.extern.UIConfiguration} */
this.config_ = this.defaultConfig_();
/** @private {!HTMLElement} */
this.videoContainer_ = videoContainer;

if (config) {
shaka.util.ConfigUtils.mergeConfigObjects(
this.config_, config, this.defaultConfig_(),
/* overrides (only used for player config)*/ {}, /* path */ '');
}

// If a cast receiver app id has been given, add a cast button to the UI
if (this.config_.castReceiverAppId &&
!this.config_.overflowMenuButtons.includes('cast')) {
this.config_.overflowMenuButtons.push('cast');
}
/** @private {!HTMLMediaElement} */
this.video_ = video;

/** @private {shaka.ui.Controls} */
this.controls_ = new shaka.ui.Controls(
player, videoContainer, video, this.config_);
/** @private {!shaka.extern.UIConfiguration} */
this.config_ = this.defaultConfig_();

// Make sure this container is discoverable and that the UI can be reached
// through it.
Expand All @@ -78,6 +65,71 @@ shaka.ui.Overlay.prototype.destroy = async function() {
};


/**
* @param {!Object} config This should follow the form of
* {@link shaka.extern.UIConfiguration}, but you may omit
* any field you do not wish to change.
* @export
*/
shaka.ui.Overlay.prototype.configure = function(config) {
// TODO: accept flattened config "configure(addSeekBar, false);"
const DomUtils = shaka.util.Dom;
// Deconstruct the old layout.

// Save the text container, so subtitles can be displayed with
// the new layout.
const textContainer =
this.videoContainer_.querySelector('.shaka-text-container');

// Remember whether the controls were shown
let shown = false;
let controlsContainer =
this.videoContainer_.querySelector('.shaka-controls-container');
if (controlsContainer != null) {
shown = controlsContainer.getAttribute('shown');
}

// Destroy the old layout.
shaka.util.Dom.removeAllChildren(this.videoContainer_);

// Add the video back in
this.videoContainer_.appendChild(this.video_);

shaka.util.ConfigUtils.mergeConfigObjects(
this.config_, config, this.defaultConfig_(),
/* overrides (only used for player config)*/ {}, /* path */ '');

// If a cast receiver app id has been given, add a cast button to the UI
if (this.config_.castReceiverAppId &&
!this.config_.overflowMenuButtons.includes('cast')) {
this.config_.overflowMenuButtons.push('cast');
}

goog.asserts.assert(this.player_ != null, 'Should have a player!');

/** @private {shaka.ui.Controls} */
this.controls_ = new shaka.ui.Controls(
this.player_, this.videoContainer_, this.video_, this.config_);

controlsContainer = DomUtils.getElementByClassName(
'shaka-controls-container', this.videoContainer_);
controlsContainer.setAttribute('shown', shown);

// Init spinner with the right buffering state
const spinner = DomUtils.getElementByClassName(
'shaka-spinner-svg', controlsContainer);
const isBuffering = this.player_.isBuffering();
shaka.ui.Utils.setDisplay(spinner, isBuffering);

// Add the text container back.
if (textContainer) {
this.videoContainer_.appendChild(textContainer);
}

this.controls_.dispatchEvent(new shaka.util.FakeEvent('uiupdated'));
};


/**
* @return {shaka.Player}
* @export
Expand Down Expand Up @@ -188,13 +240,13 @@ shaka.ui.Overlay.scanPageForShakaElements_ = function() {
castAppId = video['dataset']['shakaPlayerCastReceiverId'];
}

const videoAsMediaElement = /** @type {!HTMLMediaElement} */ (video);
const ui = shaka.ui.Overlay.createUI_(
/** @type {!HTMLElement} */ (container),
videoAsMediaElement,
{castReceiverAppId: castAppId});
shaka.util.Dom.asHTMLElement(container),
shaka.util.Dom.asHTMLMediaElement(video));

if (videoAsMediaElement.controls) {
ui.configure({castReceiverAppId: castAppId});

if (shaka.util.Dom.asHTMLMediaElement(video).controls) {
ui.getControls().setEnabledNativeControls(true);
}
}
Expand Down Expand Up @@ -233,9 +285,11 @@ shaka.ui.Overlay.scanPageForShakaElements_ = function() {
if (video['dataset'] && video['dataset']['shakaPlayerCastReceiverId']) {
castAppId = video['dataset']['shakaPlayerCastReceiverId'];
}
shaka.ui.Overlay.createUI_(/** @type {!HTMLElement} */ (container),
/** @type {!HTMLMediaElement} */ (video),
{castReceiverAppId: castAppId});
const ui = shaka.ui.Overlay.createUI_(
shaka.util.Dom.asHTMLElement(container),
shaka.util.Dom.asHTMLMediaElement(video));

ui.configure({castReceiverAppId: castAppId});
}
}

Expand All @@ -260,14 +314,12 @@ shaka.ui.Overlay.dispatchLoadedEvent_ = function() {
/**
* @param {!HTMLElement} container
* @param {!HTMLMediaElement} video
* @param {!Object} config (Possibly partial) config in the form of
* {@link shaka.extern.UIConfiguration}
* @return {!shaka.ui.Overlay}
* @private
*/
shaka.ui.Overlay.createUI_ = function(container, video, config) {
shaka.ui.Overlay.createUI_ = function(container, video) {
const player = new shaka.Player(video);
const ui = new shaka.ui.Overlay(player, container, video, config);
const ui = new shaka.ui.Overlay(player, container, video);

// If the browser's native controls are disabled, use UI TextDisplayer. Right
// now because the factory must be a constructor and () => {} can't be a
Expand Down

0 comments on commit 7a15c1e

Please sign in to comment.