diff --git a/Telegram/SourceFiles/calls/calls.style b/Telegram/SourceFiles/calls/calls.style index bdd02cc73f..a8f61b5958 100644 --- a/Telegram/SourceFiles/calls/calls.style +++ b/Telegram/SourceFiles/calls/calls.style @@ -739,6 +739,7 @@ groupCallJoinAsToggle: UserpicButton(defaultUserpicButton) { photoPosition: point(3px, 3px); } groupCallMenuPosition: point(-1px, 29px); +groupCallWideMenuPosition: point(-2px, 28px); groupCallActiveButton: IconButton { width: 36px; @@ -848,6 +849,14 @@ groupCallScreenShareSmall: CallButton(groupCallSettingsSmall) { rippleAreaPosition: point(8px, 12px); } } +groupCallMenuToggleSmall: CallButton(groupCallSettingsSmall) { + button: IconButton(groupCallSettingsInner) { + icon: icon {{ "calls/calls_more", groupCallIconFg }}; + width: 60px; + height: 68px; + rippleAreaPosition: point(8px, 12px); + } +} groupCallButtonSkip: 40px; groupCallButtonSkipSmall: 5px; groupCallButtonBottomSkip: 113px; diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.cpp b/Telegram/SourceFiles/calls/group/calls_group_call.cpp index 069819272f..899952252d 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_call.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_chat.h" #include "data/data_channel.h" #include "data/data_group_call.h" +#include "data/data_peer_values.h" #include "data/data_session.h" #include "base/global_shortcuts.h" #include "base/openssl_help.h" @@ -364,6 +365,7 @@ GroupCall::GroupCall( , _joinAs(info.joinAs) , _possibleJoinAs(std::move(info.possibleJoinAs)) , _joinHash(info.joinHash) +, _canManage(Data::CanManageGroupCallValue(_peer)) , _id(inputCall.c_inputGroupCall().vid().v) , _scheduleDate(info.scheduleDate) , _lastSpokeCheckTimer([=] { checkLastSpoke(); }) @@ -399,7 +401,7 @@ GroupCall::GroupCall( if (const auto real = lookupReal()) { subscribeToReal(real); - if (!_peer->canManageGroupCall() && real->joinMuted()) { + if (!canManage() && real->joinMuted()) { _muted = MuteState::ForceMuted; } } else { @@ -472,6 +474,14 @@ bool GroupCall::mutedByAdmin() const { return mute == MuteState::ForceMuted || mute == MuteState::RaisedHand; } +bool GroupCall::canManage() const { + return _canManage.current(); +} + +rpl::producer GroupCall::canManageValue() const { + return _canManage.value(); +} + void GroupCall::toggleVideo(bool active) { if (!_instance || !_id @@ -758,6 +768,7 @@ void GroupCall::join(const MTPInputGroupCall &inputCall) { _peerStream.events_starting_with_copy(_peer)); SubscribeToMigration(_peer, _lifetime, [=](not_null group) { _peer = group; + _canManage = Data::CanManageGroupCallValue(_peer); _peerStream.fire_copy(group); }); } @@ -1124,7 +1135,7 @@ void GroupCall::applyParticipantLocally( if (!participant || !participant->ssrc) { return; } - const auto canManageCall = _peer->canManageGroupCall(); + const auto canManageCall = canManage(); const auto isMuted = participant->muted || (mute && canManageCall); const auto canSelfUnmute = !canManageCall ? participant->canSelfUnmute @@ -1705,7 +1716,6 @@ void GroupCall::ensureControllerCreated() { const auto weak = base::make_weak(&_instanceGuard); const auto myLevel = std::make_shared(); - _videoCall = true; tgcalls::GroupInstanceDescriptor descriptor = { .threads = tgcalls::StaticThreads::getThreads(), .config = tgcalls::GroupConfig{ diff --git a/Telegram/SourceFiles/calls/group/calls_group_call.h b/Telegram/SourceFiles/calls/group/calls_group_call.h index 78f212009a..9e15e760e6 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_call.h +++ b/Telegram/SourceFiles/calls/group/calls_group_call.h @@ -238,13 +238,6 @@ public: return _muted.value(); } - [[nodiscard]] bool videoCall() const { - return _videoCall.current(); - } - [[nodiscard]] rpl::producer videoCallValue() const { - return _videoCall.value(); - } - [[nodiscard]] auto otherParticipantStateValue() const -> rpl::producer; @@ -328,6 +321,9 @@ public: static constexpr auto kSpeakLevelThreshold = 0.2; [[nodiscard]] bool mutedByAdmin() const; + [[nodiscard]] bool canManage() const; + [[nodiscard]] rpl::producer canManageValue() const; + void setCurrentAudioDevice(bool input, const QString &deviceId); void setCurrentVideoDevice(const QString &deviceId); [[nodiscard]] bool isSharingScreen() const; @@ -491,7 +487,7 @@ private: QString _joinHash; rpl::variable _muted = MuteState::Muted; - rpl::variable _videoCall = false; + rpl::variable _canManage = false; bool _initialMuteStateSent = false; bool _acceptFields = false; diff --git a/Telegram/SourceFiles/calls/group/calls_group_menu.cpp b/Telegram/SourceFiles/calls/group/calls_group_menu.cpp index 081b15fdba..ae3aa2aaef 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_menu.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_menu.cpp @@ -590,7 +590,9 @@ void FillMenu( not_null menu, not_null peer, not_null call, + bool wide, Fn chooseJoinAs, + Fn chooseShareScreenSource, Fn)> showBox) { const auto weak = base::make_weak(call.get()); const auto resolveReal = [=] { @@ -606,9 +608,10 @@ void FillMenu( } const auto addEditJoinAs = call->showChooseJoinAs(); - const auto addEditTitle = peer->canManageGroupCall(); - const auto addEditRecording = peer->canManageGroupCall() - && !real->scheduleDate(); + const auto addEditTitle = call->canManage(); + const auto addEditRecording = call->canManage() && !real->scheduleDate(); + const auto addScreenCast = !wide + && (real->canStartVideo() || call->isSharingScreen()); if (addEditJoinAs) { menu->addAction(MakeJoinAsAction( menu->menu(), @@ -660,6 +663,23 @@ void FillMenu( real->recordStartDateValue(), handler)); } + if (addScreenCast) { + const auto sharing = call->isSharingScreen(); + const auto toggle = [=] { + if (const auto strong = weak.get()) { + if (sharing) { + strong->toggleScreenSharing(std::nullopt); + } else { + chooseShareScreenSource(); + } + } + }; + menu->addAction( + (call->isSharingScreen() + ? tr::lng_group_call_screen_share_stop(tr::now) + : tr::lng_group_call_screen_share_start(tr::now)), + toggle); + } menu->addAction(tr::lng_group_call_settings(tr::now), [=] { if (const auto strong = weak.get()) { showBox(Box(SettingsBox, strong)); @@ -677,8 +697,12 @@ void FillMenu( menu->addAction(MakeAttentionAction( menu->menu(), (real->scheduleDate() - ? tr::lng_group_call_cancel(tr::now) - : tr::lng_group_call_end(tr::now)), + ? (call->canManage() + ? tr::lng_group_call_cancel(tr::now) + : tr::lng_group_call_leave(tr::now)) + : (call->canManage() + ? tr::lng_group_call_end(tr::now) + : tr::lng_group_call_leave(tr::now))), finish)); } diff --git a/Telegram/SourceFiles/calls/group/calls_group_menu.h b/Telegram/SourceFiles/calls/group/calls_group_menu.h index 4849744935..0cb70f3ee2 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_menu.h +++ b/Telegram/SourceFiles/calls/group/calls_group_menu.h @@ -61,7 +61,9 @@ void FillMenu( not_null menu, not_null peer, not_null call, + bool wide, Fn chooseJoinAs, + Fn chooseShareScreenSource, Fn)> showBox); [[nodiscard]] base::unique_qptr MakeAttentionAction( diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index 96ba47226a..e822089d34 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -38,7 +38,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_group_call.h" #include "data/data_session.h" #include "data/data_changes.h" -#include "data/data_peer_values.h" #include "main/main_session.h" #include "base/event_filter.h" #include "boxes/peers/edit_participants_box.h" @@ -555,9 +554,7 @@ void Panel::initWindow() { : Flag::None; }); - rpl::combine( - _call->hasVideoWithFramesValue(), - _call->videoCallValue() + _call->hasVideoWithFramesValue( ) | rpl::start_with_next([=] { updateMode(); }, _window->lifetime()); @@ -584,7 +581,7 @@ void Panel::initWidget() { } void Panel::endCall() { - if (!_call->peer()->canManageGroupCall()) { + if (!_call->canManage()) { _call->hangup(); return; } @@ -626,7 +623,7 @@ void Panel::initControls() { return (button == Qt::LeftButton); }) | rpl::start_with_next([=] { if (_call->scheduleDate()) { - if (_peer->canManageGroupCall()) { + if (_call->canManage()) { startScheduledNow(); } else if (const auto real = _call->lookupReal()) { _call->toggleScheduleStartSubscribed( @@ -649,6 +646,13 @@ void Panel::initControls() { refreshLeftButton(); refreshVideoButtons(); + rpl::combine( + _mode.value(), + _call->canManageValue() + ) | rpl::start_with_next([=] { + refreshTopButton(); + }, widget()->lifetime()); + _hangup->setClickedCallback([=] { endCall(); }); const auto scheduleDate = _call->scheduleDate(); @@ -730,6 +734,7 @@ void Panel::refreshLeftButton() { const auto raw = _callShare ? _callShare.data() : _settings.data(); raw->show(); raw->setColorOverrides(_mute->colorOverrides()); + updateButtonsStyles(); } void Panel::refreshVideoButtons(std::optional overrideWideMode) { @@ -783,7 +788,7 @@ void Panel::refreshVideoButtons(std::optional overrideWideMode) { _screenShare.create(widget(), st::groupCallScreenShareSmall); _screenShare->show(); _screenShare->setClickedCallback([=] { - Ui::DesktopCapture::ChooseSource(this); + chooseShareScreenSource(); }); _screenShare->setColorOverrides( toggleableOverrides(_call->isSharingScreenValue())); @@ -792,6 +797,13 @@ void Panel::refreshVideoButtons(std::optional overrideWideMode) { _screenShare->setProgress(sharing ? 1. : 0.); }, _screenShare->lifetime()); } + if (!_wideMenu) { + _wideMenu.create(widget(), st::groupCallMenuToggleSmall); + _wideMenu->show(); + _wideMenu->setClickedCallback([=] { showMainMenu(); }); + _wideMenu->setColorOverrides( + toggleableOverrides(_wideMenuShown.value())); + } updateButtonsStyles(); updateButtonsGeometry(); } @@ -825,7 +837,7 @@ void Panel::setupRealMuteButtonState(not_null real) { _call->instanceStateValue(), real->scheduleDateValue(), real->scheduleStartSubscribedValue(), - Data::CanManageGroupCallValue(_peer), + _call->canManageValue(), _mode.value() ) | rpl::distinct_until_changed( ) | rpl::filter( @@ -1103,6 +1115,7 @@ void Panel::raiseControls() { &_settings, &_callShare, &_screenShare, + &_wideMenu, &_video, &_hangup }; @@ -1323,16 +1336,37 @@ void Panel::subscribeToChanges(not_null real) { }, widget()->lifetime()); validateRecordingMark(real->recordStartDate() != 0); - const auto showMenu = _peer->canManageGroupCall(); - const auto showUserpic = !showMenu && _call->showChooseJoinAs(); - if (showMenu) { + real->canStartVideoValue( + ) | rpl::start_with_next([=] { + refreshVideoButtons(); + refreshTopButton(); + }, widget()->lifetime()); + + updateControlsGeometry(); +} + +void Panel::refreshTopButton() { + if (_mode.current() == PanelMode::Wide) { + _menuToggle.destroy(); + _joinAsToggle.destroy(); + updateButtonsGeometry(); // _wideMenu <-> _settings + return; + } + const auto real = _call->lookupReal(); + const auto hasJoinAs = _call->showChooseJoinAs(); + const auto wide = (_mode.current() == PanelMode::Wide); + const auto showNarrowMenu = _call->canManage() + || (real && real->canStartVideo()); + const auto showNarrowUserpic = !showNarrowMenu && hasJoinAs; + if (showNarrowMenu) { _joinAsToggle.destroy(); if (!_menuToggle) { _menuToggle.create(widget(), st::groupCallMenuToggle); _menuToggle->show(); _menuToggle->setClickedCallback([=] { showMainMenu(); }); + updateControlsGeometry(); } - } else if (showUserpic) { + } else if (showNarrowUserpic) { _menuToggle.destroy(); rpl::single( _call->joinAs() @@ -1357,12 +1391,10 @@ void Panel::subscribeToChanges(not_null real) { _menuToggle.destroy(); _joinAsToggle.destroy(); } - real->canStartVideoValue( - ) | rpl::start_with_next([=] { - refreshVideoButtons(); - }, widget()->lifetime()); +} - updateControlsGeometry(); +void Panel::chooseShareScreenSource() { + Ui::DesktopCapture::ChooseSource(this); } void Panel::chooseJoinAs() { @@ -1392,14 +1424,21 @@ void Panel::showMainMenu() { if (_menu) { return; } + const auto wide = (_mode.current() == PanelMode::Wide) && _wideMenu; + if (!wide && !_menuToggle) { + return; + } _menu.create(widget(), st::groupCallDropdownMenu); FillMenu( _menu.data(), _peer, _call, + wide, [=] { chooseJoinAs(); }, + [=] { chooseShareScreenSource(); }, [=](auto box) { _layerBg->showBox(std::move(box)); }); if (_menu->empty()) { + _wideMenuShown = false; _menu.destroy(); return; } @@ -1409,29 +1448,51 @@ void Panel::showMainMenu() { raw->deleteLater(); if (_menu == raw) { _menu = nullptr; - _menuToggle->setForceRippled(false); + _wideMenuShown = false; + _trackControlsMenuLifetime.destroy(); + if (_menuToggle) { + _menuToggle->setForceRippled(false); + } } }); raw->setShowStartCallback([=] { if (_menu == raw) { - _menuToggle->setForceRippled(true); + if (wide) { + _wideMenuShown = true; + } else if (_menuToggle) { + _menuToggle->setForceRippled(true); + } } }); raw->setHideStartCallback([=] { if (_menu == raw) { - _menuToggle->setForceRippled(false); + _wideMenuShown = false; + if (_menuToggle) { + _menuToggle->setForceRippled(false); + } } }); - _menuToggle->installEventFilter(_menu); - const auto x = st::groupCallMenuPosition.x(); - const auto y = st::groupCallMenuPosition.y(); - if (_menuToggle->x() > widget()->width() / 2) { - _menu->moveToRight(x, y); - _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight); + if (wide) { + _wideMenu->installEventFilter(_menu); + const auto x = st::groupCallWideMenuPosition.x(); + const auto y = st::groupCallWideMenuPosition.y(); + _menu->moveToLeft( + _wideMenu->x() + x, + _wideMenu->y() - _menu->height() + y); + _menu->showAnimated(Ui::PanelAnimation::Origin::BottomLeft); + trackControl(_menu, _trackControlsMenuLifetime); } else { - _menu->moveToLeft(x, y); - _menu->showAnimated(Ui::PanelAnimation::Origin::TopLeft); + _menuToggle->installEventFilter(_menu); + const auto x = st::groupCallMenuPosition.x(); + const auto y = st::groupCallMenuPosition.y(); + if (_menuToggle->x() > widget()->width() / 2) { + _menu->moveToRight(x, y); + _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight); + } else { + _menu->moveToLeft(x, y); + _menu->showAnimated(Ui::PanelAnimation::Origin::TopLeft); + } } } @@ -1884,6 +1945,20 @@ void Panel::setupControlsBackgroundWide() { trackControls(true); } +template +void Panel::trackControl(WidgetPointer &widget, rpl::lifetime &lifetime) { + if (widget) { + widget->events( + ) | rpl::start_with_next([=](not_null e) { + if (e->type() == QEvent::Enter) { + toggleWideControls(true); + } else if (e->type() == QEvent::Leave) { + toggleWideControls(false); + } + }, lifetime); + } +} + void Panel::trackControls(bool track) { if (_trackControls == track) { return; @@ -1892,6 +1967,7 @@ void Panel::trackControls(bool track) { if (!track) { _trackControlsLifetime.destroy(); _trackControlsOverStateLifetime.destroy(); + _trackControlsMenuLifetime.destroy(); toggleWideControls(true); if (_wideControlsAnimation.animating()) { _wideControlsAnimation.stop(); @@ -1901,24 +1977,16 @@ void Panel::trackControls(bool track) { } const auto trackOne = [=](auto &&widget) { - if (widget) { - const auto raw = &*widget; - raw->events( - ) | rpl::start_with_next([=](not_null e) { - if (e->type() == QEvent::Enter) { - toggleWideControls(true); - } else if (e->type() == QEvent::Leave) { - toggleWideControls(false); - } - }, _trackControlsOverStateLifetime); - } + trackControl(widget, _trackControlsOverStateLifetime); }; trackOne(_mute); trackOne(_video); trackOne(_screenShare); + trackOne(_wideMenu); trackOne(_settings); trackOne(_hangup); trackOne(_controlsBackgroundWide); + trackControl(_menu, _trackControlsMenuLifetime); } void Panel::updateControlsGeometry() { @@ -1956,28 +2024,21 @@ void Panel::updateButtonsGeometry() { if (widget()->size().isEmpty() || (!_settings && !_callShare)) { return; } - const auto toggle = [&](bool shown) { - const auto toggleOne = [&](auto &widget) { - if (widget && widget->isHidden() == shown) { - widget->setVisible(shown); - } - }; - toggleOne(_mute); - toggleOne(_video); - toggleOne(_screenShare); - toggleOne(_settings); - toggleOne(_callShare); - toggleOne(_hangup); + const auto toggle = [](auto &widget, bool shown) { + if (widget && widget->isHidden() == shown) { + widget->setVisible(shown); + } }; if (mode() == PanelMode::Wide) { Assert(_video != nullptr); Assert(_screenShare != nullptr); + Assert(_wideMenu != nullptr); Assert(_settings != nullptr); Assert(_callShare == nullptr); const auto shown = _wideControlsAnimation.value( _wideControlsShown ? 1. : 0.); - toggle(shown > 0.); + const auto hidden = (shown == 0.); if (_viewport) { _viewport->setControlsShown(shown); @@ -2002,17 +2063,23 @@ void Panel::updateButtonsGeometry() { - membersWidth - membersSkip - fullWidth) / 2; - _screenShare->setVisible(true); + toggle(_screenShare, !hidden); _screenShare->moveToLeft(left, buttonsTop); left += _screenShare->width() + skip; - _screenShare->setVisible(true); + toggle(_video, !hidden); _video->moveToLeft(left, buttonsTop); left += _video->width() + skip; + toggle(_mute, !hidden); _mute->moveInner({ left + addSkip, buttonsTop + addSkip }); left += muteSize + skip; - _settings->setVisible(true); + const auto wideMenuShown = _call->canManage() + || _call->showChooseJoinAs(); + toggle(_settings, !hidden && !wideMenuShown); + toggle(_wideMenu, !hidden && wideMenuShown); + _wideMenu->moveToLeft(left, buttonsTop); _settings->moveToLeft(left, buttonsTop); left += _settings->width() + skip; + toggle(_hangup, !hidden); _hangup->moveToLeft(left, buttonsTop); left += _hangup->width(); if (_controlsBackgroundWide) { @@ -2025,8 +2092,6 @@ void Panel::updateButtonsGeometry() { rect.marginsAdded(st::groupCallControlsBackMargin)); } } else { - toggle(true); - const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip; const auto buttonsTop = widget()->height() @@ -2035,24 +2100,26 @@ void Panel::updateButtonsGeometry() { const auto fullWidth = muteSize + 2 * (_settings ? _settings : _callShare)->width() + 2 * st::groupCallButtonSkip; + toggle(_mute, true); _mute->moveInner({ (widget()->width() - muteSize) / 2, muteTop }); const auto leftButtonLeft = (widget()->width() - fullWidth) / 2; - if (_screenShare) { - _screenShare->setVisible(false); - } + toggle(_screenShare, false); + toggle(_wideMenu, false); + toggle(_callShare, true); if (_callShare) { _callShare->moveToLeft(leftButtonLeft, buttonsTop); } const auto showVideoButton = videoButtonInNarrowMode(); + toggle(_video, !_callShare && showVideoButton); if (_video) { - _video->setVisible(!_callShare && showVideoButton); _video->setStyle(st::groupCallVideo, &st::groupCallVideoActive); _video->moveToLeft(leftButtonLeft, buttonsTop); } + toggle(_settings, !_callShare && !showVideoButton); if (_settings) { - _settings->setVisible(!_callShare && !showVideoButton); _settings->moveToLeft(leftButtonLeft, buttonsTop); } + toggle(_hangup, true); _hangup->moveToRight(leftButtonLeft, buttonsTop); } if (_controlsBackgroundNarrow) { diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.h b/Telegram/SourceFiles/calls/group/calls_group_panel.h index 722591d2a8..334c722a48 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.h @@ -99,6 +99,9 @@ private: void enlargeVideo(); void minimizeVideo(); + template + void trackControl(WidgetPointer &widget, rpl::lifetime &lifetime); + bool updateMode(); void updateControlsGeometry(); void updateButtonsGeometry(); @@ -111,6 +114,7 @@ private: void refreshLeftButton(); void refreshVideoButtons( std::optional overrideWideMode = std::nullopt); + void refreshTopButton(); void toggleWideControls(bool shown); [[nodiscard]] bool videoButtonInNarrowMode() const; @@ -118,6 +122,7 @@ private: void showMainMenu(); void chooseJoinAs(); + void chooseShareScreenSource(); void addMembers(); void kickParticipant(not_null participantPeer); void kickParticipantSure(not_null participantPeer); @@ -154,11 +159,13 @@ private: object_ptr _recordingMark = { nullptr }; object_ptr _menuToggle = { nullptr }; object_ptr _menu = { nullptr }; + rpl::variable _wideMenuShown = false; object_ptr _joinAsToggle = { nullptr }; object_ptr _members = { nullptr }; std::unique_ptr _viewport; rpl::lifetime _trackControlsLifetime; rpl::lifetime _trackControlsOverStateLifetime; + rpl::lifetime _trackControlsMenuLifetime; object_ptr _startsIn = { nullptr }; object_ptr _countdown = { nullptr }; std::shared_ptr _countdownData; @@ -175,6 +182,7 @@ private: object_ptr _controlsBackgroundWide = { nullptr }; std::unique_ptr _controlsBackgroundNarrow; object_ptr _settings = { nullptr }; + object_ptr _wideMenu = { nullptr }; object_ptr _callShare = { nullptr }; object_ptr _video = { nullptr }; object_ptr _screenShare = { nullptr };