diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index d82446828..0e0ab10d3 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -819,6 +819,8 @@ PRIVATE overview/overview_layout.cpp overview/overview_layout.h overview/overview_layout_delegate.h + overview/overview_mosaic_layout.cpp + overview/overview_mosaic_layout.h passport/passport_encryption.cpp passport/passport_encryption.h passport/passport_form_controller.cpp diff --git a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp index 93e1fb405..669d03e95 100644 --- a/Telegram/SourceFiles/info/media/info_media_list_widget.cpp +++ b/Telegram/SourceFiles/info/media/info_media_list_widget.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "info/info_controller.h" #include "overview/overview_layout.h" +#include "overview/overview_mosaic_layout.h" #include "data/data_media_types.h" #include "data/data_photo.h" #include "data/data_document.h" @@ -101,6 +102,8 @@ public: } bool addItem(not_null item); + void finishSection(); + bool empty() const { return _items.empty(); } @@ -168,7 +171,7 @@ private: not_null item, const Context &context) const; - int recountHeight() const; + int recountHeight(); void refreshHeight(); Type _type = Type::Photo; @@ -183,6 +186,8 @@ private: int _top = 0; int _height = 0; + Overview::Layout::MosaicLayout _mosaic; + }; bool ListWidget::IsAfter( @@ -231,6 +236,15 @@ bool ListWidget::Section::addItem(not_null item) { return false; } +void ListWidget::Section::finishSection() { + if (_type == Type::GIF) { + _mosaic.setOffset(st::infoMediaSkip, headerHeight()); + _mosaic.setRightSkip(st::infoMediaSkip); + const auto items = ranges::views::values(_items) | ranges::to_vector; + _mosaic.addItems(items); + } +} + void ListWidget::Section::setHeader(not_null item) { auto text = [&] { auto date = item->dateTime().date(); @@ -298,6 +312,9 @@ bool ListWidget::Section::removeItem(UniversalMsgId universalId) { QRect ListWidget::Section::findItemRect( not_null item) const { auto position = item->position(); + if (!_mosaic.empty()) { + return _mosaic.findRect(position); + } auto top = position / _itemsInRow; auto indexInRow = position % _itemsInRow; auto left = _itemsLeft @@ -314,6 +331,17 @@ auto ListWidget::Section::completeResult( auto ListWidget::Section::findItemByPoint( QPoint point) const -> FoundItem { Expects(!_items.empty()); + if (!_mosaic.empty()) { + const auto found = _mosaic.findByPoint(point); + if (found.item) { + const auto rect = findItemRect(found.item); + return { found.item, rect, rect.contains(point) }; + } else { + auto item = (_items.end() - 1)->second; + const auto rect = findItemRect(item); + return { item, rect, rect.contains(point) }; + } + } auto itemIt = findItemAfterTop(point.y()); if (itemIt == _items.end()) { --itemIt; @@ -362,6 +390,7 @@ auto ListWidget::Section::findItemDetails(not_null item) const auto ListWidget::Section::findItemAfterTop( int top) -> Items::iterator { + Expects(_mosaic.empty()); return ranges::lower_bound( _items, top, @@ -374,6 +403,7 @@ auto ListWidget::Section::findItemAfterTop( auto ListWidget::Section::findItemAfterTop( int top) const -> Items::const_iterator { + Expects(_mosaic.empty()); return ranges::lower_bound( _items, top, @@ -387,6 +417,7 @@ auto ListWidget::Section::findItemAfterTop( auto ListWidget::Section::findItemAfterBottom( Items::const_iterator from, int bottom) const -> Items::const_iterator { + Expects(_mosaic.empty()); return ranges::lower_bound( from, _items.end(), @@ -416,6 +447,20 @@ void ListWidget::Section::paint( auto localContext = context.layoutContext; localContext.isAfterDate = (header > 0); + if (!_mosaic.empty()) { + auto paintItem = [&](const not_null item, QPoint point) { + p.translate(point.x(), point.y()); + item->paint( + p, + clip.translated(-point), + itemSelection(item, context), + &localContext); + p.translate(-point.x(), -point.y()); + }; + _mosaic.paint(std::move(paintItem), clip); + return; + } + auto fromIt = findItemAfterTop(clip.y()); auto tillIt = findItemAfterBottom( fromIt, @@ -508,7 +553,6 @@ void ListWidget::Section::resizeToWidth(int newWidth) { }; switch (_type) { case Type::Photo: - case Type::GIF: case Type::Video: case Type::RoundFile: { _itemsLeft = st::infoMediaSkip; @@ -522,6 +566,10 @@ void ListWidget::Section::resizeToWidth(int newWidth) { } } break; + case Type::GIF: { + _mosaic.setFullWidth(newWidth - st::infoMediaSkip); + } break; + case Type::RoundVoiceFile: case Type::MusicFile: resizeOneColumn(0, newWidth); @@ -562,12 +610,11 @@ int ListWidget::Section::MinItemHeight(Type type, int width) { Unexpected("Type in ListWidget::Section::MinItemHeight()"); } -int ListWidget::Section::recountHeight() const { +int ListWidget::Section::recountHeight() { auto result = headerHeight(); switch (_type) { case Type::Photo: - case Type::GIF: case Type::Video: case Type::RoundFile: { auto itemHeight = _itemWidth + st::infoMediaSkip; @@ -588,6 +635,10 @@ int ListWidget::Section::recountHeight() const { } } break; + case Type::GIF: { + return _mosaic.countDesiredHeight(0) + result; + } break; + case Type::RoundVoiceFile: case Type::File: case Type::MusicFile: @@ -619,7 +670,9 @@ ListWidget::ListWidget( , _dateBadge(DateBadge{ .check = SingleQueuedInvokation([=] { scrollDateCheck(); }), .hideTimer = base::Timer([=] { scrollDateHide(); }), - .goodType = (_type == Type::Photo || _type == Type::Video), + .goodType = (_type == Type::Photo + || _type == Type::Video + || _type == Type::GIF), }) { setMouseTracking(true); start(); @@ -981,6 +1034,10 @@ std::unique_ptr ListWidget::createLayout( } return nullptr; case Type::GIF: + if (const auto file = getFile()) { + return std::make_unique(this, item, file); + } + return nullptr; case Type::Video: if (const auto file = getFile()) { return std::make_unique