diff --git a/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp b/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp index 26b29ac467..95074e62c2 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_large_video.cpp @@ -30,7 +30,9 @@ LargeVideo::LargeVideo( , _st(st) , _pin(st::groupCallLargeVideoPin) , _pinButton(&_content) -, _topControls(_st.controlsAlign == style::al_top) { +, _controlsShown(_st.controlsAlign == style::al_top) +, _topControls(_st.controlsAlign == style::al_top) +, _controlsShownRatio(_controlsShown.current() ? 1. : 0.) { _content.setVisible(visible); setup(std::move(track), std::move(pinned)); } @@ -47,10 +49,25 @@ void LargeVideo::setGeometry(int x, int y, int width, int height) { _content.setGeometry(x, y, width, height); } +void LargeVideo::setControlsShown(bool shown) { + if (_mouseInside == shown) { + return; + } + _mouseInside = shown; + if (!_toggleControlsScheduled) { + _toggleControlsScheduled = true; + crl::on_main(&_content, [=] { toggleControls(); }); + } +} + rpl::producer LargeVideo::pinToggled() const { return _pinButton.clicks() | rpl::map([=] { return !_pinned; }); } +rpl::producer LargeVideo::controlsShown() const { + return _controlsShownRatio.value(); +} + rpl::producer LargeVideo::trackSizeValue() const { return _trackSize.value(); } @@ -60,10 +77,26 @@ void LargeVideo::setup( rpl::producer pinned) { _content.setAttribute(Qt::WA_OpaquePaintEvent); + _content.events( + ) | rpl::start_with_next([=](not_null e) { + if (e->type() == QEvent::Enter) { + Ui::Integration::Instance().registerLeaveSubscription(&_content); + setControlsShown(true); + } else if (e->type() == QEvent::Leave) { + Ui::Integration::Instance().unregisterLeaveSubscription( + &_content); + setControlsShown(false); + } + }, _content.lifetime()); + rpl::combine( _content.shownValue(), std::move(track) ) | rpl::map([=](bool shown, LargeVideoTrack track) { + if (!shown) { + _controlsAnimation.stop(); + _controlsShownRatio = _controlsShown.current() ? 1. : 0.; + } return shown ? track : LargeVideoTrack(); }) | rpl::distinct_until_changed( ) | rpl::start_with_next([=](LargeVideoTrack track) { @@ -90,6 +123,34 @@ void LargeVideo::setup( setupControls(std::move(pinned)); } +void LargeVideo::toggleControls() { + _toggleControlsScheduled = false; + const auto shown = _mouseInside; + if (_controlsShown.current() == shown) { + return; + } + _controlsShown = shown; + const auto callback = [=] { + _controlsShownRatio = _controlsAnimation.value( + _controlsShown.current() ? 1. : 0.); + if (_topControls) { + _content.update(0, 0, _content.width(), _st.shadowHeight); + } else { + _content.update(); + } + updateControlsGeometry(); + }; + if (_content.isHidden()) { + updateControlsGeometry(); + } else { + _controlsAnimation.start( + callback, + shown ? 0. : 1., + shown ? 1. : 0., + 140); + } +} + void LargeVideo::setupControls(rpl::producer pinned) { std::move(pinned) | rpl::start_with_next([=](bool pinned) { _pinned = pinned; @@ -186,13 +247,17 @@ void LargeVideo::paint(QRect clip) { } void LargeVideo::paintControls(Painter &p, QRect clip) { - const auto shown = _controlsAnimation.value(_controlsShown ? 1. : 0.); - if (shown == 0.) { + const auto shown = _controlsShownRatio.current(); + if (shown == 0. && _topControls) { return; } const auto width = _content.width(); const auto height = _content.height(); + const auto fullShift = _st.statusPosition.y() + st::normalFont->height; + const auto shift = _topControls + ? anim::interpolate(-fullShift, 0, shown) + : 0; // Shadow. if (_shadow.isNull()) { @@ -204,7 +269,9 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { } const auto shadowRect = QRect( 0, - _topControls ? 0 : (height - _st.shadowHeight), + (_topControls + ? anim::interpolate(-_st.shadowHeight, 0, shown) + : (height - _st.shadowHeight)), width, _st.shadowHeight); const auto shadowFill = shadowRect.intersected(clip); @@ -231,7 +298,7 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { - _st.namePosition.x(); const auto nameLeft = _st.namePosition.x(); const auto nameTop = _topControls - ? _st.namePosition.y() + ? (_st.namePosition.y() + shift) : (height - _st.namePosition.y()); _track.row->name().drawLeftElided(p, nameLeft, nameTop, hasWidth, width); @@ -239,7 +306,7 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { p.setPen(st::groupCallVideoSubTextFg); const auto statusLeft = _st.statusPosition.x(); const auto statusTop = _topControls - ? _st.statusPosition.y() + ? (_st.statusPosition.y() + shift) : (height - _st.statusPosition.y()); _track.row->paintComplexStatusText( p, @@ -255,7 +322,7 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { const auto &icon = st::groupCallLargeVideoCrossLine.icon; const auto iconLeft = width - _st.iconPosition.x() - icon.width(); const auto iconTop = _topControls - ? _st.iconPosition.y() + ? (_st.iconPosition.y() + shift) : (height - _st.iconPosition.y() - icon.height()); _track.row->paintMuteIcon( p, @@ -268,7 +335,7 @@ void LargeVideo::paintControls(Painter &p, QRect clip) { ? (width - _st.pinPosition.x() - pin.width()) : _st.pinPosition.x(); const auto pinTop = _topControls - ? _st.pinPosition.y() + ? (_st.pinPosition.y() + shift) : (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 e8b635813b..9389724f26 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_large_video.h +++ b/Telegram/SourceFiles/calls/group/calls_group_large_video.h @@ -61,8 +61,10 @@ public: void raise(); void setVisible(bool visible); void setGeometry(int x, int y, int width, int height); + void setControlsShown(bool shown); [[nodiscard]] rpl::producer pinToggled() const; + [[nodiscard]] rpl::producer controlsShown() const; [[nodiscard]] rpl::producer trackSizeValue() const; [[nodiscard]] rpl::lifetime &lifetime() { @@ -99,6 +101,7 @@ private: void paint(QRect clip); void paintControls(Painter &p, QRect clip); void updateControlsGeometry(); + void toggleControls(); Content _content; const style::GroupCallLargeVideo &_st; @@ -107,9 +110,12 @@ private: Ui::CrossLineAnimation _pin; Ui::AbstractButton _pinButton; Ui::Animations::Simple _controlsAnimation; - bool _topControls = false; + rpl::variable _controlsShown = true; + const bool _topControls = true; bool _pinned = false; - bool _controlsShown = true; + bool _mouseInside = false; + bool _toggleControlsScheduled = false; + rpl::variable _controlsShownRatio = 1.; rpl::variable _trackSize; rpl::lifetime _trackLifetime; diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp index 52828ff78d..cb905dc18b 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.cpp @@ -1016,6 +1016,21 @@ void Panel::setupPinnedVideo() { _call->pinVideoEndpoint(large); } }, _pinnedVideo->lifetime()); + _pinnedVideo->controlsShown( + ) | rpl::filter([=](float64 shown) { + return (_pinnedVideoControlsShown != shown); + }) | rpl::start_with_next([=](float64 shown) { + const auto hiding = (shown <= _pinnedVideoControlsShown); + _pinnedVideoControlsShown = shown; + if (_mode == PanelMode::Wide) { + if (hiding && _trackControlsLifetime) { + _trackControlsLifetime.destroy(); + } else if (!hiding && !_trackControlsLifetime) { + trackControls(); + } + updateButtonsGeometry(); + } + }, _pinnedVideo->lifetime()); raiseControls(); } @@ -1536,6 +1551,7 @@ bool Panel::updateMode() { void Panel::refreshControlsBackground() { if (_mode != PanelMode::Wide) { + _trackControlsLifetime.destroy(); _controlsBackground.destroy(); } else if (_controlsBackground) { return; @@ -1556,17 +1572,92 @@ void Panel::refreshControlsBackground() { auto p = QPainter(_controlsBackground.data()); corners->paint(p, _controlsBackground->rect()); }, lifetime); + + if (_pinnedVideoControlsShown > 0.) { + trackControls(); + } raiseControls(); } +void Panel::trackControls() { + const auto trackOne = [&](auto &&widget) { + if (widget) { + widget->events( + ) | rpl::start_with_next([=](not_null e) { + if (e->type() == QEvent::Enter) { + _pinnedVideo->setControlsShown(true); + } else if (e->type() == QEvent::Leave) { + _pinnedVideo->setControlsShown(false); + } + }, _trackControlsLifetime); + } + }; + trackOne(_mute); + trackOne(_video); + trackOne(_screenShare); + trackOne(_settings); + trackOne(_callShare); + trackOne(_hangup); + trackOne(_controlsBackground); +} + void Panel::updateControlsGeometry() { if (widget()->size().isEmpty() || (!_settings && !_callShare)) { return; } + updateButtonsGeometry(); + updateMembersGeometry(); + refreshTitle(); + +#ifdef Q_OS_MAC + const auto controlsOnTheLeft = true; +#else // Q_OS_MAC + const auto controlsOnTheLeft = _controls->geometry().center().x() + < widget()->width() / 2; +#endif // Q_OS_MAC + const auto menux = st::groupCallMenuTogglePosition.x(); + const auto menuy = st::groupCallMenuTogglePosition.y(); + if (controlsOnTheLeft) { + if (_menuToggle) { + _menuToggle->moveToRight(menux, menuy); + } else if (_joinAsToggle) { + _joinAsToggle->moveToRight(menux, menuy); + } + } else { + if (_menuToggle) { + _menuToggle->moveToLeft(menux, menuy); + } else if (_joinAsToggle) { + _joinAsToggle->moveToLeft(menux, menuy); + } + } +} + +void Panel::updateButtonsGeometry() { + 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); + }; if (_videoMode.current()) { _mute->setStyle(st::callMuteButtonSmall); + toggle(_mode != PanelMode::Wide || _pinnedVideoControlsShown > 0.); + const auto buttonsTop = widget()->height() - - st::groupCallButtonBottomSkipSmall; + - st::groupCallButtonBottomSkipSmall + + (_mode == PanelMode::Wide + ? anim::interpolate( + st::groupCallButtonBottomSkipSmall, + 0, + _pinnedVideoControlsShown) + : 0); const auto addSkip = st::callMuteButtonSmall.active.outerRadius; const auto muteSize = _mute->innerSize().width() + 2 * addSkip; const auto skip = (_video ? 1 : 2) * st::groupCallButtonSkipSmall; @@ -1619,6 +1710,8 @@ void Panel::updateControlsGeometry() { } } else { _mute->setStyle(st::callMuteButton); + toggle(true); + const auto muteTop = widget()->height() - st::groupCallMuteBottomSkip; const auto buttonsTop = widget()->height() @@ -1640,30 +1733,6 @@ void Panel::updateControlsGeometry() { _hangup->setStyle(st::groupCallHangup); _hangup->moveToRight(leftButtonLeft, buttonsTop); } - updateMembersGeometry(); - refreshTitle(); - -#ifdef Q_OS_MAC - const auto controlsOnTheLeft = true; -#else // Q_OS_MAC - const auto controlsOnTheLeft = _controls->geometry().center().x() - < widget()->width() / 2; -#endif // Q_OS_MAC - const auto menux = st::groupCallMenuTogglePosition.x(); - const auto menuy = st::groupCallMenuTogglePosition.y(); - if (controlsOnTheLeft) { - if (_menuToggle) { - _menuToggle->moveToRight(menux, menuy); - } else if (_joinAsToggle) { - _joinAsToggle->moveToRight(menux, menuy); - } - } else { - if (_menuToggle) { - _menuToggle->moveToLeft(menux, menuy); - } else if (_joinAsToggle) { - _joinAsToggle->moveToLeft(menux, menuy); - } - } } void Panel::updateMembersGeometry() { diff --git a/Telegram/SourceFiles/calls/group/calls_group_panel.h b/Telegram/SourceFiles/calls/group/calls_group_panel.h index f82e4bc052..7777f815f9 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_panel.h +++ b/Telegram/SourceFiles/calls/group/calls_group_panel.h @@ -91,10 +91,12 @@ private: bool handleClose(); void startScheduledNow(); + void trackControls(); void raiseControls(); bool updateMode(); void updateControlsGeometry(); + void updateButtonsGeometry(); void updateMembersGeometry(); void refreshControlsBackground(); void showControls(); @@ -143,7 +145,8 @@ private: object_ptr _joinAsToggle = { nullptr }; object_ptr _members = { nullptr }; std::unique_ptr _pinnedVideo; - rpl::lifetime _pinnedTrackLifetime; + float64 _pinnedVideoControlsShown = 1.; + rpl::lifetime _trackControlsLifetime; object_ptr _startsIn = { nullptr }; object_ptr _countdown = { nullptr }; std::shared_ptr _countdownData; diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp index 0bcfbdb9f1..db83849fc1 100644 --- a/Telegram/SourceFiles/ui/controls/call_mute_button.cpp +++ b/Telegram/SourceFiles/ui/controls/call_mute_button.cpp @@ -18,6 +18,7 @@ #include "styles/style_calls.h" #include +#include namespace Ui { namespace { @@ -1038,6 +1039,10 @@ rpl::producer CallMuteButton::clicks() { }); } +rpl::producer> CallMuteButton::events() const { + return _content->events(); +} + QSize CallMuteButton::innerSize() const { return innerGeometry().size(); } @@ -1071,6 +1076,10 @@ void CallMuteButton::setVisible(bool visible) { _blobs->setVisible(visible); } +bool CallMuteButton::isHidden() const { + return _content->isHidden(); +} + void CallMuteButton::raise() { _blobs->raise(); _content->raise(); diff --git a/Telegram/SourceFiles/ui/controls/call_mute_button.h b/Telegram/SourceFiles/ui/controls/call_mute_button.h index 7759c39429..8e261aa3f5 100644 --- a/Telegram/SourceFiles/ui/controls/call_mute_button.h +++ b/Telegram/SourceFiles/ui/controls/call_mute_button.h @@ -62,6 +62,7 @@ public: void setStyle(const style::CallMuteButton &st); void setLevel(float level); [[nodiscard]] rpl::producer clicks(); + [[nodiscard]] rpl::producer> events() const; [[nodiscard]] QSize innerSize() const; [[nodiscard]] QRect innerGeometry() const; @@ -76,6 +77,7 @@ public: void hide() { setVisible(false); } + [[nodiscard]] bool isHidden() const; void raise(); void lower();