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:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user