From ad38e06555be4077dbfa102ce04fa7bbb5751fa9 Mon Sep 17 00:00:00 2001 From: 23rd <23rd@vivaldi.net> Date: Thu, 19 Mar 2026 13:25:15 +0300 Subject: [PATCH] [poll-view] Added support of document files as attached media. --- .../history/view/media/history_view_poll.cpp | 176 ++++++++++++++---- .../history/view/media/history_view_poll.h | 2 + 2 files changed, 137 insertions(+), 41 deletions(-) diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp index 827c7d5ebd..fbe8acd54a 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.cpp @@ -789,6 +789,30 @@ void Poll::updateAttachedMedia() { _attachedMedia->kind = updated.kind; _attachedMedia->rounded = updated.rounded; _attachedMedia->id = updated.id; + if (updated.kind == PollThumbnailKind::Document && updated.id) { + const auto document = _poll->owner().document( + DocumentId(updated.id)); + const auto session = &_poll->session(); + _attachedMedia->handler = std::make_shared( + [=](ClickContext context) { + const auto my = context.other.value(); + const auto controller = my.sessionWindow.get(); + if (!controller + || (&controller->session() != session)) { + return; + } + controller->openDocument( + document, + false, + messageContext); + }); + _attachedMediaAttach = CreateAttach( + _parent, + document, + nullptr); + } else { + _attachedMediaAttach = nullptr; + } if (_attachedMedia->thumbnail) { _attachedMedia->thumbnail->subscribeToUpdates( crl::guard(this, [=] { @@ -807,9 +831,25 @@ int Poll::countTopContentSkip() const { } int Poll::countTopMediaHeight() const { - return (_attachedMedia && _attachedMedia->thumbnail) - ? st::historyPollMediaHeight - : 0; + if (!_attachedMedia || !_attachedMedia->thumbnail) { + return 0; + } + if (_attachedMediaAttach) { + return countAttachedDocumentHeight(); + } + return st::historyPollMediaHeight; +} + +int Poll::countAttachedDocumentHeight() const { + if (!_attachedMediaAttach) { + return 0; + } + _attachedMediaAttach->initDimensions(); + const auto innerWidth = width() + - st::msgPadding.left() + - st::msgPadding.right(); + return _attachedMediaAttach->resizeGetHeight( + std::max(1, innerWidth)); } QRect Poll::countTopMediaRect(int top) const { @@ -1334,44 +1374,59 @@ void Poll::draw(Painter &p, const PaintContext &context) const { paintw -= padding.left() + padding.right(); if (const auto mediaHeight = countTopMediaHeight()) { - const auto target = countTopMediaRect(tshift); - p.setPen(Qt::NoPen); - p.setBrush(stm->msgFileBg); - PainterHighQualityEnabler hq(p); - if (_attachedMedia->kind == PollThumbnailKind::Emoji) { - p.drawRoundedRect( - target, - st::roundRadiusLarge, - st::roundRadiusLarge); - const auto image = _attachedMedia->thumbnail->image( - std::max(target.width(), target.height())); - if (!image.isNull()) { - const auto source = QRectF(QPointF(), QSizeF(image.size())); - const auto kx = target.width() / source.width(); - const auto ky = target.height() / source.height(); - const auto scale = std::min(kx, ky); - const auto imageSize = QSizeF( - source.width() * scale, - source.height() * scale); - const auto geometry = QRectF( - target.x() + (target.width() - imageSize.width()) / 2., - target.y() + (target.height() - imageSize.height()) / 2., - imageSize.width(), - imageSize.height()); - p.save(); - auto path = QPainterPath(); - path.addRoundedRect( + if (_attachedMediaAttach) { + const auto shift = st::msgFileLayout.padding.left(); + const auto attachLeft = padding.left() - shift; + p.translate(attachLeft, tshift); + _attachedMediaAttach->draw( + p, + context.translated(-attachLeft, -tshift) + .withSelection(TextSelection())); + p.translate(-attachLeft, -tshift); + } else { + const auto target = countTopMediaRect(tshift); + p.setPen(Qt::NoPen); + p.setBrush(stm->msgFileBg); + PainterHighQualityEnabler hq(p); + if (_attachedMedia->kind == PollThumbnailKind::Emoji) { + p.drawRoundedRect( target, st::roundRadiusLarge, st::roundRadiusLarge); - p.setClipPath(path); - p.drawImage(geometry, image, source); - p.restore(); - } - } else { - validateTopMediaCache(target.size()); - if (!_attachedMediaCache.isNull()) { - p.drawImage(target.topLeft(), _attachedMediaCache); + const auto image = _attachedMedia->thumbnail->image( + std::max(target.width(), target.height())); + if (!image.isNull()) { + const auto source = QRectF( + QPointF(), + QSizeF(image.size())); + const auto kx = target.width() / source.width(); + const auto ky = target.height() / source.height(); + const auto scale = std::min(kx, ky); + const auto imageSize = QSizeF( + source.width() * scale, + source.height() * scale); + const auto geometry = QRectF( + target.x() + + (target.width() - imageSize.width()) / 2., + target.y() + + (target.height() - imageSize.height()) / 2., + imageSize.width(), + imageSize.height()); + p.save(); + auto path = QPainterPath(); + path.addRoundedRect( + target, + st::roundRadiusLarge, + st::roundRadiusLarge); + p.setClipPath(path); + p.drawImage(geometry, image, source); + p.restore(); + } + } else { + validateTopMediaCache(target.size()); + if (!_attachedMediaCache.isNull()) { + p.drawImage(target.topLeft(), _attachedMediaCache); + } } } tshift += mediaHeight + st::historyPollMediaSkip; @@ -1780,7 +1835,16 @@ void Poll::paintSolutionBlock( if (countSolutionMediaHeight(textWidth)) { yshift += _solutionText.countHeight(textWidth) + st::historyPollExplanationMediaSkip; - const auto shift = st::msgFileLayout.padding.left(); + const auto isDocument = _solutionMedia + && (_solutionMedia->kind == PollThumbnailKind::Document); + const auto isThumbed = isDocument + && _poll->solutionMedia.document + && _poll->solutionMedia.document->hasThumbnail() + && !_poll->solutionMedia.document->isSong(); + const auto &fileSt = isThumbed + ? st::msgFileThumbLayout + : st::msgFileLayout; + const auto shift = isDocument ? fileSt.padding.left() : 0; const auto attachLeft = rtl() ? (width() - innerLeft + shift - _solutionAttach->width()) : (innerLeft - shift); @@ -2351,7 +2415,19 @@ TextState Poll::textState(QPoint point, StateRequest request) const { paintw -= padding.left() + padding.right(); if (const auto mediaHeight = countTopMediaHeight()) { - if (_attachedMedia->handler + if (_attachedMediaAttach) { + const auto shift = st::msgFileLayout.padding.left(); + const auto attachLeft = padding.left() - shift; + if (_attachedMedia->handler + && QRect( + attachLeft, + tshift, + _attachedMediaAttach->width(), + mediaHeight).contains(point)) { + result.link = _attachedMedia->handler; + return result; + } + } else if (_attachedMedia->handler && countTopMediaRect(tshift).contains(point)) { result.link = _attachedMedia->handler; return result; @@ -2424,7 +2500,19 @@ TextState Poll::textState(QPoint point, StateRequest request) const { const auto mediaTop = textTop + textHeight + st::historyPollExplanationMediaSkip; - const auto shift = st::msgFileLayout.padding.left(); + const auto isDocument = _solutionMedia + && (_solutionMedia->kind + == PollThumbnailKind::Document); + const auto isThumbed = isDocument + && _poll->solutionMedia.document + && _poll->solutionMedia.document->hasThumbnail() + && !_poll->solutionMedia.document->isSong(); + const auto &fileSt = isThumbed + ? st::msgFileThumbLayout + : st::msgFileLayout; + const auto shift = isDocument + ? fileSt.padding.left() + : 0; const auto mediaLeft = innerLeft - shift; if (_solutionAttach && QRect( @@ -2574,6 +2662,9 @@ void Poll::paintBubbleFireworks( void Poll::clickHandlerActiveChanged( const ClickHandlerPtr &handler, bool active) { + if (_attachedMediaAttach) { + _attachedMediaAttach->clickHandlerActiveChanged(handler, active); + } if (_solutionAttach) { _solutionAttach->clickHandlerActiveChanged(handler, active); } @@ -2593,6 +2684,9 @@ void Poll::clickHandlerPressedChanged( } else if (handler == _sendVotesLink || handler == _showResultsLink) { toggleLinkRipple(pressed); } + if (_attachedMediaAttach) { + _attachedMediaAttach->clickHandlerPressedChanged(handler, pressed); + } if (_solutionAttach) { _solutionAttach->clickHandlerPressedChanged(handler, pressed); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_poll.h b/Telegram/SourceFiles/history/view/media/history_view_poll.h index 13701204e4..167d58a43f 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_poll.h +++ b/Telegram/SourceFiles/history/view/media/history_view_poll.h @@ -90,6 +90,7 @@ private: void updateAttachedMedia(); [[nodiscard]] int countTopContentSkip() const; [[nodiscard]] int countTopMediaHeight() const; + [[nodiscard]] int countAttachedDocumentHeight() const; [[nodiscard]] QRect countTopMediaRect(int top) const; [[nodiscard]] Ui::BubbleRounding topMediaRounding() const; void validateTopMediaCache(QSize size) const; @@ -241,6 +242,7 @@ private: Ui::Text::String _question; Ui::Text::String _subtitle; std::unique_ptr _attachedMedia; + std::unique_ptr _attachedMediaAttach; mutable QImage _attachedMediaCache; mutable Ui::BubbleRounding _attachedMediaCacheRounding; std::vector _recentVoters;