From db564ca486cd73dec529a6bb3b537a34d38fd04e Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Sun, 4 Oct 2020 02:25:25 +0300 Subject: [PATCH] Replaced voice record processing with VoiceRecordBar in HistoryWidget. --- Telegram/SourceFiles/config.h | 3 - .../SourceFiles/history/history_widget.cpp | 310 ++++++------------ Telegram/SourceFiles/history/history_widget.h | 29 +- .../history_view_voice_record_bar.cpp | 3 +- 4 files changed, 103 insertions(+), 242 deletions(-) diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index f9b9f496d..40ee00143 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -29,9 +29,6 @@ enum { LinksOverviewPerPage = 12, MediaOverviewStartPerPage = 5, - AudioVoiceMsgMaxLength = 100 * 60, // 100 minutes - AudioVoiceMsgChannels = 2, // stereo - PreloadHeightsCount = 3, // when 3 screens to scroll left make a preload request SearchPeopleLimit = 5, diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 71e7780e9..2cdfee1ac 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -62,6 +62,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_inner_widget.h" #include "history/history_item_components.h" //#include "history/feed/history_feed_section.h" // #feed +#include "history/view/controls/history_view_voice_record_bar.h" #include "history/view/history_view_service_message.h" #include "history/view/history_view_element.h" #include "history/view/history_view_scheduled_section.h" @@ -176,7 +177,7 @@ HistoryWidget::HistoryWidget( , _supportAutocomplete(session().supportMode() ? object_ptr(this, &session()) : nullptr) -, _send(this) +, _send(std::make_shared(this)) , _unblock(this, tr::lng_unblock_button(tr::now).toUpper(), st::historyUnblock) , _botStart(this, tr::lng_bot_start(tr::now).toUpper(), st::historyComposeButton) , _joinChannel( @@ -192,15 +193,16 @@ HistoryWidget::HistoryWidget( , _botKeyboardShow(this, st::historyBotKeyboardShow) , _botKeyboardHide(this, st::historyBotKeyboardHide) , _botCommandStart(this, st::historyBotCommandStart) +, _voiceRecordBar(std::make_unique( + this, + controller, + _send, + st::historySendSize.height())) , _field( this, st::historyComposeField, Ui::InputField::Mode::MultiLine, tr::lng_message_ph()) -, _recordCancelWidth(st::historyRecordFont->width(tr::lng_record_cancel(tr::now))) -, _recordingAnimation([=](crl::time now) { - return recordingAnimationCallback(now); -}) , _kbScroll(this, st::botKbScroll) , _topShadow(this) { setAcceptDrops(true); @@ -217,7 +219,7 @@ HistoryWidget::HistoryWidget( _send->addClickHandler([=] { sendButtonClicked(); }); SendMenu::SetupMenuAndShortcuts( - _send, + _send.get(), [=] { return sendButtonMenuType(); }, [=] { sendSilent(); }, [=] { sendScheduled(); }); @@ -349,10 +351,7 @@ HistoryWidget::HistoryWidget( _joinChannel->hide(); _muteUnmute->hide(); - _send->setRecordStartCallback([this] { recordStartCallback(); }); - _send->setRecordStopCallback([this](bool active) { recordStopCallback(active); }); - _send->setRecordUpdateCallback([this](QPoint globalPos) { recordUpdateCallback(globalPos); }); - _send->setRecordAnimationCallback([this] { updateField(); }); + initVoiceRecordBar(); _attachToggle->hide(); _tabbedSelectorToggle->hide(); @@ -717,6 +716,48 @@ void HistoryWidget::refreshTabbedPanel() { } } +void HistoryWidget::initVoiceRecordBar() { + _voiceRecordBar->startRecordingRequests( + ) | rpl::start_with_next([=] { + const auto error = _peer + ? Data::RestrictionError(_peer, ChatRestriction::f_send_media) + : std::nullopt; + if (error) { + Ui::show(Box(*error)); + return; + } else if (showSlowmodeError()) { + return; + } + _voiceRecordBar->startRecording(); + }, lifetime()); + + _voiceRecordBar->sendActionUpdates( + ) | rpl::start_with_next([=](const auto &data) { + if (!_history) { + return; + } + session().sendProgressManager().update( + _history, + data.type, + data.progress); + }, lifetime()); + + _voiceRecordBar->sendVoiceRequests( + ) | rpl::start_with_next([=](const auto &data) { + if (!canWriteMessage() || data.bytes.isEmpty() || !_history) { + return; + } + + auto action = Api::SendAction(_history); + action.replyTo = replyToId(); + session().api().sendVoiceMessage( + data.bytes, + data.waveform, + data.duration, + action); + }, lifetime()); +} + void HistoryWidget::initTabbedSelector() { refreshTabbedPanel(); @@ -1145,6 +1186,7 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) { } void HistoryWidget::orderWidgets() { + _send->raise(); if (_contactStatus) { _contactStatus->raise(); } @@ -1343,6 +1385,10 @@ void HistoryWidget::writeDrafts(Data::Draft **localDraft, Data::Draft **editDraf } } +bool HistoryWidget::isRecording() const { + return _voiceRecordBar->isRecording(); +} + void HistoryWidget::activate() { if (_history) { if (!_historyInited) { @@ -1360,7 +1406,7 @@ void HistoryWidget::setInnerFocus() { } else if (_list) { if (_nonEmptySelection || (_list && _list->wasSelectedText()) - || _recording + || isRecording() || isBotStart() || isBlocked() || !_canSendMessages) { @@ -1371,41 +1417,6 @@ void HistoryWidget::setInnerFocus() { } } -void HistoryWidget::recordDone( - QByteArray result, - VoiceWaveform waveform, - int samples) { - if (!canWriteMessage() || result.isEmpty()) { - return; - } - - Window::ActivateWindow(controller()); - const auto duration = samples / Media::Player::kDefaultFrequency; - auto action = Api::SendAction(_history); - action.replyTo = replyToId(); - session().api().sendVoiceMessage(result, waveform, duration, action); -} - -void HistoryWidget::recordUpdate(ushort level, int samples) { - if (!_recording) { - return; - } - - _recordingLevel.start(level); - _recordingAnimation.start(); - _recordingSamples = samples; - if (samples < 0 || samples >= Media::Player::kDefaultFrequency * AudioVoiceMsgMaxLength) { - stopRecording(_peer && samples > 0 && _inField); - } - Core::App().updateNonIdle(); - updateField(); - if (_history) { - session().sendProgressManager().update( - _history, - Api::SendProgressType::RecordVoice); - } -} - bool HistoryWidget::notify_switchInlineBotButtonReceived(const QString &query, UserData *samePeerBot, MsgId samePeerReplyTo) { if (samePeerBot) { if (_history) { @@ -2125,63 +2136,45 @@ void HistoryWidget::updateControlsVisibility() { _muteUnmute->hide(); _send->show(); updateSendButtonType(); - if (_recording) { - _field->hide(); + + _field->show(); + if (_kbShown) { + _kbScroll->show(); _tabbedSelectorToggle->hide(); + _botKeyboardHide->show(); + _botKeyboardShow->hide(); + _botCommandStart->hide(); + } else if (_kbReplyTo) { + _kbScroll->hide(); + _tabbedSelectorToggle->show(); + _botKeyboardHide->hide(); _botKeyboardShow->hide(); - _botKeyboardHide->hide(); _botCommandStart->hide(); - _attachToggle->hide(); - if (_silent) { - _silent->hide(); - } - if (_scheduled) { - _scheduled->hide(); - } - if (_kbShown) { - _kbScroll->show(); - } else { - _kbScroll->hide(); - } } else { - _field->show(); - if (_kbShown) { - _kbScroll->show(); - _tabbedSelectorToggle->hide(); - _botKeyboardHide->show(); - _botKeyboardShow->hide(); - _botCommandStart->hide(); - } else if (_kbReplyTo) { - _kbScroll->hide(); - _tabbedSelectorToggle->show(); - _botKeyboardHide->hide(); - _botKeyboardShow->hide(); + _kbScroll->hide(); + _tabbedSelectorToggle->show(); + _botKeyboardHide->hide(); + if (_keyboard->hasMarkup()) { + _botKeyboardShow->show(); _botCommandStart->hide(); } else { - _kbScroll->hide(); - _tabbedSelectorToggle->show(); - _botKeyboardHide->hide(); - if (_keyboard->hasMarkup()) { - _botKeyboardShow->show(); - _botCommandStart->hide(); + _botKeyboardShow->hide(); + if (_cmdStartShown) { + _botCommandStart->show(); } else { - _botKeyboardShow->hide(); - if (_cmdStartShown) { - _botCommandStart->show(); - } else { - _botCommandStart->hide(); - } + _botCommandStart->hide(); } } - _attachToggle->show(); - if (_silent) { - _silent->show(); - } - if (_scheduled) { - _scheduled->show(); - } - updateFieldPlaceholder(); } + _attachToggle->show(); + if (_silent) { + _silent->show(); + } + if (_scheduled) { + _scheduled->show(); + } + updateFieldPlaceholder(); + if (_editMsgId || _replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { if (_fieldBarCancel->isHidden()) { _fieldBarCancel->show(); @@ -3275,22 +3268,6 @@ void HistoryWidget::unreadMentionsAnimationFinish() { updateUnreadMentionsPosition(); } -bool HistoryWidget::recordingAnimationCallback(crl::time now) { - const auto dt = anim::Disabled() - ? 1. - : ((now - _recordingAnimation.started()) - / float64(kRecordingUpdateDelta)); - if (dt >= 1.) { - _recordingLevel.finish(); - } else { - _recordingLevel.update(dt, anim::linear); - } - if (!anim::Disabled()) { - update(_attachToggle->geometry()); - } - return (dt < 1.); -} - void HistoryWidget::chooseAttach() { if (_editMsgId) { Ui::show(Box(tr::lng_edit_caption_attach(tr::now))); @@ -3362,13 +3339,8 @@ void HistoryWidget::mouseMoveEvent(QMouseEvent *e) { } void HistoryWidget::updateOverStates(QPoint pos) { - auto inField = pos.y() >= (_scroll->y() + _scroll->height()) && pos.y() < height() && pos.x() >= 0 && pos.x() < width(); auto inReplyEditForward = QRect(st::historyReplySkip, _field->y() - st::historySendPadding - st::historyReplyHeight, width() - st::historyReplySkip - _fieldBarCancel->width(), st::historyReplyHeight).contains(pos) && (_editMsgId || replyToId() || readyToForward()); auto inClickable = inReplyEditForward; - if (inField != _inField && _recording) { - _inField = inField; - _send->setRecordActive(_inField); - } _inReplyEditForward = inReplyEditForward; if (inClickable != _inClickable) { _inClickable = inClickable; @@ -3382,85 +3354,11 @@ void HistoryWidget::leaveToChildEvent(QEvent *e, QWidget *child) { // e -- from } } -void HistoryWidget::recordStartCallback() { - using namespace Media::Capture; - - const auto error = _peer - ? Data::RestrictionError(_peer, ChatRestriction::f_send_media) - : std::nullopt; - if (error) { - Ui::show(Box(*error)); - return; - } else if (showSlowmodeError()) { - return; - } else if (!instance()->available()) { - return; - } - - instance()->start(); - instance()->updated( - ) | rpl::start_with_next_error([=](const Update &update) { - recordUpdate(update.level, update.samples); - }, [=] { - stopRecording(false); - }, _recordingLifetime); - - _recording = _inField = true; - updateControlsVisibility(); - activate(); - - updateField(); - - _send->setRecordActive(true); -} - -void HistoryWidget::recordStopCallback(bool active) { - stopRecording(_peer && active); -} - -void HistoryWidget::recordUpdateCallback(QPoint globalPos) { - updateOverStates(mapFromGlobal(globalPos)); -} - void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) { if (_replyForwardPressed) { _replyForwardPressed = false; update(0, _field->y() - st::historySendPadding - st::historyReplyHeight, width(), st::historyReplyHeight); } - if (_recording) { - stopRecording(_peer && _inField); - } -} - -void HistoryWidget::stopRecording(bool send) { - if (send) { - const auto weak = Ui::MakeWeak(this); - Media::Capture::instance()->stop(crl::guard(this, [=]( - const Media::Capture::Result &result) { - recordDone(result.bytes, result.waveform, result.samples); - })); - } else { - Media::Capture::instance()->stop(); - } - - _recordingLevel = anim::value(); - _recordingAnimation.stop(); - - _recordingLifetime.destroy(); - _recording = false; - _recordingSamples = 0; - if (_history) { - session().sendProgressManager().update( - _history, - Api::SendProgressType::RecordVoice, - -1); - } - - updateControlsVisibility(); - activate(); - - updateField(); - _send->setRecordActive(false); } void HistoryWidget::sendBotCommand( @@ -3971,6 +3869,7 @@ void HistoryWidget::moveFieldControls() { _field->moveToLeft(left, bottom - _field->height() - st::historySendPadding); auto right = st::historySendRight; _send->moveToRight(right, buttonsBottom); right += _send->width(); + _voiceRecordBar->moveToLeft(0, bottom - _voiceRecordBar->height()); _tabbedSelectorToggle->moveToRight(right, buttonsBottom); _botKeyboardHide->moveToRight(right, buttonsBottom); right += _botKeyboardHide->width(); _botKeyboardShow->moveToRight(right, buttonsBottom); @@ -4445,6 +4344,7 @@ void HistoryWidget::resizeEvent(QResizeEvent *e) { void HistoryWidget::updateControlsGeometry() { _topBar->resizeToWidth(width()); _topBar->moveToLeft(0, 0); + _voiceRecordBar->resizeToWidth(width()); moveFieldControls(); @@ -5571,7 +5471,7 @@ void HistoryWidget::editMessage(not_null item) { } } - if (_recording) { + if (isRecording()) { // Just fix some strange inconsistency. _send->clearState(); } @@ -6070,7 +5970,7 @@ void HistoryWidget::updateTopBarSelection() { if (!Ui::isLayerShown() && !Core::App().passcodeLocked()) { if (_nonEmptySelection || (_list && _list->wasSelectedText()) - || _recording + || isRecording() || isBotStart() || isBlocked() || !_canSendMessages) { @@ -6097,7 +5997,7 @@ void HistoryWidget::updateReplyEditText(not_null item) { st::messageTextStyle, item->inReplyText(), Ui::DialogTextOptions()); - if (!_field->isHidden() || _recording) { + if (!_field->isHidden() || isRecording()) { _fieldBarCancel->show(); updateMouseTracking(); } @@ -6400,29 +6300,6 @@ void HistoryWidget::paintEditHeader(Painter &p, const QRect &rect, int left, int } } -void HistoryWidget::drawRecording(Painter &p, float64 recordActive) { - p.setPen(Qt::NoPen); - p.setBrush(st::historyRecordSignalColor); - - auto delta = qMin(_recordingLevel.current() / 0x4000, 1.); - auto d = 2 * qRound(st::historyRecordSignalMin + (delta * (st::historyRecordSignalMax - st::historyRecordSignalMin))); - { - PainterHighQualityEnabler hq(p); - p.drawEllipse(_attachToggle->x() + (_tabbedSelectorToggle->width() - d) / 2, _attachToggle->y() + (_attachToggle->height() - d) / 2, d, d); - } - - auto duration = Ui::FormatDurationText(_recordingSamples / Media::Player::kDefaultFrequency); - p.setFont(st::historyRecordFont); - - p.setPen(st::historyRecordDurationFg); - p.drawText(_attachToggle->x() + _tabbedSelectorToggle->width(), _attachToggle->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, duration); - - int32 left = _attachToggle->x() + _tabbedSelectorToggle->width() + st::historyRecordFont->width(duration) + ((_send->width() - st::historyRecordVoice.width()) / 2); - int32 right = width() - _send->width(); - - p.setPen(anim::pen(st::historyRecordCancel, st::historyRecordCancelActive, 1. - recordActive)); - p.drawText(left + (right - left - _recordCancelWidth) / 2, _attachToggle->y() + st::historyRecordTextTop + st::historyRecordFont->ascent, tr::lng_record_cancel(tr::now)); -} // //void HistoryWidget::drawPinnedBar(Painter &p) { // //if (_pinnedBar->msg) { @@ -6478,11 +6355,8 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { Painter p(this); const auto clip = e->rect(); if (_list) { - if (!_field->isHidden() || _recording) { + if (!_field->isHidden() || isRecording()) { drawField(p, clip); - if (!_send->isHidden() && _recording) { - drawRecording(p, _send->recordActiveRatio()); - } } else if (const auto error = writeRestriction()) { drawRestrictedWrite(p, *error); } diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index b2b1220b4..ecd664f6d 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -94,6 +94,9 @@ class TopBarWidget; class ContactStatus; class Element; class PinnedTracker; +namespace Controls { +class VoiceRecordBar; +} // namespace Controls } // namespace HistoryView class DragArea; @@ -108,6 +111,7 @@ class HistoryWidget final : public Window::AbstractSectionWidget { public: using FieldHistoryAction = Ui::InputField::HistoryAction; + using VoiceRecordBar = HistoryView::Controls::VoiceRecordBar; HistoryWidget( QWidget *parent, @@ -201,9 +205,6 @@ public: void updatePreview(); void previewCancel(); - bool recordingAnimationCallback(crl::time now); - void stopRecording(bool send); - void escape(); void sendBotCommand( @@ -345,6 +346,7 @@ private: friend inline constexpr bool is_flag_type(TextUpdateEvent) { return true; }; void initTabbedSelector(); + void initVoiceRecordBar(); void refreshTabbedPanel(); void createTabbedPanel(); void setTabbedPanel(std::unique_ptr panel); @@ -395,11 +397,6 @@ private: void animationCallback(); void updateOverStates(QPoint pos); - void recordDone(QByteArray result, VoiceWaveform waveform, int samples); - void recordUpdate(ushort level, int samples); - void recordStartCallback(); - void recordStopCallback(bool active); - void recordUpdateCallback(QPoint globalPos); void chooseAttach(); void historyDownAnimationFinish(); void unreadMentionsAnimationFinish(); @@ -495,7 +492,6 @@ private: const QRect &rect, int left, int top) const; - void drawRecording(Painter &p, float64 recordActive); void drawRestrictedWrite(Painter &p, const QString &error); bool paintShowAnimationFrame(); @@ -566,6 +562,8 @@ private: void inlineBotResolveDone(const MTPcontacts_ResolvedPeer &result); void inlineBotResolveFail(const RPCError &error, const QString &username); + bool isRecording() const; + bool isBotStart() const; bool isBlocked() const; bool isJoinChannel() const; @@ -673,7 +671,7 @@ private: std::unique_ptr _contactStatus; - object_ptr _send; + const std::shared_ptr _send; object_ptr _unblock; object_ptr _botStart; object_ptr _joinChannel; @@ -685,20 +683,11 @@ private: object_ptr _botCommandStart; object_ptr _silent = { nullptr }; object_ptr _scheduled = { nullptr }; + const std::unique_ptr _voiceRecordBar; bool _cmdStartShown = false; object_ptr _field; - bool _recording = false; - bool _inField = false; bool _inReplyEditForward = false; bool _inClickable = false; - int _recordingSamples = 0; - int _recordCancelWidth; - rpl::lifetime _recordingLifetime; - - // This can animate for a very long time (like in music playing), - // so it should be a Basic, not a Simple animation. - Ui::Animations::Basic _recordingAnimation; - anim::value _recordingLevel; bool kbWasHidden() const; 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 9685f5d01..6f52b02d4 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 @@ -26,8 +26,9 @@ using SendActionUpdate = VoiceRecordBar::SendActionUpdate; using VoiceToSend = VoiceRecordBar::VoiceToSend; constexpr auto kRecordingUpdateDelta = crl::time(100); +constexpr auto kAudioVoiceMaxLength = 100 * 60; // 100 minutes constexpr auto kMaxSamples = - ::Media::Player::kDefaultFrequency * AudioVoiceMsgMaxLength; + ::Media::Player::kDefaultFrequency * kAudioVoiceMaxLength; [[nodiscard]] auto Duration(int samples) { return samples / ::Media::Player::kDefaultFrequency;