From 85d08c8f52d071670620221ad906304181382c3f Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 16 Oct 2020 20:48:27 +0300 Subject: [PATCH] Send files grouped in albums, show captions. --- Telegram/SourceFiles/apiwrap.cpp | 4 +- Telegram/SourceFiles/boxes/send_files_box.cpp | 9 ++ Telegram/SourceFiles/boxes/send_files_box.h | 1 + .../SourceFiles/data/data_media_types.cpp | 14 +- .../SourceFiles/history/history_widget.cpp | 83 ++++++++++-- Telegram/SourceFiles/history/history_widget.h | 8 ++ .../history/view/history_view_pinned_bar.cpp | 2 +- .../view/media/history_view_document.cpp | 73 +++++++++-- .../view/media/history_view_document.h | 11 +- .../history/view/media/history_view_gif.cpp | 12 +- .../history/view/media/history_view_gif.h | 9 +- .../history/view/media/history_view_media.cpp | 3 +- .../history/view/media/history_view_media.h | 13 +- .../view/media/history_view_media_common.cpp | 2 +- .../view/media/history_view_media_grouped.cpp | 120 +++++++++++------- .../view/media/history_view_media_grouped.h | 4 +- .../history/view/media/history_view_photo.cpp | 12 +- .../history/view/media/history_view_photo.h | 11 +- 18 files changed, 292 insertions(+), 99 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 7de96c1ad8..9f82ba51c6 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -4232,7 +4232,9 @@ void ApiWrap::sendFiles( if (album) { switch (file.type) { case Ui::PreparedFile::AlbumType::Photo: - type = SendMediaType::Photo; + type = (type == SendMediaType::File) + ? type + : SendMediaType::Photo; break; case Ui::PreparedFile::AlbumType::Video: case Ui::PreparedFile::AlbumType::File: diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 9126230364..649d414d3f 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -763,6 +763,9 @@ void SendFilesBox::addPreparedAsyncFile(Ui::PreparedFile &&file) { if (_list.files.size() > count) { refreshAllAfterChanges(count); } + if (!_preparing && _whenReadySend) { + _whenReadySend(); + } } void SendFilesBox::addFile(Ui::PreparedFile &&file) { @@ -893,6 +896,12 @@ void SendFilesBox::send( && !options.scheduled) { return sendScheduled(); } + if (_preparing) { + _whenReadySend = [=] { + send(options, ctrlShiftEnter); + }; + return; + } auto way = _sendWay.current(); auto oldWay = Core::App().settings().sendFilesWay(); diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index d9e8d57191..25384cdf66 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -187,6 +187,7 @@ private: object_ptr _scroll; QPointer _inner; std::vector _blocks; + Fn _whenReadySend; bool _preparing = false; int _lastScrollTop = 0; diff --git a/Telegram/SourceFiles/data/data_media_types.cpp b/Telegram/SourceFiles/data/data_media_types.cpp index 7a89c6252e..d32e043cba 100644 --- a/Telegram/SourceFiles/data/data_media_types.cpp +++ b/Telegram/SourceFiles/data/data_media_types.cpp @@ -479,7 +479,14 @@ Storage::SharedMediaTypesMask MediaFile::sharedMediaTypes() const { } bool MediaFile::canBeGrouped() const { - return _document->isVideoFile() || _document->isSong(); + if (_document->sticker() || _document->isAnimation()) { + return false; + } else if (_document->isVideoFile()) { + return true; + } else if (_document->isTheme() && _document->hasThumbnail()) { + return false; + } + return true; } bool MediaFile::hasReplyPreview() const { @@ -704,7 +711,10 @@ std::unique_ptr MediaFile::createView( message, _document); } - return std::make_unique(message, _document); + return std::make_unique( + message, + realParent, + _document); } MediaContact::MediaContact( diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index f731557956..c6f3e5d550 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -4177,22 +4177,12 @@ bool HistoryWidget::confirmSendingFiles( TextWithTags &&caption, Api::SendOptions options, bool ctrlShiftEnter) { - if (showSendingFilesError(list)) { - return; - } - const auto type = way.sendImagesAsPhotos() - ? SendMediaType::Photo - : SendMediaType::File; - const auto album = way.groupMediaInAlbums() // #TODO files - ? std::make_shared() - : nullptr; - uploadFilesAfterConfirmation( + sendingFilesConfirmed( std::move(list), - type, + way, std::move(caption), - replyToId(), options, - album); + ctrlShiftEnter); })); box->setCancelledCallback(crl::guard(this, [=] { _field->setTextWithTags(text); @@ -4214,6 +4204,73 @@ bool HistoryWidget::confirmSendingFiles( return true; } +void HistoryWidget::sendingFilesConfirmed( + Ui::PreparedList &&list, + Ui::SendFilesWay way, + TextWithTags &&caption, + Api::SendOptions options, + bool ctrlShiftEnter) { + Expects(list.filesToProcess.empty()); + + if (showSendingFilesError(list)) { + return; + } + const auto slowmode = _peer->slowmodeApplied(); + const auto sendImagesAsPhotos = way.sendImagesAsPhotos(); + const auto sendType = sendImagesAsPhotos + ? SendMediaType::Photo + : SendMediaType::File; + const auto groupMedia = way.groupMediaInAlbums() || slowmode; + const auto groupFiles = way.groupFiles() || slowmode; + + auto group = Ui::PreparedList(); + + // For groupType Type::Video means media album, + // Type::File means file album, + // Type::None means no grouping. + using Type = Ui::PreparedFile::AlbumType; + auto groupType = Type::None; + + const auto reply = replyToId(); + auto sendGroup = [&] { + if (group.files.empty()) { + return; + } + const auto album = (groupType == Type::None) + ? nullptr + : std::make_shared(); + uploadFilesAfterConfirmation( + base::take(group), + sendType, + base::take(caption), + reply, + options, + std::move(album)); + }; + for (auto i = 0; i != list.files.size(); ++i) { + auto &file = list.files[i]; + const auto fileGroupType = (file.type == Type::Video) + ? (groupMedia ? Type::Video : Type::None) + : (file.type == Type::Photo) + ? ((groupMedia && sendImagesAsPhotos) + ? Type::Video + : (groupFiles && !sendImagesAsPhotos) + ? Type::File + : Type::None) + : (file.type == Type::File) + ? (groupFiles ? Type::File : Type::None) + : Type::None; + if ((!group.files.empty() && groupType != fileGroupType) + || ((groupType != Type::None) + && (group.files.size() == Ui::MaxAlbumItems()))) { + sendGroup(); + } + group.files.push_back(std::move(file)); + groupType = fileGroupType; + } + sendGroup(); +} + bool HistoryWidget::confirmSendingFiles( QImage &&image, QByteArray &&content, diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 377bb4ede9..76b698785d 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -66,6 +66,7 @@ class LinkButton; class RoundButton; class PinnedBar; struct PreparedList; +class SendFilesWay; namespace Toast { class Instance; } // namespace Toast @@ -425,6 +426,13 @@ private: const QString &insertTextOnCancel = QString()); bool showSendingFilesError(const Ui::PreparedList &list) const; + void sendingFilesConfirmed( + Ui::PreparedList &&list, + Ui::SendFilesWay way, + TextWithTags &&caption, + Api::SendOptions options, + bool ctrlShiftEnter); + void uploadFile(const QByteArray &fileContent, SendMediaType type); void uploadFilesAfterConfirmation( diff --git a/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp b/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp index 267a9513f8..a73c9e4cfe 100644 --- a/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp +++ b/Telegram/SourceFiles/history/view/history_view_pinned_bar.cpp @@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history_item.h" #include "history/history.h" #include "apiwrap.h" -#include "styles/style_history.h" +#include "styles/style_chat.h" namespace HistoryView { namespace { diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.cpp b/Telegram/SourceFiles/history/view/media/history_view_document.cpp index 0aa633d6e1..9d3b475823 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_document.cpp @@ -65,8 +65,9 @@ constexpr auto kAudioVoiceMsgUpdateView = crl::time(100); Document::Document( not_null parent, + not_null realParent, not_null document) -: File(parent, parent->data()) +: File(parent, realParent) , _data(document) { const auto item = parent->data(); auto caption = createCaption(); @@ -152,7 +153,7 @@ QSize Document::countOptimalSize() { const auto item = _parent->data(); auto captioned = Get(); - if (_parent->media() != this) { + if (_parent->media() != this && !_realParent->groupId()) { if (captioned) { RemoveComponents(HistoryDocumentCaptioned::Bit()); captioned = nullptr; @@ -508,7 +509,7 @@ void Document::draw( } } - if (mode == LayoutMode::Full) { + if (mode != LayoutMode::GroupedLast) { if (auto captioned = Get()) { p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); captioned->_caption.draw(p, st::msgPadding.left(), bottom, captionw, style::al_left, 0, -1, selection); @@ -682,7 +683,7 @@ TextState Document::textState( } auto painth = layout.height(); - if (mode == LayoutMode::Full) { + if (mode != LayoutMode::GroupedLast) { if (const auto captioned = Get()) { if (point.y() >= bottom) { result = TextState(_parent, captioned->_caption.getState( @@ -859,10 +860,43 @@ bool Document::hideForwardedFrom() const { return _data->isSong(); } -QSize Document::sizeForGrouping() const { - const auto height = st::msgFilePadding.top() - + st::msgFileSize - + st::msgFilePadding.bottom(); +QSize Document::sizeForGroupingOptimal(int maxWidth, bool last) const { + auto height = Has() + ? (st::msgFileThumbPadding.top() + + st::msgFileThumbSize + + st::msgFileThumbPadding.bottom()) + : (st::msgFilePadding.top() + + st::msgFileSize + + st::msgFilePadding.bottom()); + if (!last) { + if (const auto captioned = Get()) { + auto captionw = maxWidth + - st::msgPadding.left() + - st::msgPadding.right(); + height += captioned->_caption.countHeight(captionw); + } + } + + return { maxWidth, height }; +} + +QSize Document::sizeForGrouping(int width, bool last) const { + auto height = Has() + ? (st::msgFileThumbPadding.top() + + st::msgFileThumbSize + + st::msgFileThumbPadding.bottom()) + : (st::msgFilePadding.top() + + st::msgFileSize + + st::msgFilePadding.bottom()); + if (!last) { + if (const auto captioned = Get()) { + auto captionw = width + - st::msgPadding.left() + - st::msgPadding.right(); + height += captioned->_caption.countHeight(captionw); + } + } + return { maxWidth(), height }; } @@ -875,9 +909,15 @@ void Document::drawGrouped( RectParts sides, RectParts corners, not_null cacheKey, - not_null cache) const { + not_null cache, + bool last) const { p.translate(geometry.topLeft()); - draw(p, geometry.width(), selection, ms, LayoutMode::Grouped); + draw( + p, + geometry.width(), + selection, + ms, + last ? LayoutMode::GroupedLast : LayoutMode::Grouped); p.translate(-geometry.topLeft()); } @@ -885,9 +925,14 @@ TextState Document::getStateGrouped( const QRect &geometry, RectParts sides, QPoint point, - StateRequest request) const { + StateRequest request, + bool last) const { point -= geometry.topLeft(); - return textState(point, geometry.size(), request, LayoutMode::Grouped); + return textState( + point, + geometry.size(), + request, + last ? LayoutMode::GroupedLast : LayoutMode::Grouped); } bool Document::voiceProgressAnimationCallback(crl::time now) { @@ -952,7 +997,7 @@ void Document::refreshParentId(not_null realParent) { } void Document::parentTextUpdated() { - auto caption = (_parent->media() == this) + auto caption = (_parent->media() == this || _realParent->groupId()) ? createCaption() : Ui::Text::String(); if (!caption.isEmpty()) { @@ -980,7 +1025,7 @@ Ui::Text::String Document::createCaption() { ? DocumentTimestampLinkBase(_data, _realParent->fullId()) : QString(); return File::createCaption( - _parent->data(), + _realParent, timestampLinksDuration, timestampLinkBase); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_document.h b/Telegram/SourceFiles/history/view/media/history_view_document.h index 5f7dc99ec0..c3f8b03c1b 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_document.h +++ b/Telegram/SourceFiles/history/view/media/history_view_document.h @@ -30,6 +30,7 @@ class Document final public: Document( not_null parent, + not_null realParent, not_null document); ~Document(); @@ -61,7 +62,8 @@ public: QMargins bubbleMargins() const override; bool hideForwardedFrom() const override; - QSize sizeForGrouping() const override; + QSize sizeForGroupingOptimal(int maxWidth, bool last) const override; + QSize sizeForGrouping(int width, bool last) const override; void drawGrouped( Painter &p, const QRect &clip, @@ -71,12 +73,14 @@ public: RectParts sides, RectParts corners, not_null cacheKey, - not_null cache) const override; + not_null cache, + bool last) const override; TextState getStateGrouped( const QRect &geometry, RectParts sides, QPoint point, - StateRequest request) const override; + StateRequest request, + bool last) const override; bool voiceProgressAnimationCallback(crl::time now); @@ -102,6 +106,7 @@ private: enum class LayoutMode { Full, Grouped, + GroupedLast, }; void draw( diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp index 43916d11d3..fed2eb9125 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.cpp @@ -884,7 +884,11 @@ bool Gif::fullFeaturedGrouped(RectParts sides) const { return (sides & RectPart::Left) && (sides & RectPart::Right); } -QSize Gif::sizeForGrouping() const { +QSize Gif::sizeForGroupingOptimal(int maxWidth, bool last) const { + return sizeForAspectRatio(); +} + +QSize Gif::sizeForGrouping(int width, bool last) const { return sizeForAspectRatio(); } @@ -897,7 +901,8 @@ void Gif::drawGrouped( RectParts sides, RectParts corners, not_null cacheKey, - not_null cache) const { + not_null cache, + bool last) const { ensureDataMediaCreated(); const auto item = _parent->data(); const auto loaded = dataLoaded(); @@ -1085,7 +1090,8 @@ TextState Gif::getStateGrouped( const QRect &geometry, RectParts sides, QPoint point, - StateRequest request) const { + StateRequest request, + bool last) const { if (!geometry.contains(point)) { return {}; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_gif.h b/Telegram/SourceFiles/history/view/media/history_view_gif.h index e11bf0dbc0..1a1f77f7f7 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_gif.h +++ b/Telegram/SourceFiles/history/view/media/history_view_gif.h @@ -69,7 +69,8 @@ public: } bool fullFeaturedGrouped(RectParts sides) const; - QSize sizeForGrouping() const override; + QSize sizeForGroupingOptimal(int maxWidth, bool last) const override; + QSize sizeForGrouping(int width, bool last) const override; void drawGrouped( Painter &p, const QRect &clip, @@ -79,12 +80,14 @@ public: RectParts sides, RectParts corners, not_null cacheKey, - not_null cache) const override; + not_null cache, + bool last) const override; TextState getStateGrouped( const QRect &geometry, RectParts sides, QPoint point, - StateRequest request) const override; + StateRequest request, + bool last) const override; void stopAnimation() override; void checkAnimation() override; diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.cpp b/Telegram/SourceFiles/history/view/media/history_view_media.cpp index 64eb1ff5dd..5b243babed 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media.cpp @@ -182,7 +182,8 @@ TextState Media::getStateGrouped( const QRect &geometry, RectParts sides, QPoint point, - StateRequest request) const { + StateRequest request, + bool last) const { Unexpected("Grouping method call."); } diff --git a/Telegram/SourceFiles/history/view/media/history_view_media.h b/Telegram/SourceFiles/history/view/media/history_view_media.h index e0a2274307..7d52ec3d51 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media.h @@ -163,7 +163,12 @@ public: virtual void checkAnimation() { } - [[nodiscard]] virtual QSize sizeForGrouping() const { + [[nodiscard]] virtual QSize sizeForGroupingOptimal( + int maxWidth, + bool last) const { + Unexpected("Grouping method call."); + } + [[nodiscard]] virtual QSize sizeForGrouping(int width, bool last) const { Unexpected("Grouping method call."); } virtual void drawGrouped( @@ -175,14 +180,16 @@ public: RectParts sides, RectParts corners, not_null cacheKey, - not_null cache) const { + not_null cache, + bool last) const { Unexpected("Grouping method call."); } [[nodiscard]] virtual TextState getStateGrouped( const QRect &geometry, RectParts sides, QPoint point, - StateRequest request) const; + StateRequest request, + bool last) const; [[nodiscard]] virtual bool animating() const { return false; diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_common.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_common.cpp index 200325885a..5ab2ea3b55 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_common.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_common.cpp @@ -80,7 +80,7 @@ std::unique_ptr CreateAttach( document, webpageUrl); } - return std::make_unique(parent, document); + return std::make_unique(parent, parent->data(), document); } else if (photo) { return std::make_unique( parent, diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp index a756931aaa..b4b449273f 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.cpp @@ -94,8 +94,8 @@ GroupedMedia::~GroupedMedia() { GroupedMedia::Mode GroupedMedia::DetectMode(not_null media) { const auto document = media->document(); - return (document && document->isSong()) - ? Mode::Playlist + return (document && !document->isVideoFile()) + ? Mode::Column : Mode::Grid; } @@ -107,11 +107,20 @@ QSize GroupedMedia::countOptimalSize() { } std::vector sizes; - sizes.reserve(_parts.size()); + const auto partsCount = _parts.size(); + sizes.reserve(partsCount); + auto maxWidth = 0; + if (_mode == Mode::Column) { + for (const auto &part : _parts) { + const auto &media = part.content; + media->initDimensions(); + accumulate_max(maxWidth, media->maxWidth()); + } + } + auto index = 0; for (const auto &part : _parts) { - const auto &media = part.content; - media->initDimensions(); - sizes.push_back(media->sizeForGrouping()); + const auto last = (++index == partsCount); + sizes.push_back(part.content->sizeForGroupingOptimal(maxWidth, last)); } const auto layout = (_mode == Mode::Grid) @@ -123,7 +132,6 @@ QSize GroupedMedia::countOptimalSize() { : LayoutPlaylist(sizes); Assert(layout.size() == _parts.size()); - auto maxWidth = 0; auto minHeight = 0; for (auto i = 0, count = int(layout.size()); i != count; ++i) { const auto &item = layout[i]; @@ -146,42 +154,51 @@ QSize GroupedMedia::countOptimalSize() { QSize GroupedMedia::countCurrentSize(int newWidth) { accumulate_min(newWidth, maxWidth()); auto newHeight = 0; - if (newWidth < st::historyGroupWidthMin) { + if (_mode == Mode::Grid && newWidth < st::historyGroupWidthMin) { return { newWidth, newHeight }; + } else if (_mode == Mode::Column) { + auto index = 0; + auto top = 0; + for (auto &part : _parts) { + const auto last = (++index == _parts.size()); + const auto size = part.content->sizeForGrouping(newWidth, last); + part.geometry = QRect(0, top, newWidth, size.height()); + top += size.height(); + } + newHeight = top; + } else { + const auto initialSpacing = st::historyGroupSkip; + const auto factor = newWidth / float64(maxWidth()); + const auto scale = [&](int value) { + return int(std::round(value * factor)); + }; + const auto spacing = scale(initialSpacing); + for (auto &part : _parts) { + const auto sides = part.sides; + const auto initialGeometry = part.initialGeometry; + const auto needRightSkip = !(sides & RectPart::Right); + const auto needBottomSkip = !(sides & RectPart::Bottom); + const auto initialLeft = initialGeometry.x(); + const auto initialTop = initialGeometry.y(); + const auto initialRight = initialLeft + + initialGeometry.width() + + (needRightSkip ? initialSpacing : 0); + const auto initialBottom = initialTop + + initialGeometry.height() + + (needBottomSkip ? initialSpacing : 0); + const auto left = scale(initialLeft); + const auto top = scale(initialTop); + const auto width = scale(initialRight) + - left + - (needRightSkip ? spacing : 0); + const auto height = scale(initialBottom) + - top + - (needBottomSkip ? spacing : 0); + part.geometry = QRect(left, top, width, height); + + accumulate_max(newHeight, top + height); + } } - - const auto initialSpacing = st::historyGroupSkip; - const auto factor = newWidth / float64(maxWidth()); - const auto scale = [&](int value) { - return int(std::round(value * factor)); - }; - const auto spacing = scale(initialSpacing); - for (auto &part : _parts) { - const auto sides = part.sides; - const auto initialGeometry = part.initialGeometry; - const auto needRightSkip = !(sides & RectPart::Right); - const auto needBottomSkip = !(sides & RectPart::Bottom); - const auto initialLeft = initialGeometry.x(); - const auto initialTop = initialGeometry.y(); - const auto initialRight = initialLeft - + initialGeometry.width() - + (needRightSkip ? initialSpacing : 0); - const auto initialBottom = initialTop - + initialGeometry.height() - + (needBottomSkip ? initialSpacing : 0); - const auto left = scale(initialLeft); - const auto top = scale(initialTop); - const auto width = scale(initialRight) - - left - - (needRightSkip ? spacing : 0); - const auto height = scale(initialBottom) - - top - - (needBottomSkip ? spacing : 0); - part.geometry = QRect(left, top, width, height); - - accumulate_max(newHeight, top + height); - } - if (!_caption.isEmpty()) { const auto captionw = newWidth - st::msgPadding.left() - st::msgPadding.right(); newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw); @@ -223,6 +240,7 @@ void GroupedMedia::draw( : IsGroupItemSelection(selection, i) ? FullSelection : TextSelection(); + const auto last = (i + 1 == count); part.content->drawGrouped( p, clip, @@ -232,7 +250,8 @@ void GroupedMedia::draw( part.sides, cornersFromSides(part.sides), &part.cacheKey, - &part.cache); + &part.cache, + last); } // date @@ -262,13 +281,17 @@ void GroupedMedia::draw( TextState GroupedMedia::getPartState( QPoint point, StateRequest request) const { + auto index = 0; for (const auto &part : _parts) { + ++index; if (part.geometry.contains(point)) { + const auto last = (index == _parts.size()); auto result = part.content->getStateGrouped( part.geometry, part.sides, point, - request); + request, + last); result.itemId = part.item->fullId(); return result; } @@ -372,6 +395,9 @@ auto GroupedMedia::getBubbleSelectionIntervals( last = BubbleSelectionInterval{ newTop, newHeight }; } } + if (IsGroupItemSelection(selection, _parts.size() - 1)) { + result.back().height = height() - result.back().top; + } return result; } @@ -465,6 +491,10 @@ HistoryMessageEdited *GroupedMedia::displayedEditBadge() const { void GroupedMedia::updateNeedBubbleState() { const auto captionItem = [&]() -> HistoryItem* { + if (_mode == Mode::Column) { + const auto last = _parts.back().item.get(); + return last->emptyText() ? nullptr : last; + } auto result = (HistoryItem*)nullptr; for (const auto &part : _parts) { if (!part.item->emptyText()) { @@ -519,7 +549,7 @@ bool GroupedMedia::needsBubble() const { } bool GroupedMedia::computeNeedBubble() const { - if (!_caption.isEmpty() || _mode == Mode::Playlist) { + if (!_caption.isEmpty() || _mode == Mode::Column) { return true; } if (const auto item = _parent->data()) { @@ -537,7 +567,7 @@ bool GroupedMedia::computeNeedBubble() const { } bool GroupedMedia::needInfoDisplay() const { - return (_mode != Mode::Playlist) + return (_mode != Mode::Column) && (_parent->data()->id < 0 || _parent->isUnderCursor() || _parent->isLastAndSelfMessage()); diff --git a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h index 3e1185c42a..29b1eaedf8 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h +++ b/Telegram/SourceFiles/history/view/media/history_view_media_grouped.h @@ -86,7 +86,7 @@ public: void updateNeedBubbleState() override; bool needsBubble() const override; bool customInfoLayout() const override { - return _caption.isEmpty() && (_mode != Mode::Playlist); + return _caption.isEmpty() && (_mode != Mode::Column); } bool allowsFastShare() const override { return true; @@ -102,7 +102,7 @@ public: private: enum class Mode : char { Grid, - Playlist, + Column, }; struct Part { Part( diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp index 87f7e7bf96..7025dea4e9 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.cpp +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.cpp @@ -465,12 +465,16 @@ TextState Photo::textState(QPoint point, StateRequest request) const { return result; } -QSize Photo::sizeForGrouping() const { +QSize Photo::sizeForGroupingOptimal(int maxWidth, bool last) const { const auto width = _data->width(); const auto height = _data->height(); return { std::max(width, 1), std::max(height, 1) }; } +QSize Photo::sizeForGrouping(int width, bool last) const { + return sizeForGroupingOptimal(width, last); +} + void Photo::drawGrouped( Painter &p, const QRect &clip, @@ -480,7 +484,8 @@ void Photo::drawGrouped( RectParts sides, RectParts corners, not_null cacheKey, - not_null cache) const { + not_null cache, + bool last) const { ensureDataMediaCreated(); _dataMedia->automaticLoad(_realParent->fullId(), _parent->data()); @@ -580,7 +585,8 @@ TextState Photo::getStateGrouped( const QRect &geometry, RectParts sides, QPoint point, - StateRequest request) const { + StateRequest request, + bool last) const { if (!geometry.contains(point)) { return {}; } diff --git a/Telegram/SourceFiles/history/view/media/history_view_photo.h b/Telegram/SourceFiles/history/view/media/history_view_photo.h index b35880df02..418c192849 100644 --- a/Telegram/SourceFiles/history/view/media/history_view_photo.h +++ b/Telegram/SourceFiles/history/view/media/history_view_photo.h @@ -24,7 +24,7 @@ struct Information; namespace HistoryView { -class Photo : public File { +class Photo final : public File { public: Photo( not_null parent, @@ -58,7 +58,8 @@ public: return _data; } - QSize sizeForGrouping() const override; + QSize sizeForGroupingOptimal(int maxWidth, bool last) const override; + QSize sizeForGrouping(int width, bool last) const override; void drawGrouped( Painter &p, const QRect &clip, @@ -68,12 +69,14 @@ public: RectParts sides, RectParts corners, not_null cacheKey, - not_null cache) const override; + not_null cache, + bool last) const override; TextState getStateGrouped( const QRect &geometry, RectParts sides, QPoint point, - StateRequest request) const override; + StateRequest request, + bool last) const override; TextWithEntities getCaption() const override { return _caption.toTextWithEntities();