/* 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) { _recordProgress = 0.; } } 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(); } void SendButton::requestPaintRecord(float64 progress) { if (_type == Type::Record) { _recordProgress = progress; update(); } } 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) { const auto recordActive = _recordProgress; if (!isDisabled()) { auto rippleColor = anim::color(st::historyAttachEmoji.ripple.color, st::historyRecordVoiceRippleBgActive, recordActive); paintRipple(p, (width() - st::historyAttachEmoji.rippleAreaSize) / 2, st::historyAttachEmoji.rippleAreaPosition.y(), &rippleColor); } auto fastIcon = [&] { if (isDisabled()) { return &st::historyRecordVoice; } else if (recordActive == 1.) { return &st::historyRecordVoiceActive; } 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