diff --git a/Telegram/Resources/icons/voice2x.png b/Telegram/Resources/icons/voice2x.png deleted file mode 100644 index f01a81116..000000000 Binary files a/Telegram/Resources/icons/voice2x.png and /dev/null differ diff --git a/Telegram/Resources/icons/voice2x@2x.png b/Telegram/Resources/icons/voice2x@2x.png deleted file mode 100644 index a37c17097..000000000 Binary files a/Telegram/Resources/icons/voice2x@2x.png and /dev/null differ diff --git a/Telegram/Resources/icons/voice2x@3x.png b/Telegram/Resources/icons/voice2x@3x.png deleted file mode 100644 index 6f60123ce..000000000 Binary files a/Telegram/Resources/icons/voice2x@3x.png and /dev/null differ diff --git a/Telegram/Resources/icons/voice_speed/voice_speed0.5.png b/Telegram/Resources/icons/voice_speed/voice_speed0.5.png new file mode 100644 index 000000000..a209f0ff1 Binary files /dev/null and b/Telegram/Resources/icons/voice_speed/voice_speed0.5.png differ diff --git a/Telegram/Resources/icons/voice_speed/voice_speed0.5@2x.png b/Telegram/Resources/icons/voice_speed/voice_speed0.5@2x.png new file mode 100644 index 000000000..ee279c333 Binary files /dev/null and b/Telegram/Resources/icons/voice_speed/voice_speed0.5@2x.png differ diff --git a/Telegram/Resources/icons/voice_speed/voice_speed0.5@3x.png b/Telegram/Resources/icons/voice_speed/voice_speed0.5@3x.png new file mode 100644 index 000000000..2d7c14d6c Binary files /dev/null and b/Telegram/Resources/icons/voice_speed/voice_speed0.5@3x.png differ diff --git a/Telegram/Resources/icons/voice_speed/voice_speed1.5.png b/Telegram/Resources/icons/voice_speed/voice_speed1.5.png new file mode 100644 index 000000000..cae61e3e9 Binary files /dev/null and b/Telegram/Resources/icons/voice_speed/voice_speed1.5.png differ diff --git a/Telegram/Resources/icons/voice_speed/voice_speed1.5@2x.png b/Telegram/Resources/icons/voice_speed/voice_speed1.5@2x.png new file mode 100644 index 000000000..680a02eb9 Binary files /dev/null and b/Telegram/Resources/icons/voice_speed/voice_speed1.5@2x.png differ diff --git a/Telegram/Resources/icons/voice_speed/voice_speed1.5@3x.png b/Telegram/Resources/icons/voice_speed/voice_speed1.5@3x.png new file mode 100644 index 000000000..07faa2ea1 Binary files /dev/null and b/Telegram/Resources/icons/voice_speed/voice_speed1.5@3x.png differ diff --git a/Telegram/Resources/icons/voice_speed/voice_speed2.png b/Telegram/Resources/icons/voice_speed/voice_speed2.png new file mode 100644 index 000000000..f2cda767d Binary files /dev/null and b/Telegram/Resources/icons/voice_speed/voice_speed2.png differ diff --git a/Telegram/Resources/icons/voice_speed/voice_speed2@2x.png b/Telegram/Resources/icons/voice_speed/voice_speed2@2x.png new file mode 100644 index 000000000..22d3ee280 Binary files /dev/null and b/Telegram/Resources/icons/voice_speed/voice_speed2@2x.png differ diff --git a/Telegram/Resources/icons/voice_speed/voice_speed2@3x.png b/Telegram/Resources/icons/voice_speed/voice_speed2@3x.png new file mode 100644 index 000000000..facf359c7 Binary files /dev/null and b/Telegram/Resources/icons/voice_speed/voice_speed2@3x.png differ diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index c909cdaec..763b1cc00 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -2855,6 +2855,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_photo_editor_menu_flip" = "Flip"; "lng_photo_editor_menu_duplicate" = "Duplicate"; +"lng_voice_speed_slow" = "Slow"; +"lng_voice_speed_normal" = "Normal"; +"lng_voice_speed_fast" = "Fast"; +"lng_voice_speed_very_fast" = "Very fast"; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/SourceFiles/media/player/media_player.style b/Telegram/SourceFiles/media/player/media_player.style index 493675cd6..2304220f9 100644 --- a/Telegram/SourceFiles/media/player/media_player.style +++ b/Telegram/SourceFiles/media/player/media_player.style @@ -77,9 +77,9 @@ mediaPlayerSpeedButton: IconButton { height: 30px; icon: icon { - { "voice2x", mediaPlayerActiveFg, point(8px, 11px) } + { "voice_speed/voice_speed2", mediaPlayerActiveFg } }; - iconPosition: point(0px, 0px); + iconPosition: point(3px, 10px); rippleAreaPosition: point(3px, 5px); rippleAreaSize: 25px; @@ -88,15 +88,38 @@ mediaPlayerSpeedButton: IconButton { } } mediaPlayerSpeedDisabledIcon: icon { - { "voice2x", menuIconFg, point(8px, 11px)} + { "voice_speed/voice_speed2", menuIconFg } }; mediaPlayerSpeedDisabledIconOver: icon { - { "voice2x", menuIconFgOver, point(8px, 11px)} + { "voice_speed/voice_speed2", menuIconFgOver } +}; +mediaPlayerSpeedSlowIcon: icon { + { "voice_speed/voice_speed0.5", mediaPlayerActiveFg } +}; +mediaPlayerSpeedSlowDisabledIcon: icon { + { "voice_speed/voice_speed0.5", menuIconFg } +}; +mediaPlayerSpeedSlowDisabledIconOver: icon { + { "voice_speed/voice_speed0.5", menuIconFgOver } +}; +mediaPlayerSpeedFastIcon: icon { + { "voice_speed/voice_speed1.5", mediaPlayerActiveFg } +}; +mediaPlayerSpeedFastDisabledIcon: icon { + { "voice_speed/voice_speed1.5", menuIconFg } +}; +mediaPlayerSpeedFastDisabledIconOver: icon { + { "voice_speed/voice_speed1.5", menuIconFgOver } }; mediaPlayerSpeedDisabledRippleBg: windowBgOver; -mediaPlayerSpeedInactiveIcon: icon { - { "voice2x", mediaPlayerInactiveFg, point(8px, 11px)} -}; + +mediaPlayerPopupMenu: PopupMenu(defaultPopupMenu) { + menu: Menu(defaultMenu) { + itemIconPosition: point(6px, 5px); + itemPadding: margins(34px, 8px, 17px, 7px); + } +} +mediaPlayerMenuCheck: icon {{ "player_check", mediaPlayerActiveFg }}; mediaPlayerVolumeIcon0: icon { { "player_volume0", mediaPlayerActiveFg }, diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index ffc0421ad..fa60aa915 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -17,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/widgets/continuous_sliders.h" #include "ui/widgets/shadow.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/popup_menu.h" #include "ui/effects/ripple_animation.h" #include "ui/text/format_values.h" #include "ui/text/format_song_document_name.h" @@ -60,6 +61,153 @@ private: }; +class Widget::SpeedButton : public Ui::IconButton { +public: + SpeedButton(QWidget *parent, const style::IconButton &st); + + [[nodiscard]] rpl::producer<> saved() const; + +protected: + void contextMenuEvent(QContextMenuEvent *e) override; + +private: + class SpeedController final { + public: + SpeedController() { + setSpeed(Core::App().settings().voicePlaybackSpeed()); + } + + [[nodiscard]] rpl::producer speedValue() const { + return _speedChanged.events_starting_with(speed()); + } + [[nodiscard]] rpl::producer<> saved() const { + return _saved.events(); + } + [[nodiscard]] float64 speed() const { + return _isDefault ? 1. : _speed; + } + [[nodiscard]] bool isDefault() const { + return _isDefault; + } + [[nodiscard]] float64 lastNonDefaultSpeed() const { + return _speed; + } + void toggleDefault() { + _isDefault = !_isDefault; + _speedChanged.fire(speed()); + } + void setSpeed(float64 newSpeed) { + if (!(_isDefault = (newSpeed == 1.))) { + _speed = newSpeed; + } + _speedChanged.fire(speed()); + } + void save() { + Core::App().settings().setVoicePlaybackSpeed(speed()); + Core::App().saveSettingsDelayed(); + _saved.fire({}); + } + + private: + float64 _speed = 2.; + bool _isDefault = true; + rpl::event_stream _speedChanged; + rpl::event_stream<> _saved; + }; + + SpeedController _speed; + + base::unique_qptr _menu; + +}; + +Widget::SpeedButton::SpeedButton(QWidget *parent, const style::IconButton &st) +: IconButton(parent, st) { + setClickedCallback([=] { + _speed.toggleDefault(); + _speed.save(); + }); + + struct Icons { + const style::icon *icon = nullptr; + const style::icon *over = nullptr; + }; + + _speed.speedValue( + ) | rpl::start_with_next([=](float64 speed) { + const auto isDefaultSpeed = _speed.isDefault(); + const auto nonDefaultSpeed = _speed.lastNonDefaultSpeed(); + + const auto icons = [&]() -> Icons { + if (nonDefaultSpeed == .5) { + return { + .icon = isDefaultSpeed + ? &st::mediaPlayerSpeedSlowDisabledIcon + : &st::mediaPlayerSpeedSlowIcon, + .over = isDefaultSpeed + ? &st::mediaPlayerSpeedSlowDisabledIconOver + : &st::mediaPlayerSpeedSlowIcon, + }; + } else if (nonDefaultSpeed == 1.5) { + return { + .icon = isDefaultSpeed + ? &st::mediaPlayerSpeedFastDisabledIcon + : &st::mediaPlayerSpeedFastIcon, + .over = isDefaultSpeed + ? &st::mediaPlayerSpeedFastDisabledIconOver + : &st::mediaPlayerSpeedFastIcon, + }; + } else { + return { + .icon = isDefaultSpeed + ? &st::mediaPlayerSpeedDisabledIcon + : nullptr, // 2x icon. + .over = isDefaultSpeed + ? &st::mediaPlayerSpeedDisabledIconOver + : nullptr, // 2x icon. + }; + } + }(); + + setIconOverride(icons.icon, icons.over); + setRippleColorOverride(isDefaultSpeed + ? &st::mediaPlayerSpeedDisabledRippleBg + : nullptr); + }, lifetime()); +} + +void Widget::SpeedButton::contextMenuEvent(QContextMenuEvent *e) { + _menu = base::make_unique_q( + this, + st::mediaPlayerPopupMenu); + + const auto setPlaybackSpeed = [=](float64 speed) { + _speed.setSpeed(speed); + _speed.save(); + }; + + const auto currentSpeed = _speed.speed(); + const auto addSpeed = [&](float64 speed, QString text = QString()) { + if (text.isEmpty()) { + text = QString::number(speed); + } + const auto action = _menu->addAction( + text, + [=] { setPlaybackSpeed(speed); }, + (speed == currentSpeed) ? &st::mediaPlayerMenuCheck : nullptr); + }; + addSpeed(0.5, tr::lng_voice_speed_slow(tr::now)); + addSpeed(1., tr::lng_voice_speed_normal(tr::now)); + addSpeed(1.5, tr::lng_voice_speed_fast(tr::now)); + addSpeed(2., tr::lng_voice_speed_very_fast(tr::now)); + + _menu->popup(e->globalPos()); +} + +rpl::producer<> Widget::SpeedButton::saved() const { + return _speed.saved(); +} + Widget::PlayButton::PlayButton(QWidget *parent) : Ui::RippleButton(parent, st::mediaPlayerButton.ripple) , _layout(st::mediaPlayerButton, [this] { update(); }) { resize(st::mediaPlayerButtonSize); @@ -146,15 +294,10 @@ Widget::Widget(QWidget *parent, not_null session) instance()->toggleRepeat(AudioMsgId::Type::Song); }); - updatePlaybackSpeedIcon(); - _playbackSpeed->setClickedCallback([=] { - const auto doubled = (Core::App().settings().voicePlaybackSpeed() - == 2.); - Core::App().settings().setVoicePlaybackSpeed(doubled ? 1. : 2.); + _playbackSpeed->saved( + ) | rpl::start_with_next([=] { instance()->updateVoicePlaybackSpeed(); - updatePlaybackSpeedIcon(); - Core::App().saveSettingsDelayed(); - }); + }, lifetime()); subscribe(instance()->repeatChangedNotifier(), [this](AudioMsgId::Type type) { if (type == _type) { @@ -403,16 +546,6 @@ void Widget::updateRepeatTrackIcon() { _repeatTrack->setRippleColorOverride(repeating ? nullptr : &st::mediaPlayerRepeatDisabledRippleBg); } -void Widget::updatePlaybackSpeedIcon() { - const auto speed = Core::App().settings().voicePlaybackSpeed(); - const auto isDefaultSpeed = (speed == 1.); - _playbackSpeed->setIconOverride( - isDefaultSpeed ? &st::mediaPlayerSpeedDisabledIcon : nullptr, - isDefaultSpeed ? &st::mediaPlayerSpeedDisabledIconOver : nullptr); - _playbackSpeed->setRippleColorOverride( - isDefaultSpeed ? &st::mediaPlayerSpeedDisabledRippleBg : nullptr); -} - void Widget::checkForTypeChange() { auto hasActiveType = [](AudioMsgId::Type type) { const auto current = instance()->current(type); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.h b/Telegram/SourceFiles/media/player/media_player_widget.h index e9d3bd747..a676af2cb 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.h +++ b/Telegram/SourceFiles/media/player/media_player_widget.h @@ -35,6 +35,7 @@ namespace Media { namespace Player { class PlayButton; +class SpeedButton; class VolumeWidget; struct TrackState; @@ -77,7 +78,6 @@ private: void updateRepeatTrackIcon(); void updateControlsVisibility(); void updateControlsGeometry(); - void updatePlaybackSpeedIcon(); void createPrevNextButtons(); void destroyPrevNextButtons(); @@ -113,6 +113,7 @@ private: bool _labelsDown = false; class PlayButton; + class SpeedButton; object_ptr _nameLabel; object_ptr _timeLabel; object_ptr _previousTrack = { nullptr }; @@ -120,7 +121,7 @@ private: object_ptr _nextTrack = { nullptr }; object_ptr _volumeToggle; object_ptr _repeatTrack; - object_ptr _playbackSpeed; + object_ptr _playbackSpeed; object_ptr _close; object_ptr _shadow = { nullptr }; object_ptr _playbackSlider;