diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index 4569ec1a2e..e5e2e3ebd1 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -1139,7 +1139,7 @@ groupCallNarrowNameTop: 65px; groupCallNarrowIconTop: 62px; groupCallNarrowIconLess: 5px; groupCallWideModeWidthMin: 550px; -groupCallWideModeSize: size(720px, 480px); +groupCallWideModeSize: size(960px, 580px); groupCallNarrowAddMemberHeight: 32px; groupCallNarrowOutline: 2px; groupCallNarrowShadowHeight: 36px; @@ -1184,6 +1184,7 @@ GroupCallLargeVideo { statusPosition: point; pinPosition: point; iconPosition: point; + minimizePosition: point; } groupCallLargeVideoWide: GroupCallLargeVideo { @@ -1193,6 +1194,7 @@ groupCallLargeVideoWide: GroupCallLargeVideo { statusPosition: point(15px, 28px); pinPosition: point(52px, 16px); iconPosition: point(14px, 16px); + minimizePosition: point(94px, 16px); } groupCallLargeVideoNarrow: GroupCallLargeVideo { shadowHeight: 80px; @@ -1216,3 +1218,5 @@ groupCallLargeVideoPin: CrossLineAnimation { endPosition: point(20px, 17px); stroke: 2px; } +groupCallVideoEnlarge: icon {{ "calls/voice_enlarge", mediaviewPipControlsFgOver }}; +groupCallVideoMinimize: icon {{ "calls/voice_minimize", groupCallVideoSubTextFg }}; diff --git a/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp b/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp index 95074e62c2..92ee09a504 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp @@ -30,6 +30,9 @@ LargeVideo::LargeVideo( , _st(st) , _pin(st::groupCallLargeVideoPin) , _pinButton(&_content) +, _minimizeButton((_st.controlsAlign == style::al_top) + ? std::make_unique(&_content) + : nullptr) , _controlsShown(_st.controlsAlign == style::al_top) , _topControls(_st.controlsAlign == style::al_top) , _controlsShownRatio(_controlsShown.current() ? 1. : 0.) { @@ -64,6 +67,12 @@ rpl::producer LargeVideo::pinToggled() const { return _pinButton.clicks() | rpl::map([=] { return !_pinned; }); } +rpl::producer<> LargeVideo::minimizeClicks() const { + return _minimizeButton + ? (_minimizeButton->clicks() | rpl::to_empty) + : (rpl::never() | rpl::type_erased()); +} + rpl::producer LargeVideo::controlsShown() const { return _controlsShownRatio.value(); } @@ -86,6 +95,18 @@ void LargeVideo::setup( Ui::Integration::Instance().unregisterLeaveSubscription( &_content); setControlsShown(false); + } else if (e->type() == QEvent::MouseButtonPress + && static_cast( + e.get())->button() == Qt::LeftButton) { + _mouseDown = true; + } else if (e->type() == QEvent::MouseButtonRelease + && static_cast( + e.get())->button() == Qt::LeftButton + && _mouseDown) { + _mouseDown = false; + if (!_content.isHidden()) { + _clicks.fire({}); + } } }, _content.lifetime()); @@ -118,6 +139,9 @@ void LargeVideo::setup( } _content.update(); }, _trackLifetime); + if (const auto size = track.track->frameSize(); !size.isEmpty()) { + _trackSize = size; + } }, _content.lifetime()); setupControls(std::move(pinned)); @@ -147,7 +171,7 @@ void LargeVideo::toggleControls() { callback, shown ? 0. : 1., shown ? 1. : 0., - 140); + st::slideWrapDuration); } } @@ -166,18 +190,26 @@ void LargeVideo::setupControls(rpl::producer pinned) { void LargeVideo::updateControlsGeometry() { if (_topControls) { const auto &pin = st::groupCallLargeVideoPin.icon; - const auto pinWidth = pin.width(); const auto pinRight = (_content.width() - _st.pinPosition.x()); + const auto pinLeft = pinRight - pin.width(); const auto pinTop = _st.pinPosition.y(); const auto &icon = st::groupCallLargeVideoCrossLine.icon; const auto iconLeft = _content.width() - _st.iconPosition.x() - icon.width(); - const auto skip = iconLeft - pinRight; + const auto skip1 = iconLeft - pinRight; + const auto &min = st::groupCallVideoMinimize; + const auto minRight = _content.width() - _st.minimizePosition.x(); + const auto skip2 = pinLeft - minRight; _pinButton.setGeometry( - pinRight - pin.width() - (skip / 2), + pinLeft - (skip2 / 2), 0, - pin.width() + skip, + pin.width() + (skip2 / 2) + (skip1 / 2), + pinTop * 2 + pin.height()); + _minimizeButton->setGeometry( + minRight - min.width() - (skip2 / 2), + 0, + min.width() + skip2, pinTop * 2 + pin.height()); } else { _pinButton.setGeometry( @@ -271,11 +303,11 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { 0, (_topControls ? anim::interpolate(-_st.shadowHeight, 0, shown) - : (height - _st.shadowHeight)), + : (height - anim::interpolate(_st.shadowHeight, 0, shown))), width, _st.shadowHeight); const auto shadowFill = shadowRect.intersected(clip); - if (shadowFill.isEmpty()) { + if (shadowFill.isEmpty() && (_topControls || shown == 0.)) { return; } const auto factor = style::DevicePixelRatio(); @@ -287,6 +319,16 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { (shadowFill.y() - shadowRect.y()) * factor, _shadow.width(), shadowFill.height() * factor)); + if (!_topControls && shown > 0.) { + auto color = st::radialBg->c; + color.setAlphaF(color.alphaF() * shown); + p.fillRect(clip, color); + + p.setOpacity(shown); + st::groupCallVideoEnlarge.paintInCenter(p, _content.rect()); + p.setOpacity(1.); + } + _track.row->lazyInitialize(st::groupCallMembersListItem); // Name. @@ -338,6 +380,14 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { ? (_st.pinPosition.y() + shift) : (height - _st.pinPosition.y() - pin.height()); _pin.paint(p, pinLeft, pinTop, _pinned ? 1. : 0.); + + // Minimize. + if (_topControls) { + const auto &min = st::groupCallVideoMinimize; + const auto minLeft = width - _st.minimizePosition.x() - min.width(); + const auto minTop = _st.minimizePosition.y() + shift; + min.paint(p, minLeft, minTop, width); + } } QImage GenerateShadow(int height, int topAlpha, int bottomAlpha) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_large_video.h b/Telegram/SourceFiles/calls/group/calls_group_large_video.h index 9389724f26..902f680cf8 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_large_video.h +++ b/Telegram/SourceFiles/calls/group/calls_group_large_video.h @@ -64,8 +64,12 @@ public: void setControlsShown(bool shown); [[nodiscard]] rpl::producer pinToggled() const; + [[nodiscard]] rpl::producer<> minimizeClicks() const; [[nodiscard]] rpl::producer controlsShown() const; [[nodiscard]] rpl::producer trackSizeValue() const; + [[nodiscard]] rpl::producer<> clicks() const { + return _clicks.events(); + } [[nodiscard]] rpl::lifetime &lifetime() { return _content.lifetime(); @@ -109,11 +113,14 @@ private: QImage _shadow; Ui::CrossLineAnimation _pin; Ui::AbstractButton _pinButton; + std::unique_ptr _minimizeButton; Ui::Animations::Simple _controlsAnimation; rpl::variable _controlsShown = true; + rpl::event_stream<> _clicks; const bool _topControls = true; bool _pinned = false; bool _mouseInside = false; + bool _mouseDown = false; bool _toggleControlsScheduled = false; rpl::variable _controlsShownRatio = 1.; rpl::variable _trackSize; diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.cpp b/Telegram/SourceFiles/calls/group/calls_group_members.cpp index a04271efe1..e19e387072 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_members.cpp @@ -1605,6 +1605,10 @@ void Members::setupAddMember(not_null call) { }, lifetime()); } +rpl::producer<> Members::enlargeVideo() const { + return _pinnedVideo->clicks(); +} + Row *Members::lookupRow(not_null peer) const { return _listController->findRow(peer); } @@ -1681,8 +1685,8 @@ void Members::setupPinnedVideo() { QSize(width, heightMax), Qt::KeepAspectRatio); const auto height = std::max(scaled.height(), heightMin); - _pinnedVideoWrap->resize(width, height); _pinnedVideo->setGeometry(0, 0, width, height); + _pinnedVideoWrap->resize(width, height); }, _pinnedVideo->lifetime()); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_members.h b/Telegram/SourceFiles/calls/group/calls_group_members.h index f60dcaf00b..3ab0a19b3f 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_members.h +++ b/Telegram/SourceFiles/calls/group/calls_group_members.h @@ -53,6 +53,7 @@ public: [[nodiscard]] rpl::producer<> addMembersRequests() const { return _addMemberRequests.events(); } + [[nodiscard]] rpl::producer<> enlargeVideo() const; [[nodiscard]] MembersRow *lookupRow(not_null peer) const; diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index cb905dc18b..8e1701ccf0 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -56,6 +56,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include +#include namespace Calls::Group { namespace { @@ -970,6 +971,79 @@ void Panel::setupMembers() { addMembers(); } }, _callLifetime); + + _members->enlargeVideo( + ) | rpl::start_with_next([=] { + enlargeVideo(); + }, _callLifetime); +} + +void Panel::enlargeVideo() { + _lastSmallGeometry = _window->geometry(); + + const auto available = _window->screen()->availableGeometry(); + const auto width = std::max( + _window->width(), + std::max( + std::min(available.width(), st::groupCallWideModeSize.width()), + st::groupCallWideModeWidthMin)); + const auto height = std::max( + _window->height(), + std::min(available.height(), st::groupCallWideModeSize.height())); + auto geometry = QRect( + _window->x() - (width - _window->width()) / 2, + _window->y() - (height - _window->height()) / 2, + width, + height); + if (geometry.x() < available.x()) { + geometry.setX(std::min(available.x(), _window->x())); + } + if (geometry.x() + geometry.width() + > available.x() + available.width()) { + geometry.setX(std::max( + available.x() + available.width(), + _window->x() + _window->width()) - geometry.width()); + } + if (geometry.y() < available.y()) { + geometry.setY(std::min(available.y(), _window->y())); + } + if (geometry.y() + geometry.height() > available.y() + available.height()) { + geometry.setY(std::max( + available.y() + available.height(), + _window->y() + _window->height()) - geometry.height()); + } + if (_lastLargeMaximized) { + _window->setWindowState( + _window->windowState() | Qt::WindowMaximized); + } else { + _window->setGeometry((_lastLargeGeometry + && available.intersects(*_lastLargeGeometry)) + ? *_lastLargeGeometry + : geometry); + } +} + +void Panel::minimizeVideo() { + if (_window->windowState() & Qt::WindowMaximized) { + _lastLargeMaximized = true; + _window->setWindowState( + _window->windowState() & ~Qt::WindowMaximized); + } else { + _lastLargeMaximized = false; + _lastLargeGeometry = _window->geometry(); + } + const auto available = _window->screen()->availableGeometry(); + const auto width = st::groupCallWidth; + const auto height = st::groupCallHeight; + auto geometry = QRect( + _window->x() + (_window->width() - width) / 2, + _window->y() + (_window->height() - height) / 2, + width, + height); + _window->setGeometry((_lastSmallGeometry + && available.intersects(*_lastSmallGeometry)) + ? *_lastSmallGeometry + : geometry); } void Panel::raiseControls() { @@ -1008,6 +1082,10 @@ void Panel::setupPinnedVideo() { visible, std::move(track), _call->videoEndpointPinnedValue()); + _pinnedVideo->minimizeClicks( + ) | rpl::start_with_next([=] { + minimizeVideo(); + }, _pinnedVideo->lifetime()); _pinnedVideo->pinToggled( ) | rpl::start_with_next([=](bool pinned) { if (!pinned) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.h b/Telegram/SourceFiles/calls/group/calls_group_panel.h index 7777f815f9..3ad5d7570f 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.h @@ -93,6 +93,8 @@ private: void startScheduledNow(); void trackControls(); void raiseControls(); + void enlargeVideo(); + void minimizeVideo(); bool updateMode(); void updateControlsGeometry(); @@ -153,6 +155,9 @@ private: object_ptr _startsWhen = { nullptr }; ChooseJoinAsProcess _joinAsProcess; rpl::variable _videoMode; + std::optional _lastSmallGeometry; + std::optional _lastLargeGeometry; + bool _lastLargeMaximized = false; object_ptr _controlsBackground = { nullptr }; object_ptr _settings = { nullptr };