From 51e96bf07441a4068300ddc66d2da8f8dac1f6fa Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 18 Dec 2021 14:02:36 +0000 Subject: [PATCH] Support reaction button with gradient bubbles. --- .../history/view/history_view_reactions.cpp | 72 ++++++++++++++++++- .../history/view/history_view_reactions.h | 17 +++-- 2 files changed, 83 insertions(+), 6 deletions(-) diff --git a/Telegram/SourceFiles/history/view/history_view_reactions.cpp b/Telegram/SourceFiles/history/view/history_view_reactions.cpp index 4919c189a..61cfafb58 100644 --- a/Telegram/SourceFiles/history/view/history_view_reactions.cpp +++ b/Telegram/SourceFiles/history/view/history_view_reactions.cpp @@ -51,6 +51,26 @@ constexpr auto kCacheColumsCount = 3; return CountMaxSizeWithMargins(st::reactionCornerShadow); } +void CopyImagePart(QImage &to, const QImage &from, QRect source) { + Expects(to.size() == source.size()); + Expects(QRect(QPoint(), from.size()).contains(source)); + Expects(to.format() == from.format()); + Expects(to.bytesPerLine() == to.width() * 4); + + const auto perPixel = 4; + const auto fromPerLine = from.bytesPerLine(); + const auto toPerLine = to.bytesPerLine(); + auto toBytes = reinterpret_cast(to.bits()); + auto fromBytes = reinterpret_cast(from.bits()) + + (source.y() * fromPerLine) + + (source.x() * perPixel); + for (auto y = 0, height = source.height(); y != height; ++y) { + memcpy(toBytes, fromBytes, toPerLine); + toBytes += toPerLine; + fromBytes += fromPerLine; + } +} + } // namespace InlineList::InlineList(Data &&data) @@ -399,6 +419,10 @@ Manager::Manager(QWidget *selectorParent, Fn buttonUpdate) QImage::Format_ARGB32_Premultiplied); _cacheParts.setDevicePixelRatio(ratio); _cacheParts.fill(Qt::transparent); + _cacheForPattern = QImage( + _outer * ratio, + QImage::Format_ARGB32_Premultiplied); + _cacheForPattern.setDevicePixelRatio(ratio); _shadowBuffer = QImage( _outer * ratio, QImage::Format_ARGB32_Premultiplied); @@ -556,7 +580,11 @@ void Manager::paintButton( position, _cacheParts, validateShadow(frameIndex, scale, shadow)); - // #TODO reactions + + validateCacheForPattern(frameIndex, scale, geometry, context); + p.drawImage(geometry, _cacheForPattern); + + p.drawImage(position, _cacheParts, validateEmoji(frameIndex, scale)); } else { const auto &stm = context.st->messageStyle(outbg, false); const auto background = stm.msgBg->c; @@ -573,6 +601,24 @@ void Manager::paintButton( } } +void Manager::validateCacheForPattern( + int frameIndex, + float64 scale, + const QRect &geometry, + const PaintContext &context) { + CopyImagePart( + _cacheForPattern, + _cacheParts, + validateMask(frameIndex, scale)); + auto q = QPainter(&_cacheForPattern); + q.setCompositionMode(QPainter::CompositionMode_SourceIn); + Ui::PaintPatternBubblePart( + q, + context.viewport.translated(-geometry.topLeft()), + context.bubblesPattern->pixmap, + QRect(QPoint(), _outer)); +} + void Manager::applyPatternedShadow(const QColor &shadow) { if (_shadow == shadow) { return; @@ -702,6 +748,30 @@ QRect Manager::validateFrame( return result; } +QRect Manager::validateMask(int frameIndex, float64 scale) { + const auto result = cacheRect(frameIndex, kMaskCacheIndex); + if (_validMask[frameIndex]) { + return result; + } + + auto p = QPainter(&_cacheParts); + auto hq = PainterHighQualityEnabler(p); + const auto position = result.topLeft() / style::DevicePixelRatio(); + const auto inner = _inner.translated(position); + const auto radius = inner.height() / 2; + const auto center = inner.center(); + p.setPen(Qt::NoPen); + p.setBrush(Qt::white); + p.save(); + p.translate(center); + p.scale(scale, scale); + p.translate(-center); + p.drawRoundedRect(inner, radius, radius); + + _validMask[frameIndex] = true; + return result; +} + void Manager::showSelector(Fn mapToGlobal) { if (!_button) { showSelector({}, {}); diff --git a/Telegram/SourceFiles/history/view/history_view_reactions.h b/Telegram/SourceFiles/history/view/history_view_reactions.h index 0bdc3d0ea..31621d99f 100644 --- a/Telegram/SourceFiles/history/view/history_view_reactions.h +++ b/Telegram/SourceFiles/history/view/history_view_reactions.h @@ -206,6 +206,12 @@ private: float64 scale, const QColor &background, const QColor &shadow); + QRect validateMask(int frameIndex, float64 scale); + void validateCacheForPattern( + int frameIndex, + float64 scale, + const QRect &geometry, + const PaintContext &context); rpl::event_stream _chosen; std::vector _list; @@ -214,12 +220,13 @@ private: QRect _innerActive; QImage _cacheInOut; QImage _cacheParts; + QImage _cacheForPattern; QImage _shadowBuffer; - std::array _validIn; - std::array _validOut; - std::array _validShadow; - std::array _validEmoji; - std::array _validMask; + std::array _validIn = { { false } }; + std::array _validOut = { { false } }; + std::array _validShadow = { { false } }; + std::array _validEmoji = { { false } }; + std::array _validMask = { { false } }; QColor _backgroundIn; QColor _backgroundOut; QColor _shadow;