Move FieldAutocomplete to ChatHelpers.

This commit is contained in:
John Preston 2024-09-13 13:13:28 +04:00
parent b78443cf2d
commit 969152e949
6 changed files with 78 additions and 58 deletions

View file

@ -53,6 +53,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <QtWidgets/QApplication> #include <QtWidgets/QApplication>
namespace ChatHelpers {
namespace { namespace {
[[nodiscard]] QString PrimaryUsername(not_null<UserData*> user) { [[nodiscard]] QString PrimaryUsername(not_null<UserData*> user) {
@ -60,6 +61,18 @@ namespace {
return usernames.empty() ? user->username() : usernames.front(); return usernames.empty() ? user->username() : usernames.front();
} }
template <typename T, typename U>
inline int indexOfInFirstN(const T &v, const U &elem, int last) {
for (auto b = v.cbegin(), i = b, e = b + std::max(int(v.size()), last)
; i != e
; ++i) {
if (i->user == elem) {
return (i - b);
}
}
return -1;
}
} // namespace } // namespace
class FieldAutocomplete::Inner final : public Ui::RpWidget { class FieldAutocomplete::Inner final : public Ui::RpWidget {
@ -70,7 +83,7 @@ public:
}; };
Inner( Inner(
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<Show> show,
const style::EmojiPan &st, const style::EmojiPan &st,
not_null<FieldAutocomplete*> parent, not_null<FieldAutocomplete*> parent,
not_null<MentionRows*> mrows, not_null<MentionRows*> mrows,
@ -127,7 +140,7 @@ private:
Media::Clip::Notification notification, Media::Clip::Notification notification,
not_null<DocumentData*> document); not_null<DocumentData*> document);
const std::shared_ptr<ChatHelpers::Show> _show; const std::shared_ptr<Show> _show;
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
const style::EmojiPan &_st; const style::EmojiPan &_st;
const not_null<FieldAutocomplete*> _parent; const not_null<FieldAutocomplete*> _parent;
@ -197,7 +210,7 @@ FieldAutocomplete::FieldAutocomplete(
FieldAutocomplete::FieldAutocomplete( FieldAutocomplete::FieldAutocomplete(
QWidget *parent, QWidget *parent,
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<Show> show,
const style::EmojiPan *stOverride) const style::EmojiPan *stOverride)
: RpWidget(parent) : RpWidget(parent)
, _show(std::move(show)) , _show(std::move(show))
@ -235,7 +248,7 @@ FieldAutocomplete::FieldAutocomplete(
}), lifetime()); }), lifetime());
} }
std::shared_ptr<ChatHelpers::Show> FieldAutocomplete::uiShow() const { std::shared_ptr<Show> FieldAutocomplete::uiShow() const {
return _show; return _show;
} }
@ -373,18 +386,6 @@ bool FieldAutocomplete::clearFilteredBotCommands() {
return true; return true;
} }
namespace {
template <typename T, typename U>
inline int indexOfInFirstN(const T &v, const U &elem, int last) {
for (auto b = v.cbegin(), i = b, e = b + std::max(int(v.size()), last); i != e; ++i) {
if (i->user == elem) {
return (i - b);
}
}
return -1;
}
}
FieldAutocomplete::StickerRows FieldAutocomplete::getStickerSuggestions() { FieldAutocomplete::StickerRows FieldAutocomplete::getStickerSuggestions() {
const auto data = &_session->data().stickers(); const auto data = &_session->data().stickers();
const auto list = data->getListByEmoji({ _emoji }, _stickersSeed); const auto list = data->getListByEmoji({ _emoji }, _stickersSeed);
@ -871,7 +872,7 @@ bool FieldAutocomplete::eventFilter(QObject *obj, QEvent *e) {
} }
FieldAutocomplete::Inner::Inner( FieldAutocomplete::Inner::Inner(
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<Show> show,
const style::EmojiPan &st, const style::EmojiPan &st,
not_null<FieldAutocomplete*> parent, not_null<FieldAutocomplete*> parent,
not_null<MentionRows*> mrows, not_null<MentionRows*> mrows,
@ -963,8 +964,8 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
media->checkStickerSmall(); media->checkStickerSmall();
const auto paused = _show->paused( const auto paused = _show->paused(
ChatHelpers::PauseReason::TabbedPanel); PauseReason::TabbedPanel);
const auto size = ChatHelpers::ComputeStickerSize( const auto size = ComputeStickerSize(
document, document,
stickerBoundingBox()); stickerBoundingBox());
const auto ppos = pos + QPoint( const auto ppos = pos + QPoint(
@ -989,7 +990,7 @@ void FieldAutocomplete::Inner::paintEvent(QPaintEvent *e) {
} else if (const auto image = media->getStickerSmall()) { } else if (const auto image = media->getStickerSmall()) {
p.drawPixmapLeft(ppos, width(), image->pix(size)); p.drawPixmapLeft(ppos, width(), image->pix(size));
} else { } else {
ChatHelpers::PaintStickerThumbnailPath( PaintStickerThumbnailPath(
p, p,
media.get(), media.get(),
QRect(ppos, size), QRect(ppos, size),
@ -1250,7 +1251,7 @@ bool FieldAutocomplete::Inner::chooseAtIndex(
const auto bounding = selectedRect(index); const auto bounding = selectedRect(index);
auto contentRect = QRect( auto contentRect = QRect(
QPoint(), QPoint(),
ChatHelpers::ComputeStickerSize( ComputeStickerSize(
document, document,
stickerBoundingBox())); stickerBoundingBox()));
contentRect.moveCenter(bounding.center()); contentRect.moveCenter(bounding.center());
@ -1464,9 +1465,9 @@ auto FieldAutocomplete::Inner::getLottieRenderer()
void FieldAutocomplete::Inner::setupLottie(StickerSuggestion &suggestion) { void FieldAutocomplete::Inner::setupLottie(StickerSuggestion &suggestion) {
const auto document = suggestion.document; const auto document = suggestion.document;
suggestion.lottie = ChatHelpers::LottiePlayerFromDocument( suggestion.lottie = LottiePlayerFromDocument(
suggestion.documentMedia.get(), suggestion.documentMedia.get(),
ChatHelpers::StickerLottieSize::InlineResults, StickerLottieSize::InlineResults,
stickerBoundingBox() * style::DevicePixelRatio(), stickerBoundingBox() * style::DevicePixelRatio(),
Lottie::Quality::Default, Lottie::Quality::Default,
getLottieRenderer()); getLottieRenderer());
@ -1534,7 +1535,7 @@ void FieldAutocomplete::Inner::clipCallback(
} else if (i->webm->state() == State::Error) { } else if (i->webm->state() == State::Error) {
i->webm.setBad(); i->webm.setBad();
} else if (i->webm->ready() && !i->webm->started()) { } else if (i->webm->ready() && !i->webm->started()) {
const auto size = ChatHelpers::ComputeStickerSize( const auto size = ComputeStickerSize(
i->document, i->document,
stickerBoundingBox()); stickerBoundingBox());
i->webm->start({ .frame = size, .keepAlpha = true }); i->webm->start({ .frame = size, .keepAlpha = true });
@ -1632,3 +1633,5 @@ auto FieldAutocomplete::Inner::scrollToRequested() const
-> rpl::producer<ScrollTo> { -> rpl::producer<ScrollTo> {
return _scrollToRequested.events(); return _scrollToRequested.events();
} }
} // namespace ChatHelpers

View file

@ -46,9 +46,15 @@ struct Details;
} // namespace SendMenu } // namespace SendMenu
namespace ChatHelpers { namespace ChatHelpers {
struct FileChosen; struct FileChosen;
class Show; class Show;
} // namespace ChatHelpers
enum class FieldAutocompleteChooseMethod {
ByEnter,
ByTab,
ByClick,
};
class FieldAutocomplete final : public Ui::RpWidget { class FieldAutocomplete final : public Ui::RpWidget {
public: public:
@ -57,11 +63,11 @@ public:
not_null<Window::SessionController*> controller); not_null<Window::SessionController*> controller);
FieldAutocomplete( FieldAutocomplete(
QWidget *parent, QWidget *parent,
std::shared_ptr<ChatHelpers::Show> show, std::shared_ptr<Show> show,
const style::EmojiPan *stOverride = nullptr); const style::EmojiPan *stOverride = nullptr);
~FieldAutocomplete(); ~FieldAutocomplete();
[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const; [[nodiscard]] std::shared_ptr<Show> uiShow() const;
bool clearFilteredBotCommands(); bool clearFilteredBotCommands();
void showFiltered( void showFiltered(
@ -81,11 +87,7 @@ public:
bool eventFilter(QObject *obj, QEvent *e) override; bool eventFilter(QObject *obj, QEvent *e) override;
enum class ChooseMethod { using ChooseMethod = FieldAutocompleteChooseMethod;
ByEnter,
ByTab,
ByClick,
};
struct MentionChosen { struct MentionChosen {
not_null<UserData*> user; not_null<UserData*> user;
QString mention; QString mention;
@ -100,7 +102,7 @@ public:
QString command; QString command;
ChooseMethod method = ChooseMethod::ByEnter; ChooseMethod method = ChooseMethod::ByEnter;
}; };
using StickerChosen = ChatHelpers::FileChosen; using StickerChosen = FileChosen;
enum class Type { enum class Type {
Mentions, Mentions,
Hashtags, Hashtags,
@ -157,7 +159,7 @@ private:
void recount(bool resetScroll = false); void recount(bool resetScroll = false);
StickerRows getStickerSuggestions(); StickerRows getStickerSuggestions();
const std::shared_ptr<ChatHelpers::Show> _show; const std::shared_ptr<Show> _show;
const not_null<Main::Session*> _session; const not_null<Main::Session*> _session;
const style::EmojiPan &_st; const style::EmojiPan &_st;
QPixmap _cache; QPixmap _cache;
@ -193,3 +195,5 @@ private:
Fn<bool(int)> _moderateKeyActivateCallback; Fn<bool(int)> _moderateKeyActivateCallback;
}; };
} // namespace ChatHelpers

View file

@ -118,6 +118,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/media/history_view_media.h" #include "history/view/media/history_view_media.h"
#include "profile/profile_block_group_members.h" #include "profile/profile_block_group_members.h"
#include "core/click_handler_types.h" #include "core/click_handler_types.h"
#include "chat_helpers/field_autocomplete.h"
#include "chat_helpers/tabbed_panel.h" #include "chat_helpers/tabbed_panel.h"
#include "chat_helpers/tabbed_selector.h" #include "chat_helpers/tabbed_selector.h"
#include "chat_helpers/tabbed_section.h" #include "chat_helpers/tabbed_section.h"
@ -473,7 +474,8 @@ HistoryWidget::HistoryWidget(
}, lifetime()); }, lifetime());
_fieldAutocomplete->mentionChosen( _fieldAutocomplete->mentionChosen(
) | rpl::start_with_next([=](FieldAutocomplete::MentionChosen data) { ) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::MentionChosen data) {
auto replacement = QString(); auto replacement = QString();
auto entityTag = QString(); auto entityTag = QString();
if (data.mention.isEmpty()) { if (data.mention.isEmpty()) {
@ -489,13 +491,15 @@ HistoryWidget::HistoryWidget(
}, lifetime()); }, lifetime());
_fieldAutocomplete->hashtagChosen( _fieldAutocomplete->hashtagChosen(
) | rpl::start_with_next([=](FieldAutocomplete::HashtagChosen data) { ) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::HashtagChosen data) {
insertHashtagOrBotCommand(data.hashtag, data.method); insertHashtagOrBotCommand(data.hashtag, data.method);
}, lifetime()); }, lifetime());
_fieldAutocomplete->botCommandChosen( _fieldAutocomplete->botCommandChosen(
) | rpl::start_with_next([=](FieldAutocomplete::BotCommandChosen data) { ) | rpl::start_with_next([=](
using Method = FieldAutocomplete::ChooseMethod; ChatHelpers::FieldAutocomplete::BotCommandChosen data) {
using Method = ChatHelpers::FieldAutocompleteChooseMethod;
const auto messages = &data.user->owner().shortcutMessages(); const auto messages = &data.user->owner().shortcutMessages();
const auto shortcut = data.user->isSelf(); const auto shortcut = data.user->isSelf();
const auto command = data.command.mid(1); const auto command = data.command.mid(1);
@ -528,11 +532,11 @@ HistoryWidget::HistoryWidget(
}); });
_fieldAutocomplete->choosingProcesses( _fieldAutocomplete->choosingProcesses(
) | rpl::start_with_next([=](FieldAutocomplete::Type type) { ) | rpl::start_with_next([=](ChatHelpers::FieldAutocomplete::Type type) {
if (!_history) { if (!_history) {
return; return;
} }
if (type == FieldAutocomplete::Type::Stickers) { if (type == ChatHelpers::FieldAutocomplete::Type::Stickers) {
session().sendProgressManager().update( session().sendProgressManager().update(
_history, _history,
Api::SendProgressType::ChooseSticker); Api::SendProgressType::ChooseSticker);
@ -1488,13 +1492,14 @@ void HistoryWidget::start() {
void HistoryWidget::insertHashtagOrBotCommand( void HistoryWidget::insertHashtagOrBotCommand(
QString str, QString str,
FieldAutocomplete::ChooseMethod method) { ChatHelpers::FieldAutocompleteChooseMethod method) {
if (!_peer) { if (!_peer) {
return; return;
} }
// Send bot command at once, if it was not inserted by pressing Tab. // Send bot command at once, if it was not inserted by pressing Tab.
if (str.at(0) == '/' && method != FieldAutocomplete::ChooseMethod::ByTab) { using Method = ChatHelpers::FieldAutocompleteChooseMethod;
if (str.at(0) == '/' && method != Method::ByTab) {
sendBotCommand({ _peer, str, FullMsgId(), replyTo() }); sendBotCommand({ _peer, str, FullMsgId(), replyTo() });
session().api().finishForwarding(prepareSendAction({})); session().api().finishForwarding(prepareSendAction({}));
setFieldText(_field->getTextWithTagsPart(_field->textCursor().position())); setFieldText(_field->getTextWithTagsPart(_field->textCursor().position()));
@ -6935,7 +6940,8 @@ void HistoryWidget::fieldTabbed() {
if (_supportAutocomplete) { if (_supportAutocomplete) {
_supportAutocomplete->activate(_field.data()); _supportAutocomplete->activate(_field.data());
} else if (!_fieldAutocomplete->isHidden()) { } else if (!_fieldAutocomplete->isHidden()) {
_fieldAutocomplete->chooseSelected(FieldAutocomplete::ChooseMethod::ByTab); _fieldAutocomplete->chooseSelected(
ChatHelpers::FieldAutocomplete::ChooseMethod::ByTab);
} }
} }

View file

@ -13,7 +13,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_view_highlight_manager.h" #include "history/history_view_highlight_manager.h"
#include "history/history_view_top_toast.h" #include "history/history_view_top_toast.h"
#include "history/history.h" #include "history/history.h"
#include "chat_helpers/field_autocomplete.h"
#include "chat_helpers/field_characters_count_manager.h" #include "chat_helpers/field_characters_count_manager.h"
#include "window/section_widget.h" #include "window/section_widget.h"
#include "ui/widgets/fields/input_field.h" #include "ui/widgets/fields/input_field.h"
@ -84,6 +83,8 @@ class SessionController;
namespace ChatHelpers { namespace ChatHelpers {
class TabbedPanel; class TabbedPanel;
class TabbedSelector; class TabbedSelector;
class FieldAutocomplete;
enum class FieldAutocompleteChooseMethod;
} // namespace ChatHelpers } // namespace ChatHelpers
namespace HistoryView { namespace HistoryView {
@ -368,7 +369,7 @@ private:
void insertHashtagOrBotCommand( void insertHashtagOrBotCommand(
QString str, QString str,
FieldAutocomplete::ChooseMethod method); ChatHelpers::FieldAutocompleteChooseMethod method);
void cancelInlineBot(); void cancelInlineBot();
void saveDraft(bool delayed = false); void saveDraft(bool delayed = false);
void saveCloudDraft(); void saveCloudDraft();
@ -739,7 +740,7 @@ private:
HistoryView::CornerButtons _cornerButtons; HistoryView::CornerButtons _cornerButtons;
const object_ptr<FieldAutocomplete> _fieldAutocomplete; const object_ptr<ChatHelpers::FieldAutocomplete> _fieldAutocomplete;
object_ptr<Support::Autocomplete> _supportAutocomplete; object_ptr<Support::Autocomplete> _supportAutocomplete;
UserData *_inlineBot = nullptr; UserData *_inlineBot = nullptr;

View file

@ -859,7 +859,7 @@ ComposeControls::ComposeControls(
_wrap.get(), _wrap.get(),
st::historyBotCommandStart) st::historyBotCommandStart)
: nullptr) : nullptr)
, _autocomplete(std::make_unique<FieldAutocomplete>( , _autocomplete(std::make_unique<ChatHelpers::FieldAutocomplete>(
parent, parent,
_show, _show,
&_st.tabbed)) &_st.tabbed))
@ -1701,9 +1701,10 @@ void ComposeControls::updateSubmitSettings() {
void ComposeControls::initAutocomplete() { void ComposeControls::initAutocomplete() {
const auto insertHashtagOrBotCommand = [=]( const auto insertHashtagOrBotCommand = [=](
const QString &string, const QString &string,
FieldAutocomplete::ChooseMethod method) { ChatHelpers::FieldAutocompleteChooseMethod method) {
// Send bot command at once, if it was not inserted by pressing Tab. // Send bot command at once, if it was not inserted by pressing Tab.
if (string.at(0) == '/' && method != FieldAutocomplete::ChooseMethod::ByTab) { using Method = ChatHelpers::FieldAutocompleteChooseMethod;
if (string.at(0) == '/' && method != Method::ByTab) {
_sendCommandRequests.fire_copy(string); _sendCommandRequests.fire_copy(string);
setText( setText(
_field->getTextWithTagsPart(_field->textCursor().position())); _field->getTextWithTagsPart(_field->textCursor().position()));
@ -1713,7 +1714,8 @@ void ComposeControls::initAutocomplete() {
}; };
_autocomplete->mentionChosen( _autocomplete->mentionChosen(
) | rpl::start_with_next([=](FieldAutocomplete::MentionChosen data) { ) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::MentionChosen data) {
const auto user = data.user; const auto user = data.user;
if (data.mention.isEmpty()) { if (data.mention.isEmpty()) {
_field->insertTag( _field->insertTag(
@ -1725,17 +1727,20 @@ void ComposeControls::initAutocomplete() {
}, _autocomplete->lifetime()); }, _autocomplete->lifetime());
_autocomplete->hashtagChosen( _autocomplete->hashtagChosen(
) | rpl::start_with_next([=](FieldAutocomplete::HashtagChosen data) { ) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::HashtagChosen data) {
insertHashtagOrBotCommand(data.hashtag, data.method); insertHashtagOrBotCommand(data.hashtag, data.method);
}, _autocomplete->lifetime()); }, _autocomplete->lifetime());
_autocomplete->botCommandChosen( _autocomplete->botCommandChosen(
) | rpl::start_with_next([=](FieldAutocomplete::BotCommandChosen data) { ) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::BotCommandChosen data) {
insertHashtagOrBotCommand(data.command, data.method); insertHashtagOrBotCommand(data.command, data.method);
}, _autocomplete->lifetime()); }, _autocomplete->lifetime());
_autocomplete->stickerChosen( _autocomplete->stickerChosen(
) | rpl::start_with_next([=](FieldAutocomplete::StickerChosen data) { ) | rpl::start_with_next([=](
ChatHelpers::FieldAutocomplete::StickerChosen data) {
if (!_showSlowmodeError || !_showSlowmodeError()) { if (!_showSlowmodeError || !_showSlowmodeError()) {
setText({}); setText({});
} }
@ -1747,8 +1752,8 @@ void ComposeControls::initAutocomplete() {
}, _autocomplete->lifetime()); }, _autocomplete->lifetime());
_autocomplete->choosingProcesses( _autocomplete->choosingProcesses(
) | rpl::start_with_next([=](FieldAutocomplete::Type type) { ) | rpl::start_with_next([=](ChatHelpers::FieldAutocomplete::Type type) {
if (type == FieldAutocomplete::Type::Stickers) { if (type == ChatHelpers::FieldAutocomplete::Type::Stickers) {
_sendActionUpdates.fire({ _sendActionUpdates.fire({
.type = Api::SendProgressType::ChooseSticker, .type = Api::SendProgressType::ChooseSticker,
}); });
@ -2125,7 +2130,8 @@ void ComposeControls::cancelForward() {
void ComposeControls::fieldTabbed() { void ComposeControls::fieldTabbed() {
if (!_autocomplete->isHidden()) { if (!_autocomplete->isHidden()) {
_autocomplete->chooseSelected(FieldAutocomplete::ChooseMethod::ByTab); _autocomplete->chooseSelected(
ChatHelpers::FieldAutocomplete::ChooseMethod::ByTab);
} }
} }

View file

@ -21,7 +21,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class History; class History;
class DocumentData; class DocumentData;
class FieldAutocomplete;
class Image; class Image;
namespace style { namespace style {
@ -39,6 +38,7 @@ struct FileChosen;
struct PhotoChosen; struct PhotoChosen;
class Show; class Show;
enum class PauseReason; enum class PauseReason;
class FieldAutocomplete;
} // namespace ChatHelpers } // namespace ChatHelpers
namespace Data { namespace Data {
@ -391,7 +391,7 @@ private:
std::unique_ptr<InlineBots::Layout::Widget> _inlineResults; std::unique_ptr<InlineBots::Layout::Widget> _inlineResults;
std::unique_ptr<ChatHelpers::TabbedPanel> _tabbedPanel; std::unique_ptr<ChatHelpers::TabbedPanel> _tabbedPanel;
std::unique_ptr<Ui::DropdownMenu> _attachBotsMenu; std::unique_ptr<Ui::DropdownMenu> _attachBotsMenu;
std::unique_ptr<FieldAutocomplete> _autocomplete; std::unique_ptr<ChatHelpers::FieldAutocomplete> _autocomplete;
friend class FieldHeader; friend class FieldHeader;
const std::unique_ptr<FieldHeader> _header; const std::unique_ptr<FieldHeader> _header;