Added context menu to voice playback speed button.
Fixed #16868. Fixed #16138.
Before Width: | Height: | Size: 479 B |
Before Width: | Height: | Size: 1,015 B |
Before Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/voice_speed/voice_speed0.5.png
Normal file
After Width: | Height: | Size: 637 B |
BIN
Telegram/Resources/icons/voice_speed/voice_speed0.5@2x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/voice_speed/voice_speed0.5@3x.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
Telegram/Resources/icons/voice_speed/voice_speed1.5.png
Normal file
After Width: | Height: | Size: 603 B |
BIN
Telegram/Resources/icons/voice_speed/voice_speed1.5@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/voice_speed/voice_speed1.5@3x.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Telegram/Resources/icons/voice_speed/voice_speed2.png
Normal file
After Width: | Height: | Size: 581 B |
BIN
Telegram/Resources/icons/voice_speed/voice_speed2@2x.png
Normal file
After Width: | Height: | Size: 1 KiB |
BIN
Telegram/Resources/icons/voice_speed/voice_speed2@3x.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
|
@ -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...";
|
||||
|
|
|
@ -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 },
|
||||
|
|
|
@ -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<float64> 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<float64> _speedChanged;
|
||||
rpl::event_stream<> _saved;
|
||||
};
|
||||
|
||||
SpeedController _speed;
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> _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<Ui::PopupMenu>(
|
||||
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<Main::Session*> 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);
|
||||
|
|
|
@ -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<Ui::FlatLabel> _nameLabel;
|
||||
object_ptr<Ui::LabelSimple> _timeLabel;
|
||||
object_ptr<Ui::IconButton> _previousTrack = { nullptr };
|
||||
|
@ -120,7 +121,7 @@ private:
|
|||
object_ptr<Ui::IconButton> _nextTrack = { nullptr };
|
||||
object_ptr<Ui::IconButton> _volumeToggle;
|
||||
object_ptr<Ui::IconButton> _repeatTrack;
|
||||
object_ptr<Ui::IconButton> _playbackSpeed;
|
||||
object_ptr<SpeedButton> _playbackSpeed;
|
||||
object_ptr<Ui::IconButton> _close;
|
||||
object_ptr<Ui::PlainShadow> _shadow = { nullptr };
|
||||
object_ptr<Ui::FilledSlider> _playbackSlider;
|
||||
|
|