Add large / small video animation.

This commit is contained in:
John Preston 2021-05-31 21:25:15 +04:00
parent b22363224f
commit 2fe75f8296
9 changed files with 473 additions and 152 deletions

View file

@ -972,7 +972,11 @@ void GroupCall::markTrackShown(const VideoEndpoint &endpoint, bool shown) {
_videoStreamShownUpdates.fire_copy({ endpoint, shown }); _videoStreamShownUpdates.fire_copy({ endpoint, shown });
} }
if (shown && changed && endpoint.type == VideoEndpointType::Screen) { if (shown && changed && endpoint.type == VideoEndpointType::Screen) {
pinVideoEndpoint(endpoint); crl::on_main(this, [=] {
if (_shownVideoTracks.contains(endpoint)) {
pinVideoEndpoint(endpoint);
}
});
} }
} }

View file

@ -1189,8 +1189,9 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
if (const auto real = _call->lookupReal()) { if (const auto real = _call->lookupReal()) {
auto oneFound = false; auto oneFound = false;
auto hasTwoOrMore = false; auto hasTwoOrMore = false;
const auto &shown = _call->shownVideoTracks();
for (const auto &[endpoint, track] : _call->activeVideoTracks()) { for (const auto &[endpoint, track] : _call->activeVideoTracks()) {
if (_call->shownVideoTracks().contains(endpoint)) { if (shown.contains(endpoint)) {
if (oneFound) { if (oneFound) {
hasTwoOrMore = true; hasTwoOrMore = true;
break; break;
@ -1202,36 +1203,36 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
if (participant && hasTwoOrMore) { if (participant && hasTwoOrMore) {
const auto &large = _call->videoEndpointLarge(); const auto &large = _call->videoEndpointLarge();
const auto pinned = _call->videoEndpointPinned(); const auto pinned = _call->videoEndpointPinned();
const auto &camera = computeCameraEndpoint(participant); const auto camera = VideoEndpoint{
const auto &screen = computeScreenEndpoint(participant); VideoEndpointType::Camera,
if (!camera.empty()) { participantPeer,
if (pinned && large.id == camera) { computeCameraEndpoint(participant)
};
const auto screen = VideoEndpoint{
VideoEndpointType::Screen,
participantPeer,
computeScreenEndpoint(participant)
};
if (shown.contains(camera)) {
if (pinned && large == camera) {
result->addAction( result->addAction(
tr::lng_group_call_context_unpin_camera(tr::now), tr::lng_group_call_context_unpin_camera(tr::now),
[=] { _call->pinVideoEndpoint(VideoEndpoint()); }); [=] { _call->pinVideoEndpoint({}); });
} else { } else {
result->addAction( result->addAction(
tr::lng_group_call_context_pin_camera(tr::now), tr::lng_group_call_context_pin_camera(tr::now),
[=] { _call->pinVideoEndpoint(VideoEndpoint{ [=] { _call->pinVideoEndpoint(camera); });
VideoEndpointType::Camera,
participantPeer,
camera });
});
} }
} }
if (!screen.empty()) { if (shown.contains(screen)) {
if (pinned && large.id == screen) { if (pinned && large == screen) {
result->addAction( result->addAction(
tr::lng_group_call_context_unpin_screen(tr::now), tr::lng_group_call_context_unpin_screen(tr::now),
[=] { _call->pinVideoEndpoint(VideoEndpoint()); }); [=] { _call->pinVideoEndpoint({}); });
} else { } else {
result->addAction( result->addAction(
tr::lng_group_call_context_pin_screen(tr::now), tr::lng_group_call_context_pin_screen(tr::now),
[=] { _call->pinVideoEndpoint(VideoEndpoint{ [=] { _call->pinVideoEndpoint(screen); });
VideoEndpointType::Screen,
participantPeer,
screen });
});
} }
} }
} }

View file

@ -1359,7 +1359,7 @@ void Panel::chooseShareScreenSource() {
} }
return nullptr; return nullptr;
}(); }();
if (!screencastFromPeer) { if (!screencastFromPeer || _call->isSharingScreen()) {
choose(); choose();
return; return;
} }

View file

@ -29,6 +29,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtGui/QOpenGLShader> #include <QtGui/QOpenGLShader>
namespace Calls::Group { namespace Calls::Group {
namespace {
[[nodiscard]] QRect InterpolateRect(QRect a, QRect b, float64 ratio) {
const auto left = anim::interpolate(a.x(), b.x(), ratio);
const auto top = anim::interpolate(a.y(), b.y(), ratio);
const auto right = anim::interpolate(
a.x() + a.width(),
b.x() + b.width(),
ratio);
const auto bottom = anim::interpolate(
a.y() + a.height(),
b.y() + b.height(),
ratio);
return { left, top, right - left, bottom - top };
}
} // namespace
Viewport::Viewport(not_null<QWidget*> parent, PanelMode mode) Viewport::Viewport(not_null<QWidget*> parent, PanelMode mode)
: _mode(mode) : _mode(mode)
@ -230,7 +247,9 @@ void Viewport::remove(const VideoEndpoint &endpoint) {
return; return;
} }
const auto removing = i->get(); const auto removing = i->get();
if (_large == removing) { const auto largeRemoved = (_large == removing);
if (largeRemoved) {
prepareLargeChangeAnimation();
_large = nullptr; _large = nullptr;
} }
if (_selected.tile == removing) { if (_selected.tile == removing) {
@ -239,18 +258,278 @@ void Viewport::remove(const VideoEndpoint &endpoint) {
if (_pressed.tile == removing) { if (_pressed.tile == removing) {
setPressed({}); setPressed({});
} }
for (auto &geometry : _startTilesLayout.list) {
if (geometry.tile == removing) {
geometry.tile = nullptr;
}
}
for (auto &geometry : _finishTilesLayout.list) {
if (geometry.tile == removing) {
geometry.tile = nullptr;
}
}
_tiles.erase(i); _tiles.erase(i);
updateTilesGeometry(); if (largeRemoved) {
startLargeChangeAnimation();
} else {
updateTilesGeometry();
}
}
void Viewport::prepareLargeChangeAnimation() {
if (!wide()) {
return;
} else if (_largeChangeAnimation.animating()) {
updateTilesAnimated();
const auto field = _finishTilesLayout.useColumns
? &Geometry::columns
: &Geometry::rows;
for (auto &finish : _finishTilesLayout.list) {
const auto tile = finish.tile;
if (!tile) {
continue;
}
finish.*field = tile->geometry();
}
_startTilesLayout = std::move(_finishTilesLayout);
_largeChangeAnimation.stop();
_startTilesLayout.list.erase(
ranges::remove(_startTilesLayout.list, nullptr, &Geometry::tile),
end(_startTilesLayout.list));
} else {
_startTilesLayout = applyLarge(std::move(_startTilesLayout));
}
}
void Viewport::startLargeChangeAnimation() {
Expects(!_largeChangeAnimation.animating());
if (!wide()
|| anim::Disabled()
|| (_startTilesLayout.list.size() < 2)
|| !_opengl
|| widget()->size().isEmpty()) {
updateTilesGeometry();
return;
}
_finishTilesLayout = applyLarge(
countWide(widget()->width(), widget()->height()));
if (_finishTilesLayout.list.empty()
|| _finishTilesLayout.outer != _startTilesLayout.outer) {
updateTilesGeometry();
return;
}
_largeChangeAnimation.start(
[=] { updateTilesAnimated(); },
0.,
1.,
st::slideDuration);
}
Viewport::Layout Viewport::applyLarge(Layout layout) const {
auto &list = layout.list;
if (!_large) {
return layout;
}
const auto i = ranges::find(list, _large, &Geometry::tile);
if (i == end(list)) {
return layout;
}
const auto field = layout.useColumns
? &Geometry::columns
: &Geometry::rows;
const auto fullWidth = layout.outer.width();
const auto fullHeight = layout.outer.height();
const auto largeRect = (*i).*field;
const auto largeLeft = largeRect.x();
const auto largeTop = largeRect.y();
const auto largeRight = largeLeft + largeRect.width();
const auto largeBottom = largeTop + largeRect.height();
const auto largeCenter = largeRect.center();
for (auto &geometry : list) {
if (geometry.tile == _large) {
geometry.*field = { QPoint(), layout.outer };
} else if (layout.useColumns) {
auto &rect = geometry.columns;
const auto center = rect.center();
if (center.x() < largeLeft) {
rect = rect.translated(-largeLeft, 0);
} else if (center.x() > largeRight) {
rect = rect.translated(fullWidth - largeRight, 0);
} else if (center.y() < largeTop) {
rect = QRect(
0,
rect.y() - largeTop,
fullWidth,
rect.height());
} else if (center.y() > largeBottom) {
rect = QRect(
0,
rect.y() + (fullHeight - largeBottom),
fullWidth,
rect.height());
}
} else {
auto &rect = geometry.rows;
const auto center = rect.center();
if (center.y() < largeTop) {
rect = rect.translated(0, -largeTop);
} else if (center.y() > largeBottom) {
rect = rect.translated(0, fullHeight - largeBottom);
} else if (center.x() < largeLeft) {
rect = QRect(
rect.x() - largeLeft,
0,
rect.width(),
fullHeight);
} else {
rect = QRect(
rect.x() + (fullWidth - largeRight),
0,
rect.width(),
fullHeight);
}
}
}
return layout;
}
void Viewport::updateTilesAnimated() {
if (!_largeChangeAnimation.animating()) {
updateTilesGeometry();
return;
}
const auto ratio = _largeChangeAnimation.value(1.);
const auto field = _finishTilesLayout.useColumns
? &Geometry::columns
: &Geometry::rows;
for (const auto &finish : _finishTilesLayout.list) {
const auto tile = finish.tile;
if (!tile) {
continue;
}
const auto i = ranges::find(
_startTilesLayout.list,
tile,
&Geometry::tile);
if (i == end(_startTilesLayout.list)) {
LOG(("Tiles Animation Error 1!"));
_largeChangeAnimation.stop();
updateTilesGeometry();
return;
}
const auto from = (*i).*field;
const auto to = finish.*field;
tile->setGeometry(
InterpolateRect(from, to, ratio),
TileAnimation{ from.size(), to.size(), ratio });
}
widget()->update();
}
Viewport::Layout Viewport::countWide(int outerWidth, int outerHeight) const {
auto result = Layout{ .outer = QSize(outerWidth, outerHeight) };
auto &sizes = result.list;
sizes.reserve(_tiles.size());
for (const auto &tile : _tiles) {
const auto video = tile.get();
const auto size = video->trackSize();
if (!size.isEmpty()) {
sizes.push_back(Geometry{ video, size });
}
}
if (sizes.empty()) {
return result;
} else if (sizes.size() == 1) {
sizes.front().rows = { 0, 0, outerWidth, outerHeight };
return result;
}
auto columnsBlack = uint64();
auto rowsBlack = uint64();
const auto count = int(sizes.size());
const auto skip = st::groupCallVideoLargeSkip;
const auto slices = int(std::ceil(std::sqrt(float64(count))));
{
auto index = 0;
const auto columns = slices;
const auto sizew = (outerWidth + skip) / float64(columns);
for (auto column = 0; column != columns; ++column) {
const auto left = int(std::round(column * sizew));
const auto width = int(std::round(column * sizew + sizew - skip))
- left;
const auto rows = int(std::round((count - index)
/ float64(columns - column)));
const auto sizeh = (outerHeight + skip) / float64(rows);
for (auto row = 0; row != rows; ++row) {
const auto top = int(std::round(row * sizeh));
const auto height = int(std::round(
row * sizeh + sizeh - skip)) - top;
auto &geometry = sizes[index];
geometry.columns = {
left,
top,
width,
height };
const auto scaled = geometry.size.scaled(
width,
height,
Qt::KeepAspectRatio);
columnsBlack += (scaled.width() < width)
? (width - scaled.width()) * height
: (height - scaled.height()) * width;
++index;
}
}
}
{
auto index = 0;
const auto rows = slices;
const auto sizeh = (outerHeight + skip) / float64(rows);
for (auto row = 0; row != rows; ++row) {
const auto top = int(std::round(row * sizeh));
const auto height = int(std::round(row * sizeh + sizeh - skip))
- top;
const auto columns = int(std::round((count - index)
/ float64(rows - row)));
const auto sizew = (outerWidth + skip) / float64(columns);
for (auto column = 0; column != columns; ++column) {
const auto left = int(std::round(column * sizew));
const auto width = int(std::round(
column * sizew + sizew - skip)) - left;
auto &geometry = sizes[index];
geometry.rows = {
left,
top,
width,
height };
const auto scaled = geometry.size.scaled(
width,
height,
Qt::KeepAspectRatio);
rowsBlack += (scaled.width() < width)
? (width - scaled.width()) * height
: (height - scaled.height()) * width;
++index;
}
}
}
result.useColumns = (columnsBlack < rowsBlack);
return result;
} }
void Viewport::showLarge(const VideoEndpoint &endpoint) { void Viewport::showLarge(const VideoEndpoint &endpoint) {
const auto i = ranges::find(_tiles, endpoint, &VideoTile::endpoint); const auto i = ranges::find(_tiles, endpoint, &VideoTile::endpoint);
const auto large = (i != end(_tiles)) ? i->get() : nullptr; const auto large = (i != end(_tiles)) ? i->get() : nullptr;
if (_large != large) { if (_large != large) {
prepareLargeChangeAnimation();
_large = large; _large = large;
updateTopControlsVisibility(); updateTopControlsVisibility();
updateTilesGeometry(); startLargeChangeAnimation();
} }
Ensures(!_large || !_large->trackSize().isEmpty());
} }
void Viewport::updateTilesGeometry() { void Viewport::updateTilesGeometry() {
@ -311,111 +590,32 @@ void Viewport::updateTopControlsVisibility() {
void Viewport::updateTilesGeometryWide(int outerWidth, int outerHeight) { void Viewport::updateTilesGeometryWide(int outerWidth, int outerHeight) {
if (!outerHeight) { if (!outerHeight) {
return; return;
} else if (_largeChangeAnimation.animating()) {
if (_startTilesLayout.outer == QSize(outerWidth, outerHeight)) {
return;
}
_largeChangeAnimation.stop();
} }
struct Geometry { _startTilesLayout = countWide(outerWidth, outerHeight);
QSize size; if (_large && !_large->trackSize().isEmpty()) {
QRect columns; for (const auto &geometry : _startTilesLayout.list) {
QRect rows; if (geometry.tile == _large) {
}; setTileGeometry(_large, { 0, 0, outerWidth, outerHeight });
auto sizes = base::flat_map<not_null<VideoTile*>, Geometry>(); } else {
sizes.reserve(_tiles.size()); geometry.tile->hide();
for (const auto &tile : _tiles) {
const auto video = tile.get();
const auto size = (_large && video != _large)
? QSize()
: video->trackSize();
if (size.isEmpty()) {
setTileGeometry(video, { 0, 0, outerWidth, 0 });
} else {
sizes.emplace(video, Geometry{ size });
}
}
if (sizes.size() == 1) {
setTileGeometry(
sizes.front().first,
{ 0, 0, outerWidth, outerHeight });
return;
}
if (sizes.empty()) {
return;
}
auto columnsBlack = uint64();
auto rowsBlack = uint64();
const auto count = int(sizes.size());
const auto skip = st::groupCallVideoLargeSkip;
const auto slices = int(std::ceil(std::sqrt(float64(count))));
{
auto index = 0;
const auto columns = slices;
const auto sizew = (outerWidth + skip) / float64(columns);
for (auto column = 0; column != columns; ++column) {
const auto left = int(std::round(column * sizew));
const auto width = int(std::round(column * sizew + sizew - skip))
- left;
const auto rows = int(std::round((count - index)
/ float64(columns - column)));
const auto sizeh = (outerHeight + skip) / float64(rows);
for (auto row = 0; row != rows; ++row) {
const auto top = int(std::round(row * sizeh));
const auto height = int(std::round(
row * sizeh + sizeh - skip)) - top;
auto &geometry = (sizes.begin() + index)->second;
geometry.columns = {
left,
top,
width,
height };
const auto scaled = geometry.size.scaled(
width,
height,
Qt::KeepAspectRatio);
columnsBlack += (scaled.width() < width)
? (width - scaled.width()) * height
: (height - scaled.height()) * width;
++index;
} }
} }
} } else {
{ const auto field = _startTilesLayout.useColumns
auto index = 0; ? &Geometry::columns
const auto rows = slices; : &Geometry::rows;
const auto sizeh = (outerHeight + skip) / float64(rows); for (const auto &geometry : _startTilesLayout.list) {
for (auto row = 0; row != rows; ++row) { if (const auto video = geometry.tile) {
const auto top = int(std::round(row * sizeh)); setTileGeometry(video, geometry.*field);
const auto height = int(std::round(row * sizeh + sizeh - skip))
- top;
const auto columns = int(std::round((count - index)
/ float64(rows - row)));
const auto sizew = (outerWidth + skip) / float64(columns);
for (auto column = 0; column != columns; ++column) {
const auto left = int(std::round(column * sizew));
const auto width = int(std::round(
column * sizew + sizew - skip)) - left;
auto &geometry = (sizes.begin() + index)->second;
geometry.rows = {
left,
top,
width,
height };
const auto scaled = geometry.size.scaled(
width,
height,
Qt::KeepAspectRatio);
rowsBlack += (scaled.width() < width)
? (width - scaled.width()) * height
: (height - scaled.height()) * width;
++index;
} }
} }
} }
const auto layout = (columnsBlack < rowsBlack)
? &Geometry::columns
: &Geometry::rows;
for (const auto &[video, geometry] : sizes) {
setTileGeometry(video, geometry.*layout);
}
} }
void Viewport::updateTilesGeometryNarrow(int outerWidth) { void Viewport::updateTilesGeometryNarrow(int outerWidth) {
@ -431,7 +631,7 @@ void Viewport::updateTilesGeometryNarrow(int outerWidth) {
const auto video = tile.get(); const auto video = tile.get();
const auto size = video->trackSize(); const auto size = video->trackSize();
if (size.isEmpty()) { if (size.isEmpty()) {
setTileGeometry(video, { 0, y, outerWidth, 0 }); video->hide();
} else { } else {
sizes.emplace(video, size); sizes.emplace(video, size);
} }
@ -536,7 +736,6 @@ void Viewport::updateTilesGeometryColumn(int outerWidth) {
void Viewport::setTileGeometry(not_null<VideoTile*> tile, QRect geometry) { void Viewport::setTileGeometry(not_null<VideoTile*> tile, QRect geometry) {
tile->setGeometry(geometry); tile->setGeometry(geometry);
tile->setShown(!geometry.isEmpty());
const auto min = std::min(geometry.width(), geometry.height()); const auto min = std::min(geometry.width(), geometry.height());
const auto kMedium = style::ConvertScale(480); const auto kMedium = style::ConvertScale(480);

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "ui/rp_widget.h" #include "ui/rp_widget.h"
#include "ui/effects/animations.h"
namespace Ui { namespace Ui {
class AbstractButton; class AbstractButton;
@ -91,6 +92,26 @@ private:
class VideoTile; class VideoTile;
class Renderer; class Renderer;
class RendererGL; class RendererGL;
using TileId = quintptr;
struct Geometry {
VideoTile *tile = nullptr;
QSize size;
QRect rows;
QRect columns;
};
struct Layout {
std::vector<Geometry> list;
QSize outer;
bool useColumns = false;
};
struct TileAnimation {
QSize from;
QSize to;
float64 ratio = -1.;
};
struct Selection { struct Selection {
enum class Element { enum class Element {
@ -122,6 +143,12 @@ private:
void refreshHasTwoOrMore(); void refreshHasTwoOrMore();
void updateTopControlsVisibility(); void updateTopControlsVisibility();
void prepareLargeChangeAnimation();
void startLargeChangeAnimation();
void updateTilesAnimated();
[[nodiscard]] Layout countWide(int outerWidth, int outerHeight) const;
[[nodiscard]] Layout applyLarge(Layout layout) const;
void setSelected(Selection value); void setSelected(Selection value);
void setPressed(Selection value); void setPressed(Selection value);
@ -149,6 +176,9 @@ private:
rpl::event_stream<VideoQualityRequest> _qualityRequests; rpl::event_stream<VideoQualityRequest> _qualityRequests;
float64 _controlsShownRatio = 1.; float64 _controlsShownRatio = 1.;
VideoTile *_large = nullptr; VideoTile *_large = nullptr;
Ui::Animations::Simple _largeChangeAnimation;
Layout _startTilesLayout;
Layout _finishTilesLayout;
Selection _selected; Selection _selected;
Selection _pressed; Selection _pressed;
rpl::variable<bool> _mouseInside = false; rpl::variable<bool> _mouseInside = false;

View file

@ -124,14 +124,36 @@ vec4 background() {
: NonEmpty(scaled); : NonEmpty(scaled);
} }
[[nodiscard]] QSize InterpolateScaledSize(
QSize unscaled,
QSize size,
float64 ratio) {
if (ratio == 0.) {
return NonEmpty(unscaled.scaled(
size,
Qt::KeepAspectRatio));
} else if (ratio == 1.) {
return NonEmpty(unscaled.scaled(
size,
Qt::KeepAspectRatioByExpanding));
}
const auto notExpanded = NonEmpty(unscaled.scaled(
size,
Qt::KeepAspectRatio));
const auto expanded = NonEmpty(unscaled.scaled(
size,
Qt::KeepAspectRatioByExpanding));
return QSize(
anim::interpolate(notExpanded.width(), expanded.width(), ratio),
anim::interpolate(notExpanded.height(), expanded.height(), ratio));
}
[[nodiscard]] std::array<std::array<GLfloat, 2>, 4> CountTexCoords( [[nodiscard]] std::array<std::array<GLfloat, 2>, 4> CountTexCoords(
QSize unscaled, QSize unscaled,
QSize size, QSize size,
bool expand, float64 expandRatio,
bool swap = false) { bool swap = false) {
const auto scaled = NonEmpty(unscaled.scaled( const auto scaled = InterpolateScaledSize(unscaled, size, expandRatio);
size,
expand ? Qt::KeepAspectRatioByExpanding : Qt::KeepAspectRatio));
const auto left = (size.width() - scaled.width()) / 2; const auto left = (size.width() - scaled.width()) / 2;
const auto top = (size.height() - scaled.height()) / 2; const auto top = (size.height() - scaled.height()) / 2;
const auto right = left + scaled.width(); const auto right = left + scaled.width();
@ -457,12 +479,17 @@ void Viewport::RendererGL::paintTile(
data.rotation); data.rotation);
const auto tileSize = geometry.size(); const auto tileSize = geometry.size();
const auto swap = (((data.rotation / 90) % 2) == 1); const auto swap = (((data.rotation / 90) % 2) == 1);
const auto expand = !tile->screencast() const auto expand = isExpanded(tile, unscaled, tileSize);
&& (!_owner->wide() || UseExpandForCamera(unscaled, tileSize)); const auto animation = tile->animation();
auto texCoords = CountTexCoords(unscaled, tileSize, expand, swap); const auto expandRatio = (animation.ratio >= 0.)
auto blurTexCoords = expand ? countExpandRatio(tile, unscaled, animation)
: expand
? 1.
: 0.;
auto texCoords = CountTexCoords(unscaled, tileSize, expandRatio, swap);
auto blurTexCoords = (expandRatio == 1.)
? texCoords ? texCoords
: CountTexCoords(unscaled, tileSize, true); : CountTexCoords(unscaled, tileSize, 1.);
const auto rect = transformRect(geometry); const auto rect = transformRect(geometry);
auto toBlurTexCoords = std::array<std::array<GLfloat, 2>, 4> { { auto toBlurTexCoords = std::array<std::array<GLfloat, 2>, 4> { {
{ { 0.f, 1.f } }, { { 0.f, 1.f } },
@ -763,6 +790,29 @@ void Viewport::RendererGL::prepareObjects(
create(1, kFirstBlurPassTextureIndex); create(1, kFirstBlurPassTextureIndex);
} }
bool Viewport::RendererGL::isExpanded(
not_null<VideoTile*> tile,
QSize unscaled,
QSize tileSize) const {
return !tile->screencast()
&& (!_owner->wide() || UseExpandForCamera(unscaled, tileSize));
}
float64 Viewport::RendererGL::countExpandRatio(
not_null<VideoTile*> tile,
QSize unscaled,
const TileAnimation &animation) const {
const auto expandedFrom = isExpanded(tile, unscaled, animation.from);
const auto expandedTo = isExpanded(tile, unscaled, animation.to);
return (expandedFrom && expandedTo)
? 1.
: (!expandedFrom && !expandedTo)
? 0.
: expandedFrom
? (1. - animation.ratio)
: animation.ratio;
}
void Viewport::RendererGL::bindFrame( void Viewport::RendererGL::bindFrame(
QOpenGLFunctions &f, QOpenGLFunctions &f,
const Webrtc::FrameWithInfo &data, const Webrtc::FrameWithInfo &data,

View file

@ -102,6 +102,15 @@ private:
not_null<VideoTile*> tile, not_null<VideoTile*> tile,
TileData &data); TileData &data);
[[nodiscard]] bool isExpanded(
not_null<VideoTile*> tile,
QSize unscaled,
QSize tileSize) const;
[[nodiscard]] float64 countExpandRatio(
not_null<VideoTile*> tile,
QSize unscaled,
const TileAnimation &animation) const;
const not_null<Viewport*> _owner; const not_null<Viewport*> _owner;
GLfloat _factor = 1.; GLfloat _factor = 1.;

View file

@ -58,13 +58,18 @@ bool Viewport::VideoTile::screencast() const {
return (_endpoint.type == VideoEndpointType::Screen); return (_endpoint.type == VideoEndpointType::Screen);
} }
void Viewport::VideoTile::setGeometry(QRect geometry) { void Viewport::VideoTile::setGeometry(
QRect geometry,
TileAnimation animation) {
_shown = true;
_geometry = geometry; _geometry = geometry;
updateTopControlsGeometry(); _animation = animation;
updateTopControlsPosition();
} }
void Viewport::VideoTile::setShown(bool shown) { void Viewport::VideoTile::hide() {
_shown = shown; _shown = false;
_quality = std::nullopt;
} }
void Viewport::VideoTile::toggleTopControlsShown(bool shown) { void Viewport::VideoTile::toggleTopControlsShown(bool shown) {
@ -183,27 +188,38 @@ void Viewport::VideoTile::PaintBackButton(
tr::lng_create_group_back(tr::now)); tr::lng_create_group_back(tr::now));
} }
void Viewport::VideoTile::updateTopControlsGeometry() { void Viewport::VideoTile::updateTopControlsSize() {
const auto &st = st::groupCallVideoTile; const auto &st = st::groupCallVideoTile;
const auto pinSize = PinInnerSize(_pinned); const auto pinSize = PinInnerSize(_pinned);
const auto pinWidth = st.pinPosition.x() * 2 + pinSize.width(); const auto pinWidth = st.pinPosition.x() * 2 + pinSize.width();
const auto pinHeight = st.pinPosition.y() * 2 + pinSize.height(); const auto pinHeight = st.pinPosition.y() * 2 + pinSize.height();
_pinInner = QRect(QPoint(), pinSize).translated( _pinInner = QRect(QPoint(), pinSize);
_geometry.width() - st.pinPosition.x() - pinSize.width(), _pinOuter = QRect(0, 0, pinWidth, pinHeight);
st.pinPosition.y());
_pinOuter = QRect(
_geometry.width() - pinWidth,
0,
pinWidth,
pinHeight);
const auto backSize = BackInnerSize(); const auto backSize = BackInnerSize();
const auto backWidth = st.pinPosition.x() * 2 + backSize.width(); const auto backWidth = st.pinPosition.x() * 2 + backSize.width();
const auto backHeight = st.pinPosition.y() * 2 + backSize.height(); const auto backHeight = st.pinPosition.y() * 2 + backSize.height();
_backInner = QRect(QPoint(), backSize).translated(st.pinPosition); _backInner = QRect(QPoint(), backSize);
_backOuter = QRect(0, 0, backWidth, backHeight); _backOuter = QRect(0, 0, backWidth, backHeight);
} }
void Viewport::VideoTile::updateTopControlsPosition() {
const auto &st = st::groupCallVideoTile;
_pinInner = QRect(
_geometry.width() - st.pinPosition.x() - _pinInner.width(),
st.pinPosition.y(),
_pinInner.width(),
_pinInner.height());
_pinOuter = QRect(
_geometry.width() - _pinOuter.width(),
0,
_pinOuter.width(),
_pinOuter.height());
_backInner = QRect(st.pinPosition, _backInner.size());
}
void Viewport::VideoTile::setup(rpl::producer<bool> pinned) { void Viewport::VideoTile::setup(rpl::producer<bool> pinned) {
std::move( std::move(
pinned pinned
@ -211,8 +227,11 @@ void Viewport::VideoTile::setup(rpl::producer<bool> pinned) {
return (_pinned != pinned); return (_pinned != pinned);
}) | rpl::start_with_next([=](bool pinned) { }) | rpl::start_with_next([=](bool pinned) {
_pinned = pinned; _pinned = pinned;
updateTopControlsGeometry(); updateTopControlsSize();
_update(); if (_shown) {
updateTopControlsPosition();
_update();
}
}, _lifetime); }, _lifetime);
_track.track->renderNextFrame( _track.track->renderNextFrame(
@ -229,6 +248,8 @@ void Viewport::VideoTile::setup(rpl::producer<bool> pinned) {
if (const auto size = _track.track->frameSize(); !size.isEmpty()) { if (const auto size = _track.track->frameSize(); !size.isEmpty()) {
_trackSize = size; _trackSize = size;
} }
updateTopControlsSize();
} }
} // namespace Calls::Group } // namespace Calls::Group

View file

@ -38,6 +38,9 @@ public:
[[nodiscard]] QRect geometry() const { [[nodiscard]] QRect geometry() const {
return _geometry; return _geometry;
} }
[[nodiscard]] TileAnimation animation() const {
return _animation;
}
[[nodiscard]] bool pinned() const { [[nodiscard]] bool pinned() const {
return _pinned; return _pinned;
} }
@ -59,8 +62,10 @@ public:
} }
[[nodiscard]] bool screencast() const; [[nodiscard]] bool screencast() const;
void setGeometry(QRect geometry); void setGeometry(
void setShown(bool shown); QRect geometry,
TileAnimation animation = TileAnimation());
void hide();
void toggleTopControlsShown(bool shown); void toggleTopControlsShown(bool shown);
bool updateRequestedQuality(VideoQuality quality); bool updateRequestedQuality(VideoQuality quality);
@ -89,13 +94,15 @@ public:
private: private:
void setup(rpl::producer<bool> pinned); void setup(rpl::producer<bool> pinned);
[[nodiscard]] int topControlsSlide() const; [[nodiscard]] int topControlsSlide() const;
void updateTopControlsGeometry(); void updateTopControlsSize();
void updateTopControlsPosition();
const VideoEndpoint _endpoint; const VideoEndpoint _endpoint;
const Fn<void()> _update; const Fn<void()> _update;
VideoTileTrack _track; VideoTileTrack _track;
QRect _geometry; QRect _geometry;
TileAnimation _animation;
rpl::variable<QSize> _trackSize; rpl::variable<QSize> _trackSize;
QRect _pinOuter; QRect _pinOuter;
QRect _pinInner; QRect _pinInner;