Add search in GIFs to EmojiPanel.

This commit is contained in:
John Preston 2017-03-31 18:50:02 +03:00
parent 0690c4f98c
commit e8ed307278
18 changed files with 480 additions and 478 deletions

View File

@ -774,13 +774,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_recent_stickers" = "Frequently used";
"lng_switch_stickers" = "Stickers";
"lng_switch_stickers_gifs" = "GIFs & Stickers";
"lng_switch_emoji" = "Emoji";
"lng_switch_gifs" = "GIFs";
"lng_stickers_featured_add" = "Add";
"lng_gifs_search" = "Search GIFs";
"lng_saved_gifs" = "Saved GIFs";
"lng_inline_bot_results" = "Results from {inline_bot}";
"lng_inline_bot_no_results" = "No results";
"lng_inline_bot_via" = "via {inline_bot}";

View File

@ -1138,7 +1138,7 @@ namespace {
if (saved.size() > Global::SavedGifsLimit()) saved.pop_back();
Local::writeSavedGifs();
if (App::main()) emit App::main()->savedGifsUpdated();
AuthSession::Current().data().savedGifsUpdated().notify();
cSetLastSavedGifsUpdate(0);
App::main()->updateStickers();
}
@ -1146,8 +1146,8 @@ namespace {
void checkSavedGif(HistoryItem *item) {
if (!item->Has<HistoryMessageForwarded>() && (item->out() || item->history()->peer == App::self())) {
if (HistoryMedia *media = item->getMedia()) {
if (DocumentData *doc = media->getDocument()) {
if (auto media = item->getMedia()) {
if (auto doc = media->getDocument()) {
if (doc->isGifv()) {
addSavedGif(doc);
}

View File

@ -47,6 +47,9 @@ public:
base::Observable<void> &moreChatsLoaded() {
return _moreChatsLoaded;
}
base::Observable<void> &savedGifsUpdated() {
return _savedGifsUpdated;
}
void copyFrom(const AuthSessionData &other) {
_variables = other._variables;
@ -76,6 +79,7 @@ private:
base::Variable<bool> _contactsLoaded = { false };
base::Variable<bool> _allChatsLoaded = { false };
base::Observable<void> _moreChatsLoaded;
base::Observable<void> _savedGifsUpdated;
Variables _variables;
};

View File

@ -237,6 +237,43 @@ contactsAboutFg: windowSubTextFgOver;
contactsAboutTop: 60px;
contactsAboutBottom: 19px;
contactsSearchField: InputField(defaultInputField) {
textBg: transparent;
textMargins: margins(2px, 7px, 2px, 0px);
placeholderFg: placeholderFg;
placeholderFgActive: placeholderFgActive;
placeholderFgError: placeholderFgActive;
placeholderMargins: margins(2px, 0px, 2px, 0px);
placeholderScale: 0.;
placeholderFont: normalFont;
border: 0px;
borderActive: 0px;
heightMin: 32px;
font: normalFont;
}
contactsSearchCancel: CrossButton {
width: 44px;
height: 44px;
cross: CrossAnimation {
size: 36px;
skip: 12px;
stroke: 2px;
minScale: 0.3;
}
crossFg: boxTitleCloseFg;
crossFgOver: boxTitleCloseFgOver;
crossPosition: point(4px, 4px);
duration: 150;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}
contactsMultiSelect: MultiSelect {
bg: boxSearchBg;
padding: margins(8px, 6px, 8px, 6px);
@ -271,47 +308,12 @@ contactsMultiSelect: MultiSelect {
}
itemSkip: 8px;
field: InputField(defaultInputField) {
textBg: transparent;
textMargins: margins(2px, 7px, 2px, 0px);
placeholderFg: placeholderFg;
placeholderFgActive: placeholderFgActive;
placeholderFgError: placeholderFgActive;
placeholderMargins: margins(2px, 0px, 2px, 0px);
placeholderScale: 0.;
placeholderFont: normalFont;
border: 0px;
borderActive: 0px;
heightMin: 32px;
font: normalFont;
}
field: contactsSearchField;
fieldMinWidth: 42px;
fieldIcon: boxFieldSearchIcon;
fieldIconSkip: 36px;
fieldCancel: CrossButton {
width: 44px;
height: 44px;
cross: CrossAnimation {
size: 36px;
skip: 12px;
stroke: 2px;
minScale: 0.3;
}
crossFg: boxTitleCloseFg;
crossFgOver: boxTitleCloseFgOver;
crossPosition: point(4px, 4px);
duration: 150;
ripple: RippleAnimation(defaultRippleAnimation) {
color: windowBgOver;
}
}
fieldCancel: contactsSearchCancel;
fieldCancelSkip: 40px;
}
contactsPhotoCheckbox: RoundImageCheckbox {

View File

@ -79,7 +79,6 @@ enum {
ClipThreadsCount = 8,
AverageGifSize = 320 * 240,
WaitBeforeGifPause = 200, // wait 200ms for gif draw before pausing it
InlineBotRequestDelay = 400, // wait 400ms before context bot realtime request
RecentInlineBotsLimit = 10,
AVBlockSize = 4096, // 4Kb for ffmpeg blocksize

View File

@ -3360,10 +3360,8 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null<Window::Controller*>
void HistoryWidget::start() {
connect(App::main(), SIGNAL(stickersUpdated()), this, SLOT(onStickersUpdated()));
connect(App::main(), SIGNAL(savedGifsUpdated()), _emojiPanel, SLOT(refreshSavedGifs()));
updateRecentStickers();
if (App::main()) emit App::main()->savedGifsUpdated();
AuthSession::Current().data().savedGifsUpdated().notify();
connect(App::api(), SIGNAL(fullPeerUpdated(PeerData*)), this, SLOT(onFullPeerUpdated(PeerData*)));
}
@ -4176,7 +4174,7 @@ void HistoryWidget::savedGifsGot(const MTPmessages_SavedGifs &gifs) {
Local::writeSavedGifs();
if (App::main()) emit App::main()->savedGifsUpdated();
AuthSession::Current().data().savedGifsUpdated().notify();
}
void HistoryWidget::saveGif(DocumentData *doc) {
@ -4430,6 +4428,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re
_peer = App::peer(peerId);
_channel = peerToChannel(_peer->id);
_canSendMessages = canSendMessages(_peer);
_emojiPanel->setInlineQueryPeer(_peer);
}
updateTopBarSelection();

View File

@ -30,7 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "media/player/media_player_instance.h"
#include "history/history_location_manager.h"
#include "storage/localstorage.h"
#include "mainwidget.h"
#include "auth_session.h"
#include "lang.h"
namespace InlineBots {
@ -122,14 +122,14 @@ void Gif::setPosition(int32 position) {
}
void DeleteSavedGifClickHandler::onClickImpl() const {
int32 index = cSavedGifs().indexOf(_data);
auto index = cSavedGifs().indexOf(_data);
if (index >= 0) {
cRefSavedGifs().remove(index);
Local::writeSavedGifs();
MTP::send(MTPmessages_SaveGif(_data->mtpInput(), MTP_bool(true)));
}
if (App::main()) emit App::main()->savedGifsUpdated();
AuthSession::Current().data().savedGifsUpdated().notify();
}
void Gif::paint(Painter &p, const QRect &clip, const PaintContext *context) const {

View File

@ -39,6 +39,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace InlineBots {
namespace Layout {
namespace internal {
namespace {
constexpr auto kInlineBotRequestDelay = 400;
} // namespace
Inner::Inner(QWidget *parent) : TWidget(parent) {
setMaxHeight(st::emojiPanMaxHeight - st::emojiCategory.height);
@ -413,7 +418,6 @@ int Inner::refreshInlineRows(UserData *bot, const CacheEntry *entry, bool result
clearSelection();
t_assert(_inlineBot != 0);
_inlineBotTitle = lng_inline_bot_results(lt_inline_bot, _inlineBot->username.isEmpty() ? _inlineBot->name : ('@' + _inlineBot->username));
auto count = int(entry->results.size());
auto from = validateExistingInlineRows(entry->results);
@ -1048,7 +1052,7 @@ void Widget::queryInlineBot(UserData *bot, PeerData *peer, QString query) {
showInlineRows(true);
} else {
_inlineNextQuery = query;
_inlineRequestTimer.start(InlineBotRequestDelay);
_inlineRequestTimer.start(internal::kInlineBotRequestDelay);
}
}
}

View File

@ -120,7 +120,6 @@ private:
int _visibleBottom = 0;
UserData *_inlineBot;
QString _inlineBotTitle;
TimeMs _lastScrolled = 0;
QTimer _updateInlineItems;
bool _inlineWithThumb = false;

View File

@ -413,7 +413,6 @@ signals:
void dialogRowReplaced(Dialogs::Row *oldRow, Dialogs::Row *newRow);
void dialogsUpdated();
void stickersUpdated();
void savedGifsUpdated();
public slots:
void webPagesOrGamesUpdate();

View File

@ -364,11 +364,11 @@ private:
template <typename Request, typename, typename>
Sender::SpecificRequestBuilder<Request> Sender::request(Request &&request) noexcept {
return SpecificRequestBuilder<Request>(this, std::move(request));
return SpecificRequestBuilder<Request>(this, std::move(request));
}
inline Sender::SentRequestWrap Sender::request(mtpRequestId requestId) noexcept {
return SentRequestWrap(this, requestId);
return SentRequestWrap(this, requestId);
}
} // namespace MTP

View File

@ -28,7 +28,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/widgets/shadow.h"
#include "ui/widgets/discrete_sliders.h"
#include "ui/widgets/scroll_area.h"
#include "inline_bots/inline_bot_result.h"
#include "stickers/stickers.h"
#include "storage/localstorage.h"
#include "lang.h"
@ -332,16 +331,14 @@ EmojiPanel::EmojiPanel(QWidget *parent) : TWidget(parent)
connect(stickers(), SIGNAL(checkForHide()), this, SLOT(onCheckForHide()));
connect(gifs(), SIGNAL(selected(DocumentData*)), this, SIGNAL(stickerSelected(DocumentData*)));
connect(gifs(), SIGNAL(selected(PhotoData*)), this, SIGNAL(photoSelected(PhotoData*)));
connect(gifs(), SIGNAL(selected(InlineBots::Result*, UserData*)), this, SIGNAL(inlineResultSelected(InlineBots::Result*, UserData*)));
connect(gifs(), SIGNAL(emptyInlineRows()), this, SLOT(onEmptyInlineRows()));
connect(gifs(), SIGNAL(selected(InlineBots::Result*,UserData*)), this, SIGNAL(inlineResultSelected(InlineBots::Result*,UserData*)));
connect(gifs(), &GifsListWidget::cancelled, this, [this] {
hideAnimated();
});
_saveConfigTimer.setSingleShot(true);
connect(&_saveConfigTimer, SIGNAL(timeout()), this, SLOT(onSaveConfig()));
// inline bots
_inlineRequestTimer.setSingleShot(true);
connect(&_inlineRequestTimer, SIGNAL(timeout()), this, SLOT(onInlineRequest()));
if (cPlatform() == dbipMac || cPlatform() == dbipMacOld) {
connect(App::wnd()->windowHandle(), SIGNAL(activeChanged()), this, SLOT(onWndActiveChanged()));
}
@ -436,6 +433,7 @@ void EmojiPanel::paintEvent(QPaintEvent *e) {
_showAnimation.reset();
if (!switching && !opacityAnimating) {
showAll();
currentTab()->widget()->afterShown();
}
}
@ -455,6 +453,7 @@ void EmojiPanel::paintEvent(QPaintEvent *e) {
_slideAnimation.reset();
if (!opacityAnimating) {
showAll();
currentTab()->widget()->afterShown();
}
InvokeQueued(this, [this] {
if (_hideAfterSlide && !_a_slide.animating()) {
@ -567,15 +566,6 @@ void EmojiPanel::refreshStickers() {
if (isHidden() || _currentTabType != TabType::Stickers) {
stickers()->preloadImages();
}
update();
}
void EmojiPanel::refreshSavedGifs() {
gifs()->refreshSavedGifs();
if (isHidden() || _currentTabType != TabType::Gifs) {
gifs()->preloadImages();
}
update();
}
void EmojiPanel::opacityAnimationCallback() {
@ -586,6 +576,7 @@ void EmojiPanel::opacityAnimationCallback() {
hideFinished();
} else if (!_a_show.animating() && !_a_slide.animating()) {
showAll();
currentTab()->widget()->afterShown();
}
}
}
@ -613,6 +604,9 @@ void EmojiPanel::prepareCache() {
}
void EmojiPanel::startOpacityAnimation(bool hiding) {
if (!_scroll->isHidden()) {
currentTab()->widget()->beforeHiding();
}
_hiding = false;
prepareCache();
_hiding = hiding;
@ -747,6 +741,10 @@ void EmojiPanel::stickersInstalled(uint64 setId) {
showAnimated();
}
void EmojiPanel::setInlineQueryPeer(PeerData *peer) {
gifs()->setInlineQueryPeer(peer);
}
bool EmojiPanel::ui_isInlineItemBeingChosen() {
return (_currentTabType == TabType::Gifs && !isHidden());
}
@ -816,6 +814,10 @@ void EmojiPanel::switchTab() {
auto wasTab = _currentTabType;
currentTab()->saveScrollTop();
if (!_scroll->isHidden()) {
currentTab()->widget()->beforeHiding();
}
auto wasCache = grabForComplexAnimation(GrabType::Slide);
auto widget = _scroll->takeWidget<Inner>();
@ -828,6 +830,8 @@ void EmojiPanel::switchTab() {
if (_currentTabType == TabType::Gifs) {
Notify::clipStopperHidden(ClipStopperSavedGifsPanel);
}
currentTab()->widget()->refreshRecent();
currentTab()->widget()->preloadImages();
setWidgetToScrollArea();
auto nowCache = grabForComplexAnimation(GrabType::Slide);
@ -883,10 +887,6 @@ void EmojiPanel::onCheckForHide() {
}
}
void EmojiPanel::clearInlineBot() {
inlineBotChanged();
}
bool EmojiPanel::overlaps(const QRect &globalRect) const {
if (isHidden() || !_cache.isNull()) return false;
@ -896,147 +896,6 @@ bool EmojiPanel::overlaps(const QRect &globalRect) const {
|| inner.marginsRemoved(QMargins(0, st::buttonRadius, 0, st::buttonRadius)).contains(testRect);
}
void EmojiPanel::inlineBotChanged() {
if (!_inlineBot) return;
if (!isHidden() && !_hiding) {
if (!rect().contains(mapFromGlobal(QCursor::pos()))) {
hideAnimated();
}
}
if (_inlineRequestId) MTP::cancel(_inlineRequestId);
_inlineRequestId = 0;
_inlineQuery = _inlineNextQuery = _inlineNextOffset = QString();
_inlineBot = nullptr;
_inlineCache.clear();
gifs()->inlineBotChanged();
gifs()->hideInlineRowsPanel();
Notify::inlineBotRequesting(false);
}
void EmojiPanel::inlineResultsDone(const MTPmessages_BotResults &result) {
_inlineRequestId = 0;
Notify::inlineBotRequesting(false);
auto it = _inlineCache.find(_inlineQuery);
auto adding = (it != _inlineCache.cend());
if (result.type() == mtpc_messages_botResults) {
auto &d = result.c_messages_botResults();
auto &v = d.vresults.v;
auto queryId = d.vquery_id.v;
if (it == _inlineCache.cend()) {
it = _inlineCache.emplace(_inlineQuery, std::make_unique<InlineCacheEntry>()).first;
}
auto entry = it->second.get();
entry->nextOffset = qs(d.vnext_offset);
if (d.has_switch_pm() && d.vswitch_pm.type() == mtpc_inlineBotSwitchPM) {
auto &switchPm = d.vswitch_pm.c_inlineBotSwitchPM();
entry->switchPmText = qs(switchPm.vtext);
entry->switchPmStartToken = qs(switchPm.vstart_param);
}
if (auto count = v.size()) {
entry->results.reserve(entry->results.size() + count);
}
auto added = 0;
for_const (const auto &res, v) {
if (auto result = InlineBots::Result::create(queryId, res)) {
++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();
}
onScroll();
}
void EmojiPanel::queryInlineBot(UserData *bot, PeerData *peer, QString query) {
bool force = false;
_inlineQueryPeer = peer;
if (bot != _inlineBot) {
inlineBotChanged();
_inlineBot = bot;
force = true;
//if (_inlineBot->isBotInlineGeo()) {
// Ui::show(Box<InformBox>(lang(lng_bot_inline_geo_unavailable)));
//}
}
//if (_inlineBot && _inlineBot->isBotInlineGeo()) {
// return;
//}
if (_inlineQuery != query || force) {
if (_inlineRequestId) {
MTP::cancel(_inlineRequestId);
_inlineRequestId = 0;
Notify::inlineBotRequesting(false);
}
if (_inlineCache.find(query) != _inlineCache.cend()) {
_inlineRequestTimer.stop();
_inlineQuery = _inlineNextQuery = query;
showInlineRows(true);
} else {
_inlineNextQuery = query;
_inlineRequestTimer.start(InlineBotRequestDelay);
}
}
}
void EmojiPanel::onInlineRequest() {
if (_inlineRequestId || !_inlineBot || !_inlineQueryPeer) return;
_inlineQuery = _inlineNextQuery;
QString nextOffset;
auto it = _inlineCache.find(_inlineQuery);
if (it != _inlineCache.cend()) {
nextOffset = it->second->nextOffset;
if (nextOffset.isEmpty()) return;
}
Notify::inlineBotRequesting(true);
_inlineRequestId = request(MTPmessages_GetInlineBotResults(MTP_flags(0), _inlineBot->inputUser, _inlineQueryPeer->input, MTPInputGeoPoint(), MTP_string(_inlineQuery), MTP_string(nextOffset))).done([this](const MTPmessages_BotResults &result, mtpRequestId requestId) {
inlineResultsDone(result);
}).fail([this](const RPCError &error) {
// show error?
Notify::inlineBotRequesting(false);
_inlineRequestId = 0;
}).handleAllErrors().send();
}
void EmojiPanel::onEmptyInlineRows() {
if (!_inlineBot) {
gifs()->hideInlineRowsPanel();
} else {
gifs()->clearInlineRowsPanel();
}
}
bool EmojiPanel::refreshInlineRows(int32 *added) {
auto it = _inlineCache.find(_inlineQuery);
const InlineCacheEntry *entry = nullptr;
if (it != _inlineCache.cend()) {
if (!it->second->results.empty() || !it->second->switchPmText.isEmpty()) {
entry = it->second.get();
}
_inlineNextOffset = it->second->nextOffset;
}
if (!entry) prepareCache();
auto result = gifs()->refreshInlineRows(_inlineBot, entry, false);
if (added) *added = result;
return (entry != nullptr);
}
void EmojiPanel::scrollToY(int y) {
_scroll->scrollToY(y);
@ -1044,28 +903,6 @@ void EmojiPanel::scrollToY(int y) {
_topShadow->update();
}
int32 EmojiPanel::showInlineRows(bool newResults) {
int32 added = 0;
bool clear = !refreshInlineRows(&added);
if (newResults) {
scrollToY(0);
}
auto hidden = isHidden();
if (clear) {
if (!_hiding) {
_cache = QPixmap(); // clear after refreshInlineRows()
}
} else {
if (_currentTabType != TabType::Gifs) {
_tabsSlider->setActiveSection(static_cast<int>(TabType::Gifs));
}
showAnimated();
}
return added;
}
EmojiPanel::Inner::Inner(QWidget *parent) : TWidget(parent) {
}

View File

@ -23,13 +23,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "ui/twidget.h"
#include "ui/effects/panel_animation.h"
#include "mtproto/sender.h"
#include "inline_bots/inline_bot_layout_item.h"
#include "auth_session.h"
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
class Result;
} // namespace InlineBots
@ -46,17 +42,7 @@ class EmojiListWidget;
class StickersListWidget;
class GifsListWidget;
using InlineResult = InlineBots::Result;
using InlineResults = std::vector<std::unique_ptr<InlineResult>>;
using InlineItem = InlineBots::Layout::ItemBase;
struct InlineCacheEntry {
QString nextOffset;
QString switchPmText, switchPmStartToken;
InlineResults results;
};
class EmojiPanel : public TWidget, private MTP::Sender {
class EmojiPanel : public TWidget {
Q_OBJECT
public:
@ -73,10 +59,8 @@ public:
void stickersInstalled(uint64 setId);
void queryInlineBot(UserData *bot, PeerData *peer, QString query);
void clearInlineBot();
bool overlaps(const QRect &globalRect) const;
void setInlineQueryPeer(PeerData *peer);
bool ui_isInlineItemBeingChosen();
@ -102,7 +86,6 @@ public slots:
private slots:
void hideByTimerOrLeave();
void refreshSavedGifs();
void onWndActiveChanged();
void onScroll();
@ -112,9 +95,6 @@ private slots:
void onSaveConfig();
void onSaveConfigDelayed(int delay);
void onInlineRequest();
void onEmptyInlineRows();
signals:
void emojiSelected(EmojiPtr emoji);
void stickerSelected(DocumentData *sticker);
@ -255,19 +235,6 @@ private:
QTimer _saveConfigTimer;
// inline bots
std::map<QString, std::unique_ptr<InlineCacheEntry>> _inlineCache;
QTimer _inlineRequestTimer;
void inlineBotChanged();
int32 showInlineRows(bool newResults);
bool refreshInlineRows(int32 *added = 0);
UserData *_inlineBot = nullptr;
PeerData *_inlineQueryPeer = nullptr;
QString _inlineQuery, _inlineNextQuery, _inlineNextOffset;
mtpRequestId _inlineRequestId = 0;
void inlineResultsDone(const MTPmessages_BotResults &result);
};
class EmojiPanel::Inner : public TWidget {
@ -292,6 +259,11 @@ public:
void panelHideFinished();
virtual void clearSelection() = 0;
virtual void afterShown() {
}
virtual void beforeHiding() {
}
virtual object_ptr<InnerFooter> createFooter() = 0;
signals:

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_stickers.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
#include "ui/effects/ripple_animation.h"
#include "boxes/stickers_box.h"
#include "inline_bots/inline_bot_result.h"
@ -34,8 +35,10 @@ namespace ChatHelpers {
namespace {
constexpr auto kSaveChosenTabTimeout = 1000;
constexpr auto kSearchRequestDelay = 400;
constexpr auto kStickersPanelPerRow = Stickers::kPanelPerRow;
constexpr auto kInlineItemsMaxPerRow = 5;
constexpr auto kSearchBotUsername = "gif";
} // namespace
@ -43,20 +46,67 @@ class GifsListWidget::Footer : public EmojiPanel::InnerFooter {
public:
Footer(gsl::not_null<GifsListWidget*> parent);
void stealFocus();
void returnFocus();
protected:
void paintEvent(QPaintEvent *e) override;
void processPanelHideFinished() override;
private:
gsl::not_null<GifsListWidget*> _pan;
object_ptr<Ui::InputField> _field;
object_ptr<Ui::CrossButton> _cancel;
QPointer<QWidget> _focusTakenFrom;
};
GifsListWidget::Footer::Footer(gsl::not_null<GifsListWidget*> parent) : InnerFooter(parent)
, _pan(parent) {
, _pan(parent)
, _field(this, st::gifsSearchField, lang(lng_gifs_search))
, _cancel(this, st::gifsSearchCancel) {
_field->resize(width() - st::gifsSearchFieldPosition.x() - st::gifsSearchCancelPosition.x() - st::gifsSearchCancel.width, _field->height());
_field->moveToLeft(st::gifsSearchFieldPosition.x(), st::gifsSearchFieldPosition.y());
connect(_field, &Ui::InputField::submitted, this, [this](bool ctrlShiftEnter) {
_pan->sendInlineRequest();
});
connect(_field, &Ui::InputField::cancelled, this, [this] {
if (_field->getLastText().isEmpty()) {
emit _pan->cancelled();
} else {
_field->setText(QString());
}
});
connect(_field, &Ui::InputField::changed, this, [this] {
_cancel->toggleAnimated(!_field->getLastText().isEmpty());
_pan->searchForGifs(_field->getLastText());
});
_cancel->moveToRight(st::gifsSearchCancelPosition.x(), st::gifsSearchCancelPosition.y());
}
void GifsListWidget::Footer::stealFocus() {
if (!_focusTakenFrom) {
_focusTakenFrom = QApplication::focusWidget();
}
_field->setFocus();
}
void GifsListWidget::Footer::returnFocus() {
if (_focusTakenFrom) {
_focusTakenFrom->setFocus();
}
}
void GifsListWidget::Footer::paintEvent(QPaintEvent *e) {
Painter p(this);
st::gifsSearchIcon.paint(p, st::gifsSearchIconPosition.x(), st::gifsSearchIconPosition.y(), width());
}
void GifsListWidget::Footer::processPanelHideFinished() {
// TODO Clear search
_field->setText(QString());
}
GifsListWidget::GifsListWidget(QWidget *parent) : Inner(parent)
@ -72,6 +122,12 @@ GifsListWidget::GifsListWidget(QWidget *parent) : Inner(parent)
_updateInlineItems.setSingleShot(true);
connect(&_updateInlineItems, SIGNAL(timeout()), this, SLOT(onUpdateInlineItems()));
_inlineRequestTimer.setSingleShot(true);
connect(&_inlineRequestTimer, &QTimer::timeout, this, [this] { sendInlineRequest(); });
subscribe(AuthSession::Current().data().savedGifsUpdated(), [this] {
refreshSavedGifs();
});
subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] {
update();
});
@ -90,9 +146,13 @@ void GifsListWidget::setVisibleTopBottom(int visibleTop, int visibleBottom) {
if (top != getVisibleTop()) {
_lastScrolled = getms();
}
auto visibleHeight = (visibleBottom - visibleTop);
if (visibleBottom + visibleHeight > height()) {
// onInlineRequest(); // TODO
checkLoadMore();
}
void GifsListWidget::checkLoadMore() {
auto visibleHeight = (getVisibleBottom() - getVisibleTop());
if (getVisibleBottom() + visibleHeight > height()) {
sendInlineRequest();
}
}
@ -103,11 +163,8 @@ int GifsListWidget::countHeight() {
}
auto minimalLastHeight = (visibleHeight - st::stickerPanPadding);
auto result = st::stickerPanPadding;
if (_switchPmButton) {
result += _switchPmButton->height() + st::inlineResultsSkip;
}
for (int i = 0, l = _inlineRows.count(); i < l; ++i) {
result += _inlineRows[i].height;
for (int i = 0, l = _rows.count(); i < l; ++i) {
result += _rows[i].height;
}
return qMax(minimalLastHeight, result) + st::stickerPanPadding;
}
@ -118,6 +175,56 @@ GifsListWidget::~GifsListWidget() {
deleteUnusedInlineLayouts();
}
void GifsListWidget::cancelGifsSearch() {
if (_inlineRequestId) {
request(_inlineRequestId).cancel();
_inlineRequestId = 0;
}
_inlineRequestTimer.stop();
_inlineQuery = _inlineNextQuery = _inlineNextOffset = QString();
_inlineCache.clear();
refreshInlineRows(nullptr, true);
}
void GifsListWidget::inlineResultsDone(const MTPmessages_BotResults &result) {
_inlineRequestId = 0;
auto it = _inlineCache.find(_inlineQuery);
auto adding = (it != _inlineCache.cend());
if (result.type() == mtpc_messages_botResults) {
auto &d = result.c_messages_botResults();
auto &v = d.vresults.v;
auto queryId = d.vquery_id.v;
if (it == _inlineCache.cend()) {
it = _inlineCache.emplace(_inlineQuery, std::make_unique<InlineCacheEntry>()).first;
}
auto entry = it->second.get();
entry->nextOffset = qs(d.vnext_offset);
if (auto count = v.size()) {
entry->results.reserve(entry->results.size() + count);
}
auto added = 0;
for_const (const auto &res, v) {
if (auto result = InlineBots::Result::create(queryId, res)) {
++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();
@ -127,7 +234,7 @@ void GifsListWidget::paintEvent(QPaintEvent *e) {
}
void GifsListWidget::paintInlineItems(Painter &p, QRect clip) {
if (_inlineRows.isEmpty() && !_switchPmButton) {
if (_rows.isEmpty()) {
p.setFont(st::normalFont);
p.setPen(st::noContactsColor);
p.drawText(QRect(0, 0, width(), (height() / 3) * 2 + st::normalFont->height), lang(lng_inline_bot_no_results), style::al_center);
@ -137,14 +244,10 @@ void GifsListWidget::paintInlineItems(Painter &p, QRect clip) {
InlineBots::Layout::PaintContext context(getms(), false, gifPaused, false);
auto top = st::stickerPanPadding;
if (_switchPmButton) {
top += _switchPmButton->height() + st::inlineResultsSkip;
}
auto fromx = rtl() ? (width() - clip.x() - clip.width()) : clip.x();
auto tox = rtl() ? (width() - clip.x()) : (clip.x() + clip.width());
for (auto row = 0, rows = _inlineRows.size(); row != rows; ++row) {
auto &inlineRow = _inlineRows[row];
for (auto row = 0, rows = _rows.size(); row != rows; ++row) {
auto &inlineRow = _rows[row];
if (top >= clip.top() + clip.height()) {
break;
}
@ -210,11 +313,11 @@ void GifsListWidget::mouseReleaseEvent(QMouseEvent *e) {
}
void GifsListWidget::selectInlineResult(int row, int column) {
if (row >= _inlineRows.size() || column >= _inlineRows.at(row).items.size()) {
if (row >= _rows.size() || column >= _rows[row].items.size()) {
return;
}
auto item = _inlineRows[row].items[column];
auto item = _rows[row].items[column];
if (auto photo = item->getPhoto()) {
if (photo->medium->loaded() || photo->thumb->loaded()) {
emit selected(photo);
@ -232,7 +335,7 @@ void GifsListWidget::selectInlineResult(int row, int column) {
}
} else if (auto inlineResult = item->getResult()) {
if (inlineResult->onChoose(item)) {
emit selected(inlineResult, _inlineBot);
emit selected(inlineResult, _searchBot);
}
}
}
@ -258,8 +361,8 @@ void GifsListWidget::enterFromChildEvent(QEvent *e, QWidget *child) {
void GifsListWidget::clearSelection() {
if (_selected >= 0) {
int srow = _selected / MatrixRowShift, scol = _selected % MatrixRowShift;
t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size());
ClickHandler::clearActive(_inlineRows.at(srow).items.at(scol));
t_assert(srow >= 0 && srow < _rows.size() && scol >= 0 && scol < _rows[srow].items.size());
ClickHandler::clearActive(_rows[srow].items[scol]);
setCursor(style::cur_default);
}
_selected = _pressed = -1;
@ -295,18 +398,18 @@ void GifsListWidget::processPanelHideFinished() {
}
}
bool GifsListWidget::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth) {
InlineItem *layout = nullptr;
bool GifsListWidget::inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, Row &row, int32 &sumWidth) {
LayoutItem *layout = nullptr;
if (savedGif) {
layout = layoutPrepareSavedGif(savedGif, (_inlineRows.size() * MatrixRowShift) + row.items.size());
layout = layoutPrepareSavedGif(savedGif, (_rows.size() * MatrixRowShift) + row.items.size());
} else if (result) {
layout = layoutPrepareInlineResult(result, (_inlineRows.size() * MatrixRowShift) + row.items.size());
layout = layoutPrepareInlineResult(result, (_rows.size() * MatrixRowShift) + row.items.size());
}
if (!layout) return false;
layout->preload();
if (inlineRowFinalize(row, sumWidth, layout->isFullLine())) {
layout->setPosition(_inlineRows.size() * MatrixRowShift);
layout->setPosition(_rows.size() * MatrixRowShift);
}
sumWidth += layout->maxWidth();
@ -318,14 +421,14 @@ bool GifsListWidget::inlineRowsAddItem(DocumentData *savedGif, InlineResult *res
return true;
}
bool GifsListWidget::inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force) {
bool GifsListWidget::inlineRowFinalize(Row &row, int32 &sumWidth, bool force) {
if (row.items.isEmpty()) return false;
auto full = (row.items.size() >= kInlineItemsMaxPerRow);
auto big = (sumWidth >= st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft);
if (full || big || force) {
_inlineRows.push_back(layoutInlineRow(row, (full || big) ? sumWidth : 0));
row = InlineRow();
_rows.push_back(layoutInlineRow(row, (full || big) ? sumWidth : 0));
row = Row();
row.items.reserve(kInlineItemsMaxPerRow);
sumWidth = 0;
return true;
@ -339,8 +442,8 @@ void GifsListWidget::refreshSavedGifs() {
auto &saved = cSavedGifs();
if (!saved.isEmpty()) {
_inlineRows.reserve(saved.size());
auto row = InlineRow();
_rows.reserve(saved.size());
auto row = Row();
row.items.reserve(kInlineItemsMaxPerRow);
auto sumWidth = 0;
for_const (auto &gif, saved) {
@ -350,16 +453,19 @@ void GifsListWidget::refreshSavedGifs() {
}
deleteUnusedGifLayouts();
int32 h = countHeight();
if (h != height()) resize(width(), h);
auto newHeight = countHeight();
if (newHeight != height()) {
resize(width(), newHeight);
}
update();
}
updateSelected();
}
void GifsListWidget::inlineBotChanged() {
refreshInlineRows(nullptr, nullptr, true);
if (isVisible()) {
updateSelected();
} else {
preloadImages();
}
}
void GifsListWidget::clearInlineRows(bool resultsDeleted) {
@ -367,19 +473,19 @@ void GifsListWidget::clearInlineRows(bool resultsDeleted) {
_selected = _pressed = -1;
} else {
clearSelection();
for_const (auto &row, _inlineRows) {
for_const (auto &row, _rows) {
for_const (auto &item, row.items) {
item->setPosition(-1);
}
}
}
_inlineRows.clear();
_rows.clear();
}
InlineItem *GifsListWidget::layoutPrepareSavedGif(DocumentData *doc, int32 position) {
GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareSavedGif(DocumentData *doc, int32 position) {
auto it = _gifLayouts.find(doc);
if (it == _gifLayouts.cend()) {
if (auto layout = InlineItem::createLayoutGif(this, doc)) {
if (auto layout = LayoutItem::createLayoutGif(this, doc)) {
it = _gifLayouts.emplace(doc, std::move(layout)).first;
it->second->initDimensions();
} else {
@ -392,10 +498,10 @@ InlineItem *GifsListWidget::layoutPrepareSavedGif(DocumentData *doc, int32 posit
return it->second.get();
}
InlineItem *GifsListWidget::layoutPrepareInlineResult(InlineResult *result, int32 position) {
GifsListWidget::LayoutItem *GifsListWidget::layoutPrepareInlineResult(InlineResult *result, int32 position) {
auto it = _inlineLayouts.find(result);
if (it == _inlineLayouts.cend()) {
if (auto layout = InlineItem::createLayout(this, result, _inlineWithThumb)) {
if (auto layout = LayoutItem::createLayout(this, result, _inlineWithThumb)) {
it = _inlineLayouts.emplace(result, std::move(layout)).first;
it->second->initDimensions();
} else {
@ -409,7 +515,7 @@ InlineItem *GifsListWidget::layoutPrepareInlineResult(InlineResult *result, int3
}
void GifsListWidget::deleteUnusedGifLayouts() {
if (_inlineRows.isEmpty() || _section != Section::Gifs) { // delete all
if (_rows.isEmpty() || _section != Section::Gifs) { // delete all
_gifLayouts.clear();
} else {
for (auto i = _gifLayouts.begin(); i != _gifLayouts.cend();) {
@ -423,7 +529,7 @@ void GifsListWidget::deleteUnusedGifLayouts() {
}
void GifsListWidget::deleteUnusedInlineLayouts() {
if (_inlineRows.isEmpty() || _section == Section::Gifs) { // delete all
if (_rows.isEmpty() || _section == Section::Gifs) { // delete all
_inlineLayouts.clear();
} else {
for (auto i = _inlineLayouts.begin(); i != _inlineLayouts.cend();) {
@ -436,7 +542,7 @@ void GifsListWidget::deleteUnusedInlineLayouts() {
}
}
GifsListWidget::InlineRow &GifsListWidget::layoutInlineRow(InlineRow &row, int32 sumWidth) {
GifsListWidget::Row &GifsListWidget::layoutInlineRow(Row &row, int32 sumWidth) {
auto count = int(row.items.size());
t_assert(count <= kInlineItemsMaxPerRow);
@ -456,7 +562,7 @@ GifsListWidget::InlineRow &GifsListWidget::layoutInlineRow(InlineRow &row, int32
int index = indices[i];
int w = sumWidth ? (row.items.at(index)->maxWidth() * availw / sumWidth) : row.items.at(index)->maxWidth();
int actualw = qMax(w, int(st::inlineResultsMinWidth));
row.height = qMax(row.height, row.items.at(index)->resizeGetHeight(actualw));
row.height = qMax(row.height, row.items[index]->resizeGetHeight(actualw));
if (sumWidth) {
availw -= actualw;
sumWidth -= row.items.at(index)->maxWidth();
@ -470,14 +576,14 @@ GifsListWidget::InlineRow &GifsListWidget::layoutInlineRow(InlineRow &row, int32
}
void GifsListWidget::preloadImages() {
for (auto row = 0, rows = _inlineRows.size(); row != rows; ++row) {
for (auto col = 0, cols = _inlineRows[row].items.size(); col != cols; ++col) {
_inlineRows[row].items[col]->preload();
for (auto row = 0, rows = _rows.size(); row != rows; ++row) {
for (auto col = 0, cols = _rows[row].items.size(); col != cols; ++col) {
_rows[row].items[col]->preload();
}
}
}
void GifsListWidget::hideInlineRowsPanel() {
void GifsListWidget::switchToSavedGifs() {
clearInlineRows(false);
_section = Section::Gifs;
refreshSavedGifs();
@ -485,66 +591,25 @@ void GifsListWidget::hideInlineRowsPanel() {
emit scrollUpdated();
}
void GifsListWidget::clearInlineRowsPanel() {
clearInlineRows(false);
}
void GifsListWidget::refreshSwitchPmButton(const InlineCacheEntry *entry) {
if (!entry || entry->switchPmText.isEmpty()) {
_switchPmButton.destroy();
_switchPmStartToken.clear();
} else {
if (!_switchPmButton) {
_switchPmButton.create(this, QString(), st::switchPmButton);
_switchPmButton->show();
_switchPmButton->setTextTransform(Ui::RoundButton::TextTransform::NoTransform);
connect(_switchPmButton, SIGNAL(clicked()), this, SLOT(onSwitchPm()));
}
_switchPmButton->setText(entry->switchPmText); // doesn't perform text.toUpper()
_switchPmStartToken = entry->switchPmStartToken;
auto buttonTop = st::stickerPanPadding;
_switchPmButton->move(st::inlineResultsLeft - st::buttonRadius, buttonTop);
}
update();
}
int GifsListWidget::refreshInlineRows(UserData *bot, const InlineCacheEntry *entry, bool resultsDeleted) {
_inlineBot = bot;
refreshSwitchPmButton(entry);
auto clearResults = [this, entry]() {
if (!entry) {
return true;
}
if (entry->results.empty() && entry->switchPmText.isEmpty()) {
if (!_inlineBot) {
return true;
}
}
return false;
};
auto clearResultsResult = clearResults();
if (clearResultsResult) {
int GifsListWidget::refreshInlineRows(const InlineCacheEntry *entry, bool resultsDeleted) {
if (!entry) {
if (resultsDeleted) {
clearInlineRows(true);
deleteUnusedInlineLayouts();
}
emit emptyInlineRows();
switchToSavedGifs();
return 0;
}
clearSelection();
t_assert(_inlineBot != 0);
_inlineBotTitle = lng_inline_bot_results(lt_inline_bot, _inlineBot->username.isEmpty() ? _inlineBot->name : ('@' + _inlineBot->username));
_section = Section::Inlines;
auto count = int(entry->results.size());
auto from = validateExistingInlineRows(entry->results);
auto added = 0;
if (count) {
_inlineRows.reserve(count);
auto row = InlineRow();
_rows.reserve(count);
auto row = Row();
row.items.reserve(kInlineItemsMaxPerRow);
auto sumWidth = 0;
for (auto i = from; i != count; ++i) {
@ -568,52 +633,52 @@ int GifsListWidget::refreshInlineRows(UserData *bot, const InlineCacheEntry *ent
int GifsListWidget::validateExistingInlineRows(const InlineResults &results) {
int count = results.size(), until = 0, untilrow = 0, untilcol = 0;
for (; until < count;) {
if (untilrow >= _inlineRows.size() || _inlineRows[untilrow].items[untilcol]->getResult() != results[until].get()) {
if (untilrow >= _rows.size() || _rows[untilrow].items[untilcol]->getResult() != results[until].get()) {
break;
}
++until;
if (++untilcol == _inlineRows[untilrow].items.size()) {
if (++untilcol == _rows[untilrow].items.size()) {
++untilrow;
untilcol = 0;
}
}
if (until == count) { // all items are layed out
if (untilrow == _inlineRows.size()) { // nothing changed
if (untilrow == _rows.size()) { // nothing changed
return until;
}
for (int i = untilrow, l = _inlineRows.size(), skip = untilcol; i < l; ++i) {
for (int j = 0, s = _inlineRows[i].items.size(); j < s; ++j) {
for (int i = untilrow, l = _rows.size(), skip = untilcol; i < l; ++i) {
for (int j = 0, s = _rows[i].items.size(); j < s; ++j) {
if (skip) {
--skip;
} else {
_inlineRows[i].items[j]->setPosition(-1);
_rows[i].items[j]->setPosition(-1);
}
}
}
if (!untilcol) { // all good rows are filled
_inlineRows.resize(untilrow);
_rows.resize(untilrow);
return until;
}
_inlineRows.resize(untilrow + 1);
_inlineRows[untilrow].items.resize(untilcol);
_inlineRows[untilrow] = layoutInlineRow(_inlineRows[untilrow]);
_rows.resize(untilrow + 1);
_rows[untilrow].items.resize(untilcol);
_rows[untilrow] = layoutInlineRow(_rows[untilrow]);
return until;
}
if (untilrow && !untilcol) { // remove last row, maybe it is not full
--untilrow;
untilcol = _inlineRows[untilrow].items.size();
untilcol = _rows[untilrow].items.size();
}
until -= untilcol;
for (int i = untilrow, l = _inlineRows.size(); i < l; ++i) {
for (int j = 0, s = _inlineRows[i].items.size(); j < s; ++j) {
_inlineRows[i].items[j]->setPosition(-1);
for (int i = untilrow, l = _rows.size(); i < l; ++i) {
for (int j = 0, s = _rows[i].items.size(); j < s; ++j) {
_rows[i].items[j]->setPosition(-1);
}
}
_inlineRows.resize(untilrow);
_rows.resize(untilrow);
if (_inlineRows.isEmpty()) {
if (_rows.isEmpty()) {
_inlineWithThumb = false;
for (int i = until; i < count; ++i) {
if (results.at(i)->hasThumbDisplay()) {
@ -625,20 +690,20 @@ int GifsListWidget::validateExistingInlineRows(const InlineResults &results) {
return until;
}
void GifsListWidget::inlineItemLayoutChanged(const InlineItem *layout) {
void GifsListWidget::inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) {
if (_selected < 0 || !isVisible()) {
return;
}
int row = _selected / MatrixRowShift, col = _selected % MatrixRowShift;
if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) {
if (layout == _inlineRows.at(row).items.at(col)) {
if (row < _rows.size() && col < _rows[row].items.size()) {
if (layout == _rows[row].items[col]) {
updateSelected();
}
}
}
void GifsListWidget::inlineItemRepaint(const InlineItem *layout) {
void GifsListWidget::inlineItemRepaint(const InlineBots::Layout::ItemBase *layout) {
auto ms = getms();
if (_lastScrolled + 100 <= ms) {
update();
@ -647,7 +712,7 @@ void GifsListWidget::inlineItemRepaint(const InlineItem *layout) {
}
}
bool GifsListWidget::inlineItemVisible(const InlineItem *layout) {
bool GifsListWidget::inlineItemVisible(const InlineBots::Layout::ItemBase *layout) {
auto position = layout->position();
if (position < 0 || !isVisible()) {
return false;
@ -655,15 +720,113 @@ bool GifsListWidget::inlineItemVisible(const InlineItem *layout) {
auto row = position / MatrixRowShift;
auto col = position % MatrixRowShift;
t_assert((row < _inlineRows.size()) && (col < _inlineRows[row].items.size()));
t_assert((row < _rows.size()) && (col < _rows[row].items.size()));
auto &inlineItems = _inlineRows[row].items;
auto &inlineItems = _rows[row].items;
auto top = 0;
for (auto i = 0; i != row; ++i) {
top += _inlineRows[i].height;
top += _rows[i].height;
}
return (top < getVisibleBottom()) && (top + _inlineRows[row].items[col]->height() > getVisibleTop());
return (top < getVisibleBottom()) && (top + _rows[row].items[col]->height() > getVisibleTop());
}
void GifsListWidget::afterShown() {
if (_footer) {
_footer->stealFocus();
}
}
void GifsListWidget::beforeHiding() {
if (_footer) {
_footer->returnFocus();
}
}
bool GifsListWidget::refreshInlineRows(int32 *added) {
auto it = _inlineCache.find(_inlineQuery);
const InlineCacheEntry *entry = nullptr;
if (it != _inlineCache.cend()) {
if (!it->second->results.empty()) {
entry = it->second.get();
}
_inlineNextOffset = it->second->nextOffset;
}
auto result = refreshInlineRows(entry, false);
if (added) *added = result;
return (entry != nullptr);
}
int32 GifsListWidget::showInlineRows(bool newResults) {
auto added = 0;
auto clear = !refreshInlineRows(&added);
if (newResults) {
scrollToY(0);
}
return added;
}
void GifsListWidget::searchForGifs(const QString &query) {
if (query.isEmpty()) {
cancelGifsSearch();
return;
}
if (_inlineQuery != query) {
if (_inlineRequestId) {
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) {
_searchBotRequestId = request(MTPcontacts_ResolveUsername(MTP_string(kSearchBotUsername))).done([this](const MTPcontacts_ResolvedPeer &result) {
Expects(result.type() == mtpc_contacts_resolvedPeer);
auto &data = result.c_contacts_resolvedPeer();
App::feedUsers(data.vusers);
App::feedChats(data.vchats);
if (auto peer = App::peerLoaded(peerFromMTP(data.vpeer))) {
if (auto user = peer->asUser()) {
_searchBot = user;
}
}
}).send();
}
}
void GifsListWidget::sendInlineRequest() {
if (_inlineRequestId || !_inlineQueryPeer || _inlineNextQuery.isEmpty()) {
return;
}
if (!_searchBot) {
// Wait for the bot being resolved.
_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()) return;
}
_inlineRequestId = request(MTPmessages_GetInlineBotResults(MTP_flags(0), _searchBot->inputUser, _inlineQueryPeer->input, MTPInputGeoPoint(), MTP_string(_inlineQuery), MTP_string(nextOffset))).done([this](const MTPmessages_BotResults &result, mtpRequestId requestId) {
inlineResultsDone(result);
}).fail([this](const RPCError &error) {
// show error?
_inlineRequestId = 0;
}).handleAllErrors().send();
}
void GifsListWidget::refreshRecent() {
@ -682,32 +845,29 @@ void GifsListWidget::updateSelected() {
int sx = (rtl() ? width() - p.x() : p.x()) - (st::inlineResultsLeft - st::buttonRadius);
int sy = p.y() - st::stickerPanPadding;
if (_switchPmButton) {
sy -= _switchPmButton->height() + st::inlineResultsSkip;
}
int row = -1, col = -1, sel = -1;
ClickHandlerPtr lnk;
ClickHandlerHost *lnkhost = nullptr;
HistoryCursorState cursor = HistoryDefaultCursorState;
if (sy >= 0) {
row = 0;
for (int rows = _inlineRows.size(); row < rows; ++row) {
if (sy < _inlineRows.at(row).height) {
for (int rows = _rows.size(); row < rows; ++row) {
if (sy < _rows[row].height) {
break;
}
sy -= _inlineRows.at(row).height;
sy -= _rows[row].height;
}
}
if (sx >= 0 && row >= 0 && row < _inlineRows.size()) {
auto &inlineItems = _inlineRows[row].items;
if (sx >= 0 && row >= 0 && row < _rows.size()) {
auto &inlineItems = _rows[row].items;
col = 0;
for (int cols = inlineItems.size(); col < cols; ++col) {
int width = inlineItems.at(col)->width();
int width = inlineItems[col]->width();
if (sx < width) {
break;
}
sx -= width;
if (inlineItems.at(col)->hasRightSkip()) {
if (inlineItems[col]->hasRightSkip()) {
sx -= st::inlineResultsSkip;
}
}
@ -725,18 +885,18 @@ void GifsListWidget::updateSelected() {
int scol = (_selected >= 0) ? (_selected % MatrixRowShift) : -1;
if (_selected != sel) {
if (srow >= 0 && scol >= 0) {
t_assert(srow >= 0 && srow < _inlineRows.size() && scol >= 0 && scol < _inlineRows.at(srow).items.size());
_inlineRows[srow].items[scol]->update();
t_assert(srow >= 0 && srow < _rows.size() && scol >= 0 && scol < _rows[srow].items.size());
_rows[srow].items[scol]->update();
}
_selected = sel;
if (row >= 0 && col >= 0) {
t_assert(row >= 0 && row < _inlineRows.size() && col >= 0 && col < _inlineRows.at(row).items.size());
_inlineRows[row].items[col]->update();
t_assert(row >= 0 && row < _rows.size() && col >= 0 && col < _rows[row].items.size());
_rows[row].items[col]->update();
}
if (_previewShown && _selected >= 0 && _pressed != _selected) {
_pressed = _selected;
if (row >= 0 && col >= 0) {
auto layout = _inlineRows.at(row).items.at(col);
auto layout = _rows[row].items[col];
if (auto previewDocument = layout->getPreviewDocument()) {
Ui::showMediaPreview(previewDocument);
} else if (auto previewPhoto = layout->getPreviewPhoto()) {
@ -753,8 +913,8 @@ void GifsListWidget::updateSelected() {
void GifsListWidget::onPreview() {
if (_pressed < 0) return;
int row = _pressed / MatrixRowShift, col = _pressed % MatrixRowShift;
if (row < _inlineRows.size() && col < _inlineRows.at(row).items.size()) {
auto layout = _inlineRows.at(row).items.at(col);
if (row < _rows.size() && col < _rows[row].items.size()) {
auto layout = _rows[row].items[col];
if (auto previewDocument = layout->getPreviewDocument()) {
Ui::showMediaPreview(previewDocument);
_previewShown = true;
@ -774,11 +934,4 @@ void GifsListWidget::onUpdateInlineItems() {
}
}
void GifsListWidget::onSwitchPm() {
if (_inlineBot && _inlineBot->botInfo) {
_inlineBot->botInfo->startToken = _switchPmStartToken;
Ui::showPeerHistory(_inlineBot, ShowAndStartBotMsgId);
}
}
} // namespace ChatHelpers

View File

@ -21,6 +21,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#pragma once
#include "stickers/emoji_panel.h"
#include "inline_bots/inline_bot_layout_item.h"
namespace InlineBots {
namespace Layout {
class ItemBase;
} // namespace Layout
class Result;
} // namespace InlineBots
namespace Ui {
class RoundButton;
@ -28,7 +36,7 @@ class RoundButton;
namespace ChatHelpers {
class GifsListWidget : public EmojiPanel::Inner, public InlineBots::Layout::Context, private base::Subscriber {
class GifsListWidget : public EmojiPanel::Inner, public InlineBots::Layout::Context, private base::Subscriber, private MTP::Sender {
Q_OBJECT
public:
@ -39,17 +47,20 @@ public:
void clearSelection() override;
object_ptr<EmojiPanel::InnerFooter> createFooter() override;
void refreshSavedGifs();
int refreshInlineRows(UserData *bot, const InlineCacheEntry *results, bool resultsDeleted);
void inlineBotChanged();
void hideInlineRowsPanel();
void clearInlineRowsPanel();
void setVisibleTopBottom(int visibleTop, int visibleBottom) override;
void inlineItemLayoutChanged(const InlineItem *layout) override;
void inlineItemRepaint(const InlineItem *layout) override;
bool inlineItemVisible(const InlineItem *layout) override;
void inlineItemLayoutChanged(const InlineBots::Layout::ItemBase *layout) override;
void inlineItemRepaint(const InlineBots::Layout::ItemBase *layout) override;
bool inlineItemVisible(const InlineBots::Layout::ItemBase *layout) override;
void afterShown() override;
void beforeHiding() override;
void setInlineQueryPeer(PeerData *peer) {
_inlineQueryPeer = peer;
}
void searchForGifs(const QString &query);
void sendInlineRequest();
~GifsListWidget();
@ -70,12 +81,12 @@ protected:
private slots:
void onPreview();
void onUpdateInlineItems();
void onSwitchPm();
signals:
void selected(DocumentData *sticker);
void selected(PhotoData *photo);
void selected(InlineBots::Result *result, UserData *bot);
void cancelled();
void emptyInlineRows();
void scrollUpdated();
@ -87,39 +98,50 @@ private:
};
class Footer;
using InlineResult = InlineBots::Result;
using InlineResults = std::vector<std::unique_ptr<InlineResult>>;
using LayoutItem = InlineBots::Layout::ItemBase;
struct InlineCacheEntry {
QString nextOffset;
InlineResults results;
};
void cancelGifsSearch();
void switchToSavedGifs();
void refreshSavedGifs();
int refreshInlineRows(const InlineCacheEntry *results, bool resultsDeleted);
void checkLoadMore();
int32 showInlineRows(bool newResults);
bool refreshInlineRows(int32 *added = 0);
void inlineResultsDone(const MTPmessages_BotResults &result);
void updateSelected();
void paintInlineItems(Painter &p, QRect clip);
void refreshSwitchPmButton(const InlineCacheEntry *entry);
Section _section = Section::Gifs;
UserData *_inlineBot;
QString _inlineBotTitle;
TimeMs _lastScrolled = 0;
QTimer _updateInlineItems;
bool _inlineWithThumb = false;
object_ptr<Ui::RoundButton> _switchPmButton = { nullptr };
QString _switchPmStartToken;
typedef QVector<InlineItem*> InlineItems;
struct InlineRow {
struct Row {
int height = 0;
InlineItems items;
QVector<LayoutItem*> items;
};
typedef QVector<InlineRow> InlineRows;
InlineRows _inlineRows;
QVector<Row> _rows;
void clearInlineRows(bool resultsDeleted);
std::map<DocumentData*, std::unique_ptr<InlineItem>> _gifLayouts;
InlineItem *layoutPrepareSavedGif(DocumentData *doc, int32 position);
std::map<DocumentData*, std::unique_ptr<LayoutItem>> _gifLayouts;
LayoutItem *layoutPrepareSavedGif(DocumentData *doc, int32 position);
std::map<InlineResult*, std::unique_ptr<InlineItem>> _inlineLayouts;
InlineItem *layoutPrepareInlineResult(InlineResult *result, int32 position);
std::map<InlineResult*, std::unique_ptr<LayoutItem>> _inlineLayouts;
LayoutItem *layoutPrepareInlineResult(InlineResult *result, int32 position);
bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, InlineRow &row, int32 &sumWidth);
bool inlineRowFinalize(InlineRow &row, int32 &sumWidth, bool force = false);
bool inlineRowsAddItem(DocumentData *savedGif, InlineResult *result, Row &row, int32 &sumWidth);
bool inlineRowFinalize(Row &row, int32 &sumWidth, bool force = false);
InlineRow &layoutInlineRow(InlineRow &row, int32 sumWidth = 0);
Row &layoutInlineRow(Row &row, int32 sumWidth = 0);
void deleteUnusedGifLayouts();
void deleteUnusedInlineLayouts();
@ -136,6 +158,15 @@ private:
QTimer _previewTimer;
bool _previewShown = false;
std::map<QString, std::unique_ptr<InlineCacheEntry>> _inlineCache;
QTimer _inlineRequestTimer;
UserData *_searchBot = nullptr;
mtpRequestId _searchBotRequestId = 0;
PeerData *_inlineQueryPeer = nullptr;
QString _inlineQuery, _inlineNextQuery, _inlineNextOffset;
mtpRequestId _inlineRequestId = 0;
};
} // namespace ChatHelpers

View File

@ -211,3 +211,10 @@ inlineBotsScroll: ScrollArea(defaultSolidScroll) {
deltat: stickerPanPadding;
deltab: stickerPanPadding;
}
gifsSearchField: contactsSearchField;
gifsSearchFieldPosition: point(42px, 7px);
gifsSearchCancel: contactsSearchCancel;
gifsSearchCancelPosition: point(1px, 1px);
gifsSearchIcon: boxFieldSearchIcon;
gifsSearchIconPosition: point(6px, 7px);

View File

@ -631,28 +631,14 @@ CrossButton::CrossButton(QWidget *parent, const style::CrossButton &st) : Ripple
hide();
}
void CrossButton::showAnimated() {
startAnimation(true);
}
void CrossButton::showFast() {
showAnimated();
_a_show.finish();
}
void CrossButton::hideAnimated() {
startAnimation(false);
}
void CrossButton::hideFast() {
hideAnimated();
_a_show.finish();
}
void CrossButton::startAnimation(bool shown) {
if (_shown == shown) return;
_shown = shown;
if (isHidden()) show();
void CrossButton::toggleAnimated(bool visible) {
if (_shown == visible) {
return;
}
_shown = visible;
if (isHidden()) {
show();
}
_a_show.start([this] { animationCallback(); }, _shown ? 0. : 1., _shown ? 1. : 0., _st.duration);
}

View File

@ -212,10 +212,23 @@ class CrossButton : public RippleButton {
public:
CrossButton(QWidget *parent, const style::CrossButton &st);
void showAnimated();
void showFast();
void hideAnimated();
void hideFast();
void showAnimated() {
toggleAnimated(true);
}
void hideAnimated() {
toggleAnimated(false);
}
void toggleAnimated(bool visible);
void showFast() {
toggleFast(true);
}
void hideFast() {
toggleFast(false);
}
void toggleFast(bool visible) {
toggleAnimated(visible);
_a_show.finish();
}
bool isShown() const {
return _shown;
@ -230,7 +243,6 @@ protected:
QPoint prepareRippleStartPosition() const override;
private:
void startAnimation(bool shown);
void animationCallback();
const style::CrossButton &_st;