From dd2378b591517a2428b5bd9c89df1c93f1eda0f4 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 11 Feb 2025 11:46:42 +0400 Subject: [PATCH] Hide call controls in one-on-one video. --- Telegram/SourceFiles/calls/calls_instance.cpp | 3 +- Telegram/SourceFiles/calls/calls_panel.cpp | 139 ++++++++++++++++-- Telegram/SourceFiles/calls/calls_panel.h | 14 ++ 3 files changed, 145 insertions(+), 11 deletions(-) diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index 7d5793727..02c5fb144 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -578,8 +578,7 @@ void Instance::handleCallUpdate( if (inCall() && _currentCall->type() == Call::Type::Outgoing && _currentCall->user()->id == session->userPeerId() - && (peerFromUser(phoneCall.vparticipant_id()) - == _currentCall->user()->session().userPeerId())) { + && (user->id == _currentCall->user()->session().userPeerId())) { // Ignore call from the same running app, other account. return; } diff --git a/Telegram/SourceFiles/calls/calls_panel.cpp b/Telegram/SourceFiles/calls/calls_panel.cpp index 74ddcb8f2..673216650 100644 --- a/Telegram/SourceFiles/calls/calls_panel.cpp +++ b/Telegram/SourceFiles/calls/calls_panel.cpp @@ -42,6 +42,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/emoji_config.h" #include "ui/painter.h" #include "ui/rect.h" +#include "ui/integration.h" #include "core/application.h" #include "lang/lang_keys.h" #include "main/main_session.h" @@ -65,6 +66,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Calls { namespace { +constexpr auto kHideControlsTimeout = 5 * crl::time(1000); +constexpr auto kHideControlsQuickTimeout = 2 * crl::time(1000); + [[nodiscard]] QByteArray BatterySvg( const QSize &s, const QColor &c) { @@ -118,7 +122,9 @@ Panel::Panel(not_null call) st::callMicrophoneMute, &st::callMicrophoneUnmute)) , _name(widget(), st::callName) -, _status(widget(), st::callStatus) { +, _status(widget(), st::callStatus) +, _hideControlsTimer([=] { requestControlsHidden(true); }) +, _controlsShownForceTimer([=] { controlsShownForce(false); }) { _layerBg->setStyleOverrides(&st::groupCallBox, &st::groupCallLayerBox); _layerBg->setHideByBackgroundClick(true); @@ -188,6 +194,25 @@ void Panel::initWindow() { && window()->isFullScreen()) { window()->showNormal(); } + } else if (e->type() == QEvent::WindowStateChange) { + const auto state = window()->windowState(); + _fullScreenOrMaximized = (state & Qt::WindowFullScreen) + || (state & Qt::WindowMaximized); + } else if (e->type() == QEvent::Enter) { + _mouseInside = true; + Ui::Integration::Instance().registerLeaveSubscription( + window().get()); + if (!_fullScreenOrMaximized.current()) { + requestControlsHidden(false); + _hideControlsTimer.cancel(); + } + } else if (e->type() == QEvent::Leave) { + _mouseInside = false; + Ui::Integration::Instance().unregisterLeaveSubscription( + window().get()); + if (!_fullScreenOrMaximized.current()) { + _hideControlsTimer.callOnce(kHideControlsQuickTimeout); + } } return base::EventFilterResult::Continue; }); @@ -415,7 +440,11 @@ void Panel::refreshIncomingGeometry() { void Panel::reinitWithCall(Call *call) { _callLifetime.destroy(); _call = call; + const auto guard = gsl::finally([&] { + updateControlsShown(); + }); if (!_call) { + _fingerprint.destroy(); _incoming = nullptr; _outgoingVideoBubble = nullptr; _powerSaveBlocker = nullptr; @@ -457,6 +486,51 @@ void Panel::reinitWithCall(Call *call) { _window.backend()); _incoming->widget()->hide(); + _incoming->rp()->shownValue() | rpl::start_with_next([=] { + updateControlsShown(); + }, _incoming->rp()->lifetime()); + + _hideControlsFilter = nullptr; + _fullScreenOrMaximized.value( + ) | rpl::start_with_next([=](bool fullScreenOrMaximized) { + if (fullScreenOrMaximized) { + class Filter final : public QObject { + public: + explicit Filter(Fn moved) : _moved(moved) { + qApp->installEventFilter(this); + } + + bool eventFilter(QObject *watched, QEvent *event) { + if (event->type() == QEvent::MouseMove) { + _moved(watched); + } + return false; + } + + private: + Fn _moved; + + }; + _hideControlsFilter.reset(new Filter([=](QObject *what) { + _mouseInside = true; + if (what->isWidgetType() + && window()->isAncestorOf(static_cast(what))) { + _hideControlsTimer.callOnce(kHideControlsTimeout); + requestControlsHidden(false); + updateControlsShown(); + } + })); + _hideControlsTimer.callOnce(kHideControlsTimeout); + } else { + _hideControlsFilter = nullptr; + _hideControlsTimer.cancel(); + if (_mouseInside) { + requestControlsHidden(false); + updateControlsShown(); + } + } + }, _incoming->rp()->lifetime()); + _call->mutedValue( ) | rpl::start_with_next([=](bool mute) { _mute->entity()->setProgress(mute ? 1. : 0.); @@ -603,6 +677,8 @@ void Panel::createRemoteAudioMute() { const auto r = _remoteAudioMute->rect(); auto hq = PainterHighQualityEnabler(p); + p.setOpacity(_controlsShownAnimation.value( + _controlsShown ? 1. : 0.)); p.setBrush(st::videoPlayIconBg); p.setPen(Qt::NoPen); p.drawRoundedRect(r, r.height() / 2, r.height() / 2); @@ -661,6 +737,8 @@ void Panel::createRemoteLowBattery() { const auto r = _remoteLowBattery->rect(); auto hq = PainterHighQualityEnabler(p); + p.setOpacity(_controlsShownAnimation.value( + _controlsShown ? 1. : 0.)); p.setBrush(st::videoPlayIconBg); p.setPen(Qt::NoPen); p.drawRoundedRect(r, r.height() / 2, r.height() / 2); @@ -782,6 +860,9 @@ void Panel::showDevicesMenu( } Core::App().saveSettingsDelayed(); }; + controlsShownForce(true); + updateControlsShown(); + _devicesMenu = MakeDeviceSelectionMenu( widget(), &Core::App().mediaDevices(), @@ -791,6 +872,9 @@ void Panel::showDevicesMenu( Ui::PopupMenu::VerticalOrigin::Bottom); _devicesMenu->popup(button->mapToGlobal(QPoint()) - QPoint(st::callDeviceSelectionMenu.menu.widthMin / 2, 0)); + QObject::connect(_devicesMenu.get(), &QObject::destroyed, window(), [=] { + _controlsShownForceTimer.callOnce(kHideControlsQuickTimeout); + }); } void Panel::refreshOutgoingPreviewInBody(State state) { @@ -823,6 +907,33 @@ QRect Panel::outgoingFrameGeometry() const { return _outgoingVideoBubble->geometry(); } +void Panel::requestControlsHidden(bool hidden) { + _hideControlsRequested = hidden; + updateControlsShown(); +} + +void Panel::controlsShownForce(bool shown) { + _controlsShownForce = shown; + if (shown) { + _controlsShownForceTimer.cancel(); + } + updateControlsShown(); +} + +void Panel::updateControlsShown() { + const auto shown = !_incoming + || _incoming->widget()->isHidden() + || _controlsShownForce + || !_hideControlsRequested; + if (_controlsShown != shown) { + _controlsShown = shown; + _controlsShownAnimation.start([=] { + updateControlsGeometry(); + }, shown ? 0. : 1., shown ? 1. : 0., st::slideDuration); + updateControlsGeometry(); + } +} + void Panel::updateControlsGeometry() { if (widget()->size().isEmpty()) { return; @@ -830,6 +941,8 @@ void Panel::updateControlsGeometry() { if (_incoming) { refreshIncomingGeometry(); } + const auto shown = _controlsShownAnimation.value( + _controlsShown ? 1. : 0.); if (_fingerprint) { #ifndef Q_OS_MAC const auto controlsGeometry = _controls->controls.geometry(); @@ -848,14 +961,14 @@ void Panel::updateControlsGeometry() { const auto minRight = 0; #endif // _controls const auto desired = (widget()->width() - _fingerprint->width()) / 2; + const auto top = anim::interpolate( + -_fingerprint->height(), + st::callFingerprintTop, + shown); if (minLeft) { - _fingerprint->moveToLeft( - std::max(desired, minLeft), - st::callFingerprintTop); + _fingerprint->moveToLeft(std::max(desired, minLeft), top); } else { - _fingerprint->moveToRight( - std::max(desired, minRight), - st::callFingerprintTop); + _fingerprint->moveToRight(std::max(desired, minRight), top); } } const auto innerHeight = std::max(widget()->height(), st::callHeightMin); @@ -885,7 +998,11 @@ void Panel::updateControlsGeometry() { / (_outgoingPreviewInBody ? 3 : 2); _bodyTop = availableTop + skipHeight; - _buttonsTop = availableTop + available; + _buttonsTopShown = availableTop + available; + _buttonsTop = anim::interpolate( + widget()->height(), + _buttonsTopShown, + shown); const auto previewTop = _bodyTop + _bodySt->height + skipHeight; _userpic->setGeometry( @@ -908,6 +1025,8 @@ void Panel::updateControlsGeometry() { (_buttonsTop - st::callRemoteAudioMuteSkip - _remoteAudioMute->height())); + _remoteAudioMute->update(); + _remoteAudioMute->entity()->setOpacity(shown); } if (_remoteLowBattery) { _remoteLowBattery->moveToLeft( @@ -915,6 +1034,8 @@ void Panel::updateControlsGeometry() { (_buttonsTop - st::callRemoteAudioMuteSkip - _remoteLowBattery->height())); + _remoteLowBattery->update(); + _remoteLowBattery->entity()->setOpacity(shown); } if (_outgoingPreviewInBody) { @@ -925,7 +1046,7 @@ void Panel::updateControlsGeometry() { previewTop, bodyPreviewSize.width(), bodyPreviewSize.height())); - } else { + } else if (_outgoingVideoBubble) { updateOutgoingVideoBubbleGeometry(); } diff --git a/Telegram/SourceFiles/calls/calls_panel.h b/Telegram/SourceFiles/calls/calls_panel.h index e53045a44..ee24ab2c4 100644 --- a/Telegram/SourceFiles/calls/calls_panel.h +++ b/Telegram/SourceFiles/calls/calls_panel.h @@ -111,6 +111,9 @@ private: [[nodiscard]] bool handleClose() const; + void requestControlsHidden(bool hidden); + void controlsShownForce(bool shown); + void updateControlsShown(); void updateControlsGeometry(); void updateHangupGeometry(); void updateStatusGeometry(); @@ -177,8 +180,19 @@ private: std::unique_ptr _outgoingVideoBubble; QPixmap _bottomShadow; int _bodyTop = 0; + int _buttonsTopShown = 0; int _buttonsTop = 0; + base::Timer _hideControlsTimer; + base::Timer _controlsShownForceTimer; + std::unique_ptr _hideControlsFilter; + bool _hideControlsRequested = false; + rpl::variable _fullScreenOrMaximized; + Ui::Animations::Simple _controlsShownAnimation; + bool _controlsShownForce = false; + bool _controlsShown = true; + bool _mouseInside = false; + base::unique_qptr _devicesMenu; base::Timer _updateDurationTimer;