Implement new voice speed change control design.
|
@ -946,8 +946,6 @@ PRIVATE
|
|||
media/audio/media_audio_track.h
|
||||
media/audio/media_child_ffmpeg_loader.cpp
|
||||
media/audio/media_child_ffmpeg_loader.h
|
||||
media/player/media_player_button.cpp
|
||||
media/player/media_player_button.h
|
||||
media/player/media_player_float.cpp
|
||||
media/player/media_player_float.h
|
||||
media/player/media_player_instance.cpp
|
||||
|
|
Before Width: | Height: | Size: 284 B After Width: | Height: | Size: 265 B |
Before Width: | Height: | Size: 410 B After Width: | Height: | Size: 412 B |
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 556 B |
Before Width: | Height: | Size: 314 B After Width: | Height: | Size: 328 B |
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 557 B |
Before Width: | Height: | Size: 736 B After Width: | Height: | Size: 764 B |
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 307 B |
Before Width: | Height: | Size: 510 B After Width: | Height: | Size: 556 B |
Before Width: | Height: | Size: 775 B After Width: | Height: | Size: 804 B |
Before Width: | Height: | Size: 540 B After Width: | Height: | Size: 622 B |
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 412 B After Width: | Height: | Size: 534 B |
Before Width: | Height: | Size: 665 B After Width: | Height: | Size: 847 B |
Before Width: | Height: | Size: 951 B After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 306 B After Width: | Height: | Size: 532 B |
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 825 B |
Before Width: | Height: | Size: 647 B After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 314 B After Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 527 B After Width: | Height: | Size: 581 B |
Before Width: | Height: | Size: 772 B After Width: | Height: | Size: 832 B |
Before Width: | Height: | Size: 229 B After Width: | Height: | Size: 230 B |
Before Width: | Height: | Size: 306 B After Width: | Height: | Size: 310 B |
Before Width: | Height: | Size: 422 B After Width: | Height: | Size: 427 B |
Before Width: | Height: | Size: 298 B After Width: | Height: | Size: 295 B |
Before Width: | Height: | Size: 437 B After Width: | Height: | Size: 478 B |
Before Width: | Height: | Size: 609 B After Width: | Height: | Size: 646 B |
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 534 B |
Before Width: | Height: | Size: 606 B After Width: | Height: | Size: 888 B |
Before Width: | Height: | Size: 1,011 B After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 531 B |
Before Width: | Height: | Size: 731 B After Width: | Height: | Size: 902 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 198 B |
Before Width: | Height: | Size: 212 B |
Before Width: | Height: | Size: 298 B |
Before Width: | Height: | Size: 514 B After Width: | Height: | Size: 396 B |
Before Width: | Height: | Size: 924 B After Width: | Height: | Size: 568 B |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 815 B |
BIN
Telegram/Resources/icons/player/player_speed.png
Normal file
After Width: | Height: | Size: 152 B |
BIN
Telegram/Resources/icons/player/player_speed@2x.png
Normal file
After Width: | Height: | Size: 208 B |
BIN
Telegram/Resources/icons/player/player_speed@3x.png
Normal file
After Width: | Height: | Size: 255 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_0.5.png
Normal file
After Width: | Height: | Size: 536 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_0.5@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_0.5@3x.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.0.png
Normal file
After Width: | Height: | Size: 478 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.0@2x.png
Normal file
After Width: | Height: | Size: 947 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.0@3x.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.2.png
Normal file
After Width: | Height: | Size: 502 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.2@2x.png
Normal file
After Width: | Height: | Size: 939 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.2@3x.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.5.png
Normal file
After Width: | Height: | Size: 522 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.5@2x.png
Normal file
After Width: | Height: | Size: 1,020 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.5@3x.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.7.png
Normal file
After Width: | Height: | Size: 475 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.7@2x.png
Normal file
After Width: | Height: | Size: 889 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_1.7@3x.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_2.0.png
Normal file
After Width: | Height: | Size: 463 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_2.0@2x.png
Normal file
After Width: | Height: | Size: 817 B |
BIN
Telegram/Resources/icons/player/speed/audiospeed_menu_2.0@3x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 559 B |
Before Width: | Height: | Size: 1 KiB |
Before Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 542 B |
Before Width: | Height: | Size: 1,017 B |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 530 B |
Before Width: | Height: | Size: 928 B |
Before Width: | Height: | Size: 1.4 KiB |
|
@ -3552,8 +3552,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
"lng_voice_speed_slow" = "Slow";
|
||||
"lng_voice_speed_normal" = "Normal";
|
||||
"lng_voice_speed_medium" = "Medium";
|
||||
"lng_voice_speed_fast" = "Fast";
|
||||
"lng_voice_speed_very_fast" = "Very fast";
|
||||
"lng_voice_speed_super_fast" = "Super fast";
|
||||
|
||||
"lng_view_button_user" = "View user";
|
||||
"lng_view_button_bot" = "View bot";
|
||||
|
|
|
@ -3193,6 +3193,9 @@ void HistoryItem::createComponents(const MTPDmessage &data) {
|
|||
}
|
||||
}
|
||||
const auto id = data.vreply_to_msg_id().v;
|
||||
if (data.is_reply_to_scheduled()) {
|
||||
int a = 0;
|
||||
}
|
||||
config.replyTo = data.is_reply_to_scheduled()
|
||||
? _history->owner().scheduledMessages().localMessageId(id)
|
||||
: id;
|
||||
|
|
|
@ -491,7 +491,7 @@ void Mixer::Track::updateWithSpeedPosition() {
|
|||
int64 Mixer::Track::SpeedIndependentPosition(
|
||||
int64 position,
|
||||
float64 speed) {
|
||||
Expects(speed < 2.5);
|
||||
Expects(speed <= Audio::kSpeedMax);
|
||||
|
||||
return int64(base::SafeRound(position * speed));
|
||||
}
|
||||
|
@ -499,7 +499,7 @@ int64 Mixer::Track::SpeedIndependentPosition(
|
|||
int64 Mixer::Track::SpeedDependentPosition(
|
||||
int64 position,
|
||||
float64 speed) {
|
||||
Expects(speed >= 0.5);
|
||||
Expects(speed >= Audio::kSpeedMin);
|
||||
|
||||
return int64(base::SafeRound(position / speed));
|
||||
}
|
||||
|
|
|
@ -10,6 +10,50 @@ using "ui/basic.style";
|
|||
using "ui/widgets/widgets.style";
|
||||
using "overview/overview.style";
|
||||
|
||||
MediaPlayerButton {
|
||||
playPosition: point;
|
||||
playOuter: size;
|
||||
pausePosition: point;
|
||||
pauseOuter: size;
|
||||
pauseStroke: pixels;
|
||||
cancelPosition: point;
|
||||
cancelOuter: size;
|
||||
cancelStroke: pixels;
|
||||
|
||||
rippleAreaPosition: point;
|
||||
rippleAreaSize: pixels;
|
||||
ripple: RippleAnimation;
|
||||
|
||||
duration: int;
|
||||
}
|
||||
|
||||
MediaSpeedButton {
|
||||
width: pixels;
|
||||
height: pixels;
|
||||
font: font;
|
||||
icon: icon;
|
||||
}
|
||||
|
||||
MediaSpeedMenu {
|
||||
menu: Menu;
|
||||
iconFg: color;
|
||||
iconFgActive: color;
|
||||
textFgActive: color;
|
||||
activeCheck: icon;
|
||||
activeCheckSkip: pixels;
|
||||
sliderStyle: TextStyle;
|
||||
sliderPadding: margins;
|
||||
sliderWidth: pixels;
|
||||
slider: MediaSlider;
|
||||
}
|
||||
|
||||
mediaSpeedButton: MediaSpeedButton {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font: font(11px semibold);
|
||||
icon: icon{{ "player/player_speed", menuIconFg }};
|
||||
}
|
||||
|
||||
mediaPlayerButton: MediaPlayerButton {
|
||||
playPosition: point(2px, 0px);
|
||||
playOuter: size(17px, 15px);
|
||||
|
@ -25,6 +69,8 @@ mediaPlayerButton: MediaPlayerButton {
|
|||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: lightButtonBgOver;
|
||||
}
|
||||
|
||||
duration: 200;
|
||||
}
|
||||
mediaPlayerWideWidth: 460px;
|
||||
mediaPlayerHeight: 35px;
|
||||
|
@ -96,59 +142,64 @@ mediaPlayerCancelIcon: icon{
|
|||
{ "player/panel_close", mediaPlayerActiveFg }
|
||||
};
|
||||
|
||||
mediaPlayerSpeedButton: IconButton {
|
||||
width: 31px;
|
||||
height: 30px;
|
||||
|
||||
icon: icon {
|
||||
{ "player/voice_speed/voice_speed2", mediaPlayerActiveFg }
|
||||
};
|
||||
iconPosition: point(3px, 10px);
|
||||
|
||||
rippleAreaPosition: point(3px, 5px);
|
||||
rippleAreaSize: 25px;
|
||||
ripple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: lightButtonBgOver;
|
||||
}
|
||||
mediaPlayerSpeedSize: size(30px, 30px);
|
||||
mediaPlayerSpeedRadius: 4px;
|
||||
mediaPlayerSpeedRipple: RippleAnimation(defaultRippleAnimation) {
|
||||
color: lightButtonBgOver;
|
||||
}
|
||||
mediaPlayerSpeedDisabledIcon: icon {
|
||||
{ "player/voice_speed/voice_speed2", menuIconFg }
|
||||
};
|
||||
mediaPlayerSpeedDisabledIconOver: icon {
|
||||
{ "player/voice_speed/voice_speed2", menuIconFgOver }
|
||||
};
|
||||
mediaPlayerSpeedSlowIcon: icon {
|
||||
{ "player/voice_speed/voice_speed0.5", mediaPlayerActiveFg }
|
||||
};
|
||||
mediaPlayerSpeedSlowDisabledIcon: icon {
|
||||
{ "player/voice_speed/voice_speed0.5", menuIconFg }
|
||||
};
|
||||
mediaPlayerSpeedSlowDisabledIconOver: icon {
|
||||
{ "player/voice_speed/voice_speed0.5", menuIconFgOver }
|
||||
};
|
||||
mediaPlayerSpeedFastIcon: icon {
|
||||
{ "player/voice_speed/voice_speed1.5", mediaPlayerActiveFg }
|
||||
};
|
||||
mediaPlayerSpeedFastDisabledIcon: icon {
|
||||
{ "player/voice_speed/voice_speed1.5", menuIconFg }
|
||||
};
|
||||
mediaPlayerSpeedFastDisabledIconOver: icon {
|
||||
{ "player/voice_speed/voice_speed1.5", menuIconFgOver }
|
||||
};
|
||||
mediaPlayerSpeedDisabledRippleBg: windowBgOver;
|
||||
|
||||
mediaPlayerMenu: DropdownMenu(defaultDropdownMenu) {
|
||||
wrap: InnerDropdown(defaultInnerDropdown) {
|
||||
scrollPadding: margins(0px, 8px, 0px, 8px);
|
||||
scrollPadding: margins(0px, 4px, 0px, 4px);
|
||||
padding: margins(10px, 2px, 10px, 10px);
|
||||
}
|
||||
}
|
||||
mediaPlayerSpeedMenu: Menu(defaultMenu) {
|
||||
itemIconPosition: point(6px, 5px);
|
||||
itemPadding: margins(34px, 8px, 17px, 7px);
|
||||
}
|
||||
mediaPlayerMenuCheck: icon {{ "player/player_check", mediaPlayerActiveFg }};
|
||||
|
||||
mediaSpeedMenu: MediaSpeedMenu {
|
||||
menu: Menu(menuWithIcons) {
|
||||
separator: MenuSeparator(defaultMenuSeparator) {
|
||||
padding: margins(0px, 4px, 0px, 4px);
|
||||
width: 6px;
|
||||
}
|
||||
itemPadding: margins(54px, 7px, 54px, 9px);
|
||||
itemFgDisabled: mediaPlayerActiveFg;
|
||||
}
|
||||
iconFg: menuIconColor;
|
||||
iconFgActive: mediaPlayerActiveFg;
|
||||
textFgActive: mediaPlayerActiveFg;
|
||||
activeCheck: mediaPlayerMenuCheck;
|
||||
activeCheckSkip: 8px;
|
||||
sliderStyle: TextStyle(defaultTextStyle) {
|
||||
font: font(12px semibold);
|
||||
}
|
||||
sliderPadding: margins(50px, 8px, 12px, 8px);
|
||||
sliderWidth: 122px;
|
||||
slider: MediaSlider(defaultContinuousSlider) {
|
||||
activeFg: mediaPlayerActiveFg;
|
||||
inactiveFg: windowBgOver;
|
||||
activeFgOver: mediaPlayerActiveFg;
|
||||
inactiveFgOver: windowBgOver;
|
||||
activeFgDisabled: windowBgOver;
|
||||
receivedTillFg: windowBgOver;
|
||||
width: 6px;
|
||||
seekSize: size(6px, 6px);
|
||||
}
|
||||
}
|
||||
mediaSpeedSlow: icon {{ "player/speed/audiospeed_menu_0.5", menuIconColor }};
|
||||
mediaSpeedSlowActive: icon {{ "player/speed/audiospeed_menu_0.5", mediaPlayerActiveFg }};
|
||||
mediaSpeedNormal: icon {{ "player/speed/audiospeed_menu_1.0", menuIconColor }};
|
||||
mediaSpeedNormalActive: icon {{ "player/speed/audiospeed_menu_1.0", mediaPlayerActiveFg }};
|
||||
mediaSpeedMedium: icon {{ "player/speed/audiospeed_menu_1.2", menuIconColor }};
|
||||
mediaSpeedMediumActive: icon {{ "player/speed/audiospeed_menu_1.2", mediaPlayerActiveFg }};
|
||||
mediaSpeedFast: icon {{ "player/speed/audiospeed_menu_1.5", menuIconColor }};
|
||||
mediaSpeedFastActive: icon {{ "player/speed/audiospeed_menu_1.5", mediaPlayerActiveFg }};
|
||||
mediaSpeedVeryFast: icon {{ "player/speed/audiospeed_menu_1.7", menuIconColor }};
|
||||
mediaSpeedVeryFastActive: icon {{ "player/speed/audiospeed_menu_1.7", mediaPlayerActiveFg }};
|
||||
mediaSpeedSuperFast: icon {{ "player/speed/audiospeed_menu_2.0", menuIconColor }};
|
||||
mediaSpeedSuperFastActive: icon {{ "player/speed/audiospeed_menu_2.0", mediaPlayerActiveFg }};
|
||||
|
||||
mediaPlayerVolumeIcon0: icon {
|
||||
{ "player/player_mini_off", mediaPlayerActiveFg },
|
||||
};
|
||||
|
@ -211,8 +262,6 @@ mediaPlayerPlayback: FilledSlider {
|
|||
duration: 150;
|
||||
}
|
||||
|
||||
mediaPlayerButtonTransformDuration: 200;
|
||||
|
||||
mediaPlayerPanelMarginLeft: 10px;
|
||||
mediaPlayerPanelMarginBottom: 10px;
|
||||
mediaPlayerPanelWidth: 344px;
|
||||
|
|
|
@ -8,18 +8,30 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "media/player/media_player_button.h"
|
||||
|
||||
#include "ui/painter.h"
|
||||
#include "styles/style_widgets.h"
|
||||
#include "styles/style_media_player.h"
|
||||
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
#include <QtCore/QtMath>
|
||||
|
||||
PlayButtonLayout::PlayButtonLayout(const style::MediaPlayerButton &st, Fn<void()> callback)
|
||||
namespace Media::Player {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] QString SpeedText(float64 speed) {
|
||||
return QString::number(base::SafeRound(speed * 10) / 10.) + 'X';
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PlayButtonLayout::PlayButtonLayout(
|
||||
const style::MediaPlayerButton &st,
|
||||
Fn<void()> callback)
|
||||
: _st(st)
|
||||
, _callback(std::move(callback)) {
|
||||
}
|
||||
|
||||
void PlayButtonLayout::setState(State state) {
|
||||
if (_nextState == state) return;
|
||||
if (_nextState == state) {
|
||||
return;
|
||||
}
|
||||
|
||||
_nextState = state;
|
||||
if (!_transformProgress.animating()) {
|
||||
|
@ -238,8 +250,95 @@ void PlayButtonLayout::animationCallback() {
|
|||
}
|
||||
|
||||
void PlayButtonLayout::startTransform(float64 from, float64 to) {
|
||||
_transformProgress.start([this] { animationCallback(); }, from, to, st::mediaPlayerButtonTransformDuration);
|
||||
_transformProgress.start(
|
||||
[=] { animationCallback(); },
|
||||
from,
|
||||
to,
|
||||
_st.duration);
|
||||
}
|
||||
|
||||
} // namespace Player
|
||||
} // namespace Media
|
||||
SpeedButtonLayout::SpeedButtonLayout(
|
||||
const style::MediaSpeedButton &st,
|
||||
Fn<void()> callback,
|
||||
float64 speed)
|
||||
: _st(st)
|
||||
, _speed(speed)
|
||||
, _oldSpeed(speed)
|
||||
, _nextSpeed(speed)
|
||||
, _metrics(_st.font->f)
|
||||
, _text(SpeedText(speed))
|
||||
, _textWidth(_metrics.horizontalAdvance(_text))
|
||||
, _oldText(_text)
|
||||
, _oldTextWidth(_textWidth)
|
||||
, _callback(std::move(callback)) {
|
||||
}
|
||||
|
||||
void SpeedButtonLayout::setSpeed(float64 speed) {
|
||||
speed = base::SafeRound(speed * 10.) / 10.;
|
||||
if (_nextSpeed == speed) {
|
||||
return;
|
||||
}
|
||||
|
||||
_nextSpeed = speed;
|
||||
if (!_transformProgress.animating()) {
|
||||
_oldSpeed = _speed;
|
||||
_oldColor = _lastPaintColor;
|
||||
_oldText = _text;
|
||||
_oldTextWidth = _textWidth;
|
||||
_speed = _nextSpeed;
|
||||
_text = SpeedText(_speed);
|
||||
_textWidth = _metrics.horizontalAdvance(_text);
|
||||
_transformBackward = false;
|
||||
if (_speed != _speed) {
|
||||
startTransform(0., 1.);
|
||||
if (_callback) _callback();
|
||||
}
|
||||
} else if (_oldSpeed == _nextSpeed) {
|
||||
std::swap(_oldSpeed, _speed);
|
||||
std::swap(_oldColor, _lastPaintColor);
|
||||
std::swap(_oldText, _text);
|
||||
std::swap(_oldTextWidth, _textWidth);
|
||||
startTransform(
|
||||
_transformBackward ? 0. : 1.,
|
||||
_transformBackward ? 1. : 0.);
|
||||
_transformBackward = !_transformBackward;
|
||||
}
|
||||
}
|
||||
|
||||
void SpeedButtonLayout::finishTransform() {
|
||||
_transformProgress.stop();
|
||||
_transformBackward = false;
|
||||
if (_callback) _callback();
|
||||
}
|
||||
|
||||
void SpeedButtonLayout::paint(QPainter &p, const QColor &color) {
|
||||
_lastPaintColor = color;
|
||||
|
||||
_st.icon.paint(p, 0, 0, _st.width, color);
|
||||
|
||||
p.setPen(color);
|
||||
p.setFont(_st.font);
|
||||
|
||||
p.drawText(
|
||||
QPointF(
|
||||
(_st.width - _textWidth) / 2.,
|
||||
(_st.height - _metrics.height()) / 2. + _metrics.ascent()),
|
||||
_text);
|
||||
}
|
||||
|
||||
void SpeedButtonLayout::animationCallback() {
|
||||
if (!_transformProgress.animating()) {
|
||||
const auto finalSpeed = _nextSpeed;
|
||||
_nextSpeed = _speed;
|
||||
setSpeed(finalSpeed);
|
||||
}
|
||||
_callback();
|
||||
}
|
||||
|
||||
void SpeedButtonLayout::startTransform(float64 from, float64 to) {
|
||||
// No animation for now.
|
||||
_transformProgress.stop();
|
||||
animationCallback();
|
||||
}
|
||||
|
||||
} // namespace Media::Player
|
||||
|
|
|
@ -7,12 +7,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "ui/abstract_button.h"
|
||||
#include "ui/effects/animations.h"
|
||||
#include "styles/style_media_player.h"
|
||||
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
#include <QtGui/QFontMetrics>
|
||||
|
||||
namespace style {
|
||||
struct MediaPlayerButton;
|
||||
struct MediaSpeedButton;
|
||||
} // namespace style
|
||||
|
||||
namespace Media::Player {
|
||||
|
||||
class PlayButtonLayout {
|
||||
public:
|
||||
|
@ -48,5 +52,40 @@ private:
|
|||
|
||||
};
|
||||
|
||||
} // namespace Player
|
||||
} // namespace Media
|
||||
class SpeedButtonLayout {
|
||||
public:
|
||||
SpeedButtonLayout(
|
||||
const style::MediaSpeedButton &st,
|
||||
Fn<void()> callback,
|
||||
float64 speed);
|
||||
|
||||
void setSpeed(float64 speed);
|
||||
void finishTransform();
|
||||
void paint(QPainter &p, const QColor &color);
|
||||
|
||||
private:
|
||||
void animationCallback();
|
||||
void startTransform(float64 from, float64 to);
|
||||
|
||||
const style::MediaSpeedButton &_st;
|
||||
|
||||
float64 _speed = 1.;
|
||||
float64 _oldSpeed = 1.;
|
||||
float64 _nextSpeed = 1.;
|
||||
std::optional<QColor> _lastPaintColor;
|
||||
std::optional<QColor> _oldColor;
|
||||
Ui::Animations::Simple _transformProgress;
|
||||
bool _transformBackward = false;
|
||||
|
||||
QFontMetricsF _metrics;
|
||||
|
||||
QString _text;
|
||||
float64 _textWidth = 0;
|
||||
QString _oldText;
|
||||
float64 _oldTextWidth = 0;
|
||||
|
||||
Fn<void()> _callback;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Media::Player
|
||||
|
|
|
@ -7,12 +7,213 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "media/player/media_player_dropdown.h"
|
||||
|
||||
#include "base/timer.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/cached_round_corners.h"
|
||||
#include "ui/widgets/menu/menu.h"
|
||||
#include "ui/widgets/menu/menu_action.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/shadow.h"
|
||||
#include "ui/painter.h"
|
||||
#include "styles/style_media_player.h"
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
#include "base/debug_log.h"
|
||||
|
||||
namespace Media::Player {
|
||||
namespace {
|
||||
|
||||
constexpr auto kSpeedMin = 0.5;
|
||||
constexpr auto kSpeedMax = 2.5;
|
||||
constexpr auto kSpeedDebounceTimeout = crl::time(1000);
|
||||
|
||||
[[nodiscard]] float64 SpeedToSliderValue(float64 speed) {
|
||||
return (speed - kSpeedMin) / (kSpeedMax - kSpeedMin);
|
||||
}
|
||||
|
||||
[[nodiscard]] float64 SliderValueToSpeed(float64 value) {
|
||||
const auto speed = value * (kSpeedMax - kSpeedMin) + kSpeedMin;
|
||||
return base::SafeRound(speed * 10) / 10.;
|
||||
}
|
||||
|
||||
constexpr auto kSpeedStickedValues =
|
||||
std::array<std::pair<float64, float64>, 7>{{
|
||||
{ 0.8, 0.05 },
|
||||
{ 1.0, 0.05 },
|
||||
{ 1.2, 0.05 },
|
||||
{ 1.5, 0.05 },
|
||||
{ 1.7, 0.05 },
|
||||
{ 2.0, 0.05 },
|
||||
{ 2.2, 0.05 },
|
||||
}};
|
||||
|
||||
class SpeedSliderItem final : public Ui::Menu::ItemBase {
|
||||
public:
|
||||
SpeedSliderItem(
|
||||
not_null<RpWidget*> parent,
|
||||
const style::MediaSpeedMenu &st,
|
||||
rpl::producer<float64> value);
|
||||
|
||||
not_null<QAction*> action() const override;
|
||||
bool isEnabled() const override;
|
||||
|
||||
[[nodiscard]] float64 current() const;
|
||||
[[nodiscard]] rpl::producer<float64> changing() const;
|
||||
[[nodiscard]] rpl::producer<float64> changed() const;
|
||||
[[nodiscard]] rpl::producer<float64> debouncedChanges() const;
|
||||
|
||||
protected:
|
||||
int contentHeight() const override;
|
||||
|
||||
private:
|
||||
void setExternalValue(float64 speed);
|
||||
void setSliderValue(float64 speed);
|
||||
|
||||
const base::unique_qptr<Ui::MediaSlider> _slider;
|
||||
const not_null<QAction*> _dummyAction;
|
||||
const style::MediaSpeedMenu &_st;
|
||||
Ui::Text::String _text;
|
||||
int _height = 0;
|
||||
|
||||
rpl::event_stream<float64> _changing;
|
||||
rpl::event_stream<float64> _changed;
|
||||
rpl::event_stream<float64> _debounced;
|
||||
base::Timer _debounceTimer;
|
||||
rpl::variable<float64> _last = 0.;
|
||||
|
||||
};
|
||||
|
||||
SpeedSliderItem::SpeedSliderItem(
|
||||
not_null<RpWidget*> parent,
|
||||
const style::MediaSpeedMenu &st,
|
||||
rpl::producer<float64> value)
|
||||
: Ui::Menu::ItemBase(parent, st.menu)
|
||||
, _slider(base::make_unique_q<Ui::MediaSlider>(this, st.slider))
|
||||
, _dummyAction(new QAction(parent))
|
||||
, _st(st)
|
||||
, _height(st.sliderPadding.top()
|
||||
+ st.menu.itemStyle.font->height
|
||||
+ st.sliderPadding.bottom())
|
||||
, _debounceTimer([=] { _debounced.fire(current()); }) {
|
||||
initResizeHook(parent->sizeValue());
|
||||
enableMouseSelecting();
|
||||
enableMouseSelecting(_slider.get());
|
||||
|
||||
setMinWidth(st.sliderPadding.left()
|
||||
+ st.sliderWidth
|
||||
+ st.sliderPadding.right());
|
||||
_slider->setAlwaysDisplayMarker(true);
|
||||
|
||||
sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &size) {
|
||||
const auto geometry = QRect(QPoint(), size);
|
||||
const auto padding = _st.sliderPadding;
|
||||
const auto inner = geometry - padding;
|
||||
_slider->setGeometry(
|
||||
padding.left(),
|
||||
inner.y(),
|
||||
(geometry.width() - padding.left() - padding.right()),
|
||||
inner.height());
|
||||
}, lifetime());
|
||||
|
||||
paintRequest(
|
||||
) | rpl::start_with_next([=](const QRect &clip) {
|
||||
auto p = Painter(this);
|
||||
|
||||
p.fillRect(clip, _st.menu.itemBg);
|
||||
|
||||
const auto left = (_st.sliderPadding.left() - _text.maxWidth()) / 2;
|
||||
const auto top = _st.menu.itemPadding.top();
|
||||
p.setPen(_st.menu.itemFg);
|
||||
_text.drawLeftElided(p, left, top, _text.maxWidth(), width());
|
||||
}, lifetime());
|
||||
|
||||
_slider->setChangeProgressCallback([=](float64 value) {
|
||||
const auto speed = SliderValueToSpeed(value);
|
||||
if (current() != speed) {
|
||||
_last = speed;
|
||||
_changing.fire_copy(speed);
|
||||
_debounceTimer.callOnce(kSpeedDebounceTimeout);
|
||||
}
|
||||
});
|
||||
|
||||
_slider->setChangeFinishedCallback([=](float64 value) {
|
||||
const auto speed = SliderValueToSpeed(value);
|
||||
_last = speed;
|
||||
_changed.fire_copy(speed);
|
||||
_debounced.fire_copy(speed);
|
||||
_debounceTimer.cancel();
|
||||
});
|
||||
|
||||
std::move(
|
||||
value
|
||||
) | rpl::start_with_next([=](float64 external) {
|
||||
setExternalValue(external);
|
||||
}, lifetime());
|
||||
|
||||
_last.value(
|
||||
) | rpl::start_with_next([=](float64 value) {
|
||||
const auto text = QString::number(value, 'f', 1) + 'x';
|
||||
if (_text.toString() != text) {
|
||||
_text.setText(_st.sliderStyle, text);
|
||||
update();
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
_slider->setAdjustCallback([=](float64 value) {
|
||||
const auto speed = SliderValueToSpeed(value);
|
||||
for (const auto &snap : kSpeedStickedValues) {
|
||||
if (speed > (snap.first - snap.second)
|
||||
&& speed < (snap.first + snap.second)) {
|
||||
return SpeedToSliderValue(snap.first);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
void SpeedSliderItem::setExternalValue(float64 speed) {
|
||||
if (!_slider->isChanging()) {
|
||||
setSliderValue(speed);
|
||||
}
|
||||
}
|
||||
|
||||
void SpeedSliderItem::setSliderValue(float64 speed) {
|
||||
const auto value = SpeedToSliderValue(speed);
|
||||
_slider->setValue(value);
|
||||
_last = speed;
|
||||
_changed.fire_copy(speed);
|
||||
}
|
||||
|
||||
not_null<QAction*> SpeedSliderItem::action() const {
|
||||
return _dummyAction;
|
||||
}
|
||||
|
||||
bool SpeedSliderItem::isEnabled() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
int SpeedSliderItem::contentHeight() const {
|
||||
return _height;
|
||||
}
|
||||
|
||||
float64 SpeedSliderItem::current() const {
|
||||
return _last.current();
|
||||
}
|
||||
|
||||
rpl::producer<float64> SpeedSliderItem::changing() const {
|
||||
return _changing.events();
|
||||
}
|
||||
|
||||
rpl::producer<float64> SpeedSliderItem::changed() const {
|
||||
return _changed.events();
|
||||
}
|
||||
|
||||
rpl::producer<float64> SpeedSliderItem::debouncedChanges() const {
|
||||
return _debounced.events();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Dropdown::Dropdown(QWidget *parent)
|
||||
: RpWidget(parent)
|
||||
|
@ -178,4 +379,109 @@ bool Dropdown::eventFilter(QObject *obj, QEvent *e) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void FillSpeedMenu(
|
||||
not_null<Ui::Menu::Menu*> menu,
|
||||
const style::MediaSpeedMenu &st,
|
||||
rpl::producer<float64> value,
|
||||
Fn<void(float64)> callback) {
|
||||
auto slider = base::make_unique_q<SpeedSliderItem>(
|
||||
menu,
|
||||
st,
|
||||
rpl::duplicate(value));
|
||||
|
||||
slider->debouncedChanges(
|
||||
) | rpl::start_with_next(callback, slider->lifetime());
|
||||
|
||||
struct State {
|
||||
rpl::variable<float64> realtime;
|
||||
};
|
||||
const auto state = slider->lifetime().make_state<State>();
|
||||
state->realtime = rpl::single(
|
||||
slider->current()
|
||||
) | rpl::then(rpl::merge(
|
||||
slider->changing(),
|
||||
slider->changed()
|
||||
));
|
||||
|
||||
menu->addAction(std::move(slider));
|
||||
menu->addSeparator(&st.menu.separator);
|
||||
|
||||
struct SpeedPoint {
|
||||
float64 speed = 0.;
|
||||
tr::phrase<> text;
|
||||
const style::icon &icon;
|
||||
const style::icon &iconActive;
|
||||
};
|
||||
const auto points = std::vector<SpeedPoint>{
|
||||
{
|
||||
0.5,
|
||||
tr::lng_voice_speed_slow,
|
||||
st::mediaSpeedSlow,
|
||||
st::mediaSpeedSlowActive },
|
||||
{
|
||||
1.0,
|
||||
tr::lng_voice_speed_normal,
|
||||
st::mediaSpeedNormal,
|
||||
st::mediaSpeedNormalActive },
|
||||
{
|
||||
1.2,
|
||||
tr::lng_voice_speed_medium,
|
||||
st::mediaSpeedMedium,
|
||||
st::mediaSpeedMediumActive },
|
||||
{
|
||||
1.5,
|
||||
tr::lng_voice_speed_fast,
|
||||
st::mediaSpeedFast,
|
||||
st::mediaSpeedFastActive },
|
||||
{
|
||||
1.7,
|
||||
tr::lng_voice_speed_very_fast,
|
||||
st::mediaSpeedVeryFast,
|
||||
st::mediaSpeedVeryFastActive },
|
||||
{
|
||||
2.0,
|
||||
tr::lng_voice_speed_super_fast,
|
||||
st::mediaSpeedSuperFast,
|
||||
st::mediaSpeedSuperFastActive },
|
||||
};
|
||||
for (const auto &point : points) {
|
||||
const auto speed = point.speed;
|
||||
const auto text = point.text(tr::now);
|
||||
const auto icon = &point.icon;
|
||||
const auto iconActive = &point.iconActive;
|
||||
auto action = base::make_unique_q<Ui::Menu::Action>(
|
||||
menu,
|
||||
st::mediaSpeedMenu.menu,
|
||||
Ui::Menu::CreateAction(menu, text, [=] { callback(speed); }),
|
||||
&point.icon,
|
||||
&point.icon);
|
||||
const auto raw = action.get();
|
||||
const auto check = Ui::CreateChild<Ui::RpWidget>(raw);
|
||||
const auto skip = st.activeCheckSkip;
|
||||
check->resize(st.activeCheck.size());
|
||||
check->paintRequest(
|
||||
) | rpl::start_with_next([check, icon = &st.activeCheck] {
|
||||
auto p = QPainter(check);
|
||||
icon->paint(p, 0, 0, check->width());
|
||||
}, check->lifetime());
|
||||
raw->sizeValue(
|
||||
) | rpl::start_with_next([=, skip = st.activeCheckSkip](QSize size) {
|
||||
check->moveToRight(
|
||||
skip,
|
||||
(size.height() - check->height()) / 2,
|
||||
size.width());
|
||||
}, check->lifetime());
|
||||
check->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
state->realtime.value(
|
||||
) | rpl::start_with_next([=](float64 now) {
|
||||
const auto chosen = (speed == now);
|
||||
const auto overriden = chosen ? iconActive : icon;
|
||||
raw->setIcon(overriden, overriden);
|
||||
raw->action()->setEnabled(!chosen);
|
||||
check->setVisible(chosen);
|
||||
}, raw->lifetime());
|
||||
menu->addAction(std::move(action));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Media::Player
|
||||
|
|
|
@ -11,6 +11,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/effects/animations.h"
|
||||
#include "base/timer.h"
|
||||
|
||||
namespace style {
|
||||
struct MediaSpeedMenu;
|
||||
} // namespace style
|
||||
|
||||
namespace Ui::Menu {
|
||||
class Menu;
|
||||
} // namespace Ui::Menu
|
||||
|
||||
namespace Media::Player {
|
||||
|
||||
class Dropdown final : public Ui::RpWidget {
|
||||
|
@ -49,4 +57,10 @@ private:
|
|||
|
||||
};
|
||||
|
||||
void FillSpeedMenu(
|
||||
not_null<Ui::Menu::Menu*> menu,
|
||||
const style::MediaSpeedMenu &st,
|
||||
rpl::producer<float64> value,
|
||||
Fn<void(float64)> callback);
|
||||
|
||||
} // namespace Media::Player
|
||||
|
|
|
@ -31,12 +31,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "media/player/media_player_instance.h"
|
||||
#include "media/player/media_player_dropdown.h"
|
||||
#include "media/player/media_player_volume_controller.h"
|
||||
#include "styles/style_media_player.h"
|
||||
#include "styles/style_media_view.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_item_helpers.h"
|
||||
#include "storage/storage_account.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_media_player.h"
|
||||
#include "styles/style_media_view.h"
|
||||
#include "styles/style_chat.h" // expandedMenuSeparator.
|
||||
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
|
@ -44,12 +45,12 @@ namespace Player {
|
|||
class WithDropdownController {
|
||||
public:
|
||||
WithDropdownController(
|
||||
not_null<Ui::IconButton*> button,
|
||||
not_null<Ui::AbstractButton*> button,
|
||||
not_null<Ui::RpWidget*> menuParent,
|
||||
Fn<void(bool)> menuOverCallback);
|
||||
virtual ~WithDropdownController() = default;
|
||||
|
||||
[[nodiscard]] not_null<Ui::IconButton*> button() const;
|
||||
[[nodiscard]] not_null<Ui::AbstractButton*> button() const;
|
||||
Ui::DropdownMenu *menu() const;
|
||||
|
||||
void updateDropdownGeometry();
|
||||
|
@ -63,7 +64,7 @@ protected:
|
|||
private:
|
||||
virtual void fillMenu(not_null<Ui::DropdownMenu*> menu) = 0;
|
||||
|
||||
const not_null<Ui::IconButton*> _button;
|
||||
const not_null<Ui::AbstractButton*> _button;
|
||||
const not_null<Ui::RpWidget*> _menuParent;
|
||||
const Fn<void(bool)> _menuOverCallback;
|
||||
base::unique_qptr<Ui::DropdownMenu> _menu;
|
||||
|
@ -72,6 +73,24 @@ private:
|
|||
|
||||
};
|
||||
|
||||
class Widget::SpeedButton final : public Ui::RippleButton {
|
||||
public:
|
||||
SpeedButton(QWidget *parent);
|
||||
|
||||
void setSpeed(float64 speed, anim::type animated = anim::type::normal);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
||||
QImage prepareRippleMask() const override;
|
||||
QPoint prepareRippleStartPosition() const override;
|
||||
|
||||
SpeedButtonLayout _layout;
|
||||
QPoint _layoutPosition;
|
||||
bool _isDefault = false;
|
||||
|
||||
};
|
||||
|
||||
class Widget::OrderController final : public WithDropdownController {
|
||||
public:
|
||||
OrderController(
|
||||
|
@ -83,12 +102,14 @@ private:
|
|||
void fillMenu(not_null<Ui::DropdownMenu*> menu) override;
|
||||
void updateIcon();
|
||||
|
||||
const not_null<Ui::IconButton*> _button;
|
||||
|
||||
};
|
||||
|
||||
class Widget::SpeedController final : public WithDropdownController {
|
||||
public:
|
||||
SpeedController(
|
||||
not_null<Ui::IconButton*> button,
|
||||
not_null<SpeedButton*> button,
|
||||
not_null<Ui::RpWidget*> menuParent,
|
||||
Fn<void(bool)> menuOverCallback);
|
||||
|
||||
|
@ -96,7 +117,6 @@ public:
|
|||
|
||||
private:
|
||||
void fillMenu(not_null<Ui::DropdownMenu*> menu) override;
|
||||
void updateIcon();
|
||||
|
||||
[[nodiscard]] float64 speed() const;
|
||||
[[nodiscard]] bool isDefault() const;
|
||||
|
@ -105,7 +125,7 @@ private:
|
|||
void setSpeed(float64 newSpeed);
|
||||
void save();
|
||||
|
||||
float64 _speed = 2.;
|
||||
float64 _speed = 1.7;
|
||||
bool _isDefault = true;
|
||||
rpl::event_stream<float64> _speedChanged;
|
||||
rpl::event_stream<> _saved;
|
||||
|
@ -113,7 +133,7 @@ private:
|
|||
};
|
||||
|
||||
WithDropdownController::WithDropdownController(
|
||||
not_null<Ui::IconButton*> button,
|
||||
not_null<Ui::AbstractButton*> button,
|
||||
not_null<Ui::RpWidget*> menuParent,
|
||||
Fn<void(bool)> menuOverCallback)
|
||||
: _button(button)
|
||||
|
@ -135,7 +155,7 @@ WithDropdownController::WithDropdownController(
|
|||
}, button->lifetime());
|
||||
}
|
||||
|
||||
not_null<Ui::IconButton*> WithDropdownController::button() const {
|
||||
not_null<Ui::AbstractButton*> WithDropdownController::button() const {
|
||||
return _button;
|
||||
}
|
||||
|
||||
|
@ -199,11 +219,48 @@ void WithDropdownController::showMenu() {
|
|||
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
|
||||
}
|
||||
|
||||
Widget::SpeedButton::SpeedButton(QWidget *parent)
|
||||
: RippleButton(parent, st::mediaPlayerSpeedRipple)
|
||||
, _layout(st::mediaSpeedButton, [=] { update(); }, 2.)
|
||||
, _isDefault(true) {
|
||||
resize(st::mediaPlayerSpeedSize);
|
||||
_layoutPosition = QPoint(
|
||||
(st::mediaPlayerSpeedSize.width() - st::mediaSpeedButton.width) / 2,
|
||||
st::mediaPlayerSpeedSize.height() - st::mediaSpeedButton.height);
|
||||
}
|
||||
|
||||
void Widget::SpeedButton::setSpeed(float64 speed, anim::type animated) {
|
||||
_isDefault = (speed == 1.);
|
||||
_layout.setSpeed(speed);
|
||||
if (animated == anim::type::instant) {
|
||||
_layout.finishTransform();
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
void Widget::SpeedButton::paintEvent(QPaintEvent *e) {
|
||||
auto p = QPainter(this);
|
||||
|
||||
paintRipple(
|
||||
p,
|
||||
QPoint(),
|
||||
_isDefault ? &st::mediaPlayerSpeedDisabledRippleBg->c : nullptr);
|
||||
|
||||
const auto &color = !_isDefault
|
||||
? st::mediaPlayerActiveFg
|
||||
: isOver()
|
||||
? st::menuIconFgOver
|
||||
: st::menuIconFg;
|
||||
p.translate(_layoutPosition);
|
||||
_layout.paint(p, color->c);
|
||||
}
|
||||
|
||||
Widget::OrderController::OrderController(
|
||||
not_null<Ui::IconButton*> button,
|
||||
not_null<Ui::RpWidget*> menuParent,
|
||||
Fn<void(bool)> menuOverCallback)
|
||||
: WithDropdownController(button, menuParent, std::move(menuOverCallback)) {
|
||||
: WithDropdownController(button, menuParent, std::move(menuOverCallback))
|
||||
, _button(button) {
|
||||
button->setClickedCallback([=] {
|
||||
showMenu();
|
||||
});
|
||||
|
@ -260,25 +317,25 @@ void Widget::OrderController::fillMenu(not_null<Ui::DropdownMenu*> menu) {
|
|||
void Widget::OrderController::updateIcon() {
|
||||
switch (Core::App().settings().playerOrderMode()) {
|
||||
case OrderMode::Default:
|
||||
button()->setIconOverride(
|
||||
_button->setIconOverride(
|
||||
&st::mediaPlayerReverseDisabledIcon,
|
||||
&st::mediaPlayerReverseDisabledIconOver);
|
||||
button()->setRippleColorOverride(
|
||||
_button->setRippleColorOverride(
|
||||
&st::mediaPlayerRepeatDisabledRippleBg);
|
||||
break;
|
||||
case OrderMode::Reverse:
|
||||
button()->setIconOverride(&st::mediaPlayerReverseIcon);
|
||||
button()->setRippleColorOverride(nullptr);
|
||||
_button->setIconOverride(&st::mediaPlayerReverseIcon);
|
||||
_button->setRippleColorOverride(nullptr);
|
||||
break;
|
||||
case OrderMode::Shuffle:
|
||||
button()->setIconOverride(&st::mediaPlayerShuffleIcon);
|
||||
button()->setRippleColorOverride(nullptr);
|
||||
_button->setIconOverride(&st::mediaPlayerShuffleIcon);
|
||||
_button->setRippleColorOverride(nullptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Widget::SpeedController::SpeedController(
|
||||
not_null<Ui::IconButton*> button,
|
||||
not_null<SpeedButton*> button,
|
||||
not_null<Ui::RpWidget*> menuParent,
|
||||
Fn<void(bool)> menuOverCallback)
|
||||
: WithDropdownController(button, menuParent, std::move(menuOverCallback)) {
|
||||
|
@ -293,45 +350,15 @@ Widget::SpeedController::SpeedController(
|
|||
setSpeed(Core::App().settings().voicePlaybackSpeed());
|
||||
_speed = Core::App().settings().voicePlaybackSpeed(true);
|
||||
|
||||
button->setSpeed(_speed, anim::type::instant);
|
||||
|
||||
_speedChanged.events_starting_with(
|
||||
speed()
|
||||
) | rpl::start_with_next([=] {
|
||||
updateIcon();
|
||||
) | rpl::start_with_next([=](float64 speed) {
|
||||
button->setSpeed(speed);
|
||||
}, button->lifetime());
|
||||
}
|
||||
|
||||
void Widget::SpeedController::updateIcon() {
|
||||
const auto isDefaultSpeed = isDefault();
|
||||
const auto nonDefaultSpeed = lastNonDefaultSpeed();
|
||||
|
||||
if (nonDefaultSpeed == .5) {
|
||||
button()->setIconOverride(
|
||||
(isDefaultSpeed
|
||||
? &st::mediaPlayerSpeedSlowDisabledIcon
|
||||
: &st::mediaPlayerSpeedSlowIcon),
|
||||
(isDefaultSpeed
|
||||
? &st::mediaPlayerSpeedSlowDisabledIconOver
|
||||
: &st::mediaPlayerSpeedSlowIcon));
|
||||
} else if (nonDefaultSpeed == 1.5) {
|
||||
button()->setIconOverride(
|
||||
(isDefaultSpeed
|
||||
? &st::mediaPlayerSpeedFastDisabledIcon
|
||||
: &st::mediaPlayerSpeedFastIcon),
|
||||
(isDefaultSpeed
|
||||
? &st::mediaPlayerSpeedFastDisabledIconOver
|
||||
: &st::mediaPlayerSpeedFastIcon));
|
||||
} else {
|
||||
button()->setIconOverride(
|
||||
isDefaultSpeed ? &st::mediaPlayerSpeedDisabledIcon : nullptr,
|
||||
(isDefaultSpeed
|
||||
? &st::mediaPlayerSpeedDisabledIconOver
|
||||
: nullptr));
|
||||
}
|
||||
button()->setRippleColorOverride(isDefaultSpeed
|
||||
? &st::mediaPlayerSpeedDisabledRippleBg
|
||||
: nullptr);
|
||||
}
|
||||
|
||||
rpl::producer<> Widget::SpeedController::saved() const {
|
||||
return _saved.events();
|
||||
}
|
||||
|
@ -367,35 +394,11 @@ void Widget::SpeedController::save() {
|
|||
}
|
||||
|
||||
void Widget::SpeedController::fillMenu(not_null<Ui::DropdownMenu*> menu) {
|
||||
const auto currentSpeed = speed();
|
||||
const auto addSpeedAction = [&](float64 speed, QString text) {
|
||||
const auto callback = [=] {
|
||||
setSpeed(speed);
|
||||
save();
|
||||
};
|
||||
const auto icon = (speed == currentSpeed)
|
||||
? &st::mediaPlayerMenuCheck
|
||||
: nullptr;
|
||||
auto action = base::make_unique_q<Ui::Menu::Action>(
|
||||
menu,
|
||||
st::mediaPlayerSpeedMenu,
|
||||
Ui::Menu::CreateAction(menu, text, callback),
|
||||
icon,
|
||||
icon);
|
||||
const auto raw = action.get();
|
||||
_speedChanged.events(
|
||||
) | rpl::start_with_next([=](float64 updatedSpeed) {
|
||||
const auto icon = (speed == updatedSpeed)
|
||||
? &st::mediaPlayerMenuCheck
|
||||
: nullptr;
|
||||
raw->setIcon(icon, icon);
|
||||
}, raw->lifetime());
|
||||
menu->addAction(std::move(action));
|
||||
};
|
||||
addSpeedAction(0.5, tr::lng_voice_speed_slow(tr::now));
|
||||
addSpeedAction(1., tr::lng_voice_speed_normal(tr::now));
|
||||
addSpeedAction(1.5, tr::lng_voice_speed_fast(tr::now));
|
||||
addSpeedAction(2., tr::lng_voice_speed_very_fast(tr::now));
|
||||
FillSpeedMenu(
|
||||
menu->menu(),
|
||||
st::mediaSpeedMenu,
|
||||
_speedChanged.events_starting_with(speed()),
|
||||
[=](float64 speed) { setSpeed(speed); save(); });
|
||||
}
|
||||
|
||||
Widget::Widget(
|
||||
|
@ -412,7 +415,7 @@ Widget::Widget(
|
|||
, _volumeToggle(rightControls(), st::mediaPlayerVolumeToggle)
|
||||
, _repeatToggle(rightControls(), st::mediaPlayerRepeatButton)
|
||||
, _orderToggle(rightControls(), st::mediaPlayerRepeatButton)
|
||||
, _speedToggle(rightControls(), st::mediaPlayerSpeedButton)
|
||||
, _speedToggle(rightControls())
|
||||
, _close(this, st::mediaPlayerClose)
|
||||
, _shadow(this)
|
||||
, _playbackSlider(this, st::mediaPlayerPlayback)
|
||||
|
|
|
@ -23,21 +23,16 @@ template <typename Widget>
|
|||
class FadeWrap;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Media {
|
||||
namespace View {
|
||||
namespace Media::View {
|
||||
class PlaybackProgress;
|
||||
} // namespace Clip
|
||||
} // namespace Media
|
||||
} // namespace Media::View
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace Media {
|
||||
namespace Player {
|
||||
namespace Media::Player {
|
||||
|
||||
class PlayButton;
|
||||
class SpeedButton;
|
||||
class Dropdown;
|
||||
struct TrackState;
|
||||
|
||||
|
@ -47,6 +42,7 @@ public:
|
|||
QWidget *parent,
|
||||
not_null<Ui::RpWidget*> dropdownsParent,
|
||||
not_null<Window::SessionController*> controller);
|
||||
~Widget();
|
||||
|
||||
void setCloseCallback(Fn<void()> callback);
|
||||
void setShowItemCallback(Fn<void(not_null<const HistoryItem*>)> callback);
|
||||
|
@ -61,8 +57,6 @@ public:
|
|||
return _togglePlaylistRequests.events();
|
||||
}
|
||||
|
||||
~Widget();
|
||||
|
||||
private:
|
||||
void resizeEvent(QResizeEvent *e) override;
|
||||
void paintEvent(QPaintEvent *e) override;
|
||||
|
@ -134,7 +128,7 @@ private:
|
|||
bool _wontBeOver = false;
|
||||
bool _volumeHidden = false;
|
||||
|
||||
class PlayButton;
|
||||
class SpeedButton;
|
||||
class OrderController;
|
||||
class SpeedController;
|
||||
object_ptr<Ui::FlatLabel> _nameLabel;
|
||||
|
@ -146,7 +140,7 @@ private:
|
|||
object_ptr<Ui::IconButton> _volumeToggle;
|
||||
object_ptr<Ui::IconButton> _repeatToggle;
|
||||
object_ptr<Ui::IconButton> _orderToggle;
|
||||
object_ptr<Ui::IconButton> _speedToggle;
|
||||
object_ptr<SpeedButton> _speedToggle;
|
||||
object_ptr<Ui::IconButton> _close;
|
||||
object_ptr<Ui::PlainShadow> _shadow = { nullptr };
|
||||
object_ptr<Ui::FilledSlider> _playbackSlider;
|
||||
|
@ -159,5 +153,4 @@ private:
|
|||
|
||||
};
|
||||
|
||||
} // namespace Player
|
||||
} // namespace Media
|
||||
} // namespace Media::Player
|
||||
|
|
|
@ -528,7 +528,8 @@ void Player::fail(Error error) {
|
|||
}
|
||||
|
||||
void Player::play(const PlaybackOptions &options) {
|
||||
Expects(options.speed >= 0.5 && options.speed <= 2.);
|
||||
Expects(options.speed >= Audio::kSpeedMin
|
||||
&& options.speed <= Audio::kSpeedMax);
|
||||
|
||||
// Looping video with audio is not supported for now.
|
||||
Expects(!options.loop || (options.mode != Mode::Both));
|
||||
|
@ -828,7 +829,7 @@ float64 Player::speed() const {
|
|||
}
|
||||
|
||||
void Player::setSpeed(float64 speed) {
|
||||
Expects(speed >= 0.5 && speed <= 2.);
|
||||
Expects(speed >= Audio::kSpeedMin && speed <= Audio::kSpeedMax);
|
||||
|
||||
if (!Media::Audio::SupportsSpeedControl()) {
|
||||
speed = 1.;
|
||||
|
|
|
@ -1093,9 +1093,6 @@ void Pip::handleMousePress(QPoint position, Qt::MouseButton button) {
|
|||
}
|
||||
|
||||
void Pip::handleMouseRelease(QPoint position, Qt::MouseButton button) {
|
||||
Expects(1 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
const auto weak = Ui::MakeWeak(_panel.widget());
|
||||
const auto guard = gsl::finally([&] {
|
||||
if (weak) {
|
||||
|
@ -1107,21 +1104,11 @@ void Pip::handleMouseRelease(QPoint position, Qt::MouseButton button) {
|
|||
}
|
||||
seekUpdate(position);
|
||||
|
||||
Assert(2 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
volumeControllerUpdate(position);
|
||||
|
||||
Assert(3 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
const auto pressed = base::take(_pressed);
|
||||
if (pressed && *pressed == OverState::Playback) {
|
||||
_panel.setDragDisabled(false);
|
||||
|
||||
Assert(4 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
seekFinish(_playbackProgress->value());
|
||||
} else if (pressed && *pressed == OverState::VolumeController) {
|
||||
_panel.setDragDisabled(false);
|
||||
|
@ -1182,9 +1169,6 @@ void Pip::seekProgress(float64 value) {
|
|||
}
|
||||
|
||||
void Pip::seekFinish(float64 value) {
|
||||
Expects(5 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
if (!_lastDurationMs) {
|
||||
return;
|
||||
}
|
||||
|
@ -1671,31 +1655,18 @@ void Pip::playbackPauseResume() {
|
|||
}
|
||||
|
||||
void Pip::restartAtSeekPosition(crl::time position) {
|
||||
Expects(6 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
if (!_instance.info().video.cover.isNull()) {
|
||||
_preparedCoverStorage = QImage();
|
||||
_preparedCoverState = ThumbState::Empty;
|
||||
_instance.saveFrameToCover();
|
||||
}
|
||||
|
||||
Assert(7 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
auto options = Streaming::PlaybackOptions();
|
||||
options.position = position;
|
||||
options.hwAllowed = Core::App().settings().hardwareAcceleratedVideo();
|
||||
options.audioId = _instance.player().prepareLegacyState().id;
|
||||
|
||||
Assert(8 && _delegate->pipPlaybackSpeed() >= 0.5
|
||||
&& _delegate->pipPlaybackSpeed() <= 2.); // Debugging strange crash.
|
||||
|
||||
options.speed = _delegate->pipPlaybackSpeed();
|
||||
|
||||
Assert(9 && options.speed >= 0.5
|
||||
&& options.speed <= 2.); // Debugging strange crash.
|
||||
|
||||
_instance.play(options);
|
||||
if (_startPaused) {
|
||||
_instance.pause();
|
||||
|
|
|
@ -120,6 +120,8 @@ PRIVATE
|
|||
media/clip/media_clip_reader.cpp
|
||||
media/clip/media_clip_reader.h
|
||||
|
||||
media/player/media_player_button.cpp
|
||||
media/player/media_player_button.h
|
||||
media/player/media_player_dropdown.cpp
|
||||
media/player/media_player_dropdown.h
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit c80df2cdd2d2838d5e4aab50075e4f6e7c05e380
|
||||
Subproject commit 62a62d1fb5abe9dc1e6b9f89af1ccef6fef33c52
|