diff --git a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp index bd12a4521..87c2147e0 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.cpp @@ -1534,13 +1534,11 @@ void VoiceRecordBar::init() { if (_startRecordingFilter && _startRecordingFilter()) { return; } - _recordingTipRequired = true; + _recordingTipRequire = crl::now(); _recordingVideo = (_send->type() == Ui::SendButton::Type::Round); _startTimer.callOnce(st::universalDuration); } else if (e->type() == QEvent::MouseButtonRelease) { - if (base::take(_recordingTipRequired)) { - _recordingTipRequests.fire({}); - } + checkTipRequired(); _startTimer.cancel(); } }, lifetime()); @@ -1694,7 +1692,6 @@ void VoiceRecordBar::startRecording() { } instance()->updated( ) | rpl::start_with_next_error([=](const Update &update) { - _recordingTipRequired = (update.samples < kMinSamples); recordUpdated(update.level, update.samples); }, [=] { stop(false); @@ -1702,10 +1699,9 @@ void VoiceRecordBar::startRecording() { if (_videoRecorder) { _videoRecorder->updated( ) | rpl::start_with_next_error([=](const Update &update) { - _recordingTipRequired = (update.samples < kMinSamples); recordUpdated(update.level, update.samples); if (update.finished) { - stop(true); + stop(update.samples >= kMinSamples); } }, [=] { stop(false); @@ -1742,14 +1738,22 @@ void VoiceRecordBar::startRecording() { } computeAndSetLockProgress(mouse->globalPos()); } else if (type == QEvent::MouseButtonRelease) { - if (base::take(_recordingTipRequired)) { - _recordingTipRequests.fire({}); - } + checkTipRequired(); stop(_inField.current()); } }, _recordingLifetime); } +void VoiceRecordBar::checkTipRequired() { + const auto require = base::take(_recordingTipRequire); + const auto duration = st::universalDuration + + (kMinSamples * crl::time(1000) + / ::Media::Player::kDefaultFrequency); + if (require && (require + duration > crl::now())) { + _recordingTipRequests.fire({}); + } +} + void VoiceRecordBar::recordUpdated(quint16 level, int samples) { _level->requestPaintLevel(level); _recordingSamples = samples; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.h b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.h index 92ac2e6f8..3d6f1a878 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.h +++ b/Telegram/SourceFiles/history/view/controls/history_view_voice_record_bar.h @@ -124,6 +124,7 @@ private: void updateTTLGeometry(TTLAnimationType type, float64 progress); void recordUpdated(quint16 level, int samples); + void checkTipRequired(); void stop(bool send); void stopRecording(StopType type, bool ttlBeforeHide = false); @@ -192,7 +193,7 @@ private: float64 _redCircleProgress = 0.; rpl::event_stream<> _recordingTipRequests; - bool _recordingTipRequired = false; + crl::time _recordingTipRequire = 0; bool _lockFromBottom = false; std::unique_ptr _videoRecorder; diff --git a/Telegram/SourceFiles/ui/controls/round_video_recorder.cpp b/Telegram/SourceFiles/ui/controls/round_video_recorder.cpp index b903a27af..c249ecf5b 100644 --- a/Telegram/SourceFiles/ui/controls/round_video_recorder.cpp +++ b/Telegram/SourceFiles/ui/controls/round_video_recorder.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ffmpeg/ffmpeg_utility.h" #include "media/audio/media_audio_capture.h" #include "ui/image/image_prepare.h" +#include "ui/arc_angles.h" #include "ui/painter.h" #include "ui/rp_widget.h" #include "webrtc/webrtc_video_track.h" @@ -703,10 +704,14 @@ Fn RoundVideoRecorder::audioChunkProcessor() { }; } -auto RoundVideoRecorder::updated() const +auto RoundVideoRecorder::updated() -> rpl::producer { return _private.producer_on_main([](const Private &that) { return that.updated(); + }) | rpl::before_next([=](const Update &update) { + const auto duration = (update.samples * crl::time(1000)) + / kAudioFrequency; + progressTo(duration / (1. * kMaxDuration)); }); } @@ -725,6 +730,19 @@ void RoundVideoRecorder::hide(Fn done) { } } +void RoundVideoRecorder::progressTo(float64 progress) { + if (_progress == progress) { + return; + } + _progressAnimation.start( + [=] { _preview->update(); }, + progress, + _progress, + kUpdateEach); + _progress = progress; + _preview->update(); +} + void RoundVideoRecorder::prepareFrame() { if (_frameOriginal.isNull() || _preparedIndex == _lastAddedIndex) { return; @@ -755,29 +773,82 @@ void RoundVideoRecorder::prepareFrame() { _framePrepared.setDevicePixelRatio(ratio); } +void RoundVideoRecorder::createImages() { + const auto ratio = style::DevicePixelRatio(); + _framePrepared = QImage( + QSize(_side, _side) * ratio, + QImage::Format_ARGB32_Premultiplied); + _framePrepared.fill(Qt::transparent); + _framePrepared.setDevicePixelRatio(ratio); + auto p = QPainter(&_framePrepared); + auto hq = PainterHighQualityEnabler(p); + + p.setPen(Qt::NoPen); + p.setBrush(Qt::black); + p.drawEllipse(0, 0, _side, _side); + + const auto side = _side + 2 * _extent; + _shadow = QImage( + QSize(side, side) * ratio, + QImage::Format_ARGB32_Premultiplied); + _shadow.fill(Qt::transparent); + _shadow.setDevicePixelRatio(ratio); + + auto sp = QPainter(&_shadow); + auto shq = PainterHighQualityEnabler(sp); + + QRadialGradient gradient( + QPointF(_extent + _side / 2, _extent + _side / 2), + _side / 2 + _extent); + gradient.setColorAt(0, QColor(0, 0, 0, 128)); + gradient.setColorAt(0.8, QColor(0, 0, 0, 64)); + gradient.setColorAt(1, QColor(0, 0, 0, 0)); + + sp.setPen(Qt::NoPen); + sp.fillRect(0, 0, side, side, gradient); + sp.end(); +} + void RoundVideoRecorder::setup() { const auto raw = _preview.get(); _side = style::ConvertScale(kSide * 3 / 4); + _progressStroke = st::radialLine; + _extent = _progressStroke * 8; + createImages(); + _descriptor.container->sizeValue( ) | rpl::start_with_next([=](QSize outer) { + const auto side = _side + 2 * _extent; raw->setGeometry( style::centerrect( QRect(QPoint(), outer), - QRect(0, 0, _side, _side))); + QRect(0, 0, side, side))); }, raw->lifetime()); raw->paintRequest() | rpl::start_with_next([=] { prepareFrame(); auto p = QPainter(raw); - if (!_framePrepared.isNull()) { - p.drawImage(QRect(0, 0, _side, _side), _framePrepared); - } else { + p.drawImage(raw->rect(), _shadow); + const auto inner = QRect(_extent, _extent, _side, _side); + p.drawImage(inner, _framePrepared); + if (_progress > 0.) { auto hq = PainterHighQualityEnabler(p); - p.setPen(Qt::NoPen); - p.setBrush(QColor(0, 0, 0)); - p.drawEllipse(0, 0, _side, _side); + p.setPen(QPen( + Qt::white, + _progressStroke, + Qt::SolidLine, + Qt::RoundCap)); + p.setBrush(Qt::NoBrush); + const auto add = _progressStroke * 3 / 2.; + const auto full = arc::kFullLength; + const auto length = int(base::SafeRound( + _progressAnimation.value(_progress) * full)); + p.drawArc( + QRectF(inner).marginsAdded({ add, add, add, add }), + (full / 4) - length, + length); } }, raw->lifetime()); diff --git a/Telegram/SourceFiles/ui/controls/round_video_recorder.h b/Telegram/SourceFiles/ui/controls/round_video_recorder.h index 4d2042fab..6e9917d76 100644 --- a/Telegram/SourceFiles/ui/controls/round_video_recorder.h +++ b/Telegram/SourceFiles/ui/controls/round_video_recorder.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "ui/effects/animations.h" + #include namespace Media::Capture { @@ -51,22 +53,29 @@ public: void hide(Fn done = nullptr); using Update = Media::Capture::Update; - [[nodiscard]] rpl::producer updated() const; + [[nodiscard]] rpl::producer updated(); private: class Private; void setup(); void prepareFrame(); + void createImages(); + void progressTo(float64 progress); const RoundVideoRecorderDescriptor _descriptor; std::unique_ptr _preview; crl::object_on_queue _private; + Ui::Animations::Simple _progressAnimation; + float64 _progress = 0.; QImage _frameOriginal; QImage _framePrepared; + QImage _shadow; int _lastAddedIndex = 0; int _preparedIndex = 0; int _side = 0; + int _progressStroke = 0; + int _extent = 0; bool _paused = false; };