fix: don't indent inner namespaces (#6235)
This commit is contained in:
+1
-1
@@ -45,7 +45,7 @@ IncludeCategories:
|
|||||||
# Third party library includes
|
# Third party library includes
|
||||||
- Regex: "^<([a-zA-Z_0-9-]+/)*[a-zA-Z_0-9-]+.h(pp)?>$"
|
- Regex: "^<([a-zA-Z_0-9-]+/)*[a-zA-Z_0-9-]+.h(pp)?>$"
|
||||||
Priority: 3
|
Priority: 3
|
||||||
NamespaceIndentation: Inner
|
NamespaceIndentation: None
|
||||||
PointerBindsToType: false
|
PointerBindsToType: false
|
||||||
SpacesBeforeTrailingComments: 2
|
SpacesBeforeTrailingComments: 2
|
||||||
Standard: Auto
|
Standard: Auto
|
||||||
|
|||||||
+2
-2
@@ -54,10 +54,10 @@ class IStreamerMode;
|
|||||||
class ITwitchUsers;
|
class ITwitchUsers;
|
||||||
class NativeMessagingServer;
|
class NativeMessagingServer;
|
||||||
namespace pronouns {
|
namespace pronouns {
|
||||||
class Pronouns;
|
class Pronouns;
|
||||||
} // namespace pronouns
|
} // namespace pronouns
|
||||||
namespace eventsub {
|
namespace eventsub {
|
||||||
class IController;
|
class IController;
|
||||||
} // namespace eventsub
|
} // namespace eventsub
|
||||||
|
|
||||||
class IApplication
|
class IApplication
|
||||||
|
|||||||
+157
-162
@@ -40,209 +40,204 @@
|
|||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace {
|
namespace {
|
||||||
void installCustomPalette()
|
void installCustomPalette()
|
||||||
{
|
{
|
||||||
// borrowed from
|
// borrowed from
|
||||||
// https://stackoverflow.com/questions/15035767/is-the-qt-5-dark-fusion-theme-available-for-windows
|
// https://stackoverflow.com/questions/15035767/is-the-qt-5-dark-fusion-theme-available-for-windows
|
||||||
auto dark = QApplication::palette();
|
auto dark = QApplication::palette();
|
||||||
|
|
||||||
dark.setColor(QPalette::Window, QColor(22, 22, 22));
|
dark.setColor(QPalette::Window, QColor(22, 22, 22));
|
||||||
dark.setColor(QPalette::WindowText, Qt::white);
|
dark.setColor(QPalette::WindowText, Qt::white);
|
||||||
dark.setColor(QPalette::Text, Qt::white);
|
dark.setColor(QPalette::Text, Qt::white);
|
||||||
dark.setColor(QPalette::Base, QColor("#333"));
|
dark.setColor(QPalette::Base, QColor("#333"));
|
||||||
dark.setColor(QPalette::AlternateBase, QColor("#444"));
|
dark.setColor(QPalette::AlternateBase, QColor("#444"));
|
||||||
dark.setColor(QPalette::ToolTipBase, Qt::white);
|
dark.setColor(QPalette::ToolTipBase, Qt::white);
|
||||||
dark.setColor(QPalette::ToolTipText, Qt::black);
|
dark.setColor(QPalette::ToolTipText, Qt::black);
|
||||||
dark.setColor(QPalette::Dark, QColor(35, 35, 35));
|
dark.setColor(QPalette::Dark, QColor(35, 35, 35));
|
||||||
dark.setColor(QPalette::Shadow, QColor(20, 20, 20));
|
dark.setColor(QPalette::Shadow, QColor(20, 20, 20));
|
||||||
dark.setColor(QPalette::Button, QColor(70, 70, 70));
|
dark.setColor(QPalette::Button, QColor(70, 70, 70));
|
||||||
dark.setColor(QPalette::ButtonText, Qt::white);
|
dark.setColor(QPalette::ButtonText, Qt::white);
|
||||||
dark.setColor(QPalette::BrightText, Qt::red);
|
dark.setColor(QPalette::BrightText, Qt::red);
|
||||||
dark.setColor(QPalette::Link, QColor(42, 130, 218));
|
dark.setColor(QPalette::Link, QColor(42, 130, 218));
|
||||||
dark.setColor(QPalette::Highlight, QColor(42, 130, 218));
|
dark.setColor(QPalette::Highlight, QColor(42, 130, 218));
|
||||||
dark.setColor(QPalette::HighlightedText, Qt::white);
|
dark.setColor(QPalette::HighlightedText, Qt::white);
|
||||||
dark.setColor(QPalette::PlaceholderText, QColor(127, 127, 127));
|
dark.setColor(QPalette::PlaceholderText, QColor(127, 127, 127));
|
||||||
|
|
||||||
dark.setColor(QPalette::Disabled, QPalette::Highlight,
|
dark.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80));
|
||||||
QColor(80, 80, 80));
|
dark.setColor(QPalette::Disabled, QPalette::HighlightedText,
|
||||||
dark.setColor(QPalette::Disabled, QPalette::HighlightedText,
|
QColor(127, 127, 127));
|
||||||
QColor(127, 127, 127));
|
dark.setColor(QPalette::Disabled, QPalette::ButtonText,
|
||||||
dark.setColor(QPalette::Disabled, QPalette::ButtonText,
|
QColor(127, 127, 127));
|
||||||
QColor(127, 127, 127));
|
dark.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127));
|
||||||
dark.setColor(QPalette::Disabled, QPalette::Text,
|
dark.setColor(QPalette::Disabled, QPalette::WindowText,
|
||||||
QColor(127, 127, 127));
|
QColor(127, 127, 127));
|
||||||
dark.setColor(QPalette::Disabled, QPalette::WindowText,
|
|
||||||
QColor(127, 127, 127));
|
|
||||||
|
|
||||||
QApplication::setPalette(dark);
|
QApplication::setPalette(dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
void initQt()
|
void initQt()
|
||||||
{
|
{
|
||||||
// set up the QApplication flags
|
// set up the QApplication flags
|
||||||
QApplication::setAttribute(Qt::AA_Use96Dpi, true);
|
QApplication::setAttribute(Qt::AA_Use96Dpi, true);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN32
|
#ifdef Q_OS_WIN32
|
||||||
// Avoid promoting child widgets to child windows
|
// Avoid promoting child widgets to child windows
|
||||||
// This causes bugs with frameless windows as not all child events
|
// This causes bugs with frameless windows as not all child events
|
||||||
// get sent to the parent - effectively making the window immovable.
|
// get sent to the parent - effectively making the window immovable.
|
||||||
QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QApplication::setStyle(QStyleFactory::create("Fusion"));
|
QApplication::setStyle(QStyleFactory::create("Fusion"));
|
||||||
|
|
||||||
#ifndef Q_OS_MAC
|
#ifndef Q_OS_MAC
|
||||||
QApplication::setWindowIcon(QIcon(":/icon.ico"));
|
QApplication::setWindowIcon(QIcon(":/icon.ico"));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
// On the Mac/Cocoa platform this attribute is enabled by default
|
// On the Mac/Cocoa platform this attribute is enabled by default
|
||||||
// We override it to ensure shortcuts show in context menus on that platform
|
// We override it to ensure shortcuts show in context menus on that platform
|
||||||
QApplication::setAttribute(Qt::AA_DontShowShortcutsInContextMenus,
|
QApplication::setAttribute(Qt::AA_DontShowShortcutsInContextMenus, false);
|
||||||
false);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
installCustomPalette();
|
installCustomPalette();
|
||||||
}
|
}
|
||||||
|
|
||||||
void showLastCrashDialog(const Args &args, const Paths &paths)
|
void showLastCrashDialog(const Args &args, const Paths &paths)
|
||||||
|
{
|
||||||
|
auto *dialog = new LastRunCrashDialog(args, paths);
|
||||||
|
// Use exec() over open() to block the app from being loaded
|
||||||
|
// and to be able to set the safe mode.
|
||||||
|
dialog->exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
void createRunningFile(const QString &path)
|
||||||
|
{
|
||||||
|
QFile runningFile(path);
|
||||||
|
|
||||||
|
runningFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
||||||
|
runningFile.flush();
|
||||||
|
runningFile.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeRunningFile(const QString &path)
|
||||||
|
{
|
||||||
|
QFile::remove(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::chrono::steady_clock::time_point signalsInitTime;
|
||||||
|
|
||||||
|
[[noreturn]] void handleSignal(int signum)
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
if (std::chrono::steady_clock::now() - signalsInitTime > 30s &&
|
||||||
|
getApp()->getCrashHandler()->shouldRecover())
|
||||||
{
|
{
|
||||||
auto *dialog = new LastRunCrashDialog(args, paths);
|
QProcess proc;
|
||||||
// Use exec() over open() to block the app from being loaded
|
|
||||||
// and to be able to set the safe mode.
|
|
||||||
dialog->exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
void createRunningFile(const QString &path)
|
|
||||||
{
|
|
||||||
QFile runningFile(path);
|
|
||||||
|
|
||||||
runningFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
|
|
||||||
runningFile.flush();
|
|
||||||
runningFile.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeRunningFile(const QString &path)
|
|
||||||
{
|
|
||||||
QFile::remove(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::chrono::steady_clock::time_point signalsInitTime;
|
|
||||||
|
|
||||||
[[noreturn]] void handleSignal(int signum)
|
|
||||||
{
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
|
|
||||||
if (std::chrono::steady_clock::now() - signalsInitTime > 30s &&
|
|
||||||
getApp()->getCrashHandler()->shouldRecover())
|
|
||||||
{
|
|
||||||
QProcess proc;
|
|
||||||
|
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
// On macOS, programs are bundled into ".app" Application bundles,
|
// On macOS, programs are bundled into ".app" Application bundles,
|
||||||
// when restarting Chatterino that bundle should be opened with the "open"
|
// when restarting Chatterino that bundle should be opened with the "open"
|
||||||
// terminal command instead of directly starting the underlying executable,
|
// terminal command instead of directly starting the underlying executable,
|
||||||
// as those are 2 different things for the OS and i.e. do not use
|
// as those are 2 different things for the OS and i.e. do not use
|
||||||
// the same dock icon (resulting in a second Chatterino icon on restarting)
|
// the same dock icon (resulting in a second Chatterino icon on restarting)
|
||||||
CFURLRef appUrlRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
CFURLRef appUrlRef = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
||||||
CFStringRef macPath =
|
CFStringRef macPath =
|
||||||
CFURLCopyFileSystemPath(appUrlRef, kCFURLPOSIXPathStyle);
|
CFURLCopyFileSystemPath(appUrlRef, kCFURLPOSIXPathStyle);
|
||||||
const char *pathPtr =
|
const char *pathPtr =
|
||||||
CFStringGetCStringPtr(macPath, CFStringGetSystemEncoding());
|
CFStringGetCStringPtr(macPath, CFStringGetSystemEncoding());
|
||||||
|
|
||||||
proc.setProgram("open");
|
proc.setProgram("open");
|
||||||
proc.setArguments({pathPtr, "-n", "--args", "--crash-recovery"});
|
proc.setArguments({pathPtr, "-n", "--args", "--crash-recovery"});
|
||||||
|
|
||||||
CFRelease(appUrlRef);
|
CFRelease(appUrlRef);
|
||||||
CFRelease(macPath);
|
CFRelease(macPath);
|
||||||
#else
|
#else
|
||||||
proc.setProgram(QApplication::applicationFilePath());
|
proc.setProgram(QApplication::applicationFilePath());
|
||||||
proc.setArguments({"--crash-recovery"});
|
proc.setArguments({"--crash-recovery"});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
proc.startDetached();
|
proc.startDetached();
|
||||||
}
|
|
||||||
|
|
||||||
std::_Exit(signum);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We want to restart Chatterino when it crashes and the setting is set to
|
std::_Exit(signum);
|
||||||
// true.
|
}
|
||||||
void initSignalHandler()
|
|
||||||
{
|
|
||||||
#if defined(NDEBUG) && !defined(CHATTERINO_WITH_CRASHPAD)
|
|
||||||
signalsInitTime = std::chrono::steady_clock::now();
|
|
||||||
|
|
||||||
signal(SIGSEGV, handleSignal);
|
// We want to restart Chatterino when it crashes and the setting is set to
|
||||||
|
// true.
|
||||||
|
void initSignalHandler()
|
||||||
|
{
|
||||||
|
#if defined(NDEBUG) && !defined(CHATTERINO_WITH_CRASHPAD)
|
||||||
|
signalsInitTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
signal(SIGSEGV, handleSignal);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX)
|
#if defined(Q_OS_UNIX)
|
||||||
auto *sigintHandler = new UnixSignalHandler(SIGINT);
|
auto *sigintHandler = new UnixSignalHandler(SIGINT);
|
||||||
QObject::connect(sigintHandler, &UnixSignalHandler::signalFired, [] {
|
QObject::connect(sigintHandler, &UnixSignalHandler::signalFired, [] {
|
||||||
qCInfo(chatterinoApp)
|
qCInfo(chatterinoApp) << "Received SIGINT, request application quit";
|
||||||
<< "Received SIGINT, request application quit";
|
QApplication::quit();
|
||||||
QApplication::quit();
|
});
|
||||||
});
|
auto *sigtermHandler = new UnixSignalHandler(SIGTERM);
|
||||||
auto *sigtermHandler = new UnixSignalHandler(SIGTERM);
|
QObject::connect(sigtermHandler, &UnixSignalHandler::signalFired, [] {
|
||||||
QObject::connect(sigtermHandler, &UnixSignalHandler::signalFired, [] {
|
qCInfo(chatterinoApp) << "Received SIGTERM, request application quit";
|
||||||
qCInfo(chatterinoApp)
|
QApplication::quit();
|
||||||
<< "Received SIGTERM, request application quit";
|
});
|
||||||
QApplication::quit();
|
|
||||||
});
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// We delete cache files that haven't been modified in 14 days. This strategy may be
|
// We delete cache files that haven't been modified in 14 days. This strategy may be
|
||||||
// improved in the future.
|
// improved in the future.
|
||||||
void clearCache(const QDir &dir)
|
void clearCache(const QDir &dir)
|
||||||
|
{
|
||||||
|
size_t deletedCount = 0;
|
||||||
|
for (const auto &info : dir.entryInfoList(QDir::Files))
|
||||||
{
|
{
|
||||||
size_t deletedCount = 0;
|
if (info.lastModified().addDays(14) < QDateTime::currentDateTime())
|
||||||
for (const auto &info : dir.entryInfoList(QDir::Files))
|
|
||||||
{
|
{
|
||||||
if (info.lastModified().addDays(14) < QDateTime::currentDateTime())
|
bool res = QFile(info.absoluteFilePath()).remove();
|
||||||
|
if (res)
|
||||||
{
|
{
|
||||||
bool res = QFile(info.absoluteFilePath()).remove();
|
++deletedCount;
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
++deletedCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qCDebug(chatterinoCache)
|
|
||||||
<< "Deleted" << deletedCount << "files in" << dir.path();
|
|
||||||
}
|
}
|
||||||
|
qCDebug(chatterinoCache)
|
||||||
|
<< "Deleted" << deletedCount << "files in" << dir.path();
|
||||||
|
}
|
||||||
|
|
||||||
// We delete all but the five most recent crashdumps. This strategy may be
|
// We delete all but the five most recent crashdumps. This strategy may be
|
||||||
// improved in the future.
|
// improved in the future.
|
||||||
void clearCrashes(QDir dir)
|
void clearCrashes(QDir dir)
|
||||||
|
{
|
||||||
|
// crashpad crashdumps are stored inside the Crashes/report directory
|
||||||
|
if (!dir.cd("reports"))
|
||||||
{
|
{
|
||||||
// crashpad crashdumps are stored inside the Crashes/report directory
|
// no reports directory exists = no files to delete
|
||||||
if (!dir.cd("reports"))
|
return;
|
||||||
{
|
|
||||||
// no reports directory exists = no files to delete
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dir.setNameFilters({"*.dmp"});
|
|
||||||
|
|
||||||
size_t deletedCount = 0;
|
|
||||||
// TODO: use std::views::drop once supported by all compilers
|
|
||||||
size_t filesToSkip = 5;
|
|
||||||
for (auto &&info : dir.entryInfoList(QDir::Files, QDir::Time))
|
|
||||||
{
|
|
||||||
if (filesToSkip > 0)
|
|
||||||
{
|
|
||||||
filesToSkip--;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (QFile(info.absoluteFilePath()).remove())
|
|
||||||
{
|
|
||||||
deletedCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
qCDebug(chatterinoApp) << "Deleted" << deletedCount << "crashdumps";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dir.setNameFilters({"*.dmp"});
|
||||||
|
|
||||||
|
size_t deletedCount = 0;
|
||||||
|
// TODO: use std::views::drop once supported by all compilers
|
||||||
|
size_t filesToSkip = 5;
|
||||||
|
for (auto &&info : dir.entryInfoList(QDir::Files, QDir::Time))
|
||||||
|
{
|
||||||
|
if (filesToSkip > 0)
|
||||||
|
{
|
||||||
|
filesToSkip--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (QFile(info.absoluteFilePath()).remove())
|
||||||
|
{
|
||||||
|
deletedCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qCDebug(chatterinoApp) << "Deleted" << deletedCount << "crashdumps";
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void runGui(QApplication &a, const Paths &paths, Settings &settings,
|
void runGui(QApplication &a, const Paths &paths, Settings &settings,
|
||||||
|
|||||||
+43
-43
@@ -10,61 +10,61 @@ namespace chatterino {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void warn(const char *envName, const QString &envString, T defaultValue)
|
void warn(const char *envName, const QString &envString, T defaultValue)
|
||||||
{
|
{
|
||||||
const auto typeName = QString::fromStdString(
|
const auto typeName = QString::fromStdString(
|
||||||
std::string(type_name<decltype(defaultValue)>()));
|
std::string(type_name<decltype(defaultValue)>()));
|
||||||
|
|
||||||
qCWarning(chatterinoEnv).noquote()
|
qCWarning(chatterinoEnv).noquote()
|
||||||
<< QStringLiteral(
|
<< QStringLiteral(
|
||||||
"Cannot parse value '%1' of environment variable '%2' "
|
"Cannot parse value '%1' of environment variable '%2' "
|
||||||
"as a %3, reverting to default value '%4'")
|
"as a %3, reverting to default value '%4'")
|
||||||
.arg(envString)
|
.arg(envString)
|
||||||
.arg(envName)
|
.arg(envName)
|
||||||
.arg(typeName)
|
.arg(typeName)
|
||||||
.arg(defaultValue);
|
.arg(defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<QString> readOptionalStringEnv(const char *envName)
|
||||||
|
{
|
||||||
|
auto envString = qEnvironmentVariable(envName);
|
||||||
|
if (!envString.isEmpty())
|
||||||
|
{
|
||||||
|
return envString;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<QString> readOptionalStringEnv(const char *envName)
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t readPortEnv(const char *envName, uint16_t defaultValue)
|
||||||
|
{
|
||||||
|
auto envString = qEnvironmentVariable(envName);
|
||||||
|
if (!envString.isEmpty())
|
||||||
{
|
{
|
||||||
auto envString = qEnvironmentVariable(envName);
|
bool ok = false;
|
||||||
if (!envString.isEmpty())
|
auto val = envString.toUShort(&ok);
|
||||||
|
if (ok)
|
||||||
{
|
{
|
||||||
return envString;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
warn(envName, envString, defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t readPortEnv(const char *envName, uint16_t defaultValue)
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool readBoolEnv(const char *envName, bool defaultValue)
|
||||||
|
{
|
||||||
|
auto envString = qEnvironmentVariable(envName);
|
||||||
|
if (!envString.isEmpty())
|
||||||
{
|
{
|
||||||
auto envString = qEnvironmentVariable(envName);
|
return QVariant(envString).toBool();
|
||||||
if (!envString.isEmpty())
|
|
||||||
{
|
|
||||||
bool ok = false;
|
|
||||||
auto val = envString.toUShort(&ok);
|
|
||||||
if (ok)
|
|
||||||
{
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
warn(envName, envString, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool readBoolEnv(const char *envName, bool defaultValue)
|
return defaultValue;
|
||||||
{
|
}
|
||||||
auto envString = qEnvironmentVariable(envName);
|
|
||||||
if (!envString.isEmpty())
|
|
||||||
{
|
|
||||||
return QVariant(envString).toBool();
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -11,85 +11,85 @@ namespace chatterino {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
QJsonArray loadWindowArray(const QString &settingsPath)
|
QJsonArray loadWindowArray(const QString &settingsPath)
|
||||||
|
{
|
||||||
|
QFile file(settingsPath);
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
QByteArray data = file.readAll();
|
||||||
|
QJsonDocument document = QJsonDocument::fromJson(data);
|
||||||
|
QJsonArray windows_arr = document.object().value("windows").toArray();
|
||||||
|
return windows_arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T loadNodes(const QJsonObject &obj)
|
||||||
|
{
|
||||||
|
static_assert("loadNodes must be called with the SplitNodeDescriptor "
|
||||||
|
"or ContainerNodeDescriptor type");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
SplitNodeDescriptor loadNodes(const QJsonObject &root)
|
||||||
|
{
|
||||||
|
SplitNodeDescriptor descriptor;
|
||||||
|
|
||||||
|
descriptor.flexH_ = root.value("flexh").toDouble(1.0);
|
||||||
|
descriptor.flexV_ = root.value("flexv").toDouble(1.0);
|
||||||
|
|
||||||
|
auto data = root.value("data").toObject();
|
||||||
|
|
||||||
|
SplitDescriptor::loadFromJSON(descriptor, root, data);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
ContainerNodeDescriptor loadNodes(const QJsonObject &root)
|
||||||
|
{
|
||||||
|
ContainerNodeDescriptor descriptor;
|
||||||
|
|
||||||
|
descriptor.flexH_ = root.value("flexh").toDouble(1.0);
|
||||||
|
descriptor.flexV_ = root.value("flexv").toDouble(1.0);
|
||||||
|
|
||||||
|
descriptor.vertical_ = root.value("type").toString() == "vertical";
|
||||||
|
|
||||||
|
for (QJsonValue _val : root.value("items").toArray())
|
||||||
{
|
{
|
||||||
QFile file(settingsPath);
|
auto _obj = _val.toObject();
|
||||||
file.open(QIODevice::ReadOnly);
|
|
||||||
QByteArray data = file.readAll();
|
|
||||||
QJsonDocument document = QJsonDocument::fromJson(data);
|
|
||||||
QJsonArray windows_arr = document.object().value("windows").toArray();
|
|
||||||
return windows_arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
auto _type = _obj.value("type");
|
||||||
T loadNodes(const QJsonObject &obj)
|
if (_type == "split")
|
||||||
{
|
|
||||||
static_assert("loadNodes must be called with the SplitNodeDescriptor "
|
|
||||||
"or ContainerNodeDescriptor type");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
SplitNodeDescriptor loadNodes(const QJsonObject &root)
|
|
||||||
{
|
|
||||||
SplitNodeDescriptor descriptor;
|
|
||||||
|
|
||||||
descriptor.flexH_ = root.value("flexh").toDouble(1.0);
|
|
||||||
descriptor.flexV_ = root.value("flexv").toDouble(1.0);
|
|
||||||
|
|
||||||
auto data = root.value("data").toObject();
|
|
||||||
|
|
||||||
SplitDescriptor::loadFromJSON(descriptor, root, data);
|
|
||||||
|
|
||||||
return descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <>
|
|
||||||
ContainerNodeDescriptor loadNodes(const QJsonObject &root)
|
|
||||||
{
|
|
||||||
ContainerNodeDescriptor descriptor;
|
|
||||||
|
|
||||||
descriptor.flexH_ = root.value("flexh").toDouble(1.0);
|
|
||||||
descriptor.flexV_ = root.value("flexv").toDouble(1.0);
|
|
||||||
|
|
||||||
descriptor.vertical_ = root.value("type").toString() == "vertical";
|
|
||||||
|
|
||||||
for (QJsonValue _val : root.value("items").toArray())
|
|
||||||
{
|
{
|
||||||
auto _obj = _val.toObject();
|
descriptor.items_.emplace_back(
|
||||||
|
loadNodes<SplitNodeDescriptor>(_obj));
|
||||||
auto _type = _obj.value("type");
|
|
||||||
if (_type == "split")
|
|
||||||
{
|
|
||||||
descriptor.items_.emplace_back(
|
|
||||||
loadNodes<SplitNodeDescriptor>(_obj));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
descriptor.items_.emplace_back(
|
|
||||||
loadNodes<ContainerNodeDescriptor>(_obj));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
return descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QList<QUuid> loadFilters(QJsonValue val)
|
|
||||||
{
|
|
||||||
QList<QUuid> filterIds;
|
|
||||||
|
|
||||||
if (!val.isUndefined())
|
|
||||||
{
|
{
|
||||||
const auto array = val.toArray();
|
descriptor.items_.emplace_back(
|
||||||
filterIds.reserve(array.size());
|
loadNodes<ContainerNodeDescriptor>(_obj));
|
||||||
for (const auto &id : array)
|
|
||||||
{
|
|
||||||
filterIds.append(QUuid::fromString(id.toString()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return filterIds;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QList<QUuid> loadFilters(QJsonValue val)
|
||||||
|
{
|
||||||
|
QList<QUuid> filterIds;
|
||||||
|
|
||||||
|
if (!val.isUndefined())
|
||||||
|
{
|
||||||
|
const auto array = val.toArray();
|
||||||
|
filterIds.reserve(array.size());
|
||||||
|
for (const auto &id : array)
|
||||||
|
{
|
||||||
|
filterIds.append(QUuid::fromString(id.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filterIds;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void SplitDescriptor::loadFromJSON(SplitDescriptor &descriptor,
|
void SplitDescriptor::loadFromJSON(SplitDescriptor &descriptor,
|
||||||
|
|||||||
@@ -8,20 +8,20 @@ struct CommandContext;
|
|||||||
|
|
||||||
namespace commands {
|
namespace commands {
|
||||||
|
|
||||||
QString emoteOnly(const CommandContext &ctx);
|
QString emoteOnly(const CommandContext &ctx);
|
||||||
QString emoteOnlyOff(const CommandContext &ctx);
|
QString emoteOnlyOff(const CommandContext &ctx);
|
||||||
|
|
||||||
QString subscribers(const CommandContext &ctx);
|
QString subscribers(const CommandContext &ctx);
|
||||||
QString subscribersOff(const CommandContext &ctx);
|
QString subscribersOff(const CommandContext &ctx);
|
||||||
|
|
||||||
QString slow(const CommandContext &ctx);
|
QString slow(const CommandContext &ctx);
|
||||||
QString slowOff(const CommandContext &ctx);
|
QString slowOff(const CommandContext &ctx);
|
||||||
|
|
||||||
QString followers(const CommandContext &ctx);
|
QString followers(const CommandContext &ctx);
|
||||||
QString followersOff(const CommandContext &ctx);
|
QString followersOff(const CommandContext &ctx);
|
||||||
|
|
||||||
QString uniqueChat(const CommandContext &ctx);
|
QString uniqueChat(const CommandContext &ctx);
|
||||||
QString uniqueChatOff(const CommandContext &ctx);
|
QString uniqueChatOff(const CommandContext &ctx);
|
||||||
|
|
||||||
} // namespace commands
|
} // namespace commands
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
namespace completion {
|
namespace completion {
|
||||||
class Source;
|
class Source;
|
||||||
} // namespace completion
|
} // namespace completion
|
||||||
|
|
||||||
/// @brief Represents the kind of completion occurring
|
/// @brief Represents the kind of completion occurring
|
||||||
|
|||||||
@@ -11,23 +11,23 @@ namespace chatterino::completion {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void addCommand(const QString &command, std::vector<CommandItem> &out)
|
void addCommand(const QString &command, std::vector<CommandItem> &out)
|
||||||
|
{
|
||||||
|
if (command.startsWith('/') || command.startsWith('.'))
|
||||||
{
|
{
|
||||||
if (command.startsWith('/') || command.startsWith('.'))
|
out.push_back({
|
||||||
{
|
.name = command.mid(1),
|
||||||
out.push_back({
|
.prefix = command.at(0),
|
||||||
.name = command.mid(1),
|
});
|
||||||
.prefix = command.at(0),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out.push_back({
|
|
||||||
.name = command,
|
|
||||||
.prefix = "",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out.push_back({
|
||||||
|
.name = command,
|
||||||
|
.prefix = "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -17,37 +17,36 @@ namespace chatterino::completion {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void addEmotes(std::vector<EmoteItem> &out, const EmoteMap &map,
|
void addEmotes(std::vector<EmoteItem> &out, const EmoteMap &map,
|
||||||
const QString &providerName)
|
const QString &providerName)
|
||||||
|
{
|
||||||
|
for (auto &&emote : map)
|
||||||
{
|
{
|
||||||
for (auto &&emote : map)
|
out.push_back({.emote = emote.second,
|
||||||
{
|
.searchName = emote.first.string,
|
||||||
out.push_back({.emote = emote.second,
|
.tabCompletionName = emote.first.string,
|
||||||
.searchName = emote.first.string,
|
.displayName = emote.second->name.string,
|
||||||
.tabCompletionName = emote.first.string,
|
.providerName = providerName,
|
||||||
.displayName = emote.second->name.string,
|
.isEmoji = false});
|
||||||
.providerName = providerName,
|
|
||||||
.isEmoji = false});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void addEmojis(std::vector<EmoteItem> &out,
|
void addEmojis(std::vector<EmoteItem> &out, const std::vector<EmojiPtr> &map)
|
||||||
const std::vector<EmojiPtr> &map)
|
{
|
||||||
|
for (const auto &emoji : map)
|
||||||
{
|
{
|
||||||
for (const auto &emoji : map)
|
for (auto &&shortCode : emoji->shortCodes)
|
||||||
{
|
{
|
||||||
for (auto &&shortCode : emoji->shortCodes)
|
out.push_back(
|
||||||
{
|
{.emote = emoji->emote,
|
||||||
out.push_back(
|
.searchName = shortCode,
|
||||||
{.emote = emoji->emote,
|
.tabCompletionName = QStringLiteral(":%1:").arg(shortCode),
|
||||||
.searchName = shortCode,
|
.displayName = shortCode,
|
||||||
.tabCompletionName = QStringLiteral(":%1:").arg(shortCode),
|
.providerName = "Emoji",
|
||||||
.displayName = shortCode,
|
.isEmoji = true});
|
||||||
.providerName = "Emoji",
|
}
|
||||||
.isEmoji = true});
|
};
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ namespace chatterino::completion {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
size_t sizeWithinLimit(size_t size, size_t limit)
|
size_t sizeWithinLimit(size_t size, size_t limit)
|
||||||
|
{
|
||||||
|
if (limit == 0)
|
||||||
{
|
{
|
||||||
if (limit == 0)
|
return size;
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return std::min(size, limit);
|
|
||||||
}
|
}
|
||||||
|
return std::min(size, limit);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -11,149 +11,148 @@
|
|||||||
|
|
||||||
namespace chatterino::completion {
|
namespace chatterino::completion {
|
||||||
namespace {
|
namespace {
|
||||||
/**
|
/**
|
||||||
* @brief This function calculates the "cost" of the changes that need to
|
* @brief This function calculates the "cost" of the changes that need to
|
||||||
* be done to the query to make it the value.
|
* be done to the query to make it the value.
|
||||||
*
|
*
|
||||||
* By default an emote with more differences in character casing from the
|
* By default an emote with more differences in character casing from the
|
||||||
* query will get a higher cost, each additional letter also increases cost.
|
* query will get a higher cost, each additional letter also increases cost.
|
||||||
*
|
*
|
||||||
* @param prioritizeUpper If set, then differences in casing don't matter, but
|
* @param prioritizeUpper If set, then differences in casing don't matter, but
|
||||||
* instead the more lowercase letters an emote contains, the higher cost it
|
* instead the more lowercase letters an emote contains, the higher cost it
|
||||||
* will get. Additional letters also increase the cost in this mode.
|
* will get. Additional letters also increase the cost in this mode.
|
||||||
*
|
*
|
||||||
* @return How different the emote is from query. Values in the range [-10,
|
* @return How different the emote is from query. Values in the range [-10,
|
||||||
* \infty].
|
* \infty].
|
||||||
*/
|
*/
|
||||||
int costOfEmote(QStringView query, QStringView emote, bool prioritizeUpper)
|
int costOfEmote(QStringView query, QStringView emote, bool prioritizeUpper)
|
||||||
|
{
|
||||||
|
int score = 0;
|
||||||
|
|
||||||
|
if (prioritizeUpper)
|
||||||
{
|
{
|
||||||
int score = 0;
|
// We are in case 3, push 'more uppercase' emotes to the top
|
||||||
|
for (const auto i : emote)
|
||||||
if (prioritizeUpper)
|
|
||||||
{
|
{
|
||||||
// We are in case 3, push 'more uppercase' emotes to the top
|
score += int(!i.isUpper());
|
||||||
for (const auto i : emote)
|
|
||||||
{
|
|
||||||
score += int(!i.isUpper());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
// Push more matching emotes to the top
|
|
||||||
int len = std::min(emote.size(), query.size());
|
|
||||||
for (int i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
// Different casing gets a higher cost score
|
|
||||||
score += query.at(i).isUpper() ^ emote.at(i).isUpper();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// No case differences, put this at the top
|
|
||||||
if (score == 0)
|
|
||||||
{
|
|
||||||
score = -10;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto diff = emote.size() - query.size();
|
|
||||||
if (diff > 0)
|
|
||||||
{
|
|
||||||
// Case changes are way less changes to the user compared to adding characters
|
|
||||||
score += diff * 100;
|
|
||||||
}
|
|
||||||
return score;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This contains the brains of emote tab completion. Updates output to sorted completions.
|
|
||||||
// Ensure that the query string is already normalized, that is doesn't have a leading ':'
|
|
||||||
// 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,
|
|
||||||
const std::function<bool(EmoteItem, Qt::CaseSensitivity)>
|
|
||||||
&matchingFunction)
|
|
||||||
{
|
{
|
||||||
// Given these emotes: pajaW, PAJAW
|
// Push more matching emotes to the top
|
||||||
// There are a few cases of input:
|
int len = std::min(emote.size(), query.size());
|
||||||
// 1. "pajaw" expect {pajaW, PAJAW} - no uppercase characters, do regular case insensitive search
|
for (int i = 0; i < len; i++)
|
||||||
// 2. "PA" expect {PAJAW} - uppercase characters, case sensitive search gives results
|
{
|
||||||
// 3. "Pajaw" expect {PAJAW, pajaW} - case sensitive search doesn't give results, need to use sorting
|
// Different casing gets a higher cost score
|
||||||
// 4. "NOTHING" expect {} - no results
|
score += query.at(i).isUpper() ^ emote.at(i).isUpper();
|
||||||
// 5. "nothing" expect {} - same as 4 but first search is case insensitive
|
}
|
||||||
|
}
|
||||||
|
// No case differences, put this at the top
|
||||||
|
if (score == 0)
|
||||||
|
{
|
||||||
|
score = -10;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the query contains any uppercase characters
|
auto diff = emote.size() - query.size();
|
||||||
// This tells us if we're in case 1 or 5 vs all others
|
if (diff > 0)
|
||||||
bool haveUpper =
|
{
|
||||||
std::any_of(query.begin(), query.end(), [](const QChar &c) {
|
// Case changes are way less changes to the user compared to adding characters
|
||||||
return c.isUpper();
|
score += diff * 100;
|
||||||
});
|
}
|
||||||
|
return score;
|
||||||
|
};
|
||||||
|
|
||||||
// First search, for case 1 it will be case insensitive,
|
// This contains the brains of emote tab completion. Updates output to sorted completions.
|
||||||
// for cases 2, 3 and 4 it will be case sensitive
|
// Ensure that the query string is already normalized, that is doesn't have a leading ':'
|
||||||
|
// 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,
|
||||||
|
const std::function<bool(EmoteItem, Qt::CaseSensitivity)> &matchingFunction)
|
||||||
|
{
|
||||||
|
// Given these emotes: pajaW, PAJAW
|
||||||
|
// There are a few cases of input:
|
||||||
|
// 1. "pajaw" expect {pajaW, PAJAW} - no uppercase characters, do regular case insensitive search
|
||||||
|
// 2. "PA" expect {PAJAW} - uppercase characters, case sensitive search gives results
|
||||||
|
// 3. "Pajaw" expect {PAJAW, pajaW} - case sensitive search doesn't give results, need to use sorting
|
||||||
|
// 4. "NOTHING" expect {} - no results
|
||||||
|
// 5. "nothing" expect {} - same as 4 but first search is case insensitive
|
||||||
|
|
||||||
|
// Check if the query contains any uppercase characters
|
||||||
|
// This tells us if we're in case 1 or 5 vs all others
|
||||||
|
bool haveUpper =
|
||||||
|
std::any_of(query.begin(), query.end(), [](const QChar &c) {
|
||||||
|
return c.isUpper();
|
||||||
|
});
|
||||||
|
|
||||||
|
// First search, for case 1 it will be case insensitive,
|
||||||
|
// for cases 2, 3 and 4 it will be case sensitive
|
||||||
|
for (const auto &item : items)
|
||||||
|
{
|
||||||
|
if (matchingFunction(
|
||||||
|
item, haveUpper ? Qt::CaseSensitive : Qt::CaseInsensitive))
|
||||||
|
{
|
||||||
|
output.push_back(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if case 3: then true; false otherwise
|
||||||
|
bool prioritizeUpper = false;
|
||||||
|
|
||||||
|
// No results from search
|
||||||
|
if (output.empty())
|
||||||
|
{
|
||||||
|
if (!haveUpper)
|
||||||
|
{
|
||||||
|
// Optimisation: First search was case insensitive, but we found nothing
|
||||||
|
// There is nothing to be found: case 5.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Case sensitive search from case 2 found nothing, therefore we can
|
||||||
|
// only be in case 3 or 4.
|
||||||
|
|
||||||
|
prioritizeUpper = true;
|
||||||
|
// Run the search again but this time without case sensitivity
|
||||||
for (const auto &item : items)
|
for (const auto &item : items)
|
||||||
{
|
{
|
||||||
if (matchingFunction(
|
if (matchingFunction(item, Qt::CaseInsensitive))
|
||||||
item, haveUpper ? Qt::CaseSensitive : Qt::CaseInsensitive))
|
|
||||||
{
|
{
|
||||||
output.push_back(item);
|
output.push_back(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if case 3: then true; false otherwise
|
|
||||||
bool prioritizeUpper = false;
|
|
||||||
|
|
||||||
// No results from search
|
|
||||||
if (output.empty())
|
if (output.empty())
|
||||||
{
|
{
|
||||||
if (!haveUpper)
|
// The second search found nothing, so don't even try to sort: case 4
|
||||||
{
|
return;
|
||||||
// Optimisation: First search was case insensitive, but we found nothing
|
|
||||||
// There is nothing to be found: case 5.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Case sensitive search from case 2 found nothing, therefore we can
|
|
||||||
// only be in case 3 or 4.
|
|
||||||
|
|
||||||
prioritizeUpper = true;
|
|
||||||
// Run the search again but this time without case sensitivity
|
|
||||||
for (const auto &item : items)
|
|
||||||
{
|
|
||||||
if (matchingFunction(item, Qt::CaseInsensitive))
|
|
||||||
{
|
|
||||||
output.push_back(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (output.empty())
|
|
||||||
{
|
|
||||||
// The second search found nothing, so don't even try to sort: case 4
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(output.begin(), output.end(),
|
|
||||||
[query, prioritizeUpper, ignoreColonForCost](
|
|
||||||
const EmoteItem &a, const EmoteItem &b) -> bool {
|
|
||||||
auto tempA = a.searchName;
|
|
||||||
auto tempB = b.searchName;
|
|
||||||
if (ignoreColonForCost && tempA.startsWith(":"))
|
|
||||||
{
|
|
||||||
tempA = tempA.mid(1);
|
|
||||||
}
|
|
||||||
if (ignoreColonForCost && tempB.startsWith(":"))
|
|
||||||
{
|
|
||||||
tempB = tempB.mid(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto costA = costOfEmote(query, tempA, prioritizeUpper);
|
|
||||||
auto costB = costOfEmote(query, tempB, prioritizeUpper);
|
|
||||||
if (costA == costB)
|
|
||||||
{
|
|
||||||
// Case difference and length came up tied for (a, b), break the tie
|
|
||||||
return QString::compare(tempA, tempB,
|
|
||||||
Qt::CaseInsensitive) < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return costA < costB;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::sort(output.begin(), output.end(),
|
||||||
|
[query, prioritizeUpper, ignoreColonForCost](
|
||||||
|
const EmoteItem &a, const EmoteItem &b) -> bool {
|
||||||
|
auto tempA = a.searchName;
|
||||||
|
auto tempB = b.searchName;
|
||||||
|
if (ignoreColonForCost && tempA.startsWith(":"))
|
||||||
|
{
|
||||||
|
tempA = tempA.mid(1);
|
||||||
|
}
|
||||||
|
if (ignoreColonForCost && tempB.startsWith(":"))
|
||||||
|
{
|
||||||
|
tempB = tempB.mid(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto costA = costOfEmote(query, tempA, prioritizeUpper);
|
||||||
|
auto costB = costOfEmote(query, tempB, prioritizeUpper);
|
||||||
|
if (costA == costB)
|
||||||
|
{
|
||||||
|
// Case difference and length came up tied for (a, b), break the tie
|
||||||
|
return QString::compare(tempA, tempB,
|
||||||
|
Qt::CaseInsensitive) < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return costA < costB;
|
||||||
|
});
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void SmartEmoteStrategy::apply(const std::vector<EmoteItem> &items,
|
void SmartEmoteStrategy::apply(const std::vector<EmoteItem> &items,
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ namespace chatterino {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr QStringView REGEX_START_BOUNDARY(u"(?:\\b|\\s|^)");
|
constexpr QStringView REGEX_START_BOUNDARY(u"(?:\\b|\\s|^)");
|
||||||
constexpr QStringView REGEX_END_BOUNDARY(u"(?:\\b|\\s|$)");
|
constexpr QStringView REGEX_END_BOUNDARY(u"(?:\\b|\\s|$)");
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -105,11 +105,11 @@ private:
|
|||||||
namespace pajlada {
|
namespace pajlada {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
chatterino::HighlightPhrase constructError()
|
chatterino::HighlightPhrase constructError()
|
||||||
{
|
{
|
||||||
return chatterino::HighlightPhrase(QString(), false, false, false,
|
return chatterino::HighlightPhrase(QString(), false, false, false, false,
|
||||||
false, false, QString(), QColor());
|
false, QString(), QColor());
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ struct ChannelPointReward;
|
|||||||
struct TwitchEmoteOccurrence;
|
struct TwitchEmoteOccurrence;
|
||||||
|
|
||||||
namespace linkparser {
|
namespace linkparser {
|
||||||
struct Parsed;
|
struct Parsed;
|
||||||
} // namespace linkparser
|
} // namespace linkparser
|
||||||
|
|
||||||
struct SystemMessageTag {
|
struct SystemMessageTag {
|
||||||
|
|||||||
@@ -26,21 +26,21 @@ using namespace literals;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Computes the bounding box for the given vector of images
|
// Computes the bounding box for the given vector of images
|
||||||
QSizeF getBoundingBoxSize(const std::vector<ImagePtr> &images)
|
QSizeF getBoundingBoxSize(const std::vector<ImagePtr> &images)
|
||||||
|
{
|
||||||
|
qreal width = 0;
|
||||||
|
qreal height = 0;
|
||||||
|
for (const auto &img : images)
|
||||||
{
|
{
|
||||||
qreal width = 0;
|
QSizeF s = img->size();
|
||||||
qreal height = 0;
|
width = std::max(width, s.width());
|
||||||
for (const auto &img : images)
|
height = std::max(height, s.height());
|
||||||
{
|
|
||||||
QSizeF s = img->size();
|
|
||||||
width = std::max(width, s.width());
|
|
||||||
height = std::max(height, s.height());
|
|
||||||
}
|
|
||||||
|
|
||||||
return {width, height};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {width, height};
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
MessageElement::MessageElement(MessageElementFlags flags)
|
MessageElement::MessageElement(MessageElementFlags flags)
|
||||||
|
|||||||
@@ -23,15 +23,15 @@ namespace chatterino {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
QColor blendColors(const QColor &base, const QColor &apply)
|
QColor blendColors(const QColor &base, const QColor &apply)
|
||||||
{
|
{
|
||||||
const qreal &alpha = apply.alphaF();
|
const qreal &alpha = apply.alphaF();
|
||||||
QColor result;
|
QColor result;
|
||||||
result.setRgbF(base.redF() * (1 - alpha) + apply.redF() * alpha,
|
result.setRgbF(base.redF() * (1 - alpha) + apply.redF() * alpha,
|
||||||
base.greenF() * (1 - alpha) + apply.greenF() * alpha,
|
base.greenF() * (1 - alpha) + apply.greenF() * alpha,
|
||||||
base.blueF() * (1 - alpha) + apply.blueF() * alpha);
|
base.blueF() * (1 - alpha) + apply.blueF() * alpha);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
MessageLayout::MessageLayout(MessagePtr message)
|
MessageLayout::MessageLayout(MessagePtr message)
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ struct BttvLiveUpdateEmoteRemoveMessage;
|
|||||||
|
|
||||||
namespace bttv::detail {
|
namespace bttv::detail {
|
||||||
|
|
||||||
EmoteMap parseChannelEmotes(const QJsonObject &jsonRoot,
|
EmoteMap parseChannelEmotes(const QJsonObject &jsonRoot,
|
||||||
const QString &channelDisplayName);
|
const QString &channelDisplayName);
|
||||||
|
|
||||||
} // namespace bttv::detail
|
} // namespace bttv::detail
|
||||||
|
|
||||||
|
|||||||
@@ -27,12 +27,10 @@ using FfzChannelBadgeMap =
|
|||||||
|
|
||||||
namespace ffz::detail {
|
namespace ffz::detail {
|
||||||
|
|
||||||
EmoteMap parseChannelEmotes(const QJsonObject &jsonRoot);
|
EmoteMap parseChannelEmotes(const QJsonObject &jsonRoot);
|
||||||
|
|
||||||
/**
|
/// Parse the `user_badge_ids` into a map of User IDs -> Badge IDs
|
||||||
* Parse the `user_badge_ids` into a map of User IDs -> Badge IDs
|
FfzChannelBadgeMap parseChannelBadges(const QJsonObject &badgeRoot);
|
||||||
*/
|
|
||||||
FfzChannelBadgeMap parseChannelBadges(const QJsonObject &badgeRoot);
|
|
||||||
|
|
||||||
} // namespace ffz::detail
|
} // namespace ffz::detail
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace chatterino {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const auto payload = "chatterino/" + CHATTERINO_VERSION;
|
const auto payload = "chatterino/" + CHATTERINO_VERSION;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ struct BasicPubSubConfig : public websocketpp::config::asio_tls_client {
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace liveupdates {
|
namespace liveupdates {
|
||||||
using WebsocketClient = websocketpp::client<chatterino::BasicPubSubConfig>;
|
using WebsocketClient = websocketpp::client<chatterino::BasicPubSubConfig>;
|
||||||
using WebsocketHandle = websocketpp::connection_hdl;
|
using WebsocketHandle = websocketpp::connection_hdl;
|
||||||
using WebsocketErrorCode = websocketpp::lib::error_code;
|
using WebsocketErrorCode = websocketpp::lib::error_code;
|
||||||
} // namespace liveupdates
|
} // namespace liveupdates
|
||||||
|
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ namespace chatterino {
|
|||||||
class ImageSet;
|
class ImageSet;
|
||||||
class Channel;
|
class Channel;
|
||||||
namespace seventv::eventapi {
|
namespace seventv::eventapi {
|
||||||
struct EmoteAddDispatch;
|
struct EmoteAddDispatch;
|
||||||
struct EmoteUpdateDispatch;
|
struct EmoteUpdateDispatch;
|
||||||
struct EmoteRemoveDispatch;
|
struct EmoteRemoveDispatch;
|
||||||
} // namespace seventv::eventapi
|
} // namespace seventv::eventapi
|
||||||
|
|
||||||
// https://github.com/SevenTV/API/blob/a84e884b5590dbb5d91a5c6b3548afabb228f385/data/model/emote-set.model.go#L29-L36
|
// https://github.com/SevenTV/API/blob/a84e884b5590dbb5d91a5c6b3548afabb228f385/data/model/emote-set.model.go#L29-L36
|
||||||
@@ -86,7 +86,7 @@ using SeventvEmoteSetFlags = FlagsEnum<SeventvEmoteSetFlag>;
|
|||||||
|
|
||||||
namespace seventv::detail {
|
namespace seventv::detail {
|
||||||
|
|
||||||
EmoteMap parseEmotes(const QJsonArray &emoteSetEmotes, bool isGlobal);
|
EmoteMap parseEmotes(const QJsonArray &emoteSetEmotes, bool isGlobal);
|
||||||
|
|
||||||
} // namespace seventv::detail
|
} // namespace seventv::detail
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,13 @@
|
|||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
namespace seventv::eventapi {
|
namespace seventv::eventapi {
|
||||||
struct Dispatch;
|
struct Dispatch;
|
||||||
struct EmoteAddDispatch;
|
struct EmoteAddDispatch;
|
||||||
struct EmoteUpdateDispatch;
|
struct EmoteUpdateDispatch;
|
||||||
struct EmoteRemoveDispatch;
|
struct EmoteRemoveDispatch;
|
||||||
struct UserConnectionUpdateDispatch;
|
struct UserConnectionUpdateDispatch;
|
||||||
struct CosmeticCreateDispatch;
|
struct CosmeticCreateDispatch;
|
||||||
struct EntitlementCreateDeleteDispatch;
|
struct EntitlementCreateDeleteDispatch;
|
||||||
} // namespace seventv::eventapi
|
} // namespace seventv::eventapi
|
||||||
|
|
||||||
class SeventvBadges;
|
class SeventvBadges;
|
||||||
|
|||||||
@@ -63,35 +63,34 @@ using namespace literals;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(6, 1, 0)
|
#if QT_VERSION < QT_VERSION_CHECK(6, 1, 0)
|
||||||
const QString MAGIC_MESSAGE_SUFFIX = QString((const char *)u8" \U000E0000");
|
const QString MAGIC_MESSAGE_SUFFIX = QString((const char *)u8" \U000E0000");
|
||||||
#else
|
#else
|
||||||
const QString MAGIC_MESSAGE_SUFFIX = QString::fromUtf8(u8" \U000E0000");
|
const QString MAGIC_MESSAGE_SUFFIX = QString::fromUtf8(u8" \U000E0000");
|
||||||
#endif
|
#endif
|
||||||
constexpr int CLIP_CREATION_COOLDOWN = 5000;
|
constexpr int CLIP_CREATION_COOLDOWN = 5000;
|
||||||
const QString CLIPS_LINK("https://clips.twitch.tv/%1");
|
const QString CLIPS_LINK("https://clips.twitch.tv/%1");
|
||||||
const QString CLIPS_FAILURE_CLIPS_UNAVAILABLE_TEXT(
|
const QString CLIPS_FAILURE_CLIPS_UNAVAILABLE_TEXT(
|
||||||
"Failed to create a clip - clips are temporarily unavailable: %1");
|
"Failed to create a clip - clips are temporarily unavailable: %1");
|
||||||
const QString CLIPS_FAILURE_CLIPS_DISABLED_TEXT(
|
const QString CLIPS_FAILURE_CLIPS_DISABLED_TEXT(
|
||||||
"Failed to create a clip - the streamer has clips disabled in their "
|
"Failed to create a clip - the streamer has clips disabled in their "
|
||||||
"channel.");
|
"channel.");
|
||||||
const QString CLIPS_FAILURE_CLIPS_RESTRICTED_TEXT(
|
const QString CLIPS_FAILURE_CLIPS_RESTRICTED_TEXT(
|
||||||
"Failed to create a clip - the streamer has restricted clip creation "
|
"Failed to create a clip - the streamer has restricted clip creation "
|
||||||
"to subscribers, or followers of an unknown duration.");
|
"to subscribers, or followers of an unknown duration.");
|
||||||
const QString CLIPS_FAILURE_CLIPS_RESTRICTED_CATEGORY_TEXT(
|
const QString CLIPS_FAILURE_CLIPS_RESTRICTED_CATEGORY_TEXT(
|
||||||
"Failed to create a clip - the streamer has disabled clips while in "
|
"Failed to create a clip - the streamer has disabled clips while in "
|
||||||
"this category.");
|
"this category.");
|
||||||
const QString CLIPS_FAILURE_NOT_AUTHENTICATED_TEXT(
|
const QString CLIPS_FAILURE_NOT_AUTHENTICATED_TEXT(
|
||||||
"Failed to create a clip - you need to re-authenticate.");
|
"Failed to create a clip - you need to re-authenticate.");
|
||||||
const QString CLIPS_FAILURE_UNKNOWN_ERROR_TEXT(
|
const QString CLIPS_FAILURE_UNKNOWN_ERROR_TEXT("Failed to create a clip: %1");
|
||||||
"Failed to create a clip: %1");
|
const QString LOGIN_PROMPT_TEXT("Click here to add your account again.");
|
||||||
const QString LOGIN_PROMPT_TEXT("Click here to add your account again.");
|
const Link ACCOUNTS_LINK(Link::OpenAccountsPage, QString());
|
||||||
const Link ACCOUNTS_LINK(Link::OpenAccountsPage, QString());
|
|
||||||
|
|
||||||
// Maximum number of chatters to fetch when refreshing chatters
|
// Maximum number of chatters to fetch when refreshing chatters
|
||||||
constexpr auto MAX_CHATTERS_TO_FETCH = 5000;
|
constexpr auto MAX_CHATTERS_TO_FETCH = 5000;
|
||||||
|
|
||||||
// From Twitch docs - expected size for a badge (1x)
|
// From Twitch docs - expected size for a badge (1x)
|
||||||
constexpr QSize BASE_BADGE_SIZE(18, 18);
|
constexpr QSize BASE_BADGE_SIZE(18, 18);
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TwitchChannel::TwitchChannel(const QString &name)
|
TwitchChannel::TwitchChannel(const QString &name)
|
||||||
|
|||||||
@@ -45,10 +45,10 @@ struct BttvLiveUpdateEmoteRemoveMessage;
|
|||||||
|
|
||||||
class SeventvEmotes;
|
class SeventvEmotes;
|
||||||
namespace seventv::eventapi {
|
namespace seventv::eventapi {
|
||||||
struct EmoteAddDispatch;
|
struct EmoteAddDispatch;
|
||||||
struct EmoteUpdateDispatch;
|
struct EmoteUpdateDispatch;
|
||||||
struct EmoteRemoveDispatch;
|
struct EmoteRemoveDispatch;
|
||||||
struct UserConnectionUpdateDispatch;
|
struct UserConnectionUpdateDispatch;
|
||||||
} // namespace seventv::eventapi
|
} // namespace seventv::eventapi
|
||||||
|
|
||||||
struct ChannelPointReward;
|
struct ChannelPointReward;
|
||||||
|
|||||||
@@ -200,22 +200,22 @@ std::string &getNmQueueName(const Paths &paths)
|
|||||||
|
|
||||||
namespace nm::client {
|
namespace nm::client {
|
||||||
|
|
||||||
void sendMessage(const QByteArray &array)
|
void sendMessage(const QByteArray &array)
|
||||||
{
|
{
|
||||||
ipc::sendMessage("chatterino_gui", array);
|
ipc::sendMessage("chatterino_gui", array);
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeToCout(const QByteArray &array)
|
void writeToCout(const QByteArray &array)
|
||||||
{
|
{
|
||||||
const auto *data = array.data();
|
const auto *data = array.data();
|
||||||
auto size = uint32_t(array.size());
|
auto size = uint32_t(array.size());
|
||||||
|
|
||||||
// We're writing the raw bytes to cout.
|
// We're writing the raw bytes to cout.
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
|
||||||
std::cout.write(reinterpret_cast<char *>(&size), 4);
|
std::cout.write(reinterpret_cast<char *>(&size), 4);
|
||||||
std::cout.write(data, size);
|
std::cout.write(data, size);
|
||||||
std::cout.flush();
|
std::cout.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace nm::client
|
} // namespace nm::client
|
||||||
|
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ Atomic<std::optional<QString>> &nmIpcError();
|
|||||||
|
|
||||||
namespace nm::client {
|
namespace nm::client {
|
||||||
|
|
||||||
void sendMessage(const QByteArray &array);
|
void sendMessage(const QByteArray &array);
|
||||||
void writeToCout(const QByteArray &array);
|
void writeToCout(const QByteArray &array);
|
||||||
|
|
||||||
} // namespace nm::client
|
} // namespace nm::client
|
||||||
|
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ namespace chatterino {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void appendDuration(int count, QChar &&suffix, QString &out)
|
void appendDuration(int count, QChar &&suffix, QString &out)
|
||||||
|
{
|
||||||
|
if (!out.isEmpty())
|
||||||
{
|
{
|
||||||
if (!out.isEmpty())
|
out.append(' ');
|
||||||
{
|
|
||||||
out.append(' ');
|
|
||||||
}
|
|
||||||
out.append(QString::number(count));
|
|
||||||
out.append(suffix);
|
|
||||||
}
|
}
|
||||||
|
out.append(QString::number(count));
|
||||||
|
out.append(suffix);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
+86
-86
@@ -33,105 +33,105 @@ namespace chatterino {
|
|||||||
|
|
||||||
namespace helpers::detail {
|
namespace helpers::detail {
|
||||||
|
|
||||||
SizeType skipSpace(QStringView view, SizeType startPos)
|
SizeType skipSpace(QStringView view, SizeType startPos)
|
||||||
|
{
|
||||||
|
while (startPos < view.length() && view.at(startPos).isSpace())
|
||||||
{
|
{
|
||||||
while (startPos < view.length() && view.at(startPos).isSpace())
|
startPos++;
|
||||||
{
|
|
||||||
startPos++;
|
|
||||||
}
|
|
||||||
return startPos - 1;
|
|
||||||
}
|
}
|
||||||
|
return startPos - 1;
|
||||||
|
}
|
||||||
|
|
||||||
bool matchesIgnorePlural(QStringView word, const QString &expected)
|
bool matchesIgnorePlural(QStringView word, const QString &expected)
|
||||||
|
{
|
||||||
|
if (!word.startsWith(expected))
|
||||||
{
|
{
|
||||||
if (!word.startsWith(expected))
|
return false;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (word.length() == expected.length())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return word.length() == expected.length() + 1 &&
|
|
||||||
word.at(word.length() - 1).toLatin1() == 's';
|
|
||||||
}
|
}
|
||||||
|
if (word.length() == expected.length())
|
||||||
std::pair<uint64_t, bool> findUnitMultiplierToSec(QStringView view,
|
|
||||||
SizeType &pos)
|
|
||||||
{
|
{
|
||||||
// Step 1. find end of unit
|
return true;
|
||||||
auto startIdx = pos;
|
}
|
||||||
auto endIdx = view.length();
|
return word.length() == expected.length() + 1 &&
|
||||||
for (; pos < view.length(); pos++)
|
word.at(word.length() - 1).toLatin1() == 's';
|
||||||
{
|
}
|
||||||
auto c = view.at(pos);
|
|
||||||
if (c.isSpace() || c.isDigit())
|
|
||||||
{
|
|
||||||
endIdx = pos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos--;
|
|
||||||
|
|
||||||
// TODO(QT6): use sliced (more readable)
|
std::pair<uint64_t, bool> findUnitMultiplierToSec(QStringView view,
|
||||||
auto unit = view.mid(startIdx, endIdx - startIdx);
|
SizeType &pos)
|
||||||
if (unit.isEmpty())
|
{
|
||||||
|
// Step 1. find end of unit
|
||||||
|
auto startIdx = pos;
|
||||||
|
auto endIdx = view.length();
|
||||||
|
for (; pos < view.length(); pos++)
|
||||||
|
{
|
||||||
|
auto c = view.at(pos);
|
||||||
|
if (c.isSpace() || c.isDigit())
|
||||||
{
|
{
|
||||||
return std::make_pair(0, false);
|
endIdx = pos;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
pos--;
|
||||||
|
|
||||||
auto first = unit.at(0).toLatin1();
|
// TODO(QT6): use sliced (more readable)
|
||||||
switch (first)
|
auto unit = view.mid(startIdx, endIdx - startIdx);
|
||||||
{
|
if (unit.isEmpty())
|
||||||
case 's': {
|
{
|
||||||
if (unit.length() == 1 ||
|
|
||||||
matchesIgnorePlural(unit, QStringLiteral("second")))
|
|
||||||
{
|
|
||||||
return std::make_pair(1, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'm': {
|
|
||||||
if (unit.length() == 1 ||
|
|
||||||
matchesIgnorePlural(unit, QStringLiteral("minute")))
|
|
||||||
{
|
|
||||||
return std::make_pair(60, true);
|
|
||||||
}
|
|
||||||
if ((unit.length() == 2 && unit.at(1).toLatin1() == 'o') ||
|
|
||||||
matchesIgnorePlural(unit, QStringLiteral("month")))
|
|
||||||
{
|
|
||||||
return std::make_pair(60 * 60 * 24 * 30, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'h': {
|
|
||||||
if (unit.length() == 1 ||
|
|
||||||
matchesIgnorePlural(unit, QStringLiteral("hour")))
|
|
||||||
{
|
|
||||||
return std::make_pair(60 * 60, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'd': {
|
|
||||||
if (unit.length() == 1 ||
|
|
||||||
matchesIgnorePlural(unit, QStringLiteral("day")))
|
|
||||||
{
|
|
||||||
return std::make_pair(60 * 60 * 24, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'w': {
|
|
||||||
if (unit.length() == 1 ||
|
|
||||||
matchesIgnorePlural(unit, QStringLiteral("week")))
|
|
||||||
{
|
|
||||||
return std::make_pair(60 * 60 * 24 * 7, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return std::make_pair(0, false);
|
return std::make_pair(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto first = unit.at(0).toLatin1();
|
||||||
|
switch (first)
|
||||||
|
{
|
||||||
|
case 's': {
|
||||||
|
if (unit.length() == 1 ||
|
||||||
|
matchesIgnorePlural(unit, QStringLiteral("second")))
|
||||||
|
{
|
||||||
|
return std::make_pair(1, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'm': {
|
||||||
|
if (unit.length() == 1 ||
|
||||||
|
matchesIgnorePlural(unit, QStringLiteral("minute")))
|
||||||
|
{
|
||||||
|
return std::make_pair(60, true);
|
||||||
|
}
|
||||||
|
if ((unit.length() == 2 && unit.at(1).toLatin1() == 'o') ||
|
||||||
|
matchesIgnorePlural(unit, QStringLiteral("month")))
|
||||||
|
{
|
||||||
|
return std::make_pair(60 * 60 * 24 * 30, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'h': {
|
||||||
|
if (unit.length() == 1 ||
|
||||||
|
matchesIgnorePlural(unit, QStringLiteral("hour")))
|
||||||
|
{
|
||||||
|
return std::make_pair(60 * 60, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'd': {
|
||||||
|
if (unit.length() == 1 ||
|
||||||
|
matchesIgnorePlural(unit, QStringLiteral("day")))
|
||||||
|
{
|
||||||
|
return std::make_pair(60 * 60 * 24, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'w': {
|
||||||
|
if (unit.length() == 1 ||
|
||||||
|
matchesIgnorePlural(unit, QStringLiteral("week")))
|
||||||
|
{
|
||||||
|
return std::make_pair(60 * 60 * 24 * 7, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return std::make_pair(0, false);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace helpers::detail
|
} // namespace helpers::detail
|
||||||
using namespace helpers::detail;
|
using namespace helpers::detail;
|
||||||
|
|
||||||
|
|||||||
+35
-35
@@ -18,44 +18,44 @@ namespace chatterino {
|
|||||||
// only qualified for tests
|
// only qualified for tests
|
||||||
namespace helpers::detail {
|
namespace helpers::detail {
|
||||||
|
|
||||||
using SizeType = QStringView::size_type;
|
using SizeType = QStringView::size_type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Skips all spaces.
|
* Skips all spaces.
|
||||||
* The caller must guarantee view.at(startPos).isSpace().
|
* The caller must guarantee view.at(startPos).isSpace().
|
||||||
*
|
*
|
||||||
* @param view The string to skip spaces in.
|
* @param view The string to skip spaces in.
|
||||||
* @param startPos The starting position (there must be a space in the view).
|
* @param startPos The starting position (there must be a space in the view).
|
||||||
* @return The position of the last space.
|
* @return The position of the last space.
|
||||||
*/
|
*/
|
||||||
SizeType skipSpace(QStringView view, SizeType startPos);
|
SizeType skipSpace(QStringView view, SizeType startPos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if `word` equals `expected` (singular) or `expected` + 's' (plural).
|
* Checks if `word` equals `expected` (singular) or `expected` + 's' (plural).
|
||||||
*
|
*
|
||||||
* @param word Word to test. Must not be empty.
|
* @param word Word to test. Must not be empty.
|
||||||
* @param expected Singular of the expected word.
|
* @param expected Singular of the expected word.
|
||||||
* @return true if `word` is singular or plural of `expected`.
|
* @return true if `word` is singular or plural of `expected`.
|
||||||
*/
|
*/
|
||||||
bool matchesIgnorePlural(QStringView word, const QString &expected);
|
bool matchesIgnorePlural(QStringView word, const QString &expected);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to find the unit starting at `pos` and returns its multiplier so
|
* Tries to find the unit starting at `pos` and returns its multiplier so
|
||||||
* `valueInUnit * multiplier = valueInSeconds` (e.g. 60 for minutes).
|
* `valueInUnit * multiplier = valueInSeconds` (e.g. 60 for minutes).
|
||||||
*
|
*
|
||||||
* Supported units are
|
* Supported units are
|
||||||
* 'w[eek(s)]', 'd[ay(s)]',
|
* 'w[eek(s)]', 'd[ay(s)]',
|
||||||
* 'h[our(s)]', 'm[inute(s)]', 's[econd(s)]'.
|
* 'h[our(s)]', 'm[inute(s)]', 's[econd(s)]'.
|
||||||
* The unit must be in lowercase.
|
* The unit must be in lowercase.
|
||||||
*
|
*
|
||||||
* @param view A view into a string
|
* @param view A view into a string
|
||||||
* @param pos The starting position.
|
* @param pos The starting position.
|
||||||
* This is set to the last position of the unit
|
* This is set to the last position of the unit
|
||||||
* if it's a valid unit, undefined otherwise.
|
* if it's a valid unit, undefined otherwise.
|
||||||
* @return (multiplier, ok)
|
* @return (multiplier, ok)
|
||||||
*/
|
*/
|
||||||
std::pair<uint64_t, bool> findUnitMultiplierToSec(QStringView view,
|
std::pair<uint64_t, bool> findUnitMultiplierToSec(QStringView view,
|
||||||
SizeType &pos);
|
SizeType &pos);
|
||||||
|
|
||||||
} // namespace helpers::detail
|
} // namespace helpers::detail
|
||||||
|
|
||||||
|
|||||||
@@ -5,45 +5,43 @@
|
|||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace rj {
|
namespace rj {
|
||||||
|
|
||||||
void addMember(rapidjson::Value &obj, const char *key,
|
void addMember(rapidjson::Value &obj, const char *key, rapidjson::Value &&value,
|
||||||
rapidjson::Value &&value,
|
rapidjson::Document::AllocatorType &a)
|
||||||
rapidjson::Document::AllocatorType &a)
|
{
|
||||||
|
obj.AddMember(rapidjson::Value(key, a).Move(), value, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addMember(rapidjson::Value &obj, const char *key, rapidjson::Value &value,
|
||||||
|
rapidjson::Document::AllocatorType &a)
|
||||||
|
{
|
||||||
|
obj.AddMember(rapidjson::Value(key, a).Move(), value.Move(), a);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString stringify(const rapidjson::Value &value)
|
||||||
|
{
|
||||||
|
rapidjson::StringBuffer buffer;
|
||||||
|
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
||||||
|
value.Accept(writer);
|
||||||
|
|
||||||
|
return buffer.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool getSafeObject(rapidjson::Value &obj, const char *key,
|
||||||
|
rapidjson::Value &out)
|
||||||
|
{
|
||||||
|
if (!checkJsonValue(obj, key))
|
||||||
{
|
{
|
||||||
obj.AddMember(rapidjson::Value(key, a).Move(), value, a);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addMember(rapidjson::Value &obj, const char *key,
|
out = obj[key].Move();
|
||||||
rapidjson::Value &value,
|
return true;
|
||||||
rapidjson::Document::AllocatorType &a)
|
}
|
||||||
{
|
|
||||||
obj.AddMember(rapidjson::Value(key, a).Move(), value.Move(), a);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString stringify(const rapidjson::Value &value)
|
bool checkJsonValue(const rapidjson::Value &obj, const char *key)
|
||||||
{
|
{
|
||||||
rapidjson::StringBuffer buffer;
|
return obj.IsObject() && !obj.IsNull() && obj.HasMember(key);
|
||||||
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
|
}
|
||||||
value.Accept(writer);
|
|
||||||
|
|
||||||
return buffer.GetString();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getSafeObject(rapidjson::Value &obj, const char *key,
|
|
||||||
rapidjson::Value &out)
|
|
||||||
{
|
|
||||||
if (!checkJsonValue(obj, key))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
out = obj[key].Move();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkJsonValue(const rapidjson::Value &obj, const char *key)
|
|
||||||
{
|
|
||||||
return obj.IsObject() && !obj.IsNull() && obj.HasMember(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace rj
|
} // namespace rj
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|||||||
@@ -11,91 +11,89 @@
|
|||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace rj {
|
namespace rj {
|
||||||
|
|
||||||
void addMember(rapidjson::Value &obj, const char *key,
|
void addMember(rapidjson::Value &obj, const char *key, rapidjson::Value &&value,
|
||||||
rapidjson::Value &&value,
|
rapidjson::Document::AllocatorType &a);
|
||||||
rapidjson::Document::AllocatorType &a);
|
void addMember(rapidjson::Value &obj, const char *key, rapidjson::Value &value,
|
||||||
void addMember(rapidjson::Value &obj, const char *key,
|
rapidjson::Document::AllocatorType &a);
|
||||||
rapidjson::Value &value,
|
|
||||||
rapidjson::Document::AllocatorType &a);
|
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
void set(rapidjson::Value &obj, const char *key, const Type &value,
|
void set(rapidjson::Value &obj, const char *key, const Type &value,
|
||||||
rapidjson::Document::AllocatorType &a)
|
rapidjson::Document::AllocatorType &a)
|
||||||
|
{
|
||||||
|
assert(obj.IsObject());
|
||||||
|
|
||||||
|
addMember(obj, key, pajlada::Serialize<Type>::get(value, a), a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void set(rapidjson::Value &obj, const char *key,
|
||||||
|
const rapidjson::Value &value,
|
||||||
|
rapidjson::Document::AllocatorType &a)
|
||||||
|
{
|
||||||
|
assert(obj.IsObject());
|
||||||
|
|
||||||
|
addMember(obj, key, const_cast<rapidjson::Value &>(value), a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
void set(rapidjson::Document &obj, const char *key, const Type &value)
|
||||||
|
{
|
||||||
|
assert(obj.IsObject());
|
||||||
|
|
||||||
|
auto &a = obj.GetAllocator();
|
||||||
|
|
||||||
|
addMember(obj, key, pajlada::Serialize<Type>::get(value, a), a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void set(rapidjson::Document &obj, const char *key,
|
||||||
|
const rapidjson::Value &value)
|
||||||
|
{
|
||||||
|
assert(obj.IsObject());
|
||||||
|
|
||||||
|
auto &a = obj.GetAllocator();
|
||||||
|
|
||||||
|
addMember(obj, key, const_cast<rapidjson::Value &>(value), a);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
void add(rapidjson::Value &arr, const Type &value,
|
||||||
|
rapidjson::Document::AllocatorType &a)
|
||||||
|
{
|
||||||
|
assert(arr.IsArray());
|
||||||
|
|
||||||
|
arr.PushBack(pajlada::Serialize<Type>::get(value, a), a);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool checkJsonValue(const rapidjson::Value &obj, const char *key);
|
||||||
|
|
||||||
|
template <typename Type>
|
||||||
|
bool getSafe(const rapidjson::Value &obj, const char *key, Type &out)
|
||||||
|
{
|
||||||
|
if (!checkJsonValue(obj, key))
|
||||||
{
|
{
|
||||||
assert(obj.IsObject());
|
return false;
|
||||||
|
|
||||||
addMember(obj, key, pajlada::Serialize<Type>::get(value, a), a);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
bool error = false;
|
||||||
inline void set(rapidjson::Value &obj, const char *key,
|
out = pajlada::Deserialize<Type>::get(obj[key], &error);
|
||||||
const rapidjson::Value &value,
|
|
||||||
rapidjson::Document::AllocatorType &a)
|
|
||||||
{
|
|
||||||
assert(obj.IsObject());
|
|
||||||
|
|
||||||
addMember(obj, key, const_cast<rapidjson::Value &>(value), a);
|
return !error;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
void set(rapidjson::Document &obj, const char *key, const Type &value)
|
bool getSafe(const rapidjson::Value &value, Type &out)
|
||||||
{
|
{
|
||||||
assert(obj.IsObject());
|
bool error = false;
|
||||||
|
out = pajlada::Deserialize<Type>::get(value, &error);
|
||||||
|
|
||||||
auto &a = obj.GetAllocator();
|
return !error;
|
||||||
|
}
|
||||||
|
|
||||||
addMember(obj, key, pajlada::Serialize<Type>::get(value, a), a);
|
bool getSafeObject(rapidjson::Value &obj, const char *key,
|
||||||
}
|
rapidjson::Value &out);
|
||||||
|
|
||||||
template <>
|
QString stringify(const rapidjson::Value &value);
|
||||||
inline void set(rapidjson::Document &obj, const char *key,
|
|
||||||
const rapidjson::Value &value)
|
|
||||||
{
|
|
||||||
assert(obj.IsObject());
|
|
||||||
|
|
||||||
auto &a = obj.GetAllocator();
|
|
||||||
|
|
||||||
addMember(obj, key, const_cast<rapidjson::Value &>(value), a);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
void add(rapidjson::Value &arr, const Type &value,
|
|
||||||
rapidjson::Document::AllocatorType &a)
|
|
||||||
{
|
|
||||||
assert(arr.IsArray());
|
|
||||||
|
|
||||||
arr.PushBack(pajlada::Serialize<Type>::get(value, a), a);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkJsonValue(const rapidjson::Value &obj, const char *key);
|
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
bool getSafe(const rapidjson::Value &obj, const char *key, Type &out)
|
|
||||||
{
|
|
||||||
if (!checkJsonValue(obj, key))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool error = false;
|
|
||||||
out = pajlada::Deserialize<Type>::get(obj[key], &error);
|
|
||||||
|
|
||||||
return !error;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
bool getSafe(const rapidjson::Value &value, Type &out)
|
|
||||||
{
|
|
||||||
bool error = false;
|
|
||||||
out = pajlada::Deserialize<Type>::get(value, &error);
|
|
||||||
|
|
||||||
return !error;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool getSafeObject(rapidjson::Value &obj, const char *key,
|
|
||||||
rapidjson::Value &out);
|
|
||||||
|
|
||||||
QString stringify(const rapidjson::Value &value);
|
|
||||||
|
|
||||||
} // namespace rj
|
} // namespace rj
|
||||||
} // namespace chatterino
|
} // namespace chatterino
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace chatterino {
|
|||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#ifdef Q_OS_WIN
|
||||||
namespace windows::detail {
|
namespace windows::detail {
|
||||||
void renameThread(void *hThread, const QString &name);
|
void renameThread(void *hThread, const QString &name);
|
||||||
} // namespace windows::detail
|
} // namespace windows::detail
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
+9
-9
@@ -11,16 +11,16 @@ namespace chatterino {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
const auto TWITCH_USER_LOGIN_PATTERN = R"(^[a-z0-9]\w{0,24}$)";
|
const auto TWITCH_USER_LOGIN_PATTERN = R"(^[a-z0-9]\w{0,24}$)";
|
||||||
|
|
||||||
// Remember to keep VALID_HELIX_COLORS up-to-date if a new color is implemented to keep naming for users consistent
|
// Remember to keep VALID_HELIX_COLORS up-to-date if a new color is implemented to keep naming for users consistent
|
||||||
const std::unordered_map<QString, QString> HELIX_COLOR_REPLACEMENTS{
|
const std::unordered_map<QString, QString> HELIX_COLOR_REPLACEMENTS{
|
||||||
{"blueviolet", "blue_violet"}, {"cadetblue", "cadet_blue"},
|
{"blueviolet", "blue_violet"}, {"cadetblue", "cadet_blue"},
|
||||||
{"dodgerblue", "dodger_blue"}, {"goldenrod", "golden_rod"},
|
{"dodgerblue", "dodger_blue"}, {"goldenrod", "golden_rod"},
|
||||||
{"hotpink", "hot_pink"}, {"orangered", "orange_red"},
|
{"hotpink", "hot_pink"}, {"orangered", "orange_red"},
|
||||||
{"seagreen", "sea_green"}, {"springgreen", "spring_green"},
|
{"seagreen", "sea_green"}, {"springgreen", "spring_green"},
|
||||||
{"yellowgreen", "yellow_green"},
|
{"yellowgreen", "yellow_green"},
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -13,23 +13,23 @@ namespace chatterino {
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
FlagsEnum<BaseWindow::Flags> popupFlags{
|
FlagsEnum<BaseWindow::Flags> popupFlags{
|
||||||
BaseWindow::Dialog,
|
BaseWindow::Dialog,
|
||||||
BaseWindow::EnableCustomFrame,
|
BaseWindow::EnableCustomFrame,
|
||||||
};
|
};
|
||||||
FlagsEnum<BaseWindow::Flags> popupFlagsCloseAutomatically{
|
FlagsEnum<BaseWindow::Flags> popupFlagsCloseAutomatically{
|
||||||
BaseWindow::Dialog,
|
BaseWindow::Dialog,
|
||||||
BaseWindow::EnableCustomFrame,
|
BaseWindow::EnableCustomFrame,
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
FlagsEnum<BaseWindow::Flags> popupFlags{
|
FlagsEnum<BaseWindow::Flags> popupFlags{
|
||||||
BaseWindow::EnableCustomFrame,
|
BaseWindow::EnableCustomFrame,
|
||||||
};
|
};
|
||||||
FlagsEnum<BaseWindow::Flags> popupFlagsCloseAutomatically{
|
FlagsEnum<BaseWindow::Flags> popupFlagsCloseAutomatically{
|
||||||
BaseWindow::EnableCustomFrame,
|
BaseWindow::EnableCustomFrame,
|
||||||
BaseWindow::Frameless,
|
BaseWindow::Frameless,
|
||||||
BaseWindow::FramelessDraggable,
|
BaseWindow::FramelessDraggable,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|||||||
@@ -8,14 +8,13 @@
|
|||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const QStringList friendlyBinaryOps = {
|
const QStringList friendlyBinaryOps = {
|
||||||
"and", "or", "+", "-", "*", "/",
|
"and", "or", "+", "-", "*", "/",
|
||||||
"%", "equals", "not equals", "<", ">", "<=",
|
"%", "equals", "not equals", "<", ">", "<=",
|
||||||
">=", "contains", "starts with", "ends with", "(nothing)"};
|
">=", "contains", "starts with", "ends with", "(nothing)"};
|
||||||
const QStringList realBinaryOps = {
|
const QStringList realBinaryOps = {
|
||||||
"&&", "||", "+", "-", "*", "/",
|
"&&", "||", "+", "-", "*", "/", "%", "==", "!=",
|
||||||
"%", "==", "!=", "<", ">", "<=",
|
"<", ">", "<=", ">=", "contains", "startswith", "endswith", ""};
|
||||||
">=", "contains", "startswith", "endswith", ""};
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ChannelFilterEditorDialog::ChannelFilterEditorDialog(QWidget *parent)
|
ChannelFilterEditorDialog::ChannelFilterEditorDialog(QWidget *parent)
|
||||||
|
|||||||
@@ -25,53 +25,51 @@ namespace chatterino {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool logInWithCredentials(QWidget *parent, const QString &userID,
|
bool logInWithCredentials(QWidget *parent, const QString &userID,
|
||||||
const QString &username, const QString &clientID,
|
const QString &username, const QString &clientID,
|
||||||
const QString &oauthToken)
|
const QString &oauthToken)
|
||||||
|
{
|
||||||
|
QStringList errors;
|
||||||
|
|
||||||
|
if (userID.isEmpty())
|
||||||
{
|
{
|
||||||
QStringList errors;
|
errors.append("Missing user ID");
|
||||||
|
|
||||||
if (userID.isEmpty())
|
|
||||||
{
|
|
||||||
errors.append("Missing user ID");
|
|
||||||
}
|
|
||||||
if (username.isEmpty())
|
|
||||||
{
|
|
||||||
errors.append("Missing username");
|
|
||||||
}
|
|
||||||
if (clientID.isEmpty())
|
|
||||||
{
|
|
||||||
errors.append("Missing Client ID");
|
|
||||||
}
|
|
||||||
if (oauthToken.isEmpty())
|
|
||||||
{
|
|
||||||
errors.append("Missing OAuth Token");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errors.length() > 0)
|
|
||||||
{
|
|
||||||
QMessageBox messageBox(parent);
|
|
||||||
messageBox.setWindowTitle("Invalid account credentials");
|
|
||||||
messageBox.setIcon(QMessageBox::Critical);
|
|
||||||
messageBox.setText(errors.join("<br>"));
|
|
||||||
messageBox.exec();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string basePath = "/accounts/uid" + userID.toStdString();
|
|
||||||
pajlada::Settings::Setting<QString>::set(basePath + "/username",
|
|
||||||
username);
|
|
||||||
pajlada::Settings::Setting<QString>::set(basePath + "/userID", userID);
|
|
||||||
pajlada::Settings::Setting<QString>::set(basePath + "/clientID",
|
|
||||||
clientID);
|
|
||||||
pajlada::Settings::Setting<QString>::set(basePath + "/oauthToken",
|
|
||||||
oauthToken);
|
|
||||||
|
|
||||||
getApp()->getAccounts()->twitch.reloadUsers();
|
|
||||||
getApp()->getAccounts()->twitch.currentUsername = username;
|
|
||||||
getSettings()->requestSave();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
if (username.isEmpty())
|
||||||
|
{
|
||||||
|
errors.append("Missing username");
|
||||||
|
}
|
||||||
|
if (clientID.isEmpty())
|
||||||
|
{
|
||||||
|
errors.append("Missing Client ID");
|
||||||
|
}
|
||||||
|
if (oauthToken.isEmpty())
|
||||||
|
{
|
||||||
|
errors.append("Missing OAuth Token");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors.length() > 0)
|
||||||
|
{
|
||||||
|
QMessageBox messageBox(parent);
|
||||||
|
messageBox.setWindowTitle("Invalid account credentials");
|
||||||
|
messageBox.setIcon(QMessageBox::Critical);
|
||||||
|
messageBox.setText(errors.join("<br>"));
|
||||||
|
messageBox.exec();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string basePath = "/accounts/uid" + userID.toStdString();
|
||||||
|
pajlada::Settings::Setting<QString>::set(basePath + "/username", username);
|
||||||
|
pajlada::Settings::Setting<QString>::set(basePath + "/userID", userID);
|
||||||
|
pajlada::Settings::Setting<QString>::set(basePath + "/clientID", clientID);
|
||||||
|
pajlada::Settings::Setting<QString>::set(basePath + "/oauthToken",
|
||||||
|
oauthToken);
|
||||||
|
|
||||||
|
getApp()->getAccounts()->twitch.reloadUsers();
|
||||||
|
getApp()->getAccounts()->twitch.currentUsername = username;
|
||||||
|
getSettings()->requestSave();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|||||||
@@ -33,52 +33,52 @@
|
|||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace {
|
namespace {
|
||||||
// Translates the given rectangle by an amount in the direction to appear like the tab is selected.
|
// Translates the given rectangle by an amount in the direction to appear like the tab is selected.
|
||||||
// For example, if location is Top, the rectangle will be translated in the negative Y direction,
|
// For example, if location is Top, the rectangle will be translated in the negative Y direction,
|
||||||
// or "up" on the screen, by amount.
|
// or "up" on the screen, by amount.
|
||||||
void translateRectForLocation(QRect &rect, NotebookTabLocation location,
|
void translateRectForLocation(QRect &rect, NotebookTabLocation location,
|
||||||
int amount)
|
int amount)
|
||||||
|
{
|
||||||
|
switch (location)
|
||||||
{
|
{
|
||||||
switch (location)
|
case NotebookTabLocation::Top:
|
||||||
{
|
rect.translate(0, -amount);
|
||||||
case NotebookTabLocation::Top:
|
break;
|
||||||
rect.translate(0, -amount);
|
case NotebookTabLocation::Left:
|
||||||
break;
|
rect.translate(-amount, 0);
|
||||||
case NotebookTabLocation::Left:
|
break;
|
||||||
rect.translate(-amount, 0);
|
case NotebookTabLocation::Right:
|
||||||
break;
|
rect.translate(amount, 0);
|
||||||
case NotebookTabLocation::Right:
|
break;
|
||||||
rect.translate(amount, 0);
|
case NotebookTabLocation::Bottom:
|
||||||
break;
|
rect.translate(0, amount);
|
||||||
case NotebookTabLocation::Bottom:
|
break;
|
||||||
rect.translate(0, amount);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float getCompactDivider(TabStyle tabStyle)
|
float getCompactDivider(TabStyle tabStyle)
|
||||||
|
{
|
||||||
|
switch (tabStyle)
|
||||||
{
|
{
|
||||||
switch (tabStyle)
|
case TabStyle::Compact:
|
||||||
{
|
return 1.5;
|
||||||
case TabStyle::Compact:
|
case TabStyle::Normal:
|
||||||
return 1.5;
|
default:
|
||||||
case TabStyle::Normal:
|
return 1.0;
|
||||||
default:
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float getCompactReducer(TabStyle tabStyle)
|
float getCompactReducer(TabStyle tabStyle)
|
||||||
|
{
|
||||||
|
switch (tabStyle)
|
||||||
{
|
{
|
||||||
switch (tabStyle)
|
case TabStyle::Compact:
|
||||||
{
|
return 4.0;
|
||||||
case TabStyle::Compact:
|
case TabStyle::Normal:
|
||||||
return 4.0;
|
default:
|
||||||
case TabStyle::Normal:
|
return 0.0;
|
||||||
default:
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
NotebookTab::NotebookTab(Notebook *notebook)
|
NotebookTab::NotebookTab(Notebook *notebook)
|
||||||
|
|||||||
@@ -27,19 +27,19 @@
|
|||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Add additional badges for highlights here
|
// Add additional badges for highlights here
|
||||||
QList<DisplayBadge> availableBadges = {
|
QList<DisplayBadge> availableBadges = {
|
||||||
{"Broadcaster", "broadcaster"},
|
{"Broadcaster", "broadcaster"},
|
||||||
{"Admin", "admin"},
|
{"Admin", "admin"},
|
||||||
{"Staff", "staff"},
|
{"Staff", "staff"},
|
||||||
{"Moderator", "moderator"},
|
{"Moderator", "moderator"},
|
||||||
{"Verified", "partner"},
|
{"Verified", "partner"},
|
||||||
{"VIP", "vip"},
|
{"VIP", "vip"},
|
||||||
{"Founder", "founder"},
|
{"Founder", "founder"},
|
||||||
{"Subscriber", "subscriber"},
|
{"Subscriber", "subscriber"},
|
||||||
{"Predicted Blue", "predictions/blue-1,predictions/blue-2"},
|
{"Predicted Blue", "predictions/blue-1,predictions/blue-2"},
|
||||||
{"Predicted Pink", "predictions/pink-2,predictions/pink-1"},
|
{"Predicted Pink", "predictions/pink-2,predictions/pink-1"},
|
||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
HighlightingPage::HighlightingPage()
|
HighlightingPage::HighlightingPage()
|
||||||
|
|||||||
@@ -183,28 +183,28 @@ QString formatChattersError(HelixGetChattersError error, const QString &message)
|
|||||||
|
|
||||||
namespace chatterino {
|
namespace chatterino {
|
||||||
namespace {
|
namespace {
|
||||||
void showTutorialVideo(QWidget *parent, const QString &source,
|
void showTutorialVideo(QWidget *parent, const QString &source,
|
||||||
const QString &title, const QString &description)
|
const QString &title, const QString &description)
|
||||||
{
|
{
|
||||||
auto *window = new BasePopup(
|
auto *window = new BasePopup(
|
||||||
{
|
{
|
||||||
BaseWindow::EnableCustomFrame,
|
BaseWindow::EnableCustomFrame,
|
||||||
BaseWindow::BoundsCheckOnShow,
|
BaseWindow::BoundsCheckOnShow,
|
||||||
},
|
},
|
||||||
parent);
|
parent);
|
||||||
window->setWindowTitle("Chatterino - " + title);
|
window->setWindowTitle("Chatterino - " + title);
|
||||||
window->setAttribute(Qt::WA_DeleteOnClose);
|
window->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
auto *layout = new QVBoxLayout();
|
auto *layout = new QVBoxLayout();
|
||||||
layout->addWidget(new QLabel(description));
|
layout->addWidget(new QLabel(description));
|
||||||
auto *label = new QLabel(window);
|
auto *label = new QLabel(window);
|
||||||
layout->addWidget(label);
|
layout->addWidget(label);
|
||||||
auto *movie = new QMovie(label);
|
auto *movie = new QMovie(label);
|
||||||
movie->setFileName(source);
|
movie->setFileName(source);
|
||||||
label->setMovie(movie);
|
label->setMovie(movie);
|
||||||
movie->start();
|
movie->start();
|
||||||
window->getLayoutContainer()->setLayout(layout);
|
window->getLayoutContainer()->setLayout(layout);
|
||||||
window->show();
|
window->show();
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
pajlada::Signals::Signal<Qt::KeyboardModifiers> Split::modifierStatusChanged;
|
pajlada::Signals::Signal<Qt::KeyboardModifiers> Split::modifierStatusChanged;
|
||||||
|
|||||||
Reference in New Issue
Block a user