Merge branch 'master' into git_is_pepega

This commit is contained in:
Mm2PL
2020-01-01 21:06:29 +01:00
committed by GitHub
174 changed files with 2445 additions and 1112 deletions
+13
View File
@@ -0,0 +1,13 @@
#pragma once
namespace chatterino {
// http://en.cppreference.com/w/cpp/algorithm/clamp
template <class T>
constexpr const T &clamp(const T &v, const T &lo, const T &hi)
{
return assert(!(hi < lo)), (v < lo) ? lo : (hi < v) ? hi : v;
}
} // namespace chatterino
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <QDir>
#include <QString>
namespace chatterino {
// https://stackoverflow.com/a/13014491
inline QString combinePath(const QString &a, const QString &b)
{
return QDir::cleanPath(a + QDir::separator() + b);
}
} // namespace chatterino
+20
View File
@@ -0,0 +1,20 @@
#pragma once
#include <QPointF>
#include <cmath>
namespace chatterino {
inline float distanceBetweenPoints(const QPointF &p1, const QPointF &p2)
{
QPointF tmp = p1 - p2;
float distance = 0.f;
distance += tmp.x() * tmp.x();
distance += tmp.y() * tmp.y();
return sqrt(distance);
}
} // namespace chatterino
+17
View File
@@ -0,0 +1,17 @@
#include "FunctionEventFilter.hpp"
namespace chatterino {
FunctionEventFilter::FunctionEventFilter(
QObject *parent, std::function<bool(QObject *, QEvent *)> function)
: QObject(parent)
, function_(std::move(function))
{
}
bool FunctionEventFilter::eventFilter(QObject *watched, QEvent *event)
{
return this->function_(watched, event);
}
} // namespace chatterino
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <QEvent>
#include <QObject>
#include <functional>
namespace chatterino {
class FunctionEventFilter : public QObject
{
Q_OBJECT
public:
FunctionEventFilter(QObject *parent,
std::function<bool(QObject *, QEvent *)> function);
protected:
bool eventFilter(QObject *watched, QEvent *event) override;
private:
std::function<bool(QObject *, QEvent *)> function_;
};
} // namespace chatterino
+33
View File
@@ -0,0 +1,33 @@
#include "FuzzyConvert.hpp"
#include <QRegularExpression>
namespace chatterino {
int fuzzyToInt(const QString &str, int default_)
{
static auto intFinder = QRegularExpression("[0-9]+");
auto match = intFinder.match(str);
if (match.hasMatch())
{
return match.captured().toInt();
}
return default_;
}
float fuzzyToFloat(const QString &str, float default_)
{
static auto floatFinder = QRegularExpression("[0-9]+(\\.[0-9]+)?");
auto match = floatFinder.match(str);
if (match.hasMatch())
{
return match.captured().toFloat();
}
return default_;
}
} // namespace chatterino
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <QString>
namespace chatterino {
int fuzzyToInt(const QString &str, int default_);
float fuzzyToFloat(const QString &str, float default_);
} // namespace chatterino
+38
View File
@@ -0,0 +1,38 @@
#include "Helpers.hpp"
#include <QUuid>
namespace chatterino {
QString generateUuid()
{
auto uuid = QUuid::createUuid();
return uuid.toString();
}
QString formatRichLink(const QString &url, bool file)
{
return QString("<a href=\"") + (file ? "file:///" : "") + url + "\">" +
url + "</a>";
}
QString formatRichNamedLink(const QString &url, const QString &name, bool file)
{
return QString("<a href=\"") + (file ? "file:///" : "") + url + "\">" +
name + "</a>";
}
QString shortenString(const QString &str, unsigned maxWidth)
{
auto shortened = QString(str);
if (str.size() > int(maxWidth))
{
shortened.resize(int(maxWidth));
shortened += "...";
}
return shortened;
}
} // namespace chatterino
+33
View File
@@ -0,0 +1,33 @@
#pragma once
#include <fmt/format.h>
#include <QString>
namespace chatterino {
template <typename... Args>
auto fS(Args &&... args)
{
return fmt::format(std::forward<Args>(args)...);
}
QString generateUuid();
QString formatRichLink(const QString &url, bool file = false);
QString formatRichNamedLink(const QString &url, const QString &name,
bool file = false);
QString shortenString(const QString &str, unsigned maxWidth = 50);
} // namespace chatterino
namespace fmt {
// format_arg for QString
inline void format_arg(BasicFormatter<char> &f, const char *&, const QString &v)
{
f.writer().write("{}", v.toStdString());
}
} // namespace fmt
+7 -9
View File
@@ -22,14 +22,12 @@ void initUpdateButton(Button &button,
dialog->buttonClicked.connect([&button](auto buttonType) {
switch (buttonType)
{
case UpdateDialog::Dismiss:
{
case UpdateDialog::Dismiss: {
button.hide();
}
break;
case UpdateDialog::Install:
{
Updates::getInstance().installUpdates();
case UpdateDialog::Install: {
Updates::instance().installUpdates();
}
break;
}
@@ -41,18 +39,18 @@ void initUpdateButton(Button &button,
// update image when state changes
auto updateChange = [&button](auto) {
button.setVisible(Updates::getInstance().shouldShowUpdateButton());
button.setVisible(Updates::instance().shouldShowUpdateButton());
auto imageUrl = Updates::getInstance().isError()
auto imageUrl = Updates::instance().isError()
? ":/buttons/updateError.png"
: ":/buttons/update.png";
button.setPixmap(QPixmap(imageUrl));
};
updateChange(Updates::getInstance().getStatus());
updateChange(Updates::instance().getStatus());
signalHolder.managedConnect(
Updates::getInstance().statusUpdated,
Updates::instance().statusUpdated,
[updateChange](auto status) { updateChange(status); });
}
+6 -12
View File
@@ -19,38 +19,32 @@ inline QString parseTagString(const QString &input)
switch (c.cell())
{
case 'n':
{
case 'n': {
output.replace(i, 2, '\n');
}
break;
case 'r':
{
case 'r': {
output.replace(i, 2, '\r');
}
break;
case 's':
{
case 's': {
output.replace(i, 2, ' ');
}
break;
case '\\':
{
case '\\': {
output.replace(i, 2, '\\');
}
break;
case ':':
{
case ':': {
output.replace(i, 2, ';');
}
break;
default:
{
default: {
output.remove(i, 1);
}
break;
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include <QLayout>
#include <QWidget>
#include <boost/variant.hpp>
namespace chatterino {
using LayoutItem = boost::variant<QWidget *, QLayout *>;
template <typename T>
T *makeLayout(std::initializer_list<LayoutItem> items)
{
auto t = new T;
for (auto &item : items)
{
switch (item.which())
{
case 0:
t->addItem(new QWidgetItem(boost::get<QWidget *>(item)));
break;
case 1:
t->addItem(boost::get<QLayout *>(item));
break;
}
}
return t;
}
template <typename T, typename With>
T *makeWidget(With with)
{
auto t = new T;
with(t);
return t;
}
} // namespace chatterino
+59
View File
@@ -0,0 +1,59 @@
#pragma once
#include <QCoreApplication>
#include <QRunnable>
#include <QThreadPool>
#include <functional>
#define async_exec(a) \
QThreadPool::globalInstance()->start(new LambdaRunnable(a));
namespace chatterino {
class LambdaRunnable : public QRunnable
{
public:
LambdaRunnable(std::function<void()> action)
{
this->action_ = action;
}
void run()
{
this->action_();
}
private:
std::function<void()> action_;
};
// Taken from
// https://stackoverflow.com/questions/21646467/how-to-execute-a-functor-or-a-lambda-in-a-given-thread-in-qt-gcd-style
// Qt 5/4 - preferred, has least allocations
template <typename F>
static void postToThread(F &&fun, QObject *obj = qApp)
{
struct Event : public QEvent {
using Fun = typename std::decay<F>::type;
Fun fun;
Event(Fun &&fun)
: QEvent(QEvent::None)
, fun(std::move(fun))
{
}
Event(const Fun &fun)
: QEvent(QEvent::None)
, fun(fun)
{
}
~Event() override
{
fun();
}
};
QCoreApplication::postEvent(obj, new Event(std::forward<F>(fun)));
}
} // namespace chatterino
+4 -4
View File
@@ -61,10 +61,10 @@ private:
// new
if (other)
{
this->conn_ =
QObject::connect(other, &QObject::destroyed, qApp,
[this](QObject *) { this->set(nullptr); },
Qt::DirectConnection);
this->conn_ = QObject::connect(
other, &QObject::destroyed, qApp,
[this](QObject *) { this->set(nullptr); },
Qt::DirectConnection);
}
this->t_ = other;
+2
View File
@@ -5,6 +5,7 @@
namespace std {
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
template <>
struct hash<QString> {
std::size_t operator()(const QString &s) const
@@ -12,5 +13,6 @@ struct hash<QString> {
return qHash(s);
}
};
#endif
} // namespace std
+45
View File
@@ -0,0 +1,45 @@
#pragma once
#include <QString>
#include <pajlada/serialize.hpp>
namespace pajlada {
template <>
struct Serialize<QString> {
static rapidjson::Value get(const QString &value,
rapidjson::Document::AllocatorType &a)
{
return rapidjson::Value(value.toUtf8(), a);
}
};
template <>
struct Deserialize<QString> {
static QString get(const rapidjson::Value &value, bool *error = nullptr)
{
if (!value.IsString())
{
PAJLADA_REPORT_ERROR(error)
return QString{};
}
try
{
return QString::fromUtf8(value.GetString(),
value.GetStringLength());
}
catch (const std::exception &)
{
// int x = 5;
}
catch (...)
{
// int y = 5;
}
return QString{};
}
};
} // namespace pajlada
+174
View File
@@ -0,0 +1,174 @@
#pragma once
#include <QStringList>
namespace chatterino {
QStringList getValidLinks()
{
return {
R"(http://github.com/)",
R"(https://github.com/)",
R"(http://username@github.com/)",
R"(https://username@github.com/)",
R"(http://pajlada.github.io)",
R"(https://pajlada.github.io)",
R"(http://pajlada.github.io/)",
R"(https://pajlada.github.io/)",
R"(http://github.com/some/random/path)",
R"(https://github.com/some/random/path)",
R"(http://github.com?query=value)",
R"(https://github.com?query=value)",
R"(http://github.com?query=value&abc=123)",
R"(https://github.com?query=value&abc=123)",
R"(http://github.com/?query=value&abc=123&yhf=abc_def)",
R"(http://github.com/?query=value&abc=123&yhf)",
R"(http://github.com?query=value&abc=)",
R"(http://github.com?query=value&abc=)",
R"(http://github.com/#block)",
R"(https://github.com/#anchor)",
R"(http://github.com/path/?qs=true#block)",
R"(https://github.com/path/?qs=true#anchor)",
R"(github.com/)",
R"(username@github.com/)",
R"(pajlada.github.io)",
R"(pajlada.github.io/)",
R"(github.com/some/random/path)",
R"(github.com?query=value)",
R"(github.com?query=value&abc=123)",
R"(github.com/?query=value&abc=123&yhf=abc_def)",
R"(github.com/?query=value&abc=123&yhf)",
R"(github.com?query=value&abc=)",
R"(github.com?query=value&abc=)",
R"(github.com/#block)",
R"(github.com/path/?qs=true#block)",
R"(HTTP://GITHUB.COM/)",
R"(HTTPS://GITHUB.COM/)",
R"(HTTP://USERNAME@GITHUB.COM/)",
R"(HTTPS://USERNAME@GITHUB.COM/)",
R"(HTTP://PAJLADA.GITHUB.IO)",
R"(HTTPS://PAJLADA.GITHUB.IO)",
R"(HTTP://PAJLADA.GITHUB.IO/)",
R"(HTTPS://PAJLADA.GITHUB.IO/)",
R"(HTTP://GITHUB.COM/SOME/RANDOM/PATH)",
R"(HTTPS://GITHUB.COM/SOME/RANDOM/PATH)",
R"(HTTP://GITHUB.COM?QUERY=VALUE)",
R"(HTTPS://GITHUB.COM?QUERY=VALUE)",
R"(HTTP://GITHUB.COM?QUERY=VALUE&ABC=123)",
R"(HTTPS://GITHUB.COM?QUERY=VALUE&ABC=123)",
R"(HTTP://GITHUB.COM/?QUERY=VALUE&ABC=123&YHF=ABC_DEF)",
R"(HTTP://GITHUB.COM/?QUERY=VALUE&ABC=123&YHF)",
R"(HTTP://GITHUB.COM?QUERY=VALUE&ABC=)",
R"(HTTP://GITHUB.COM?QUERY=VALUE&ABC=)",
R"(HTTP://GITHUB.COM/#BLOCK)",
R"(HTTPS://GITHUB.COM/#ANCHOR)",
R"(HTTP://GITHUB.COM/PATH/?QS=TRUE#BLOCK)",
R"(HTTPS://GITHUB.COM/PATH/?QS=TRUE#ANCHOR)",
R"(GITHUB.COM/)",
R"(USERNAME@GITHUB.COM/)",
R"(PAJLADA.GITHUB.IO)",
R"(PAJLADA.GITHUB.IO/)",
R"(GITHUB.COM/SOME/RANDOM/PATH)",
R"(GITHUB.COM?QUERY=VALUE)",
R"(GITHUB.COM?QUERY=VALUE&ABC=123)",
R"(GITHUB.COM/?QUERY=VALUE&ABC=123&YHF=ABC_DEF)",
R"(GITHUB.COM/?QUERY=VALUE&ABC=123&YHF)",
R"(GITHUB.COM?QUERY=VALUE&ABC=)",
R"(GITHUB.COM?QUERY=VALUE&ABC=)",
R"(GITHUB.COM/#BLOCK)",
R"(GITHUB.COM/PATH/?QS=TRUE#BLOCK)",
R"(http://foo.com/blah_blah)",
R"(http://foo.com/blah_blah/)",
R"(http://foo.com/blah_blah_(wikipedia))",
R"(http://foo.com/blah_blah_(wikipedia)_(again))",
R"(http://www.example.com/wpstyle/?p=364)",
R"(https://www.example.com/foo/?bar=baz&inga=42&quux)",
R"(http://✪df.ws/123)",
R"(http://userid@example.com)",
R"(http://userid@example.com/)",
R"(http://userid@example.com:8080)",
R"(http://userid@example.com:8080/)",
R"(http://142.42.1.1/)",
R"(http://142.42.1.1:8080/)",
R"(http://➡.ws/䨹)",
R"(http://⌘.ws)",
R"(http://⌘.ws/)",
R"(http://foo.com/blah_(wikipedia)#cite-1)",
R"(http://foo.com/blah_(wikipedia)_blah#cite-1)",
R"(http://foo.com/unicode_(✪)_in_parens)",
R"(http://foo.com/(something)?after=parens)",
R"(http://☺.damowmow.com/)",
R"(http://code.google.com/events/#&product=browser)",
R"(http://j.mp)",
R"(ftp://foo.bar/baz)",
R"(http://foo.bar/?q=Test%20URL-encoded%20stuff)",
R"(http://مثال.إختبار)",
R"(http://例子.测试)",
R"(http://उदाहरण.परीक्षा)",
R"(http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com)",
R"(http://1337.net)",
R"(http://a.b-c.de)",
R"(http://223.255.255.254)",
};
}
QStringList getValidButIgnoredLinks()
{
return {
R"(http://username:password@github.com/)",
R"(https://username:password@github.com/)",
R"(http://userid:password@example.com)",
R"(http://userid:password@example.com/)",
R"(http://userid:password@example.com:8080)",
R"(http://userid:password@example.com:8080/)",
};
}
QStringList getInvalidLinks()
{
return {
R"(1.40)",
R"(test..)",
R"(test.)",
R"(http://)",
R"(http://.)",
R"(http://..)",
R"(http://../)",
R"(http://?)",
R"(http://??)",
R"(http://??/)",
R"(http://#)",
R"(http://##)",
R"(http://##/)",
R"(http://foo.bar?q=Spaces should be encoded)",
R"(//)",
R"(//a)",
R"(///a)",
R"(///)",
R"(http:///a)",
R"(foo.com)",
R"(rdar://1234)",
R"(h://test)",
R"(http:// shouldfail.com)",
R"(:// should fail)",
R"(http://foo.bar/foo(bar)baz quux)",
R"(ftps://foo.bar/)",
R"(http://-error-.invalid/)",
R"(http://a.b--c.de/)",
R"(http://-a.b.co)",
R"(http://a.b-.co)",
R"(http://0.0.0.0)",
R"(http://10.1.1.0)",
R"(http://10.1.1.255)",
R"(http://224.1.1.1)",
R"(http://1.1.1.1.1)",
R"(http://123.123.123)",
R"(http://3628126748)",
R"(http://.www.foo.bar/)",
R"(http://www.foo.bar./)",
R"(http://.www.foo.bar./)",
R"(http://10.1.1.1)",
};
}
} // namespace chatterino
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <QShortcut>
#include <QWidget>
namespace chatterino {
template <typename WidgetType, typename Func>
inline void createShortcut(WidgetType *w, const char *key, Func func)
{
auto s = new QShortcut(QKeySequence(key), w);
s->setContext(Qt::WidgetWithChildrenShortcut);
QObject::connect(s, &QShortcut::activated, w, func);
}
template <typename WidgetType, typename Func>
inline void createWindowShortcut(WidgetType *w, const char *key, Func func)
{
auto s = new QShortcut(QKeySequence(key), w);
s->setContext(Qt::WindowShortcut);
QObject::connect(s, &QShortcut::activated, w, func);
}
} // namespace chatterino
+1 -1
View File
@@ -199,7 +199,7 @@ void openStreamlinkForChannel(const QString &channel)
{
QString channelURL = "twitch.tv/" + channel;
QString preferredQuality = getSettings()->preferredQuality;
QString preferredQuality = getSettings()->preferredQuality.getValue();
preferredQuality = preferredQuality.toLower();
if (preferredQuality == "choose")
+86
View File
@@ -0,0 +1,86 @@
#include "WindowsHelper.hpp"
#include <QSettings>
#ifdef USEWINSDK
namespace chatterino {
typedef enum MONITOR_DPI_TYPE {
MDT_EFFECTIVE_DPI = 0,
MDT_ANGULAR_DPI = 1,
MDT_RAW_DPI = 2,
MDT_DEFAULT = MDT_EFFECTIVE_DPI
} MONITOR_DPI_TYPE;
typedef HRESULT(CALLBACK *GetDpiForMonitor_)(HMONITOR, MONITOR_DPI_TYPE, UINT *,
UINT *);
boost::optional<UINT> getWindowDpi(HWND hwnd)
{
static HINSTANCE shcore = LoadLibrary(L"Shcore.dll");
if (shcore != nullptr)
{
if (auto getDpiForMonitor =
GetDpiForMonitor_(GetProcAddress(shcore, "GetDpiForMonitor")))
{
HMONITOR monitor =
MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
UINT xScale, yScale;
getDpiForMonitor(monitor, MDT_DEFAULT, &xScale, &yScale);
return xScale;
}
}
return boost::none;
}
typedef HRESULT(CALLBACK *OleFlushClipboard_)();
void flushClipboard()
{
static HINSTANCE ole32 = LoadLibrary(L"Ole32.dll");
if (ole32 != nullptr)
{
if (auto oleFlushClipboard =
OleFlushClipboard_(GetProcAddress(ole32, "OleFlushClipboard")))
{
oleFlushClipboard();
}
}
}
constexpr const char *runKey =
"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
bool isRegisteredForStartup()
{
QSettings settings(runKey, QSettings::NativeFormat);
return !settings.value("Chatterino").toString().isEmpty();
}
void setRegisteredForStartup(bool isRegistered)
{
QSettings settings(runKey, QSettings::NativeFormat);
if (isRegistered)
{
auto exePath = QFileInfo(QCoreApplication::applicationFilePath())
.absoluteFilePath()
.replace('/', '\\');
settings.setValue("Chatterino", "\"" + exePath + "\" --autorun");
}
else
{
settings.remove("Chatterino");
}
}
} // namespace chatterino
#endif
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#ifdef USEWINSDK
# include <Windows.h>
# include <boost/optional.hpp>
namespace chatterino {
boost::optional<UINT> getWindowDpi(HWND hwnd);
void flushClipboard();
bool isRegisteredForStartup();
void setRegisteredForStartup(bool isRegistered);
} // namespace chatterino
#endif