Extract FieldAutocomplete code.

This commit is contained in:
John Preston 2024-09-13 17:39:15 +04:00
parent 969152e949
commit ba7cd25f21
10 changed files with 497 additions and 481 deletions

View file

@ -10,20 +10,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace ChatHelpers {
struct ComposeFeatures {
bool likes = false;
bool sendAs = true;
bool ttlInfo = true;
bool botCommandSend = true;
bool silentBroadcastToggle = true;
bool attachBotsMenu = true;
bool inlineBots = true;
bool megagroupSet = true;
bool stickersSettings = true;
bool openStickerSets = true;
bool autocompleteHashtags = true;
bool autocompleteMentions = true;
bool autocompleteCommands = true;
bool commonTabbedPanel = true;
bool likes : 1 = false;
bool sendAs : 1 = true;
bool ttlInfo : 1 = true;
bool botCommandSend : 1 = true;
bool silentBroadcastToggle : 1 = true;
bool attachBotsMenu : 1 = true;
bool inlineBots : 1 = true;
bool megagroupSet : 1 = true;
bool stickersSettings : 1 = true;
bool openStickerSets : 1 = true;
bool autocompleteHashtags : 1 = true;
bool autocompleteMentions : 1 = true;
bool autocompleteCommands : 1 = true;
bool suggestStickersByEmoji : 1 = true;
bool commonTabbedPanel : 1 = true;
};
} // namespace ChatHelpers

View file

@ -713,11 +713,13 @@ void SuggestionsWidget::leaveEventHook(QEvent *e) {
}
SuggestionsController::SuggestionsController(
not_null<QWidget*> parent,
not_null<QWidget*> outer,
not_null<QTextEdit*> field,
not_null<Main::Session*> session,
const Options &options)
: _st(options.st ? *options.st : st::defaultEmojiSuggestions)
: QObject(parent)
, _st(options.st ? *options.st : st::defaultEmojiSuggestions)
, _field(field)
, _session(session)
, _showExactTimer([=] { showWithQuery(getEmojiQuery()); })

View file

@ -37,7 +37,7 @@ class SuggestionsWidget;
using SuggestionsQuery = std::variant<QString, EmojiPtr>;
class SuggestionsController {
class SuggestionsController final : public QObject {
public:
struct Options {
bool suggestExactFirstWord = true;
@ -47,6 +47,7 @@ public:
};
SuggestionsController(
not_null<QWidget*> parent,
not_null<QWidget*> outer,
not_null<QTextEdit*> field,
not_null<Main::Session*> session,

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/business/data_shortcut_messages.h"
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_chat.h"
#include "data/data_user.h"
@ -202,12 +203,6 @@ struct FieldAutocomplete::BotCommandRow {
Ui::Text::String descriptionText;
};
FieldAutocomplete::FieldAutocomplete(
QWidget *parent,
not_null<Window::SessionController*> controller)
: FieldAutocomplete(parent, controller->uiShow()) {
}
FieldAutocomplete::FieldAutocomplete(
QWidget *parent,
std::shared_ptr<Show> show,
@ -252,6 +247,22 @@ std::shared_ptr<Show> FieldAutocomplete::uiShow() const {
return _show;
}
void FieldAutocomplete::requestRefresh() {
_refreshRequests.fire({});
}
rpl::producer<> FieldAutocomplete::refreshRequests() const {
return _refreshRequests.events();
}
void FieldAutocomplete::requestStickersUpdate() {
_stickersUpdateRequests.fire({});
}
rpl::producer<> FieldAutocomplete::stickersUpdateRequests() const {
return _stickersUpdateRequests.events();
}
auto FieldAutocomplete::mentionChosen() const
-> rpl::producer<FieldAutocomplete::MentionChosen> {
return _inner->mentionChosen();
@ -378,6 +389,10 @@ void FieldAutocomplete::showStickers(EmojiPtr emoji) {
updateFiltered(resetScroll);
}
EmojiPtr FieldAutocomplete::stickersEmoji() const {
return _emoji;
}
bool FieldAutocomplete::clearFilteredBotCommands() {
if (_brows.empty()) {
return false;
@ -1634,4 +1649,165 @@ auto FieldAutocomplete::Inner::scrollToRequested() const
return _scrollToRequested.events();
}
void InitFieldAutocomplete(
std::unique_ptr<FieldAutocomplete> &autocomplete,
FieldAutocompleteDescriptor &&descriptor) {
Expects(!autocomplete);
autocomplete = std::make_unique<FieldAutocomplete>(
descriptor.parent,
descriptor.show,
descriptor.stOverride);
const auto raw = autocomplete.get();
const auto field = descriptor.field;
field->rawTextEdit()->installEventFilter(raw);
raw->mentionChosen(
) | rpl::start_with_next([=](FieldAutocomplete::MentionChosen data) {
const auto user = data.user;
if (data.mention.isEmpty()) {
field->insertTag(
user->firstName.isEmpty() ? user->name() : user->firstName,
PrepareMentionTag(user));
} else {
field->insertTag('@' + data.mention);
}
}, raw->lifetime());
const auto sendCommand = descriptor.sendBotCommand;
const auto setText = descriptor.setText;
raw->hashtagChosen(
) | rpl::start_with_next([=](FieldAutocomplete::HashtagChosen data) {
field->insertTag(data.hashtag);
}, raw->lifetime());
const auto peer = descriptor.peer;
const auto processShortcut = descriptor.processShortcut;
const auto shortcutMessages = (processShortcut != nullptr)
? &peer->owner().shortcutMessages()
: nullptr;
raw->botCommandChosen(
) | rpl::start_with_next([=](FieldAutocomplete::BotCommandChosen data) {
using Method = FieldAutocompleteChooseMethod;
const auto byTab = (data.method == Method::ByTab);
const auto shortcut = data.user->isSelf();
// Send bot command at once, if it was not inserted by pressing Tab.
if (byTab && data.command.size() > 1) {
field->insertTag(data.command);
} else if (!shortcut) {
sendCommand(data.command);
setText(
field->getTextWithTagsPart(field->textCursor().position()));
} else if (processShortcut) {
processShortcut(data.command.mid(1));
}
}, raw->lifetime());
raw->setModerateKeyActivateCallback(std::move(descriptor.moderateKeyActivateCallback));
const auto stickerChoosing = descriptor.stickerChoosing;
raw->choosingProcesses(
) | rpl::start_with_next([=](FieldAutocomplete::Type type) {
if (type == FieldAutocomplete::Type::Stickers) {
stickerChoosing();
}
}, raw->lifetime());
if (auto chosen = descriptor.stickerChosen) {
raw->stickerChosen(
) | rpl::start_with_next(std::move(chosen), raw->lifetime());
}
field->tabbed(
) | rpl::start_with_next([=] {
if (!raw->isHidden()) {
raw->chooseSelected(FieldAutocomplete::ChooseMethod::ByTab);
}
}, raw->lifetime());
const auto features = descriptor.features;
const auto check = [=] {
auto parsed = ParseMentionHashtagBotCommandQuery(field, features());
if (parsed.query.isEmpty()) {
} else if (parsed.query[0] == '#'
&& cRecentWriteHashtags().isEmpty()
&& cRecentSearchHashtags().isEmpty()) {
peer->session().local().readRecentHashtagsAndBots();
} else if (parsed.query[0] == '@'
&& cRecentInlineBots().isEmpty()) {
peer->session().local().readRecentHashtagsAndBots();
} else if (parsed.query[0] == '/'
&& peer->isUser()
&& !peer->asUser()->isBot()
&& (!shortcutMessages
|| shortcutMessages->shortcuts().list.empty())) {
parsed = {};
}
raw->showFiltered(peer, parsed.query, parsed.fromStart);
};
const auto updateStickersByEmoji = [=] {
const auto errorForStickers = Data::RestrictionError(
peer,
ChatRestriction::SendStickers);
if (features().suggestStickersByEmoji && !errorForStickers) {
const auto &text = field->getTextWithTags().text;
auto length = 0;
if (const auto emoji = Ui::Emoji::Find(text, &length)) {
if (text.size() <= length) {
raw->showStickers(emoji);
return;
}
}
}
raw->showStickers(nullptr);
};
raw->refreshRequests(
) | rpl::start_with_next(check, raw->lifetime());
raw->stickersUpdateRequests(
) | rpl::start_with_next(updateStickersByEmoji, raw->lifetime());
peer->owner().botCommandsChanges(
) | rpl::filter([=](not_null<PeerData*> changed) {
return (peer == changed);
}) | rpl::start_with_next([=] {
if (raw->clearFilteredBotCommands()) {
check();
}
}, raw->lifetime());
peer->owner().stickers().updated(
Data::StickersType::Stickers
) | rpl::start_with_next(updateStickersByEmoji, raw->lifetime());
QObject::connect(
field->rawTextEdit(),
&QTextEdit::cursorPositionChanged,
raw,
check,
Qt::QueuedConnection);
field->changes() | rpl::start_with_next(
updateStickersByEmoji,
raw->lifetime());
peer->session().changes().peerUpdates(
Data::PeerUpdate::Flag::Rights
) | rpl::filter([=](const Data::PeerUpdate &update) {
return (update.peer == peer);
}) | rpl::start_with_next(updateStickersByEmoji, raw->lifetime());
if (shortcutMessages) {
shortcutMessages->shortcutsChanged(
) | rpl::start_with_next(check, raw->lifetime());
}
raw->setSendMenuDetails(std::move(descriptor.sendMenuDetails));
raw->hideFast();
}
} // namespace ChatHelpers

View file

@ -47,6 +47,7 @@ struct Details;
namespace ChatHelpers {
struct ComposeFeatures;
struct FileChosen;
class Show;
@ -58,9 +59,6 @@ enum class FieldAutocompleteChooseMethod {
class FieldAutocomplete final : public Ui::RpWidget {
public:
FieldAutocomplete(
QWidget *parent,
not_null<Window::SessionController*> controller);
FieldAutocomplete(
QWidget *parent,
std::shared_ptr<Show> show,
@ -74,16 +72,19 @@ public:
not_null<PeerData*> peer,
QString query,
bool addInlineBots);
void showStickers(EmojiPtr emoji);
[[nodiscard]] EmojiPtr stickersEmoji() const;
void setBoundings(QRect boundings);
const QString &filter() const;
ChatData *chat() const;
ChannelData *channel() const;
UserData *user() const;
[[nodiscard]] const QString &filter() const;
[[nodiscard]] ChatData *chat() const;
[[nodiscard]] ChannelData *channel() const;
[[nodiscard]] UserData *user() const;
int32 innerTop();
int32 innerBottom();
[[nodiscard]] int32 innerTop();
[[nodiscard]] int32 innerBottom();
bool eventFilter(QObject *obj, QEvent *e) override;
@ -112,13 +113,14 @@ public:
bool chooseSelected(ChooseMethod method) const;
bool stickersShown() const {
[[nodiscard]] bool stickersShown() const {
return !_srows.empty();
}
bool overlaps(const QRect &globalRect) {
if (isHidden() || !testAttribute(Qt::WA_OpaquePaintEvent)) return false;
[[nodiscard]] bool overlaps(const QRect &globalRect) {
if (isHidden() || !testAttribute(Qt::WA_OpaquePaintEvent)) {
return false;
}
return rect().contains(QRect(mapFromGlobal(globalRect.topLeft()), globalRect.size()));
}
@ -131,11 +133,16 @@ public:
void showAnimated();
void hideAnimated();
rpl::producer<MentionChosen> mentionChosen() const;
rpl::producer<HashtagChosen> hashtagChosen() const;
rpl::producer<BotCommandChosen> botCommandChosen() const;
rpl::producer<StickerChosen> stickerChosen() const;
rpl::producer<Type> choosingProcesses() const;
void requestRefresh();
[[nodiscard]] rpl::producer<> refreshRequests() const;
void requestStickersUpdate();
[[nodiscard]] rpl::producer<> stickersUpdateRequests() const;
[[nodiscard]] rpl::producer<MentionChosen> mentionChosen() const;
[[nodiscard]] rpl::producer<HashtagChosen> hashtagChosen() const;
[[nodiscard]] rpl::producer<BotCommandChosen> botCommandChosen() const;
[[nodiscard]] rpl::producer<StickerChosen> stickerChosen() const;
[[nodiscard]] rpl::producer<Type> choosingProcesses() const;
protected:
void paintEvent(QPaintEvent *e) override;
@ -191,9 +198,30 @@ private:
bool _hiding = false;
Ui::Animations::Simple _a_opacity;
rpl::event_stream<> _refreshRequests;
rpl::event_stream<> _stickersUpdateRequests;
Fn<bool(int)> _moderateKeyActivateCallback;
};
struct FieldAutocompleteDescriptor {
not_null<QWidget*> parent;
std::shared_ptr<Show> show;
not_null<Ui::InputField*> field;
const style::EmojiPan *stOverride = nullptr;
not_null<PeerData*> peer;
Fn<ComposeFeatures()> features;
Fn<SendMenu::Details()> sendMenuDetails;
Fn<void()> stickerChoosing;
Fn<void(FileChosen&&)> stickerChosen;
Fn<void(TextWithTags)> setText;
Fn<void(QString)> sendBotCommand;
Fn<void(QString)> processShortcut;
Fn<bool(int)> moderateKeyActivateCallback;
};
void InitFieldAutocomplete(
std::unique_ptr<FieldAutocomplete> &autocomplete,
FieldAutocompleteDescriptor &&descriptor);
} // namespace ChatHelpers

View file

@ -243,7 +243,6 @@ HistoryWidget::HistoryWidget(
_scroll.data(),
controller->chatStyle(),
static_cast<HistoryView::CornerButtonsDelegate*>(this))
, _fieldAutocomplete(this, controller->uiShow())
, _supportAutocomplete(session().supportMode()
? object_ptr<Support::Autocomplete>(this, &session())
: nullptr)
@ -416,15 +415,6 @@ HistoryWidget::HistoryWidget(
saveDraftDelayed();
}, _field->lifetime());
connect(rawTextEdit, &QTextEdit::cursorPositionChanged, this, [=] {
checkFieldAutocomplete();
}, Qt::QueuedConnection);
controller->session().data().shortcutMessages().shortcutsChanged(
) | rpl::start_with_next([=] {
checkFieldAutocomplete();
}, lifetime());
_fieldBarCancel->hide();
_topBar->hide();
@ -473,85 +463,10 @@ HistoryWidget::HistoryWidget(
sendBotCommand(r);
}, lifetime());
_fieldAutocomplete->mentionChosen(
) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::MentionChosen data) {
auto replacement = QString();
auto entityTag = QString();
if (data.mention.isEmpty()) {
replacement = data.user->firstName;
if (replacement.isEmpty()) {
replacement = data.user->name();
}
entityTag = PrepareMentionTag(data.user);
} else {
replacement = '@' + data.mention;
}
_field->insertTag(replacement, entityTag);
}, lifetime());
_fieldAutocomplete->hashtagChosen(
) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::HashtagChosen data) {
insertHashtagOrBotCommand(data.hashtag, data.method);
}, lifetime());
_fieldAutocomplete->botCommandChosen(
) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::BotCommandChosen data) {
using Method = ChatHelpers::FieldAutocompleteChooseMethod;
const auto messages = &data.user->owner().shortcutMessages();
const auto shortcut = data.user->isSelf();
const auto command = data.command.mid(1);
const auto byTab = (data.method == Method::ByTab);
const auto shortcutId = (_peer && shortcut && !byTab)
? messages->lookupShortcutId(command)
: BusinessShortcutId();
if (shortcut && command.isEmpty()) {
controller->showSettings(Settings::QuickRepliesId());
} else if (!shortcutId) {
insertHashtagOrBotCommand(data.command, data.method);
} else if (!_peer->session().premium()) {
ShowPremiumPreviewToBuy(
controller,
PremiumFeature::QuickReplies);
} else {
session().api().sendShortcutMessages(_peer, shortcutId);
session().api().finishForwarding(prepareSendAction({}));
setFieldText(_field->getTextWithTagsPart(_field->textCursor().position()));
}
}, lifetime());
_fieldAutocomplete->setModerateKeyActivateCallback([=](int key) {
const auto context = [=](FullMsgId itemId) {
return _list->prepareClickContext(Qt::LeftButton, itemId);
};
return !_keyboard->isHidden() && _keyboard->moderateKeyActivate(
key,
context);
});
_fieldAutocomplete->choosingProcesses(
) | rpl::start_with_next([=](ChatHelpers::FieldAutocomplete::Type type) {
if (!_history) {
return;
}
if (type == ChatHelpers::FieldAutocomplete::Type::Stickers) {
session().sendProgressManager().update(
_history,
Api::SendProgressType::ChooseSticker);
}
}, lifetime());
_fieldAutocomplete->setSendMenuDetails([=] {
return sendMenuDetails();
});
if (_supportAutocomplete) {
supportInitAutocomplete();
}
_field->rawTextEdit()->installEventFilter(this);
_field->rawTextEdit()->installEventFilter(_fieldAutocomplete);
_field->setMimeDataHook([=](
not_null<const QMimeData*> data,
Ui::InputField::MimeAction action) {
@ -566,15 +481,6 @@ HistoryWidget::HistoryWidget(
Unexpected("action in MimeData hook.");
});
const auto allow = [=](const auto&) {
return _peer && _peer->isSelf();
};
const auto suggestions = Ui::Emoji::SuggestionsController::Init(
this,
_field,
&controller->session(),
{ .suggestCustomEmoji = true, .allowCustomWithoutPremium = allow });
_raiseEmojiSuggestions = [=] { suggestions->raise(); };
updateFieldSubmitSettings();
_field->hide();
@ -699,9 +605,6 @@ HistoryWidget::HistoryWidget(
updateControlsVisibility();
updateControlsGeometry();
}
if (_fieldAutocomplete->clearFilteredBotCommands()) {
checkFieldAutocomplete();
}
}, lifetime());
using EntryUpdateFlag = Data::EntryUpdate::Flag;
@ -834,7 +737,6 @@ HistoryWidget::HistoryWidget(
return update.flags;
}) | rpl::start_with_next([=](Data::PeerUpdate::Flags flags) {
if (flags & PeerUpdateFlag::Rights) {
updateStickersByEmoji();
updateFieldPlaceholder();
_preview->checkNow(false);
@ -1191,35 +1093,10 @@ void HistoryWidget::initTabbedSelector() {
rpl::merge(
selector->fileChosen() | filter,
_fieldAutocomplete->stickerChosen(),
selector->customEmojiChosen() | filter,
controller()->stickerOrEmojiChosen() | filter
) | rpl::start_with_next([=](ChatHelpers::FileChosen data) {
controller()->hideLayer(anim::type::normal);
if (const auto info = data.document->sticker()
; info && info->setType == Data::StickersType::Emoji) {
if (data.document->isPremiumEmoji()
&& !session().premium()
&& (!_peer
|| !Data::AllowEmojiWithoutPremium(
_peer,
data.document))) {
showPremiumToast(data.document);
} else if (!_field->isHidden()) {
Data::InsertCustomEmoji(_field.data(), data.document);
}
} else {
controller()->sendingAnimation().appendSending(
data.messageSendingFrom);
const auto localId = data.messageSendingFrom.localId;
auto messageToSend = Api::MessageToSend(
prepareSendAction(data.options));
messageToSend.textWithTags = base::take(data.caption);
sendExistingDocument(
data.document,
std::move(messageToSend),
localId);
}
) | rpl::start_with_next([=](ChatHelpers::FileChosen &&data) {
fileChosen(std::move(data));
}, lifetime());
selector->photoChosen(
@ -1481,66 +1358,95 @@ int HistoryWidget::itemTopForHighlight(
return itemTop;
}
void HistoryWidget::start() {
session().data().stickers().updated(
Data::StickersType::Stickers
) | rpl::start_with_next([=] {
updateStickersByEmoji();
}, lifetime());
session().data().stickers().notifySavedGifsUpdated();
}
void HistoryWidget::insertHashtagOrBotCommand(
QString str,
ChatHelpers::FieldAutocompleteChooseMethod method) {
void HistoryWidget::initFieldAutocomplete() {
_emojiSuggestions = nullptr;
_autocomplete = nullptr;
if (!_peer) {
return;
}
// Send bot command at once, if it was not inserted by pressing Tab.
using Method = ChatHelpers::FieldAutocompleteChooseMethod;
if (str.at(0) == '/' && method != Method::ByTab) {
sendBotCommand({ _peer, str, FullMsgId(), replyTo() });
session().api().finishForwarding(prepareSendAction({}));
setFieldText(_field->getTextWithTagsPart(_field->textCursor().position()));
} else {
_field->insertTag(str);
}
const auto processShortcut = [=](QString shortcut) {
if (!_peer) {
return;
}
const auto messages = &_peer->owner().shortcutMessages();
const auto shortcutId = messages->lookupShortcutId(shortcut);
if (shortcut.isEmpty()) {
controller()->showSettings(Settings::QuickRepliesId());
} else if (!_peer->session().premium()) {
ShowPremiumPreviewToBuy(
controller(),
PremiumFeature::QuickReplies);
} else if (shortcutId) {
session().api().sendShortcutMessages(_peer, shortcutId);
session().api().finishForwarding(prepareSendAction({}));
setFieldText(_field->getTextWithTagsPart(
_field->textCursor().position()));
}
};
ChatHelpers::InitFieldAutocomplete(_autocomplete, {
.parent = this,
.show = controller()->uiShow(),
.field = _field.data(),
.peer = _peer,
.features = [=] {
auto result = ChatHelpers::ComposeFeatures();
if (_showAnimation
|| isChoosingTheme()
|| (_inlineBot && !_inlineLookingUpBot)) {
result.autocompleteMentions = false;
result.autocompleteHashtags = false;
result.autocompleteCommands = false;
}
if (_editMsgId) {
result.autocompleteCommands = false;
result.suggestStickersByEmoji = false;
}
return result;
},
.sendMenuDetails = [=] { return sendMenuDetails(); },
.stickerChoosing = [=] {
if (_history) {
session().sendProgressManager().update(
_history,
Api::SendProgressType::ChooseSticker);
}
},
.stickerChosen = [=](ChatHelpers::FileChosen &&data) {
fileChosen(std::move(data));
},
.setText = [=](TextWithTags text) { if (_peer) setFieldText(text); },
.sendBotCommand = [=](QString command) {
if (_peer) {
sendBotCommand({ _peer, command, FullMsgId(), replyTo() });
session().api().finishForwarding(prepareSendAction({}));
}
},
.processShortcut = processShortcut,
.moderateKeyActivateCallback = [=](int key) {
const auto context = [=](FullMsgId itemId) {
return _list->prepareClickContext(Qt::LeftButton, itemId);
};
return !_keyboard->isHidden() && _keyboard->moderateKeyActivate(
key,
context);
},
});
const auto allow = [=](const auto&) {
return _peer->isSelf();
};
_emojiSuggestions.reset(Ui::Emoji::SuggestionsController::Init(
this,
_field,
&controller()->session(),
{ .suggestCustomEmoji = true, .allowCustomWithoutPremium = allow }));
}
InlineBotQuery HistoryWidget::parseInlineBotQuery() const {
return (isChoosingTheme() || _editMsgId)
? InlineBotQuery()
: ParseInlineBotQuery(&session(), _field);
}
AutocompleteQuery HistoryWidget::parseMentionHashtagBotCommandQuery() const {
const auto result = (isChoosingTheme()
|| (_inlineBot && !_inlineLookingUpBot))
? AutocompleteQuery()
: ParseMentionHashtagBotCommandQuery(_field, {});
if (result.query.isEmpty()) {
return result;
} else if (result.query[0] == '#'
&& cRecentWriteHashtags().isEmpty()
&& cRecentSearchHashtags().isEmpty()) {
session().local().readRecentHashtagsAndBots();
} else if (result.query[0] == '@'
&& cRecentInlineBots().isEmpty()) {
session().local().readRecentHashtagsAndBots();
} else if (result.query[0] == '/') {
if (_editMsgId) {
return {};
} else if (_peer->isUser()
&& !_peer->asUser()->isBot()
&& _peer->owner().shortcutMessages().shortcuts().list.empty()) {
return {};
}
}
return result;
}
void HistoryWidget::updateInlineBotQuery() {
if (!_history) {
return;
@ -1632,8 +1538,8 @@ void HistoryWidget::applyInlineBotQuery(UserData *bot, const QString &query) {
orderWidgets();
}
_inlineResults->queryInlineBot(_inlineBot, _peer, query);
if (!_fieldAutocomplete->isHidden()) {
_fieldAutocomplete->hideAnimated();
if (_autocomplete) {
_autocomplete->hideAnimated();
}
} else {
clearInlineBot();
@ -1665,7 +1571,9 @@ void HistoryWidget::orderWidgets() {
_chooseTheme->raise();
}
_topShadow->raise();
_fieldAutocomplete->raise();
if (_autocomplete) {
_autocomplete->raise();
}
if (_membersDropdown) {
_membersDropdown->raise();
}
@ -1675,34 +1583,13 @@ void HistoryWidget::orderWidgets() {
if (_tabbedPanel) {
_tabbedPanel->raise();
}
_raiseEmojiSuggestions();
if (_emojiSuggestions) {
_emojiSuggestions->raise();
}
_attachDragAreas.document->raise();
_attachDragAreas.photo->raise();
}
bool HistoryWidget::updateStickersByEmoji() {
if (!_peer) {
return false;
}
const auto emoji = [&] {
const auto errorForStickers = Data::RestrictionError(
_peer,
ChatRestriction::SendStickers);
if (!_editMsgId && !errorForStickers) {
const auto &text = _field->getTextWithTags().text;
auto length = 0;
if (const auto emoji = Ui::Emoji::Find(text, &length)) {
if (text.size() <= length) {
return emoji;
}
}
}
return EmojiPtr(nullptr);
}();
_fieldAutocomplete->showStickers(emoji);
return (emoji != nullptr);
}
void HistoryWidget::toggleChooseChatTheme(
not_null<PeerData*> peer,
std::optional<bool> show) {
@ -1745,11 +1632,10 @@ void HistoryWidget::fieldChanged() {
InvokeQueued(this, [=] {
updateInlineBotQuery();
const auto choosingSticker = updateStickersByEmoji();
if (_history
&& !_inlineBot
&& !_editMsgId
&& !choosingSticker
&& (!_autocomplete || !_autocomplete->stickersEmoji())
&& updateTyping) {
session().sendProgressManager().update(
_history,
@ -1839,6 +1725,34 @@ void HistoryWidget::saveFieldToHistoryLocalDraft() {
}
}
void HistoryWidget::fileChosen(ChatHelpers::FileChosen &&data) {
controller()->hideLayer(anim::type::normal);
if (const auto info = data.document->sticker()
; info && info->setType == Data::StickersType::Emoji) {
if (data.document->isPremiumEmoji()
&& !session().premium()
&& (!_peer
|| !Data::AllowEmojiWithoutPremium(
_peer,
data.document))) {
showPremiumToast(data.document);
} else if (!_field->isHidden()) {
Data::InsertCustomEmoji(_field.data(), data.document);
}
} else {
controller()->sendingAnimation().appendSending(
data.messageSendingFrom);
const auto localId = data.messageSendingFrom.localId;
auto messageToSend = Api::MessageToSend(
prepareSendAction(data.options));
messageToSend.textWithTags = base::take(data.caption);
sendExistingDocument(
data.document,
std::move(messageToSend),
localId);
}
}
void HistoryWidget::saveCloudDraft() {
controller()->session().api().saveCurrentDraftToCloud();
}
@ -2043,7 +1957,11 @@ void HistoryWidget::fastShowAtEnd(not_null<History*> history) {
}
bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
InvokeQueued(this, [=] { updateStickersByEmoji(); });
InvokeQueued(this, [=] {
if (_autocomplete) {
_autocomplete->requestStickersUpdate();
}
});
const auto editDraft = _history ? _history->localEditDraft({}) : nullptr;
const auto draft = editDraft
@ -2380,6 +2298,7 @@ void HistoryWidget::showHistory(
controller()->tabbedSelector()->setCurrentPeer(_peer);
}
refreshTabbedPanel();
initFieldAutocomplete();
if (_peer) {
_unblock->setText(((_peer->isUser()
@ -2901,7 +2820,7 @@ void HistoryWidget::refreshSendAsToggle() {
bool HistoryWidget::contentOverlapped(const QRect &globalRect) {
return (_attachDragAreas.document->overlaps(globalRect)
|| _attachDragAreas.photo->overlaps(globalRect)
|| _fieldAutocomplete->overlaps(globalRect)
|| (_autocomplete && _autocomplete->overlaps(globalRect))
|| (_tabbedPanel && _tabbedPanel->overlaps(globalRect))
|| (_inlineResults && _inlineResults->overlaps(globalRect)));
}
@ -3005,7 +2924,9 @@ void HistoryWidget::updateControlsVisibility() {
toggle(_botStart);
}
_kbShown = false;
_fieldAutocomplete->hide();
if (_autocomplete) {
_autocomplete->hide();
}
if (_supportAutocomplete) {
_supportAutocomplete->hide();
}
@ -3049,7 +2970,9 @@ void HistoryWidget::updateControlsVisibility() {
}
hideFieldIfVisible();
} else if (editingMessage() || _canSendMessages) {
checkFieldAutocomplete();
if (_autocomplete) {
_autocomplete->requestRefresh();
}
_unblock->hide();
_botStart->hide();
_joinChannel->hide();
@ -3156,7 +3079,9 @@ void HistoryWidget::updateControlsVisibility() {
_fieldBarCancel->hide();
}
} else {
_fieldAutocomplete->hide();
if (_autocomplete) {
_autocomplete->hide();
}
if (_supportAutocomplete) {
_supportAutocomplete->hide();
}
@ -4157,7 +4082,9 @@ void HistoryWidget::hideChildWidgets() {
}
void HistoryWidget::hideSelectorControlsAnimated() {
_fieldAutocomplete->hideAnimated();
if (_autocomplete) {
_autocomplete->hideAnimated();
}
if (_supportAutocomplete) {
_supportAutocomplete->hide();
}
@ -4992,10 +4919,10 @@ bool HistoryWidget::updateCmdStartShown() {
},
.source = InlineBots::WebViewSourceBotMenu(),
});
} else if (!_fieldAutocomplete->isHidden()) {
_fieldAutocomplete->hideAnimated();
} else {
_fieldAutocomplete->showFiltered(_peer, "/", true);
} else if (_autocomplete && !_autocomplete->isHidden()) {
_autocomplete->hideAnimated();
} else if (_autocomplete) {
_autocomplete->showFiltered(_peer, "/", true);
}
});
_botMenu.button->widthValue(
@ -5458,7 +5385,9 @@ void HistoryWidget::clearInlineBot() {
if (_inlineResults) {
_inlineResults->clearInlineBot();
}
checkFieldAutocomplete();
if (_autocomplete) {
_autocomplete->requestRefresh();
}
}
void HistoryWidget::inlineBotChanged() {
@ -5483,18 +5412,6 @@ void HistoryWidget::fieldFocused() {
}
}
void HistoryWidget::checkFieldAutocomplete() {
if (!_history || _showAnimation) {
return;
}
const auto autocomplete = parseMentionHashtagBotCommandQuery();
_fieldAutocomplete->showFiltered(
_peer,
autocomplete.query,
autocomplete.fromStart);
}
void HistoryWidget::updateFieldPlaceholder() {
if (!_editMsgId && _inlineBot && !_inlineLookingUpBot) {
_field->setPlaceholder(
@ -5968,7 +5885,9 @@ void HistoryWidget::updateControlsGeometry() {
const auto scrollAreaTop = businessBotTop + (_businessBotStatus ? _businessBotStatus->bar().height() : 0);
if (_scroll->y() != scrollAreaTop) {
_scroll->moveToLeft(0, scrollAreaTop);
_fieldAutocomplete->setBoundings(_scroll->geometry());
if (_autocomplete) {
_autocomplete->setBoundings(_scroll->geometry());
}
if (_supportAutocomplete) {
_supportAutocomplete->setBoundings(_scroll->geometry());
}
@ -6245,7 +6164,9 @@ void HistoryWidget::updateHistoryGeometry(
visibleAreaUpdated();
}
_fieldAutocomplete->setBoundings(_scroll->geometry());
if (_autocomplete) {
_autocomplete->setBoundings(_scroll->geometry());
}
if (_supportAutocomplete) {
_supportAutocomplete->setBoundings(_scroll->geometry());
}
@ -6939,9 +6860,6 @@ bool HistoryWidget::showSlowmodeError() {
void HistoryWidget::fieldTabbed() {
if (_supportAutocomplete) {
_supportAutocomplete->activate(_field.data());
} else if (!_fieldAutocomplete->isHidden()) {
_fieldAutocomplete->chooseSelected(
ChatHelpers::FieldAutocomplete::ChooseMethod::ByTab);
}
}
@ -7500,7 +7418,7 @@ bool HistoryWidget::sendExistingDocument(
document,
localId);
if (_fieldAutocomplete->stickersShown()) {
if (_autocomplete && _autocomplete->stickersShown()) {
clearFieldText();
//_saveDraftText = true;
//_saveDraftStart = crl::now();
@ -7986,7 +7904,9 @@ void HistoryWidget::fullInfoUpdated() {
if (updateCanSendMessage()) {
refresh = true;
}
checkFieldAutocomplete();
if (_autocomplete) {
_autocomplete->requestRefresh();
}
_list->refreshAboutView();
_list->updateBotInfo();
@ -8129,8 +8049,8 @@ void HistoryWidget::escape() {
} else {
cancelEdit();
}
} else if (!_fieldAutocomplete->isHidden()) {
_fieldAutocomplete->hideAnimated();
} else if (_autocomplete && !_autocomplete->isHidden()) {
_autocomplete->hideAnimated();
} else if (_replyTo && _field->getTextWithTags().text.isEmpty()) {
cancelReply();
} else if (auto &voice = _voiceRecordBar; voice->isActive()) {

View file

@ -21,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
enum class SendMediaType;
class MessageLinksParser;
struct InlineBotQuery;
struct AutocompleteQuery;
namespace MTP {
class Error;
@ -76,6 +75,10 @@ class ContinuousScroll;
struct ChatPaintHighlight;
} // namespace Ui
namespace Ui::Emoji {
class SuggestionsController;
} // namespace Ui::Emoji
namespace Window {
class SessionController;
} // namespace Window
@ -84,7 +87,7 @@ namespace ChatHelpers {
class TabbedPanel;
class TabbedSelector;
class FieldAutocomplete;
enum class FieldAutocompleteChooseMethod;
struct FileChosen;
} // namespace ChatHelpers
namespace HistoryView {
@ -126,8 +129,6 @@ public:
QWidget *parent,
not_null<Window::SessionController*> controller);
void start();
void historyLoaded();
[[nodiscard]] bool preventsClose(Fn<void()> &&continueCallback) const;
@ -159,7 +160,6 @@ public:
bool updateReplaceMediaButton();
void updateFieldPlaceholder();
bool updateStickersByEmoji();
bool confirmSendingFiles(const QStringList &files);
bool confirmSendingFiles(not_null<const QMimeData*> data);
@ -367,17 +367,15 @@ private:
void fieldFocused();
void fieldResized();
void insertHashtagOrBotCommand(
QString str,
ChatHelpers::FieldAutocompleteChooseMethod method);
void initFieldAutocomplete();
void cancelInlineBot();
void saveDraft(bool delayed = false);
void saveCloudDraft();
void saveDraftDelayed();
void checkFieldAutocomplete();
void showMembersDropdown();
void windowIsVisibleChanged();
void saveFieldToHistoryLocalDraft();
void fileChosen(ChatHelpers::FileChosen &&data);
void updateFieldSubmitSettings();
@ -485,8 +483,6 @@ private:
void orderWidgets();
[[nodiscard]] InlineBotQuery parseInlineBotQuery() const;
[[nodiscard]] auto parseMentionHashtagBotCommandQuery() const
-> AutocompleteQuery;
void clearInlineBot();
void inlineBotChanged();
@ -740,7 +736,8 @@ private:
HistoryView::CornerButtons _cornerButtons;
const object_ptr<ChatHelpers::FieldAutocomplete> _fieldAutocomplete;
std::unique_ptr<ChatHelpers::FieldAutocomplete> _autocomplete;
std::unique_ptr<Ui::Emoji::SuggestionsController> _emojiSuggestions;
object_ptr<Support::Autocomplete> _supportAutocomplete;
UserData *_inlineBot = nullptr;
@ -807,8 +804,6 @@ private:
DragArea::Areas _attachDragAreas;
Fn<void()> _raiseEmojiSuggestions;
bool _nonEmptySelection = false;
TextUpdateEvents _textUpdateEvents = (TextUpdateEvents() | TextUpdateEvent::SaveDraft | TextUpdateEvent::SendTyping);

View file

@ -859,10 +859,6 @@ ComposeControls::ComposeControls(
_wrap.get(),
st::historyBotCommandStart)
: nullptr)
, _autocomplete(std::make_unique<ChatHelpers::FieldAutocomplete>(
parent,
_show,
&_st.tabbed))
, _header(std::make_unique<FieldHeader>(
_wrap.get(),
_show,
@ -963,6 +959,7 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) {
_header->setHistory(args);
registerDraftSource();
_selector->setCurrentPeer(history ? history->peer.get() : nullptr);
initFieldAutocomplete();
initWebpageProcess();
initWriteRestriction();
initForwardProcess();
@ -1238,8 +1235,8 @@ void ComposeControls::raisePanels() {
if (_attachBotsMenu) {
_attachBotsMenu->raise();
}
if (_raiseEmojiSuggestions) {
_raiseEmojiSuggestions();
if (_emojiSuggestions) {
_emojiSuggestions->raise();
}
}
@ -1323,35 +1320,6 @@ void ComposeControls::hidePanelsAnimated() {
}
}
void ComposeControls::checkAutocomplete() {
if (!_history) {
return;
}
const auto peer = _history->peer;
const auto autocomplete = _isInlineBot
? AutocompleteQuery()
: ParseMentionHashtagBotCommandQuery(_field, _features);
if (!autocomplete.query.isEmpty()) {
if (autocomplete.query[0] == '#'
&& cRecentWriteHashtags().isEmpty()
&& cRecentSearchHashtags().isEmpty()) {
peer->session().local().readRecentHashtagsAndBots();
} else if (autocomplete.query[0] == '@'
&& cRecentInlineBots().isEmpty()) {
peer->session().local().readRecentHashtagsAndBots();
} else if (autocomplete.query[0] == '/'
&& peer->isUser()
&& !peer->asUser()->isBot()) {
return;
}
}
_autocomplete->showFiltered(
peer,
autocomplete.query,
autocomplete.fromStart);
}
void ComposeControls::hide() {
showStarted();
_hidden = true;
@ -1361,7 +1329,9 @@ void ComposeControls::show() {
if (_hidden.current()) {
_hidden = false;
showFinished();
checkAutocomplete();
if (_autocomplete) {
_autocomplete->requestRefresh();
}
}
}
@ -1630,10 +1600,6 @@ void ComposeControls::initField() {
) | rpl::start_with_next([=] {
escape();
}, _field->lifetime());
_field->tabbed(
) | rpl::start_with_next([=] {
fieldTabbed();
}, _field->lifetime());
_field->heightChanges(
) | rpl::start_with_next([=] {
updateHeight();
@ -1664,21 +1630,6 @@ void ComposeControls::initField() {
_field->setEditLinkCallback(
DefaultEditLinkCallback(_show, _field, &_st.boxField));
_field->setEditLanguageCallback(DefaultEditLanguageCallback(_show));
initAutocomplete();
const auto allow = [=](not_null<DocumentData*> emoji) {
return _history
&& Data::AllowEmojiWithoutPremium(_history->peer, emoji);
};
const auto suggestions = Ui::Emoji::SuggestionsController::Init(
_panelsParent,
_field,
_session,
{
.suggestCustomEmoji = true,
.allowCustomWithoutPremium = allow,
.st = &_st.suggestions,
});
_raiseEmojiSuggestions = [=] { suggestions->raise(); };
const auto rawTextEdit = _field->rawTextEdit().get();
rpl::merge(
@ -1698,124 +1649,65 @@ void ComposeControls::updateSubmitSettings() {
_field->setSubmitSettings(settings);
}
void ComposeControls::initAutocomplete() {
const auto insertHashtagOrBotCommand = [=](
const QString &string,
ChatHelpers::FieldAutocompleteChooseMethod method) {
// Send bot command at once, if it was not inserted by pressing Tab.
using Method = ChatHelpers::FieldAutocompleteChooseMethod;
if (string.at(0) == '/' && method != Method::ByTab) {
_sendCommandRequests.fire_copy(string);
setText(
_field->getTextWithTagsPart(_field->textCursor().position()));
} else {
_field->insertTag(string);
}
};
_autocomplete->mentionChosen(
) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::MentionChosen data) {
const auto user = data.user;
if (data.mention.isEmpty()) {
_field->insertTag(
user->firstName.isEmpty() ? user->name() : user->firstName,
PrepareMentionTag(user));
} else {
_field->insertTag('@' + data.mention);
}
}, _autocomplete->lifetime());
_autocomplete->hashtagChosen(
) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::HashtagChosen data) {
insertHashtagOrBotCommand(data.hashtag, data.method);
}, _autocomplete->lifetime());
_autocomplete->botCommandChosen(
) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::BotCommandChosen data) {
insertHashtagOrBotCommand(data.command, data.method);
}, _autocomplete->lifetime());
_autocomplete->stickerChosen(
) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::StickerChosen data) {
if (!_showSlowmodeError || !_showSlowmodeError()) {
setText({});
}
//_saveDraftText = true;
//_saveDraftStart = crl::now();
//saveDraft();
//saveCloudDraft(); // won't be needed if SendInlineBotResult will clear the cloud draft
_fileChosen.fire(std::move(data));
}, _autocomplete->lifetime());
_autocomplete->choosingProcesses(
) | rpl::start_with_next([=](ChatHelpers::FieldAutocomplete::Type type) {
if (type == ChatHelpers::FieldAutocomplete::Type::Stickers) {
void ComposeControls::initFieldAutocomplete() {
_emojiSuggestions = nullptr;
_autocomplete = nullptr;
if (!_history) {
return;
}
ChatHelpers::InitFieldAutocomplete(_autocomplete, {
.parent = _parent,
.show = _show,
.field = _field.get(),
.stOverride = &_st.tabbed,
.peer = _history->peer,
.features = [=] {
auto result = _features;
if (_inlineBot && !_inlineLookingUpBot) {
result.autocompleteMentions = false;
result.autocompleteHashtags = false;
result.autocompleteCommands = false;
}
if (isEditingMessage()) {
result.autocompleteCommands = false;
result.suggestStickersByEmoji = false;
}
return result;
},
.sendMenuDetails = [=] { return sendMenuDetails(); },
.stickerChoosing = [=] {
_sendActionUpdates.fire({
.type = Api::SendProgressType::ChooseSticker,
});
}
}, _autocomplete->lifetime());
_autocomplete->setSendMenuDetails([=] { return sendMenuDetails(); });
//_autocomplete->setModerateKeyActivateCallback([=](int key) {
// return _keyboard->isHidden()
// ? false
// : _keyboard->moderateKeyActivate(key);
//});
_field->rawTextEdit()->installEventFilter(_autocomplete.get());
_session->data().botCommandsChanges(
) | rpl::filter([=](not_null<PeerData*> peer) {
return _history && (_history->peer == peer);
}) | rpl::start_with_next([=] {
if (_autocomplete->clearFilteredBotCommands()) {
checkAutocomplete();
}
}, _autocomplete->lifetime());
_session->data().stickers().updated(
Data::StickersType::Stickers
) | rpl::start_with_next([=] {
updateStickersByEmoji();
}, _autocomplete->lifetime());
QObject::connect(
_field->rawTextEdit(),
&QTextEdit::cursorPositionChanged,
_autocomplete.get(),
[=] { checkAutocomplete(); },
Qt::QueuedConnection);
_autocomplete->hideFast();
}
bool ComposeControls::updateStickersByEmoji() {
if (!_history) {
return false;
}
const auto emoji = [&] {
const auto errorForStickers = Data::RestrictionError(
_history->peer,
ChatRestriction::SendStickers);
if (!isEditingMessage() && !errorForStickers) {
const auto &text = _field->getTextWithTags().text;
auto length = 0;
if (const auto emoji = Ui::Emoji::Find(text, &length)) {
if (text.size() <= length) {
return emoji;
}
},
.stickerChosen = [=](ChatHelpers::FileChosen &&data) {
if (!_showSlowmodeError || !_showSlowmodeError()) {
setText({});
}
}
return EmojiPtr(nullptr);
}();
_autocomplete->showStickers(emoji);
return (emoji != nullptr);
//_saveDraftText = true;
//_saveDraftStart = crl::now();
//saveDraft();
// Won't be needed if SendInlineBotResult clears the cloud draft.
//saveCloudDraft();
_fileChosen.fire(std::move(data));
},
.setText = [=](TextWithTags text) { setText(text); },
.sendBotCommand = [=](QString command) {
_sendCommandRequests.fire_copy(command);
},
});
const auto allow = [=](not_null<DocumentData*> emoji) {
return Data::AllowEmojiWithoutPremium(_history->peer, emoji);
};
_emojiSuggestions.reset(Ui::Emoji::SuggestionsController::Init(
_panelsParent,
_field,
_session,
{
.suggestCustomEmoji = true,
.allowCustomWithoutPremium = allow,
.st = &_st.suggestions,
}));
}
void ComposeControls::updateFieldPlaceholder() {
@ -1872,10 +1764,9 @@ void ComposeControls::fieldChanged() {
updateControlsVisibility();
updateControlsGeometry(_wrap->size());
}
InvokeQueued(_autocomplete.get(), [=] {
InvokeQueued(_field.get(), [=] {
updateInlineBotQuery();
const auto choosingSticker = updateStickersByEmoji();
if (!choosingSticker && typing) {
if ((!_autocomplete || !_autocomplete->stickersEmoji()) && typing) {
_sendActionUpdates.fire({ Api::SendProgressType::Typing });
}
});
@ -2026,7 +1917,11 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
? draft->reply.messageId
: FullMsgId();
InvokeQueued(_autocomplete.get(), [=] { updateStickersByEmoji(); });
InvokeQueued(_autocomplete.get(), [=] {
if (_autocomplete) {
_autocomplete->requestStickersUpdate();
}
});
const auto guard = gsl::finally([&] {
updateSendButtonType();
updateReplaceMediaButton();
@ -2128,13 +2023,6 @@ void ComposeControls::cancelForward() {
updateForwarding();
}
void ComposeControls::fieldTabbed() {
if (!_autocomplete->isHidden()) {
_autocomplete->chooseSelected(
ChatHelpers::FieldAutocomplete::ChooseMethod::ByTab);
}
}
rpl::producer<SendActionUpdate> ComposeControls::sendActionUpdates() const {
return rpl::merge(
_sendActionUpdates.events(),
@ -2301,7 +2189,9 @@ void ComposeControls::clearInlineBot() {
if (_inlineResults) {
_inlineResults->clearInlineBot();
}
checkAutocomplete();
if (_autocomplete) {
_autocomplete->requestRefresh();
}
}
void ComposeControls::inlineBotChanged() {
@ -2310,7 +2200,9 @@ void ComposeControls::inlineBotChanged() {
_isInlineBot = isInlineBot;
updateFieldPlaceholder();
updateSubmitSettings();
checkAutocomplete();
if (_autocomplete) {
_autocomplete->requestRefresh();
}
}
}
@ -2983,7 +2875,9 @@ void ComposeControls::editMessage(not_null<HistoryItem*> item) {
}
if (_autocomplete) {
InvokeQueued(_autocomplete.get(), [=] { checkAutocomplete(); });
InvokeQueued(_autocomplete.get(), [=] {
_autocomplete->requestRefresh();
});
}
}
@ -3169,7 +3063,6 @@ void ComposeControls::initWebpageProcess() {
}) | rpl::start_with_next([=](Data::PeerUpdate::Flags flags) {
if (flags & Data::PeerUpdate::Flag::Rights) {
_preview->checkNow(false);
updateStickersByEmoji();
updateFieldPlaceholder();
}
if (flags & Data::PeerUpdate::Flag::Notifications) {
@ -3401,7 +3294,7 @@ void ComposeControls::applyInlineBotQuery(
updateOuterGeometry(_wrap->geometry());
}
_inlineResults->queryInlineBot(_inlineBot, _history->peer, query);
if (!_autocomplete->isHidden()) {
if (_autocomplete) {
_autocomplete->hideAnimated();
}
} else {

View file

@ -68,6 +68,10 @@ class DropdownMenu;
struct PreparedList;
} // namespace Ui
namespace Ui::Emoji {
class SuggestionsController;
} // namespace Ui::Emoji
namespace Main {
class Session;
} // namespace Main
@ -259,6 +263,7 @@ private:
void init();
void initField();
void initFieldAutocomplete();
void initTabbedSelector();
void initSendButton();
void initSendAsButton(not_null<PeerData*> peer);
@ -266,7 +271,6 @@ private:
void initForwardProcess();
void initWriteRestriction();
void initVoiceRecordBar();
void initAutocomplete();
void initKeyHandler();
void updateSubmitSettings();
void updateSendButtonType();
@ -290,15 +294,12 @@ private:
SendRequestType requestType = SendRequestType::Text) const;
void orderControls();
void checkAutocomplete();
bool updateStickersByEmoji();
void updateFieldPlaceholder();
void updateSilentBroadcast();
void editMessage(not_null<HistoryItem*> item);
void escape();
void fieldChanged();
void fieldTabbed();
void toggleTabbedSelectorMode();
void createTabbedPanel();
void setTabbedPanel(std::unique_ptr<ChatHelpers::TabbedPanel> panel);
@ -392,6 +393,7 @@ private:
std::unique_ptr<ChatHelpers::TabbedPanel> _tabbedPanel;
std::unique_ptr<Ui::DropdownMenu> _attachBotsMenu;
std::unique_ptr<ChatHelpers::FieldAutocomplete> _autocomplete;
std::unique_ptr<Ui::Emoji::SuggestionsController> _emojiSuggestions;
friend class FieldHeader;
const std::unique_ptr<FieldHeader> _header;
@ -441,8 +443,6 @@ private:
std::unique_ptr<Controls::WebpageProcessor> _preview;
Fn<void()> _raiseEmojiSuggestions;
rpl::lifetime _historyLifetime;
rpl::lifetime _uploaderSubscriptions;

View file

@ -422,7 +422,7 @@ MainWidget::MainWidget(
cSetOtherOnline(0);
_history->start();
session().data().stickers().notifySavedGifsUpdated();
}
MainWidget::~MainWidget() = default;