mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 22:54:01 +02:00
Added initial ability to delete recorded voice data.
This commit is contained in:
parent
131c2e1c56
commit
647cbc5464
3 changed files with 175 additions and 14 deletions
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "base/event_filter.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "core/application.h"
|
||||
#include "data/data_document.h"
|
||||
#include "history/view/controls/history_view_voice_record_button.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "mainwindow.h"
|
||||
|
@ -60,14 +61,93 @@ enum class FilterType {
|
|||
.arg(decimalPart);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<VoiceData> ProcessCaptureResult(
|
||||
const ::Media::Capture::Result &data) {
|
||||
auto voiceData = std::make_unique<VoiceData>();
|
||||
voiceData->duration = Duration(data.samples);
|
||||
voiceData->waveform = data.waveform;
|
||||
voiceData->wavemax = voiceData->waveform.empty()
|
||||
? uchar(0)
|
||||
: *ranges::max_element(voiceData->waveform);
|
||||
return voiceData;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class ListenWrap final {
|
||||
public:
|
||||
ListenWrap(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const ::Media::Capture::Result &data);
|
||||
|
||||
void requestPaintProgress(float64 progress);
|
||||
rpl::producer<> stopRequests() const;
|
||||
|
||||
rpl::lifetime &lifetime();
|
||||
|
||||
private:
|
||||
void init();
|
||||
|
||||
not_null<Ui::RpWidget*> _parent;
|
||||
|
||||
const std::unique_ptr<VoiceData> _voiceData;
|
||||
const style::IconButton &_stDelete;
|
||||
base::unique_qptr<Ui::IconButton> _delete;
|
||||
|
||||
rpl::variable<float64> _showProgress = 0.;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
ListenWrap::ListenWrap(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
const ::Media::Capture::Result &data)
|
||||
: _parent(parent)
|
||||
, _voiceData(ProcessCaptureResult(data))
|
||||
, _stDelete(st::historyRecordDelete)
|
||||
, _delete(base::make_unique_q<Ui::IconButton>(parent, _stDelete)) {
|
||||
init();
|
||||
}
|
||||
|
||||
void ListenWrap::init() {
|
||||
auto deleteShow = _showProgress.value(
|
||||
) | rpl::map([](auto value) {
|
||||
return value == 1.;
|
||||
}) | rpl::distinct_until_changed();
|
||||
_delete->showOn(std::move(deleteShow));
|
||||
|
||||
_parent->paintRequest(
|
||||
) | rpl::start_with_next([=](const QRect &clip) {
|
||||
const auto progress = _showProgress.current();
|
||||
if (progress == 0. || progress == 1.) {
|
||||
return;
|
||||
}
|
||||
Painter p(_parent);
|
||||
p.setOpacity(progress);
|
||||
_stDelete.icon.paint(p, _stDelete.iconPosition, _parent->width());
|
||||
}, _lifetime);
|
||||
}
|
||||
|
||||
void ListenWrap::requestPaintProgress(float64 progress) {
|
||||
_showProgress = progress;
|
||||
}
|
||||
|
||||
rpl::producer<> ListenWrap::stopRequests() const {
|
||||
return _delete->clicks() | rpl::to_empty;
|
||||
}
|
||||
|
||||
rpl::lifetime &ListenWrap::lifetime() {
|
||||
return _lifetime;
|
||||
}
|
||||
|
||||
class RecordLock final : public Ui::RpWidget {
|
||||
public:
|
||||
RecordLock(not_null<Ui::RpWidget*> parent);
|
||||
|
||||
void requestPaintProgress(float64 progress);
|
||||
|
||||
[[nodiscard]] rpl::producer<> stops() const;
|
||||
[[nodiscard]] rpl::producer<> locks() const;
|
||||
[[nodiscard]] bool isLocked() const;
|
||||
|
||||
|
@ -102,10 +182,11 @@ RecordLock::RecordLock(not_null<Ui::RpWidget*> parent)
|
|||
}
|
||||
|
||||
void RecordLock::init() {
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
shownValue(
|
||||
) | rpl::start_with_next([=](bool shown) {
|
||||
if (!shown) {
|
||||
setCursor(style::cur_default);
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents, true);
|
||||
_lockAnimation.stop();
|
||||
_lockEnderAnimation.stop();
|
||||
_progress = 0.;
|
||||
|
@ -129,8 +210,17 @@ void RecordLock::init() {
|
|||
|
||||
locks(
|
||||
) | rpl::start_with_next([=] {
|
||||
const auto duration = st::historyRecordVoiceShowDuration;
|
||||
_lockAnimation.start([=] { update(); }, 0., 1., duration);
|
||||
const auto &duration = st::historyRecordVoiceShowDuration;
|
||||
const auto from = 0.;
|
||||
const auto to = 1.;
|
||||
auto callback = [=](auto value) {
|
||||
update();
|
||||
if (value == to) {
|
||||
setCursor(style::cur_pointer);
|
||||
setAttribute(Qt::WA_TransparentForMouseEvents, false);
|
||||
}
|
||||
};
|
||||
_lockAnimation.start(std::move(callback), from, to, duration);
|
||||
}, lifetime());
|
||||
}
|
||||
|
||||
|
@ -282,6 +372,15 @@ rpl::producer<> RecordLock::locks() const {
|
|||
) | rpl::filter([=] { return isLocked(); }) | rpl::to_empty;
|
||||
}
|
||||
|
||||
rpl::producer<> RecordLock::stops() const {
|
||||
return events(
|
||||
) | rpl::filter([=](not_null<QEvent*> e) {
|
||||
return isLocked()
|
||||
&& (_lockAnimation.value(1.) == 1.)
|
||||
&& (e->type() == QEvent::MouseButtonRelease);
|
||||
}) | rpl::to_empty;
|
||||
}
|
||||
|
||||
VoiceRecordBar::VoiceRecordBar(
|
||||
not_null<Ui::RpWidget*> parent,
|
||||
not_null<Ui::RpWidget*> sectionWidget,
|
||||
|
@ -313,7 +412,7 @@ VoiceRecordBar::VoiceRecordBar(
|
|||
|
||||
VoiceRecordBar::~VoiceRecordBar() {
|
||||
if (isRecording()) {
|
||||
stopRecording(false);
|
||||
stopRecording(StopType::Cancel);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,6 +494,7 @@ void VoiceRecordBar::init() {
|
|||
}
|
||||
p.fillRect(clip, st::historyComposeAreaBg);
|
||||
|
||||
p.setOpacity(std::min(p.opacity(), 1. - showListenAnimationRatio()));
|
||||
if (clip.intersects(_messageRect)) {
|
||||
// The message should be painted first to avoid flickering.
|
||||
drawMessage(p, activeAnimationRatio());
|
||||
|
@ -430,6 +530,29 @@ void VoiceRecordBar::init() {
|
|||
_showLockAnimation.start(std::move(callback), from, to, duration);
|
||||
}, lifetime());
|
||||
|
||||
_lock->stops(
|
||||
) | rpl::start_with_next([=] {
|
||||
::Media::Capture::instance()->startedChanges(
|
||||
) | rpl::filter([](auto capturing) {
|
||||
return !capturing;
|
||||
}) | rpl::take(1) | rpl::start_with_next([=] {
|
||||
Assert(_listen != nullptr);
|
||||
|
||||
_lockShowing = false;
|
||||
|
||||
const auto to = 1.;
|
||||
const auto &duration = st::historyRecordVoiceShowDuration;
|
||||
auto callback = [=](auto value) {
|
||||
_listen->requestPaintProgress(value);
|
||||
_level->requestPaintProgress(to - value);
|
||||
update();
|
||||
};
|
||||
_showListenAnimation.start(std::move(callback), 0., to, duration);
|
||||
}, lifetime());
|
||||
|
||||
stopRecording(StopType::Listen);
|
||||
}, lifetime());
|
||||
|
||||
_lock->locks(
|
||||
) | rpl::start_with_next([=] {
|
||||
installClickOutsideFilter();
|
||||
|
@ -504,7 +627,11 @@ void VoiceRecordBar::visibilityAnimate(bool show, Fn<void()> &&callback) {
|
|||
const auto from = show ? 0. : 1.;
|
||||
const auto duration = st::historyRecordVoiceShowDuration;
|
||||
auto animationCallback = [=, callback = std::move(callback)](auto value) {
|
||||
_level->requestPaintProgress(value);
|
||||
if (!_listen) {
|
||||
_level->requestPaintProgress(value);
|
||||
} else {
|
||||
_listen->requestPaintProgress(value);
|
||||
}
|
||||
update();
|
||||
if ((show && value == 1.) || (!show && value == 0.)) {
|
||||
if (callback) {
|
||||
|
@ -621,7 +748,7 @@ void VoiceRecordBar::stop(bool send) {
|
|||
auto disappearanceCallback = [=] {
|
||||
hide();
|
||||
|
||||
stopRecording(send);
|
||||
stopRecording(send ? StopType::Send : StopType::Cancel);
|
||||
};
|
||||
_lockShowing = false;
|
||||
visibilityAnimate(false, std::move(disappearanceCallback));
|
||||
|
@ -637,6 +764,8 @@ void VoiceRecordBar::finish() {
|
|||
|
||||
_showAnimation.stop();
|
||||
|
||||
_listen = nullptr;
|
||||
|
||||
_sendActionUpdates.fire({ Api::SendProgressType::RecordVoice, -1 });
|
||||
_controller->widget()->setInnerFocus();
|
||||
}
|
||||
|
@ -645,12 +774,12 @@ void VoiceRecordBar::hideFast() {
|
|||
hide();
|
||||
_lock->hide();
|
||||
_level->hide();
|
||||
stopRecording(false);
|
||||
stopRecording(StopType::Cancel);
|
||||
}
|
||||
|
||||
void VoiceRecordBar::stopRecording(bool send) {
|
||||
void VoiceRecordBar::stopRecording(StopType type) {
|
||||
using namespace ::Media::Capture;
|
||||
if (!send) {
|
||||
if (type == StopType::Cancel) {
|
||||
instance()->stop();
|
||||
return;
|
||||
}
|
||||
|
@ -661,7 +790,17 @@ void VoiceRecordBar::stopRecording(bool send) {
|
|||
|
||||
Window::ActivateWindow(_controller);
|
||||
const auto duration = Duration(data.samples);
|
||||
_sendVoiceRequests.fire({ data.bytes, data.waveform, duration });
|
||||
if (type == StopType::Send) {
|
||||
_sendVoiceRequests.fire({ data.bytes, data.waveform, duration });
|
||||
} else if (type == StopType::Listen) {
|
||||
_listen = std::make_unique<ListenWrap>(this, data);
|
||||
_listen->stopRequests(
|
||||
) | rpl::take(1) | rpl::start_with_next([=] {
|
||||
visibilityAnimate(false, [=] { hide(); });
|
||||
}, _listen->lifetime());
|
||||
|
||||
_lockShowing = false;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -693,11 +832,12 @@ void VoiceRecordBar::drawRedCircle(Painter &p) {
|
|||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(st::historyRecordVoiceFgInactive);
|
||||
|
||||
p.setOpacity(1. - _redCircleProgress);
|
||||
const auto opacity = p.opacity();
|
||||
p.setOpacity(opacity * (1. - _redCircleProgress));
|
||||
const int radii = st::historyRecordSignalRadius * showAnimationRatio();
|
||||
const auto center = _redCircleRect.center() + QPoint(1, 1);
|
||||
p.drawEllipse(center, radii, radii);
|
||||
p.setOpacity(1.);
|
||||
p.setOpacity(opacity);
|
||||
}
|
||||
|
||||
void VoiceRecordBar::drawMessage(Painter &p, float64 recordActive) {
|
||||
|
@ -761,6 +901,10 @@ float64 VoiceRecordBar::showAnimationRatio() const {
|
|||
return _showAnimation.value(1.);
|
||||
}
|
||||
|
||||
float64 VoiceRecordBar::showListenAnimationRatio() const {
|
||||
return _showListenAnimation.value(_listen ? 1. : 0.);
|
||||
}
|
||||
|
||||
void VoiceRecordBar::computeAndSetLockProgress(QPoint globalPos) {
|
||||
const auto localPos = mapFromGlobal(globalPos);
|
||||
const auto lower = _lock->height();
|
||||
|
@ -817,7 +961,7 @@ void VoiceRecordBar::installClickOutsideFilter() {
|
|||
} else if (type == QEvent::ContextMenu || type == QEvent::Shortcut) {
|
||||
return Type::ShowBox;
|
||||
} else if (type == QEvent::MouseButtonPress) {
|
||||
return (noBox && !_inField.current())
|
||||
return (noBox && !_inField.current() && !_lock->underMouse())
|
||||
? Type::ShowBox
|
||||
: Type::Continue;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/effects/animations.h"
|
||||
#include "ui/rp_widget.h"
|
||||
|
||||
struct VoiceData;
|
||||
|
||||
namespace Ui {
|
||||
class SendButton;
|
||||
} // namespace Ui
|
||||
|
@ -24,6 +26,7 @@ class SessionController;
|
|||
namespace HistoryView::Controls {
|
||||
|
||||
class VoiceRecordButton;
|
||||
class ListenWrap;
|
||||
class RecordLock;
|
||||
|
||||
class VoiceRecordBar final : public Ui::RpWidget {
|
||||
|
@ -64,6 +67,12 @@ public:
|
|||
[[nodiscard]] bool isLockPresent() const;
|
||||
|
||||
private:
|
||||
enum class StopType {
|
||||
Cancel,
|
||||
Send,
|
||||
Listen,
|
||||
};
|
||||
|
||||
void init();
|
||||
|
||||
void updateMessageGeometry();
|
||||
|
@ -74,7 +83,7 @@ private:
|
|||
bool recordingAnimationCallback(crl::time now);
|
||||
|
||||
void stop(bool send);
|
||||
void stopRecording(bool send);
|
||||
void stopRecording(StopType type);
|
||||
void visibilityAnimate(bool show, Fn<void()> &&callback);
|
||||
|
||||
bool showRecordButton() const;
|
||||
|
@ -92,6 +101,7 @@ private:
|
|||
|
||||
void activeAnimate(bool active);
|
||||
float64 showAnimationRatio() const;
|
||||
float64 showListenAnimationRatio() const;
|
||||
float64 activeAnimationRatio() const;
|
||||
|
||||
void computeAndSetLockProgress(QPoint globalPos);
|
||||
|
@ -101,6 +111,7 @@ private:
|
|||
const std::shared_ptr<Ui::SendButton> _send;
|
||||
const std::unique_ptr<RecordLock> _lock;
|
||||
const std::unique_ptr<VoiceRecordButton> _level;
|
||||
std::unique_ptr<ListenWrap> _listen;
|
||||
|
||||
base::Timer _startTimer;
|
||||
|
||||
|
@ -129,6 +140,7 @@ private:
|
|||
rpl::variable<bool> _lockShowing = false;
|
||||
|
||||
Ui::Animations::Simple _showLockAnimation;
|
||||
Ui::Animations::Simple _showListenAnimation;
|
||||
Ui::Animations::Simple _activeAnimation;
|
||||
Ui::Animations::Simple _showAnimation;
|
||||
|
||||
|
|
|
@ -381,6 +381,11 @@ historyRecordLockBody: icon {{ "voice_lock/record_lock_body", historyToDownBg }}
|
|||
historyRecordLockMargin: margins(4px, 4px, 4px, 4px);
|
||||
historyRecordLockArrow: icon {{ "voice_lock/voice_arrow", historyToDownFg }};
|
||||
|
||||
historyRecordDelete: IconButton(historyAttach) {
|
||||
icon: icon {{ "info_media_delete", historyComposeIconFg }};
|
||||
iconOver: icon {{ "info_media_delete", historyComposeIconFgOver }};
|
||||
}
|
||||
|
||||
historyRecordLockPosition: point(7px, 35px);
|
||||
|
||||
historySilentToggle: IconButton(historyBotKeyboardShow) {
|
||||
|
|
Loading…
Add table
Reference in a new issue