diff --git a/react/features/filmstrip/actions.web.js b/react/features/filmstrip/actions.web.js index 9bc9cd914..1d3856757 100644 --- a/react/features/filmstrip/actions.web.js +++ b/react/features/filmstrip/actions.web.js @@ -83,14 +83,15 @@ export function setTileViewDimensions() { disableTileEnlargement, maxColumns, numberOfParticipants, - numberOfVisibleTiles + desiredNumberOfVisibleTiles: numberOfVisibleTiles }); const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height); - const hasScroll = clientHeight < thumbnailsTotalHeight; + const availableHeight = clientHeight - TILE_VIEW_GRID_VERTICAL_MARGIN; + const hasScroll = availableHeight < thumbnailsTotalHeight; const filmstripWidth = Math.min(clientWidth - TILE_VIEW_GRID_HORIZONTAL_MARGIN, columns * (TILE_HORIZONTAL_MARGIN + width)) + (hasScroll ? SCROLL_SIZE : 0); - const filmstripHeight = Math.min(clientHeight - TILE_VIEW_GRID_VERTICAL_MARGIN, thumbnailsTotalHeight); + const filmstripHeight = Math.min(availableHeight, thumbnailsTotalHeight); dispatch({ type: SET_TILE_VIEW_DIMENSIONS, @@ -139,7 +140,11 @@ export function setVerticalViewDimensions() { const { tileView = {} } = state['features/base/config']; const { numberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES } = tileView; const numberOfParticipants = getNumberOfPartipantsForTileView(state); - const maxColumns = getMaxColumnCount(state); + const maxColumns = getMaxColumnCount(state, { + width: filmstripWidth.current, + disableResponsiveTiles: false, + disableTileEnlargement: false + }); const { height, width, @@ -152,7 +157,7 @@ export function setVerticalViewDimensions() { maxColumns, noHorizontalContainerMargin: true, numberOfParticipants, - numberOfVisibleTiles + desiredNumberOfVisibleTiles: numberOfVisibleTiles }); const thumbnailsTotalHeight = rows * (TILE_VERTICAL_MARGIN + height); @@ -269,7 +274,8 @@ export function setStageFilmstripViewDimensions() { const verticalWidth = visible ? getVerticalViewMaxWidth(state) : 0; const { numberOfVisibleTiles = MAX_ACTIVE_PARTICIPANTS } = tileView; const numberOfParticipants = state['features/filmstrip'].activeParticipants.length; - const maxColumns = getMaxColumnCount(state); + const availableWidth = clientWidth - verticalWidth; + const maxColumns = getMaxColumnCount(state, { width: availableWidth }); const { height, @@ -279,7 +285,7 @@ export function setStageFilmstripViewDimensions() { } = disableResponsiveTiles ? calculateNonResponsiveTileViewDimensions(state, true) : calculateResponsiveTileViewDimensions({ - clientWidth: clientWidth - verticalWidth, + clientWidth: availableWidth, clientHeight, disableTileEnlargement, maxColumns, diff --git a/react/features/filmstrip/functions.web.js b/react/features/filmstrip/functions.web.js index 8a831a921..66115abe6 100644 --- a/react/features/filmstrip/functions.web.js +++ b/react/features/filmstrip/functions.web.js @@ -297,7 +297,7 @@ export function calculateResponsiveTileViewDimensions({ noHorizontalContainerMargin = false, maxColumns, numberOfParticipants, - numberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES + desiredNumberOfVisibleTiles = TILE_VIEW_DEFAULT_NUMBER_OF_VISIBLE_TILES }) { let height, width; let columns, rows; @@ -311,12 +311,12 @@ export function calculateResponsiveTileViewDimensions({ maxArea: 0 }; - for (let c = 1; c <= Math.min(maxColumns, numberOfParticipants); c++) { + for (let c = 1; c <= Math.min(maxColumns, numberOfParticipants, desiredNumberOfVisibleTiles); c++) { const r = Math.ceil(numberOfParticipants / c); - // we want to display as much as possible tumbnails up to numberOfVisibleTiles + // we want to display as much as possible tumbnails up to desiredNumberOfVisibleTiles const visibleRows - = numberOfParticipants <= numberOfVisibleTiles ? r : Math.floor(numberOfVisibleTiles / c); + = numberOfParticipants <= desiredNumberOfVisibleTiles ? r : Math.floor(desiredNumberOfVisibleTiles / c); const size = calculateThumbnailSizeForTileView({ columns: c, @@ -330,18 +330,38 @@ export function calculateResponsiveTileViewDimensions({ if (size) { const { height: currentHeight, width: currentWidth, minHeightEnforced, maxVisibleRows } = size; - let area = currentHeight * currentWidth * Math.min(c * maxVisibleRows, numberOfParticipants); + const numberOfVisibleParticipants = Math.min(c * maxVisibleRows, numberOfParticipants); + + let area = Math.round( + (currentHeight + TILE_VERTICAL_MARGIN) + * (currentWidth + TILE_HORIZONTAL_MARGIN) + * numberOfVisibleParticipants); + const currentDimensions = { maxArea: area, height: currentHeight, width: currentWidth, columns: c, - rows: r + rows: r, + numberOfVisibleParticipants }; + const { numberOfVisibleParticipants: oldNumberOfVisibleParticipants = 0 } = dimensions; - if (!minHeightEnforced && area > dimensions.maxArea) { - dimensions = currentDimensions; - } else if (minHeightEnforced && area > minHeightEnforcedDimensions.maxArea) { + if (!minHeightEnforced) { + if (area > dimensions.maxArea) { + dimensions = currentDimensions; + } else if ((area === dimensions.maxArea) + && ((oldNumberOfVisibleParticipants > desiredNumberOfVisibleTiles + && oldNumberOfVisibleParticipants >= numberOfParticipants) + || (oldNumberOfVisibleParticipants < numberOfParticipants + && numberOfVisibleParticipants <= desiredNumberOfVisibleTiles)) + ) { // If the area of the new candidates and the old ones are equal we preffer the one that will have + // closer number of visible participants to desiredNumberOfVisibleTiles config. + dimensions = currentDimensions; + } + } else if (minHeightEnforced && area >= minHeightEnforcedDimensions.maxArea) { + // If we choose configuration with minHeightEnforced there will be less than desiredNumberOfVisibleTiles + // visible tiles, that's why we prefer more columns when the area is the same. minHeightEnforcedDimensions = currentDimensions; } else if (minHeightEnforced && maxVisibleRows === 0) { area = currentHeight * currentWidth * Math.min(c, numberOfParticipants); @@ -400,7 +420,8 @@ export function calculateThumbnailSizeForTileView({ const minHeight = getThumbnailMinHeight(clientWidth); const viewWidth = clientWidth - (columns * TILE_HORIZONTAL_MARGIN) - (noHorizontalContainerMargin ? SCROLL_SIZE : TILE_VIEW_GRID_HORIZONTAL_MARGIN); - const viewHeight = clientHeight - (minVisibleRows * TILE_VERTICAL_MARGIN) - TILE_VIEW_GRID_VERTICAL_MARGIN; + const availableHeight = clientHeight - TILE_VIEW_GRID_VERTICAL_MARGIN; + const viewHeight = availableHeight - (minVisibleRows * TILE_VERTICAL_MARGIN); const initialWidth = viewWidth / columns; let initialHeight = viewHeight / minVisibleRows; let minHeightEnforced = false; @@ -417,52 +438,47 @@ export function calculateThumbnailSizeForTileView({ return; } - const height = Math.floor(Math.min(aspectRatioHeight, initialHeight)); + const height = Math.min(aspectRatioHeight, initialHeight); return { height, - width: Math.floor(aspectRatio * height), + width: aspectRatio * height, minHeightEnforced, - maxVisibleRows: Math.floor(viewHeight / height) + maxVisibleRows: Math.floor(availableHeight / (height + TILE_VERTICAL_MARGIN)) }; } const initialRatio = initialWidth / initialHeight; - let height = Math.floor(initialHeight); + let height = initialHeight; + let width; // The biggest area of the grid will be when the grid's height is equal to clientHeight or when the grid's width is // equal to clientWidth. if (initialRatio > aspectRatio) { - return { - height, - width: Math.floor(initialHeight * aspectRatio), - minHeightEnforced, - maxVisibleRows: Math.floor(viewHeight / height) - }; + width = initialHeight * aspectRatio; } else if (initialRatio >= TILE_PORTRAIT_ASPECT_RATIO) { - return { - height, - width: Math.floor(initialWidth), - minHeightEnforced, - maxVisibleRows: Math.floor(viewHeight / height) - }; + width = initialWidth; + // eslint-disable-next-line no-negated-condition } else if (!minHeightEnforced) { - height = Math.floor(initialWidth / TILE_PORTRAIT_ASPECT_RATIO); + height = initialWidth / TILE_PORTRAIT_ASPECT_RATIO; if (height >= minHeight) { - return { - height, - width: Math.floor(initialWidth), - minHeightEnforced, - maxVisibleRows: Math.floor(viewHeight / height) - }; + width = initialWidth; + } else { // The width is so small that we can't reach the minimum height with portrait aspect ratio. + return; } + } else { + // We can't fit that number of columns with the desired min height and aspect ratio. + return; } - // else - // We can't fit that number of columns with the desired min height and aspect ratio. - return; + return { + height, + width, + minHeightEnforced, + maxVisibleRows: Math.floor(availableHeight / (height + TILE_VERTICAL_MARGIN)) + }; } /** diff --git a/react/features/video-layout/functions.js b/react/features/video-layout/functions.js index 97c6caf83..803ad574a 100644 --- a/react/features/video-layout/functions.js +++ b/react/features/video-layout/functions.js @@ -9,9 +9,14 @@ import { } from '../base/participants'; import { DEFAULT_MAX_COLUMNS, - ABSOLUTE_MAX_COLUMNS + ABSOLUTE_MAX_COLUMNS, + TILE_PORTRAIT_ASPECT_RATIO } from '../filmstrip/constants'; -import { getNumberOfPartipantsForTileView } from '../filmstrip/functions.web'; +import { + getNumberOfPartipantsForTileView, + getThumbnailMinHeight, + getTileDefaultAspectRatio +} from '../filmstrip/functions.web'; import { isVideoPlaying } from '../shared-video/functions'; import { VIDEO_QUALITY_LEVELS } from '../video-quality/constants'; @@ -56,15 +61,45 @@ export function getCurrentLayout(state: Object) { * returned will be between 1 and 7, inclusive. * * @param {Object} state - The redux store state. - * @param {number} width - Custom width to use for calculation. + * @param {Object} options - Object with custom values used to override the values that we get from redux by default. + * @param {number} options.width - Custom width to be used. + * @param {boolean} options.disableResponsiveTiles - Custom value to be used instead of config.disableResponsiveTiles. + * @param {boolean} options.disableTileEnlargement - Custom value to be used instead of config.disableTileEnlargement. * @returns {number} */ -export function getMaxColumnCount() { - const configuredMax = (typeof interfaceConfig === 'undefined' - ? DEFAULT_MAX_COLUMNS - : interfaceConfig.TILE_VIEW_MAX_COLUMNS) || DEFAULT_MAX_COLUMNS; +export function getMaxColumnCount(state, options = {}) { + if (typeof interfaceConfig === 'undefined') { + return DEFAULT_MAX_COLUMNS; + } - return Math.min(Math.max(configuredMax, 1), ABSOLUTE_MAX_COLUMNS); + const { + disableResponsiveTiles: configDisableResponsiveTiles, + disableTileEnlargement: configDisableTileEnlargement + } = state['features/base/config']; + const { + width, + disableResponsiveTiles = configDisableResponsiveTiles, + disableTileEnlargement = configDisableTileEnlargement + } = options; + const { clientWidth } = state['features/base/responsive-ui']; + const widthToUse = width || clientWidth; + const configuredMax = interfaceConfig.TILE_VIEW_MAX_COLUMNS; + + if (disableResponsiveTiles) { + return Math.min(Math.max(configuredMax || DEFAULT_MAX_COLUMNS, 1), ABSOLUTE_MAX_COLUMNS); + } + + if (typeof interfaceConfig.TILE_VIEW_MAX_COLUMNS !== 'undefined' && interfaceConfig.TILE_VIEW_MAX_COLUMNS > 0) { + return Math.max(configuredMax, 1); + } + + const aspectRatio = disableTileEnlargement + ? getTileDefaultAspectRatio(true, disableTileEnlargement, widthToUse) + : TILE_PORTRAIT_ASPECT_RATIO; + const minHeight = getThumbnailMinHeight(widthToUse); + const minWidth = aspectRatio * minHeight; + + return Math.floor(widthToUse / minWidth); } /**