diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 9ac55035c..a0137e22f 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2317,6 +2317,12 @@ void OverlayWidget::playbackControlsFromFullScreen() { playbackToggleFullScreen(); } +void OverlayWidget::playbackControlsToPictureInPicture() { + if (!videoIsGifv()) { + switchToPip(); + } +} + void OverlayWidget::playbackPauseResume() { Expects(_streamed != nullptr); @@ -2412,11 +2418,6 @@ void OverlayWidget::switchToPip() { void OverlayWidget::playbackToggleFullScreen() { Expects(_streamed != nullptr); - if (!videoIsGifv() && !_fullScreenVideo) { - switchToPip(); - return; - } - if (!videoShown() || (videoIsGifv() && !_fullScreenVideo)) { return; } diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h index 860c6afba..ab13d5c81 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.h +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.h @@ -174,6 +174,7 @@ private: float64 playbackControlsCurrentVolume() override; void playbackControlsToFullScreen() override; void playbackControlsFromFullScreen() override; + void playbackControlsToPictureInPicture() override; void playbackPauseResume(); void playbackToggleFullScreen(); void playbackPauseOnCall(); diff --git a/Telegram/SourceFiles/media/view/media_view_pip.cpp b/Telegram/SourceFiles/media/view/media_view_pip.cpp index 6f97722d7..b3c722d78 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.cpp +++ b/Telegram/SourceFiles/media/view/media_view_pip.cpp @@ -10,11 +10,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/streaming/media_streaming_player.h" #include "media/streaming/media_streaming_document.h" #include "media/streaming/media_streaming_utility.h" +#include "media/audio/media_audio.h" #include "core/application.h" #include "ui/platform/ui_platform_utility.h" +#include "ui/widgets/buttons.h" +#include "ui/wrap/fade_wrap.h" #include "window/window_controller.h" #include "styles/style_window.h" #include "styles/style_mediaview.h" +#include "styles/style_layers.h" // st::boxTitleClose #include #include @@ -564,16 +568,6 @@ void PipPanel::moveAnimated(QPoint to) { anim::easeOutCirc); } -void PipPanel::keyPressEvent(QKeyEvent *e) { - if (e->key() == Qt::Key_Space) { - //playbackPauseResume(); - } else if (e->key() == Qt::Key_Escape) { - //crl::on_main(this, [=] { - // _closeAndContinue(); - //}); - } -} - Pip::Pip( QWidget *parent, std::shared_ptr document, @@ -583,8 +577,32 @@ Pip::Pip( , _panel(parent, [=](QPainter &p, const FrameRequest &request) { paint(p, request); }) +, _playPauseResume( + std::in_place, + &_panel, + object_ptr(&_panel, st::mediaviewPlayButton)) +, _pictureInPicture( + std::in_place, + &_panel, + object_ptr(&_panel, st::mediaviewFullScreenButton)) +, _close( + std::in_place, + &_panel, + object_ptr(&_panel, st::boxTitleClose)) , _closeAndContinue(std::move(closeAndContinue)) , _destroy(std::move(destroy)) { + _close->entity()->addClickHandler([=] { + _panel.close(); + }); + _pictureInPicture->entity()->addClickHandler([=] { + _closeAndContinue(); + }); + _playPauseResume->entity()->addClickHandler([=] { + playbackPauseResume(); + }); + _close->show(anim::type::instant); + _pictureInPicture->show(anim::type::instant); + _playPauseResume->show(anim::type::instant); setupPanel(); setupStreaming(); } @@ -597,13 +615,35 @@ void Pip::setupPanel() { _panel.setAspectRatio(size); } _panel.setPosition(PipPanel::Position()); - //_panel.events( - //) | rpl::filter([=](not_null e) { - // return (e->type() == QEvent::WindowActivate); - //}) | rpl::start_with_next([=](not_null e) { - // int a = 0; - //}, _panel.lifetime()); _panel.show(); + + _panel.sizeValue( + ) | rpl::start_with_next([=](QSize size) { + _close->moveToLeft(0, 0, size.width()); + const auto skip = st::mediaviewFullScreenLeft; + const auto sum = _playPauseResume->width() + skip + _pictureInPicture->width(); + const auto left = (size.width() - sum) / 2; + const auto top = size.height() - _playPauseResume->height() - skip; + _playPauseResume->moveToLeft(left, top); + _pictureInPicture->moveToRight(left, top); + }, _panel.lifetime()); + + _panel.events( + ) | rpl::filter([=](not_null e) { + return e->type() == QEvent::Close; + }) | rpl::start_with_next([=] { + _destroy(); + }, _panel.lifetime()); +} + +void Pip::updatePlayPauseResumeState(const Player::TrackState &state) { + auto showPause = Player::ShowPauseIcon(state.state); + if (showPause != _showPause) { + _showPause = showPause; + _playPauseResume->entity()->setIconOverride( + _showPause ? &st::mediaviewPauseIcon : nullptr, + _showPause ? &st::mediaviewPauseIconOver : nullptr); + } } void Pip::setupStreaming() { @@ -632,39 +672,59 @@ void Pip::handleStreamingUpdate(Streaming::Update &&update) { update.data.match([&](Information &update) { _panel.setAspectRatio(update.video.size); }, [&](const PreloadedVideo &update) { - //updatePlaybackState(); + updatePlaybackState(); }, [&](const UpdateVideo &update) { _panel.update(); Core::App().updateNonIdle(); - //updatePlaybackState(); + updatePlaybackState(); }, [&](const PreloadedAudio &update) { - //updatePlaybackState(); + updatePlaybackState(); }, [&](const UpdateAudio &update) { - //updatePlaybackState(); + updatePlaybackState(); }, [&](WaitingForData) { }, [&](MutedByOther) { }, [&](Finished) { - //updatePlaybackState(); + updatePlaybackState(); }); } +void Pip::updatePlaybackState() { + const auto state = _instance.player().prepareLegacyState(); + if (state.position != kTimeUnknown && state.length != kTimeUnknown) { + updatePlayPauseResumeState(state); + } +} + void Pip::handleStreamingError(Streaming::Error &&error) { _panel.close(); } void Pip::playbackPauseResume() { - if (_instance.player().finished() || !_instance.player().active()) { - //restartAtSeekPosition(0); + if (_instance.player().failed()) { + _panel.close(); + } else if (_instance.player().finished() + || !_instance.player().active()) { + restartAtSeekPosition(0); } else if (_instance.player().paused()) { _instance.resume(); - //updatePlaybackState(); - //playbackPauseMusic(); + updatePlaybackState(); } else { _instance.pause(); - //updatePlaybackState(); + updatePlaybackState(); } } +void Pip::restartAtSeekPosition(crl::time position) { + if (!_instance.info().video.cover.isNull()) { + _instance.saveFrameToCover(); + } + auto options = Streaming::PlaybackOptions(); + options.position = position; + options.audioId = _instance.player().prepareLegacyState().id; + _instance.play(options); + updatePlaybackState(); +} + QImage Pip::videoFrame(const FrameRequest &request) const { if (_instance.player().ready()) { return _instance.frame(request); diff --git a/Telegram/SourceFiles/media/view/media_view_pip.h b/Telegram/SourceFiles/media/view/media_view_pip.h index 160d3c94a..2b76e7bbd 100644 --- a/Telegram/SourceFiles/media/view/media_view_pip.h +++ b/Telegram/SourceFiles/media/view/media_view_pip.h @@ -13,7 +13,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include +namespace Ui { +class IconButton; +template +class FadeWrap; +} // namespace Ui + namespace Media { +namespace Player { +struct TrackState; +} // namespace Player + namespace View { #if defined Q_OS_MAC && !defined OS_MAC_OLD @@ -49,7 +59,6 @@ protected: void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; - void keyPressEvent(QKeyEvent *e) override; private: void setPositionDefault(); @@ -97,6 +106,10 @@ private: void handleStreamingUpdate(Streaming::Update &&update); void handleStreamingError(Streaming::Error &&error); + void updatePlaybackState(); + void updatePlayPauseResumeState(const Player::TrackState &state); + void restartAtSeekPosition(crl::time position); + [[nodiscard]] QImage videoFrame(const FrameRequest &request) const; [[nodiscard]] QImage videoFrameForDirectPaint( const FrameRequest &request) const; @@ -105,6 +118,11 @@ private: PipPanel _panel; QSize _size; + base::unique_qptr> _playPauseResume; + base::unique_qptr< Ui::FadeWrap> _pictureInPicture; + base::unique_qptr< Ui::FadeWrap> _close; + bool _showPause = false; + FnMut _closeAndContinue; FnMut _destroy; diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index e4adf5ece..1c1f3cc40 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -31,6 +31,7 @@ PlaybackControls::PlaybackControls( , _playbackProgress(std::make_unique()) , _volumeController(this, st::mediaviewPlayback) , _fullScreenToggle(this, st::mediaviewFullScreenButton) +, _pictureInPicture(this, st::mediaviewFullScreenButton) , _playedAlready(this, st::mediaviewPlayProgressLabel) , _toPlayLeft(this, st::mediaviewPlayProgressLabel) , _fadeAnimation(std::make_unique(this)) { @@ -42,6 +43,13 @@ PlaybackControls::PlaybackControls( fadeUpdated(opacity); }); + _pictureInPicture->setIconOverride( + &st::mediaviewFullScreenOutIcon, + &st::mediaviewFullScreenOutIconOver); + _pictureInPicture->addClickHandler([=] { + _delegate->playbackControlsToPictureInPicture(); + }); + _volumeController->setValue(_delegate->playbackControlsCurrentVolume()); _volumeController->setChangeProgressCallback([=](float64 value) { _delegate->playbackControlsVolumeChanged(value); @@ -275,26 +283,31 @@ void PlaybackControls::setInFullScreen(bool inFullScreen) { } void PlaybackControls::resizeEvent(QResizeEvent *e) { - int playTop = (height() - _playPauseResume->height()) / 2; - _playPauseResume->moveToLeft(st::mediaviewPlayPauseLeft, playTop); + const auto skip = st::mediaviewFullScreenLeft; - int fullScreenTop = (height() - _fullScreenToggle->height()) / 2; - _fullScreenToggle->moveToRight(st::mediaviewFullScreenLeft, fullScreenTop); - - _volumeController->resize(st::mediaviewVolumeWidth, st::mediaviewPlayback.seekSize.height()); - _volumeController->moveToRight(st::mediaviewFullScreenLeft + _fullScreenToggle->width() + st::mediaviewVolumeLeft, st::mediaviewPlaybackTop); - - auto playbackWidth = width() - st::mediaviewPlayPauseLeft - _playPauseResume->width() - playTop - fullScreenTop - _volumeController->width() - st::mediaviewVolumeLeft - _fullScreenToggle->width() - st::mediaviewFullScreenLeft; + auto playbackWidth = width() - 2 * skip; _playbackSlider->resize(playbackWidth, st::mediaviewPlayback.seekSize.height()); - _playbackSlider->moveToLeft(st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop, st::mediaviewPlaybackTop); - - _playedAlready->moveToLeft(st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop, st::mediaviewPlayProgressTop); - _toPlayLeft->moveToRight(width() - (st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop) - playbackWidth, st::mediaviewPlayProgressTop); + _playbackSlider->moveToLeft(st::mediaviewPlayPauseLeft, st::mediaviewPlaybackTop); + _playedAlready->moveToLeft(st::mediaviewPlayPauseLeft, st::mediaviewPlayProgressTop); + _toPlayLeft->moveToRight(st::mediaviewPlayPauseLeft, st::mediaviewPlayProgressTop); if (_downloadProgress) { const auto left = (_playbackSlider->width() - _downloadProgress->width()) / 2; _downloadProgress->move(_playbackSlider->x() + left, st::mediaviewPlayProgressTop); } + + auto left = skip; + const auto playTop = height() - _playPauseResume->height() - skip; + _playPauseResume->moveToLeft(left, playTop); + left += _playPauseResume->width() + skip; + + _fullScreenToggle->moveToLeft(left, playTop); + left += _fullScreenToggle->width() + skip; + + _pictureInPicture->moveToLeft(left, playTop); + + _volumeController->resize(st::mediaviewVolumeWidth, st::mediaviewPlayback.seekSize.height()); + _volumeController->moveToRight(skip, playTop + (_fullScreenToggle->height() - _volumeController->height()) / 2); } void PlaybackControls::paintEvent(QPaintEvent *e) { diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.h b/Telegram/SourceFiles/media/view/media_view_playback_controls.h index 4e91c157c..f2addb92f 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.h +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.h @@ -38,6 +38,7 @@ public: [[nodiscard]] virtual float64 playbackControlsCurrentVolume() = 0; virtual void playbackControlsToFullScreen() = 0; virtual void playbackControlsFromFullScreen() = 0; + virtual void playbackControlsToPictureInPicture() = 0; }; PlaybackControls(QWidget *parent, not_null delegate); @@ -90,6 +91,7 @@ private: std::unique_ptr _receivedTillProgress; object_ptr _volumeController; object_ptr _fullScreenToggle; + object_ptr _pictureInPicture; object_ptr _playedAlready; object_ptr _toPlayLeft; object_ptr _downloadProgress = { nullptr }; diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index 046a0f39e..eef9f2dcd 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -24,7 +24,7 @@ mediaviewPlayback: MediaSlider { duration: mediaviewOverDuration; } -mediaviewControllerSize: size(600px, 50px); +mediaviewControllerSize: size(480px, 100px); mediaviewPlayProgressLabel: LabelSimple(defaultLabelSimple) { font: semiboldFont; textFg: mediaviewPlaybackProgressFg; @@ -57,7 +57,7 @@ mediaviewFullScreenOutIconOver: icon {{ "media_fullscreen_from", mediaviewPlayba mediaviewPlaybackTop: 28px; mediaviewVolumeWidth: 60px; -mediaviewControllerRadius: 25px; +mediaviewControllerRadius: roundRadiusLarge; mediaviewLeft: icon {{ "mediaview_next-flip_horizontal", mediaviewControlFg }}; mediaviewRight: icon {{ "mediaview_next", mediaviewControlFg }};