2020-10-13 10:00:35 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
|
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
*/
|
|
|
|
#include "ui/controls/send_button.h"
|
|
|
|
|
|
|
|
#include "ui/effects/ripple_animation.h"
|
|
|
|
#include "styles/style_chat.h"
|
|
|
|
|
|
|
|
namespace Ui {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr int kWideScale = 5;
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
SendButton::SendButton(QWidget *parent)
|
|
|
|
: RippleButton(parent, st::historyReplyCancel.ripple) {
|
|
|
|
resize(st::historySendSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendButton::setType(Type type) {
|
|
|
|
Expects(isSlowmode() || type != Type::Slowmode);
|
|
|
|
|
|
|
|
if (isSlowmode() && type != Type::Slowmode) {
|
|
|
|
_afterSlowmodeType = type;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (_type != type) {
|
|
|
|
_contentFrom = grabContent();
|
|
|
|
_type = type;
|
|
|
|
_a_typeChanged.stop();
|
|
|
|
_contentTo = grabContent();
|
|
|
|
_a_typeChanged.start(
|
|
|
|
[=] { update(); },
|
|
|
|
0.,
|
|
|
|
1.,
|
|
|
|
st::historyRecordVoiceDuration);
|
|
|
|
setPointerCursor(_type != Type::Slowmode);
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
if (_type != Type::Record) {
|
2020-10-09 14:07:58 +00:00
|
|
|
clearRecordState();
|
2020-10-13 10:00:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendButton::setSlowmodeDelay(int seconds) {
|
|
|
|
Expects(seconds >= 0 && seconds < kSlowmodeDelayLimit);
|
|
|
|
|
|
|
|
if (_slowmodeDelay == seconds) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_slowmodeDelay = seconds;
|
|
|
|
_slowmodeDelayText = isSlowmode()
|
|
|
|
? u"%1:%2"_q.arg(seconds / 60).arg(seconds % 60, 2, 10, QChar('0'))
|
|
|
|
: QString();
|
|
|
|
setType(isSlowmode() ? Type::Slowmode : _afterSlowmodeType);
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendButton::finishAnimating() {
|
|
|
|
_a_typeChanged.stop();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2020-10-04 13:32:45 +00:00
|
|
|
void SendButton::requestPaintRecord(float64 progress) {
|
|
|
|
if (_type == Type::Record) {
|
|
|
|
_recordProgress = progress;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-09 14:07:58 +00:00
|
|
|
void SendButton::setLockRecord(bool lock) {
|
|
|
|
if (_type == Type::Record) {
|
|
|
|
_recordLocked = lock;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendButton::clearRecordState() {
|
|
|
|
_recordLocked = false;
|
|
|
|
_recordProgress = 0.;
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
2020-10-13 10:00:35 +00:00
|
|
|
void SendButton::paintEvent(QPaintEvent *e) {
|
|
|
|
Painter p(this);
|
|
|
|
|
|
|
|
auto over = (isDown() || isOver());
|
|
|
|
auto changed = _a_typeChanged.value(1.);
|
|
|
|
if (changed < 1.) {
|
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
p.setOpacity(1. - changed);
|
|
|
|
auto targetRect = QRect((1 - kWideScale) / 2 * width(), (1 - kWideScale) / 2 * height(), kWideScale * width(), kWideScale * height());
|
|
|
|
auto hiddenWidth = anim::interpolate(0, (1 - kWideScale) / 2 * width(), changed);
|
|
|
|
auto hiddenHeight = anim::interpolate(0, (1 - kWideScale) / 2 * height(), changed);
|
|
|
|
p.drawPixmap(targetRect.marginsAdded(QMargins(hiddenWidth, hiddenHeight, hiddenWidth, hiddenHeight)), _contentFrom);
|
|
|
|
p.setOpacity(changed);
|
|
|
|
auto shownWidth = anim::interpolate((1 - kWideScale) / 2 * width(), 0, changed);
|
|
|
|
auto shownHeight = anim::interpolate((1 - kWideScale) / 2 * height(), 0, changed);
|
|
|
|
p.drawPixmap(targetRect.marginsAdded(QMargins(shownWidth, shownHeight, shownWidth, shownHeight)), _contentTo);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
switch (_type) {
|
|
|
|
case Type::Record: paintRecord(p, over); break;
|
|
|
|
case Type::Save: paintSave(p, over); break;
|
|
|
|
case Type::Cancel: paintCancel(p, over); break;
|
|
|
|
case Type::Send: paintSend(p, over); break;
|
|
|
|
case Type::Schedule: paintSchedule(p, over); break;
|
|
|
|
case Type::Slowmode: paintSlowmode(p); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendButton::paintRecord(Painter &p, bool over) {
|
2020-10-04 13:32:45 +00:00
|
|
|
const auto recordActive = _recordProgress;
|
2020-10-13 10:00:35 +00:00
|
|
|
if (!isDisabled()) {
|
2020-10-09 14:07:58 +00:00
|
|
|
auto rippleColor = anim::color(
|
|
|
|
_recordLocked
|
|
|
|
? st::historyRecordVoiceRippleBgCancel
|
|
|
|
: st::historyAttachEmoji.ripple.color,
|
|
|
|
st::historyRecordVoiceRippleBgActive,
|
|
|
|
recordActive);
|
|
|
|
paintRipple(
|
|
|
|
p,
|
|
|
|
(width() - st::historyAttachEmoji.rippleAreaSize) / 2,
|
|
|
|
st::historyAttachEmoji.rippleAreaPosition.y(),
|
|
|
|
&rippleColor);
|
2020-10-13 10:00:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
auto fastIcon = [&] {
|
|
|
|
if (isDisabled()) {
|
|
|
|
return &st::historyRecordVoice;
|
|
|
|
} else if (recordActive == 1.) {
|
|
|
|
return &st::historyRecordVoiceActive;
|
2020-10-09 14:07:58 +00:00
|
|
|
} else if (_recordLocked) {
|
|
|
|
return &st::historyRecordVoiceCancel;
|
2020-10-13 10:00:35 +00:00
|
|
|
} else if (over) {
|
|
|
|
return &st::historyRecordVoiceOver;
|
|
|
|
}
|
|
|
|
return &st::historyRecordVoice;
|
|
|
|
};
|
|
|
|
fastIcon()->paintInCenter(p, rect());
|
|
|
|
if (!isDisabled() && recordActive > 0. && recordActive < 1.) {
|
|
|
|
p.setOpacity(recordActive);
|
|
|
|
st::historyRecordVoiceActive.paintInCenter(p, rect());
|
|
|
|
p.setOpacity(1.);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendButton::paintSave(Painter &p, bool over) {
|
|
|
|
const auto &saveIcon = over
|
|
|
|
? st::historyEditSaveIconOver
|
|
|
|
: st::historyEditSaveIcon;
|
|
|
|
saveIcon.paint(p, st::historySendIconPosition, width());
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendButton::paintCancel(Painter &p, bool over) {
|
|
|
|
paintRipple(p, (width() - st::historyAttachEmoji.rippleAreaSize) / 2, st::historyAttachEmoji.rippleAreaPosition.y());
|
|
|
|
|
|
|
|
const auto &cancelIcon = over
|
|
|
|
? st::historyReplyCancelIconOver
|
|
|
|
: st::historyReplyCancelIcon;
|
|
|
|
cancelIcon.paintInCenter(p, rect());
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendButton::paintSend(Painter &p, bool over) {
|
|
|
|
const auto &sendIcon = over
|
|
|
|
? st::historySendIconOver
|
|
|
|
: st::historySendIcon;
|
|
|
|
if (isDisabled()) {
|
|
|
|
const auto color = st::historyRecordVoiceFg->c;
|
|
|
|
sendIcon.paint(p, st::historySendIconPosition, width(), color);
|
|
|
|
} else {
|
|
|
|
sendIcon.paint(p, st::historySendIconPosition, width());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendButton::paintSchedule(Painter &p, bool over) {
|
|
|
|
{
|
|
|
|
PainterHighQualityEnabler hq(p);
|
|
|
|
p.setPen(Qt::NoPen);
|
|
|
|
p.setBrush(over ? st::historySendIconFgOver : st::historySendIconFg);
|
|
|
|
p.drawEllipse(
|
|
|
|
st::historyScheduleIconPosition.x(),
|
|
|
|
st::historyScheduleIconPosition.y(),
|
|
|
|
st::historyScheduleIcon.width(),
|
|
|
|
st::historyScheduleIcon.height());
|
|
|
|
}
|
|
|
|
st::historyScheduleIcon.paint(
|
|
|
|
p,
|
|
|
|
st::historyScheduleIconPosition,
|
|
|
|
width());
|
|
|
|
}
|
|
|
|
|
|
|
|
void SendButton::paintSlowmode(Painter &p) {
|
|
|
|
p.setFont(st::normalFont);
|
|
|
|
p.setPen(st::windowSubTextFg);
|
|
|
|
p.drawText(
|
|
|
|
rect().marginsRemoved(st::historySlowmodeCounterMargins),
|
|
|
|
_slowmodeDelayText,
|
|
|
|
style::al_center);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SendButton::isSlowmode() const {
|
|
|
|
return (_slowmodeDelay > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
QPixmap SendButton::grabContent() {
|
|
|
|
auto result = QImage(
|
|
|
|
kWideScale * size() * style::DevicePixelRatio(),
|
|
|
|
QImage::Format_ARGB32_Premultiplied);
|
|
|
|
result.setDevicePixelRatio(style::DevicePixelRatio());
|
|
|
|
result.fill(Qt::transparent);
|
|
|
|
{
|
|
|
|
Painter p(&result);
|
|
|
|
p.drawPixmap(
|
|
|
|
(kWideScale - 1) / 2 * width(),
|
|
|
|
(kWideScale - 1) / 2 * height(),
|
|
|
|
GrabWidget(this));
|
|
|
|
}
|
|
|
|
return Ui::PixmapFromImage(std::move(result));
|
|
|
|
}
|
|
|
|
|
|
|
|
QImage SendButton::prepareRippleMask() const {
|
|
|
|
auto size = (_type == Type::Record)
|
|
|
|
? st::historyAttachEmoji.rippleAreaSize
|
|
|
|
: st::historyReplyCancel.rippleAreaSize;
|
|
|
|
return RippleAnimation::ellipseMask(QSize(size, size));
|
|
|
|
}
|
|
|
|
|
|
|
|
QPoint SendButton::prepareRippleStartPosition() const {
|
|
|
|
auto real = mapFromGlobal(QCursor::pos());
|
|
|
|
auto size = (_type == Type::Record)
|
|
|
|
? st::historyAttachEmoji.rippleAreaSize
|
|
|
|
: st::historyReplyCancel.rippleAreaSize;
|
|
|
|
auto y = (_type == Type::Record)
|
|
|
|
? st::historyAttachEmoji.rippleAreaPosition.y()
|
|
|
|
: (height() - st::historyReplyCancel.rippleAreaSize) / 2;
|
|
|
|
return real - QPoint((width() - size) / 2, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Ui
|