| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "media/view/media_view_group_thumbs.h"
- #include "data/data_shared_media.h"
- #include "data/data_user_photos.h"
- #include "data/data_photo.h"
- #include "data/data_photo_media.h"
- #include "data/data_document.h"
- #include "data/data_document_media.h"
- #include "data/data_media_types.h"
- #include "data/data_session.h"
- #include "data/data_web_page.h"
- #include "data/data_file_origin.h"
- #include "history/history.h"
- #include "history/view/media/history_view_media.h"
- #include "ui/image/image.h"
- #include "ui/ui_utility.h"
- #include "main/main_session.h"
- #include "core/crash_reports.h"
- #include "styles/style_media_view.h"
- namespace Media {
- namespace View {
- namespace {
- constexpr auto kThumbDuration = crl::time(150);
- int Round(float64 value) {
- return int(base::SafeRound(value));
- }
- using Context = GroupThumbs::Context;
- using Key = GroupThumbs::Key;
- #if 0
- [[nodiscard]] QString DebugSerializeMsgId(FullMsgId itemId) {
- return QString("msg%1_%2").arg(itemId.channel.bare).arg(itemId.msg);
- }
- [[nodiscard]] QString DebugSerializePeer(PeerId peerId) {
- return peerIsUser(peerId)
- ? QString("user%1").arg(peerToUser(peerId).bare)
- : peerIsChat(peerId)
- ? QString("chat%1").arg(peerToChat(peerId).bare)
- : QString("channel%1").arg(peerToChannel(peerId).bare);
- }
- [[nodiscard]] QString DebugSerializeKey(const Key &key) {
- return v::match(key, [&](PhotoId photoId) {
- return QString("photo%1").arg(photoId);
- }, [](FullMsgId itemId) {
- return DebugSerializeMsgId(itemId);
- }, [&](GroupThumbs::CollageKey key) {
- return QString("collage%1").arg(key.index);
- });
- }
- [[nodiscard]] QString DebugSerializeContext(const Context &context) {
- return v::match(context, [](PeerId peerId) {
- return DebugSerializePeer(peerId);
- }, [](MessageGroupId groupId) {
- return QString("group_%1_%2"
- ).arg(DebugSerializePeer(groupId.peer)
- ).arg(groupId.value);
- }, [](FullMsgId item) {
- return DebugSerializeMsgId(item);
- }, [](v::null_t) -> QString {
- return "null";
- });
- }
- #endif
- Data::FileOrigin ComputeFileOrigin(const Key &key, const Context &context) {
- return v::match(key, [&](PhotoId photoId) {
- return v::match(context, [&](PeerId peerId) {
- return peerIsUser(peerId)
- ? Data::FileOriginUserPhoto(peerToUser(peerId), photoId)
- : Data::FileOrigin(Data::FileOriginPeerPhoto(peerId));
- }, [](auto&&) {
- return Data::FileOrigin();
- });
- }, [](FullMsgId itemId) {
- return Data::FileOrigin(itemId);
- }, [&](GroupThumbs::CollageKey) {
- return v::match(context, [](const GroupThumbs::CollageSlice &slice) {
- return Data::FileOrigin(slice.context);
- }, [](auto&&) {
- return Data::FileOrigin();
- });
- });
- }
- Context ComputeContext(
- not_null<Main::Session*> session,
- const SharedMediaWithLastSlice &slice,
- int index) {
- Expects(index >= 0 && index < slice.size());
- const auto value = slice[index];
- if (const auto photo = std::get_if<not_null<PhotoData*>>(&value)) {
- if (const auto peer = (*photo)->peer) {
- return peer->id;
- }
- return v::null;
- } else if (const auto msgId = std::get_if<FullMsgId>(&value)) {
- if (const auto item = session->data().message(*msgId)) {
- if (item->isService()) {
- return item->history()->peer->id;
- } else if (const auto groupId = item->groupId()) {
- return groupId;
- }
- }
- return v::null;
- }
- Unexpected("Variant in ComputeContext(SharedMediaWithLastSlice::Value)");
- }
- Context ComputeContext(
- not_null<Main::Session*> session,
- const UserPhotosSlice &slice,
- int index) {
- return peerFromUser(slice.key().userId);
- }
- Context ComputeContext(
- not_null<Main::Session*> session,
- const GroupThumbs::CollageSlice &slice,
- int index) {
- return slice.context;
- }
- Key ComputeKey(const SharedMediaWithLastSlice &slice, int index) {
- Expects(index >= 0 && index < slice.size());
- const auto value = slice[index];
- if (const auto photo = std::get_if<not_null<PhotoData*>>(&value)) {
- return (*photo)->id;
- } else if (const auto msgId = std::get_if<FullMsgId>(&value)) {
- return *msgId;
- }
- Unexpected("Variant in ComputeKey(SharedMediaWithLastSlice::Value)");
- }
- 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;
- const auto currentWidth = st::mediaviewGroupWidthMax
- + 2 * st::mediaviewGroupSkipCurrent;
- const auto skipForAnimation = 2 * singleWidth;
- const auto leftWidth = availableWidth
- - currentWidth
- - skipForAnimation;
- return std::max(leftWidth / (2 * singleWidth), 1);
- }
- } // namespace
- class GroupThumbs::Thumb {
- public:
- enum class State {
- Unknown,
- Current,
- Alive,
- Dying,
- };
- Thumb(Key key, Fn<void()> handler);
- Thumb(
- Key key,
- not_null<PhotoData*> photo,
- Data::FileOrigin origin,
- Fn<void()> handler);
- Thumb(
- Key key,
- not_null<DocumentData*> document,
- Data::FileOrigin origin,
- Fn<void()> handler);
- [[nodiscard]] int leftToUpdate() const;
- [[nodiscard]] int rightToUpdate() const;
- void animateToLeft(not_null<Thumb*> next);
- void animateToRight(not_null<Thumb*> prev);
- void setState(State state);
- [[nodiscard]] State state() const;
- [[nodiscard]] bool inited() const;
- [[nodiscard]] bool removed() const;
- void paint(QPainter &p, int x, int y, int outerWidth, float64 progress);
- [[nodiscard]] ClickHandlerPtr getState(QPoint point) const;
- private:
- QSize wantedPixSize() const;
- void validateImage();
- int currentLeft() const;
- int currentWidth() const;
- int finalLeft() const;
- int finalWidth() const;
- void animateTo(int left, int width);
- ClickHandlerPtr _link;
- const Key _key;
- std::shared_ptr<Data::DocumentMedia> _documentMedia;
- std::shared_ptr<Data::PhotoMedia> _photoMedia;
- Image *_image = nullptr;
- Data::FileOrigin _origin;
- State _state = State::Alive;
- QPixmap _full;
- int _fullWidth = 0;
- bool _hiding = false;
- anim::value _left = { 0. };
- anim::value _width = { 0. };
- anim::value _opacity = { 0., 1. };
- };
- GroupThumbs::Thumb::Thumb(Key key, Fn<void()> handler)
- : _key(key) {
- _link = std::make_shared<LambdaClickHandler>(std::move(handler));
- validateImage();
- }
- GroupThumbs::Thumb::Thumb(
- Key key,
- not_null<PhotoData*> photo,
- Data::FileOrigin origin,
- Fn<void()> handler)
- : _key(key)
- , _photoMedia(photo->createMediaView())
- , _origin(origin) {
- _link = std::make_shared<LambdaClickHandler>(std::move(handler));
- _photoMedia->wanted(Data::PhotoSize::Thumbnail, origin);
- validateImage();
- }
- GroupThumbs::Thumb::Thumb(
- Key key,
- not_null<DocumentData*> document,
- Data::FileOrigin origin,
- Fn<void()> handler)
- : _key(key)
- , _documentMedia(document->createMediaView())
- , _origin(origin) {
- _link = std::make_shared<LambdaClickHandler>(std::move(handler));
- _documentMedia->thumbnailWanted(origin);
- validateImage();
- }
- QSize GroupThumbs::Thumb::wantedPixSize() const {
- const auto originalWidth = _image ? std::max(_image->width(), 1) : 1;
- const auto originalHeight = _image ? std::max(_image->height(), 1) : 1;
- const auto pixHeight = st::mediaviewGroupHeight;
- const auto pixWidth = originalWidth * pixHeight / originalHeight;
- return { pixWidth, pixHeight };
- }
- void GroupThumbs::Thumb::validateImage() {
- if (!_image) {
- if (_photoMedia) {
- _image = _photoMedia->image(Data::PhotoSize::Thumbnail);
- } else if (_documentMedia) {
- _image = _documentMedia->thumbnail();
- }
- }
- if (!_full.isNull() || !_image) {
- return;
- }
- const auto pixSize = wantedPixSize();
- if (pixSize.width() > st::mediaviewGroupWidthMax) {
- const auto originalWidth = _image->width();
- const auto originalHeight = _image->height();
- const auto takeWidth = originalWidth * st::mediaviewGroupWidthMax
- / pixSize.width();
- auto original = _image->original();
- original.setDevicePixelRatio(style::DevicePixelRatio());
- _full = Ui::PixmapFromImage(original.copy(
- (originalWidth - takeWidth) / 2,
- 0,
- takeWidth,
- originalHeight
- ).scaled(
- st::mediaviewGroupWidthMax * style::DevicePixelRatio(),
- pixSize.height() * style::DevicePixelRatio(),
- Qt::IgnoreAspectRatio,
- Qt::SmoothTransformation));
- } else {
- _full = _image->pixNoCache(pixSize * style::DevicePixelRatio());
- }
- _fullWidth = std::min(
- wantedPixSize().width(),
- st::mediaviewGroupWidthMax);
- }
- int GroupThumbs::Thumb::leftToUpdate() const {
- return Round(std::min(_left.from(), _left.to()));
- }
- int GroupThumbs::Thumb::rightToUpdate() const {
- return Round(std::max(
- _left.from() + _width.from(),
- _left.to() + _width.to()));
- }
- int GroupThumbs::Thumb::currentLeft() const {
- return Round(_left.current());
- }
- int GroupThumbs::Thumb::currentWidth() const {
- return Round(_width.current());
- }
- int GroupThumbs::Thumb::finalLeft() const {
- return Round(_left.to());
- }
- int GroupThumbs::Thumb::finalWidth() const {
- return Round(_width.to());
- }
- void GroupThumbs::Thumb::setState(State state) {
- const auto isNewThumb = (_state == State::Alive);
- _state = state;
- if (_state == State::Current) {
- if (isNewThumb) {
- _opacity = anim::value(1.);
- _left = anim::value(-_fullWidth / 2);
- _width = anim::value(_fullWidth);
- } else {
- _opacity.start(1.);
- }
- _hiding = false;
- animateTo(-_fullWidth / 2, _fullWidth);
- } else if (_state == State::Alive) {
- _opacity.start(0.7);
- _hiding = false;
- } else if (_state == State::Dying) {
- _opacity.start(0.);
- _hiding = true;
- _left.restart();
- _width.restart();
- }
- }
- void GroupThumbs::Thumb::animateTo(int left, int width) {
- _left.start(left);
- _width.start(width);
- }
- void GroupThumbs::Thumb::animateToLeft(not_null<Thumb*> next) {
- const auto width = st::mediaviewGroupWidth;
- if (_state == State::Alive) {
- // New item animation, start exactly from the next, move only.
- _left = anim::value(next->currentLeft() - width);
- _width = anim::value(width);
- } else if (_state == State::Unknown) {
- // Existing item animation.
- setState(State::Alive);
- }
- const auto skip1 = st::mediaviewGroupSkip;
- const auto skip2 = (next->state() == State::Current)
- ? st::mediaviewGroupSkipCurrent
- : st::mediaviewGroupSkip;
- animateTo(next->finalLeft() - width - skip1 - skip2, width);
- }
- void GroupThumbs::Thumb::animateToRight(not_null<Thumb*> prev) {
- const auto width = st::mediaviewGroupWidth;
- if (_state == State::Alive) {
- // New item animation, start exactly from the next, move only.
- _left = anim::value(prev->currentLeft() + prev->currentWidth());
- _width = anim::value(width);
- } else if (_state == State::Unknown) {
- // Existing item animation.
- setState(State::Alive);
- }
- const auto skip1 = st::mediaviewGroupSkip;
- const auto skip2 = (prev->state() == State::Current)
- ? st::mediaviewGroupSkipCurrent
- : st::mediaviewGroupSkip;
- animateTo(prev->finalLeft() + prev->finalWidth() + skip1 + skip2, width);
- }
- auto GroupThumbs::Thumb::state() const -> State {
- return _state;
- }
- bool GroupThumbs::Thumb::inited() const {
- return _fullWidth != 0;
- }
- bool GroupThumbs::Thumb::removed() const {
- return (_state == State::Dying) && _hiding && !_opacity.current();
- }
- void GroupThumbs::Thumb::paint(
- QPainter &p,
- int x,
- int y,
- int outerWidth,
- float64 progress) {
- validateImage();
- _opacity.update(progress, anim::linear);
- _left.update(progress, anim::linear);
- _width.update(progress, anim::linear);
- const auto left = x + currentLeft();
- const auto width = currentWidth();
- const auto opacity = p.opacity();
- p.setOpacity(_opacity.current() * opacity);
- if (width == _fullWidth) {
- p.drawPixmap(left, y, _full);
- } else {
- const auto takeWidth = width * style::DevicePixelRatio();
- const auto from = QRect(
- (_full.width() - takeWidth) / 2,
- 0,
- takeWidth,
- _full.height());
- const auto to = QRect(left, y, width, st::mediaviewGroupHeight);
- p.drawPixmap(to, _full, from);
- }
- p.setOpacity(opacity);
- }
- ClickHandlerPtr GroupThumbs::Thumb::getState(QPoint point) const {
- if (_state != State::Alive) {
- return nullptr;
- }
- const auto left = finalLeft();
- const auto width = finalWidth();
- return QRect(left, 0, width, st::mediaviewGroupHeight).contains(point)
- ? _link
- : nullptr;
- }
- int GroupThumbs::CollageSlice::size() const {
- return data->items.size();
- }
- GroupThumbs::GroupThumbs(not_null<Main::Session*> session, Context context)
- : _session(session)
- , _context(context) {
- }
- void GroupThumbs::updateContext(Context context) {
- if (_context != context) {
- clear();
- _context = context;
- }
- }
- template <typename Slice>
- void GroupThumbs::RefreshFromSlice(
- not_null<Main::Session*> session,
- std::unique_ptr<GroupThumbs> &instance,
- const Slice &slice,
- int index,
- int availableWidth) {
- const auto context = ComputeContext(session, slice, index);
- if (instance) {
- instance->updateContext(context);
- }
- if (v::is_null(context)) {
- if (instance) {
- instance->resizeToWidth(availableWidth);
- }
- return;
- }
- const auto limit = ComputeThumbsLimit(availableWidth);
- const auto from = [&] {
- const auto edge = std::max(index - limit, 0);
- for (auto result = index; result != edge; --result) {
- if (ComputeContext(session, slice, result - 1) != context) {
- return result;
- }
- }
- return edge;
- }();
- const auto till = [&] {
- const auto edge = std::min(index + limit + 1, slice.size());
- for (auto result = index + 1; result != edge; ++result) {
- if (ComputeContext(session, slice, result) != context) {
- return result;
- }
- }
- return edge;
- }();
- if (from + 1 < till) {
- if (!instance) {
- instance = std::make_unique<GroupThumbs>(session, context);
- }
- instance->fillItems(slice, from, index, till);
- instance->resizeToWidth(availableWidth);
- } else if (instance) {
- instance->clear();
- instance->resizeToWidth(availableWidth);
- }
- }
- #if 0
- template <typename Slice>
- void ValidateSlice(
- const Slice &slice,
- const Context &context,
- int from,
- int index,
- int till) {
- auto keys = base::flat_set<Key>();
- for (auto i = from; i != till; ++i) {
- const auto key = ComputeKey(slice, i);
- if (keys.contains(key)) {
- // All items should be unique!
- auto strings = QStringList();
- strings.reserve(till - from);
- for (auto i = from; i != till; ++i) {
- strings.push_back(DebugSerializeKey(ComputeKey(slice, i)));
- }
- CrashReports::SetAnnotation(
- "keys",
- QString("%1:%2-(%3)-%4:"
- ).arg(DebugSerializeContext(context)
- ).arg(from
- ).arg(index
- ).arg(till) + strings.join(","));
- if (Logs::DebugEnabled()) {
- Unexpected("Bad slice in GroupThumbs.");
- }
- break;
- } else {
- keys.emplace(key);
- }
- }
- }
- #endif
- template <typename Slice>
- void GroupThumbs::fillItems(
- const Slice &slice,
- int from,
- int index,
- int till) {
- Expects(from <= index);
- Expects(index < till);
- Expects(from + 1 < till);
- const auto current = (index - from);
- const auto old = base::take(_items);
- //ValidateSlice(slice, _context, from, index, till);
- markCacheStale();
- _items.reserve(till - from);
- for (auto i = from; i != till; ++i) {
- _items.push_back(validateCacheEntry(ComputeKey(slice, i)));
- }
- animateAliveItems(current);
- fillDyingItems(old);
- startDelayedAnimation();
- }
- void GroupThumbs::animateAliveItems(int current) {
- Expects(current >= 0 && current < _items.size());
- _items[current]->setState(Thumb::State::Current);
- for (auto i = current; i != 0;) {
- const auto prev = _items[i];
- const auto item = _items[--i];
- item->animateToLeft(prev);
- }
- for (auto i = current + 1; i != _items.size(); ++i) {
- const auto prev = _items[i - 1];
- const auto item = _items[i];
- item->animateToRight(prev);
- }
- }
- void GroupThumbs::fillDyingItems(const std::vector<not_null<Thumb*>> &old) {
- //Expects(_cache.size() >= _items.size());
- if (_cache.size() >= _items.size()) {
- _dying.reserve(_cache.size() - _items.size());
- }
- animatePreviouslyAlive(old);
- markRestAsDying();
- }
- void GroupThumbs::markRestAsDying() {
- //Expects(_cache.size() >= _items.size());
- if (_cache.size() >= _items.size()) {
- _dying.reserve(_cache.size() - _items.size());
- }
- for (const auto &cacheItem : _cache) {
- const auto &thumb = cacheItem.second;
- const auto state = thumb->state();
- if (state == Thumb::State::Unknown) {
- markAsDying(thumb.get());
- }
- }
- }
- void GroupThumbs::markAsDying(not_null<Thumb*> thumb) {
- thumb->setState(Thumb::State::Dying);
- _dying.push_back(thumb.get());
- }
- void GroupThumbs::animatePreviouslyAlive(
- const std::vector<not_null<Thumb*>> &old) {
- auto toRight = false;
- for (auto i = 0; i != old.size(); ++i) {
- const auto item = old[i];
- if (item->state() == Thumb::State::Unknown) {
- if (toRight) {
- markAsDying(item);
- item->animateToRight(old[i - 1]);
- }
- } else if (!toRight) {
- for (auto j = i; j != 0;) {
- const auto next = old[j];
- const auto prev = old[--j];
- markAsDying(prev);
- prev->animateToLeft(next);
- }
- toRight = true;
- }
- }
- }
- auto GroupThumbs::createThumb(Key key)
- -> std::unique_ptr<Thumb> {
- if (const auto photoId = std::get_if<PhotoId>(&key)) {
- const auto photo = _session->data().photo(*photoId);
- return createThumb(key, photo);
- } else if (const auto msgId = std::get_if<FullMsgId>(&key)) {
- if (const auto item = _session->data().message(*msgId)) {
- if (const auto media = item->media()) {
- if (const auto photo = media->photo()) {
- return createThumb(key, photo);
- } else if (const auto document = media->document()) {
- return createThumb(key, document);
- }
- }
- }
- return createThumb(key, nullptr);
- } else if (const auto collageKey = std::get_if<CollageKey>(&key)) {
- if (const auto itemId = std::get_if<FullMsgId>(&_context)) {
- if (const auto item = _session->data().message(*itemId)) {
- if (const auto media = item->media()) {
- if (const auto page = media->webpage()) {
- return createThumb(
- key,
- page->collage,
- collageKey->index);
- } else if (const auto invoice = media->invoice()) {
- return createThumb(
- key,
- *invoice,
- collageKey->index);
- }
- }
- }
- }
- return createThumb(key, nullptr);
- }
- 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, nullptr);
- }
- const auto &item = collage.items[index];
- if (const auto photo = std::get_if<PhotoData*>(&item)) {
- return createThumb(key, (*photo));
- } else if (const auto document = std::get_if<DocumentData*>(&item)) {
- return createThumb(key, (*document));
- }
- return createThumb(key, nullptr);
- }
- auto GroupThumbs::createThumb(
- Key key,
- const Data::Invoice &invoice,
- int index)
- -> std::unique_ptr<Thumb> {
- if (index < 0 || index >= invoice.extendedMedia.size()) {
- return createThumb(key, nullptr);
- }
- const auto &media = invoice.extendedMedia[index];
- if (const auto photo = media->photo()) {
- return createThumb(key, photo);
- } else if (const auto document = media->document()) {
- return createThumb(key, document);
- }
- return createThumb(key, nullptr);
- }
- auto GroupThumbs::createThumb(Key key, std::nullptr_t)
- -> std::unique_ptr<Thumb> {
- const auto weak = base::make_weak(this);
- const auto origin = ComputeFileOrigin(key, _context);
- return std::make_unique<Thumb>(key, [=] {
- if (const auto strong = weak.get()) {
- strong->_activateStream.fire_copy(key);
- }
- });
- }
- auto GroupThumbs::createThumb(Key key, not_null<PhotoData*> photo)
- -> std::unique_ptr<Thumb> {
- const auto weak = base::make_weak(this);
- const auto origin = ComputeFileOrigin(key, _context);
- return std::make_unique<Thumb>(key, photo, origin, [=] {
- if (const auto strong = weak.get()) {
- strong->_activateStream.fire_copy(key);
- }
- });
- }
- auto GroupThumbs::createThumb(Key key, not_null<DocumentData*> document)
- -> std::unique_ptr<Thumb> {
- const auto weak = base::make_weak(this);
- const auto origin = ComputeFileOrigin(key, _context);
- return std::make_unique<Thumb>(key, document, origin, [=] {
- if (const auto strong = weak.get()) {
- strong->_activateStream.fire_copy(key);
- }
- });
- }
- auto GroupThumbs::validateCacheEntry(Key key) -> not_null<Thumb*> {
- const auto i = _cache.find(key);
- return (i != _cache.end())
- ? i->second.get()
- : _cache.emplace(key, createThumb(key)).first->second.get();
- }
- void GroupThumbs::markCacheStale() {
- _dying.clear();
- for (const auto &cacheItem : _cache) {
- const auto &thumb = cacheItem.second;
- thumb->setState(Thumb::State::Unknown);
- }
- }
- void GroupThumbs::Refresh(
- not_null<Main::Session*> session,
- std::unique_ptr<GroupThumbs> &instance,
- const SharedMediaWithLastSlice &slice,
- int index,
- int availableWidth) {
- RefreshFromSlice(session, instance, slice, index, availableWidth);
- }
- void GroupThumbs::Refresh(
- not_null<Main::Session*> session,
- std::unique_ptr<GroupThumbs> &instance,
- const UserPhotosSlice &slice,
- int index,
- int availableWidth) {
- RefreshFromSlice(session, instance, slice, index, availableWidth);
- }
- void GroupThumbs::Refresh(
- not_null<Main::Session*> session,
- std::unique_ptr<GroupThumbs> &instance,
- const CollageSlice &slice,
- int index,
- int availableWidth) {
- RefreshFromSlice(session, instance, slice, index, availableWidth);
- }
- void GroupThumbs::clear() {
- if (_items.empty()) {
- return;
- }
- base::take(_items);
- markCacheStale();
- markRestAsDying();
- startDelayedAnimation();
- }
- void GroupThumbs::startDelayedAnimation() {
- _animation.stop();
- _waitingForAnimationStart = true;
- countUpdatedRect();
- }
- void GroupThumbs::resizeToWidth(int newWidth) {
- _width = newWidth;
- }
- int GroupThumbs::height() const {
- return st::mediaviewGroupPadding.top()
- + st::mediaviewGroupHeight
- + st::mediaviewGroupPadding.bottom();
- }
- bool GroupThumbs::hiding() const {
- return _items.empty();
- }
- bool GroupThumbs::hidden() const {
- return hiding() && !_waitingForAnimationStart && !_animation.animating();
- }
- void GroupThumbs::checkForAnimationStart() {
- if (_waitingForAnimationStart) {
- _waitingForAnimationStart = false;
- _animation.start([=] { update(); }, 0., 1., kThumbDuration);
- }
- }
- void GroupThumbs::update() {
- if (_cache.empty()) {
- return;
- }
- _updateRequests.fire_copy(_updatedRect);
- }
- void GroupThumbs::paint(QPainter &p, int x, int y, int outerWidth) {
- const auto progress = _waitingForAnimationStart
- ? 0.
- : _animation.value(1.);
- x += (_width / 2);
- y += st::mediaviewGroupPadding.top();
- auto initedCurrentIndex = int(_items.size());
- for (auto i = _cache.begin(); i != _cache.end();) {
- const auto thumb = not_null{ i->second.get() };
- const auto inited = thumb->inited();
- thumb->paint(p, x, y, outerWidth, progress);
- if (thumb->removed()) {
- _dying.erase(ranges::remove(_dying, thumb), _dying.end());
- i = _cache.erase(i);
- } else {
- if (!inited
- && thumb->inited()
- && thumb->state() == Thumb::State::Current) {
- initedCurrentIndex = ranges::find(_items, thumb)
- - begin(_items);
- }
- ++i;
- }
- }
- if (initedCurrentIndex < _items.size()) {
- animateAliveItems(initedCurrentIndex);
- }
- }
- ClickHandlerPtr GroupThumbs::getState(QPoint point) const {
- point -= QPoint((_width / 2), st::mediaviewGroupPadding.top());
- for (const auto &cacheItem : _cache) {
- const auto &thumb = cacheItem.second;
- if (auto link = thumb->getState(point)) {
- return link;
- }
- }
- return nullptr;
- }
- void GroupThumbs::countUpdatedRect() {
- if (_cache.empty()) {
- return;
- }
- auto min = _width;
- auto max = 0;
- const auto left = [](const auto &cacheItem) {
- const auto &[key, thumb] = cacheItem;
- return thumb->leftToUpdate();
- };
- const auto right = [](const auto &cacheItem) {
- const auto &[key, thumb] = cacheItem;
- return thumb->rightToUpdate();
- };
- accumulate_min(min, left(*ranges::max_element(
- _cache,
- std::greater<>(),
- left)));
- accumulate_max(max, right(*ranges::max_element(
- _cache,
- std::less<>(),
- right)));
- _updatedRect = QRect(
- min,
- st::mediaviewGroupPadding.top(),
- max - min,
- st::mediaviewGroupHeight);
- }
- GroupThumbs::~GroupThumbs() = default;
- } // namespace View
- } // namespace Media
|