Make Media::Clip::Playback independent of slider.

Now animation of the playback progress is processed inside the
Media::Clip::Playback and the sliders just hold plain float64 value.
This commit is contained in:
John Preston 2017-05-18 19:10:39 +03:00
parent 8446fa5a4d
commit 87ff770020
12 changed files with 149 additions and 116 deletions

View File

@ -72,7 +72,8 @@ CoverWidget::CoverWidget(QWidget *parent) : TWidget(parent)
, _nameLabel(this, st::mediaPlayerName)
, _timeLabel(this, st::mediaPlayerTime)
, _close(this, st::mediaPlayerPanelClose)
, _playback(std::make_unique<Clip::Playback>(new Ui::MediaSlider(this, st::mediaPlayerPanelPlayback)))
, _playbackSlider(this, st::mediaPlayerPanelPlayback)
, _playback(std::make_unique<Clip::Playback>())
, _playPause(this)
, _volumeToggle(this, st::mediaPlayerVolumeToggle)
, _volumeController(this)
@ -86,11 +87,19 @@ CoverWidget::CoverWidget(QWidget *parent) : TWidget(parent)
_timeLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
setMouseTracking(true);
_playback->setChangeProgressCallback([this](float64 value) {
handleSeekProgress(value);
_playback->setInLoadingStateChangedCallback([this](bool loading) {
_playbackSlider->setDisabled(loading);
});
_playback->setChangeFinishedCallback([this](float64 value) {
_playback->setValueChangedCallback([this](float64 value) {
_playbackSlider->setValue(value);
});
_playbackSlider->setChangeProgressCallback([this](float64 value) {
handleSeekProgress(value);
_playback->setValue(value, false);
});
_playbackSlider->setChangeFinishedCallback([this](float64 value) {
handleSeekFinished(value);
_playback->setValue(value, false);
});
_playPause->setClickedCallback([this] {
instance()->playPauseCancelClicked();
@ -168,7 +177,7 @@ void CoverWidget::resizeEvent(QResizeEvent *e) {
int skip = (st::mediaPlayerPanelPlayback.seekSize.width() / 2);
int length = (width() - 2 * st::mediaPlayerPanelPadding + st::mediaPlayerPanelPlayback.seekSize.width());
_playback->setGeometry(st::mediaPlayerPanelPadding - skip, st::mediaPlayerPanelPlaybackTop, length, 2 * st::mediaPlayerPanelPlaybackPadding + st::mediaPlayerPanelPlayback.width);
_playbackSlider->setGeometry(st::mediaPlayerPanelPadding - skip, st::mediaPlayerPanelPlaybackTop, length, 2 * st::mediaPlayerPanelPlaybackPadding + st::mediaPlayerPanelPlayback.width);
auto top = st::mediaPlayerPanelVolumeToggleTop;
auto right = st::mediaPlayerPanelPlayLeft;
@ -269,11 +278,11 @@ void CoverWidget::updateTimeText(const TrackState &state) {
if (state.id.audio()->loading()) {
_time = QString::number(qRound(state.id.audio()->progress() * 100)) + '%';
_playback->setDisabled(true);
_playbackSlider->setDisabled(true);
} else {
display = display / frequency;
_time = formatDurationText(display);
_playback->setDisabled(false);
_playbackSlider->setDisabled(false);
}
if (_seekPositionMs < 0) {
updateTimeLabel();

View File

@ -26,6 +26,7 @@ namespace Ui {
class FlatLabel;
class LabelSimple;
class IconButton;
class MediaSlider;
} // namespace Ui
namespace Media {
@ -80,6 +81,7 @@ private:
object_ptr<Ui::FlatLabel> _nameLabel;
object_ptr<Ui::LabelSimple> _timeLabel;
object_ptr<Ui::IconButton> _close;
object_ptr<Ui::MediaSlider> _playbackSlider;
std::unique_ptr<Clip::Playback> _playback;
object_ptr<Ui::IconButton> _previousTrack = { nullptr };
object_ptr<PlayButton> _playPause;

View File

@ -45,12 +45,10 @@ VolumeController::VolumeController(QWidget *parent) : TWidget(parent)
});
subscribe(Global::RefSongVolumeChanged(), [this] {
if (!_slider->isChanging()) {
_slider->setValue(Global::SongVolume(), true);
_slider->setValue(Global::SongVolume());
}
});
auto animated = false;
setVolume(Global::SongVolume(), animated);
setVolume(Global::SongVolume());
resize(st::mediaPlayerPanelVolumeWidth, 2 * st::mediaPlayerPanelPlaybackPadding + st::mediaPlayerPanelPlayback.width);
}
@ -65,8 +63,8 @@ void VolumeController::resizeEvent(QResizeEvent *e) {
_slider->setGeometry(rect());
}
void VolumeController::setVolume(float64 volume, bool animated) {
_slider->setValue(volume, animated);
void VolumeController::setVolume(float64 volume) {
_slider->setValue(volume);
if (volume > 0) {
Global::SetRememberedSongVolume(volume);
}

View File

@ -38,7 +38,7 @@ protected:
void resizeEvent(QResizeEvent *e) override;
private:
void setVolume(float64 volume, bool animated = true);
void setVolume(float64 volume);
void applyVolumeChange(float64 volume);
object_ptr<Ui::MediaSlider> _slider;

View File

@ -91,7 +91,8 @@ Widget::Widget(QWidget *parent) : TWidget(parent)
, _repeatTrack(this, st::mediaPlayerRepeatButton)
, _close(this, st::mediaPlayerClose)
, _shadow(this, st::shadowFg)
, _playback(std::make_unique<Clip::Playback>(new Ui::FilledSlider(this, st::mediaPlayerPlayback))) {
, _playbackSlider(this, st::mediaPlayerPlayback)
, _playback(std::make_unique<Clip::Playback>()) {
setAttribute(Qt::WA_OpaquePaintEvent);
setMouseTracking(true);
resize(width(), st::mediaPlayerHeight + st::lineWidth);
@ -99,11 +100,19 @@ Widget::Widget(QWidget *parent) : TWidget(parent)
_nameLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
_timeLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
_playback->setChangeProgressCallback([this](float64 value) {
handleSeekProgress(value);
_playback->setInLoadingStateChangedCallback([this](bool loading) {
_playbackSlider->setDisabled(loading);
});
_playback->setChangeFinishedCallback([this](float64 value) {
_playback->setValueChangedCallback([this](float64 value) {
_playbackSlider->setValue(value);
});
_playbackSlider->setChangeProgressCallback([this](float64 value) {
handleSeekProgress(value);
_playback->setValue(value, false);
});
_playbackSlider->setChangeFinishedCallback([this](float64 value) {
handleSeekFinished(value);
_playback->setValue(value, false);
});
_playPause->setClickedCallback([this] {
instance()->playPauseCancelClicked();
@ -167,12 +176,12 @@ void Widget::setShadowGeometryToLeft(int x, int y, int w, int h) {
void Widget::showShadow() {
_shadow->show();
_playback->show();
_playbackSlider->show();
}
void Widget::hideShadow() {
_shadow->hide();
_playback->hide();
_playbackSlider->hide();
}
QPoint Widget::getPositionForVolumeWidget() const {
@ -223,7 +232,7 @@ void Widget::resizeEvent(QResizeEvent *e) {
updatePlayPrevNextPositions();
_playback->setGeometry(0, height() - st::mediaPlayerPlayback.fullWidth, width(), st::mediaPlayerPlayback.fullWidth);
_playbackSlider->setGeometry(0, height() - st::mediaPlayerPlayback.fullWidth, width(), st::mediaPlayerPlayback.fullWidth);
}
void Widget::paintEvent(QPaintEvent *e) {
@ -346,11 +355,11 @@ void Widget::updateTimeText(const TrackState &state) {
if (state.id.audio()->loading()) {
_time = QString::number(qRound(state.id.audio()->progress() * 100)) + '%';
_playback->setDisabled(true);
_playbackSlider->setDisabled(true);
} else {
display = display / frequency;
_time = formatDurationText(display);
_playback->setDisabled(false);
_playbackSlider->setDisabled(false);
}
if (_seekPositionMs < 0) {
updateTimeLabel();

View File

@ -27,6 +27,7 @@ class FlatLabel;
class LabelSimple;
class IconButton;
class PlainShadow;
class FilledSlider;
} // namespace Ui
namespace Media {
@ -101,6 +102,7 @@ private:
object_ptr<Ui::IconButton> _repeatTrack;
object_ptr<Ui::IconButton> _close;
object_ptr<Ui::PlainShadow> _shadow = { nullptr };
object_ptr<Ui::FilledSlider> _playbackSlider;
std::unique_ptr<Clip::Playback> _playback;
};

View File

@ -34,7 +34,8 @@ namespace Clip {
Controller::Controller(QWidget *parent) : TWidget(parent)
, _playPauseResume(this, st::mediaviewPlayButton)
, _playback(std::make_unique<Playback>(new Ui::MediaSlider(this, st::mediaviewPlayback)))
, _playbackSlider(this, st::mediaviewPlayback)
, _playback(std::make_unique<Playback>())
, _volumeController(this)
, _fullScreenToggle(this, st::mediaviewFullScreenButton)
, _playedAlready(this, st::mediaviewPlayProgressLabel)
@ -50,11 +51,19 @@ Controller::Controller(QWidget *parent) : TWidget(parent)
connect(_fullScreenToggle, SIGNAL(clicked()), this, SIGNAL(toFullScreenPressed()));
connect(_volumeController, SIGNAL(volumeChanged(float64)), this, SIGNAL(volumeChanged(float64)));
_playback->setChangeProgressCallback([this](float64 value) {
handleSeekProgress(value);
_playback->setInLoadingStateChangedCallback([this](bool loading) {
_playbackSlider->setDisabled(loading);
});
_playback->setChangeFinishedCallback([this](float64 value) {
_playback->setValueChangedCallback([this](float64 value) {
_playbackSlider->setValue(value);
});
_playbackSlider->setChangeProgressCallback([this](float64 value) {
handleSeekProgress(value);
_playback->setValue(value, false);
});
_playbackSlider->setChangeFinishedCallback([this](float64 value) {
handleSeekFinished(value);
_playback->setValue(value, false);
});
}
@ -93,7 +102,7 @@ void Controller::hideAnimated() {
template <typename Callback>
void Controller::startFading(Callback start) {
start();
_playback->show();
_playbackSlider->show();
}
void Controller::fadeFinished() {
@ -101,7 +110,7 @@ void Controller::fadeFinished() {
}
void Controller::fadeUpdated(float64 opacity) {
_playback->setFadeOpacity(opacity);
_playbackSlider->setFadeOpacity(opacity);
}
void Controller::updatePlayback(const Player::TrackState &state) {
@ -178,12 +187,12 @@ void Controller::setInFullScreen(bool inFullScreen) {
void Controller::grabStart() {
showChildren();
_playback->hide();
_playbackSlider->hide();
}
void Controller::grabFinish() {
hideChildren();
_playback->show();
_playbackSlider->show();
}
void Controller::resizeEvent(QResizeEvent *e) {
@ -195,9 +204,9 @@ void Controller::resizeEvent(QResizeEvent *e) {
_volumeController->moveToRight(st::mediaviewFullScreenLeft + _fullScreenToggle->width() + st::mediaviewVolumeLeft, (height() - _volumeController->height()) / 2);
int playbackWidth = width() - st::mediaviewPlayPauseLeft - _playPauseResume->width() - playTop - fullScreenTop - _volumeController->width() - st::mediaviewVolumeLeft - _fullScreenToggle->width() - st::mediaviewFullScreenLeft;
_playback->resize(playbackWidth, st::mediaviewPlayback.seekSize.height());
_playback->moveToLeft(st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop, st::mediaviewPlaybackTop);
auto playbackWidth = width() - st::mediaviewPlayPauseLeft - _playPauseResume->width() - playTop - fullScreenTop - _volumeController->width() - st::mediaviewVolumeLeft - _fullScreenToggle->width() - st::mediaviewFullScreenLeft;
_playbackSlider->resize(playbackWidth, st::mediaviewPlayback.seekSize.height());
_playbackSlider->moveToLeft(st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop, st::mediaviewPlaybackTop);
_playedAlready->moveToLeft(st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop, st::mediaviewPlayProgressTop);
_toPlayLeft->moveToRight(width() - (st::mediaviewPlayPauseLeft + _playPauseResume->width() + playTop) - playbackWidth, st::mediaviewPlayProgressTop);

View File

@ -24,6 +24,7 @@ namespace Ui {
class LabelSimple;
class FadeAnimation;
class IconButton;
class MediaSlider;
} // namespace Ui
namespace Media {
@ -86,6 +87,7 @@ private:
TimeMs _lastDurationMs = 0;
object_ptr<Ui::IconButton> _playPauseResume;
object_ptr<Ui::MediaSlider> _playbackSlider;
std::unique_ptr<Playback> _playback;
object_ptr<VolumeController> _volumeController;
object_ptr<Ui::IconButton> _fullScreenToggle;

View File

@ -26,14 +26,19 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Media {
namespace Clip {
Playback::Playback(Ui::ContinuousSlider *slider) : _slider(slider) {
Playback::Playback() : _a_value(animation(this, &Playback::step_value)) {
}
void Playback::updateState(const Player::TrackState &state) {
qint64 position = 0, length = state.length;
auto wasDisabled = _slider->isDisabled();
if (wasDisabled) setDisabled(false);
auto wasInLoadingState = _inLoadingState;
if (wasInLoadingState) {
_inLoadingState = false;
if (_inLoadingStateChanged) {
_inLoadingStateChanged(false);
}
}
_playing = !Player::IsStopped(state.state);
if (_playing || state.state == Player::State::Stopped) {
@ -44,25 +49,60 @@ void Playback::updateState(const Player::TrackState &state) {
position = 0;
}
float64 progress = 0.;
auto progress = 0.;
if (position > length) {
progress = 1.;
} else if (length) {
progress = length ? snap(float64(position) / length, 0., 1.) : 0.;
}
if (length != _length || position != _position || wasDisabled) {
auto animated = (length && _length&& progress > _slider->value());
_slider->setValue(progress, animated);
if (length != _length || position != _position || wasInLoadingState) {
auto animated = (length && _length && progress > value());
setValue(progress, animated);
_position = position;
_length = length;
}
_slider->update();
}
void Playback::updateLoadingState(float64 progress) {
setDisabled(true);
auto animated = progress > _slider->value();
_slider->setValue(progress, animated);
if (!_inLoadingState) {
_inLoadingState = true;
if (_inLoadingStateChanged) {
_inLoadingStateChanged(true);
}
}
auto animated = (progress > value());
setValue(progress, animated);
}
float64 Playback::value() const {
return a_value.current();
}
void Playback::setValue(float64 value, bool animated) {
if (animated) {
a_value.start(value);
_a_value.start();
} else {
a_value = anim::value(value, value);
_a_value.stop();
}
if (_valueChanged) {
_valueChanged(a_value.current());
}
}
void Playback::step_value(float64 ms, bool timer) {
auto dt = ms / (2 * AudioVoiceMsgUpdateView);
if (dt >= 1) {
_a_value.stop();
a_value.finish();
} else {
a_value.update(qMin(dt, 1.), anim::linear);
}
if (timer && _valueChanged) {
_valueChanged(a_value.current());
}
}
} // namespace Clip

View File

@ -31,41 +31,31 @@ namespace Clip {
class Playback {
public:
Playback(Ui::ContinuousSlider *slider);
Playback();
void setValueChangedCallback(base::lambda<void(float64)> callback) {
_valueChanged = std::move(callback);
}
void setInLoadingStateChangedCallback(base::lambda<void(bool)> callback) {
_inLoadingStateChanged = std::move(callback);
}
void setValue(float64 value, bool animated);
void updateState(const Player::TrackState &state);
void updateLoadingState(float64 progress);
void setFadeOpacity(float64 opacity) {
_slider->setFadeOpacity(opacity);
}
void setChangeProgressCallback(Ui::ContinuousSlider::Callback &&callback) {
_slider->setChangeProgressCallback(std::move(callback));
}
void setChangeFinishedCallback(Ui::ContinuousSlider::Callback &&callback) {
_slider->setChangeFinishedCallback(std::move(callback));
}
void setGeometry(int x, int y, int w, int h) {
_slider->setGeometry(x, y, w, h);
}
void hide() {
_slider->hide();
}
void show() {
_slider->show();
}
void moveToLeft(int x, int y) {
_slider->moveToLeft(x, y);
}
void resize(int w, int h) {
_slider->resize(w, h);
}
void setDisabled(bool disabled) {
_slider->setDisabled(disabled);
}
private:
Ui::ContinuousSlider *_slider;
float64 value() const;
void step_value(float64 ms, bool timer);
// This can animate for a very long time (like in music playing),
// so it should be a BasicAnimation, not an Animation.
anim::value a_value;
BasicAnimation _a_value;
base::lambda<void(float64)> _valueChanged;
bool _inLoadingState = false;
base::lambda<void(bool)> _inLoadingStateChanged;
int64 _position = 0;
int64 _length = 0;

View File

@ -27,15 +27,10 @@ constexpr auto kByWheelFinishedTimeout = 1000;
} // namespace
ContinuousSlider::ContinuousSlider(QWidget *parent) : TWidget(parent)
, _a_value(animation(this, &ContinuousSlider::step_value)) {
ContinuousSlider::ContinuousSlider(QWidget *parent) : TWidget(parent) {
setCursor(style::cur_pointer);
}
float64 ContinuousSlider::value() const {
return a_value.current();
}
void ContinuousSlider::setDisabled(bool disabled) {
if (_disabled != disabled) {
_disabled = disabled;
@ -50,7 +45,7 @@ void ContinuousSlider::setMoveByWheel(bool move) {
_byWheelFinished = std::make_unique<SingleTimer>();
_byWheelFinished->setTimeoutHandler([this] {
if (_changeFinishedCallback) {
_changeFinishedCallback(getCurrentValue(getms()));
_changeFinishedCallback(getCurrentValue());
}
});
} else {
@ -59,14 +54,8 @@ void ContinuousSlider::setMoveByWheel(bool move) {
}
}
void ContinuousSlider::setValue(float64 value, bool animated) {
if (animated) {
a_value.start(value);
_a_value.start();
} else {
a_value = anim::value(value, value);
_a_value.stop();
}
void ContinuousSlider::setValue(float64 value) {
_value = value;
update();
}
@ -75,17 +64,6 @@ void ContinuousSlider::setFadeOpacity(float64 opacity) {
update();
}
void ContinuousSlider::step_value(float64 ms, bool timer) {
float64 dt = ms / (2 * AudioVoiceMsgUpdateView);
if (dt >= 1) {
_a_value.stop();
a_value.finish();
} else {
a_value.update(qMin(dt, 1.), anim::linear);
}
if (timer) update();
}
void ContinuousSlider::mouseMoveEvent(QMouseEvent *e) {
if (_mouseDown) {
updateDownValueFromPos(e->pos());
@ -115,8 +93,7 @@ void ContinuousSlider::mouseReleaseEvent(QMouseEvent *e) {
if (_changeFinishedCallback) {
_changeFinishedCallback(_downValue);
}
a_value = anim::value(_downValue, _downValue);
_a_value.stop();
_value = _downValue;
update();
}
}
@ -139,8 +116,8 @@ void ContinuousSlider::wheelEvent(QWheelEvent *e) {
deltaX *= -1;
}
auto delta = (qAbs(deltaX) > qAbs(deltaY)) ? deltaX : deltaY;
auto finalValue = snap(a_value.to() + delta * coef, 0., 1.);
setValue(finalValue, false);
auto finalValue = snap(_value + delta * coef, 0., 1.);
setValue(finalValue);
if (_changeProgressCallback) {
_changeProgressCallback(finalValue);
}
@ -197,7 +174,7 @@ void FilledSlider::paintEvent(QPaintEvent *e) {
auto lineWidthRounded = qFloor(lineWidth);
auto lineWidthPartial = lineWidth - lineWidthRounded;
auto seekRect = getSeekRect();
auto value = getCurrentValue(ms);
auto value = getCurrentValue();
auto from = seekRect.x(), mid = qRound(from + value * seekRect.width()), end = from + seekRect.width();
if (mid > from) {
p.setOpacity(masterOpacity);
@ -244,7 +221,7 @@ void MediaSlider::paintEvent(QPaintEvent *e) {
auto disabled = isDisabled();
auto over = getCurrentOverFactor(ms);
auto seekRect = getSeekRect();
auto value = getCurrentValue(ms);
auto value = getCurrentValue();
// invert colors and value for vertical
if (!horizontal) value = 1. - value;

View File

@ -38,7 +38,7 @@ public:
}
float64 value() const;
void setValue(float64 value, bool animated);
void setValue(float64 value);
void setFadeOpacity(float64 opacity);
void setDisabled(bool disabled);
bool isDisabled() const {
@ -69,9 +69,8 @@ protected:
float64 fadeOpacity() const {
return _fadeOpacity;
}
float64 getCurrentValue(TimeMs ms) {
_a_value.step(ms);
return _mouseDown ? _downValue : a_value.current();
float64 getCurrentValue() {
return _mouseDown ? _downValue : _value;
}
float64 getCurrentOverFactor(TimeMs ms) {
return _disabled ? 0. : _a_over.current(ms, _over ? 1. : 0.);
@ -91,7 +90,6 @@ private:
return _byWheelFinished != nullptr;
}
void step_value(float64 ms, bool timer);
void setOver(bool over);
float64 computeValue(const QPoint &pos) const;
void updateDownValueFromPos(const QPoint &pos);
@ -107,10 +105,7 @@ private:
bool _over = false;
Animation _a_over;
// This can animate for a very long time (like in music playing),
// so it should be a BasicAnimation, not an Animation.
anim::value a_value;
BasicAnimation _a_value;
float64 _value = 0.;
bool _mouseDown = false;
float64 _downValue = 0.;