From 71b733a01824f359f2420b623663d9658f3811e7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 5 Mar 2019 15:06:54 +0400 Subject: [PATCH] Display receivedTill in video player controls. --- .../SourceFiles/media/audio/media_audio.h | 1 + .../player/media_player_round_controller.cpp | 2 +- .../media/player/media_player_widget.cpp | 2 +- .../streaming/media_streaming_player.cpp | 36 ++++- .../media/streaming/media_streaming_player.h | 10 +- .../media/view/media_view_overlay_widget.cpp | 9 +- .../view/media_view_playback_controls.cpp | 9 +- .../media/view/media_view_playback_controls.h | 1 + .../view/media_view_playback_progress.cpp | 90 ++++++++---- .../media/view/media_view_playback_progress.h | 15 +- .../SourceFiles/media/view/mediaview.style | 3 +- .../ui/widgets/continuous_sliders.cpp | 133 ++++++++++++------ .../ui/widgets/continuous_sliders.h | 14 +- Telegram/SourceFiles/ui/widgets/widgets.style | 2 + 14 files changed, 231 insertions(+), 96 deletions(-) diff --git a/Telegram/SourceFiles/media/audio/media_audio.h b/Telegram/SourceFiles/media/audio/media_audio.h index 0c95fd644..50737abff 100644 --- a/Telegram/SourceFiles/media/audio/media_audio.h +++ b/Telegram/SourceFiles/media/audio/media_audio.h @@ -116,6 +116,7 @@ struct TrackState { AudioMsgId id; State state = State::Stopped; int64 position = 0; + int64 receivedTill = 0; int64 length = 0; int frequency = kDefaultFrequency; bool waitingForData = false; diff --git a/Telegram/SourceFiles/media/player/media_player_round_controller.cpp b/Telegram/SourceFiles/media/player/media_player_round_controller.cpp index 8549c8edd..792a4c180 100644 --- a/Telegram/SourceFiles/media/player/media_player_round_controller.cpp +++ b/Telegram/SourceFiles/media/player/media_player_round_controller.cpp @@ -57,7 +57,7 @@ RoundController::RoundController( [=](Clip::Notification notification) { callback(notification); }, Clip::Reader::Mode::Video); _playbackProgress = std::make_unique(); - _playbackProgress->setValueChangedCallback([=](float64 value) { + _playbackProgress->setValueChangedCallback([=](float64, float64) { Auth().data().requestItemRepaint(_context); }); Auth().data().markMediaRead(_data); diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index 45ae234e6..a1d5b93c7 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -97,7 +97,7 @@ Widget::Widget(QWidget *parent) : RpWidget(parent) _playbackProgress->setInLoadingStateChangedCallback([this](bool loading) { _playbackSlider->setDisabled(loading); }); - _playbackProgress->setValueChangedCallback([this](float64 value) { + _playbackProgress->setValueChangedCallback([this](float64 value, float64) { _playbackSlider->setValue(value); }); _playbackSlider->setChangeProgressCallback([this](float64 value) { diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp index 1ed46c0b2..d62350726 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.cpp @@ -140,8 +140,8 @@ void Player::trackPlayedTill( if (guard && position != kTimeUnknown) { state.position = position; const auto value = _options.loop - ? position - : (position % _totalDuration); + ? (position % _totalDuration) + : position; _updates.fire({ PlaybackUpdate{ value } }); } if (_pauseReading @@ -159,9 +159,12 @@ void Player::trackSendReceivedTill( Expects(state.duration != kTimeUnknown); Expects(state.receivedTill != kTimeUnknown); + const auto receivedTill = std::max( + state.receivedTill, + _previousReceivedTill); const auto value = _options.loop - ? state.receivedTill - : (state.receivedTill % _totalDuration); + ? (receivedTill % _totalDuration) + : receivedTill; _updates.fire({ PreloadedUpdate{ value } }); } @@ -376,9 +379,12 @@ void Player::play(const PlaybackOptions &options) { // Looping video with audio is not supported for now. Expects(!options.loop || (options.mode != Mode::Both)); + const auto previous = getCurrentReceivedTill(); + stop(); _lastFailureStage = Stage::Uninitialized; + savePreviousReceivedTill(options, previous); _options = options; if (!Media::Audio::SupportsSpeedControl()) { _options.speed = 1.; @@ -387,6 +393,17 @@ void Player::play(const PlaybackOptions &options) { _file->start(delegate(), _options.position); } +void Player::savePreviousReceivedTill( + const PlaybackOptions &options, + crl::time previousReceivedTill) { + // Save previous 'receivedTill' values if we seek inside the range. + _previousReceivedTill = ((options.position >= _options.position) + && (options.mode == _options.mode) + && (options.position < previousReceivedTill)) + ? previousReceivedTill + : kTimeUnknown; +} + void Player::pause() { Expects(active()); @@ -626,6 +643,7 @@ Media::Player::TrackState Player::prepareLegacyState() const { } else if (_options.loop && _totalDuration > 0) { result.position %= _totalDuration; } + result.receivedTill = getCurrentReceivedTill(); result.length = _totalDuration; if (result.length == kTimeUnknown) { const auto document = _options.audioId.audio(); @@ -644,6 +662,16 @@ Media::Player::TrackState Player::prepareLegacyState() const { return result; } +crl::time Player::getCurrentReceivedTill() const { + const auto previous = std::max(_previousReceivedTill, crl::time(0)); + const auto result = std::min( + std::max(_information.audio.state.receivedTill, previous), + std::max(_information.video.state.receivedTill, previous)); + return (result >= 0 && _totalDuration > 1 && _options.loop) + ? (result % _totalDuration) + : result; +} + rpl::lifetime &Player::lifetime() { return _lifetime; } diff --git a/Telegram/SourceFiles/media/streaming/media_streaming_player.h b/Telegram/SourceFiles/media/streaming/media_streaming_player.h index 3794a70bc..02f4ffae2 100644 --- a/Telegram/SourceFiles/media/streaming/media_streaming_player.h +++ b/Telegram/SourceFiles/media/streaming/media_streaming_player.h @@ -105,6 +105,10 @@ private: [[nodiscard]] bool bothReceivedEnough(crl::time amount) const; [[nodiscard]] bool receivedTillEnd() const; void checkResumeFromWaitingForData(); + [[nodiscard]] crl::time getCurrentReceivedTill() const; + void savePreviousReceivedTill( + const PlaybackOptions &options, + crl::time previousReceivedTill); template void trackReceivedTill( @@ -153,14 +157,16 @@ private: bool _audioFinished = false; bool _videoFinished = false; - crl::time _totalDuration = 0; - crl::time _loopingShift = 0; crl::time _startedTime = kTimeUnknown; crl::time _pausedTime = kTimeUnknown; crl::time _nextFrameTime = kTimeUnknown; base::Timer _renderFrameTimer; rpl::event_stream _updates; + crl::time _totalDuration = 0; + crl::time _loopingShift = 0; + crl::time _previousReceivedTill = kTimeUnknown; + rpl::lifetime _lifetime; rpl::lifetime _sessionLifetime; diff --git a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp index 528f384ef..48b40e8c8 100644 --- a/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp +++ b/Telegram/SourceFiles/media/view/media_view_overlay_widget.cpp @@ -2011,7 +2011,7 @@ void OverlayWidget::handleStreamingUpdate(Streaming::Update &&update) { streamingReady(std::move(update)); }, [&](const PreloadedVideo &update) { _streamed->info.video.state.receivedTill = update.till; - //updatePlaybackState(); + updatePlaybackState(); }, [&](const UpdateVideo &update) { _streamed->info.video.state.position = update.position; this->update(contentRect()); @@ -2019,7 +2019,7 @@ void OverlayWidget::handleStreamingUpdate(Streaming::Update &&update) { updatePlaybackState(); }, [&](const PreloadedAudio &update) { _streamed->info.audio.state.receivedTill = update.till; - //updatePlaybackState(); + updatePlaybackState(); }, [&](const UpdateAudio &update) { _streamed->info.audio.state.position = update.position; updatePlaybackState(); @@ -2240,7 +2240,7 @@ void OverlayWidget::playbackControlsVolumeChanged(float64 volume) { void OverlayWidget::playbackToggleFullScreen() { Expects(_streamed != nullptr); - if (!videoShown()) { + if (!videoShown() || (videoIsGifv() && !_fullScreenVideo)) { return; } _fullScreenVideo = !_fullScreenVideo; @@ -2279,6 +2279,9 @@ void OverlayWidget::playbackResumeOnCall() { void OverlayWidget::updatePlaybackState() { Expects(_streamed != nullptr); + if (videoIsGifv()) { + return; + } auto state = _streamed->player.prepareLegacyState(); if (state.length == kTimeUnknown) { const auto duration = _doc->song() diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp index be91e337c..c71aa9021 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.cpp @@ -58,11 +58,10 @@ PlaybackControls::PlaybackControls(QWidget *parent, not_null delegat } }); - _playbackProgress->setInLoadingStateChangedCallback([=](bool loading) { - _playbackSlider->setDisabled(loading); - }); - _playbackProgress->setValueChangedCallback([=](float64 value) { - _playbackSlider->setValue(value); + _playbackProgress->setValueChangedCallback([=]( + float64 value, + float64 receivedTill) { + _playbackSlider->setValue(value, receivedTill); }); _playbackSlider->setChangeProgressCallback([=](float64 value) { _playbackProgress->setValue(value, false); diff --git a/Telegram/SourceFiles/media/view/media_view_playback_controls.h b/Telegram/SourceFiles/media/view/media_view_playback_controls.h index 94c17b24d..44ca3b5a9 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_controls.h +++ b/Telegram/SourceFiles/media/view/media_view_playback_controls.h @@ -78,6 +78,7 @@ private: object_ptr _playPauseResume; object_ptr _playbackSlider; std::unique_ptr _playbackProgress; + std::unique_ptr _receivedTillProgress; object_ptr _volumeController; object_ptr _fullScreenToggle; object_ptr _playedAlready; diff --git a/Telegram/SourceFiles/media/view/media_view_playback_progress.cpp b/Telegram/SourceFiles/media/view/media_view_playback_progress.cpp index 57f7f025d..66c8836bc 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_progress.cpp +++ b/Telegram/SourceFiles/media/view/media_view_playback_progress.cpp @@ -18,13 +18,24 @@ constexpr auto kPlaybackAnimationDurationMs = crl::time(200); } // namespace -PlaybackProgress::PlaybackProgress() : _a_value(animation(this, &PlaybackProgress::step_value)) { +PlaybackProgress::PlaybackProgress() +: _a_value(animation(this, &PlaybackProgress::step_value)) +, _a_receivedTill(animation(this, &PlaybackProgress::step_receivedTill)) { } void PlaybackProgress::updateState(const Player::TrackState &state) { - qint64 position = 0, length = state.length; + _playing = !Player::IsStopped(state.state); + const auto length = state.length; + const auto position = Player::IsStoppedAtEnd(state.state) + ? state.length + : Player::IsStoppedOrStopping(state.state) + ? 0 + : state.position; + const auto receivedTill = (length && state.receivedTill > position) + ? state.receivedTill + : -1; - auto wasInLoadingState = _inLoadingState; + const auto wasInLoadingState = _inLoadingState; if (wasInLoadingState) { _inLoadingState = false; if (_inLoadingStateChanged) { @@ -32,23 +43,16 @@ void PlaybackProgress::updateState(const Player::TrackState &state) { } } - _playing = !Player::IsStopped(state.state); - if (Player::IsStoppedAtEnd(state.state)) { - position = state.length; - } else if (!Player::IsStoppedOrStopping(state.state)) { - position = state.position; - } else { - position = 0; - } - - auto progress = 0.; - if (position > length) { - progress = 1.; - } else if (length) { - progress = snap(float64(position) / length, 0., 1.); - } - auto animatedPosition = position + (state.frequency * kPlaybackAnimationDurationMs / 1000); - auto animatedProgress = length ? qMax(float64(animatedPosition) / length, 0.) : 0.; + const auto progress = (position > length) + ? 1. + : length + ? snap(float64(position) / length, 0., 1.) + : 0.; + const auto receivedTillProgress = (receivedTill > position) + ? snap(float64(receivedTill) / length, 0., 1.) + : -1.; + const auto animatedPosition = position + (state.frequency * kPlaybackAnimationDurationMs / 1000); + const auto animatedProgress = length ? qMax(float64(animatedPosition) / length, 0.) : 0.; if (length != _length || position != _position || wasInLoadingState) { if (auto animated = (length && _length && animatedProgress > value())) { setValue(animatedProgress, animated); @@ -58,6 +62,10 @@ void PlaybackProgress::updateState(const Player::TrackState &state) { _position = position; _length = length; } + if (receivedTill != _receivedTill) { + setReceivedTill(receivedTillProgress); + _receivedTill = receivedTill; + } } void PlaybackProgress::updateLoadingState(float64 progress) { @@ -88,9 +96,22 @@ void PlaybackProgress::setValue(float64 value, bool animated) { a_value = anim::value(value, value); _a_value.stop(); } - if (_valueChanged) { - _valueChanged(a_value.current()); + emitUpdatedValue(); +} + +void PlaybackProgress::setReceivedTill(float64 value) { + const auto current = a_receivedTill.current(); + if (value > current && current > 0.) { + a_receivedTill.start(value); + _a_receivedTill.start(); + } else if (value > a_value.current()) { + a_receivedTill = anim::value(a_value.current(), value); + _a_receivedTill.start(); + } else { + a_receivedTill = anim::value(-1., -1.); + _a_receivedTill.stop(); } + emitUpdatedValue(); } void PlaybackProgress::step_value(float64 ms, bool timer) { @@ -101,8 +122,29 @@ void PlaybackProgress::step_value(float64 ms, bool timer) { } else { a_value.update(dt, anim::linear); } - if (timer && _valueChanged) { - _valueChanged(a_value.current()); + if (timer) { + emitUpdatedValue(); + } +} + +void PlaybackProgress::step_receivedTill(float64 ms, bool timer) { + auto dt = anim::Disabled() ? 1. : (ms / kPlaybackAnimationDurationMs); + if (dt >= 1.) { + _a_receivedTill.stop(); + a_receivedTill.finish(); + } else { + a_receivedTill.update(dt, anim::linear); + } + if (timer) { + emitUpdatedValue(); + } +} + +void PlaybackProgress::emitUpdatedValue() { + if (_valueChanged) { + const auto value = a_value.current(); + const auto receivedTill = a_receivedTill.current(); + _valueChanged(value, std::max(value, receivedTill)); } } diff --git a/Telegram/SourceFiles/media/view/media_view_playback_progress.h b/Telegram/SourceFiles/media/view/media_view_playback_progress.h index 55b577ae5..6692797ac 100644 --- a/Telegram/SourceFiles/media/view/media_view_playback_progress.h +++ b/Telegram/SourceFiles/media/view/media_view_playback_progress.h @@ -20,7 +20,7 @@ class PlaybackProgress { public: PlaybackProgress(); - void setValueChangedCallback(Fn callback) { + void setValueChangedCallback(Fn callback) { _valueChanged = std::move(callback); } void setInLoadingStateChangedCallback(Fn callback) { @@ -35,18 +35,23 @@ public: private: void step_value(float64 ms, bool timer); + void step_receivedTill(float64 ms, bool timer); + void setReceivedTill(float64 value); + void emitUpdatedValue(); // 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; - Fn _valueChanged; + // so it should be a BasicAnimation, not an Animation, because + // Animation pauses mtproto responses/updates handling while playing. + anim::value a_value, a_receivedTill; + BasicAnimation _a_value, _a_receivedTill; + Fn _valueChanged; bool _inLoadingState = false; Fn _inLoadingStateChanged; int64 _position = 0; int64 _length = 0; + int64 _receivedTill = -1; bool _playing = false; diff --git a/Telegram/SourceFiles/media/view/mediaview.style b/Telegram/SourceFiles/media/view/mediaview.style index 53eb0e2e5..444a9198b 100644 --- a/Telegram/SourceFiles/media/view/mediaview.style +++ b/Telegram/SourceFiles/media/view/mediaview.style @@ -16,9 +16,10 @@ mediaviewPlayback: MediaSlider { activeFg: mediaviewPlaybackActive; inactiveFg: mediaviewPlaybackInactive; activeFgOver: mediaviewPlaybackActiveOver; - inactiveFgOver: mediaviewPlaybackInactiveOver; + inactiveFgOver: mediaviewPlaybackInactive; activeFgDisabled: mediaviewPlaybackActive; inactiveFgDisabled: mediaviewPlaybackInactive; + receivedTillFg: mediaviewPlaybackInactiveOver; seekSize: size(11px, 11px); duration: mediaviewOverDuration; } diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp index bbb0b56bf..50304f4a8 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.cpp @@ -42,9 +42,26 @@ void ContinuousSlider::setMoveByWheel(bool move) { } } +QRect ContinuousSlider::getSeekRect() const { + const auto decrease = getSeekDecreaseSize(); + return isHorizontal() + ? QRect(decrease.width() / 2, 0, width() - decrease.width(), height()) + : QRect(0, decrease.height() / 2, width(), height() - decrease.width()); +} + void ContinuousSlider::setValue(float64 value) { - _value = value; - update(); + setValue(value, value); +} + +void ContinuousSlider::setValue(float64 value, float64 receivedTill) { + if (_value != value || _receivedTill != receivedTill) { + LOG(("UPDATED")); + _value = value; + _receivedTill = receivedTill; + update(); + } else { + LOG(("SKIPPED")); + } } void ContinuousSlider::setFadeOpacity(float64 opacity) { @@ -141,8 +158,8 @@ FilledSlider::FilledSlider(QWidget *parent, const style::FilledSlider &st) : Con , _st(st) { } -QRect FilledSlider::getSeekRect() const { - return QRect(0, 0, width(), height()); +QSize FilledSlider::getSeekDecreaseSize() const { + return QSize(0, 0); } float64 FilledSlider::getOverDuration() const { @@ -155,16 +172,18 @@ void FilledSlider::paintEvent(QPaintEvent *e) { p.setPen(Qt::NoPen); - auto masterOpacity = fadeOpacity(); - auto ms = crl::now(); - auto disabled = isDisabled(); - auto over = getCurrentOverFactor(ms); - auto lineWidth = _st.lineWidth + ((_st.fullWidth - _st.lineWidth) * over); - auto lineWidthRounded = qFloor(lineWidth); - auto lineWidthPartial = lineWidth - lineWidthRounded; - auto seekRect = getSeekRect(); - auto value = getCurrentValue(); - auto from = seekRect.x(), mid = qRound(from + value * seekRect.width()), end = from + seekRect.width(); + const auto masterOpacity = fadeOpacity(); + const auto ms = crl::now(); + const auto disabled = isDisabled(); + const auto over = getCurrentOverFactor(ms); + const auto lineWidth = _st.lineWidth + ((_st.fullWidth - _st.lineWidth) * over); + const auto lineWidthRounded = qFloor(lineWidth); + const auto lineWidthPartial = lineWidth - lineWidthRounded; + const auto seekRect = getSeekRect(); + const auto value = getCurrentValue(); + const auto from = seekRect.x(); + const auto mid = qRound(from + value * seekRect.width()); + const auto end = from + seekRect.width(); if (mid > from) { p.setOpacity(masterOpacity); p.fillRect(from, height() - lineWidthRounded, (mid - from), lineWidthRounded, disabled ? _st.disabledFg : _st.activeFg); @@ -187,10 +206,8 @@ MediaSlider::MediaSlider(QWidget *parent, const style::MediaSlider &st) : Contin , _st(st) { } -QRect MediaSlider::getSeekRect() const { - return isHorizontal() - ? QRect(_st.seekSize.width() / 2, 0, width() - _st.seekSize.width(), height()) - : QRect(0, _st.seekSize.height() / 2, width(), height() - _st.seekSize.width()); +QSize MediaSlider::getSeekDecreaseSize() const { + return _alwaysDisplayMarker ? _st.seekSize : QSize(); } float64 MediaSlider::getOverDuration() const { @@ -211,57 +228,81 @@ void MediaSlider::paintEvent(QPaintEvent *e) { p.setPen(Qt::NoPen); p.setOpacity(fadeOpacity()); - auto horizontal = isHorizontal(); - auto ms = crl::now(); - auto radius = _st.width / 2; - auto disabled = isDisabled(); - auto over = getCurrentOverFactor(ms); - auto seekRect = getSeekRect(); - auto value = getCurrentValue(); + const auto horizontal = isHorizontal(); + const auto ms = crl::now(); + const auto radius = _st.width / 2; + const auto disabled = isDisabled(); + const auto over = getCurrentOverFactor(ms); + const auto seekRect = getSeekRect(); // invert colors and value for vertical - if (!horizontal) value = 1. - value; + const auto value = horizontal + ? getCurrentValue() + : (1. - getCurrentValue()); - auto markerFrom = (horizontal ? seekRect.x() : seekRect.y()); - auto markerLength = (horizontal ? seekRect.width() : seekRect.height()); - auto from = _alwaysDisplayMarker ? 0 : markerFrom; - auto length = _alwaysDisplayMarker ? (horizontal ? width() : height()) : markerLength; - auto mid = qRound(from + value * length); - auto end = from + length; - auto activeFg = disabled ? _st.activeFgDisabled : anim::brush(_st.activeFg, _st.activeFgOver, over); - auto inactiveFg = disabled ? _st.inactiveFgDisabled : anim::brush(_st.inactiveFg, _st.inactiveFgOver, over); + // receivedTill is not supported for vertical + const auto receivedTill = horizontal + ? getCurrentReceivedTill() + : value; + + const auto markerFrom = (horizontal ? seekRect.x() : seekRect.y()); + const auto markerLength = (horizontal ? seekRect.width() : seekRect.height()); + const auto from = 0; + const auto length = (horizontal ? width() : height()); + const auto mid = qRound(from + value * length); + const auto till = std::max(mid, qRound(from + receivedTill * length)); + const auto end = from + length; + const auto activeFg = disabled ? _st.activeFgDisabled : anim::brush(_st.activeFg, _st.activeFgOver, over); + const auto receivedTillFg = _st.receivedTillFg; + const auto inactiveFg = disabled ? _st.inactiveFgDisabled : anim::brush(_st.inactiveFg, _st.inactiveFgOver, over); if (mid > from) { - auto fromClipRect = horizontal ? QRect(0, 0, mid, height()) : QRect(0, 0, width(), mid); + const auto fromClipRect = horizontal ? QRect(0, 0, mid, height()) : QRect(0, 0, width(), mid); const auto till = std::min(mid + radius, end); - auto fromRect = horizontal + const auto fromRect = horizontal ? QRect(from, (height() - _st.width) / 2, till - from, _st.width) : QRect((width() - _st.width) / 2, from, _st.width, till - from); p.setClipRect(fromClipRect); p.setBrush(horizontal ? activeFg : inactiveFg); p.drawRoundedRect(fromRect, radius, radius); } - if (end > mid) { - auto endClipRect = horizontal ? QRect(mid, 0, width() - mid, height()) : QRect(0, mid, width(), height() - mid); - const auto begin = std::max(mid - radius, from); - auto endRect = horizontal + if (till > mid) { + Assert(horizontal); + auto clipRect = QRect(mid, 0, till - mid, height()); + const auto left = std::max(mid - radius, from); + const auto right = std::min(till + radius, end); + const auto rect = QRect(left, (height() - _st.width) / 2, right - left, _st.width); + p.setClipRect(clipRect); + p.setBrush(receivedTillFg); + p.drawRoundedRect(rect, radius, radius); + } + if (end > till) { + const auto endClipRect = horizontal ? QRect(till, 0, width() - till, height()) : QRect(0, till, width(), height() - till); + const auto begin = std::max(till - radius, from); + const auto endRect = horizontal ? QRect(begin, (height() - _st.width) / 2, end - begin, _st.width) : QRect((width() - _st.width) / 2, begin, _st.width, end - begin); p.setClipRect(endClipRect); p.setBrush(horizontal ? inactiveFg : activeFg); p.drawRoundedRect(endRect, radius, radius); } - auto markerSizeRatio = disabled ? 0. : (_alwaysDisplayMarker ? 1. : over); + const auto markerSizeRatio = disabled ? 0. : (_alwaysDisplayMarker ? 1. : over); if (markerSizeRatio > 0) { - auto position = qRound(markerFrom + value * markerLength) - (horizontal ? seekRect.x() : seekRect.y()); - auto seekButton = horizontal + const auto position = qRound(markerFrom + value * markerLength) - (horizontal ? (_st.seekSize.width() / 2) : (_st.seekSize.height() / 2)); + const auto seekButton = horizontal ? QRect(position, (height() - _st.seekSize.height()) / 2, _st.seekSize.width(), _st.seekSize.height()) : QRect((width() - _st.seekSize.width()) / 2, position, _st.seekSize.width(), _st.seekSize.height()); - auto size = horizontal ? _st.seekSize.width() : _st.seekSize.height(); - auto remove = static_cast(((1. - markerSizeRatio) * size) / 2.); + const auto size = horizontal ? _st.seekSize.width() : _st.seekSize.height(); + const auto remove = static_cast(((1. - markerSizeRatio) * size) / 2.); if (remove * 2 < size) { p.setClipRect(rect()); p.setBrush(activeFg); - p.drawEllipse(seekButton.marginsRemoved(QMargins(remove, remove, remove, remove))); + const auto xshift = horizontal + ? std::max(seekButton.x() + seekButton.width() - remove - width(), 0) + std::min(seekButton.x() + remove, 0) + : 0; + const auto yshift = horizontal + ? 0 + : std::max(seekButton.y() + seekButton.height() - remove - height(), 0) + std::min(seekButton.y() + remove, 0); + p.drawEllipse(seekButton.marginsRemoved(QMargins(remove, remove, remove, remove)).translated(-xshift, -yshift)); } } } diff --git a/Telegram/SourceFiles/ui/widgets/continuous_sliders.h b/Telegram/SourceFiles/ui/widgets/continuous_sliders.h index 079940ef9..55fce2e1f 100644 --- a/Telegram/SourceFiles/ui/widgets/continuous_sliders.h +++ b/Telegram/SourceFiles/ui/widgets/continuous_sliders.h @@ -31,6 +31,7 @@ public: float64 value() const; void setValue(float64 value); + void setValue(float64 value, float64 receivedTill); void setFadeOpacity(float64 opacity); void setDisabled(bool disabled); bool isDisabled() const { @@ -63,9 +64,12 @@ protected: float64 fadeOpacity() const { return _fadeOpacity; } - float64 getCurrentValue() { + float64 getCurrentValue() const { return _mouseDown ? _downValue : _value; } + float64 getCurrentReceivedTill() const { + return _receivedTill; + } float64 getCurrentOverFactor(crl::time ms) { return _disabled ? 0. : _a_over.current(ms, _over ? 1. : 0.); } @@ -75,9 +79,10 @@ protected: bool isHorizontal() const { return (_direction == Direction::Horizontal); } + QRect getSeekRect() const; + virtual QSize getSeekDecreaseSize() const = 0; private: - virtual QRect getSeekRect() const = 0; virtual float64 getOverDuration() const = 0; bool moveByWheel() const { @@ -101,6 +106,7 @@ private: Animation _a_over; float64 _value = 0.; + float64 _receivedTill = 0.; bool _mouseDown = false; float64 _downValue = 0.; @@ -117,7 +123,7 @@ protected: void paintEvent(QPaintEvent *e) override; private: - QRect getSeekRect() const override; + QSize getSeekDecreaseSize() const override; float64 getOverDuration() const override; const style::FilledSlider &_st; @@ -176,7 +182,7 @@ protected: void paintEvent(QPaintEvent *e) override; private: - QRect getSeekRect() const override; + QSize getSeekDecreaseSize() const override; float64 getOverDuration() const override; const style::MediaSlider &_st; diff --git a/Telegram/SourceFiles/ui/widgets/widgets.style b/Telegram/SourceFiles/ui/widgets/widgets.style index 793ecae8a..0a8865129 100644 --- a/Telegram/SourceFiles/ui/widgets/widgets.style +++ b/Telegram/SourceFiles/ui/widgets/widgets.style @@ -291,6 +291,7 @@ MediaSlider { inactiveFgOver: color; activeFgDisabled: color; inactiveFgDisabled: color; + receivedTillFg: color; seekSize: size; duration: int; } @@ -906,6 +907,7 @@ defaultContinuousSlider: MediaSlider { inactiveFgOver: mediaPlayerInactiveFg; activeFgDisabled: mediaPlayerInactiveFg; inactiveFgDisabled: windowBg; + receivedTillFg: mediaPlayerInactiveFg; seekSize: size(9px, 9px); duration: 150; }