Show collage/slideshow as an album in MediaView.

This commit is contained in:
John Preston 2018-10-24 17:09:31 +04:00
parent 251f51ca1b
commit bf31722931
4 changed files with 231 additions and 14 deletions

View File

@ -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<CollageKey>(&key)) {
if (const auto itemId = base::get_if<FullMsgId>(&_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<Thumb> {
if (index < 0 || index >= collage.items.size()) {
return createThumb(key, ImagePtr());
}
const auto &item = collage.items[index];
if (const auto photo = base::get_if<PhotoData*>(&item)) {
return createThumb(key, (*photo)->thumb);
} else if (const auto document = base::get_if<DocumentData*>(&item)) {
return createThumb(key, (*document)->thumb);
}
return createThumb(key, ImagePtr());
}
auto GroupThumbs::createThumb(Key key, ImagePtr image)
-> std::unique_ptr<Thumb> {
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<GroupThumbs> &instance,
const CollageSlice &slice,
int index,
int availableWidth) {
RefreshFromSlice(instance, slice, index, availableWidth);
}
void GroupThumbs::clear() {
if (_items.empty()) {
return;

View File

@ -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<PhotoId, FullMsgId>;
struct CollageKey {
int index = 0;
inline bool operator<(const CollageKey &other) const {
return index < other.index;
}
};
struct CollageSlice {
FullMsgId context;
not_null<const WebPageCollage*> data;
int size() const;
};
using Key = base::variant<PhotoId, FullMsgId, CollageKey>;
static void Refresh(
std::unique_ptr<GroupThumbs> &instance,
@ -30,6 +44,11 @@ public:
const UserPhotosSlice &slice,
int index,
int availableWidth);
static void Refresh(
std::unique_ptr<GroupThumbs> &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<PeerId, MessageGroupId>;
using Context = base::optional_variant<
PeerId,
MessageGroupId,
FullMsgId>;
GroupThumbs(Context context);
~GroupThumbs();
@ -73,6 +95,10 @@ private:
void markCacheStale();
not_null<Thumb*> validateCacheEntry(Key key);
std::unique_ptr<Thumb> createThumb(Key key);
std::unique_ptr<Thumb> createThumb(
Key key,
const WebPageCollage &collage,
int index);
std::unique_ptr<Thumb> createThumb(Key key, ImagePtr image);
void update();

View File

@ -57,21 +57,28 @@ Images::Options VideoThumbOptions(not_null<DocumentData*> 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> 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> 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<UserPhotos>(*key);
UserPhotosReversedViewer(
*key,
@ -1274,6 +1290,66 @@ void MediaView::handleUserPhotosUpdate(UserPhotosSlice &&update) {
preloadData(0);
}
std::optional<MediaView::CollageKey> 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<Collage>(*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<PhotoId>(&key)) {
const auto photo = Auth().data().photo(*photoId);
moveToEntity({ photo, nullptr });
} else if (const auto itemId = base::get_if<FullMsgId>(&key)) {
moveToEntity(entityForItemId(*itemId));
} else if (const auto collageKey = base::get_if<CollageKey>(&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<DocumentData*>(&items[index])) {
return { *document, item };
} else if (const auto photo = base::get_if<PhotoData*>(&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;
}

View File

@ -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> collageKey() const;
bool validCollage() const;
void validateCollage();
Data::FileOrigin fileOrigin() const;
void refreshCaption(HistoryItem *item);
@ -252,6 +260,8 @@ private:
std::optional<SharedMediaWithLastSlice::Key> _sharedMediaDataKey;
std::unique_ptr<UserPhotos> _userPhotos;
std::optional<UserPhotosSlice> _userPhotosData;
std::unique_ptr<Collage> _collage;
std::optional<WebPageCollage> _collageData;
QRect _closeNav, _closeNavIcon;
QRect _leftNav, _leftNavIcon, _rightNav, _rightNavIcon;