| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- /*
- 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 "lang/lang_tag.h"
- #include "ui/effects/ripple_animation.h"
- #include "ui/text/text_utilities.h"
- #include "ui/painter.h"
- #include "ui/ui_utility.h"
- #include "styles/style_boxes.h"
- #include "styles/style_chat_helpers.h"
- #include "styles/style_credits.h"
- namespace Ui {
- namespace {
- constexpr int kWideScale = 5;
- } // namespace
- SendButton::SendButton(QWidget *parent, const style::SendButton &st)
- : RippleButton(parent, st.inner.ripple)
- , _st(st) {
- updateSize();
- }
- void SendButton::setState(State state) {
- if (_state == state) {
- return;
- }
- const auto hasSlowmode = (_state.slowmodeDelay > 0);
- const auto hasSlowmodeChanged = hasSlowmode != (state.slowmodeDelay > 0);
- auto withSameSlowmode = state;
- withSameSlowmode.slowmodeDelay = _state.slowmodeDelay;
- const auto animate = hasSlowmodeChanged
- || (!hasSlowmode && withSameSlowmode != _state);
- if (animate) {
- _contentFrom = grabContent();
- }
- if (_state.slowmodeDelay != state.slowmodeDelay) {
- const auto seconds = state.slowmodeDelay;
- const auto minutes = seconds / 60;
- _slowmodeDelayText = seconds
- ? u"%1:%2"_q.arg(minutes).arg(seconds % 60, 2, 10, QChar('0'))
- : QString();
- }
- if (!state.starsToSend || state.type != Type::Send) {
- _starsToSendText = Text::String();
- } else if (_starsToSendText.isEmpty()
- || _state.starsToSend != state.starsToSend) {
- _starsToSendText.setMarkedText(
- _st.stars.style,
- Text::IconEmoji(&st::starIconEmoji).append(
- Lang::FormatCountToShort(state.starsToSend).string),
- kMarkupTextOptions);
- }
- _state = state;
- if (animate) {
- _stateChangeFromWidth = width();
- _stateChangeAnimation.stop();
- updateSize();
- _contentTo = grabContent();
- _stateChangeAnimation.start(
- [=] { updateSize(); update(); },
- 0.,
- 1.,
- st::universalDuration);
- setPointerCursor(_state.type != Type::Slowmode);
- updateSize();
- }
- update();
- }
- void SendButton::finishAnimating() {
- _stateChangeAnimation.stop();
- updateSize();
- update();
- }
- void SendButton::paintEvent(QPaintEvent *e) {
- auto p = QPainter(this);
- auto over = (isDown() || isOver());
- auto changed = _stateChangeAnimation.value(1.);
- if (changed < 1.) {
- PainterHighQualityEnabler hq(p);
- const auto ratio = style::DevicePixelRatio();
- p.setOpacity(1. - changed);
- const auto fromSize = _contentFrom.size() / (kWideScale * ratio);
- const auto fromShift = QPoint(
- (width() - fromSize.width()) / 2,
- (height() - fromSize.height()) / 2);
- auto fromRect = QRect(
- (1 - kWideScale) / 2 * fromSize.width(),
- (1 - kWideScale) / 2 * fromSize.height(),
- kWideScale * fromSize.width(),
- kWideScale * fromSize.height()
- ).translated(fromShift);
- auto hiddenWidth = anim::interpolate(0, (1 - kWideScale) / 2 * fromSize.width(), changed);
- auto hiddenHeight = anim::interpolate(0, (1 - kWideScale) / 2 * fromSize.height(), changed);
- p.drawPixmap(
- fromRect.marginsAdded(
- { hiddenWidth, hiddenHeight, hiddenWidth, hiddenHeight }),
- _contentFrom);
- p.setOpacity(changed);
- const auto toSize = _contentTo.size() / (kWideScale * ratio);
- const auto toShift = QPoint(
- (width() - toSize.width()) / 2,
- (height() - toSize.height()) / 2);
- auto toRect = QRect(
- (1 - kWideScale) / 2 * toSize.width(),
- (1 - kWideScale) / 2 * toSize.height(),
- kWideScale * toSize.width(),
- kWideScale * toSize.height()
- ).translated(toShift);
- auto shownWidth = anim::interpolate((1 - kWideScale) / 2 * width(), 0, changed);
- auto shownHeight = anim::interpolate((1 - kWideScale) / 2 * toSize.height(), 0, changed);
- p.drawPixmap(
- toRect.marginsAdded(
- { shownWidth, shownHeight, shownWidth, shownHeight }),
- _contentTo);
- return;
- }
- switch (_state.type) {
- case Type::Record: paintRecord(p, over); break;
- case Type::Round: paintRound(p, over); break;
- case Type::Save: paintSave(p, over); break;
- case Type::Cancel: paintCancel(p, over); break;
- case Type::Send:
- if (_starsToSendText.isEmpty()) {
- paintSend(p, over);
- } else {
- paintStarsToSend(p, over);
- }
- break;
- case Type::Schedule: paintSchedule(p, over); break;
- case Type::Slowmode: paintSlowmode(p); break;
- }
- }
- void SendButton::paintRecord(QPainter &p, bool over) {
- if (!isDisabled()) {
- paintRipple(
- p,
- (width() - _st.inner.rippleAreaSize) / 2,
- _st.inner.rippleAreaPosition.y());
- }
- const auto &icon = (isDisabled() || !over)
- ? _st.record
- : _st.recordOver;
- icon.paintInCenter(p, rect());
- }
- void SendButton::paintRound(QPainter &p, bool over) {
- if (!isDisabled()) {
- paintRipple(
- p,
- (width() - _st.inner.rippleAreaSize) / 2,
- _st.inner.rippleAreaPosition.y());
- }
- const auto &icon = (isDisabled() || !over)
- ? _st.round
- : _st.roundOver;
- icon.paintInCenter(p, rect());
- }
- void SendButton::paintSave(QPainter &p, bool over) {
- const auto &saveIcon = over
- ? st::historyEditSaveIconOver
- : st::historyEditSaveIcon;
- saveIcon.paintInCenter(p, rect());
- }
- void SendButton::paintCancel(QPainter &p, bool over) {
- paintRipple(
- p,
- (width() - _st.inner.rippleAreaSize) / 2,
- _st.inner.rippleAreaPosition.y());
- const auto &cancelIcon = over
- ? st::historyReplyCancelIconOver
- : st::historyReplyCancelIcon;
- cancelIcon.paintInCenter(p, rect());
- }
- void SendButton::paintSend(QPainter &p, bool over) {
- const auto &sendIcon = over ? _st.inner.iconOver : _st.inner.icon;
- if (isDisabled()) {
- const auto color = st::historyRecordVoiceFg->c;
- sendIcon.paint(p, st::historySendIconPosition, width(), color);
- } else {
- sendIcon.paint(p, st::historySendIconPosition, width());
- }
- }
- void SendButton::paintStarsToSend(QPainter &p, bool over) {
- const auto geometry = starsGeometry();
- {
- PainterHighQualityEnabler hq(p);
- p.setPen(Qt::NoPen);
- p.setBrush(over ? _st.stars.textBgOver : _st.stars.textBg);
- const auto radius = geometry.rounded.height() / 2;
- p.drawRoundedRect(geometry.rounded, radius, radius);
- }
- p.setPen(over ? _st.stars.textFgOver : _st.stars.textFg);
- _starsToSendText.draw(p, {
- .position = geometry.inner.topLeft(),
- .outerWidth = width(),
- .availableWidth = geometry.inner.width(),
- });
- }
- void SendButton::paintSchedule(QPainter &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(QPainter &p) {
- p.setFont(st::normalFont);
- p.setPen(st::windowSubTextFg);
- p.drawText(
- rect().marginsRemoved(st::historySlowmodeCounterMargins),
- _slowmodeDelayText,
- style::al_center);
- }
- SendButton::StarsGeometry SendButton::starsGeometry() const {
- const auto &st = _st.stars;
- const auto inner = QRect(
- 0,
- 0,
- _starsToSendText.maxWidth(),
- st.style.font->height);
- const auto rounded = inner.marginsAdded(QMargins(
- st.padding.left() - st.width / 2,
- st.padding.top() + st.textTop,
- st.padding.right() - st.width / 2,
- st.height - st.padding.top() - st.textTop - st.style.font->height));
- const auto add = (_st.inner.height - rounded.height()) / 2;
- const auto outer = rounded.marginsAdded(QMargins(
- add,
- add,
- add,
- _st.inner.height - add - rounded.height()));
- const auto shift = -outer.topLeft();
- return {
- .inner = inner.translated(shift),
- .rounded = rounded.translated(shift),
- .outer = outer.translated(shift),
- };
- }
- void SendButton::updateSize() {
- const auto finalWidth = _starsToSendText.isEmpty()
- ? _st.inner.width
- : starsGeometry().outer.width();
- const auto progress = _stateChangeAnimation.value(1.);
- resize(
- anim::interpolate(_stateChangeFromWidth, finalWidth, progress),
- _st.inner.height);
- }
- QPixmap SendButton::grabContent() {
- auto result = QImage(
- kWideScale * size() * style::DevicePixelRatio(),
- QImage::Format_ARGB32_Premultiplied);
- result.setDevicePixelRatio(style::DevicePixelRatio());
- result.fill(Qt::transparent);
- {
- auto p = QPainter(&result);
- p.drawPixmap(
- (kWideScale - 1) / 2 * width(),
- (kWideScale - 1) / 2 * height(),
- GrabWidget(this));
- }
- return PixmapFromImage(std::move(result));
- }
- QImage SendButton::prepareRippleMask() const {
- const auto size = _st.inner.rippleAreaSize;
- return RippleAnimation::EllipseMask(QSize(size, size));
- }
- QPoint SendButton::prepareRippleStartPosition() const {
- const auto real = mapFromGlobal(QCursor::pos());
- const auto size = _st.inner.rippleAreaSize;
- const auto y = (height() - _st.inner.rippleAreaSize) / 2;
- return real - QPoint((width() - size) / 2, y);
- }
- } // namespace Ui
|