Clean up/refactor InputCompletionPopup (#4242)
Mainly flattening anon namespaces & respecting clang-tidy
This commit is contained in:
@@ -51,3 +51,5 @@ CheckOptions:
|
|||||||
value: UPPER_CASE
|
value: UPPER_CASE
|
||||||
- key: readability-identifier-naming.VariableCase
|
- key: readability-identifier-naming.VariableCase
|
||||||
value: camelBack
|
value: camelBack
|
||||||
|
- key: readability-implicit-bool-conversion.AllowPointerConditions
|
||||||
|
value: true
|
||||||
|
|||||||
@@ -13,35 +13,47 @@
|
|||||||
#include "widgets/listview/GenericListView.hpp"
|
#include "widgets/listview/GenericListView.hpp"
|
||||||
#include "widgets/splits/InputCompletionItem.hpp"
|
#include "widgets/splits/InputCompletionItem.hpp"
|
||||||
|
|
||||||
namespace chatterino {
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct _Emote {
|
using namespace chatterino;
|
||||||
EmotePtr emote;
|
|
||||||
QString displayName;
|
|
||||||
QString providerName;
|
|
||||||
};
|
|
||||||
|
|
||||||
void addEmotes(std::vector<_Emote> &out, const EmoteMap &map,
|
struct CompletionEmote {
|
||||||
const QString &text, const QString &providerName)
|
EmotePtr emote;
|
||||||
{
|
QString displayName;
|
||||||
for (auto &&emote : map)
|
QString providerName;
|
||||||
if (emote.first.string.contains(text, Qt::CaseInsensitive))
|
};
|
||||||
out.push_back(
|
|
||||||
{emote.second, emote.second->name.string, providerName});
|
|
||||||
}
|
|
||||||
|
|
||||||
void addEmojis(std::vector<_Emote> &out, const EmojiMap &map,
|
void addEmotes(std::vector<CompletionEmote> &out, const EmoteMap &map,
|
||||||
const QString &text)
|
const QString &text, const QString &providerName)
|
||||||
|
{
|
||||||
|
for (auto &&emote : map)
|
||||||
{
|
{
|
||||||
map.each([&](const QString &, const std::shared_ptr<EmojiData> &emoji) {
|
if (emote.first.string.contains(text, Qt::CaseInsensitive))
|
||||||
for (auto &&shortCode : emoji->shortCodes)
|
{
|
||||||
if (shortCode.contains(text, Qt::CaseInsensitive))
|
out.push_back(
|
||||||
out.push_back({emoji->emote, shortCode, "Emoji"});
|
{emote.second, emote.second->name.string, providerName});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void addEmojis(std::vector<CompletionEmote> &out, const EmojiMap &map,
|
||||||
|
const QString &text)
|
||||||
|
{
|
||||||
|
map.each([&](const QString &, const std::shared_ptr<EmojiData> &emoji) {
|
||||||
|
for (auto &&shortCode : emoji->shortCodes)
|
||||||
|
{
|
||||||
|
if (shortCode.contains(text, Qt::CaseInsensitive))
|
||||||
|
{
|
||||||
|
out.push_back({emoji->emote, shortCode, "Emoji"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
namespace chatterino {
|
||||||
|
|
||||||
InputCompletionPopup::InputCompletionPopup(QWidget *parent)
|
InputCompletionPopup::InputCompletionPopup(QWidget *parent)
|
||||||
: BasePopup({BasePopup::EnableCustomFrame, BasePopup::Frameless,
|
: BasePopup({BasePopup::EnableCustomFrame, BasePopup::Frameless,
|
||||||
BasePopup::DontFocus, BaseWindow::DisableLayoutSave},
|
BasePopup::DontFocus, BaseWindow::DisableLayoutSave},
|
||||||
@@ -52,30 +64,17 @@ InputCompletionPopup::InputCompletionPopup(QWidget *parent)
|
|||||||
|
|
||||||
QObject::connect(&this->redrawTimer_, &QTimer::timeout, this, [this] {
|
QObject::connect(&this->redrawTimer_, &QTimer::timeout, this, [this] {
|
||||||
if (this->isVisible())
|
if (this->isVisible())
|
||||||
|
{
|
||||||
this->ui_.listView->doItemsLayout();
|
this->ui_.listView->doItemsLayout();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this->redrawTimer_.setInterval(33);
|
this->redrawTimer_.setInterval(33);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputCompletionPopup::initLayout()
|
|
||||||
{
|
|
||||||
LayoutCreator creator = {this};
|
|
||||||
|
|
||||||
auto listView =
|
|
||||||
creator.emplace<GenericListView>().assign(&this->ui_.listView);
|
|
||||||
listView->setInvokeActionOnTab(true);
|
|
||||||
|
|
||||||
listView->setModel(&this->model_);
|
|
||||||
QObject::connect(listView.getElement(), &GenericListView::closeRequested,
|
|
||||||
this, [this] {
|
|
||||||
this->close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputCompletionPopup::updateEmotes(const QString &text, ChannelPtr channel)
|
void InputCompletionPopup::updateEmotes(const QString &text, ChannelPtr channel)
|
||||||
{
|
{
|
||||||
std::vector<_Emote> emotes;
|
std::vector<CompletionEmote> emotes;
|
||||||
auto tc = dynamic_cast<TwitchChannel *>(channel.get());
|
auto *tc = dynamic_cast<TwitchChannel *>(channel.get());
|
||||||
// returns true also for special Twitch channels (/live, /mentions, /whispers, etc.)
|
// returns true also for special Twitch channels (/live, /mentions, /whispers, etc.)
|
||||||
if (channel->isTwitchChannel())
|
if (channel->isTwitchChannel())
|
||||||
{
|
{
|
||||||
@@ -90,7 +89,7 @@ void InputCompletionPopup::updateEmotes(const QString &text, ChannelPtr channel)
|
|||||||
if (tc &&
|
if (tc &&
|
||||||
localEmoteData->find(tc->roomId()) != localEmoteData->end())
|
localEmoteData->find(tc->roomId()) != localEmoteData->end())
|
||||||
{
|
{
|
||||||
if (auto localEmotes = &localEmoteData->at(tc->roomId()))
|
if (const auto *localEmotes = &localEmoteData->at(tc->roomId()))
|
||||||
{
|
{
|
||||||
addEmotes(emotes, *localEmotes, text,
|
addEmotes(emotes, *localEmotes, text,
|
||||||
"Local Twitch Emotes");
|
"Local Twitch Emotes");
|
||||||
@@ -102,9 +101,13 @@ void InputCompletionPopup::updateEmotes(const QString &text, ChannelPtr channel)
|
|||||||
{
|
{
|
||||||
// TODO extract "Channel {BetterTTV,7TV,FrankerFaceZ}" text into a #define.
|
// TODO extract "Channel {BetterTTV,7TV,FrankerFaceZ}" text into a #define.
|
||||||
if (auto bttv = tc->bttvEmotes())
|
if (auto bttv = tc->bttvEmotes())
|
||||||
|
{
|
||||||
addEmotes(emotes, *bttv, text, "Channel BetterTTV");
|
addEmotes(emotes, *bttv, text, "Channel BetterTTV");
|
||||||
|
}
|
||||||
if (auto ffz = tc->ffzEmotes())
|
if (auto ffz = tc->ffzEmotes())
|
||||||
|
{
|
||||||
addEmotes(emotes, *ffz, text, "Channel FrankerFaceZ");
|
addEmotes(emotes, *ffz, text, "Channel FrankerFaceZ");
|
||||||
|
}
|
||||||
if (auto seventv = tc->seventvEmotes())
|
if (auto seventv = tc->seventvEmotes())
|
||||||
{
|
{
|
||||||
addEmotes(emotes, *seventv, text, "Channel 7TV");
|
addEmotes(emotes, *seventv, text, "Channel 7TV");
|
||||||
@@ -112,9 +115,13 @@ void InputCompletionPopup::updateEmotes(const QString &text, ChannelPtr channel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (auto bttvG = getApp()->twitch->getBttvEmotes().emotes())
|
if (auto bttvG = getApp()->twitch->getBttvEmotes().emotes())
|
||||||
|
{
|
||||||
addEmotes(emotes, *bttvG, text, "Global BetterTTV");
|
addEmotes(emotes, *bttvG, text, "Global BetterTTV");
|
||||||
|
}
|
||||||
if (auto ffzG = getApp()->twitch->getFfzEmotes().emotes())
|
if (auto ffzG = getApp()->twitch->getFfzEmotes().emotes())
|
||||||
|
{
|
||||||
addEmotes(emotes, *ffzG, text, "Global FrankerFaceZ");
|
addEmotes(emotes, *ffzG, text, "Global FrankerFaceZ");
|
||||||
|
}
|
||||||
if (auto seventvG = getApp()->twitch->getSeventvEmotes().globalEmotes())
|
if (auto seventvG = getApp()->twitch->getSeventvEmotes().globalEmotes())
|
||||||
{
|
{
|
||||||
addEmotes(emotes, *seventvG, text, "Global 7TV");
|
addEmotes(emotes, *seventvG, text, "Global 7TV");
|
||||||
@@ -148,8 +155,10 @@ void InputCompletionPopup::updateEmotes(const QString &text, ChannelPtr channel)
|
|||||||
emote.emote, emote.displayName + " - " + emote.providerName,
|
emote.emote, emote.displayName + " - " + emote.providerName,
|
||||||
this->callback_));
|
this->callback_));
|
||||||
|
|
||||||
if (count++ == maxEntryCount)
|
if (count++ == MAX_ENTRY_COUNT)
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!emotes.empty())
|
if (!emotes.empty())
|
||||||
@@ -160,30 +169,33 @@ void InputCompletionPopup::updateEmotes(const QString &text, ChannelPtr channel)
|
|||||||
|
|
||||||
void InputCompletionPopup::updateUsers(const QString &text, ChannelPtr channel)
|
void InputCompletionPopup::updateUsers(const QString &text, ChannelPtr channel)
|
||||||
{
|
{
|
||||||
auto twitchChannel = dynamic_cast<TwitchChannel *>(channel.get());
|
auto *tc = dynamic_cast<TwitchChannel *>(channel.get());
|
||||||
if (twitchChannel)
|
if (!tc)
|
||||||
{
|
{
|
||||||
auto chatters = twitchChannel->accessChatters()->filterByPrefix(text);
|
return;
|
||||||
this->model_.clear();
|
}
|
||||||
int count = 0;
|
|
||||||
for (const auto &name : chatters)
|
|
||||||
{
|
|
||||||
this->model_.addItem(std::make_unique<InputCompletionItem>(
|
|
||||||
nullptr, name, this->callback_));
|
|
||||||
|
|
||||||
if (count++ == maxEntryCount)
|
auto chatters = tc->accessChatters()->filterByPrefix(text);
|
||||||
break;
|
this->model_.clear();
|
||||||
}
|
|
||||||
if (!chatters.empty())
|
if (chatters.empty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
for (const auto &name : chatters)
|
||||||
|
{
|
||||||
|
this->model_.addItem(std::make_unique<InputCompletionItem>(
|
||||||
|
nullptr, name, this->callback_));
|
||||||
|
|
||||||
|
if (count++ == MAX_ENTRY_COUNT)
|
||||||
{
|
{
|
||||||
this->ui_.listView->setCurrentIndex(this->model_.index(0));
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool InputCompletionPopup::eventFilter(QObject *watched, QEvent *event)
|
this->ui_.listView->setCurrentIndex(this->model_.index(0));
|
||||||
{
|
|
||||||
return this->ui_.listView->eventFilter(watched, event);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputCompletionPopup::setInputAction(ActionCallback callback)
|
void InputCompletionPopup::setInputAction(ActionCallback callback)
|
||||||
@@ -191,14 +203,34 @@ void InputCompletionPopup::setInputAction(ActionCallback callback)
|
|||||||
this->callback_ = std::move(callback);
|
this->callback_ = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputCompletionPopup::showEvent(QShowEvent *)
|
bool InputCompletionPopup::eventFilter(QObject *watched, QEvent *event)
|
||||||
|
{
|
||||||
|
return this->ui_.listView->eventFilter(watched, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputCompletionPopup::showEvent(QShowEvent * /*event*/)
|
||||||
{
|
{
|
||||||
this->redrawTimer_.start();
|
this->redrawTimer_.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputCompletionPopup::hideEvent(QHideEvent *)
|
void InputCompletionPopup::hideEvent(QHideEvent * /*event*/)
|
||||||
{
|
{
|
||||||
this->redrawTimer_.stop();
|
this->redrawTimer_.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputCompletionPopup::initLayout()
|
||||||
|
{
|
||||||
|
LayoutCreator creator = {this};
|
||||||
|
|
||||||
|
auto listView =
|
||||||
|
creator.emplace<GenericListView>().assign(&this->ui_.listView);
|
||||||
|
listView->setInvokeActionOnTab(true);
|
||||||
|
|
||||||
|
listView->setModel(&this->model_);
|
||||||
|
QObject::connect(listView.getElement(), &GenericListView::closeRequested,
|
||||||
|
this, [this] {
|
||||||
|
this->close();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|||||||
@@ -14,17 +14,18 @@ class InputCompletionPopup : public BasePopup
|
|||||||
{
|
{
|
||||||
using ActionCallback = std::function<void(const QString &)>;
|
using ActionCallback = std::function<void(const QString &)>;
|
||||||
|
|
||||||
constexpr static int maxEntryCount = 200;
|
constexpr static int MAX_ENTRY_COUNT = 200;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InputCompletionPopup(QWidget *parent = nullptr);
|
InputCompletionPopup(QWidget *parent = nullptr);
|
||||||
|
|
||||||
void updateEmotes(const QString &text, ChannelPtr channel);
|
void updateEmotes(const QString &text, ChannelPtr channel);
|
||||||
void updateUsers(const QString &text, ChannelPtr channel);
|
void updateUsers(const QString &text, ChannelPtr channel);
|
||||||
virtual bool eventFilter(QObject *, QEvent *event) override;
|
|
||||||
|
|
||||||
void setInputAction(ActionCallback callback);
|
void setInputAction(ActionCallback callback);
|
||||||
|
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void showEvent(QShowEvent *event) override;
|
void showEvent(QShowEvent *event) override;
|
||||||
void hideEvent(QHideEvent *event) override;
|
void hideEvent(QHideEvent *event) override;
|
||||||
|
|||||||
@@ -625,7 +625,7 @@ void SplitInput::installKeyPressedEvent()
|
|||||||
{
|
{
|
||||||
this->ui_.textEdit->keyPressed.disconnectAll();
|
this->ui_.textEdit->keyPressed.disconnectAll();
|
||||||
this->ui_.textEdit->keyPressed.connect([this](QKeyEvent *event) {
|
this->ui_.textEdit->keyPressed.connect([this](QKeyEvent *event) {
|
||||||
if (auto popup = this->inputCompletionPopup_.get())
|
if (auto *popup = this->inputCompletionPopup_.get())
|
||||||
{
|
{
|
||||||
if (popup->isVisible())
|
if (popup->isVisible())
|
||||||
{
|
{
|
||||||
@@ -674,11 +674,11 @@ void SplitInput::onCursorPositionChanged()
|
|||||||
|
|
||||||
void SplitInput::updateCompletionPopup()
|
void SplitInput::updateCompletionPopup()
|
||||||
{
|
{
|
||||||
auto channel = this->split_->getChannel().get();
|
auto *channel = this->split_->getChannel().get();
|
||||||
auto tc = dynamic_cast<TwitchChannel *>(channel);
|
auto *tc = dynamic_cast<TwitchChannel *>(channel);
|
||||||
bool showEmoteCompletion = getSettings()->emoteCompletionWithColon;
|
bool showEmoteCompletion = getSettings()->emoteCompletionWithColon;
|
||||||
bool showUsernameCompletion =
|
bool showUsernameCompletion =
|
||||||
tc && getSettings()->showUsernameCompletionMenu;
|
tc != nullptr && getSettings()->showUsernameCompletionMenu;
|
||||||
if (!showEmoteCompletion && !showUsernameCompletion)
|
if (!showEmoteCompletion && !showUsernameCompletion)
|
||||||
{
|
{
|
||||||
this->hideCompletionPopup();
|
this->hideCompletionPopup();
|
||||||
@@ -704,22 +704,32 @@ void SplitInput::updateCompletionPopup()
|
|||||||
this->hideCompletionPopup();
|
this->hideCompletionPopup();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (text[i] == ':' && showEmoteCompletion)
|
|
||||||
|
if (text[i] == ':' && showEmoteCompletion)
|
||||||
{
|
{
|
||||||
if (i == 0 || text[i - 1].isSpace())
|
if (i == 0 || text[i - 1].isSpace())
|
||||||
|
{
|
||||||
this->showCompletionPopup(text.mid(i, position - i + 1).mid(1),
|
this->showCompletionPopup(text.mid(i, position - i + 1).mid(1),
|
||||||
true);
|
true);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
this->hideCompletionPopup();
|
this->hideCompletionPopup();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (text[i] == '@' && showUsernameCompletion)
|
|
||||||
|
if (text[i] == '@' && showUsernameCompletion)
|
||||||
{
|
{
|
||||||
if (i == 0 || text[i - 1].isSpace())
|
if (i == 0 || text[i - 1].isSpace())
|
||||||
|
{
|
||||||
this->showCompletionPopup(text.mid(i, position - i + 1).mid(1),
|
this->showCompletionPopup(text.mid(i, position - i + 1).mid(1),
|
||||||
false);
|
false);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
this->hideCompletionPopup();
|
this->hideCompletionPopup();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -734,7 +744,7 @@ void SplitInput::showCompletionPopup(const QString &text, bool emoteCompletion)
|
|||||||
this->inputCompletionPopup_ = new InputCompletionPopup(this);
|
this->inputCompletionPopup_ = new InputCompletionPopup(this);
|
||||||
this->inputCompletionPopup_->setInputAction(
|
this->inputCompletionPopup_->setInputAction(
|
||||||
[that = QObjectRef(this)](const QString &text) mutable {
|
[that = QObjectRef(this)](const QString &text) mutable {
|
||||||
if (auto this2 = that.get())
|
if (auto *this2 = that.get())
|
||||||
{
|
{
|
||||||
this2->insertCompletionText(text);
|
this2->insertCompletionText(text);
|
||||||
this2->hideCompletionPopup();
|
this2->hideCompletionPopup();
|
||||||
@@ -742,13 +752,17 @@ void SplitInput::showCompletionPopup(const QString &text, bool emoteCompletion)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
auto popup = this->inputCompletionPopup_.get();
|
auto *popup = this->inputCompletionPopup_.get();
|
||||||
assert(popup);
|
assert(popup);
|
||||||
|
|
||||||
if (emoteCompletion) // autocomplete emotes
|
if (emoteCompletion)
|
||||||
|
{
|
||||||
popup->updateEmotes(text, this->split_->getChannel());
|
popup->updateEmotes(text, this->split_->getChannel());
|
||||||
else // autocomplete usernames
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
popup->updateUsers(text, this->split_->getChannel());
|
popup->updateUsers(text, this->split_->getChannel());
|
||||||
|
}
|
||||||
|
|
||||||
auto pos = this->mapToGlobal({0, 0}) - QPoint(0, popup->height()) +
|
auto pos = this->mapToGlobal({0, 0}) - QPoint(0, popup->height()) +
|
||||||
QPoint((this->width() - popup->width()) / 2, 0);
|
QPoint((this->width() - popup->width()) / 2, 0);
|
||||||
@@ -759,11 +773,13 @@ void SplitInput::showCompletionPopup(const QString &text, bool emoteCompletion)
|
|||||||
|
|
||||||
void SplitInput::hideCompletionPopup()
|
void SplitInput::hideCompletionPopup()
|
||||||
{
|
{
|
||||||
if (auto popup = this->inputCompletionPopup_.get())
|
if (auto *popup = this->inputCompletionPopup_.get())
|
||||||
|
{
|
||||||
popup->hide();
|
popup->hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SplitInput::insertCompletionText(const QString &input_)
|
void SplitInput::insertCompletionText(const QString &input_) const
|
||||||
{
|
{
|
||||||
auto &edit = *this->ui_.textEdit;
|
auto &edit = *this->ui_.textEdit;
|
||||||
auto input = input_ + ' ';
|
auto input = input_ + ' ';
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ protected:
|
|||||||
void updateCompletionPopup();
|
void updateCompletionPopup();
|
||||||
void showCompletionPopup(const QString &text, bool emoteCompletion);
|
void showCompletionPopup(const QString &text, bool emoteCompletion);
|
||||||
void hideCompletionPopup();
|
void hideCompletionPopup();
|
||||||
void insertCompletionText(const QString &text);
|
void insertCompletionText(const QString &input_) const;
|
||||||
void openEmotePopup();
|
void openEmotePopup();
|
||||||
|
|
||||||
void updateCancelReplyButton();
|
void updateCancelReplyButton();
|
||||||
|
|||||||
Reference in New Issue
Block a user