Send files grouped in albums, show captions.

This commit is contained in:
John Preston 2020-10-16 20:48:27 +03:00
parent 86612f0a67
commit 85d08c8f52
18 changed files with 292 additions and 99 deletions

View file

@ -4232,7 +4232,9 @@ void ApiWrap::sendFiles(
if (album) { if (album) {
switch (file.type) { switch (file.type) {
case Ui::PreparedFile::AlbumType::Photo: case Ui::PreparedFile::AlbumType::Photo:
type = SendMediaType::Photo; type = (type == SendMediaType::File)
? type
: SendMediaType::Photo;
break; break;
case Ui::PreparedFile::AlbumType::Video: case Ui::PreparedFile::AlbumType::Video:
case Ui::PreparedFile::AlbumType::File: case Ui::PreparedFile::AlbumType::File:

View file

@ -763,6 +763,9 @@ void SendFilesBox::addPreparedAsyncFile(Ui::PreparedFile &&file) {
if (_list.files.size() > count) { if (_list.files.size() > count) {
refreshAllAfterChanges(count); refreshAllAfterChanges(count);
} }
if (!_preparing && _whenReadySend) {
_whenReadySend();
}
} }
void SendFilesBox::addFile(Ui::PreparedFile &&file) { void SendFilesBox::addFile(Ui::PreparedFile &&file) {
@ -893,6 +896,12 @@ void SendFilesBox::send(
&& !options.scheduled) { && !options.scheduled) {
return sendScheduled(); return sendScheduled();
} }
if (_preparing) {
_whenReadySend = [=] {
send(options, ctrlShiftEnter);
};
return;
}
auto way = _sendWay.current(); auto way = _sendWay.current();
auto oldWay = Core::App().settings().sendFilesWay(); auto oldWay = Core::App().settings().sendFilesWay();

View file

@ -187,6 +187,7 @@ private:
object_ptr<Ui::ScrollArea> _scroll; object_ptr<Ui::ScrollArea> _scroll;
QPointer<Ui::VerticalLayout> _inner; QPointer<Ui::VerticalLayout> _inner;
std::vector<Block> _blocks; std::vector<Block> _blocks;
Fn<void()> _whenReadySend;
bool _preparing = false; bool _preparing = false;
int _lastScrollTop = 0; int _lastScrollTop = 0;

View file

@ -479,7 +479,14 @@ Storage::SharedMediaTypesMask MediaFile::sharedMediaTypes() const {
} }
bool MediaFile::canBeGrouped() 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 { bool MediaFile::hasReplyPreview() const {
@ -704,7 +711,10 @@ std::unique_ptr<HistoryView::Media> MediaFile::createView(
message, message,
_document); _document);
} }
return std::make_unique<HistoryView::Document>(message, _document); return std::make_unique<HistoryView::Document>(
message,
realParent,
_document);
} }
MediaContact::MediaContact( MediaContact::MediaContact(

View file

@ -4177,22 +4177,12 @@ bool HistoryWidget::confirmSendingFiles(
TextWithTags &&caption, TextWithTags &&caption,
Api::SendOptions options, Api::SendOptions options,
bool ctrlShiftEnter) { bool ctrlShiftEnter) {
if (showSendingFilesError(list)) { sendingFilesConfirmed(
return;
}
const auto type = way.sendImagesAsPhotos()
? SendMediaType::Photo
: SendMediaType::File;
const auto album = way.groupMediaInAlbums() // #TODO files
? std::make_shared<SendingAlbum>()
: nullptr;
uploadFilesAfterConfirmation(
std::move(list), std::move(list),
type, way,
std::move(caption), std::move(caption),
replyToId(),
options, options,
album); ctrlShiftEnter);
})); }));
box->setCancelledCallback(crl::guard(this, [=] { box->setCancelledCallback(crl::guard(this, [=] {
_field->setTextWithTags(text); _field->setTextWithTags(text);
@ -4214,6 +4204,73 @@ bool HistoryWidget::confirmSendingFiles(
return true; 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<SendingAlbum>();
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( bool HistoryWidget::confirmSendingFiles(
QImage &&image, QImage &&image,
QByteArray &&content, QByteArray &&content,

View file

@ -66,6 +66,7 @@ class LinkButton;
class RoundButton; class RoundButton;
class PinnedBar; class PinnedBar;
struct PreparedList; struct PreparedList;
class SendFilesWay;
namespace Toast { namespace Toast {
class Instance; class Instance;
} // namespace Toast } // namespace Toast
@ -425,6 +426,13 @@ private:
const QString &insertTextOnCancel = QString()); const QString &insertTextOnCancel = QString());
bool showSendingFilesError(const Ui::PreparedList &list) const; 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 uploadFile(const QByteArray &fileContent, SendMediaType type);
void uploadFilesAfterConfirmation( void uploadFilesAfterConfirmation(

View file

@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history.h" #include "history/history.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "styles/style_history.h" #include "styles/style_chat.h"
namespace HistoryView { namespace HistoryView {
namespace { namespace {

View file

@ -65,8 +65,9 @@ constexpr auto kAudioVoiceMsgUpdateView = crl::time(100);
Document::Document( Document::Document(
not_null<Element*> parent, not_null<Element*> parent,
not_null<HistoryItem*> realParent,
not_null<DocumentData*> document) not_null<DocumentData*> document)
: File(parent, parent->data()) : File(parent, realParent)
, _data(document) { , _data(document) {
const auto item = parent->data(); const auto item = parent->data();
auto caption = createCaption(); auto caption = createCaption();
@ -152,7 +153,7 @@ QSize Document::countOptimalSize() {
const auto item = _parent->data(); const auto item = _parent->data();
auto captioned = Get<HistoryDocumentCaptioned>(); auto captioned = Get<HistoryDocumentCaptioned>();
if (_parent->media() != this) { if (_parent->media() != this && !_realParent->groupId()) {
if (captioned) { if (captioned) {
RemoveComponents(HistoryDocumentCaptioned::Bit()); RemoveComponents(HistoryDocumentCaptioned::Bit());
captioned = nullptr; captioned = nullptr;
@ -508,7 +509,7 @@ void Document::draw(
} }
} }
if (mode == LayoutMode::Full) { if (mode != LayoutMode::GroupedLast) {
if (auto captioned = Get<HistoryDocumentCaptioned>()) { if (auto captioned = Get<HistoryDocumentCaptioned>()) {
p.setPen(outbg ? (selected ? st::historyTextOutFgSelected : st::historyTextOutFg) : (selected ? st::historyTextInFgSelected : st::historyTextInFg)); 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); 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(); auto painth = layout.height();
if (mode == LayoutMode::Full) { if (mode != LayoutMode::GroupedLast) {
if (const auto captioned = Get<HistoryDocumentCaptioned>()) { if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
if (point.y() >= bottom) { if (point.y() >= bottom) {
result = TextState(_parent, captioned->_caption.getState( result = TextState(_parent, captioned->_caption.getState(
@ -859,10 +860,43 @@ bool Document::hideForwardedFrom() const {
return _data->isSong(); return _data->isSong();
} }
QSize Document::sizeForGrouping() const { QSize Document::sizeForGroupingOptimal(int maxWidth, bool last) const {
const auto height = st::msgFilePadding.top() auto height = Has<HistoryDocumentThumbed>()
? (st::msgFileThumbPadding.top()
+ st::msgFileThumbSize
+ st::msgFileThumbPadding.bottom())
: (st::msgFilePadding.top()
+ st::msgFileSize + st::msgFileSize
+ st::msgFilePadding.bottom(); + st::msgFilePadding.bottom());
if (!last) {
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
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<HistoryDocumentThumbed>()
? (st::msgFileThumbPadding.top()
+ st::msgFileThumbSize
+ st::msgFileThumbPadding.bottom())
: (st::msgFilePadding.top()
+ st::msgFileSize
+ st::msgFilePadding.bottom());
if (!last) {
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
auto captionw = width
- st::msgPadding.left()
- st::msgPadding.right();
height += captioned->_caption.countHeight(captionw);
}
}
return { maxWidth(), height }; return { maxWidth(), height };
} }
@ -875,9 +909,15 @@ void Document::drawGrouped(
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const { not_null<QPixmap*> cache,
bool last) const {
p.translate(geometry.topLeft()); 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()); p.translate(-geometry.topLeft());
} }
@ -885,9 +925,14 @@ TextState Document::getStateGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
QPoint point, QPoint point,
StateRequest request) const { StateRequest request,
bool last) const {
point -= geometry.topLeft(); 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) { bool Document::voiceProgressAnimationCallback(crl::time now) {
@ -952,7 +997,7 @@ void Document::refreshParentId(not_null<HistoryItem*> realParent) {
} }
void Document::parentTextUpdated() { void Document::parentTextUpdated() {
auto caption = (_parent->media() == this) auto caption = (_parent->media() == this || _realParent->groupId())
? createCaption() ? createCaption()
: Ui::Text::String(); : Ui::Text::String();
if (!caption.isEmpty()) { if (!caption.isEmpty()) {
@ -980,7 +1025,7 @@ Ui::Text::String Document::createCaption() {
? DocumentTimestampLinkBase(_data, _realParent->fullId()) ? DocumentTimestampLinkBase(_data, _realParent->fullId())
: QString(); : QString();
return File::createCaption( return File::createCaption(
_parent->data(), _realParent,
timestampLinksDuration, timestampLinksDuration,
timestampLinkBase); timestampLinkBase);
} }

View file

@ -30,6 +30,7 @@ class Document final
public: public:
Document( Document(
not_null<Element*> parent, not_null<Element*> parent,
not_null<HistoryItem*> realParent,
not_null<DocumentData*> document); not_null<DocumentData*> document);
~Document(); ~Document();
@ -61,7 +62,8 @@ public:
QMargins bubbleMargins() const override; QMargins bubbleMargins() const override;
bool hideForwardedFrom() 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( void drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const QRect &clip,
@ -71,12 +73,14 @@ public:
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const override; not_null<QPixmap*> cache,
bool last) const override;
TextState getStateGrouped( TextState getStateGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
QPoint point, QPoint point,
StateRequest request) const override; StateRequest request,
bool last) const override;
bool voiceProgressAnimationCallback(crl::time now); bool voiceProgressAnimationCallback(crl::time now);
@ -102,6 +106,7 @@ private:
enum class LayoutMode { enum class LayoutMode {
Full, Full,
Grouped, Grouped,
GroupedLast,
}; };
void draw( void draw(

View file

@ -884,7 +884,11 @@ bool Gif::fullFeaturedGrouped(RectParts sides) const {
return (sides & RectPart::Left) && (sides & RectPart::Right); 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(); return sizeForAspectRatio();
} }
@ -897,7 +901,8 @@ void Gif::drawGrouped(
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const { not_null<QPixmap*> cache,
bool last) const {
ensureDataMediaCreated(); ensureDataMediaCreated();
const auto item = _parent->data(); const auto item = _parent->data();
const auto loaded = dataLoaded(); const auto loaded = dataLoaded();
@ -1085,7 +1090,8 @@ TextState Gif::getStateGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
QPoint point, QPoint point,
StateRequest request) const { StateRequest request,
bool last) const {
if (!geometry.contains(point)) { if (!geometry.contains(point)) {
return {}; return {};
} }

View file

@ -69,7 +69,8 @@ public:
} }
bool fullFeaturedGrouped(RectParts sides) const; 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( void drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const QRect &clip,
@ -79,12 +80,14 @@ public:
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const override; not_null<QPixmap*> cache,
bool last) const override;
TextState getStateGrouped( TextState getStateGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
QPoint point, QPoint point,
StateRequest request) const override; StateRequest request,
bool last) const override;
void stopAnimation() override; void stopAnimation() override;
void checkAnimation() override; void checkAnimation() override;

View file

@ -182,7 +182,8 @@ TextState Media::getStateGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
QPoint point, QPoint point,
StateRequest request) const { StateRequest request,
bool last) const {
Unexpected("Grouping method call."); Unexpected("Grouping method call.");
} }

View file

@ -163,7 +163,12 @@ public:
virtual void checkAnimation() { 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."); Unexpected("Grouping method call.");
} }
virtual void drawGrouped( virtual void drawGrouped(
@ -175,14 +180,16 @@ public:
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const { not_null<QPixmap*> cache,
bool last) const {
Unexpected("Grouping method call."); Unexpected("Grouping method call.");
} }
[[nodiscard]] virtual TextState getStateGrouped( [[nodiscard]] virtual TextState getStateGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
QPoint point, QPoint point,
StateRequest request) const; StateRequest request,
bool last) const;
[[nodiscard]] virtual bool animating() const { [[nodiscard]] virtual bool animating() const {
return false; return false;

View file

@ -80,7 +80,7 @@ std::unique_ptr<Media> CreateAttach(
document, document,
webpageUrl); webpageUrl);
} }
return std::make_unique<Document>(parent, document); return std::make_unique<Document>(parent, parent->data(), document);
} else if (photo) { } else if (photo) {
return std::make_unique<Photo>( return std::make_unique<Photo>(
parent, parent,

View file

@ -94,8 +94,8 @@ GroupedMedia::~GroupedMedia() {
GroupedMedia::Mode GroupedMedia::DetectMode(not_null<Data::Media*> media) { GroupedMedia::Mode GroupedMedia::DetectMode(not_null<Data::Media*> media) {
const auto document = media->document(); const auto document = media->document();
return (document && document->isSong()) return (document && !document->isVideoFile())
? Mode::Playlist ? Mode::Column
: Mode::Grid; : Mode::Grid;
} }
@ -107,11 +107,20 @@ QSize GroupedMedia::countOptimalSize() {
} }
std::vector<QSize> sizes; std::vector<QSize> 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) { for (const auto &part : _parts) {
const auto &media = part.content; const auto &media = part.content;
media->initDimensions(); media->initDimensions();
sizes.push_back(media->sizeForGrouping()); accumulate_max(maxWidth, media->maxWidth());
}
}
auto index = 0;
for (const auto &part : _parts) {
const auto last = (++index == partsCount);
sizes.push_back(part.content->sizeForGroupingOptimal(maxWidth, last));
} }
const auto layout = (_mode == Mode::Grid) const auto layout = (_mode == Mode::Grid)
@ -123,7 +132,6 @@ QSize GroupedMedia::countOptimalSize() {
: LayoutPlaylist(sizes); : LayoutPlaylist(sizes);
Assert(layout.size() == _parts.size()); Assert(layout.size() == _parts.size());
auto maxWidth = 0;
auto minHeight = 0; auto minHeight = 0;
for (auto i = 0, count = int(layout.size()); i != count; ++i) { for (auto i = 0, count = int(layout.size()); i != count; ++i) {
const auto &item = layout[i]; const auto &item = layout[i];
@ -146,10 +154,19 @@ QSize GroupedMedia::countOptimalSize() {
QSize GroupedMedia::countCurrentSize(int newWidth) { QSize GroupedMedia::countCurrentSize(int newWidth) {
accumulate_min(newWidth, maxWidth()); accumulate_min(newWidth, maxWidth());
auto newHeight = 0; auto newHeight = 0;
if (newWidth < st::historyGroupWidthMin) { if (_mode == Mode::Grid && newWidth < st::historyGroupWidthMin) {
return { newWidth, newHeight }; 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 initialSpacing = st::historyGroupSkip;
const auto factor = newWidth / float64(maxWidth()); const auto factor = newWidth / float64(maxWidth());
const auto scale = [&](int value) { const auto scale = [&](int value) {
@ -181,7 +198,7 @@ QSize GroupedMedia::countCurrentSize(int newWidth) {
accumulate_max(newHeight, top + height); accumulate_max(newHeight, top + height);
} }
}
if (!_caption.isEmpty()) { if (!_caption.isEmpty()) {
const auto captionw = newWidth - st::msgPadding.left() - st::msgPadding.right(); const auto captionw = newWidth - st::msgPadding.left() - st::msgPadding.right();
newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw); newHeight += st::mediaCaptionSkip + _caption.countHeight(captionw);
@ -223,6 +240,7 @@ void GroupedMedia::draw(
: IsGroupItemSelection(selection, i) : IsGroupItemSelection(selection, i)
? FullSelection ? FullSelection
: TextSelection(); : TextSelection();
const auto last = (i + 1 == count);
part.content->drawGrouped( part.content->drawGrouped(
p, p,
clip, clip,
@ -232,7 +250,8 @@ void GroupedMedia::draw(
part.sides, part.sides,
cornersFromSides(part.sides), cornersFromSides(part.sides),
&part.cacheKey, &part.cacheKey,
&part.cache); &part.cache,
last);
} }
// date // date
@ -262,13 +281,17 @@ void GroupedMedia::draw(
TextState GroupedMedia::getPartState( TextState GroupedMedia::getPartState(
QPoint point, QPoint point,
StateRequest request) const { StateRequest request) const {
auto index = 0;
for (const auto &part : _parts) { for (const auto &part : _parts) {
++index;
if (part.geometry.contains(point)) { if (part.geometry.contains(point)) {
const auto last = (index == _parts.size());
auto result = part.content->getStateGrouped( auto result = part.content->getStateGrouped(
part.geometry, part.geometry,
part.sides, part.sides,
point, point,
request); request,
last);
result.itemId = part.item->fullId(); result.itemId = part.item->fullId();
return result; return result;
} }
@ -372,6 +395,9 @@ auto GroupedMedia::getBubbleSelectionIntervals(
last = BubbleSelectionInterval{ newTop, newHeight }; last = BubbleSelectionInterval{ newTop, newHeight };
} }
} }
if (IsGroupItemSelection(selection, _parts.size() - 1)) {
result.back().height = height() - result.back().top;
}
return result; return result;
} }
@ -465,6 +491,10 @@ HistoryMessageEdited *GroupedMedia::displayedEditBadge() const {
void GroupedMedia::updateNeedBubbleState() { void GroupedMedia::updateNeedBubbleState() {
const auto captionItem = [&]() -> HistoryItem* { const auto captionItem = [&]() -> HistoryItem* {
if (_mode == Mode::Column) {
const auto last = _parts.back().item.get();
return last->emptyText() ? nullptr : last;
}
auto result = (HistoryItem*)nullptr; auto result = (HistoryItem*)nullptr;
for (const auto &part : _parts) { for (const auto &part : _parts) {
if (!part.item->emptyText()) { if (!part.item->emptyText()) {
@ -519,7 +549,7 @@ bool GroupedMedia::needsBubble() const {
} }
bool GroupedMedia::computeNeedBubble() const { bool GroupedMedia::computeNeedBubble() const {
if (!_caption.isEmpty() || _mode == Mode::Playlist) { if (!_caption.isEmpty() || _mode == Mode::Column) {
return true; return true;
} }
if (const auto item = _parent->data()) { if (const auto item = _parent->data()) {
@ -537,7 +567,7 @@ bool GroupedMedia::computeNeedBubble() const {
} }
bool GroupedMedia::needInfoDisplay() const { bool GroupedMedia::needInfoDisplay() const {
return (_mode != Mode::Playlist) return (_mode != Mode::Column)
&& (_parent->data()->id < 0 && (_parent->data()->id < 0
|| _parent->isUnderCursor() || _parent->isUnderCursor()
|| _parent->isLastAndSelfMessage()); || _parent->isLastAndSelfMessage());

View file

@ -86,7 +86,7 @@ public:
void updateNeedBubbleState() override; void updateNeedBubbleState() override;
bool needsBubble() const override; bool needsBubble() const override;
bool customInfoLayout() const override { bool customInfoLayout() const override {
return _caption.isEmpty() && (_mode != Mode::Playlist); return _caption.isEmpty() && (_mode != Mode::Column);
} }
bool allowsFastShare() const override { bool allowsFastShare() const override {
return true; return true;
@ -102,7 +102,7 @@ public:
private: private:
enum class Mode : char { enum class Mode : char {
Grid, Grid,
Playlist, Column,
}; };
struct Part { struct Part {
Part( Part(

View file

@ -465,12 +465,16 @@ TextState Photo::textState(QPoint point, StateRequest request) const {
return result; return result;
} }
QSize Photo::sizeForGrouping() const { QSize Photo::sizeForGroupingOptimal(int maxWidth, bool last) const {
const auto width = _data->width(); const auto width = _data->width();
const auto height = _data->height(); const auto height = _data->height();
return { std::max(width, 1), std::max(height, 1) }; return { std::max(width, 1), std::max(height, 1) };
} }
QSize Photo::sizeForGrouping(int width, bool last) const {
return sizeForGroupingOptimal(width, last);
}
void Photo::drawGrouped( void Photo::drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const QRect &clip,
@ -480,7 +484,8 @@ void Photo::drawGrouped(
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const { not_null<QPixmap*> cache,
bool last) const {
ensureDataMediaCreated(); ensureDataMediaCreated();
_dataMedia->automaticLoad(_realParent->fullId(), _parent->data()); _dataMedia->automaticLoad(_realParent->fullId(), _parent->data());
@ -580,7 +585,8 @@ TextState Photo::getStateGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
QPoint point, QPoint point,
StateRequest request) const { StateRequest request,
bool last) const {
if (!geometry.contains(point)) { if (!geometry.contains(point)) {
return {}; return {};
} }

View file

@ -24,7 +24,7 @@ struct Information;
namespace HistoryView { namespace HistoryView {
class Photo : public File { class Photo final : public File {
public: public:
Photo( Photo(
not_null<Element*> parent, not_null<Element*> parent,
@ -58,7 +58,8 @@ public:
return _data; return _data;
} }
QSize sizeForGrouping() const override; QSize sizeForGroupingOptimal(int maxWidth, bool last) const override;
QSize sizeForGrouping(int width, bool last) const override;
void drawGrouped( void drawGrouped(
Painter &p, Painter &p,
const QRect &clip, const QRect &clip,
@ -68,12 +69,14 @@ public:
RectParts sides, RectParts sides,
RectParts corners, RectParts corners,
not_null<uint64*> cacheKey, not_null<uint64*> cacheKey,
not_null<QPixmap*> cache) const override; not_null<QPixmap*> cache,
bool last) const override;
TextState getStateGrouped( TextState getStateGrouped(
const QRect &geometry, const QRect &geometry,
RectParts sides, RectParts sides,
QPoint point, QPoint point,
StateRequest request) const override; StateRequest request,
bool last) const override;
TextWithEntities getCaption() const override { TextWithEntities getCaption() const override {
return _caption.toTextWithEntities(); return _caption.toTextWithEntities();