2016-12-01 19:20:33 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
2018-01-03 10:23:14 +00:00
|
|
|
the official desktop application for the Telegram messaging service.
|
2016-12-01 19:20:33 +00:00
|
|
|
|
2018-01-03 10:23:14 +00:00
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
2016-12-01 19:20:33 +00:00
|
|
|
*/
|
|
|
|
#include "ui/effects/send_action_animations.h"
|
|
|
|
|
2020-07-06 09:58:18 +00:00
|
|
|
#include "api/api_send_progress.h"
|
2019-09-13 06:06:02 +00:00
|
|
|
#include "ui/effects/animation_value.h"
|
2016-12-01 19:20:33 +00:00
|
|
|
#include "styles/style_widgets.h"
|
2020-12-02 10:52:19 +00:00
|
|
|
#include "styles/style_dialogs.h"
|
2016-12-01 19:20:33 +00:00
|
|
|
|
|
|
|
namespace Ui {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr int kTypingDotsCount = 3;
|
|
|
|
constexpr int kRecordArcsCount = 4;
|
|
|
|
constexpr int kUploadArrowsCount = 3;
|
2020-12-02 10:52:19 +00:00
|
|
|
constexpr auto kSpeakingDuration = 3200;
|
|
|
|
constexpr auto kSpeakingFadeDuration = 400;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
class SendActionAnimation::Impl {
|
|
|
|
public:
|
|
|
|
using Type = Api::SendProgressType;
|
|
|
|
|
|
|
|
Impl(int period) : _period(period), _started(crl::now()) {
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MetaData {
|
|
|
|
int index;
|
|
|
|
std::unique_ptr<Impl>(*creator)();
|
|
|
|
};
|
|
|
|
virtual const MetaData *metaData() const = 0;
|
|
|
|
bool supports(Type type) const;
|
|
|
|
|
|
|
|
virtual int width() const = 0;
|
2021-09-11 14:15:40 +00:00
|
|
|
virtual int widthNoMargins() const {
|
|
|
|
return width();
|
|
|
|
}
|
2020-12-02 10:52:19 +00:00
|
|
|
virtual void paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) = 0;
|
|
|
|
|
|
|
|
virtual void restartedAt(crl::time now) {
|
|
|
|
}
|
|
|
|
virtual bool finishNow() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~Impl() = default;
|
|
|
|
|
|
|
|
protected:
|
2021-08-30 13:11:50 +00:00
|
|
|
[[nodiscard]] int period() const {
|
|
|
|
return _period;
|
|
|
|
}
|
2020-12-02 10:52:19 +00:00
|
|
|
[[nodiscard]] crl::time started() const {
|
|
|
|
return _started;
|
|
|
|
}
|
|
|
|
[[nodiscard]] int frameTime(crl::time now) const {
|
2021-08-30 13:11:43 +00:00
|
|
|
return anim::Disabled()
|
|
|
|
? 0
|
|
|
|
: (std::max(now - _started, crl::time(0)) % _period);
|
2020-12-02 10:52:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
int _period = 1;
|
|
|
|
crl::time _started = 0;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
namespace {
|
2016-12-01 19:20:33 +00:00
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
using ImplementationsMap = QMap<
|
|
|
|
Api::SendProgressType,
|
|
|
|
const SendActionAnimation::Impl::MetaData*>;
|
2016-12-01 19:20:33 +00:00
|
|
|
NeverFreedPointer<ImplementationsMap> Implementations;
|
|
|
|
|
|
|
|
class TypingAnimation : public SendActionAnimation::Impl {
|
|
|
|
public:
|
|
|
|
TypingAnimation() : Impl(st::historySendActionTypingDuration) {
|
|
|
|
}
|
|
|
|
|
|
|
|
static const MetaData kMeta;
|
2017-02-21 13:45:56 +00:00
|
|
|
static std::unique_ptr<Impl> create() {
|
|
|
|
return std::make_unique<TypingAnimation>();
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
const MetaData *metaData() const override {
|
|
|
|
return &kMeta;
|
|
|
|
}
|
|
|
|
|
|
|
|
int width() const override {
|
2021-08-30 13:11:43 +00:00
|
|
|
return st::historySendActionTypingPosition.x()
|
|
|
|
+ kTypingDotsCount * st::historySendActionTypingDelta;
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
void paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) override;
|
2016-12-01 19:20:33 +00:00
|
|
|
|
|
|
|
};
|
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
const TypingAnimation::MetaData TypingAnimation::kMeta = {
|
|
|
|
0,
|
|
|
|
&TypingAnimation::create,
|
|
|
|
};
|
2016-12-01 19:20:33 +00:00
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
void TypingAnimation::paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) {
|
2016-12-03 12:10:35 +00:00
|
|
|
PainterHighQualityEnabler hq(p);
|
2016-12-01 19:20:33 +00:00
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
p.setBrush(color);
|
2020-12-02 10:52:19 +00:00
|
|
|
auto frameMs = frameTime(now);
|
2021-08-30 13:11:43 +00:00
|
|
|
auto position = QPointF(x + 0.5, y - 0.5)
|
|
|
|
+ st::historySendActionTypingPosition;
|
2016-12-01 19:20:33 +00:00
|
|
|
for (auto i = 0; i != kTypingDotsCount; ++i) {
|
2021-08-30 13:11:43 +00:00
|
|
|
auto r = st::historySendActionTypingSmallNumerator
|
|
|
|
/ st::historySendActionTypingDenominator;
|
2016-12-01 19:20:33 +00:00
|
|
|
if (frameMs < 2 * st::historySendActionTypingHalfPeriod) {
|
2021-08-30 13:11:43 +00:00
|
|
|
const auto delta = (st::historySendActionTypingLargeNumerator
|
|
|
|
- st::historySendActionTypingSmallNumerator)
|
|
|
|
/ st::historySendActionTypingDenominator;
|
2016-12-01 19:20:33 +00:00
|
|
|
if (frameMs < st::historySendActionTypingHalfPeriod) {
|
2021-08-30 13:11:43 +00:00
|
|
|
r += delta
|
|
|
|
* anim::easeOutCirc(
|
|
|
|
1.,
|
|
|
|
float64(frameMs)
|
|
|
|
/ st::historySendActionTypingHalfPeriod);
|
2016-12-01 19:20:33 +00:00
|
|
|
} else {
|
2021-08-30 13:11:43 +00:00
|
|
|
r += delta
|
|
|
|
* (1. - anim::easeOutCirc(
|
|
|
|
1.,
|
|
|
|
float64(frameMs
|
|
|
|
- st::historySendActionTypingHalfPeriod)
|
|
|
|
/ st::historySendActionTypingHalfPeriod));
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
p.drawEllipse(position, r, r);
|
|
|
|
position.setX(position.x() + st::historySendActionTypingDelta);
|
2021-08-30 13:11:43 +00:00
|
|
|
frameMs = (frameMs + period() - st::historySendActionTypingDeltaTime)
|
|
|
|
% period();
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class RecordAnimation : public SendActionAnimation::Impl {
|
|
|
|
public:
|
|
|
|
RecordAnimation() : Impl(st::historySendActionRecordDuration) {
|
|
|
|
}
|
|
|
|
|
|
|
|
static const MetaData kMeta;
|
2017-02-21 13:45:56 +00:00
|
|
|
static std::unique_ptr<Impl> create() {
|
|
|
|
return std::make_unique<RecordAnimation>();
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
const MetaData *metaData() const override {
|
|
|
|
return &kMeta;
|
|
|
|
}
|
|
|
|
|
|
|
|
int width() const override {
|
2021-08-30 13:11:43 +00:00
|
|
|
return st::historySendActionRecordPosition.x()
|
|
|
|
+ (kRecordArcsCount + 1) * st::historySendActionRecordDelta;
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
void paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) override;
|
2016-12-01 19:20:33 +00:00
|
|
|
|
|
|
|
};
|
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
const RecordAnimation::MetaData RecordAnimation::kMeta = {
|
|
|
|
0,
|
|
|
|
&RecordAnimation::create,
|
|
|
|
};
|
2016-12-01 19:20:33 +00:00
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
void RecordAnimation::paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) {
|
2016-12-03 12:10:35 +00:00
|
|
|
PainterHighQualityEnabler hq(p);
|
2020-12-02 10:52:19 +00:00
|
|
|
const auto frameMs = frameTime(now);
|
2016-12-01 19:20:33 +00:00
|
|
|
auto pen = color->p;
|
2021-08-30 13:11:43 +00:00
|
|
|
pen.setWidth(st::historySendActionRecordStrokeNumerator
|
|
|
|
/ st::historySendActionRecordDenominator);
|
2016-12-01 19:20:33 +00:00
|
|
|
pen.setJoinStyle(Qt::RoundJoin);
|
|
|
|
pen.setCapStyle(Qt::RoundCap);
|
|
|
|
p.setPen(pen);
|
|
|
|
p.setBrush(Qt::NoBrush);
|
2021-08-30 13:11:43 +00:00
|
|
|
auto progress = frameMs / float64(period());
|
|
|
|
auto size = st::historySendActionRecordPosition.x()
|
|
|
|
+ st::historySendActionRecordDelta * progress;
|
2016-12-01 19:20:33 +00:00
|
|
|
y += st::historySendActionRecordPosition.y();
|
|
|
|
for (auto i = 0; i != kRecordArcsCount; ++i) {
|
2021-08-30 13:11:43 +00:00
|
|
|
p.setOpacity((i == 0)
|
|
|
|
? progress
|
|
|
|
: (i == kRecordArcsCount - 1)
|
|
|
|
? (1. - progress)
|
|
|
|
: 1.);
|
2016-12-01 19:20:33 +00:00
|
|
|
auto rect = QRectF(x - size, y - size, 2 * size, 2 * size);
|
|
|
|
p.drawArc(rect, -FullArcLength / 24, FullArcLength / 12);
|
|
|
|
size += st::historySendActionRecordDelta;
|
|
|
|
}
|
|
|
|
p.setOpacity(1.);
|
|
|
|
}
|
|
|
|
|
|
|
|
class UploadAnimation : public SendActionAnimation::Impl {
|
|
|
|
public:
|
|
|
|
UploadAnimation() : Impl(st::historySendActionUploadDuration) {
|
|
|
|
}
|
|
|
|
|
|
|
|
static const MetaData kMeta;
|
2017-02-21 13:45:56 +00:00
|
|
|
static std::unique_ptr<Impl> create() {
|
|
|
|
return std::make_unique<UploadAnimation>();
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
const MetaData *metaData() const override {
|
|
|
|
return &kMeta;
|
|
|
|
}
|
|
|
|
|
|
|
|
int width() const override {
|
2021-08-30 13:11:43 +00:00
|
|
|
return st::historySendActionUploadPosition.x()
|
|
|
|
+ (kUploadArrowsCount + 1) * st::historySendActionUploadDelta;
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
void paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) override;
|
2016-12-01 19:20:33 +00:00
|
|
|
|
|
|
|
};
|
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
const UploadAnimation::MetaData UploadAnimation::kMeta = {
|
|
|
|
0,
|
|
|
|
&UploadAnimation::create,
|
|
|
|
};
|
2016-12-01 19:20:33 +00:00
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
void UploadAnimation::paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) {
|
2016-12-03 12:10:35 +00:00
|
|
|
PainterHighQualityEnabler hq(p);
|
2020-12-02 10:52:19 +00:00
|
|
|
const auto frameMs = frameTime(now);
|
2016-12-01 19:20:33 +00:00
|
|
|
auto pen = color->p;
|
2021-08-30 13:11:43 +00:00
|
|
|
pen.setWidth(st::historySendActionUploadStrokeNumerator
|
|
|
|
/ st::historySendActionUploadDenominator);
|
2016-12-01 19:20:33 +00:00
|
|
|
pen.setJoinStyle(Qt::RoundJoin);
|
|
|
|
pen.setCapStyle(Qt::RoundCap);
|
|
|
|
p.setPen(pen);
|
|
|
|
p.setBrush(Qt::NoBrush);
|
2021-08-30 13:11:43 +00:00
|
|
|
auto progress = frameMs / float64(period());
|
|
|
|
auto position = st::historySendActionUploadPosition
|
|
|
|
+ QPointF(x + st::historySendActionUploadDelta * progress, y);
|
2016-12-01 19:20:33 +00:00
|
|
|
auto path = QPainterPath();
|
2021-08-30 13:11:43 +00:00
|
|
|
path.moveTo(
|
|
|
|
0.,
|
|
|
|
-st::historySendActionUploadSizeNumerator
|
|
|
|
/ st::historySendActionUploadDenominator);
|
|
|
|
path.lineTo(
|
|
|
|
st::historySendActionUploadSizeNumerator
|
|
|
|
/ st::historySendActionUploadDenominator,
|
|
|
|
0.);
|
|
|
|
path.lineTo(
|
|
|
|
0.,
|
|
|
|
st::historySendActionUploadSizeNumerator
|
|
|
|
/ st::historySendActionUploadDenominator);
|
2016-12-01 19:20:33 +00:00
|
|
|
p.translate(position);
|
|
|
|
for (auto i = 0; i != kUploadArrowsCount; ++i) {
|
2021-08-30 13:11:43 +00:00
|
|
|
p.setOpacity((i == 0)
|
|
|
|
? progress
|
|
|
|
: (i == kUploadArrowsCount - 1)
|
|
|
|
? (1. - progress)
|
|
|
|
: 1.);
|
2016-12-01 19:20:33 +00:00
|
|
|
p.drawPath(path);
|
|
|
|
position.setX(position.x() + st::historySendActionUploadDelta);
|
|
|
|
p.translate(st::historySendActionUploadDelta, 0);
|
|
|
|
}
|
|
|
|
p.setOpacity(1.);
|
|
|
|
p.translate(-position);
|
|
|
|
}
|
|
|
|
|
2020-12-02 10:52:19 +00:00
|
|
|
class SpeakingAnimation : public SendActionAnimation::Impl {
|
|
|
|
public:
|
|
|
|
SpeakingAnimation();
|
|
|
|
|
|
|
|
static const MetaData kMeta;
|
|
|
|
static std::unique_ptr<Impl> create() {
|
|
|
|
return std::make_unique<SpeakingAnimation>();
|
|
|
|
}
|
|
|
|
const MetaData *metaData() const override {
|
|
|
|
return &kMeta;
|
|
|
|
}
|
|
|
|
|
|
|
|
int width() const override {
|
2020-12-02 21:16:26 +00:00
|
|
|
const auto &numerator = st::dialogsSpeakingStrokeNumerator;
|
|
|
|
const auto &denominator = st::dialogsSpeakingDenominator;
|
|
|
|
return 4 * (numerator / denominator);
|
2020-12-02 10:52:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void restartedAt(crl::time now) override;
|
|
|
|
bool finishNow() override;
|
|
|
|
|
2020-12-02 21:16:26 +00:00
|
|
|
static void PaintIdle(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth);
|
2020-12-02 10:52:19 +00:00
|
|
|
|
2020-12-02 21:16:26 +00:00
|
|
|
void paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) override;
|
2020-12-02 10:52:19 +00:00
|
|
|
|
|
|
|
private:
|
2020-12-02 21:16:26 +00:00
|
|
|
static void PaintFrame(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
int frameMs,
|
|
|
|
float64 started);
|
2020-12-02 10:52:19 +00:00
|
|
|
|
|
|
|
crl::time _startStarted = 0;
|
|
|
|
crl::time _finishStarted = 0;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2020-12-02 21:16:26 +00:00
|
|
|
const SpeakingAnimation::MetaData SpeakingAnimation::kMeta = {
|
|
|
|
0,
|
|
|
|
&SpeakingAnimation::create };
|
2020-12-02 10:52:19 +00:00
|
|
|
|
|
|
|
SpeakingAnimation::SpeakingAnimation()
|
|
|
|
: Impl(kSpeakingDuration)
|
|
|
|
, _startStarted(crl::now()) {
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpeakingAnimation::restartedAt(crl::time now) {
|
|
|
|
if (!_finishStarted) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto finishFinishes = _finishStarted + kSpeakingFadeDuration;
|
|
|
|
const auto leftToFinish = (finishFinishes - now);
|
|
|
|
if (leftToFinish > 0) {
|
|
|
|
_startStarted = now - leftToFinish;
|
|
|
|
} else {
|
|
|
|
_startStarted = now;
|
|
|
|
}
|
|
|
|
_finishStarted = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SpeakingAnimation::finishNow() {
|
|
|
|
const auto now = crl::now();
|
|
|
|
if (_finishStarted) {
|
|
|
|
return (_finishStarted + kSpeakingFadeDuration <= now);
|
|
|
|
} else if (_startStarted >= now) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
const auto startFinishes = _startStarted + kSpeakingFadeDuration;
|
|
|
|
const auto leftToStart = (startFinishes - now);
|
|
|
|
if (leftToStart > 0) {
|
|
|
|
_finishStarted = now - leftToStart;
|
|
|
|
} else {
|
|
|
|
_finishStarted = now;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-12-02 21:16:26 +00:00
|
|
|
void SpeakingAnimation::PaintIdle(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth) {
|
2020-12-02 10:52:19 +00:00
|
|
|
PaintFrame(p, color, x, y, outerWidth, 0, 0.);
|
|
|
|
}
|
|
|
|
|
2020-12-02 21:16:26 +00:00
|
|
|
void SpeakingAnimation::paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) {
|
2020-12-02 10:52:19 +00:00
|
|
|
const auto started = _finishStarted
|
|
|
|
? (1. - ((now - _finishStarted) / float64(kSpeakingFadeDuration)))
|
|
|
|
: (now - _startStarted) / float64(kSpeakingFadeDuration);
|
2020-12-02 21:16:26 +00:00
|
|
|
const auto progress = std::clamp(started, 0., 1.);
|
|
|
|
PaintFrame(p, color, x, y, outerWidth, frameTime(now), progress);
|
2020-12-02 10:52:19 +00:00
|
|
|
}
|
|
|
|
|
2020-12-02 21:16:26 +00:00
|
|
|
void SpeakingAnimation::PaintFrame(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
int frameMs,
|
|
|
|
float64 started) {
|
2020-12-02 10:52:19 +00:00
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
|
2020-12-02 21:16:26 +00:00
|
|
|
const auto line = st::dialogsSpeakingStrokeNumerator
|
|
|
|
/ (2 * st::dialogsSpeakingDenominator);
|
2020-12-02 10:52:19 +00:00
|
|
|
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
p.setBrush(color);
|
|
|
|
|
|
|
|
const auto duration = kSpeakingDuration;
|
|
|
|
const auto stageDuration = duration / 8;
|
|
|
|
const auto fullprogress = frameMs;
|
|
|
|
const auto stage = fullprogress / stageDuration;
|
2020-12-02 21:16:26 +00:00
|
|
|
const auto progress = (fullprogress - stage * stageDuration)
|
|
|
|
/ float64(stageDuration);
|
2020-12-02 10:52:19 +00:00
|
|
|
const auto half = st::dialogsCallBadgeSize / 2.;
|
|
|
|
const auto center = QPointF(x + half, y + half);
|
|
|
|
const auto middleSize = [&] {
|
|
|
|
if (!started) {
|
|
|
|
return 2 * line;
|
|
|
|
}
|
|
|
|
auto result = line;
|
|
|
|
switch (stage) {
|
|
|
|
case 0: result += 4 * line * progress; break;
|
|
|
|
case 1: result += 4 * line * (1. - progress); break;
|
|
|
|
case 2: result += 2 * line * progress; break;
|
|
|
|
case 3: result += 2 * line * (1. - progress); break;
|
|
|
|
case 4: result += 4 * line * progress; break;
|
|
|
|
case 5: result += 4 * line * (1. - progress); break;
|
|
|
|
case 6: result += 4 * line * progress; break;
|
|
|
|
case 7: result += 4 * line * (1. - progress); break;
|
|
|
|
}
|
|
|
|
return (started == 1.)
|
|
|
|
? result
|
|
|
|
: (started * result) + ((1. - started) * 2 * line);
|
|
|
|
}();
|
|
|
|
const auto sideSize = [&] {
|
|
|
|
if (!started) {
|
|
|
|
return 2 * line;
|
|
|
|
}
|
|
|
|
auto result = line;
|
|
|
|
switch (stage) {
|
|
|
|
case 0: result += 2 * line * (1. - progress); break;
|
|
|
|
case 1: result += 4 * line * progress; break;
|
|
|
|
case 2: result += 4 * line * (1. - progress); break;
|
|
|
|
case 3: result += 2 * line * progress; break;
|
|
|
|
case 4: result += 2 * line * (1. - progress); break;
|
|
|
|
case 5: result += 4 * line * progress; break;
|
|
|
|
case 6: result += 4 * line * (1. - progress); break;
|
|
|
|
case 7: result += 2 * line * progress; break;
|
|
|
|
}
|
|
|
|
return (started == 1.)
|
|
|
|
? result
|
|
|
|
: (started * result) + ((1. - started) * 2 * line);
|
|
|
|
}();
|
|
|
|
|
2020-12-02 21:16:26 +00:00
|
|
|
const auto drawRoundedRect = [&](float left, float size) {
|
|
|
|
const auto top = center.y() - size;
|
2020-12-03 22:48:57 +00:00
|
|
|
p.drawRoundedRect(QRectF(left, top, 2 * line, 2 * size), line, line);
|
2020-12-02 21:16:26 +00:00
|
|
|
};
|
|
|
|
|
2020-12-02 10:52:19 +00:00
|
|
|
auto left = center.x() - 4 * line;
|
2020-12-02 21:16:26 +00:00
|
|
|
drawRoundedRect(left, sideSize);
|
2020-12-02 10:52:19 +00:00
|
|
|
left += 3 * line;
|
2020-12-02 21:16:26 +00:00
|
|
|
drawRoundedRect(left, middleSize);
|
2020-12-02 10:52:19 +00:00
|
|
|
left += 3 * line;
|
2020-12-02 21:16:26 +00:00
|
|
|
drawRoundedRect(left, sideSize);
|
2020-12-02 10:52:19 +00:00
|
|
|
}
|
|
|
|
|
2021-08-30 13:11:50 +00:00
|
|
|
class ChooseStickerAnimation : public SendActionAnimation::Impl {
|
|
|
|
public:
|
|
|
|
ChooseStickerAnimation()
|
|
|
|
: Impl(st::historySendActionChooseStickerDuration)
|
|
|
|
, _eye({
|
|
|
|
.outWidth = float64(st::historySendActionChooseStickerEyeWidth),
|
|
|
|
.outHeight = float64(st::historySendActionChooseStickerEyeHeight),
|
|
|
|
.step = float64(st::historySendActionChooseStickerEyeStep),
|
|
|
|
.inLeftOffset = style::ConvertScale(1.5),
|
|
|
|
.inRightOffset = -style::ConvertScale(2.5)
|
|
|
|
+ st::historySendActionChooseStickerEyeWidth,
|
|
|
|
.outXOffset = style::ConvertScale(1.5),
|
|
|
|
.outStrokeWidth = style::ConvertScale(0.8 * 1.3),
|
|
|
|
.inStrokeWidth = style::ConvertScale(1.2 * 1.3),
|
|
|
|
.inSize = style::ConvertScale(2.),
|
|
|
|
.minProgress = 0.3,
|
|
|
|
.outHeightOffset = 1.5,
|
|
|
|
}) {
|
|
|
|
}
|
|
|
|
|
|
|
|
static const MetaData kMeta;
|
|
|
|
static std::unique_ptr<Impl> create() {
|
|
|
|
return std::make_unique<ChooseStickerAnimation>();
|
|
|
|
}
|
|
|
|
const MetaData *metaData() const override {
|
|
|
|
return &kMeta;
|
|
|
|
}
|
|
|
|
|
|
|
|
int width() const override {
|
2021-09-11 14:15:40 +00:00
|
|
|
return widthNoMargins() + _eye.step * 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
int widthNoMargins() const override {
|
2021-08-30 13:11:50 +00:00
|
|
|
return st::historySendActionChooseStickerPosition.x()
|
|
|
|
+ 2 * (_eye.outWidth + _eye.step)
|
|
|
|
+ _eye.step;
|
|
|
|
}
|
|
|
|
|
|
|
|
void paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) override;
|
|
|
|
private:
|
|
|
|
const struct {
|
|
|
|
const float64 outWidth;
|
|
|
|
const float64 outHeight;
|
|
|
|
const float64 step;
|
|
|
|
const float64 inLeftOffset;
|
|
|
|
const float64 inRightOffset;
|
|
|
|
const float64 outXOffset;
|
|
|
|
const float64 outStrokeWidth;
|
|
|
|
const float64 inStrokeWidth;
|
|
|
|
const float64 inSize;
|
|
|
|
const float64 minProgress;
|
|
|
|
const float64 outHeightOffset;
|
|
|
|
} _eye;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
const ChooseStickerAnimation::MetaData ChooseStickerAnimation::kMeta = {
|
|
|
|
0,
|
|
|
|
&ChooseStickerAnimation::create,
|
|
|
|
};
|
|
|
|
|
|
|
|
void ChooseStickerAnimation::paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time now) {
|
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
const auto frameMs = frameTime(now);
|
|
|
|
auto pen = color->p;
|
|
|
|
pen.setJoinStyle(Qt::RoundJoin);
|
|
|
|
pen.setCapStyle(Qt::RoundCap);
|
|
|
|
|
|
|
|
const auto half = float64(period() / 2);
|
|
|
|
const auto increment = (frameMs < half) ? true : false;
|
|
|
|
// A double-progress within a period half.
|
|
|
|
const auto progress = (frameMs / (half / 2)) - (increment ? 0 : 2);
|
|
|
|
|
|
|
|
const auto animationProgress = std::min(progress, 1.);
|
|
|
|
|
|
|
|
const auto k = _eye.minProgress;
|
|
|
|
const auto pIn = anim::easeInCirc(1, std::min(animationProgress / k, 1.));
|
|
|
|
const auto pInRev = 1. - pIn;
|
|
|
|
const auto pOut = anim::easeOutCirc(1., (animationProgress < k)
|
|
|
|
? 0.
|
|
|
|
: (animationProgress - k) / (1. - k));
|
|
|
|
|
|
|
|
const auto inX = _eye.inLeftOffset * (increment ? pIn : pInRev)
|
|
|
|
+ _eye.inRightOffset * (increment ? pInRev : pIn);
|
|
|
|
const auto inY = (_eye.outHeight - _eye.inSize) / 2.;
|
|
|
|
|
|
|
|
const auto outLeft = _eye.outXOffset
|
|
|
|
* (increment
|
|
|
|
? (1. - anim::easeOutCirc(1., progress / 2.))
|
|
|
|
: anim::easeOutQuint(1., progress / 2.));
|
|
|
|
|
|
|
|
const auto outScaleOffset = (pIn - pOut) * _eye.outHeightOffset;
|
|
|
|
const auto top = st::historySendActionChooseStickerPosition.y() + y;
|
|
|
|
const auto left = st::historySendActionChooseStickerPosition.x()
|
|
|
|
+ x
|
|
|
|
+ outLeft;
|
|
|
|
|
|
|
|
for (auto i = 0; i < 2; i++) {
|
|
|
|
const auto currentLeft = left + (_eye.outWidth + _eye.step) * i;
|
|
|
|
|
|
|
|
pen.setWidthF(_eye.outStrokeWidth);
|
|
|
|
p.setPen(pen);
|
|
|
|
p.setBrush(Qt::NoBrush);
|
|
|
|
p.drawEllipse(QRectF(
|
|
|
|
currentLeft,
|
|
|
|
top + outScaleOffset,
|
|
|
|
_eye.outWidth,
|
|
|
|
_eye.outHeight - outScaleOffset));
|
|
|
|
|
|
|
|
pen.setWidthF(_eye.inStrokeWidth);
|
|
|
|
p.setPen(pen);
|
|
|
|
p.setBrush(color->b);
|
|
|
|
p.drawEllipse(QRectF(
|
|
|
|
currentLeft + inX,
|
|
|
|
top + inY,
|
|
|
|
_eye.inSize,
|
|
|
|
_eye.inSize));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-01 19:20:33 +00:00
|
|
|
void CreateImplementationsMap() {
|
|
|
|
if (Implementations) {
|
|
|
|
return;
|
|
|
|
}
|
2020-07-06 09:58:18 +00:00
|
|
|
using Type = Api::SendProgressType;
|
2016-12-01 19:20:33 +00:00
|
|
|
Implementations.createIfNull();
|
2020-12-02 10:52:19 +00:00
|
|
|
static constexpr auto kRecordTypes = {
|
2016-12-01 19:20:33 +00:00
|
|
|
Type::RecordVideo,
|
2016-12-08 14:08:54 +00:00
|
|
|
Type::RecordVoice,
|
2017-04-12 19:37:47 +00:00
|
|
|
Type::RecordRound,
|
2016-12-01 19:20:33 +00:00
|
|
|
};
|
2020-12-02 10:52:19 +00:00
|
|
|
for (const auto type : kRecordTypes) {
|
2016-12-01 19:20:33 +00:00
|
|
|
Implementations->insert(type, &RecordAnimation::kMeta);
|
|
|
|
}
|
2020-12-02 10:52:19 +00:00
|
|
|
static constexpr auto kUploadTypes = {
|
2016-12-01 19:20:33 +00:00
|
|
|
Type::UploadFile,
|
|
|
|
Type::UploadPhoto,
|
|
|
|
Type::UploadVideo,
|
2016-12-08 14:08:54 +00:00
|
|
|
Type::UploadVoice,
|
2017-04-12 19:37:47 +00:00
|
|
|
Type::UploadRound,
|
2016-12-01 19:20:33 +00:00
|
|
|
};
|
2020-12-02 10:52:19 +00:00
|
|
|
for (const auto type : kUploadTypes) {
|
2016-12-01 19:20:33 +00:00
|
|
|
Implementations->insert(type, &UploadAnimation::kMeta);
|
|
|
|
}
|
2020-12-02 10:52:19 +00:00
|
|
|
Implementations->insert(Type::Speaking, &SpeakingAnimation::kMeta);
|
2021-08-30 13:11:50 +00:00
|
|
|
Implementations->insert(
|
|
|
|
Type::ChooseSticker,
|
|
|
|
&ChooseStickerAnimation::kMeta);
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
2020-12-02 10:52:19 +00:00
|
|
|
SendActionAnimation::SendActionAnimation() = default;
|
|
|
|
|
|
|
|
SendActionAnimation::~SendActionAnimation() = default;
|
|
|
|
|
2016-12-01 19:20:33 +00:00
|
|
|
bool SendActionAnimation::Impl::supports(Type type) const {
|
|
|
|
CreateImplementationsMap();
|
2021-08-30 13:11:43 +00:00
|
|
|
return Implementations->value(type, &TypingAnimation::kMeta)
|
|
|
|
== metaData();
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
|
2020-12-02 10:52:19 +00:00
|
|
|
void SendActionAnimation::start(Type type) {
|
|
|
|
if (!_impl || !_impl->supports(type)) {
|
|
|
|
_impl = CreateByType(type);
|
|
|
|
} else {
|
|
|
|
_impl->restartedAt(crl::now());
|
|
|
|
}
|
2018-09-20 16:39:59 +00:00
|
|
|
}
|
|
|
|
|
2020-12-02 10:52:19 +00:00
|
|
|
void SendActionAnimation::tryToFinish() {
|
|
|
|
if (!_impl) {
|
|
|
|
return;
|
|
|
|
} else if (_impl->finishNow()) {
|
|
|
|
_impl.reset();
|
|
|
|
}
|
|
|
|
}
|
2018-09-20 16:39:59 +00:00
|
|
|
|
2020-12-02 10:52:19 +00:00
|
|
|
int SendActionAnimation::width() const {
|
|
|
|
return _impl ? _impl->width() : 0;
|
|
|
|
}
|
|
|
|
|
2021-09-11 14:15:40 +00:00
|
|
|
int SendActionAnimation::widthNoMargins() const {
|
|
|
|
return _impl ? _impl->widthNoMargins() : 0;
|
|
|
|
}
|
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
void SendActionAnimation::paint(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth,
|
|
|
|
crl::time ms) const {
|
2020-12-02 10:52:19 +00:00
|
|
|
if (_impl) {
|
|
|
|
_impl->paint(p, color, x, y, outerWidth, ms);
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-30 13:11:43 +00:00
|
|
|
void SendActionAnimation::PaintSpeakingIdle(
|
|
|
|
Painter &p,
|
|
|
|
style::color color,
|
|
|
|
int x,
|
|
|
|
int y,
|
|
|
|
int outerWidth) {
|
2020-12-02 10:52:19 +00:00
|
|
|
SpeakingAnimation::PaintIdle(p, color, x, y, outerWidth);
|
2016-12-01 19:20:33 +00:00
|
|
|
}
|
|
|
|
|
2020-12-02 10:52:19 +00:00
|
|
|
auto SendActionAnimation::CreateByType(Type type) -> std::unique_ptr<Impl> {
|
2016-12-01 19:20:33 +00:00
|
|
|
CreateImplementationsMap();
|
|
|
|
return Implementations->value(type, &TypingAnimation::kMeta)->creator();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Ui
|