diff --git a/css/_videolayout_default.scss b/css/_videolayout_default.scss index e2dfca1d8..6bea52c76 100644 --- a/css/_videolayout_default.scss +++ b/css/_videolayout_default.scss @@ -85,7 +85,7 @@ position: relative; width: 100%; height: 100%; - display: none; + visibility: hidden; background: rgba(0,0,0,.6); z-index: 2; } diff --git a/modules/UI/util/JitsiPopover.js b/modules/UI/util/JitsiPopover.js index f49c4ea50..26608ca8d 100644 --- a/modules/UI/util/JitsiPopover.js +++ b/modules/UI/util/JitsiPopover.js @@ -98,14 +98,27 @@ var JitsiPopover = (function () { var self = this; $(".jitsipopover").on("mouseenter", function () { self.popoverIsHovered = true; + if(typeof self.onHoverPopover === "function") { + self.onHoverPopover(self.popoverIsHovered); + } }).on("mouseleave", function () { self.popoverIsHovered = false; self.hide(); + if(typeof self.onHoverPopover === "function") { + self.onHoverPopover(self.popoverIsHovered); + } }); this.refreshPosition(); }; + /** + * Adds a hover listener to the popover. + */ + JitsiPopover.prototype.addOnHoverPopover = function (listener) { + this.onHoverPopover = listener; + }; + /** * Refreshes the position of the popover. */ diff --git a/modules/UI/videolayout/ConnectionIndicator.js b/modules/UI/videolayout/ConnectionIndicator.js index 7ecd83ec9..d73be9c7f 100644 --- a/modules/UI/videolayout/ConnectionIndicator.js +++ b/modules/UI/videolayout/ConnectionIndicator.js @@ -439,4 +439,11 @@ ConnectionIndicator.prototype.updateResolutionIndicator = function () { } }; +/** + * Adds a hover listener to the popover. + */ +ConnectionIndicator.prototype.addPopoverHoverListener = function (listener) { + this.popover.addOnHoverPopover(listener); +}; + export default ConnectionIndicator; diff --git a/modules/UI/videolayout/LocalVideo.js b/modules/UI/videolayout/LocalVideo.js index 021f026b6..ff1c9ca22 100644 --- a/modules/UI/videolayout/LocalVideo.js +++ b/modules/UI/videolayout/LocalVideo.js @@ -11,6 +11,7 @@ function LocalVideo(VideoLayout, emitter) { this.videoSpanId = "localVideoContainer"; this.container = $("#localVideoContainer").get(0); this.localVideoId = null; + this.createConnectionIndicator(); this.bindHoverHandler(); if(config.enableLocalVideoFlip) this._buildContextMenu(); @@ -28,7 +29,6 @@ function LocalVideo(VideoLayout, emitter) { // Set default display name. this.setDisplayName(); - this.createConnectionIndicator(); this.addAudioLevelIndicator(); } diff --git a/modules/UI/videolayout/SmallVideo.js b/modules/UI/videolayout/SmallVideo.js index c919c5f64..e1f1357e3 100644 --- a/modules/UI/videolayout/SmallVideo.js +++ b/modules/UI/videolayout/SmallVideo.js @@ -21,11 +21,27 @@ const DISPLAY_VIDEO = 0; const DISPLAY_AVATAR = 1; /** * Display mode constant used when neither video nor avatar is being displayed - * on the small video. + * on the small video. And we just show the display name. * @type {number} * @constant */ -const DISPLAY_BLACKNESS = 2; +const DISPLAY_BLACKNESS_WITH_NAME = 2; + +/** + * Display mode constant used when video is displayed and display name + * at the same time. + * @type {number} + * @constant + */ +const DISPLAY_VIDEO_WITH_NAME = 3; + +/** + * Display mode constant used when neither video nor avatar is being displayed + * on the small video. And we just show the display name. + * @type {number} + * @constant + */ +const DISPLAY_AVATAR_WITH_NAME = 4; function SmallVideo(VideoLayout) { this.isAudioMuted = false; @@ -34,6 +50,7 @@ function SmallVideo(VideoLayout) { this.videoStream = null; this.audioStream = null; this.VideoLayout = VideoLayout; + this.videoIsHovered = false; } /** @@ -144,30 +161,26 @@ SmallVideo.getStreamElementID = function (stream) { }; /** - * Configures hoverIn/hoverOut handlers. + * Configures hoverIn/hoverOut handlers. Depends on connection indicator. */ SmallVideo.prototype.bindHoverHandler = function () { // Add hover handler $(this.container).hover( () => { - if (!this.VideoLayout.isCurrentlyOnLarge(this.id)) { - $('#' + this.videoSpanId + ' .videocontainer__overlay') - .removeClass("hide") - .addClass("show-inline"); - UIUtil.setVisibility(this.$displayName(), true); - } + this.videoIsHovered = true; + this.updateView(); }, () => { - $('#' + this.videoSpanId + ' .videocontainer__overlay') - .removeClass("show-inline") - .addClass("hide"); - // If the video has been "pinned" by the user we want to - // keep the display name on place. - if (!this.VideoLayout.isLargeVideoVisible() || - !this.VideoLayout.isCurrentlyOnLarge(this.id)) - UIUtil.setVisibility(this.$displayName(), false); + this.videoIsHovered = false; + this.updateView(); } ); + if (this.connectionIndicator) { + this.connectionIndicator.addPopoverHoverListener( + () => { + this.updateView(); + }); + } }; /** @@ -433,19 +446,34 @@ SmallVideo.prototype.isVideoPlayable = function() { * Determines what should be display on the thumbnail. * * @return {number} one of DISPLAY_VIDEO,DISPLAY_AVATAR - * or DISPLAY_BLACKNESS. + * or DISPLAY_BLACKNESS_WITH_NAME. */ SmallVideo.prototype.selectDisplayMode = function() { // Display name is always and only displayed when user is on the stage if (this.isCurrentlyOnLargeVideo()) { - return DISPLAY_BLACKNESS; + return DISPLAY_BLACKNESS_WITH_NAME; } else if (this.isVideoPlayable() && this.selectVideoElement().length) { - return DISPLAY_VIDEO; + // check hovering and change state to video with name + return this._isHovered() ? + DISPLAY_VIDEO_WITH_NAME : DISPLAY_VIDEO; } else { - return DISPLAY_AVATAR; + // check hovering and change state to avatar with name + return this._isHovered() ? + DISPLAY_AVATAR_WITH_NAME : DISPLAY_AVATAR; } }; +/** + * Checks whether current video is considered hovered. Currently it is hovered + * if the mouse is over the video, or if the connection + * indicator is shown(hovered). + * @private + */ +SmallVideo.prototype._isHovered = function () { + return this.videoIsHovered + || this.connectionIndicator.popover.popoverIsHovered; +}; + /** * Hides or shows the user's avatar. * This update assumes that large video had been updated and we will @@ -469,13 +497,23 @@ SmallVideo.prototype.updateView = function () { let displayMode = this.selectDisplayMode(); // Show/hide video. UIUtil.setVisibility( this.selectVideoElement(), - displayMode === DISPLAY_VIDEO); + (displayMode === DISPLAY_VIDEO + || displayMode === DISPLAY_VIDEO_WITH_NAME)); // Show/hide the avatar. UIUtil.setVisibility( this.$avatar(), - displayMode === DISPLAY_AVATAR); + (displayMode === DISPLAY_AVATAR + || displayMode === DISPLAY_AVATAR_WITH_NAME)); // Show/hide the display name. UIUtil.setVisibility( this.$displayName(), - displayMode === DISPLAY_BLACKNESS); + (displayMode === DISPLAY_BLACKNESS_WITH_NAME + || displayMode === DISPLAY_VIDEO_WITH_NAME + || displayMode === DISPLAY_AVATAR_WITH_NAME)); + // show hide overlay when there is a video or avatar under + // the display name + UIUtil.setVisibility( $('#' + this.videoSpanId + + ' .videocontainer__overlay'), + (displayMode === DISPLAY_AVATAR_WITH_NAME + || displayMode === DISPLAY_VIDEO_WITH_NAME)); }; SmallVideo.prototype.avatarChanged = function (avatarUrl) {