From bf31722931b6cae2ac56bc52de53679c98ef19fd Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 24 Oct 2018 17:09:31 +0400 Subject: [PATCH] Show collage/slideshow as an album in MediaView. --- .../media/view/media_view_group_thumbs.cpp | 60 +++++++- .../media/view/media_view_group_thumbs.h | 30 +++- Telegram/SourceFiles/mediaview.cpp | 145 ++++++++++++++++-- Telegram/SourceFiles/mediaview.h | 10 ++ 4 files changed, 231 insertions(+), 14 deletions(-) diff --git a/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp b/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp index 0c5dae0ab3..bccb729f47 100644 --- a/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp +++ b/Telegram/SourceFiles/media/view/media_view_group_thumbs.cpp @@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_document.h" #include "data/data_media_types.h" #include "data/data_session.h" +#include "data/data_web_page.h" #include "history/history.h" #include "history/history_media.h" #include "ui/image/image.h" @@ -38,11 +39,17 @@ Data::FileOrigin ComputeFileOrigin(const Key &key, const Context &context) { return peerIsUser(peerId) ? Data::FileOriginUserPhoto(peerToUser(peerId), photoId) : Data::FileOrigin(Data::FileOriginPeerPhoto(peerId)); - }, [&](auto&&) { + }, [](auto&&) { return Data::FileOrigin(); }); }, [](FullMsgId itemId) { return Data::FileOrigin(itemId); + }, [&](GroupThumbs::CollageKey) { + return context.match([](const GroupThumbs::CollageSlice &slice) { + return Data::FileOrigin(slice.context); + }, [](auto&&) { + return Data::FileOrigin(); + }); }); } @@ -72,6 +79,10 @@ Context ComputeContext(const UserPhotosSlice &slice, int index) { return peerFromUser(slice.key().userId); } +Context ComputeContext(const GroupThumbs::CollageSlice &slice, int index) { + return slice.context; +} + Key ComputeKey(const SharedMediaWithLastSlice &slice, int index) { Expects(index >= 0 && index < slice.size()); @@ -88,6 +99,10 @@ Key ComputeKey(const UserPhotosSlice &slice, int index) { return slice[index]; } +Key ComputeKey(const GroupThumbs::CollageSlice &slice, int index) { + return GroupThumbs::CollageKey{ index }; +} + int ComputeThumbsLimit(int availableWidth) { const auto singleWidth = st::mediaviewGroupWidth + 2 * st::mediaviewGroupSkip; @@ -351,6 +366,10 @@ ClickHandlerPtr GroupThumbs::Thumb::getState(QPoint point) const { : nullptr; } +int GroupThumbs::CollageSlice::size() const { + return data->items.size(); +} + GroupThumbs::GroupThumbs(Context context) : _context(context) { } @@ -510,10 +529,41 @@ auto GroupThumbs::createThumb(Key key) } } return createThumb(key, ImagePtr()); + } else if (const auto collageKey = base::get_if(&key)) { + if (const auto itemId = base::get_if(&_context)) { + if (const auto item = App::histItemById(*itemId)) { + if (const auto media = item->media()) { + if (const auto page = media->webpage()) { + return createThumb( + key, + page->collage, + collageKey->index); + } + } + } + } + return createThumb(key, ImagePtr()); } Unexpected("Value of Key in GroupThumbs::createThumb()"); } +auto GroupThumbs::createThumb( + Key key, + const WebPageCollage &collage, + int index) +-> std::unique_ptr { + if (index < 0 || index >= collage.items.size()) { + return createThumb(key, ImagePtr()); + } + const auto &item = collage.items[index]; + if (const auto photo = base::get_if(&item)) { + return createThumb(key, (*photo)->thumb); + } else if (const auto document = base::get_if(&item)) { + return createThumb(key, (*document)->thumb); + } + return createThumb(key, ImagePtr()); +} + auto GroupThumbs::createThumb(Key key, ImagePtr image) -> std::unique_ptr { const auto weak = base::make_weak(this); @@ -556,6 +606,14 @@ void GroupThumbs::Refresh( RefreshFromSlice(instance, slice, index, availableWidth); } +void GroupThumbs::Refresh( + std::unique_ptr &instance, + const CollageSlice &slice, + int index, + int availableWidth) { + RefreshFromSlice(instance, slice, index, availableWidth); +} + void GroupThumbs::clear() { if (_items.empty()) { return; diff --git a/Telegram/SourceFiles/media/view/media_view_group_thumbs.h b/Telegram/SourceFiles/media/view/media_view_group_thumbs.h index 4d27ee0fba..74851202be 100644 --- a/Telegram/SourceFiles/media/view/media_view_group_thumbs.h +++ b/Telegram/SourceFiles/media/view/media_view_group_thumbs.h @@ -12,13 +12,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class SharedMediaWithLastSlice; class UserPhotosSlice; +struct WebPageCollage; namespace Media { namespace View { class GroupThumbs : public base::has_weak_ptr { public: - using Key = base::variant; + struct CollageKey { + int index = 0; + + inline bool operator<(const CollageKey &other) const { + return index < other.index; + } + }; + struct CollageSlice { + FullMsgId context; + not_null data; + + int size() const; + }; + using Key = base::variant; static void Refresh( std::unique_ptr &instance, @@ -30,6 +44,11 @@ public: const UserPhotosSlice &slice, int index, int availableWidth); + static void Refresh( + std::unique_ptr &instance, + const CollageSlice &slice, + int index, + int availableWidth); void clear(); void resizeToWidth(int newWidth); @@ -53,7 +72,10 @@ public: return _lifetime; } - using Context = base::optional_variant; + using Context = base::optional_variant< + PeerId, + MessageGroupId, + FullMsgId>; GroupThumbs(Context context); ~GroupThumbs(); @@ -73,6 +95,10 @@ private: void markCacheStale(); not_null validateCacheEntry(Key key); std::unique_ptr createThumb(Key key); + std::unique_ptr createThumb( + Key key, + const WebPageCollage &collage, + int index); std::unique_ptr createThumb(Key key, ImagePtr image); void update(); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 8c53b898f9..1b9ba4c753 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -57,21 +57,28 @@ Images::Options VideoThumbOptions(not_null document) { } // namespace struct MediaView::SharedMedia { - SharedMedia(SharedMediaWithLastSlice::Key key) : key(key) { + SharedMedia(SharedMediaKey key) : key(key) { } - SharedMediaWithLastSlice::Key key; + SharedMediaKey key; rpl::lifetime lifetime; }; struct MediaView::UserPhotos { - UserPhotos(UserPhotosSlice::Key key) : key(key) { + UserPhotos(UserPhotosKey key) : key(key) { } - UserPhotosSlice::Key key; + UserPhotosKey key; rpl::lifetime lifetime; }; +struct MediaView::Collage { + Collage(CollageKey key) : key(key) { + } + + CollageKey key; +}; + MediaView::MediaView() : TWidget(nullptr) , _transparentBrush(style::transparentPlaceholderBrush()) @@ -121,6 +128,7 @@ MediaView::MediaView() } else { _sharedMedia = nullptr; _userPhotos = nullptr; + _collage = nullptr; } }; subscribe(Messenger::Instance().authSessionChanged(), [handleAuthSessionChange] { @@ -299,6 +307,9 @@ void MediaView::refreshNavVisibility() { } else if (_userPhotosData) { _leftNavVisible = _index && (*_index > 0); _rightNavVisible = _index && (*_index + 1 < _userPhotosData->size()); + } else if (_collageData) { + _leftNavVisible = _index && (*_index > 0); + _rightNavVisible = _index && (*_index + 1 < _collageData->items.size()); } else { _leftNavVisible = false; _rightNavVisible = false; @@ -1092,7 +1103,12 @@ void MediaView::onCopy() { std::optional MediaView::sharedMediaType() const { using Type = SharedMediaType; - if (auto item = App::histItemById(_msgid)) { + if (const auto item = App::histItemById(_msgid)) { + if (const auto media = item->media()) { + if (media->webpage()) { + return std::nullopt; + } + } if (_photo) { if (item->toHistoryMessage()) { return Type::PhotoVideo; @@ -1225,17 +1241,17 @@ std::optional MediaView::userPhotosKey() const { } bool MediaView::validUserPhotos() const { - if (auto key = userPhotosKey()) { + if (const auto key = userPhotosKey()) { if (!_userPhotos) { return false; } - auto countDistanceInData = [](const auto &a, const auto &b) { + const auto countDistanceInData = [](const auto &a, const auto &b) { return [&](const UserPhotosSlice &data) { return data.distance(a, b); }; }; - auto distance = (key == _userPhotos->key) ? 0 : + const auto distance = (key == _userPhotos->key) ? 0 : _userPhotosData | countDistanceInData(*key, _userPhotos->key) | func::abs; @@ -1247,7 +1263,7 @@ bool MediaView::validUserPhotos() const { } void MediaView::validateUserPhotos() { - if (auto key = userPhotosKey()) { + if (const auto key = userPhotosKey()) { _userPhotos = std::make_unique(*key); UserPhotosReversedViewer( *key, @@ -1274,6 +1290,66 @@ void MediaView::handleUserPhotosUpdate(UserPhotosSlice &&update) { preloadData(0); } +std::optional MediaView::collageKey() const { + if (const auto item = App::histItemById(_msgid)) { + if (const auto media = item->media()) { + if (const auto page = media->webpage()) { + for (const auto item : page->collage.items) { + if (item == _photo || item == _doc) { + return item; + } + } + } + } + } + return std::nullopt; +} + +bool MediaView::validCollage() const { + if (const auto key = collageKey()) { + if (!_collage) { + return false; + } + const auto countDistanceInData = [](const auto &a, const auto &b) { + return [&](const WebPageCollage &data) { + const auto i = ranges::find(data.items, a); + const auto j = ranges::find(data.items, b); + return (i != end(data.items) && j != end(data.items)) + ? std::make_optional(i - j) + : std::nullopt; + }; + }; + + if (key == _collage->key) { + return true; + } else if (_collageData) { + const auto &items = _collageData->items; + if (ranges::find(items, *key) != end(items) + && ranges::find(items, _collage->key) != end(items)) { + return true; + } + } + } + return (_collage == nullptr); +} + +void MediaView::validateCollage() { + if (const auto key = collageKey()) { + _collage = std::make_unique(*key); + _collageData = WebPageCollage(); + if (const auto item = App::histItemById(_msgid)) { + if (const auto media = item->media()) { + if (const auto page = media->webpage()) { + _collageData = page->collage; + } + } + } + } else { + _collage = nullptr; + _collageData = std::nullopt; + } +} + void MediaView::refreshMediaViewer() { if (!validSharedMedia()) { validateSharedMedia(); @@ -1281,6 +1357,9 @@ void MediaView::refreshMediaViewer() { if (!validUserPhotos()) { validateUserPhotos(); } + if (!validCollage()) { + validateCollage(); + } findCurrent(); updateControls(); preloadData(0); @@ -1290,6 +1369,10 @@ void MediaView::refreshCaption(HistoryItem *item) { _caption = Text(); if (!item) { return; + } else if (const auto media = item->media()) { + if (media->webpage()) { + return; + } } const auto caption = item->originalText(); if (caption.text.isEmpty()) { @@ -1322,6 +1405,12 @@ void MediaView::refreshGroupThumbs() { *_userPhotosData, *_index, _groupThumbsAvailableWidth); + } else if (_index && _collageData) { + Media::View::GroupThumbs::Refresh( + _groupThumbs, + { _msgid, &*_collageData }, + *_index, + _groupThumbsAvailableWidth); } else if (_groupThumbs) { _groupThumbs->clear(); _groupThumbs->resizeToWidth(_groupThumbsAvailableWidth); @@ -1347,11 +1436,16 @@ void MediaView::initGroupThumbs() { _groupThumbs->activateRequests( ) | rpl::start_with_next([this](Media::View::GroupThumbs::Key key) { + using CollageKey = Media::View::GroupThumbs::CollageKey; if (const auto photoId = base::get_if(&key)) { const auto photo = Auth().data().photo(*photoId); moveToEntity({ photo, nullptr }); } else if (const auto itemId = base::get_if(&key)) { moveToEntity(entityForItemId(*itemId)); + } else if (const auto collageKey = base::get_if(&key)) { + if (_collageData) { + moveToEntity(entityForCollage(collageKey->index)); + } } }, _groupThumbs->lifetime()); @@ -2464,7 +2558,7 @@ void MediaView::setZoomLevel(int newZoom) { } MediaView::Entity MediaView::entityForUserPhotos(int index) const { - Expects(!!_userPhotosData); + Expects(_userPhotosData.has_value()); if (index < 0 || index >= _userPhotosData->size()) { return { std::nullopt, nullptr }; @@ -2476,7 +2570,7 @@ MediaView::Entity MediaView::entityForUserPhotos(int index) const { } MediaView::Entity MediaView::entityForSharedMedia(int index) const { - Expects(!!_sharedMediaData); + Expects(_sharedMediaData.has_value()); if (index < 0 || index >= _sharedMediaData->size()) { return { std::nullopt, nullptr }; @@ -2491,6 +2585,22 @@ MediaView::Entity MediaView::entityForSharedMedia(int index) const { return { std::nullopt, nullptr }; } +MediaView::Entity MediaView::entityForCollage(int index) const { + Expects(_collageData.has_value()); + + const auto item = App::histItemById(_msgid); + const auto &items = _collageData->items; + if (!item || index < 0 || index >= items.size()) { + return { std::nullopt, nullptr }; + } + if (const auto document = base::get_if(&items[index])) { + return { *document, item }; + } else if (const auto photo = base::get_if(&items[index])) { + return { *photo, item }; + } + return { std::nullopt, nullptr }; +} + MediaView::Entity MediaView::entityForItemId(const FullMsgId &itemId) const { if (const auto item = App::histItemById(itemId)) { if (const auto media = item->media()) { @@ -2510,6 +2620,8 @@ MediaView::Entity MediaView::entityByIndex(int index) const { return entityForSharedMedia(index); } else if (_userPhotosData) { return entityForUserPhotos(index); + } else if (_collageData) { + return entityForCollage(index); } return { std::nullopt, nullptr }; } @@ -3008,6 +3120,8 @@ void MediaView::setVisible(bool visible) { _sharedMediaDataKey = std::nullopt; _userPhotos = nullptr; _userPhotosData = std::nullopt; + _collage = nullptr; + _collageData = std::nullopt; if (_menu) _menu->hideMenu(true); _controlsHideTimer.stop(); _controlsState = ControlsShown; @@ -3075,6 +3189,15 @@ void MediaView::findCurrent() { ? (_index | func::add(*_userPhotosData->skippedBefore())) : std::nullopt; _fullCount = _userPhotosData->fullCount(); + } else if (_collageData) { + const auto item = _photo ? WebPageCollage::Item(_photo) : _doc; + const auto &items = _collageData->items; + const auto i = ranges::find(items, item); + _index = (i != end(items)) + ? std::make_optional(int(i - begin(items))) + : std::nullopt; + _fullIndex = _index; + _fullCount = items.size(); } else { _index = _fullIndex = _fullCount = std::nullopt; } diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index a03e1b6ce1..66940d955a 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/effects/radial_animation.h" #include "data/data_shared_media.h" #include "data/data_user_photos.h" +#include "data/data_web_page.h" namespace Media { namespace Player { @@ -147,6 +148,7 @@ private: }; Entity entityForUserPhotos(int index) const; Entity entityForSharedMedia(int index) const; + Entity entityForCollage(int index) const; Entity entityByIndex(int index) const; Entity entityForItemId(const FullMsgId &itemId) const; bool moveToEntity(const Entity &entity, int preloadDelta = 0); @@ -175,6 +177,12 @@ private: void validateUserPhotos(); void handleUserPhotosUpdate(UserPhotosSlice &&update); + struct Collage; + using CollageKey = WebPageCollage::Item; + std::optional collageKey() const; + bool validCollage() const; + void validateCollage(); + Data::FileOrigin fileOrigin() const; void refreshCaption(HistoryItem *item); @@ -252,6 +260,8 @@ private: std::optional _sharedMediaDataKey; std::unique_ptr _userPhotos; std::optional _userPhotosData; + std::unique_ptr _collage; + std::optional _collageData; QRect _closeNav, _closeNavIcon; QRect _leftNav, _leftNavIcon, _rightNav, _rightNavIcon;