mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-07-27 07:52:57 +02:00
Send files grouped in albums, show captions.
This commit is contained in:
parent
86612f0a67
commit
85d08c8f52
18 changed files with 292 additions and 99 deletions
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -187,6 +187,7 @@ private:
|
|||
object_ptr<Ui::ScrollArea> _scroll;
|
||||
QPointer<Ui::VerticalLayout> _inner;
|
||||
std::vector<Block> _blocks;
|
||||
Fn<void()> _whenReadySend;
|
||||
bool _preparing = false;
|
||||
|
||||
int _lastScrollTop = 0;
|
||||
|
|
|
@ -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<HistoryView::Media> MediaFile::createView(
|
|||
message,
|
||||
_document);
|
||||
}
|
||||
return std::make_unique<HistoryView::Document>(message, _document);
|
||||
return std::make_unique<HistoryView::Document>(
|
||||
message,
|
||||
realParent,
|
||||
_document);
|
||||
}
|
||||
|
||||
MediaContact::MediaContact(
|
||||
|
|
|
@ -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<SendingAlbum>()
|
||||
: 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<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(
|
||||
QImage &&image,
|
||||
QByteArray &&content,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -65,8 +65,9 @@ constexpr auto kAudioVoiceMsgUpdateView = crl::time(100);
|
|||
|
||||
Document::Document(
|
||||
not_null<Element*> parent,
|
||||
not_null<HistoryItem*> realParent,
|
||||
not_null<DocumentData*> 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<HistoryDocumentCaptioned>();
|
||||
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<HistoryDocumentCaptioned>()) {
|
||||
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<HistoryDocumentCaptioned>()) {
|
||||
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<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 = 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 };
|
||||
}
|
||||
|
||||
|
@ -875,9 +909,15 @@ void Document::drawGrouped(
|
|||
RectParts sides,
|
||||
RectParts corners,
|
||||
not_null<uint64*> cacheKey,
|
||||
not_null<QPixmap*> cache) const {
|
||||
not_null<QPixmap*> 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<HistoryItem*> 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);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ class Document final
|
|||
public:
|
||||
Document(
|
||||
not_null<Element*> parent,
|
||||
not_null<HistoryItem*> realParent,
|
||||
not_null<DocumentData*> 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<uint64*> cacheKey,
|
||||
not_null<QPixmap*> cache) const override;
|
||||
not_null<QPixmap*> 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(
|
||||
|
|
|
@ -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<uint64*> cacheKey,
|
||||
not_null<QPixmap*> cache) const {
|
||||
not_null<QPixmap*> 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 {};
|
||||
}
|
||||
|
|
|
@ -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<uint64*> cacheKey,
|
||||
not_null<QPixmap*> cache) const override;
|
||||
not_null<QPixmap*> 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;
|
||||
|
|
|
@ -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.");
|
||||
}
|
||||
|
||||
|
|
|
@ -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<uint64*> cacheKey,
|
||||
not_null<QPixmap*> cache) const {
|
||||
not_null<QPixmap*> 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;
|
||||
|
|
|
@ -80,7 +80,7 @@ std::unique_ptr<Media> CreateAttach(
|
|||
document,
|
||||
webpageUrl);
|
||||
}
|
||||
return std::make_unique<Document>(parent, document);
|
||||
return std::make_unique<Document>(parent, parent->data(), document);
|
||||
} else if (photo) {
|
||||
return std::make_unique<Photo>(
|
||||
parent,
|
||||
|
|
|
@ -94,8 +94,8 @@ GroupedMedia::~GroupedMedia() {
|
|||
|
||||
GroupedMedia::Mode GroupedMedia::DetectMode(not_null<Data::Media*> 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<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) {
|
||||
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());
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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<uint64*> cacheKey,
|
||||
not_null<QPixmap*> cache) const {
|
||||
not_null<QPixmap*> 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 {};
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ struct Information;
|
|||
|
||||
namespace HistoryView {
|
||||
|
||||
class Photo : public File {
|
||||
class Photo final : public File {
|
||||
public:
|
||||
Photo(
|
||||
not_null<Element*> 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<uint64*> cacheKey,
|
||||
not_null<QPixmap*> cache) const override;
|
||||
not_null<QPixmap*> 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();
|
||||
|
|
Loading…
Add table
Reference in a new issue