| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049 |
- /*
- 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 "chat_helpers/gifs_list_widget.h"
- #include "api/api_toggling_media.h" // Api::ToggleSavedGif
- #include "base/const_string.h"
- #include "base/qt/qt_key_modifiers.h"
- #include "chat_helpers/stickers_list_footer.h"
- #include "data/data_photo.h"
- #include "data/data_document.h"
- #include "data/stickers/data_custom_emoji.h"
- #include "data/data_session.h"
- #include "data/data_user.h"
- #include "data/data_file_origin.h"
- #include "data/data_photo_media.h"
- #include "data/data_document_media.h"
- #include "data/stickers/data_stickers.h"
- #include "menu/menu_send.h" // SendMenu::FillSendMenu
- #include "mtproto/mtproto_config.h"
- #include "core/click_handler_types.h"
- #include "ui/controls/tabbed_search.h"
- #include "ui/layers/generic_box.h"
- #include "ui/widgets/buttons.h"
- #include "ui/widgets/fields/input_field.h"
- #include "ui/widgets/popup_menu.h"
- #include "ui/effects/ripple_animation.h"
- #include "ui/image/image.h"
- #include "ui/painter.h"
- #include "boxes/send_gif_with_caption_box.h"
- #include "boxes/stickers_box.h"
- #include "inline_bots/inline_bot_result.h"
- #include "storage/localstorage.h"
- #include "lang/lang_keys.h"
- #include "layout/layout_position.h"
- #include "mainwindow.h"
- #include "main/main_session.h"
- #include "window/window_session_controller.h"
- #include "history/view/history_view_cursor_state.h"
- #include "storage/storage_account.h" // Account::writeSavedGifs
- #include "styles/style_chat_helpers.h"
- #include "styles/style_menu_icons.h"
- #include <QtWidgets/QApplication>
- namespace ChatHelpers {
- namespace {
- constexpr auto kSearchRequestDelay = 400;
- constexpr auto kMinRepaintDelay = crl::time(33);
- constexpr auto kMinAfterScrollDelay = crl::time(33);
- } // namespace
- void AddGifAction(
- Fn<void(QString, Fn<void()> &&, const style::icon*)> callback,
- std::shared_ptr<Show> show,
- not_null<DocumentData*> document,
- const style::ComposeIcons *iconsOverride) {
- if (!document->isGifv()) {
- return;
- }
- auto &data = document->owner();
- const auto index = data.stickers().savedGifs().indexOf(document);
- const auto saved = (index >= 0);
- const auto text = (saved
- ? tr::lng_context_delete_gif
- : tr::lng_context_save_gif)(tr::now);
- const auto &icons = iconsOverride
- ? *iconsOverride
- : st::defaultComposeIcons;
- callback(text, [=] {
- Api::ToggleSavedGif(
- show,
- document,
- Data::FileOriginSavedGifs(),
- !saved);
- auto &data = document->owner();
- if (saved) {
- data.stickers().savedGifsRef().remove(index);
- document->session().local().writeSavedGifs();
- }
- data.stickers().notifySavedGifsUpdated();
- }, saved ? &icons.menuGifRemove : &icons.menuGifAdd);
- }
- GifsListWidget::GifsListWidget(
- QWidget *parent,
- not_null<Window::SessionController*> controller,
- PauseReason level)
- : GifsListWidget(parent, {
- .show = controller->uiShow(),
- .paused = Window::PausedIn(controller, level),
- }) {
- }
- GifsListWidget::GifsListWidget(
- QWidget *parent,
- GifsListDescriptor &&descriptor)
- : Inner(
- parent,
- descriptor.st ? *descriptor.st : st::defaultEmojiPan,
- descriptor.show,
- descriptor.paused)
- , _show(std::move(descriptor.show))
- , _api(&session().mtp())
- , _section(Section::Gifs)
- , _updateInlineItems([=] { updateInlineItems(); })
- , _mosaic(st::emojiPanWidth - st::inlineResultsLeft)
- , _previewTimer([=] { showPreview(); }) {
- setMouseTracking(true);
- setAttribute(Qt::WA_OpaquePaintEvent);
- setupSearch();
- _inlineRequestTimer.setSingleShot(true);
- connect(
- &_inlineRequestTimer,
- &QTimer::timeout,
- this,
- [=] { sendInlineRequest(); });
- session().data().stickers().savedGifsUpdated(
- ) | rpl::start_with_next([=] {
- refreshSavedGifs();
- }, lifetime());
- session().downloaderTaskFinished(
- ) | rpl::start_with_next([=] {
- updateInlineItems();
- }, lifetime());
- _show->pauseChanged(
- ) | rpl::start_with_next([=] {
- if (!paused()) {
- updateInlineItems();
- }
- }, lifetime());
- sizeValue(
- ) | rpl::start_with_next([=](const QSize &s) {
- _mosaic.setFullWidth(s.width());
- }, lifetime());
- _mosaic.setPadding(st::gifsPadding
- + QMargins(-st::emojiPanRadius, _search->height(), 0, 0));
- _mosaic.setRightSkip(st::inlineResultsSkip);
- }
- rpl::producer<FileChosen> GifsListWidget::fileChosen() const {
- return _fileChosen.events();
- }
- rpl::producer<PhotoChosen> GifsListWidget::photoChosen() const {
- return _photoChosen.events();
- }
- auto GifsListWidget::inlineResultChosen() const
- -> rpl::producer<InlineChosen> {
- return _inlineResultChosen.events();
- }
- object_ptr<TabbedSelector::InnerFooter> GifsListWidget::createFooter() {
- Expects(_footer == nullptr);
- using FooterDescriptor = StickersListFooter::Descriptor;
- auto result = object_ptr<StickersListFooter>(FooterDescriptor{
- .session = &session(),
- .paused = pausedMethod(),
- .parent = this,
- .st = &st(),
- .features = { .stickersSettings = false },
- });
- _footer = result;
- _chosenSetId = Data::Stickers::RecentSetId;
- GifSectionsValue(
- &session()
- ) | rpl::start_with_next([=](std::vector<GifSection> &&list) {
- _sections = std::move(list);
- refreshIcons();
- }, _footer->lifetime());
- _footer->setChosen(
- ) | rpl::start_with_next([=](uint64 setId) {
- if (_search) {
- _search->cancel();
- }
- _chosenSetId = setId;
- refreshIcons();
- const auto i = ranges::find(_sections, setId, [](GifSection value) {
- return value.document->id;
- });
- searchForGifs((i != end(_sections)) ? i->emoji->text() : QString());
- }, _footer->lifetime());
- return result;
- }
- void GifsListWidget::refreshIcons() {
- if (_footer) {
- _footer->refreshIcons(
- fillIcons(),
- _chosenSetId,
- nullptr,
- ValidateIconAnimations::None);
- }
- }
- std::vector<StickerIcon> GifsListWidget::fillIcons() {
- auto result = std::vector<StickerIcon>();
- result.reserve(_sections.size() + 1);
- result.emplace_back(Data::Stickers::RecentSetId);
- const auto side = StickersListFooter::IconFrameSize();
- for (const auto §ion : _sections) {
- const auto s = section.document;
- const auto id = s->id;
- const auto size = s->hasThumbnail()
- ? QSize(
- s->thumbnailLocation().width(),
- s->thumbnailLocation().height())
- : QSize();
- const auto pix = size.scaled(side, side, Qt::KeepAspectRatio);
- const auto owner = &s->owner();
- const auto already = _fakeSets.find(id);
- const auto set = (already != end(_fakeSets))
- ? already
- : _fakeSets.emplace(
- id,
- std::make_unique<Data::StickersSet>(
- owner,
- id,
- 0,
- 0,
- QString(),
- QString(),
- 0,
- Data::StickersSetFlag::Special,
- 0)).first;
- result.emplace_back(set->second.get(), s, pix.width(), pix.height());
- }
- return result;
- }
- void GifsListWidget::visibleTopBottomUpdated(
- int visibleTop,
- int visibleBottom) {
- const auto top = getVisibleTop();
- Inner::visibleTopBottomUpdated(visibleTop, visibleBottom);
- if (top != getVisibleTop()) {
- _lastScrolledAt = crl::now();
- update();
- }
- checkLoadMore();
- }
- void GifsListWidget::checkLoadMore() {
- auto visibleHeight = (getVisibleBottom() - getVisibleTop());
- if (getVisibleBottom() + visibleHeight > height()) {
- sendInlineRequest();
- }
- }
- int GifsListWidget::countDesiredHeight(int newWidth) {
- return _mosaic.countDesiredHeight(newWidth);
- }
- GifsListWidget::~GifsListWidget() {
- clearInlineRows(true);
- deleteUnusedGifLayouts();
- deleteUnusedInlineLayouts();
- }
- void GifsListWidget::cancelGifsSearch() {
- _search->setLoading(false);
- if (_inlineRequestId) {
- _api.request(_inlineRequestId).cancel();
- _inlineRequestId = 0;
- }
- _inlineRequestTimer.stop();
- _inlineQuery = _inlineNextQuery = _inlineNextOffset = QString();
- _inlineCache.clear();
- refreshInlineRows(nullptr, true);
- }
- void GifsListWidget::inlineResultsDone(const MTPmessages_BotResults &result) {
- _search->setLoading(false);
- _inlineRequestId = 0;
- auto it = _inlineCache.find(_inlineQuery);
- auto adding = (it != _inlineCache.cend());
- if (result.type() == mtpc_messages_botResults) {
- auto &d = result.c_messages_botResults();
- session().data().processUsers(d.vusers());
- auto &v = d.vresults().v;
- auto queryId = d.vquery_id().v;
- if (it == _inlineCache.cend()) {
- it = _inlineCache.emplace(
- _inlineQuery,
- std::make_unique<InlineCacheEntry>()).first;
- }
- const auto entry = it->second.get();
- entry->nextOffset = qs(d.vnext_offset().value_or_empty());
- if (const auto count = v.size()) {
- entry->results.reserve(entry->results.size() + count);
- }
- auto added = 0;
- for (const auto &res : v) {
- auto result = InlineBots::Result::Create(
- &session(),
- queryId,
- res);
- if (result) {
- ++added;
- entry->results.push_back(std::move(result));
- }
- }
- if (!added) {
- entry->nextOffset = QString();
- }
- } else if (adding) {
- it->second->nextOffset = QString();
- }
- if (!showInlineRows(!adding)) {
- it->second->nextOffset = QString();
- }
- checkLoadMore();
- }
- void GifsListWidget::paintEvent(QPaintEvent *e) {
- Painter p(this);
- auto clip = e->rect();
- p.fillRect(clip, st().bg);
- paintInlineItems(p, clip);
- }
- void GifsListWidget::paintInlineItems(Painter &p, QRect clip) {
- if (_mosaic.empty()) {
- p.setFont(st::normalFont);
- p.setPen(st::noContactsColor);
- auto text = _inlineQuery.isEmpty()
- ? tr::lng_gifs_no_saved(tr::now)
- : tr::lng_inline_bot_no_results(tr::now);
- p.drawText(QRect(0, 0, width(), (height() / 3) * 2 + st::normalFont->height), text, style::al_center);
- return;
- }
- const auto gifPaused = paused();
- using namespace InlineBots::Layout;
- PaintContext context(crl::now(), false, gifPaused, false);
- auto paintItem = [&](not_null<const ItemBase*> item, QPoint point) {
- p.translate(point.x(), point.y());
- item->paint(
- p,
- clip.translated(-point),
- &context);
- p.translate(-point.x(), -point.y());
- };
- _mosaic.paint(std::move(paintItem), clip);
- }
- void GifsListWidget::mousePressEvent(QMouseEvent *e) {
- if (e->button() != Qt::LeftButton) {
- return;
- }
- _lastMousePos = e->globalPos();
- updateSelected();
- _pressed = _selected;
- ClickHandler::pressed();
- _previewTimer.callOnce(QApplication::startDragTime());
- }
- base::unique_qptr<Ui::PopupMenu> GifsListWidget::fillContextMenu(
- const SendMenu::Details &details) {
- if (_selected < 0 || _pressed >= 0) {
- return nullptr;
- }
- auto menu = base::make_unique_q<Ui::PopupMenu>(this, st().menu);
- const auto selected = _selected;
- const auto send = crl::guard(this, [=](Api::SendOptions options) {
- selectInlineResult(selected, options, true);
- });
- const auto item = _mosaic.maybeItemAt(_selected);
- const auto isInlineResult = !item->getPhoto()
- && !item->getDocument()
- && item->getResult();
- const auto icons = &st().icons;
- auto copyDetails = details;
- if (isInlineResult) {
- // inline results don't have effects
- copyDetails.effectAllowed = false;
- }
- // In case we're adding items after FillSendMenu we have
- // to pass nullptr for showForEffect and attach selector later.
- // Otherwise added items widths won't be respected in menu geometry.
- SendMenu::FillSendMenu(
- menu,
- nullptr, // showForMenu
- copyDetails,
- SendMenu::DefaultCallback(_show, send),
- icons);
- if (!isInlineResult && _inlineQueryPeer) {
- auto done = crl::guard(this, [=](
- Api::SendOptions options,
- TextWithTags text) {
- selectInlineResult(selected, options, true, std::move(text));
- });
- const auto show = _show;
- const auto peer = _inlineQueryPeer;
- menu->addAction(tr::lng_send_gif_with_caption(tr::now), [=] {
- show->show(Box(
- Ui::SendGifWithCaptionBox,
- item->getDocument(),
- peer,
- copyDetails,
- std::move(done)));
- }, &st::menuIconEdit);
- }
- if (const auto item = _mosaic.maybeItemAt(_selected)) {
- const auto document = item->getDocument()
- ? item->getDocument() // Saved GIF.
- : item->getPreviewDocument(); // Searched GIF.
- if (document) {
- auto callback = [&](
- const QString &text,
- Fn<void()> &&done,
- const style::icon *icon) {
- menu->addAction(text, std::move(done), icon);
- };
- AddGifAction(std::move(callback), _show, document, icons);
- }
- }
- SendMenu::AttachSendMenuEffect(
- menu,
- _show,
- copyDetails,
- SendMenu::DefaultCallback(_show, send));
- return menu;
- }
- void GifsListWidget::mouseReleaseEvent(QMouseEvent *e) {
- _previewTimer.cancel();
- auto pressed = std::exchange(_pressed, -1);
- auto activated = ClickHandler::unpressed();
- if (_previewShown) {
- _previewShown = false;
- return;
- }
- _lastMousePos = e->globalPos();
- updateSelected();
- if (_selected < 0 || _selected != pressed || !activated) {
- return;
- }
- if (dynamic_cast<InlineBots::Layout::SendClickHandler*>(activated.get())) {
- selectInlineResult(_selected, {});
- } else {
- ActivateClickHandler(window(), activated, {
- e->button(),
- QVariant::fromValue(ClickHandlerContext{
- .show = _show,
- })
- });
- }
- }
- void GifsListWidget::selectInlineResult(
- int index,
- Api::SendOptions options,
- bool forceSend,
- TextWithTags caption) {
- const auto item = _mosaic.maybeItemAt(index);
- if (!item) {
- return;
- }
- const auto messageSendingFrom = [&] {
- if (options.scheduled) {
- return Ui::MessageSendingAnimationFrom();
- }
- const auto rect = item->innerContentRect().translated(
- _mosaic.findRect(index).topLeft());
- return Ui::MessageSendingAnimationFrom{
- .type = Ui::MessageSendingAnimationFrom::Type::Gif,
- .localId = session().data().nextLocalMessageId(),
- .globalStartGeometry = mapToGlobal(rect),
- .crop = true,
- };
- };
- forceSend |= base::IsCtrlPressed();
- if (const auto photo = item->getPhoto()) {
- using Data::PhotoSize;
- const auto media = photo->activeMediaView();
- if (forceSend
- || (media && media->image(PhotoSize::Thumbnail))
- || (media && media->image(PhotoSize::Large))) {
- _photoChosen.fire({
- .photo = photo,
- .options = options });
- } else if (!photo->loading(PhotoSize::Thumbnail)) {
- photo->load(PhotoSize::Thumbnail, Data::FileOrigin());
- }
- } else if (const auto document = item->getDocument()) {
- const auto media = document->activeMediaView();
- const auto preview = Data::VideoPreviewState(media.get());
- if (forceSend || (media && preview.loaded())) {
- _fileChosen.fire({
- .document = document,
- .options = options,
- .messageSendingFrom = messageSendingFrom(),
- .caption = std::move(caption),
- });
- } else if (!preview.usingThumbnail()) {
- if (preview.loading()) {
- document->cancel();
- } else {
- document->save(
- document->stickerOrGifOrigin(),
- QString());
- }
- }
- } else if (const auto inlineResult = item->getResult()) {
- if (inlineResult->onChoose(item)) {
- options.hideViaBot = true;
- _inlineResultChosen.fire({
- .result = inlineResult,
- .bot = _searchBot,
- .options = options,
- .messageSendingFrom = messageSendingFrom(),
- });
- }
- }
- }
- void GifsListWidget::mouseMoveEvent(QMouseEvent *e) {
- _lastMousePos = e->globalPos();
- updateSelected();
- }
- void GifsListWidget::leaveEventHook(QEvent *e) {
- clearSelection();
- }
- void GifsListWidget::leaveToChildEvent(QEvent *e, QWidget *child) {
- clearSelection();
- }
- void GifsListWidget::enterFromChildEvent(QEvent *e, QWidget *child) {
- _lastMousePos = QCursor::pos();
- updateSelected();
- }
- void GifsListWidget::clearSelection() {
- if (_selected >= 0) {
- ClickHandler::clearActive(_mosaic.itemAt(_selected));
- setCursor(style::cur_default);
- }
- _selected = _pressed = -1;
- repaintItems();
- }
- TabbedSelector::InnerFooter *GifsListWidget::getFooter() const {
- return _footer;
- }
- void GifsListWidget::processHideFinished() {
- clearSelection();
- clearHeavyData();
- if (_footer) {
- _footer->clearHeavyData();
- }
- }
- void GifsListWidget::processPanelHideFinished() {
- clearHeavyData();
- if (_footer) {
- _footer->clearHeavyData();
- }
- }
- void GifsListWidget::clearHeavyData() {
- // Preserve panel state through visibility toggles.
- //clearInlineRows(false);
- for (const auto &[document, layout] : _gifLayouts) {
- layout->unloadHeavyPart();
- }
- for (const auto &[document, layout] : _inlineLayouts) {
- layout->unloadHeavyPart();
- }
- }
- void GifsListWidget::refreshSavedGifs() {
- if (_section == Section::Gifs) {
- clearInlineRows(false);
- const auto &saved = session().data().stickers().savedGifs();
- if (!saved.isEmpty()) {
- const auto layouts = ranges::views::all(
- saved
- ) | ranges::views::transform([&](not_null<DocumentData*> gif) {
- return layoutPrepareSavedGif(gif);
- }) | ranges::views::filter([](const LayoutItem *item) {
- return item != nullptr;
- }) | ranges::to<std::vector<not_null<LayoutItem*>>>;
- _mosaic.addItems(layouts);
- }
- deleteUnusedGifLayouts();
- resizeToWidth(width());
- repaintItems();
- }
- if (isVisible()) {
- updateSelected();
- } else {
- preloadImages();
- }
- }
- void GifsListWidget::clearInlineRows(bool resultsDeleted) {
- if (resultsDeleted) {
- _selected = _pressed = -1;
- } else {
- clearSelection();
- }
- _mosaic.clearRows(resultsDeleted);
- }
- GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareSavedGif(
- not_null<DocumentData*> document) {
- auto it = _gifLayouts.find(document);
- if (it == _gifLayouts.cend()) {
- if (auto layout = LayoutItem::createLayoutGif(this, document)) {
- it = _gifLayouts.emplace(document, std::move(layout)).first;
- it->second->initDimensions();
- } else {
- return nullptr;
- }
- }
- if (!it->second->maxWidth()) return nullptr;
- return it->second.get();
- }
- GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareInlineResult(
- std::shared_ptr<InlineResult> result) {
- const auto raw = result.get();
- auto it = _inlineLayouts.find(raw);
- if (it == _inlineLayouts.cend()) {
- if (auto layout = LayoutItem::createLayout(
- this,
- std::move(result),
- _inlineWithThumb)) {
- it = _inlineLayouts.emplace(raw, std::move(layout)).first;
- it->second->initDimensions();
- } else {
- return nullptr;
- }
- }
- if (!it->second->maxWidth()) return nullptr;
- return it->second.get();
- }
- void GifsListWidget::deleteUnusedGifLayouts() {
- if (_mosaic.empty() || _section != Section::Gifs) { // delete all
- _gifLayouts.clear();
- } else {
- for (auto i = _gifLayouts.begin(); i != _gifLayouts.cend();) {
- if (i->second->position() < 0) {
- i = _gifLayouts.erase(i);
- } else {
- ++i;
- }
- }
- }
- }
- void GifsListWidget::deleteUnusedInlineLayouts() {
- if (_mosaic.empty() || _section == Section::Gifs) { // delete all
- _inlineLayouts.clear();
- } else {
- for (auto i = _inlineLayouts.begin(); i != _inlineLayouts.cend();) {
- if (i->second->position() < 0) {
- i = _inlineLayouts.erase(i);
- } else {
- ++i;
- }
- }
- }
- }
- void GifsListWidget::preloadImages() {
- _mosaic.forEach([](not_null<const LayoutItem*> item) {
- item->preload();
- });
- }
- void GifsListWidget::switchToSavedGifs() {
- clearInlineRows(false);
- _section = Section::Gifs;
- refreshSavedGifs();
- scrollTo(0);
- }
- int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool resultsDeleted) {
- if (!entry) {
- if (resultsDeleted) {
- clearInlineRows(true);
- deleteUnusedInlineLayouts();
- }
- switchToSavedGifs();
- return 0;
- }
- clearSelection();
- _section = Section::Inlines;
- const auto count = int(entry->results.size());
- const auto from = validateExistingInlineRows(entry->results);
- auto added = 0;
- if (count) {
- const auto resultLayouts = entry->results | ranges::views::slice(
- from,
- count
- ) | ranges::views::transform([&](
- const std::shared_ptr<InlineBots::Result> &r) {
- return layoutPrepareInlineResult(r);
- }) | ranges::views::filter([](const LayoutItem *item) {
- return item != nullptr;
- }) | ranges::to<std::vector<not_null<LayoutItem*>>>;
- _mosaic.addItems(resultLayouts);
- added = resultLayouts.size();
- preloadImages();
- }
- resizeToWidth(width());
- repaintItems();
- _lastMousePos = QCursor::pos();
- updateSelected();
- return added;
- }
- int GifsListWidget::validateExistingInlineRows(const InlineResults &results) {
- const auto until = _mosaic.validateExistingRows([&](
- not_null<const LayoutItem*> item,
- int untilIndex) {
- return item->getResult().get() != results[untilIndex].get();
- }, results.size());
- if (_mosaic.empty()) {
- _inlineWithThumb = false;
- for (int i = until; i < results.size(); ++i) {
- if (results.at(i)->hasThumbDisplay()) {
- _inlineWithThumb = true;
- break;
- }
- }
- }
- return until;
- }
- void GifsListWidget::inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) {
- if (_selected < 0 || !isVisible()) {
- return;
- }
- if (const auto item = _mosaic.maybeItemAt(_selected)) {
- if (layout == item) {
- updateSelected();
- }
- }
- }
- void GifsListWidget::inlineItemRepaint(
- const InlineBots::Layout::ItemBase *layout) {
- updateInlineItems();
- }
- bool GifsListWidget::inlineItemVisible(
- const InlineBots::Layout::ItemBase *layout) {
- auto position = layout->position();
- if (position < 0 || !isVisible()) {
- return false;
- }
- const auto &[row, column] = Layout::IndexToPosition(position);
- auto top = 0;
- for (auto i = 0; i != row; ++i) {
- top += _mosaic.rowHeightAt(i);
- }
- return (top < getVisibleBottom())
- && (top + _mosaic.itemAt(row, column)->height() > getVisibleTop());
- }
- Data::FileOrigin GifsListWidget::inlineItemFileOrigin() {
- return _inlineQuery.isEmpty()
- ? Data::FileOriginSavedGifs()
- : Data::FileOrigin();
- }
- void GifsListWidget::afterShown() {
- if (_search) {
- _search->stealFocus();
- }
- }
- void GifsListWidget::beforeHiding() {
- if (_search) {
- _search->returnFocus();
- }
- }
- bool GifsListWidget::refreshInlineRows(int32 *added) {
- auto it = _inlineCache.find(_inlineQuery);
- const InlineCacheEntry *entry = nullptr;
- if (it != _inlineCache.cend()) {
- entry = it->second.get();
- _inlineNextOffset = it->second->nextOffset;
- }
- auto result = refreshInlineRows(entry, false);
- if (added) *added = result;
- return (entry != nullptr);
- }
- void GifsListWidget::setupSearch() {
- const auto session = &_show->session();
- _search = MakeSearch(this, st(), [=](std::vector<QString> &&query) {
- const auto accumulated = ranges::accumulate(query, QString(), [](
- QString a,
- QString b) {
- return a.isEmpty() ? b : (a + ' ' + b);
- });
- _chosenSetId = accumulated.isEmpty()
- ? Data::Stickers::RecentSetId
- : SearchEmojiSectionSetId();
- refreshIcons();
- searchForGifs(accumulated);
- }, session, TabbedSearchType::Emoji);
- }
- int32 GifsListWidget::showInlineRows(bool newResults) {
- auto added = 0;
- refreshInlineRows(&added);
- if (newResults) {
- scrollTo(0);
- }
- return added;
- }
- void GifsListWidget::searchForGifs(const QString &query) {
- if (query.isEmpty()) {
- cancelGifsSearch();
- return;
- }
- if (_inlineQuery != query) {
- _search->setLoading(false);
- if (_inlineRequestId) {
- _api.request(_inlineRequestId).cancel();
- _inlineRequestId = 0;
- }
- if (_inlineCache.find(query) != _inlineCache.cend()) {
- _inlineRequestTimer.stop();
- _inlineQuery = _inlineNextQuery = query;
- showInlineRows(true);
- } else {
- _inlineNextQuery = query;
- _inlineRequestTimer.start(kSearchRequestDelay);
- }
- }
- if (!_searchBot && !_searchBotRequestId) {
- const auto username = session().serverConfig().gifSearchUsername;
- _searchBotRequestId = _api.request(MTPcontacts_ResolveUsername(
- MTP_flags(0),
- MTP_string(username),
- MTP_string()
- )).done([=](const MTPcontacts_ResolvedPeer &result) {
- auto &data = result.data();
- session().data().processUsers(data.vusers());
- session().data().processChats(data.vchats());
- const auto peer = session().data().peerLoaded(
- peerFromMTP(data.vpeer()));
- if (const auto user = peer ? peer->asUser() : nullptr) {
- _searchBot = user;
- }
- }).send();
- }
- }
- void GifsListWidget::cancelled() {
- _cancelled.fire({});
- }
- rpl::producer<> GifsListWidget::cancelRequests() const {
- return _cancelled.events();
- }
- void GifsListWidget::sendInlineRequest() {
- if (_inlineRequestId || !_inlineQueryPeer || _inlineNextQuery.isEmpty()) {
- return;
- }
- if (!_searchBot) {
- // Wait for the bot being resolved.
- _search->setLoading(true);
- _inlineRequestTimer.start(kSearchRequestDelay);
- return;
- }
- _inlineRequestTimer.stop();
- _inlineQuery = _inlineNextQuery;
- auto nextOffset = QString();
- auto it = _inlineCache.find(_inlineQuery);
- if (it != _inlineCache.cend()) {
- nextOffset = it->second->nextOffset;
- if (nextOffset.isEmpty()) {
- _search->setLoading(false);
- return;
- }
- }
- _search->setLoading(true);
- _inlineRequestId = _api.request(MTPmessages_GetInlineBotResults(
- MTP_flags(0),
- _searchBot->inputUser,
- _inlineQueryPeer->input,
- MTPInputGeoPoint(),
- MTP_string(_inlineQuery),
- MTP_string(nextOffset)
- )).done([this](const MTPmessages_BotResults &result) {
- inlineResultsDone(result);
- }).fail([this] {
- // show error?
- _search->setLoading(false);
- _inlineRequestId = 0;
- }).handleAllErrors().send();
- }
- void GifsListWidget::refreshRecent() {
- if (_section == Section::Gifs) {
- refreshSavedGifs();
- }
- }
- void GifsListWidget::updateSelected() {
- if (_pressed >= 0 && !_previewShown) {
- return;
- }
- const auto p = mapFromGlobal(_lastMousePos);
- const auto sx = rtl() ? (width() - p.x()) : p.x();
- const auto sy = p.y();
- const auto &[index, exact, relative] = _mosaic.findByPoint({ sx, sy });
- const auto selected = exact ? index : -1;
- const auto item = exact ? _mosaic.itemAt(selected).get() : nullptr;
- const auto link = exact ? item->getState(relative, {}).link : nullptr;
- if (_selected != selected) {
- if (const auto s = _mosaic.maybeItemAt(_selected)) {
- s->update();
- }
- _selected = selected;
- if (item) {
- item->update();
- }
- if (_previewShown && _selected >= 0 && _pressed != _selected) {
- _pressed = _selected;
- if (item) {
- if (const auto preview = item->getPreviewDocument()) {
- _show->showMediaPreview(
- Data::FileOriginSavedGifs(),
- preview);
- } else if (const auto preview = item->getPreviewPhoto()) {
- _show->showMediaPreview(Data::FileOrigin(), preview);
- }
- }
- }
- }
- if (ClickHandler::setActive(link, item)) {
- setCursor(link ? style::cur_pointer : style::cur_default);
- }
- }
- void GifsListWidget::showPreview() {
- if (_pressed < 0) {
- return;
- }
- if (const auto layout = _mosaic.maybeItemAt(_pressed)) {
- if (const auto previewDocument = layout->getPreviewDocument()) {
- _previewShown = _show->showMediaPreview(
- Data::FileOriginSavedGifs(),
- previewDocument);
- } else if (const auto previewPhoto = layout->getPreviewPhoto()) {
- _previewShown = _show->showMediaPreview(
- Data::FileOrigin(),
- previewPhoto);
- }
- }
- }
- void GifsListWidget::updateInlineItems() {
- const auto now = crl::now();
- const auto delay = std::max(
- _lastScrolledAt + kMinAfterScrollDelay - now,
- _lastUpdatedAt + kMinRepaintDelay - now);
- if (delay <= 0) {
- repaintItems(now);
- } else if (!_updateInlineItems.isActive()
- || _updateInlineItems.remainingTime() > kMinRepaintDelay) {
- _updateInlineItems.callOnce(std::max(delay, kMinRepaintDelay));
- }
- }
- void GifsListWidget::repaintItems(crl::time now) {
- _lastUpdatedAt = now ? now : crl::now();
- update();
- }
- } // namespace ChatHelpers
|