diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 158b685136..4569ec1a2e 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -1191,14 +1191,14 @@ groupCallLargeVideoWide: GroupCallLargeVideo { controlsAlign: align(top); namePosition: point(15px, 8px); statusPosition: point(15px, 28px); - pinPosition: point(52px, 15px); - iconPosition: point(14px, 15px); + pinPosition: point(52px, 16px); + iconPosition: point(14px, 16px); } groupCallLargeVideoNarrow: GroupCallLargeVideo { shadowHeight: 80px; - controlsAlign: align(top); + controlsAlign: align(bottom); namePosition: point(64px, 44px); - statusPosition: point(64px, 20px); + statusPosition: point(64px, 25px); pinPosition: point(20px, 12px); iconPosition: point(18px, 12px); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp b/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp index 88d19d1d41..26b29ac467 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp @@ -51,6 +51,10 @@ rpl::producer LargeVideo::pinToggled() const { return _pinButton.clicks() | rpl::map([=] { return !_pinned; }); } +rpl::producer LargeVideo::trackSizeValue() const { + return _trackSize.value(); +} + void LargeVideo::setup( rpl::producer track, rpl::producer pinned) { @@ -68,6 +72,7 @@ void LargeVideo::setup( _trackLifetime.destroy(); if (!track.track) { + _trackSize = QSize(); return; } track.track->renderNextFrame( @@ -75,6 +80,8 @@ void LargeVideo::setup( const auto size = track.track->frameSize(); if (size.isEmpty()) { track.track->markFrameShown(); + } else { + _trackSize = size; } _content.update(); }, _trackLifetime); @@ -249,20 +256,20 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { const auto iconLeft = width - _st.iconPosition.x() - icon.width(); const auto iconTop = _topControls ? _st.iconPosition.y() - : (height - _st.iconPosition.y()); + : (height - _st.iconPosition.y() - icon.height()); _track.row->paintMuteIcon( p, { iconLeft, iconTop, icon.width(), icon.height() }, MembersRowStyle::LargeVideo); // Pin. - const auto pinWidth = st::groupCallLargeVideoPin.icon.width(); + const auto &pin = st::groupCallLargeVideoPin.icon; const auto pinLeft = _topControls - ? (width - _st.pinPosition.x() - pinWidth) + ? (width - _st.pinPosition.x() - pin.width()) : _st.pinPosition.x(); const auto pinTop = _topControls ? _st.pinPosition.y() - : (height - _st.pinPosition.y()); + : (height - _st.pinPosition.y() - pin.height()); _pin.paint(p, pinLeft, pinTop, _pinned ? 1. : 0.); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_large_video.h b/Telegram/SourceFiles/calls/group/calls_group_large_video.h index a93d728fdd..e8b635813b 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_large_video.h +++ b/Telegram/SourceFiles/calls/group/calls_group_large_video.h @@ -63,6 +63,7 @@ public: void setGeometry(int x, int y, int width, int height); [[nodiscard]] rpl::producer pinToggled() const; + [[nodiscard]] rpl::producer trackSizeValue() const; [[nodiscard]] rpl::lifetime &lifetime() { return _content.lifetime(); @@ -109,6 +110,7 @@ private: bool _topControls = false; bool _pinned = false; bool _controlsShown = true; + rpl::variable _trackSize; rpl::lifetime _trackLifetime; }; diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index 304da6f71d..a04271efe1 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -1457,7 +1457,19 @@ Members::Members( , _listController(std::make_unique(call, parent)) , _layout(_scroll->setOwnedWidget( object_ptr(_scroll.data()))) -, _pinnedVideo(_layout->add(object_ptr(_layout.get()))) { +, _pinnedVideoWrap(_layout->add(object_ptr(_layout.get()))) +, _pinnedVideo( + std::make_unique( + _pinnedVideoWrap.get(), + st::groupCallLargeVideoNarrow, + true, + _call->videoLargeTrackValue( + ) | rpl::map([=](GroupCall::LargeTrack track) { + const auto row = track ? lookupRow(track.peer) : nullptr; + Assert(!track || row != nullptr); + return LargeVideoTrack{ row ? track.track : nullptr, row }; + }), + _call->videoEndpointPinnedValue())) { setupAddMember(call); setupList(); setupPinnedVideo(); @@ -1485,7 +1497,7 @@ auto Members::kickParticipantRequests() const int Members::desiredHeight() const { const auto addMember = _addMemberButton.current(); - const auto top = _pinnedVideo->height() + const auto top = _pinnedVideoWrap->height() + (addMember ? addMember->height() : 0); const auto count = [&] { if (const auto real = _call->lookupReal()) { @@ -1602,6 +1614,7 @@ void Members::setMode(PanelMode mode) { return; } _mode = mode; + _pinnedVideo->setVisible(mode == PanelMode::Default); _list->setMode((mode == PanelMode::Wide) ? PeerListContent::Mode::Custom : PeerListContent::Mode::Default); @@ -1635,6 +1648,15 @@ void Members::setupList() { void Members::setupPinnedVideo() { using namespace rpl::mappers; + _pinnedVideo->pinToggled( + ) | rpl::start_with_next([=](bool pinned) { + if (!pinned) { + _call->pinVideoEndpoint(VideoEndpoint{}); + } else if (const auto &large = _call->videoEndpointLarge()) { + _call->pinVideoEndpoint(large); + } + }, _pinnedVideo->lifetime()); + // New video was pinned or mode changed. rpl::merge( _mode.changes() | rpl::filter( @@ -1646,102 +1668,22 @@ void Members::setupPinnedVideo() { }, _scroll->lifetime()); rpl::combine( - _mode.value(), - _call->videoLargeTrackValue() - ) | rpl::map([](PanelMode mode, GroupCall::LargeTrack track) { - return (mode == PanelMode::Default) ? track.track : nullptr; - }) | rpl::distinct_until_changed( - ) | rpl::start_with_next([=](Webrtc::VideoTrack *track) { - _pinnedTrackLifetime.destroy(); - if (!track) { - _pinnedVideo->resize(_pinnedVideo->width(), 0); + _layout->widthValue(), + _pinnedVideo->trackSizeValue() + ) | rpl::start_with_next([=](int width, QSize size) { + if (size.isEmpty() || !width) { + _pinnedVideoWrap->resize(width, 0); return; } - const auto frameSize = _pinnedTrackLifetime.make_state(); - const auto applyFrameSize = [=](QSize size) { - const auto width = _pinnedVideo->width(); - if (size.isEmpty() || !width) { - return; - } - const auto heightMin = (width * 9) / 16; - const auto heightMax = (width * 3) / 4; - const auto scaled = size.scaled( - QSize(width, heightMax), - Qt::KeepAspectRatio); - _pinnedVideo->resize( - width, - std::max(scaled.height(), heightMin)); - }; - track->renderNextFrame( - ) | rpl::start_with_next([=] { - const auto size = track->frameSize(); - if (size.isEmpty()) { - track->markFrameShown(); - } else { - if (*frameSize != size) { - *frameSize = size; - applyFrameSize(size); - } - _pinnedVideo->update(); - } - }, _pinnedTrackLifetime); - - _layout->widthValue( - ) | rpl::start_with_next([=] { - applyFrameSize(track->frameSize()); - }, _pinnedTrackLifetime); - - _pinnedVideo->paintRequest( - ) | rpl::start_with_next([=] { - const auto [image, rotation] - = track->frameOriginalWithRotation(); - if (image.isNull()) { - return; - } - auto p = QPainter(_pinnedVideo); - auto hq = PainterHighQualityEnabler(p); - using namespace Media::View; - const auto size = _pinnedVideo->size(); - const auto scaled = FlipSizeByRotation( - image.size(), - rotation - ).scaled(size, Qt::KeepAspectRatio); - const auto left = (size.width() - scaled.width()) / 2; - const auto top = (size.height() - scaled.height()) / 2; - const auto target = QRect(QPoint(left, top), scaled); - if (UsePainterRotation(rotation)) { - if (rotation) { - p.save(); - p.rotate(rotation); - } - p.drawImage(RotatedRect(target, rotation), image); - if (rotation) { - p.restore(); - } - } else if (rotation) { - p.drawImage(target, RotateFrameImage(image, rotation)); - } else { - p.drawImage(target, image); - } - if (left > 0) { - p.fillRect(0, 0, left, size.height(), Qt::black); - } - if (const auto right = left + scaled.width() - ; right < size.width()) { - const auto fill = size.width() - right; - p.fillRect(right, 0, fill, size.height(), Qt::black); - } - if (top > 0) { - p.fillRect(0, 0, size.width(), top, Qt::black); - } - if (const auto bottom = top + scaled.height() - ; bottom < size.height()) { - const auto fill = size.height() - bottom; - p.fillRect(0, bottom, size.width(), fill, Qt::black); - } - track->markFrameShown(); - }, _pinnedTrackLifetime); - }, lifetime()); + const auto heightMin = (width * 9) / 16; + const auto heightMax = (width * 3) / 4; + const auto scaled = size.scaled( + QSize(width, heightMax), + Qt::KeepAspectRatio); + const auto height = std::max(scaled.height(), heightMin); + _pinnedVideoWrap->resize(width, height); + _pinnedVideo->setGeometry(0, 0, width, height); + }, _pinnedVideo->lifetime()); } void Members::resizeEvent(QResizeEvent *e) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.h b/Telegram/SourceFiles/calls/group/calls_group_members.h index 7a830b68a1..f60dcaf00b 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.h +++ b/Telegram/SourceFiles/calls/group/calls_group_members.h @@ -26,6 +26,7 @@ class GroupCall; namespace Calls::Group { +class LargeVideo; class MembersRow; struct VolumeRequest; struct MuteRequest; @@ -91,7 +92,8 @@ private: object_ptr _scroll; std::unique_ptr _listController; not_null _layout; - const not_null _pinnedVideo; + const not_null _pinnedVideoWrap; + const std::unique_ptr _pinnedVideo; rpl::variable _addMemberButton = nullptr; ListWidget *_list = nullptr; rpl::event_stream<> _addMemberRequests;