Skip to content

Commit

Permalink
feat(ui): Add tooltips to control panel buttons (shaka-project#3572)
Browse files Browse the repository at this point in the history
Adds configurable tooltips that display the function of buttons in the control panel.
Closes: shaka-project#3269
  • Loading branch information
nbcl committed Aug 23, 2021
1 parent f65b093 commit d5769ee
Show file tree
Hide file tree
Showing 20 changed files with 148 additions and 1 deletion.
12 changes: 12 additions & 0 deletions docs/tutorials/ui-customization.md
Expand Up @@ -102,6 +102,18 @@ 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.

#### 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.

Example:
```js
const config = {
'enableTooltips' : true
}
ui.configure(config);
```

#### Replacing the default context menu

A custom context menu can be added through the `customContextMenu` boolean. Additionally, the `contextMenuElements` option can be used to add elements to it. Currently only the statistics button is available:
Expand Down
1 change: 1 addition & 0 deletions ui/airplay_button.js
Expand Up @@ -36,6 +36,7 @@ shaka.ui.AirPlayButton = class extends shaka.ui.Element {
/** @private {!HTMLButtonElement} */
this.airplayButton_ = shaka.util.Dom.createButton();
this.airplayButton_.classList.add('shaka-airplay-button');
this.airplayButton_.classList.add('shaka-tooltip');
this.airplayButton_.ariaPressed = 'false';

/** @private {!HTMLElement} */
Expand Down
3 changes: 3 additions & 0 deletions ui/audio_language_selection.js
Expand Up @@ -32,6 +32,7 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.SettingsMenu {
super(parent, controls, shaka.ui.Enums.MaterialDesignIcons.LANGUAGE);

this.button.classList.add('shaka-language-button');
this.button.classList.add('shaka-tooltip-status');
this.menu.classList.add('shaka-audio-languages');

this.eventManager.listen(
Expand Down Expand Up @@ -72,6 +73,8 @@ shaka.ui.AudioLanguageSelection = class extends shaka.ui.SettingsMenu {

this.controls.dispatchEvent(
new shaka.util.FakeEvent('languageselectionupdated'));

this.button.setAttribute('shaka-status', this.currentSelection.innerText);
}

/** @private */
Expand Down
1 change: 1 addition & 0 deletions ui/cast_button.js
Expand Up @@ -41,6 +41,7 @@ shaka.ui.CastButton = class extends shaka.ui.Element {
/** @private {!HTMLButtonElement} */
this.castButton_ = shaka.util.Dom.createButton();
this.castButton_.classList.add('shaka-cast-button');
this.castButton_.classList.add('shaka-tooltip');
this.castButton_.ariaPressed = 'false';

/** @private {!HTMLElement} */
Expand Down
3 changes: 3 additions & 0 deletions ui/controls.js
Expand Up @@ -812,6 +812,9 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
this.controlsButtonPanel_.classList.add('shaka-controls-button-panel');
this.controlsButtonPanel_.classList.add(
'shaka-show-controls-on-mouse-over');
if (this.config_.enableTooltips) {
this.controlsButtonPanel_.classList.add('shaka-tooltips-on');
}
this.bottomControls_.appendChild(this.controlsButtonPanel_);

// Create the elements specified by controlPanelElements
Expand Down
1 change: 1 addition & 0 deletions ui/controls.less
Expand Up @@ -12,6 +12,7 @@
@import "less/other_elements.less";
@import "less/overflow_menu.less";
@import "less/ad_controls.less";
@import "less/tooltip.less";
@import (css, inline) "https://fonts.googleapis.com/css?family=Roboto";
// NOTE: Working around google/material-design-icons#958
@import (css, inline) "https://fonts.googleapis.com/icon?family=Material+Icons+Round";
7 changes: 6 additions & 1 deletion ui/externs/ui.js
Expand Up @@ -81,7 +81,8 @@ shaka.extern.UIVolumeBarColors;
* doubleClickForFullscreen: boolean,
* enableKeyboardPlaybackControls: boolean,
* enableFullscreenOnRotation: boolean,
* forceLandscapeOnFullscreen: boolean
* forceLandscapeOnFullscreen: boolean,
* enableTooltips: boolean
* }}
*
* @property {!Array.<string>} controlPanelElements
Expand Down Expand Up @@ -165,6 +166,10 @@ shaka.extern.UIVolumeBarColors;
* enters fullscreen. Note that this behavior is based on an experimental
* browser API, and may not work on all platforms.
* Defaults to true.
* @property {boolean} enableTooltips
* Whether or not buttons in the control panel display tooltips that contain
* information about their function.
* Defaults to false.
* @exportDoc
*/
shaka.extern.UIConfiguration;
Expand Down
4 changes: 4 additions & 0 deletions ui/fast_forward_button.js
Expand Up @@ -32,6 +32,8 @@ shaka.ui.FastForwardButton = class extends shaka.ui.Element {
this.button_ = shaka.util.Dom.createButton();
this.button_.classList.add('material-icons-round');
this.button_.classList.add('shaka-fast-forward-button');
this.button_.classList.add('shaka-tooltip-status');
this.button_.setAttribute('shaka-status', '1x');
this.button_.textContent =
shaka.ui.Enums.MaterialDesignIcons.FAST_FORWARD;
this.parent.appendChild(this.button_);
Expand Down Expand Up @@ -80,6 +82,8 @@ shaka.ui.FastForwardButton = class extends shaka.ui.Element {
const newRate = (newRateIndex != this.fastForwardRates_.length) ?
this.fastForwardRates_[newRateIndex] : this.fastForwardRates_[0];
this.player.trickPlay(newRate);

this.button_.setAttribute('shaka-status', newRate + 'x');
}
};

Expand Down
1 change: 1 addition & 0 deletions ui/fullscreen_button.js
Expand Up @@ -32,6 +32,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('material-icons-round');
this.button_.classList.add('shaka-tooltip');

// Don't show the button if fullscreen is not supported
if (!document.fullscreenEnabled) {
Expand Down
97 changes: 97 additions & 0 deletions ui/less/tooltip.less
@@ -0,0 +1,97 @@
/*!
* @license
* The tooltip is based on https://github.com/felipefialho/css-components/
* Local modifications have been performed.
*
* Copyright (c) 2017 Felipe Fialho
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

@material-icons-width: 32px;

.translateX(@percent) {
-webkit-transform: translateX(percentage(@percent));
-moz-transform: translateX(percentage(@percent));
-ms-transform: translateX(percentage(@percent));
-o-transform: translateX(percentage(@percent));
transform: translateX(percentage(@percent));
}

/* .shaka-tooltips-on enables the tooltips and is only added to the
* control panel when the 'enableTooltips' option is set to true */
.shaka-tooltips-on {
overflow: visible;

& > [class*="shaka-tooltip"] {
position: relative;

/* The :after pseudo-element contains the tooltip */
&:hover:after, &:focus-visible:after, &:active:after {
content: attr(aria-label);

/* Override .material-icons-round text styling */
font-family: Roboto-Regular, Roboto, sans-serif;
line-height: @material-icons-width / 2;
white-space: nowrap;
font-size: 13px;

/* Styling */
background: rgba(35, 35, 35, 0.9);
color: white;
border-radius: 3px;
padding: 5px 10px;

/* Positioning */
position: absolute;
bottom: @material-icons-width + 5px;

/* Left attribute is set to half of the width of the parent button */
left: @material-icons-width / 2;

/* The tooltip is also translated 50% to appear centered */
.translateX(-0.5);
}
}

/* Adds an additional attribute for the status in .shaka-tooltip-status */
& > .shaka-tooltip-status {
&:hover:after, &:focus-visible:after, &:active:after {
content: attr(aria-label) " (" attr(shaka-status) ")";
}
}

/* The first tooltip of the panel is not centered on top of the button
* but rather aligned with the left border of the control panel */
button:first-child {
&:hover:after, &:focus-visible:after, &:active:after {
left: 0;
.translateX(0);
}
}

/* The last tooltip of the panel is not centered on top of the button
* but rather aligned with the right border of the control panel */
button:last-child {
&:hover:after, &:focus-visible:after,&:active:after {
left: @material-icons-width;
.translateX(-1);
}
}
}
1 change: 1 addition & 0 deletions ui/loop_button.js
Expand Up @@ -35,6 +35,7 @@ shaka.ui.LoopButton = class extends shaka.ui.Element {
/** @private {!HTMLButtonElement} */
this.button_ = shaka.util.Dom.createButton();
this.button_.classList.add('shaka-loop-button');
this.button_.classList.add('shaka-tooltip');

/** @private {!HTMLElement} */
this.icon_ = shaka.util.Dom.createHTMLElement('i');
Expand Down
1 change: 1 addition & 0 deletions ui/mute_button.js
Expand Up @@ -33,6 +33,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('material-icons-round');
this.button_.classList.add('shaka-tooltip');
this.parent.appendChild(this.button_);
this.updateAriaLabel_();
this.updateIcon_();
Expand Down
1 change: 1 addition & 0 deletions ui/overflow_menu.js
Expand Up @@ -140,6 +140,7 @@ shaka.ui.OverflowMenu = class extends shaka.ui.Element {
this.overflowMenuButton_.classList.add('shaka-overflow-menu-button');
this.overflowMenuButton_.classList.add('shaka-no-propagation');
this.overflowMenuButton_.classList.add('material-icons-round');
this.overflowMenuButton_.classList.add('shaka-tooltip');
this.overflowMenuButton_.textContent =
shaka.ui.Enums.MaterialDesignIcons.OPEN_OVERFLOW;
this.parent.appendChild(this.overflowMenuButton_);
Expand Down
1 change: 1 addition & 0 deletions ui/pip_button.js
Expand Up @@ -39,6 +39,7 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
/** @private {!HTMLButtonElement} */
this.pipButton_ = shaka.util.Dom.createButton();
this.pipButton_.classList.add('shaka-pip-button');
this.pipButton_.classList.add('shaka-tooltip');

/** @private {!HTMLElement} */
this.pipIcon_ = shaka.util.Dom.createHTMLElement('i');
Expand Down
2 changes: 2 additions & 0 deletions ui/playback_rate_selection.js
Expand Up @@ -32,6 +32,7 @@ shaka.ui.PlaybackRateSelection = class extends shaka.ui.SettingsMenu {

this.button.classList.add('shaka-playbackrate-button');
this.menu.classList.add('shaka-playback-rates');
this.button.classList.add('shaka-tooltip-status');

this.eventManager.listen(
this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
Expand Down Expand Up @@ -103,6 +104,7 @@ shaka.ui.PlaybackRateSelection = class extends shaka.ui.SettingsMenu {
// Set the label to display the current playback rate in the overflow menu,
// in the format of '1x', '1.5x', etc.
this.currentSelection.textContent = rate + 'x';
this.button.setAttribute('shaka-status', rate + 'x');
}

/** @private */
Expand Down
3 changes: 3 additions & 0 deletions ui/resolution_selection.js
Expand Up @@ -34,6 +34,7 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
super(parent, controls, shaka.ui.Enums.MaterialDesignIcons.RESOLUTION);

this.button.classList.add('shaka-resolution-button');
this.button.classList.add('shaka-tooltip-status');
this.menu.classList.add('shaka-resolutions');

this.eventManager.listen(
Expand Down Expand Up @@ -172,6 +173,8 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
this.localization.resolve(shaka.ui.Locales.Ids.AUTO_QUALITY);
}

this.button.setAttribute('shaka-status', this.currentSelection.textContent);

this.menu.appendChild(autoButton);
shaka.ui.Utils.focusOnTheChosenItem(this.menu);
this.controls.dispatchEvent(
Expand Down
5 changes: 5 additions & 0 deletions ui/rewind_button.js
Expand Up @@ -32,6 +32,9 @@ shaka.ui.RewindButton = class extends shaka.ui.Element {
this.button_ = shaka.util.Dom.createButton();
this.button_.classList.add('material-icons-round');
this.button_.classList.add('shaka-rewind-button');
this.button_.classList.add('shaka-tooltip-status');
this.button_.setAttribute('shaka-status',
this.localization.resolve(shaka.ui.Locales.Ids.OFF));
this.button_.textContent =
shaka.ui.Enums.MaterialDesignIcons.REWIND;
this.parent.appendChild(this.button_);
Expand Down Expand Up @@ -80,6 +83,8 @@ shaka.ui.RewindButton = class extends shaka.ui.Element {
const newRate = (newRateIndex != this.rewindRates_.length) ?
this.rewindRates_[newRateIndex] : this.rewindRates_[0];
this.player.trickPlay(newRate);

this.button_.setAttribute('shaka-status', newRate + 'x');
}
};

Expand Down
1 change: 1 addition & 0 deletions ui/small_play_button.js
Expand Up @@ -28,6 +28,7 @@ shaka.ui.SmallPlayButton = class extends shaka.ui.PlayButton {

this.button.classList.add('shaka-small-play-button');
this.button.classList.add('material-icons-round');
this.button.classList.add('shaka-tooltip');

this.updateIcon();
this.updateAriaLabel();
Expand Down
3 changes: 3 additions & 0 deletions ui/text_selection.js
Expand Up @@ -35,6 +35,7 @@ shaka.ui.TextSelection = class extends shaka.ui.SettingsMenu {
controls, shaka.ui.Enums.MaterialDesignIcons.CLOSED_CAPTIONS);

this.button.classList.add('shaka-caption-button');
this.button.classList.add('shaka-tooltip-status');
this.menu.classList.add('shaka-text-languages');

if (this.player && this.player.isTextTrackVisible()) {
Expand Down Expand Up @@ -155,6 +156,8 @@ shaka.ui.TextSelection = class extends shaka.ui.SettingsMenu {
this.localization.resolve(shaka.ui.Locales.Ids.OFF);
}

this.button.setAttribute('shaka-status', this.currentSelection.textContent);

shaka.ui.Utils.focusOnTheChosenItem(this.menu);

this.controls.dispatchEvent(
Expand Down
1 change: 1 addition & 0 deletions ui/ui.js
Expand Up @@ -234,6 +234,7 @@ shaka.ui.Overlay = class {
enableKeyboardPlaybackControls: true,
enableFullscreenOnRotation: true,
forceLandscapeOnFullscreen: true,
enableTooltips: false,
};

// Check AirPlay support
Expand Down

0 comments on commit d5769ee

Please sign in to comment.