mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Add large / small video animation.
This commit is contained in:
parent
b22363224f
commit
2fe75f8296
9 changed files with 473 additions and 152 deletions
|
@ -972,7 +972,11 @@ void GroupCall::markTrackShown(const VideoEndpoint &endpoint, bool shown) {
|
|||
_videoStreamShownUpdates.fire_copy({ endpoint, shown });
|
||||
}
|
||||
if (shown && changed && endpoint.type == VideoEndpointType::Screen) {
|
||||
pinVideoEndpoint(endpoint);
|
||||
crl::on_main(this, [=] {
|
||||
if (_shownVideoTracks.contains(endpoint)) {
|
||||
pinVideoEndpoint(endpoint);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1189,8 +1189,9 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
|
|||
if (const auto real = _call->lookupReal()) {
|
||||
auto oneFound = false;
|
||||
auto hasTwoOrMore = false;
|
||||
const auto &shown = _call->shownVideoTracks();
|
||||
for (const auto &[endpoint, track] : _call->activeVideoTracks()) {
|
||||
if (_call->shownVideoTracks().contains(endpoint)) {
|
||||
if (shown.contains(endpoint)) {
|
||||
if (oneFound) {
|
||||
hasTwoOrMore = true;
|
||||
break;
|
||||
|
@ -1202,36 +1203,36 @@ base::unique_qptr<Ui::PopupMenu> Members::Controller::createRowContextMenu(
|
|||
if (participant && hasTwoOrMore) {
|
||||
const auto &large = _call->videoEndpointLarge();
|
||||
const auto pinned = _call->videoEndpointPinned();
|
||||
const auto &camera = computeCameraEndpoint(participant);
|
||||
const auto &screen = computeScreenEndpoint(participant);
|
||||
if (!camera.empty()) {
|
||||
if (pinned && large.id == camera) {
|
||||
const auto camera = VideoEndpoint{
|
||||
VideoEndpointType::Camera,
|
||||
participantPeer,
|
||||
computeCameraEndpoint(participant)
|
||||
};
|
||||
const auto screen = VideoEndpoint{
|
||||
VideoEndpointType::Screen,
|
||||
participantPeer,
|
||||
computeScreenEndpoint(participant)
|
||||
};
|
||||
if (shown.contains(camera)) {
|
||||
if (pinned && large == camera) {
|
||||
result->addAction(
|
||||
tr::lng_group_call_context_unpin_camera(tr::now),
|
||||
[=] { _call->pinVideoEndpoint(VideoEndpoint()); });
|
||||
[=] { _call->pinVideoEndpoint({}); });
|
||||
} else {
|
||||
result->addAction(
|
||||
tr::lng_group_call_context_pin_camera(tr::now),
|
||||
[=] { _call->pinVideoEndpoint(VideoEndpoint{
|
||||
VideoEndpointType::Camera,
|
||||
participantPeer,
|
||||
camera });
|
||||
});
|
||||
[=] { _call->pinVideoEndpoint(camera); });
|
||||
}
|
||||
}
|
||||
if (!screen.empty()) {
|
||||
if (pinned && large.id == screen) {
|
||||
if (shown.contains(screen)) {
|
||||
if (pinned && large == screen) {
|
||||
result->addAction(
|
||||
tr::lng_group_call_context_unpin_screen(tr::now),
|
||||
[=] { _call->pinVideoEndpoint(VideoEndpoint()); });
|
||||
[=] { _call->pinVideoEndpoint({}); });
|
||||
} else {
|
||||
result->addAction(
|
||||
tr::lng_group_call_context_pin_screen(tr::now),
|
||||
[=] { _call->pinVideoEndpoint(VideoEndpoint{
|
||||
VideoEndpointType::Screen,
|
||||
participantPeer,
|
||||
screen });
|
||||
});
|
||||
[=] { _call->pinVideoEndpoint(screen); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1359,7 +1359,7 @@ void Panel::chooseShareScreenSource() {
|
|||
}
|
||||
return nullptr;
|
||||
}();
|
||||
if (!screencastFromPeer) {
|
||||
if (!screencastFromPeer || _call->isSharingScreen()) {
|
||||
choose();
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -29,6 +29,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include <QtGui/QOpenGLShader>
|
||||
|
||||
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)
|
||||
: _mode(mode)
|
||||
|
@ -230,7 +247,9 @@ void Viewport::remove(const VideoEndpoint &endpoint) {
|
|||
return;
|
||||
}
|
||||
const auto removing = i->get();
|
||||
if (_large == removing) {
|
||||
const auto largeRemoved = (_large == removing);
|
||||
if (largeRemoved) {
|
||||
prepareLargeChangeAnimation();
|
||||
_large = nullptr;
|
||||
}
|
||||
if (_selected.tile == removing) {
|
||||
|
@ -239,18 +258,278 @@ void Viewport::remove(const VideoEndpoint &endpoint) {
|
|||
if (_pressed.tile == removing) {
|
||||
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);
|
||||
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) {
|
||||
const auto i = ranges::find(_tiles, endpoint, &VideoTile::endpoint);
|
||||
const auto large = (i != end(_tiles)) ? i->get() : nullptr;
|
||||
if (_large != large) {
|
||||
prepareLargeChangeAnimation();
|
||||
_large = large;
|
||||
updateTopControlsVisibility();
|
||||
updateTilesGeometry();
|
||||
startLargeChangeAnimation();
|
||||
}
|
||||
|
||||
Ensures(!_large || !_large->trackSize().isEmpty());
|
||||
}
|
||||
|
||||
void Viewport::updateTilesGeometry() {
|
||||
|
@ -311,111 +590,32 @@ void Viewport::updateTopControlsVisibility() {
|
|||
void Viewport::updateTilesGeometryWide(int outerWidth, int outerHeight) {
|
||||
if (!outerHeight) {
|
||||
return;
|
||||
} else if (_largeChangeAnimation.animating()) {
|
||||
if (_startTilesLayout.outer == QSize(outerWidth, outerHeight)) {
|
||||
return;
|
||||
}
|
||||
_largeChangeAnimation.stop();
|
||||
}
|
||||
|
||||
struct Geometry {
|
||||
QSize size;
|
||||
QRect columns;
|
||||
QRect rows;
|
||||
};
|
||||
auto sizes = base::flat_map<not_null<VideoTile*>, Geometry>();
|
||||
sizes.reserve(_tiles.size());
|
||||
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;
|
||||
_startTilesLayout = countWide(outerWidth, outerHeight);
|
||||
if (_large && !_large->trackSize().isEmpty()) {
|
||||
for (const auto &geometry : _startTilesLayout.list) {
|
||||
if (geometry.tile == _large) {
|
||||
setTileGeometry(_large, { 0, 0, outerWidth, outerHeight });
|
||||
} else {
|
||||
geometry.tile->hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
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.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;
|
||||
} else {
|
||||
const auto field = _startTilesLayout.useColumns
|
||||
? &Geometry::columns
|
||||
: &Geometry::rows;
|
||||
for (const auto &geometry : _startTilesLayout.list) {
|
||||
if (const auto video = geometry.tile) {
|
||||
setTileGeometry(video, geometry.*field);
|
||||
}
|
||||
}
|
||||
}
|
||||
const auto layout = (columnsBlack < rowsBlack)
|
||||
? &Geometry::columns
|
||||
: &Geometry::rows;
|
||||
for (const auto &[video, geometry] : sizes) {
|
||||
setTileGeometry(video, geometry.*layout);
|
||||
}
|
||||
}
|
||||
|
||||
void Viewport::updateTilesGeometryNarrow(int outerWidth) {
|
||||
|
@ -431,7 +631,7 @@ void Viewport::updateTilesGeometryNarrow(int outerWidth) {
|
|||
const auto video = tile.get();
|
||||
const auto size = video->trackSize();
|
||||
if (size.isEmpty()) {
|
||||
setTileGeometry(video, { 0, y, outerWidth, 0 });
|
||||
video->hide();
|
||||
} else {
|
||||
sizes.emplace(video, size);
|
||||
}
|
||||
|
@ -536,7 +736,6 @@ void Viewport::updateTilesGeometryColumn(int outerWidth) {
|
|||
|
||||
void Viewport::setTileGeometry(not_null<VideoTile*> tile, QRect geometry) {
|
||||
tile->setGeometry(geometry);
|
||||
tile->setShown(!geometry.isEmpty());
|
||||
|
||||
const auto min = std::min(geometry.width(), geometry.height());
|
||||
const auto kMedium = style::ConvertScale(480);
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "ui/rp_widget.h"
|
||||
#include "ui/effects/animations.h"
|
||||
|
||||
namespace Ui {
|
||||
class AbstractButton;
|
||||
|
@ -91,6 +92,26 @@ private:
|
|||
class VideoTile;
|
||||
class Renderer;
|
||||
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 {
|
||||
enum class Element {
|
||||
|
@ -122,6 +143,12 @@ private:
|
|||
void refreshHasTwoOrMore();
|
||||
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 setPressed(Selection value);
|
||||
|
||||
|
@ -149,6 +176,9 @@ private:
|
|||
rpl::event_stream<VideoQualityRequest> _qualityRequests;
|
||||
float64 _controlsShownRatio = 1.;
|
||||
VideoTile *_large = nullptr;
|
||||
Ui::Animations::Simple _largeChangeAnimation;
|
||||
Layout _startTilesLayout;
|
||||
Layout _finishTilesLayout;
|
||||
Selection _selected;
|
||||
Selection _pressed;
|
||||
rpl::variable<bool> _mouseInside = false;
|
||||
|
|
|
@ -124,14 +124,36 @@ vec4 background() {
|
|||
: 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(
|
||||
QSize unscaled,
|
||||
QSize size,
|
||||
bool expand,
|
||||
float64 expandRatio,
|
||||
bool swap = false) {
|
||||
const auto scaled = NonEmpty(unscaled.scaled(
|
||||
size,
|
||||
expand ? Qt::KeepAspectRatioByExpanding : Qt::KeepAspectRatio));
|
||||
const auto scaled = InterpolateScaledSize(unscaled, size, expandRatio);
|
||||
const auto left = (size.width() - scaled.width()) / 2;
|
||||
const auto top = (size.height() - scaled.height()) / 2;
|
||||
const auto right = left + scaled.width();
|
||||
|
@ -457,12 +479,17 @@ void Viewport::RendererGL::paintTile(
|
|||
data.rotation);
|
||||
const auto tileSize = geometry.size();
|
||||
const auto swap = (((data.rotation / 90) % 2) == 1);
|
||||
const auto expand = !tile->screencast()
|
||||
&& (!_owner->wide() || UseExpandForCamera(unscaled, tileSize));
|
||||
auto texCoords = CountTexCoords(unscaled, tileSize, expand, swap);
|
||||
auto blurTexCoords = expand
|
||||
const auto expand = isExpanded(tile, unscaled, tileSize);
|
||||
const auto animation = tile->animation();
|
||||
const auto expandRatio = (animation.ratio >= 0.)
|
||||
? countExpandRatio(tile, unscaled, animation)
|
||||
: expand
|
||||
? 1.
|
||||
: 0.;
|
||||
auto texCoords = CountTexCoords(unscaled, tileSize, expandRatio, swap);
|
||||
auto blurTexCoords = (expandRatio == 1.)
|
||||
? texCoords
|
||||
: CountTexCoords(unscaled, tileSize, true);
|
||||
: CountTexCoords(unscaled, tileSize, 1.);
|
||||
const auto rect = transformRect(geometry);
|
||||
auto toBlurTexCoords = std::array<std::array<GLfloat, 2>, 4> { {
|
||||
{ { 0.f, 1.f } },
|
||||
|
@ -763,6 +790,29 @@ void Viewport::RendererGL::prepareObjects(
|
|||
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(
|
||||
QOpenGLFunctions &f,
|
||||
const Webrtc::FrameWithInfo &data,
|
||||
|
|
|
@ -102,6 +102,15 @@ private:
|
|||
not_null<VideoTile*> tile,
|
||||
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;
|
||||
|
||||
GLfloat _factor = 1.;
|
||||
|
|
|
@ -58,13 +58,18 @@ bool Viewport::VideoTile::screencast() const {
|
|||
return (_endpoint.type == VideoEndpointType::Screen);
|
||||
}
|
||||
|
||||
void Viewport::VideoTile::setGeometry(QRect geometry) {
|
||||
void Viewport::VideoTile::setGeometry(
|
||||
QRect geometry,
|
||||
TileAnimation animation) {
|
||||
_shown = true;
|
||||
_geometry = geometry;
|
||||
updateTopControlsGeometry();
|
||||
_animation = animation;
|
||||
updateTopControlsPosition();
|
||||
}
|
||||
|
||||
void Viewport::VideoTile::setShown(bool shown) {
|
||||
_shown = shown;
|
||||
void Viewport::VideoTile::hide() {
|
||||
_shown = false;
|
||||
_quality = std::nullopt;
|
||||
}
|
||||
|
||||
void Viewport::VideoTile::toggleTopControlsShown(bool shown) {
|
||||
|
@ -183,27 +188,38 @@ void Viewport::VideoTile::PaintBackButton(
|
|||
tr::lng_create_group_back(tr::now));
|
||||
}
|
||||
|
||||
void Viewport::VideoTile::updateTopControlsGeometry() {
|
||||
void Viewport::VideoTile::updateTopControlsSize() {
|
||||
const auto &st = st::groupCallVideoTile;
|
||||
|
||||
const auto pinSize = PinInnerSize(_pinned);
|
||||
const auto pinWidth = st.pinPosition.x() * 2 + pinSize.width();
|
||||
const auto pinHeight = st.pinPosition.y() * 2 + pinSize.height();
|
||||
_pinInner = QRect(QPoint(), pinSize).translated(
|
||||
_geometry.width() - st.pinPosition.x() - pinSize.width(),
|
||||
st.pinPosition.y());
|
||||
_pinOuter = QRect(
|
||||
_geometry.width() - pinWidth,
|
||||
0,
|
||||
pinWidth,
|
||||
pinHeight);
|
||||
_pinInner = QRect(QPoint(), pinSize);
|
||||
_pinOuter = QRect(0, 0, pinWidth, pinHeight);
|
||||
|
||||
const auto backSize = BackInnerSize();
|
||||
const auto backWidth = st.pinPosition.x() * 2 + backSize.width();
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
std::move(
|
||||
pinned
|
||||
|
@ -211,8 +227,11 @@ void Viewport::VideoTile::setup(rpl::producer<bool> pinned) {
|
|||
return (_pinned != pinned);
|
||||
}) | rpl::start_with_next([=](bool pinned) {
|
||||
_pinned = pinned;
|
||||
updateTopControlsGeometry();
|
||||
_update();
|
||||
updateTopControlsSize();
|
||||
if (_shown) {
|
||||
updateTopControlsPosition();
|
||||
_update();
|
||||
}
|
||||
}, _lifetime);
|
||||
|
||||
_track.track->renderNextFrame(
|
||||
|
@ -229,6 +248,8 @@ void Viewport::VideoTile::setup(rpl::producer<bool> pinned) {
|
|||
if (const auto size = _track.track->frameSize(); !size.isEmpty()) {
|
||||
_trackSize = size;
|
||||
}
|
||||
|
||||
updateTopControlsSize();
|
||||
}
|
||||
|
||||
} // namespace Calls::Group
|
||||
|
|
|
@ -38,6 +38,9 @@ public:
|
|||
[[nodiscard]] QRect geometry() const {
|
||||
return _geometry;
|
||||
}
|
||||
[[nodiscard]] TileAnimation animation() const {
|
||||
return _animation;
|
||||
}
|
||||
[[nodiscard]] bool pinned() const {
|
||||
return _pinned;
|
||||
}
|
||||
|
@ -59,8 +62,10 @@ public:
|
|||
}
|
||||
|
||||
[[nodiscard]] bool screencast() const;
|
||||
void setGeometry(QRect geometry);
|
||||
void setShown(bool shown);
|
||||
void setGeometry(
|
||||
QRect geometry,
|
||||
TileAnimation animation = TileAnimation());
|
||||
void hide();
|
||||
void toggleTopControlsShown(bool shown);
|
||||
bool updateRequestedQuality(VideoQuality quality);
|
||||
|
||||
|
@ -89,13 +94,15 @@ public:
|
|||
private:
|
||||
void setup(rpl::producer<bool> pinned);
|
||||
[[nodiscard]] int topControlsSlide() const;
|
||||
void updateTopControlsGeometry();
|
||||
void updateTopControlsSize();
|
||||
void updateTopControlsPosition();
|
||||
|
||||
const VideoEndpoint _endpoint;
|
||||
const Fn<void()> _update;
|
||||
|
||||
VideoTileTrack _track;
|
||||
QRect _geometry;
|
||||
TileAnimation _animation;
|
||||
rpl::variable<QSize> _trackSize;
|
||||
QRect _pinOuter;
|
||||
QRect _pinInner;
|
||||
|
|
Loading…
Add table
Reference in a new issue