Replaced voice record processing with VoiceRecordBar in HistoryWidget.

This commit is contained in:
23rd 2020-10-04 02:25:25 +03:00 committed by John Preston
parent fd76b44dbd
commit db564ca486
4 changed files with 103 additions and 242 deletions

View file

@ -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,

View file

@ -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<Support::Autocomplete>(this, &session())
: nullptr)
, _send(this)
, _send(std::make_shared<Ui::SendButton>(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<HistoryWidget::VoiceRecordBar>(
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<InformBox>(*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<InformBox>(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<InformBox>(*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<HistoryItem*> 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<HistoryItem*> 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);
}

View file

@ -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<TabbedPanel> 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<HistoryView::ContactStatus> _contactStatus;
object_ptr<Ui::SendButton> _send;
const std::shared_ptr<Ui::SendButton> _send;
object_ptr<Ui::FlatButton> _unblock;
object_ptr<Ui::FlatButton> _botStart;
object_ptr<Ui::FlatButton> _joinChannel;
@ -685,20 +683,11 @@ private:
object_ptr<Ui::IconButton> _botCommandStart;
object_ptr<Ui::SilentToggle> _silent = { nullptr };
object_ptr<Ui::IconButton> _scheduled = { nullptr };
const std::unique_ptr<VoiceRecordBar> _voiceRecordBar;
bool _cmdStartShown = false;
object_ptr<Ui::InputField> _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;

View file

@ -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;