Add emote completion widget filter for only zero-width emotes using :~ (#6362)

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
droidicus
2025-08-10 05:21:52 -04:00
committed by GitHub
parent f77d3c59bc
commit 1c9090d350
5 changed files with 60 additions and 5 deletions
+1
View File
@@ -23,6 +23,7 @@
- Minor: Message character length label is now monospaced. (#6366)
- Minor: Add a setting under Moderation -> Logs to customize the timestamp used for chat logs. (#6338)
- Minor: Add a setting under Moderation -> Logs to use server timestamp from the message instead of the local clock time for logging. (#6346)
- Minor: Add feature to search for only zero-width emotes when prepended by `:~`. (#6362)
- Bugfix: Commands are no longer tab-completable in the middle of messages. (#6273)
- Bugfix: Automatic streamer mode detection now works from Flatpak. (#6250)
- Bugfix: Don't create native messaging manifest file if browser directory doesn't exist. (#6116)
+1
View File
@@ -13,6 +13,7 @@ Q_LOGGING_CATEGORY(chatterinoBttv, "chatterino.bttv", logThreshold);
Q_LOGGING_CATEGORY(chatterinoCache, "chatterino.cache", logThreshold);
Q_LOGGING_CATEGORY(chatterinoCommands, "chatterino.commands", logThreshold);
Q_LOGGING_CATEGORY(chatterinoCommon, "chatterino.common", logThreshold);
Q_LOGGING_CATEGORY(chatterinoCompletion, "chatterino.completion", logThreshold);
Q_LOGGING_CATEGORY(chatterinoCrashhandler, "chatterino.crashhandler",
logThreshold);
Q_LOGGING_CATEGORY(chatterinoEmoji, "chatterino.emoji", logThreshold);
+1
View File
@@ -9,6 +9,7 @@ Q_DECLARE_LOGGING_CATEGORY(chatterinoBttv);
Q_DECLARE_LOGGING_CATEGORY(chatterinoCache);
Q_DECLARE_LOGGING_CATEGORY(chatterinoCommands);
Q_DECLARE_LOGGING_CATEGORY(chatterinoCommon);
Q_DECLARE_LOGGING_CATEGORY(chatterinoCompletion);
Q_DECLARE_LOGGING_CATEGORY(chatterinoCrashhandler);
Q_DECLARE_LOGGING_CATEGORY(chatterinoEmoji);
Q_DECLARE_LOGGING_CATEGORY(chatterinoEnv);
@@ -1,23 +1,45 @@
#include "controllers/completion/strategies/ClassicEmoteStrategy.hpp"
#include "common/QLogging.hpp"
#include "singletons/Settings.hpp"
#include "util/Helpers.hpp"
#include <algorithm>
namespace chatterino::completion {
namespace {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
const auto &LOG = chatterinoCompletion;
} // namespace
void ClassicEmoteStrategy::apply(const std::vector<EmoteItem> &items,
std::vector<EmoteItem> &output,
const QString &query) const
{
qCDebug(LOG) << "ClassicEmoteStrategy apply" << query;
QString normalizedQuery = query;
bool zeroWidthOnly = false;
if (normalizedQuery.startsWith(':'))
{
normalizedQuery = normalizedQuery.mid(1);
}
if (normalizedQuery.startsWith('~'))
{
normalizedQuery = normalizedQuery.mid(1);
zeroWidthOnly = true;
}
// First pass: filter by contains match
// First pass: filter by zero-width only and contains match
for (const auto &item : items)
{
if (zeroWidthOnly && !item.emote->zeroWidth)
{
continue;
}
if (item.searchName.contains(normalizedQuery, Qt::CaseInsensitive))
{
output.push_back(item);
@@ -52,6 +74,7 @@ void ClassicTabEmoteStrategy::apply(const std::vector<EmoteItem> &items,
std::vector<EmoteItem> &output,
const QString &query) const
{
qCDebug(LOG) << "ClassicTabEmoteStrategy apply" << query;
bool colonStart = query.startsWith(':');
QStringView normalizedQuery = query;
if (colonStart)
@@ -11,6 +11,10 @@
namespace chatterino::completion {
namespace {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
const auto &LOG = chatterinoCompletion;
/**
* @brief This function calculates the "cost" of the changes that need to
* be done to the query to make it the value.
@@ -67,7 +71,7 @@ int costOfEmote(QStringView query, QStringView emote, bool prioritizeUpper)
// matchingFunction is used for testing if the emote should be included in the search.
void completeEmotes(
const std::vector<EmoteItem> &items, std::vector<EmoteItem> &output,
QStringView query, bool ignoreColonForCost,
QStringView query, bool ignoreColonForCost, bool ignoreTildeForCost,
const std::function<bool(EmoteItem, Qt::CaseSensitivity)> &matchingFunction)
{
// Given these emotes: pajaW, PAJAW
@@ -128,7 +132,7 @@ void completeEmotes(
}
std::sort(output.begin(), output.end(),
[query, prioritizeUpper, ignoreColonForCost](
[query, prioritizeUpper, ignoreColonForCost, ignoreTildeForCost](
const EmoteItem &a, const EmoteItem &b) -> bool {
auto tempA = a.searchName;
auto tempB = b.searchName;
@@ -136,10 +140,18 @@ void completeEmotes(
{
tempA = tempA.mid(1);
}
if (ignoreTildeForCost && tempA.startsWith("~"))
{
tempA = tempA.mid(1);
}
if (ignoreColonForCost && tempB.startsWith(":"))
{
tempB = tempB.mid(1);
}
if (ignoreTildeForCost && tempB.startsWith("~"))
{
tempB = tempB.mid(1);
}
auto costA = costOfEmote(query, tempA, prioritizeUpper);
auto costB = costOfEmote(query, tempB, prioritizeUpper);
@@ -159,14 +171,30 @@ void SmartEmoteStrategy::apply(const std::vector<EmoteItem> &items,
std::vector<EmoteItem> &output,
const QString &query) const
{
qCDebug(LOG) << "SmartEmoteStrategy apply" << query;
std::vector<EmoteItem> filteredItems = items;
QString normalizedQuery = query;
bool ignoreColonForCost = false;
bool zeroWidthOnly = false;
if (normalizedQuery.startsWith(':'))
{
normalizedQuery = normalizedQuery.mid(1);
ignoreColonForCost = true;
}
completeEmotes(items, output, normalizedQuery, ignoreColonForCost,
if (normalizedQuery.startsWith('~'))
{
normalizedQuery = normalizedQuery.mid(1);
zeroWidthOnly = true;
auto [first, last] = std::ranges::remove_if(
filteredItems, [](const EmoteItem &emoteItem) {
return !emoteItem.emote->zeroWidth;
});
filteredItems.erase(first, last);
}
completeEmotes(filteredItems, output, normalizedQuery, ignoreColonForCost,
zeroWidthOnly,
[normalizedQuery](const EmoteItem &left,
Qt::CaseSensitivity caseHandling) {
return left.searchName.contains(normalizedQuery,
@@ -178,6 +206,7 @@ void SmartTabEmoteStrategy::apply(const std::vector<EmoteItem> &items,
std::vector<EmoteItem> &output,
const QString &query) const
{
qCDebug(LOG) << "SmartTabEmoteStrategy apply" << query;
bool colonStart = query.startsWith(':');
QStringView normalizedQuery = query;
if (colonStart)
@@ -187,7 +216,7 @@ void SmartTabEmoteStrategy::apply(const std::vector<EmoteItem> &items,
}
completeEmotes(
items, output, normalizedQuery, false,
items, output, normalizedQuery, false, false,
[&](const EmoteItem &item, Qt::CaseSensitivity caseHandling) -> bool {
QStringView itemQuery;
if (item.isEmoji)