/* 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 "data/data_photo.h" #include "data/data_document.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 "chat_helpers/send_context_menu.h" // SendMenu::FillSendMenu #include "core/click_handler_types.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" #include "ui/widgets/popup_menu.h" #include "ui/effects/ripple_animation.h" #include "ui/image/image.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 namespace ChatHelpers { namespace { constexpr auto kSearchRequestDelay = 400; constexpr auto kInlineItemsMaxPerRow = 5; constexpr auto kSearchBotUsername = "gif"_cs; } // namespace void AddGifAction( Fn &&, const style::icon*)> callback, not_null document) { 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); callback(text, [=] { Api::ToggleSavedGif( document, Data::FileOriginSavedGifs(), !saved); auto &data = document->owner(); if (saved) { data.stickers().savedGifsRef().remove(index); document->session().local().writeSavedGifs(); } data.stickers().notifySavedGifsUpdated(); }, saved ? &st::menuIconDelete : &st::menuIconGif); } class GifsListWidget::Footer : public TabbedSelector::InnerFooter { public: Footer(not_null parent); void stealFocus(); void returnFocus(); void setLoading(bool loading) { _cancel->setLoadingAnimation(loading); } protected: void paintEvent(QPaintEvent *e) override; void resizeEvent(QResizeEvent *e) override; void processPanelHideFinished() override; private: not_null _pan; object_ptr _field; object_ptr _cancel; QPointer _focusTakenFrom; }; GifsListWidget::Footer::Footer(not_null parent) : InnerFooter(parent) , _pan(parent) , _field(this, st::gifsSearchField, tr::lng_gifs_search()) , _cancel(this, st::gifsSearchCancel) { connect(_field, &Ui::InputField::submitted, [=] { _pan->sendInlineRequest(); }); connect(_field, &Ui::InputField::cancelled, [=] { if (_field->getLastText().isEmpty()) { _pan->cancelled(); } else { _field->setText(QString()); } }); connect(_field, &Ui::InputField::changed, [=] { _cancel->toggle( !_field->getLastText().isEmpty(), anim::type::normal); _pan->searchForGifs(_field->getLastText()); }); _cancel->setClickedCallback([=] { _field->setText(QString()); }); } void GifsListWidget::Footer::stealFocus() { if (!_focusTakenFrom) { _focusTakenFrom = QApplication::focusWidget(); } _field->setFocus(); } void GifsListWidget::Footer::returnFocus() { if (_focusTakenFrom) { if (_field->hasFocus()) { _focusTakenFrom->setFocus(); } _focusTakenFrom = nullptr; } } void GifsListWidget::Footer::paintEvent(QPaintEvent *e) { Painter p(this); st::gifsSearchIcon.paint(p, st::gifsSearchIconPosition.x(), st::gifsSearchIconPosition.y(), width()); } void GifsListWidget::Footer::resizeEvent(QResizeEvent *e) { auto fieldWidth = width() - st::gifsSearchFieldPosition.x() - st::gifsSearchCancelPosition.x() - st::gifsSearchCancel.width; _field->resizeToWidth(fieldWidth); _field->moveToLeft(st::gifsSearchFieldPosition.x(), st::gifsSearchFieldPosition.y()); _cancel->moveToRight(st::gifsSearchCancelPosition.x(), st::gifsSearchCancelPosition.y()); } void GifsListWidget::Footer::processPanelHideFinished() { // Preserve panel state through visibility toggles. //_field->setText(QString()); } GifsListWidget::GifsListWidget( QWidget *parent, not_null controller) : Inner(parent, controller) , _api(&controller->session().mtp()) , _section(Section::Gifs) , _updateInlineItems([=] { updateInlineItems(); }) , _mosaic(st::emojiPanWidth - st::inlineResultsLeft) , _previewTimer([=] { showPreview(); }) { setMouseTracking(true); setAttribute(Qt::WA_OpaquePaintEvent); _inlineRequestTimer.setSingleShot(true); connect( &_inlineRequestTimer, &QTimer::timeout, this, [=] { sendInlineRequest(); }); controller->session().data().stickers().savedGifsUpdated( ) | rpl::start_with_next([=] { refreshSavedGifs(); }, lifetime()); controller->session().downloaderTaskFinished( ) | rpl::start_with_next([=] { update(); }, lifetime()); controller->gifPauseLevelChanged( ) | rpl::start_with_next([=] { if (!controller->isGifPausedAtLeastFor( Window::GifPauseReason::SavedGifs)) { update(); } }, lifetime()); sizeValue( ) | rpl::start_with_next([=](const QSize &s) { _mosaic.setFullWidth(s.width()); }, lifetime()); _mosaic.setOffset( st::inlineResultsLeft - st::roundRadiusSmall, st::stickerPanPadding); _mosaic.setRightSkip(st::inlineResultsSkip); } rpl::producer GifsListWidget::fileChosen() const { return _fileChosen.events(); } auto GifsListWidget::photoChosen() const -> rpl::producer { return _photoChosen.events(); } auto GifsListWidget::inlineResultChosen() const -> rpl::producer { return _inlineResultChosen.events(); } object_ptr GifsListWidget::createFooter() { Expects(_footer == nullptr); auto result = object_ptr