diff --git a/test/ui/ui_unit.js b/test/ui/ui_unit.js index 041b20e8a..f4734abb5 100644 --- a/test/ui/ui_unit.js +++ b/test/ui/ui_unit.js @@ -844,8 +844,6 @@ describe('UI', () => { expect(contextMenu.classList.contains('shaka-hidden')).toBe(true); UiUtils.simulateEvent(controlsContainer, 'contextmenu'); expect(contextMenu.classList.contains('shaka-hidden')).toBe(false); - UiUtils.simulateEvent(controlsContainer, 'contextmenu'); - expect(contextMenu.classList.contains('shaka-hidden')).toBe(true); }); it('hides on click event', () => { @@ -853,7 +851,7 @@ describe('UI', () => { UiUtils.simulateEvent(controlsContainer, 'click'); expect(contextMenu.classList.contains('shaka-hidden')).toBe(true); UiUtils.simulateEvent(controlsContainer, 'contextmenu'); - UiUtils.simulateEvent(window, 'click'); + UiUtils.simulateEvent(controlsContainer, 'click'); expect(contextMenu.classList.contains('shaka-hidden')).toBe(true); }); diff --git a/ui/ad_statistics_button.js b/ui/ad_statistics_button.js index e71ff0393..4b7199f1a 100644 --- a/ui/ad_statistics_button.js +++ b/ui/ad_statistics_button.js @@ -9,7 +9,6 @@ goog.provide('shaka.ui.AdStatisticsButton'); goog.require('shaka.log'); goog.require('shaka.ads.Utils'); -goog.require('shaka.ui.ContextMenu'); goog.require('shaka.ui.Controls'); goog.require('shaka.ui.Element'); goog.require('shaka.ui.Enums'); @@ -257,6 +256,3 @@ shaka.ui.AdStatisticsButton.Factory = class { shaka.ui.OverflowMenu.registerElement( 'ad_statistics', new shaka.ui.AdStatisticsButton.Factory()); - -shaka.ui.ContextMenu.registerElement( - 'ad_statistics', new shaka.ui.AdStatisticsButton.Factory()); diff --git a/ui/context_menu.js b/ui/context_menu.js index 07259762f..722f53476 100644 --- a/ui/context_menu.js +++ b/ui/context_menu.js @@ -12,6 +12,7 @@ goog.require('shaka.log'); goog.require('shaka.ui.Element'); goog.require('shaka.ui.Utils'); goog.require('shaka.util.Dom'); +goog.require('shaka.util.FakeEvent'); goog.require('shaka.util.Iterables'); goog.requireType('shaka.ui.Controls'); @@ -47,23 +48,20 @@ shaka.ui.ContextMenu = class extends shaka.ui.Element { this.controlsContainer_.appendChild(this.contextMenu_); this.eventManager.listen(this.controlsContainer_, 'contextmenu', (e) => { - if (this.contextMenu_.classList.contains('shaka-hidden')) { - e.preventDefault(); - - const controlsLocation = - this.controlsContainer_.getBoundingClientRect(); - this.contextMenu_.style.left = `${e.clientX - controlsLocation.left}px`; - this.contextMenu_.style.top = `${e.clientY - controlsLocation.top}px`; - this.contextMenu_.style.bottom = 'auto'; - - shaka.ui.Utils.setDisplay(this.contextMenu_, true); - } else { - shaka.ui.Utils.setDisplay(this.contextMenu_, false); + e.preventDefault(); + if (this.controls.anySettingsMenusAreOpen()) { + this.controls.hideSettingsMenus(); } - }); + // Force to close any submenu. + this.controls.dispatchEvent(new shaka.util.FakeEvent('submenuclose')); - this.eventManager.listen(window, 'click', () => { - this.closeMenu(); + const controlsLocation = + this.controlsContainer_.getBoundingClientRect(); + this.contextMenu_.style.left = `${e.clientX - controlsLocation.left}px`; + this.contextMenu_.style.top = `${e.clientY - controlsLocation.top}px`; + this.contextMenu_.style.bottom = 'auto'; + + shaka.ui.Utils.setDisplay(this.contextMenu_, true); }); this.eventManager.listen(this.contextMenu_, 'click', () => { diff --git a/ui/controls.js b/ui/controls.js index 6ebdd89c1..c8f5aa84a 100644 --- a/ui/controls.js +++ b/ui/controls.js @@ -208,6 +208,9 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { /** @private {!Array} */ this.menus_ = []; + /** @private {!Array} */ + this.contextMenus_ = []; + /** @private {?shaka.extern.TextTrack} */ this.lastSelectedTextTrack_ = null; @@ -866,6 +869,22 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { this.hideSettingsMenusTimer_.tickNow(); } + /** + * @return {boolean} + * @export + */ + anyContextMenusAreOpen() { + return this.contextMenus_.some( + (menu) => !menu.classList.contains('shaka-hidden')); + } + + /** @export */ + hideContextMenus() { + for (const menu of this.contextMenus_) { + shaka.ui.Utils.setDisplay(menu, /* visible= */ false); + } + } + /** * @return {boolean} * @private @@ -1234,6 +1253,9 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { this.menus_.push(...Array.from( this.videoContainer_.getElementsByClassName('shaka-overflow-menu'))); + this.contextMenus_ = Array.from( + this.videoContainer_.getElementsByClassName('shaka-context-menu')); + this.showOnHoverControls_ = Array.from( this.videoContainer_.getElementsByClassName( 'shaka-show-controls-on-mouse-over')); @@ -1858,7 +1880,9 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget { return; } - if (this.anySettingsMenusAreOpen()) { + if (this.anyContextMenusAreOpen()) { + this.hideContextMenus(); + } else if (this.anySettingsMenusAreOpen()) { this.hideSettingsMenusTimer_.tickNow(); } else if (this.config_.singleClickForPlayAndPause) { this.playPausePresentation(); diff --git a/ui/copy_video_frame_button.js b/ui/copy_video_frame_button.js index 52136fda6..0fe05a430 100644 --- a/ui/copy_video_frame_button.js +++ b/ui/copy_video_frame_button.js @@ -9,7 +9,6 @@ goog.provide('shaka.ui.CopyVideoFrameButton'); goog.require('shaka.ads.Utils'); goog.require('shaka.cast.CastProxy'); -goog.require('shaka.ui.ContextMenu'); goog.require('shaka.ui.Controls'); goog.require('shaka.ui.Element'); goog.require('shaka.ui.Enums'); @@ -169,6 +168,3 @@ shaka.ui.CopyVideoFrameButton.Factory = class { shaka.ui.OverflowMenu.registerElement( 'copy_video_frame', new shaka.ui.CopyVideoFrameButton.Factory()); - -shaka.ui.ContextMenu.registerElement( - 'copy_video_frame', new shaka.ui.CopyVideoFrameButton.Factory()); diff --git a/ui/loop_button.js b/ui/loop_button.js index b8a7afc5e..f5b502c1d 100644 --- a/ui/loop_button.js +++ b/ui/loop_button.js @@ -7,7 +7,6 @@ goog.provide('shaka.ui.LoopButton'); -goog.require('shaka.ui.ContextMenu'); goog.require('shaka.ui.Controls'); goog.require('shaka.ui.Element'); goog.require('shaka.ui.Enums'); @@ -211,6 +210,3 @@ shaka.ui.OverflowMenu.registerElement( shaka.ui.Controls.registerElement( 'loop', new shaka.ui.LoopButton.Factory()); - -shaka.ui.ContextMenu.registerElement( - 'loop', new shaka.ui.LoopButton.Factory()); diff --git a/ui/mute_button.js b/ui/mute_button.js index 20279e823..7826fa37b 100644 --- a/ui/mute_button.js +++ b/ui/mute_button.js @@ -8,7 +8,6 @@ goog.provide('shaka.ui.MuteButton'); goog.require('shaka.ads.Utils'); -goog.require('shaka.ui.ContextMenu'); goog.require('shaka.ui.Controls'); goog.require('shaka.ui.Element'); goog.require('shaka.ui.Enums'); @@ -207,6 +206,3 @@ shaka.ui.OverflowMenu.registerElement( shaka.ui.Controls.registerElement( 'mute', new shaka.ui.MuteButton.Factory()); - -shaka.ui.ContextMenu.registerElement( - 'mute', new shaka.ui.MuteButton.Factory()); diff --git a/ui/overflow_menu.js b/ui/overflow_menu.js index 4cef0243c..e7d97779d 100644 --- a/ui/overflow_menu.js +++ b/ui/overflow_menu.js @@ -10,6 +10,7 @@ goog.provide('shaka.ui.OverflowMenu'); goog.require('goog.asserts'); goog.require('shaka.ads.Utils'); goog.require('shaka.log'); +goog.require('shaka.ui.ContextMenu'); goog.require('shaka.ui.Controls'); goog.require('shaka.ui.Element'); goog.require('shaka.ui.Enums'); @@ -130,10 +131,14 @@ shaka.ui.OverflowMenu = class extends shaka.ui.Element { /** * @param {string} name * @param {!shaka.extern.IUIElement.Factory} factory + * @param {boolean=} registerInContext * @export */ - static registerElement(name, factory) { + static registerElement(name, factory, registerInContext = true) { shaka.ui.OverflowMenu.elementNamesToFactories_.set(name, factory); + if (registerInContext) { + shaka.ui.ContextMenu.registerElement(name, factory); + } } @@ -190,6 +195,7 @@ shaka.ui.OverflowMenu = class extends shaka.ui.Element { /** @private */ onOverflowMenuButtonClick_() { + this.controls.hideContextMenus(); if (this.controls.anySettingsMenusAreOpen()) { this.controls.hideSettingsMenus(); } else { diff --git a/ui/pip_button.js b/ui/pip_button.js index 30d036ed6..03811d30d 100644 --- a/ui/pip_button.js +++ b/ui/pip_button.js @@ -7,7 +7,6 @@ goog.provide('shaka.ui.PipButton'); -goog.require('shaka.ui.ContextMenu'); goog.require('shaka.ui.Controls'); goog.require('shaka.ui.Element'); goog.require('shaka.ui.Enums'); @@ -220,6 +219,3 @@ shaka.ui.OverflowMenu.registerElement( shaka.ui.Controls.registerElement( 'picture_in_picture', new shaka.ui.PipButton.Factory()); - -shaka.ui.ContextMenu.registerElement( - 'picture_in_picture', new shaka.ui.PipButton.Factory()); diff --git a/ui/playback_rate_selection.js b/ui/playback_rate_selection.js index da1a2430e..f78117db7 100644 --- a/ui/playback_rate_selection.js +++ b/ui/playback_rate_selection.js @@ -35,7 +35,7 @@ shaka.ui.PlaybackRateSelection = class extends shaka.ui.SettingsMenu { this.menu.classList.add('shaka-playback-rates'); this.button.classList.add('shaka-tooltip-status'); - if (!Array.from(parent.classList).includes('shaka-overflow-menu')) { + if (!this.isSubMenu) { this.playbackRateMark = shaka.util.Dom.createHTMLElement('span'); this.playbackRateMark.classList.add('shaka-overflow-playback-rate-mark'); this.button.appendChild(this.playbackRateMark); diff --git a/ui/resolution_selection.js b/ui/resolution_selection.js index a63236f56..df78b7883 100644 --- a/ui/resolution_selection.js +++ b/ui/resolution_selection.js @@ -50,7 +50,7 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu { this.qualityMark.classList.add('shaka-current-quality-mark'); this.qualityMark.style.display = 'none'; - if (!Array.from(parent.classList).includes('shaka-overflow-menu')) { + if (!this.isSubMenu) { this.overflowQualityMark = shaka.util.Dom.createHTMLElement('span'); this.overflowQualityMark.classList.add( 'shaka-overflow-playback-rate-mark'); diff --git a/ui/save_video_frame_button.js b/ui/save_video_frame_button.js index 66149336e..9cb617798 100644 --- a/ui/save_video_frame_button.js +++ b/ui/save_video_frame_button.js @@ -9,7 +9,6 @@ goog.provide('shaka.ui.SaveVideoFrameButton'); goog.require('shaka.ads.Utils'); goog.require('shaka.cast.CastProxy'); -goog.require('shaka.ui.ContextMenu'); goog.require('shaka.ui.Controls'); goog.require('shaka.ui.Element'); goog.require('shaka.ui.Enums'); @@ -169,6 +168,3 @@ shaka.ui.SaveVideoFrameButton.Factory = class { shaka.ui.OverflowMenu.registerElement( 'save_video_frame', new shaka.ui.SaveVideoFrameButton.Factory()); - -shaka.ui.ContextMenu.registerElement( - 'save_video_frame', new shaka.ui.SaveVideoFrameButton.Factory()); diff --git a/ui/settings_menu.js b/ui/settings_menu.js index 50895f003..26154d701 100644 --- a/ui/settings_menu.js +++ b/ui/settings_menu.js @@ -190,6 +190,9 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element { /** @private */ onButtonClick_() { + if (!this.parent.classList.contains('shaka-context-menu')) { + this.controls.hideContextMenus(); + } if (!this.isSubMenu && this.controls.anySettingsMenusAreOpen()) { this.controls.hideSettingsMenus(); } else { diff --git a/ui/statistics_button.js b/ui/statistics_button.js index e8b0c9300..64baef1d4 100644 --- a/ui/statistics_button.js +++ b/ui/statistics_button.js @@ -8,7 +8,6 @@ goog.provide('shaka.ui.StatisticsButton'); goog.require('shaka.log'); -goog.require('shaka.ui.ContextMenu'); goog.require('shaka.ui.Controls'); goog.require('shaka.ui.Element'); goog.require('shaka.ui.Enums'); @@ -316,6 +315,3 @@ shaka.ui.StatisticsButton.Factory = class { shaka.ui.OverflowMenu.registerElement( 'statistics', new shaka.ui.StatisticsButton.Factory()); - -shaka.ui.ContextMenu.registerElement( - 'statistics', new shaka.ui.StatisticsButton.Factory());