From 8a1140c09f156e9aa883841a0d866b280ed63671 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 18 Nov 2021 21:02:12 +0400 Subject: [PATCH] Extract Media::Player::Dropdown widget. --- Telegram/SourceFiles/mainwidget.cpp | 6 +- Telegram/SourceFiles/mainwidget.h | 4 +- .../media/player/media_player_dropdown.cpp | 169 ++++++++++++++++ .../media/player/media_player_dropdown.h | 52 +++++ .../player/media_player_volume_controller.cpp | 186 +++--------------- .../player/media_player_volume_controller.h | 54 +---- .../media/player/media_player_widget.cpp | 18 +- .../media/player/media_player_widget.h | 6 +- Telegram/cmake/td_ui.cmake | 3 + 9 files changed, 274 insertions(+), 224 deletions(-) create mode 100644 Telegram/SourceFiles/media/player/media_player_dropdown.cpp create mode 100644 Telegram/SourceFiles/media/player/media_player_dropdown.h diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 4f1a36161..5de7f7355 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -80,6 +80,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/audio/media_audio.h" #include "media/player/media_player_panel.h" #include "media/player/media_player_widget.h" +#include "media/player/media_player_dropdown.h" #include "media/player/media_player_volume_controller.h" #include "media/player/media_player_instance.h" #include "media/player/media_player_float.h" @@ -843,7 +844,10 @@ void MainWidget::createPlayer() { not_null item) { _controller->showPeerHistoryAtItem(item); }); - _playerVolume.create(this, _controller); + _playerVolume.create(this); + Media::Player::PrepareVolumeDropdown( + _playerVolume.data(), + _controller); _player->entity()->volumeWidgetCreated(_playerVolume); orderWidgets(); if (_a_show.animating()) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index e8ecf4a11..beb97010d 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -55,7 +55,7 @@ class Widget; namespace Media { namespace Player { class Widget; -class VolumeWidget; +class Dropdown; class Panel; struct TrackState; } // namespace Player @@ -367,7 +367,7 @@ private: object_ptr> _player = { nullptr }; - object_ptr _playerVolume = { nullptr }; + object_ptr _playerVolume = { nullptr }; object_ptr _playerPlaylist; bool _playerUsingPanel = false; diff --git a/Telegram/SourceFiles/media/player/media_player_dropdown.cpp b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp new file mode 100644 index 000000000..855c8e46e --- /dev/null +++ b/Telegram/SourceFiles/media/player/media_player_dropdown.cpp @@ -0,0 +1,169 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "media/player/media_player_dropdown.h" + +#include "ui/cached_round_corners.h" +#include "ui/widgets/shadow.h" +#include "styles/style_media_player.h" +#include "styles/style_widgets.h" + +namespace Media::Player { + +Dropdown::Dropdown(QWidget *parent) +: RpWidget(parent) +, _hideTimer([=] { startHide(); }) +, _showTimer([=] { startShow(); }) { + hide(); + + macWindowDeactivateEvents( + ) | rpl::filter([=] { + return !isHidden(); + }) | rpl::start_with_next([=] { + leaveEvent(nullptr); + }, lifetime()); + + hide(); + auto margin = getMargin(); + resize(margin.left() + st::mediaPlayerVolumeSize.width() + margin.right(), margin.top() + st::mediaPlayerVolumeSize.height() + margin.bottom()); +} + +QMargins Dropdown::getMargin() const { + const auto top = st::mediaPlayerHeight + + st::lineWidth + - st::mediaPlayerPlayTop + - st::mediaPlayerVolumeToggle.height; + return QMargins(st::mediaPlayerVolumeMargin, top, st::mediaPlayerVolumeMargin, st::mediaPlayerVolumeMargin); +} + +bool Dropdown::overlaps(const QRect &globalRect) { + if (isHidden() || _a_appearance.animating()) return false; + + return rect().marginsRemoved(getMargin()).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); +} + +void Dropdown::paintEvent(QPaintEvent *e) { + Painter p(this); + + if (!_cache.isNull()) { + bool animating = _a_appearance.animating(); + if (animating) { + p.setOpacity(_a_appearance.value(_hiding ? 0. : 1.)); + } else if (_hiding || isHidden()) { + hidingFinished(); + return; + } + p.drawPixmap(0, 0, _cache); + if (!animating) { + showChildren(); + _cache = QPixmap(); + } + return; + } + + // draw shadow + auto shadowedRect = rect().marginsRemoved(getMargin()); + auto shadowedSides = RectPart::Left | RectPart::Right | RectPart::Bottom; + Ui::Shadow::paint(p, shadowedRect, width(), st::defaultRoundShadow, shadowedSides); + auto parts = RectPart::NoTopBottom | RectPart::FullBottom; + Ui::FillRoundRect(p, QRect(shadowedRect.x(), -st::roundRadiusSmall, shadowedRect.width(), shadowedRect.y() + shadowedRect.height() + st::roundRadiusSmall), st::menuBg, Ui::MenuCorners, nullptr, parts); +} + +void Dropdown::enterEventHook(QEnterEvent *e) { + _hideTimer.cancel(); + if (_a_appearance.animating()) { + startShow(); + } else { + _showTimer.callOnce(0); + } + return RpWidget::enterEventHook(e); +} + +void Dropdown::leaveEventHook(QEvent *e) { + _showTimer.cancel(); + if (_a_appearance.animating()) { + startHide(); + } else { + _hideTimer.callOnce(300); + } + return RpWidget::leaveEventHook(e); +} + +void Dropdown::otherEnter() { + _hideTimer.cancel(); + if (_a_appearance.animating()) { + startShow(); + } else { + _showTimer.callOnce(0); + } +} + +void Dropdown::otherLeave() { + _showTimer.cancel(); + if (_a_appearance.animating()) { + startHide(); + } else { + _hideTimer.callOnce(0); + } +} + +void Dropdown::startShow() { + if (isHidden()) { + show(); + } else if (!_hiding) { + return; + } + _hiding = false; + startAnimation(); +} + +void Dropdown::startHide() { + if (_hiding) { + return; + } + + _hiding = true; + startAnimation(); +} + +void Dropdown::startAnimation() { + if (_cache.isNull()) { + showChildren(); + _cache = Ui::GrabWidget(this); + } + hideChildren(); + _a_appearance.start( + [=] { appearanceCallback(); }, + _hiding ? 1. : 0., + _hiding ? 0. : 1., + st::defaultInnerDropdown.duration); +} + +void Dropdown::appearanceCallback() { + if (!_a_appearance.animating() && _hiding) { + _hiding = false; + hidingFinished(); + } else { + update(); + } +} + +void Dropdown::hidingFinished() { + hide(); + _cache = QPixmap(); +} + +bool Dropdown::eventFilter(QObject *obj, QEvent *e) { + if (e->type() == QEvent::Enter) { + otherEnter(); + } else if (e->type() == QEvent::Leave) { + otherLeave(); + } + return false; +} + +} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_dropdown.h b/Telegram/SourceFiles/media/player/media_player_dropdown.h new file mode 100644 index 000000000..67e1dfe54 --- /dev/null +++ b/Telegram/SourceFiles/media/player/media_player_dropdown.h @@ -0,0 +1,52 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +#include "ui/rp_widget.h" +#include "ui/effects/animations.h" +#include "base/timer.h" + +namespace Media::Player { + +class Dropdown final : public Ui::RpWidget { +public: + explicit Dropdown(QWidget *parent); + + bool overlaps(const QRect &globalRect); + + QMargins getMargin() const; + +protected: + void paintEvent(QPaintEvent *e) override; + void enterEventHook(QEnterEvent *e) override; + void leaveEventHook(QEvent *e) override; + + bool eventFilter(QObject *obj, QEvent *e) override; + +private: + void startHide(); + void startShow(); + + void otherEnter(); + void otherLeave(); + + void appearanceCallback(); + void hidingFinished(); + void startAnimation(); + + bool _hiding = false; + + QPixmap _cache; + Ui::Animations::Simple _a_appearance; + + base::Timer _hideTimer; + base::Timer _showTimer; + +}; + +} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp index 9c524e1c7..37d058e8b 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_volume_controller.h" #include "media/audio/media_audio.h" +#include "media/player/media_player_dropdown.h" #include "ui/widgets/buttons.h" #include "ui/widgets/shadow.h" #include "ui/widgets/continuous_sliders.h" @@ -21,8 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_media_player.h" #include "styles/style_widgets.h" -namespace Media { -namespace Player { +namespace Media::Player { VolumeController::VolumeController( QWidget *parent, @@ -76,166 +76,28 @@ void VolumeController::applyVolumeChange(float64 volume) { } } -VolumeWidget::VolumeWidget( - QWidget *parent, - not_null controller) -: RpWidget(parent) -, _controller(this, controller) -, _hideTimer([=] { startHide(); }) -, _showTimer([=] { startShow(); }) { - hide(); - _controller->setIsVertical(true); +void PrepareVolumeDropdown( + not_null dropdown, + not_null controller) { + const auto volume = Ui::CreateChild( + dropdown.get(), + controller); + volume->show(); + volume->setIsVertical(true); - macWindowDeactivateEvents( - ) | rpl::filter([=] { - return !isHidden(); - }) | rpl::start_with_next([=] { - leaveEvent(nullptr); - }, lifetime()); - - hide(); - auto margin = getMargin(); - resize(margin.left() + st::mediaPlayerVolumeSize.width() + margin.right(), margin.top() + st::mediaPlayerVolumeSize.height() + margin.bottom()); + dropdown->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + const auto rect = QRect(QPoint(), size); + const auto inner = rect.marginsRemoved(dropdown->getMargin()); + volume->setGeometry( + inner.x(), + inner.y() - st::lineWidth, + inner.width(), + (inner.height() + + st::lineWidth + - ((st::mediaPlayerVolumeSize.width() + - st::mediaPlayerPanelPlayback.width) / 2))); + }, volume->lifetime()); } -QMargins VolumeWidget::getMargin() const { - const auto top = st::mediaPlayerHeight - + st::lineWidth - - st::mediaPlayerPlayTop - - st::mediaPlayerVolumeToggle.height; - return QMargins(st::mediaPlayerVolumeMargin, top, st::mediaPlayerVolumeMargin, st::mediaPlayerVolumeMargin); -} - -bool VolumeWidget::overlaps(const QRect &globalRect) { - if (isHidden() || _a_appearance.animating()) return false; - - return rect().marginsRemoved(getMargin()).contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size())); -} - -void VolumeWidget::resizeEvent(QResizeEvent *e) { - auto inner = rect().marginsRemoved(getMargin()); - _controller->setGeometry(inner.x(), inner.y() - st::lineWidth, inner.width(), inner.height() + st::lineWidth - ((st::mediaPlayerVolumeSize.width() - st::mediaPlayerPanelPlayback.width) / 2)); -} - -void VolumeWidget::paintEvent(QPaintEvent *e) { - Painter p(this); - - if (!_cache.isNull()) { - bool animating = _a_appearance.animating(); - if (animating) { - p.setOpacity(_a_appearance.value(_hiding ? 0. : 1.)); - } else if (_hiding || isHidden()) { - hidingFinished(); - return; - } - p.drawPixmap(0, 0, _cache); - if (!animating) { - showChildren(); - _cache = QPixmap(); - } - return; - } - - // draw shadow - auto shadowedRect = rect().marginsRemoved(getMargin()); - auto shadowedSides = RectPart::Left | RectPart::Right | RectPart::Bottom; - Ui::Shadow::paint(p, shadowedRect, width(), st::defaultRoundShadow, shadowedSides); - auto parts = RectPart::NoTopBottom | RectPart::FullBottom; - Ui::FillRoundRect(p, QRect(shadowedRect.x(), -st::roundRadiusSmall, shadowedRect.width(), shadowedRect.y() + shadowedRect.height() + st::roundRadiusSmall), st::menuBg, Ui::MenuCorners, nullptr, parts); -} - -void VolumeWidget::enterEventHook(QEnterEvent *e) { - _hideTimer.cancel(); - if (_a_appearance.animating()) { - startShow(); - } else { - _showTimer.callOnce(0); - } - return RpWidget::enterEventHook(e); -} - -void VolumeWidget::leaveEventHook(QEvent *e) { - _showTimer.cancel(); - if (_a_appearance.animating()) { - startHide(); - } else { - _hideTimer.callOnce(300); - } - return RpWidget::leaveEventHook(e); -} - -void VolumeWidget::otherEnter() { - _hideTimer.cancel(); - if (_a_appearance.animating()) { - startShow(); - } else { - _showTimer.callOnce(0); - } -} - -void VolumeWidget::otherLeave() { - _showTimer.cancel(); - if (_a_appearance.animating()) { - startHide(); - } else { - _hideTimer.callOnce(0); - } -} - -void VolumeWidget::startShow() { - if (isHidden()) { - show(); - } else if (!_hiding) { - return; - } - _hiding = false; - startAnimation(); -} - -void VolumeWidget::startHide() { - if (_hiding) { - return; - } - - _hiding = true; - startAnimation(); -} - -void VolumeWidget::startAnimation() { - if (_cache.isNull()) { - showChildren(); - _cache = Ui::GrabWidget(this); - } - hideChildren(); - _a_appearance.start( - [=] { appearanceCallback(); }, - _hiding ? 1. : 0., - _hiding ? 0. : 1., - st::defaultInnerDropdown.duration); -} - -void VolumeWidget::appearanceCallback() { - if (!_a_appearance.animating() && _hiding) { - _hiding = false; - hidingFinished(); - } else { - update(); - } -} - -void VolumeWidget::hidingFinished() { - hide(); - _cache = QPixmap(); -} - -bool VolumeWidget::eventFilter(QObject *obj, QEvent *e) { - if (e->type() == QEvent::Enter) { - otherEnter(); - } else if (e->type() == QEvent::Leave) { - otherLeave(); - } - return false; -} - -} // namespace Player -} // namespace Media +} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_volume_controller.h b/Telegram/SourceFiles/media/player/media_player_volume_controller.h index bda9d80fe..05a9da23b 100644 --- a/Telegram/SourceFiles/media/player/media_player_volume_controller.h +++ b/Telegram/SourceFiles/media/player/media_player_volume_controller.h @@ -7,13 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once -#include "ui/effects/animations.h" #include "ui/rp_widget.h" #include "base/object_ptr.h" -#include "base/timer.h" namespace Ui { -class IconButton; class MediaSlider; } // namespace Ui @@ -21,8 +18,9 @@ namespace Window { class SessionController; } // namespace Window -namespace Media { -namespace Player { +namespace Media::Player { + +class Dropdown; class VolumeController final : public Ui::RpWidget { public: @@ -43,46 +41,8 @@ private: }; -class VolumeWidget final : public Ui::RpWidget { -public: - VolumeWidget( - QWidget *parent, - not_null controller); +void PrepareVolumeDropdown( + not_null dropdown, + not_null controller); - bool overlaps(const QRect &globalRect); - - QMargins getMargin() const; - -protected: - void resizeEvent(QResizeEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void enterEventHook(QEnterEvent *e) override; - void leaveEventHook(QEvent *e) override; - - bool eventFilter(QObject *obj, QEvent *e) override; - -private: - void startHide(); - void startShow(); - - void otherEnter(); - void otherLeave(); - - void appearanceCallback(); - void hidingFinished(); - void startAnimation(); - - bool _hiding = false; - - QPixmap _cache; - Ui::Animations::Simple _a_appearance; - - object_ptr _controller; - - base::Timer _hideTimer; - base::Timer _showTimer; - -}; - -} // namespace Player -} // namespace Media +} // namespace Media::Player diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 86c9dce6e..dee206a2b 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -26,7 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/view/media_view_playback_progress.h" #include "media/player/media_player_button.h" #include "media/player/media_player_instance.h" -#include "media/player/media_player_volume_controller.h" +#include "media/player/media_player_dropdown.h" #include "styles/style_media_player.h" #include "styles/style_media_view.h" #include "history/history_item.h" @@ -239,7 +239,7 @@ Widget::Widget(QWidget *parent, not_null session) , _timeLabel(this, st::mediaPlayerTime) , _playPause(this) , _volumeToggle(this, st::mediaPlayerVolumeToggle) -, _repeatTrack(this, st::mediaPlayerRepeatButton) +, _repeatToggle(this, st::mediaPlayerRepeatButton) , _playbackSpeed(this, st::mediaPlayerSpeedButton) , _close(this, st::mediaPlayerClose) , _shadow(this) @@ -291,7 +291,7 @@ Widget::Widget(QWidget *parent, not_null session) }, lifetime()); updateRepeatTrackIcon(); - _repeatTrack->setClickedCallback([=] { + _repeatToggle->setClickedCallback([=] { instance()->toggleRepeat(AudioMsgId::Type::Song); }); @@ -394,7 +394,7 @@ QPoint Widget::getPositionForVolumeWidget() const { return QPoint(x, height()); } -void Widget::volumeWidgetCreated(VolumeWidget *widget) { +void Widget::volumeWidgetCreated(Dropdown *widget) { _volumeToggle->installEventFilter(widget); } @@ -433,7 +433,7 @@ void Widget::updateControlsGeometry() { if (hasPlaybackSpeedControl()) { _playbackSpeed->moveToRight(right, st::mediaPlayerPlayTop); right += _playbackSpeed->width(); } - _repeatTrack->moveToRight(right, st::mediaPlayerPlayTop); right += _repeatTrack->width(); + _repeatToggle->moveToRight(right, st::mediaPlayerPlayTop); right += _repeatToggle->width(); _volumeToggle->moveToRight(right, st::mediaPlayerPlayTop); right += _volumeToggle->width(); updatePlayPrevNextPositions(); @@ -520,7 +520,7 @@ int Widget::getLabelsLeft() const { int Widget::getLabelsRight() const { auto result = st::mediaPlayerCloseRight + _close->width(); if (_type == AudioMsgId::Type::Song) { - result += _repeatTrack->width() + _volumeToggle->width(); + result += _repeatToggle->width() + _volumeToggle->width(); } if (hasPlaybackSpeedControl()) { result += _playbackSpeed->width(); @@ -543,8 +543,8 @@ void Widget::updateLabelsGeometry() { void Widget::updateRepeatTrackIcon() { auto repeating = instance()->repeatEnabled(AudioMsgId::Type::Song); - _repeatTrack->setIconOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledIcon, repeating ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); - _repeatTrack->setRippleColorOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); + _repeatToggle->setIconOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledIcon, repeating ? nullptr : &st::mediaPlayerRepeatDisabledIconOver); + _repeatToggle->setRippleColorOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); } void Widget::checkForTypeChange() { @@ -567,7 +567,7 @@ bool Widget::hasPlaybackSpeedControl() const { } void Widget::updateControlsVisibility() { - _repeatTrack->setVisible(_type == AudioMsgId::Type::Song); + _repeatToggle->setVisible(_type == AudioMsgId::Type::Song); _volumeToggle->setVisible(_type == AudioMsgId::Type::Song); _playbackSpeed->setVisible(hasPlaybackSpeedControl()); if (!_shadow->isHidden()) { diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index a676af2cb..0373bec7b 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -36,7 +36,7 @@ namespace Player { class PlayButton; class SpeedButton; -class VolumeWidget; +class Dropdown; struct TrackState; class Widget : public Ui::RpWidget, private base::Subscriber { @@ -51,7 +51,7 @@ public: void hideShadow(); QPoint getPositionForVolumeWidget() const; - void volumeWidgetCreated(VolumeWidget *widget); + void volumeWidgetCreated(Dropdown *widget); ~Widget(); @@ -120,7 +120,7 @@ private: object_ptr _playPause; object_ptr _nextTrack = { nullptr }; object_ptr _volumeToggle; - object_ptr _repeatTrack; + object_ptr _repeatToggle; object_ptr _playbackSpeed; object_ptr _close; object_ptr _shadow = { nullptr }; diff --git a/Telegram/cmake/td_ui.cmake b/Telegram/cmake/td_ui.cmake index 038f8673a..a087769b5 100644 --- a/Telegram/cmake/td_ui.cmake +++ b/Telegram/cmake/td_ui.cmake @@ -80,6 +80,9 @@ PRIVATE media/clip/media_clip_reader.cpp media/clip/media_clip_reader.h + media/player/media_player_dropdown.cpp + media/player/media_player_dropdown.h + passport/ui/passport_details_row.cpp passport/ui/passport_details_row.h passport/ui/passport_form_row.cpp