From b88219902fe3db2feb4b7b4f7dd3bae891acdb51 Mon Sep 17 00:00:00 2001
From: John Preston <johnprestonmail@gmail.com>
Date: Tue, 28 Jan 2020 14:48:29 +0300
Subject: [PATCH] Add sample PiP controls.

---
 .../media/view/media_view_overlay_widget.cpp  |  11 +-
 .../media/view/media_view_overlay_widget.h    |   1 +
 .../SourceFiles/media/view/media_view_pip.cpp | 112 ++++++++++++++----
 .../SourceFiles/media/view/media_view_pip.h   |  20 +++-
 .../view/media_view_playback_controls.cpp     |  39 ++++--
 .../media/view/media_view_playback_controls.h |   2 +
 .../SourceFiles/media/view/mediaview.style    |   4 +-
 7 files changed, 142 insertions(+), 47 deletions(-)

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 <QtGui/QWindow>
 #include <QtGui/QScreen>
@@ -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<Streaming::Document> document,
@@ -583,8 +577,32 @@ Pip::Pip(
 , _panel(parent, [=](QPainter &p, const FrameRequest &request) {
 	paint(p, request);
 })
+, _playPauseResume(
+	std::in_place,
+	&_panel,
+	object_ptr<Ui::IconButton>(&_panel, st::mediaviewPlayButton))
+, _pictureInPicture(
+	std::in_place,
+	&_panel,
+	object_ptr<Ui::IconButton>(&_panel, st::mediaviewFullScreenButton))
+, _close(
+	std::in_place,
+	&_panel,
+	object_ptr<Ui::IconButton>(&_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<QEvent*> e) {
-	//	return (e->type() == QEvent::WindowActivate);
-	//}) | rpl::start_with_next([=](not_null<QEvent*> 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<QEvent*> 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 <QtCore/QPointer>
 
+namespace Ui {
+class IconButton;
+template <typename Widget>
+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<Ui::FadeWrap<Ui::IconButton>> _playPauseResume;
+	base::unique_qptr< Ui::FadeWrap<Ui::IconButton>> _pictureInPicture;
+	base::unique_qptr< Ui::FadeWrap<Ui::IconButton>> _close;
+	bool _showPause = false;
+
 	FnMut<void()> _closeAndContinue;
 	FnMut<void()> _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<PlaybackProgress>())
 , _volumeController(this, st::mediaviewPlayback)
 , _fullScreenToggle(this, st::mediaviewFullScreenButton)
+, _pictureInPicture(this, st::mediaviewFullScreenButton)
 , _playedAlready(this, st::mediaviewPlayProgressLabel)
 , _toPlayLeft(this, st::mediaviewPlayProgressLabel)
 , _fadeAnimation(std::make_unique<Ui::FadeAnimation>(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*> delegate);
@@ -90,6 +91,7 @@ private:
 	std::unique_ptr<PlaybackProgress> _receivedTillProgress;
 	object_ptr<Ui::MediaSlider> _volumeController;
 	object_ptr<Ui::IconButton> _fullScreenToggle;
+	object_ptr<Ui::IconButton> _pictureInPicture;
 	object_ptr<Ui::LabelSimple> _playedAlready;
 	object_ptr<Ui::LabelSimple> _toPlayLeft;
 	object_ptr<Ui::LabelSimple> _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 }};