From 80bdf9b74cd4c0b50d5e6de90acea6072773231b Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 19 Jul 2023 18:34:17 +0400 Subject: [PATCH] Add play/pause button to video stories. --- .../stories/media_stories_controller.cpp | 14 +++++ .../media/stories/media_stories_controller.h | 8 +++ .../media/stories/media_stories_header.cpp | 52 +++++++++++++++++++ .../media/stories/media_stories_header.h | 6 +++ .../SourceFiles/media/view/media_view.style | 22 ++++++++ 5 files changed, 102 insertions(+) diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp index adad7ec8b1..62019349d6 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.cpp @@ -902,10 +902,24 @@ void Controller::subscribeToSession() { }); } +PauseState Controller::pauseState() const { + const auto inactive = !_windowActive + || _replyActive + || _layerShown + || _menuShown; + const auto playing = !inactive && !_paused; + return playing + ? PauseState::Playing + : inactive + ? PauseState::Inactive + : PauseState::Paused; +} + void Controller::updatePlayingAllowed() { if (!_shown) { return; } + _header->updatePauseState(); setPlayingAllowed(_started && _windowActive && !_paused diff --git a/Telegram/SourceFiles/media/stories/media_stories_controller.h b/Telegram/SourceFiles/media/stories/media_stories_controller.h index 0df907acd6..b29b6d5208 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_controller.h +++ b/Telegram/SourceFiles/media/stories/media_stories_controller.h @@ -72,6 +72,12 @@ enum class HeaderLayout { Outside, }; +enum class PauseState { + Playing, + Paused, + Inactive, +}; + struct SiblingLayout { QRect geometry; QRect userpic; @@ -136,6 +142,8 @@ public: void contentPressed(bool pressed); void setMenuShown(bool shown); + [[nodiscard]] PauseState pauseState() const; + void repaintSibling(not_null sibling); [[nodiscard]] SiblingView sibling(SiblingType type) const; diff --git a/Telegram/SourceFiles/media/stories/media_stories_header.cpp b/Telegram/SourceFiles/media/stories/media_stories_header.cpp index 03f40701f4..cf752495a0 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_header.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_header.cpp @@ -272,6 +272,8 @@ void Header::show(HeaderData data) { _userpic = nullptr; _info = nullptr; _privacy = nullptr; + _playPause = nullptr; + _volumeToggle = nullptr; const auto parent = _controller->wrap(); auto widget = std::make_unique(parent); const auto raw = widget.get(); @@ -336,11 +338,61 @@ void Header::show(HeaderData data) { }); + if (data.video) { + _playPause = std::make_unique( + _widget.get(), + st::storiesPlayButton); + _playPause->show(); + _playPause->setClickedCallback([=] { + _controller->togglePaused(_pauseState != PauseState::Paused); + }); + + _volumeToggle = std::make_unique( + _widget.get(), + st::storiesVolumeButton); + _volumeToggle->show(); + + _widget->widthValue() | rpl::start_with_next([=](int width) { + const auto playPause = st::storiesPlayButtonPosition; + _playPause->moveToRight(playPause.x(), playPause.y(), width); + const auto volume = st::storiesVolumeButtonPosition; + _volumeToggle->moveToRight(volume.x(), volume.y(), width); + }, _playPause->lifetime()); + + _pauseState = _controller->pauseState(); + applyPauseState(); + } else { + _playPause = nullptr; + _volumeToggle = nullptr; + } + if (timestamp.changes > 0) { _dateUpdateTimer.callOnce(timestamp.changes * crl::time(1000)); } } +void Header::updatePauseState() { + if (!_playPause) { + return; + } else if (const auto s = _controller->pauseState(); _pauseState != s) { + _pauseState = s; + applyPauseState(); + } +} + +void Header::applyPauseState() { + Expects(_playPause != nullptr); + + const auto paused = (_pauseState == PauseState::Paused); + const auto inactive = (_pauseState == PauseState::Inactive); + _playPause->setAttribute(Qt::WA_TransparentForMouseEvents, inactive); + if (inactive) { + _playPause->clearState(); + } + const auto icon = paused ? nullptr : &st::storiesPauseIcon; + _playPause->setIconOverride(icon, icon); +} + void Header::raise() { if (_widget) { _widget->raise(); diff --git a/Telegram/SourceFiles/media/stories/media_stories_header.h b/Telegram/SourceFiles/media/stories/media_stories_header.h index ae584ed8cf..8685e5bc8a 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_header.h +++ b/Telegram/SourceFiles/media/stories/media_stories_header.h @@ -27,6 +27,7 @@ class FadeWrap; namespace Media::Stories { class Controller; +enum class PauseState; struct HeaderData { not_null user; @@ -47,14 +48,19 @@ public: explicit Header(not_null controller); ~Header(); + void updatePauseState(); + void show(HeaderData data); void raise(); private: void updateDateText(); + void applyPauseState(); const not_null _controller; + PauseState _pauseState = {}; + std::unique_ptr _widget; std::unique_ptr _info; std::unique_ptr _userpic; diff --git a/Telegram/SourceFiles/media/view/media_view.style b/Telegram/SourceFiles/media/view/media_view.style index 097b498cb4..a3ae2dac09 100644 --- a/Telegram/SourceFiles/media/view/media_view.style +++ b/Telegram/SourceFiles/media/view/media_view.style @@ -851,3 +851,25 @@ storiesBadgeSelectedContacts: icon{{ "mediaview/mini_selected_contacts", history storiesBadgePadding: margins(1px, 1px, 1px, 1px); storiesBadgeOutline: 2px; storiesBadgeShift: point(5px, 4px); + +storiesPlayIcon: icon{{ "player/player_play", mediaviewPlaybackIconFgOver }}; +storiesPauseIcon: icon{{ "player/player_pause", mediaviewPlaybackIconFgOver }}; +storiesPlayButton: IconButton(defaultIconButton) { + width: 40px; + height: 40px; + icon: storiesPlayIcon; + iconOver: storiesPlayIcon; + iconPosition: point(8px, 8px); + rippleAreaPosition: point(4px, 4px); + rippleAreaSize: 32px; + ripple: RippleAnimation(defaultRippleAnimation) { + color: mediaviewPlaybackIconRipple; + } +} +storiesPlayButtonPosition: point(54px, 0px); +storiesVolumeButton: IconButton(storiesPlayButton) { + icon: mediaviewVolumeIcon2Over; + iconOver: mediaviewVolumeIcon2Over; + ripple: emptyRippleAnimation; +} +storiesVolumeButtonPosition: point(10px, 0px);