Merge branch 'master' into logging

This commit is contained in:
pajlada
2018-06-05 13:16:20 +02:00
committed by GitHub
172 changed files with 5997 additions and 3348 deletions
-12
View File
@@ -1,12 +0,0 @@
#include "singletons/accountmanager.hpp"
namespace chatterino {
namespace singletons {
void AccountManager::load()
{
this->Twitch.load();
}
} // namespace singletons
} // namespace chatterino
-21
View File
@@ -1,21 +0,0 @@
#pragma once
#include "providers/twitch/twitchaccountmanager.hpp"
namespace chatterino {
namespace singletons {
class AccountManager
{
public:
AccountManager() = default;
~AccountManager() = delete;
void load();
providers::twitch::TwitchAccountManager Twitch;
};
} // namespace singletons
} // namespace chatterino
+41 -10
View File
@@ -86,8 +86,8 @@ EmoteManager::EmoteManager()
void EmoteManager::initialize()
{
getApp()->accounts->Twitch.currentUserChanged.connect([this] {
auto currentUser = getApp()->accounts->Twitch.getCurrent();
getApp()->accounts->twitch.currentUserChanged.connect([this] {
auto currentUser = getApp()->accounts->twitch.getCurrent();
assert(currentUser);
this->refreshTwitchEmotes(currentUser);
});
@@ -130,13 +130,23 @@ void EmoteManager::reloadBTTVChannelEmotes(const QString &channelName,
QString code = emoteObject.value("code").toString();
// emoteObject.value("imageType").toString();
QString link = linkTemplate;
link.detach();
auto emote = this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [&] {
util::EmoteData emoteData;
QString link = linkTemplate;
link.detach();
emoteData.image1x = new Image(link.replace("{{id}}", id).replace("{{image}}", "1x"),
1, code, code + "<br />Channel BTTV Emote");
link = linkTemplate;
link.detach();
emoteData.image2x = new Image(link.replace("{{id}}", id).replace("{{image}}", "2x"),
0.5, code, code + "<br />Channel BTTV Emote");
link = linkTemplate;
link.detach();
emoteData.image3x = new Image(link.replace("{{id}}", id).replace("{{image}}", "3x"),
0.25, code, code + "<br />Channel BTTV Emote");
emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id;
link = link.replace("{{id}}", id).replace("{{image}}", "1x");
auto emote = this->getBTTVChannelEmoteFromCaches().getOrAdd(id, [&code, &link] {
return util::EmoteData(new Image(link, 1, code, code + "<br/>Channel BTTV Emote"));
return emoteData;
});
this->bttvChannelEmotes.insert(code, emote);
@@ -182,9 +192,11 @@ void EmoteManager::reloadFFZChannelEmotes(const QString &channelName,
QJsonObject urls = emoteObject.value("urls").toObject();
auto emote = this->getFFZChannelEmoteFromCaches().getOrAdd(id, [&code, &urls] {
auto emote = this->getFFZChannelEmoteFromCaches().getOrAdd(id, [id, &code, &urls] {
util::EmoteData emoteData;
FillInFFZEmoteData(urls, code, code + "<br/>Channel FFZ Emote", emoteData);
emoteData.pageLink =
QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code);
return emoteData;
});
@@ -436,6 +448,7 @@ void EmoteManager::refreshTwitchEmotes(const std::shared_ptr<TwitchAccount> &use
[=, &emoteData](const QJsonObject &root) {
emoteData.emoteSets.clear();
emoteData.emoteCodes.clear();
auto emoticonSets = root.value("emoticon_sets").toObject();
for (QJsonObject::iterator it = emoticonSets.begin(); it != emoticonSets.end(); ++it) {
std::string emoteSetString = it.key().toStdString();
@@ -443,7 +456,7 @@ void EmoteManager::refreshTwitchEmotes(const std::shared_ptr<TwitchAccount> &use
for (QJsonValue emoteValue : emoteSetList) {
QJsonObject emoticon = emoteValue.toObject();
std::string id = emoticon["id"].toString().toStdString();
std::string id = QString::number(emoticon["id"].toInt()).toStdString();
std::string code = emoticon["code"].toString().toStdString();
emoteData.emoteSets[emoteSetString].push_back({id, code});
emoteData.emoteCodes.push_back(code);
@@ -483,6 +496,7 @@ void EmoteManager::loadBTTVEmotes()
code + "<br />Global BTTV Emote");
emoteData.image3x = new Image(GetBTTVEmoteLink(urlTemplate, id, "3x"), 0.25, code,
code + "<br />Global BTTV Emote");
emoteData.pageLink = "https://manage.betterttv.net/emotes/" + id;
this->bttvGlobalEmotes.insert(code, emoteData);
codes.push_back(code.toStdString());
@@ -510,10 +524,13 @@ void EmoteManager::loadFFZEmotes()
QJsonObject object = emote.toObject();
QString code = object.value("name").toString();
int id = object.value("id").toInt();
QJsonObject urls = object.value("urls").toObject();
util::EmoteData emoteData;
FillInFFZEmoteData(urls, code, code + "<br/>Global FFZ Emote", emoteData);
emoteData.pageLink =
QString("https://www.frankerfacez.com/emoticon/%1-%2").arg(id).arg(code);
this->ffzGlobalEmotes.insert(code, emoteData);
codes.push_back(code.toStdString());
@@ -530,6 +547,20 @@ util::EmoteData EmoteManager::getTwitchEmoteById(long id, const QString &emoteNa
{
QString _emoteName = emoteName;
_emoteName.replace("<", "&lt;");
_emoteName.replace(">", "&gt;");
static QMap<QString, QString> emoteNameReplacements{
{"[oO](_|\\.)[oO]", "O_o"}, {"\\&gt\\;\\(", "&gt;("}, {"\\&lt\\;3", "&lt;3"},
{"\\:-?(o|O)", ":O"}, {"\\:-?(p|P)", ":P"}, {"\\:-?[\\\\/]", ":/"},
{"\\:-?[z|Z|\\|]", ":Z"}, {"\\:-?\\(", ":("}, {"\\:-?\\)", ":)"},
{"\\:-?D", ":D"}, {"\\;-?(p|P)", ";P"}, {"\\;-?\\)", ";)"},
{"R-?\\)", "R)"}, {"B-?\\)", "B)"},
};
auto it = emoteNameReplacements.find(_emoteName);
if (it != emoteNameReplacements.end()) {
_emoteName = it.value();
}
return _twitchEmoteFromCache.getOrAdd(id, [&emoteName, &_emoteName, &id] {
util::EmoteData newEmoteData;
-12
View File
@@ -46,16 +46,6 @@ public:
util::EmoteData getTwitchEmoteById(long int id, const QString &emoteName);
int getGeneration()
{
return _generation;
}
void incGeneration()
{
_generation++;
}
pajlada::Signals::NoArgSignal &getGifUpdateSignal();
// Bit badge/emotes?
@@ -145,8 +135,6 @@ private:
pajlada::Signals::NoArgSignal gifUpdateTimerSignal;
QTimer gifUpdateTimer;
bool gifUpdateTimerInitiated = false;
int _generation = 0;
};
} // namespace singletons
+103 -74
View File
@@ -3,6 +3,10 @@
#include <QDebug>
#include <QtGlobal>
#include "application.hpp"
#include "util/assertinguithread.hpp"
#include "windowmanager.hpp"
#ifdef Q_OS_WIN32
#define DEFAULT_FONT_FAMILY "Segoe UI"
#define DEFAULT_FONT_SIZE 10
@@ -20,86 +24,111 @@ namespace chatterino {
namespace singletons {
FontManager::FontManager()
: currentFontFamily("/appearance/currentFontFamily", DEFAULT_FONT_FAMILY)
, currentFontSize("/appearance/currentFontSize", DEFAULT_FONT_SIZE)
// , currentFont(this->currentFontFamily.getValue().c_str(), currentFontSize.getValue())
: chatFontFamily("/appearance/currentFontFamily", DEFAULT_FONT_FAMILY)
, chatFontSize("/appearance/currentFontSize", DEFAULT_FONT_SIZE)
{
qDebug() << "init FontManager";
this->currentFontFamily.connect([this](const std::string &newValue, auto) {
this->incGeneration();
// this->currentFont.setFamily(newValue.c_str());
this->currentFontByScale.clear();
this->fontChanged.invoke();
});
this->chatFontFamily.connect([this](const std::string &, auto) {
util::assertInGuiThread();
this->currentFontSize.connect([this](const int &newValue, auto) {
this->incGeneration();
// this->currentFont.setSize(newValue);
this->currentFontByScale.clear();
this->fontChanged.invoke();
});
}
QFont &FontManager::getFont(FontManager::Type type, float scale)
{
// return this->currentFont.getFont(type);
return this->getCurrentFont(scale).getFont(type);
}
QFontMetrics &FontManager::getFontMetrics(FontManager::Type type, float scale)
{
// return this->currentFont.getFontMetrics(type);
return this->getCurrentFont(scale).getFontMetrics(type);
}
FontManager::FontData &FontManager::Font::getFontData(FontManager::Type type)
{
switch (type) {
case Tiny:
return this->tiny;
case Small:
return this->small;
case MediumSmall:
return this->mediumSmall;
case Medium:
return this->medium;
case MediumBold:
return this->mediumBold;
case MediumItalic:
return this->mediumItalic;
case Large:
return this->large;
case VeryLarge:
return this->veryLarge;
default:
qDebug() << "Unknown font type:" << type << ", defaulting to medium";
return this->medium;
}
}
QFont &FontManager::Font::getFont(Type type)
{
return this->getFontData(type).font;
}
QFontMetrics &FontManager::Font::getFontMetrics(Type type)
{
return this->getFontData(type).metrics;
}
FontManager::Font &FontManager::getCurrentFont(float scale)
{
for (auto it = this->currentFontByScale.begin(); it != this->currentFontByScale.end(); it++) {
if (it->first == scale) {
return it->second;
if (getApp()->windows) {
getApp()->windows->incGeneration();
}
}
this->currentFontByScale.push_back(
std::make_pair(scale, Font(this->currentFontFamily.getValue().c_str(),
this->currentFontSize.getValue() * scale)));
return this->currentFontByScale.back().second;
for (auto &map : this->fontsByType) {
map.clear();
}
this->fontChanged.invoke();
});
this->chatFontSize.connect([this](const int &, auto) {
util::assertInGuiThread();
if (getApp()->windows) {
getApp()->windows->incGeneration();
}
for (auto &map : this->fontsByType) {
map.clear();
}
this->fontChanged.invoke();
});
this->fontsByType.resize(size_t(EndType));
}
QFont FontManager::getFont(FontManager::Type type, float scale)
{
return this->getOrCreateFontData(type, scale).font;
}
QFontMetrics FontManager::getFontMetrics(FontManager::Type type, float scale)
{
return this->getOrCreateFontData(type, scale).metrics;
}
FontManager::FontData &FontManager::getOrCreateFontData(Type type, float scale)
{
util::assertInGuiThread();
assert(type >= 0 && type < EndType);
auto &map = this->fontsByType[size_t(type)];
// find element
auto it = map.find(scale);
if (it != map.end()) {
// return if found
return it->second;
}
// emplace new element
auto result = map.emplace(scale, this->createFontData(type, scale));
assert(result.second);
return result.first->second;
}
FontManager::FontData FontManager::createFontData(Type type, float scale)
{
// check if it's a chat (scale the setting)
if (type >= ChatStart && type <= ChatEnd) {
static std::unordered_map<Type, ChatFontData> sizeScale{
{ChatSmall, {0.6f, false, QFont::Normal}},
{ChatMediumSmall, {0.8f, false, QFont::Normal}},
{ChatMedium, {1, false, QFont::Normal}},
{ChatMediumBold, {1, false, QFont::Medium}},
{ChatMediumItalic, {1, true, QFont::Normal}},
{ChatLarge, {1.2f, false, QFont::Normal}},
{ChatVeryLarge, {1.4f, false, QFont::Normal}},
};
auto data = sizeScale[type];
return FontData(QFont(QString::fromStdString(this->chatFontFamily.getValue()),
int(this->chatFontSize.getValue() * data.scale * scale), data.weight,
data.italic));
}
// normal Ui font (use pt size)
{
#ifdef Q_OS_MAC
constexpr float multiplier = 0.8f;
#else
constexpr float multiplier = 1.f;
#endif
static std::unordered_map<Type, UiFontData> defaultSize{
{Tiny, {8, "Monospace", false, QFont::Normal}},
{UiMedium, {int(12 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
{UiTabs, {int(9 * multiplier), DEFAULT_FONT_FAMILY, false, QFont::Normal}},
};
UiFontData &data = defaultSize[type];
QFont font(data.name, int(data.size * scale), data.weight, data.italic);
return FontData(font);
}
}
} // namespace singletons
+43 -102
View File
@@ -3,138 +3,79 @@
#include <QFont>
#include <QFontDatabase>
#include <QFontMetrics>
#include <array>
#include <boost/noncopyable.hpp>
#include <pajlada/settings/setting.hpp>
#include <pajlada/signals/signal.hpp>
#include <unordered_map>
namespace chatterino {
namespace singletons {
class FontManager
class FontManager : boost::noncopyable
{
public:
FontManager();
FontManager(const FontManager &) = delete;
FontManager(FontManager &&) = delete;
~FontManager() = delete;
// font data gets set in createFontData(...)
enum Type : uint8_t {
Tiny,
Small,
MediumSmall,
Medium,
MediumBold,
MediumItalic,
Large,
VeryLarge,
ChatSmall,
ChatMediumSmall,
ChatMedium,
ChatMediumBold,
ChatMediumItalic,
ChatLarge,
ChatVeryLarge,
UiMedium,
UiTabs,
// don't remove this value
EndType,
// make sure to update these values accordingly!
ChatStart = ChatSmall,
ChatEnd = ChatVeryLarge,
};
QFont &getFont(Type type, float scale);
QFontMetrics &getFontMetrics(Type type, float scale);
QFont getFont(Type type, float scale);
QFontMetrics getFontMetrics(Type type, float scale);
int getGeneration() const
{
return this->generation;
}
void incGeneration()
{
this->generation++;
}
pajlada::Settings::Setting<std::string> currentFontFamily;
pajlada::Settings::Setting<int> currentFontSize;
pajlada::Settings::Setting<std::string> chatFontFamily;
pajlada::Settings::Setting<int> chatFontSize;
pajlada::Signals::NoArgSignal fontChanged;
private:
struct FontData {
FontData(QFont &&_font)
FontData(const QFont &_font)
: font(_font)
, metrics(this->font)
, metrics(_font)
{
}
QFont font;
QFontMetrics metrics;
const QFont font;
const QFontMetrics metrics;
};
struct Font {
Font() = delete;
Font(const char *fontFamilyName, int mediumSize)
: tiny(QFont("Monospace", 8))
, small(QFont(fontFamilyName, mediumSize - 4))
, mediumSmall(QFont(fontFamilyName, mediumSize - 2))
, medium(QFont(fontFamilyName, mediumSize))
, mediumBold(QFont(fontFamilyName, mediumSize, QFont::DemiBold))
, mediumItalic(QFont(fontFamilyName, mediumSize, -1, true))
, large(QFont(fontFamilyName, mediumSize))
, veryLarge(QFont(fontFamilyName, mediumSize))
{
tiny.font.setStyleHint(QFont::TypeWriter);
}
void setFamily(const char *newFamily)
{
this->small.font.setFamily(newFamily);
this->mediumSmall.font.setFamily(newFamily);
this->medium.font.setFamily(newFamily);
this->mediumBold.font.setFamily(newFamily);
this->mediumItalic.font.setFamily(newFamily);
this->large.font.setFamily(newFamily);
this->veryLarge.font.setFamily(newFamily);
this->updateMetrics();
}
void setSize(int newMediumSize)
{
this->small.font.setPointSize(newMediumSize - 4);
this->mediumSmall.font.setPointSize(newMediumSize - 2);
this->medium.font.setPointSize(newMediumSize);
this->mediumBold.font.setPointSize(newMediumSize);
this->mediumItalic.font.setPointSize(newMediumSize);
this->large.font.setPointSize(newMediumSize + 2);
this->veryLarge.font.setPointSize(newMediumSize + 4);
this->updateMetrics();
}
void updateMetrics()
{
this->small.metrics = QFontMetrics(this->small.font);
this->mediumSmall.metrics = QFontMetrics(this->mediumSmall.font);
this->medium.metrics = QFontMetrics(this->medium.font);
this->mediumBold.metrics = QFontMetrics(this->mediumBold.font);
this->mediumItalic.metrics = QFontMetrics(this->mediumItalic.font);
this->large.metrics = QFontMetrics(this->large.font);
this->veryLarge.metrics = QFontMetrics(this->veryLarge.font);
}
FontData &getFontData(Type type);
QFont &getFont(Type type);
QFontMetrics &getFontMetrics(Type type);
FontData tiny;
FontData small;
FontData mediumSmall;
FontData medium;
FontData mediumBold;
FontData mediumItalic;
FontData large;
FontData veryLarge;
struct ChatFontData {
float scale;
bool italic;
QFont::Weight weight;
};
Font &getCurrentFont(float scale);
struct UiFontData {
float size;
const char *name;
bool italic;
QFont::Weight weight;
};
// Future plans:
// Could have multiple fonts in here, such as "Menu font", "Application font", "Chat font"
FontData &getOrCreateFontData(Type type, float scale);
FontData createFontData(Type type, float scale);
std::list<std::pair<float, Font>> currentFontByScale;
int generation = 0;
std::vector<std::unordered_map<float, FontData>> fontsByType;
};
} // namespace singletons
+35 -15
View File
@@ -31,6 +31,7 @@ namespace ipc = boost::interprocess;
namespace chatterino {
namespace singletons {
// fourtf: don't add this class to the application class
NativeMessagingManager::NativeMessagingManager()
{
qDebug() << "init NativeMessagingManager";
@@ -50,6 +51,10 @@ void NativeMessagingManager::registerHost()
{
auto app = getApp();
if (app->paths->isPortable()) {
return;
}
// create manifest
QJsonDocument document;
QJsonObject root_obj;
@@ -113,13 +118,14 @@ void NativeMessagingManager::ReceiverThread::run()
while (true) {
try {
char *buf = (char *)malloc(MESSAGE_SIZE);
std::unique_ptr<char> buf(static_cast<char *>(malloc(MESSAGE_SIZE)));
ipc::message_queue::size_type retSize;
unsigned int priority;
messageQueue.receive(buf, MESSAGE_SIZE, retSize, priority);
messageQueue.receive(buf.get(), MESSAGE_SIZE, retSize, priority);
QJsonDocument document = QJsonDocument::fromJson(QByteArray(buf, retSize));
QJsonDocument document =
QJsonDocument::fromJson(QByteArray::fromRawData(buf.get(), retSize));
this->handleMessage(document.object());
} catch (ipc::interprocess_exception &ex) {
@@ -143,26 +149,37 @@ void NativeMessagingManager::ReceiverThread::handleMessage(const QJsonObject &ro
QString _type = root.value("type").toString();
bool attach = root.value("attach").toBool();
QString name = root.value("name").toString();
QString winId = root.value("winId").toString();
int yOffset = root.value("yOffset").toInt(-1);
if (_type.isNull() || name.isNull() || winId.isNull()) {
#ifdef USEWINSDK
widgets::AttachedWindow::GetArgs args;
args.winId = root.value("winId").toString();
args.yOffset = root.value("yOffset").toInt(-1);
args.width = root.value("size").toObject().value("width").toInt(-1);
args.height = root.value("size").toObject().value("height").toInt(-1);
if (_type.isNull() || args.winId.isNull()) {
qDebug() << "NM type, name or winId missing";
attach = false;
return;
}
#endif
if (_type == "twitch") {
util::postToThread([name, attach, winId, yOffset, app] {
app->twitch.server->watchingChannel.update(
app->twitch.server->getOrAddChannel(name));
util::postToThread([=] {
if (!name.isEmpty()) {
app->twitch.server->watchingChannel.update(
app->twitch.server->getOrAddChannel(name));
}
if (attach) {
#ifdef USEWINSDK
auto *window =
widgets::AttachedWindow::get(::GetForegroundWindow(), winId, yOffset);
window->setChannel(app->twitch.server->getOrAddChannel(name));
window->show();
if (args.height != -1) {
auto *window = widgets::AttachedWindow::get(::GetForegroundWindow(), args);
if (!name.isEmpty()) {
window->setChannel(app->twitch.server->getOrAddChannel(name));
}
}
// window->show();
#endif
}
});
@@ -179,12 +196,15 @@ void NativeMessagingManager::ReceiverThread::handleMessage(const QJsonObject &ro
}
#ifdef USEWINSDK
util::postToThread([winId] { widgets::AttachedWindow::detach(winId); });
util::postToThread([winId] {
qDebug() << "NW detach";
widgets::AttachedWindow::detach(winId);
});
#endif
} else {
qDebug() << "NM unknown action " + action;
}
}
} // namespace singletons
} // namespace singletons
} // namespace chatterino
@@ -8,6 +8,7 @@ namespace singletons {
class NativeMessagingManager
{
public:
// fourtf: don't add this class to the application class
NativeMessagingManager();
~NativeMessagingManager() = delete;
+10 -5
View File
@@ -19,21 +19,21 @@ PathManager::PathManager(int argc, char **argv)
.replace("/", "x");
// Options
bool portable = false;
this->portable = false;
for (int i = 1; i < argc; ++i) {
if (strcmp(argv[i], "portable") == 0) {
portable = true;
this->portable = true;
}
}
if (QFileInfo::exists(QCoreApplication::applicationDirPath() + "/portable")) {
portable = true;
if (QFileInfo::exists(QCoreApplication::applicationDirPath() + "/this->portable")) {
this->portable = true;
}
// Root path = %APPDATA%/Chatterino or the folder that the executable resides in
QString rootPath;
if (portable) {
if (this->portable) {
rootPath.append(QCoreApplication::applicationDirPath());
} else {
// Get settings path
@@ -91,5 +91,10 @@ bool PathManager::createFolder(const QString &folderPath)
return QDir().mkpath(folderPath);
}
bool PathManager::isPortable()
{
return this->portable;
}
} // namespace singletons
} // namespace chatterino
+4
View File
@@ -28,6 +28,10 @@ public:
QString appPathHash;
bool createFolder(const QString &folderPath);
bool isPortable();
private:
bool portable;
};
} // namespace singletons
+1
View File
@@ -2,6 +2,7 @@
#include "util/emotemap.hpp"
#include <QIcon>
#include <QRegularExpression>
#include <map>
+11 -1
View File
@@ -39,8 +39,18 @@ void SettingManager::initialize()
this->timestampFormat.connect([](auto, auto) {
auto app = getApp();
app->windows->layoutVisibleChatWidgets();
app->windows->layoutChannelViews();
});
this->emoteScale.connect([](auto, auto) { getApp()->windows->forceLayoutChannelViews(); });
this->timestampFormat.connect([](auto, auto) { getApp()->windows->forceLayoutChannelViews(); });
this->alternateMessageBackground.connect(
[](auto, auto) { getApp()->windows->forceLayoutChannelViews(); });
this->seperateMessages.connect(
[](auto, auto) { getApp()->windows->forceLayoutChannelViews(); });
this->collpseMessagesMinLines.connect(
[](auto, auto) { getApp()->windows->forceLayoutChannelViews(); });
}
MessageElement::Flags SettingManager::getWordFlags()
+8 -4
View File
@@ -40,6 +40,8 @@ public:
BoolSetting hideEmptyInput = {"/appearance/hideEmptyInputBox", false};
BoolSetting showMessageLength = {"/appearance/messages/showMessageLength", false};
BoolSetting seperateMessages = {"/appearance/messages/separateMessages", false};
// BoolSetting collapseLongMessages = {"/appearance/messages/collapseLongMessages", false};
IntSetting collpseMessagesMinLines = {"/appearance/messages/collapseMessagesMinLines", 0};
BoolSetting alternateMessageBackground = {"/appearance/messages/alternateMessageBackground",
false};
BoolSetting windowTopMost = {"/appearance/windowAlwaysOnTop", false};
@@ -54,6 +56,8 @@ public:
/// Behaviour
BoolSetting allowDuplicateMessages = {"/behaviour/allowDuplicateMessages", true};
BoolSetting mentionUsersWithAt = {"/behaviour/mentionUsersWithAt", false};
BoolSetting showJoins = {"/behaviour/showJoins", false};
BoolSetting showParts = {"/behaviour/showParts", false};
FloatSetting mouseScrollMultiplier = {"/behaviour/mouseScrollMultiplier", 1.0};
// Auto-completion
@@ -75,10 +79,10 @@ public:
BoolSetting enableGifAnimations = {"/emotes/enableGifAnimations", true};
FloatSetting emoteScale = {"/emotes/scale", 1.f};
// 0 = Smallest size
// 1 = One size above 0 (usually size of 0 * 2)
// 2 = One size above 1 (usually size of 1 * 2)
// etc...
// 0 = No preference
// 1 = 1x
// 2 = 2x
// 3 = 3x
IntSetting preferredEmoteQuality = {"/emotes/preferredEmoteQuality", 0};
/// Links
+66 -32
View File
@@ -52,7 +52,6 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
isLight = multiplier > 0;
bool lightWin = isLight;
QColor none(0, 0, 0, 0);
QColor themeColor = QColor::fromHslF(hue, 0.43, 0.5);
QColor themeColorNoSat = QColor::fromHslF(hue, 0, 0.5);
@@ -69,7 +68,7 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
#ifdef Q_OS_LINUX
this->window.background = lightWin ? "#fff" : QColor(61, 60, 56);
#else
this->window.background = lightWin ? "#fff" : "#444";
this->window.background = lightWin ? "#fff" : "#111";
#endif
QColor fg = this->window.text = lightWin ? "#000" : "#eee";
@@ -89,27 +88,52 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
/// TABS
if (lightWin) {
this->tabs.regular = {fg, {bg, QColor("#ccc"), bg}};
this->tabs.regular = {QColor("#444"),
{QColor("#fff"), QColor("#fff"), QColor("#fff")},
{QColor("#fff"), QColor("#fff"), QColor("#fff")}};
this->tabs.newMessage = {
fg,
{QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern),
QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern),
QBrush(blendColors(themeColorNoSat, "#ccc", 0.9), Qt::FDiagPattern)}};
this->tabs.highlighted = {fg, {QColor("#ccc"), QColor("#ccc"), QColor("#bbb")}};
this->tabs.selected = {QColor("#fff"),
{QColor("#777"), QColor("#777"), QColor("#888")}};
} else {
this->tabs.regular = {fg, {bg, QColor("#555"), bg}};
this->tabs.newMessage = {
fg,
{QBrush(blendColors(themeColor, "#666", 0.7), Qt::FDiagPattern),
QBrush(blendColors(themeColor, "#666", 0.5), Qt::FDiagPattern),
QBrush(blendColors(themeColorNoSat, "#666", 0.7), Qt::FDiagPattern)}};
this->tabs.highlighted = {fg, {QColor("#777"), QColor("#777"), QColor("#666")}};
fg, {bg, QColor("#ccc"), bg}, {QColor("#aaa"), QColor("#aaa"), QColor("#aaa")}};
this->tabs.highlighted = {fg,
{bg, QColor("#ccc"), bg},
{QColor("#b60505"), QColor("#b60505"), QColor("#b60505")}};
this->tabs.selected = {QColor("#000"),
{QColor("#999"), QColor("#999"), QColor("#888")}};
{QColor("#b4d7ff"), QColor("#b4d7ff"), QColor("#b4d7ff")},
{QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}};
} else {
this->tabs.regular = {QColor("#aaa"),
{QColor("#252525"), QColor("#252525"), QColor("#252525")},
{QColor("#444"), QColor("#444"), QColor("#444")}};
this->tabs.newMessage = {fg,
{QColor("#252525"), QColor("#252525"), QColor("#252525")},
{QColor("#888"), QColor("#888"), QColor("#888")}};
this->tabs.highlighted = {fg,
{QColor("#252525"), QColor("#252525"), QColor("#252525")},
{QColor("#ee6166"), QColor("#ee6166"), QColor("#ee6166")}};
this->tabs.selected = {QColor("#fff"),
{QColor("#555555"), QColor("#555555"), QColor("#555555")},
{QColor("#00aeef"), QColor("#00aeef"), QColor("#00aeef")}};
}
// scrollbar
this->scrollbars.highlights.highlight = QColor("#ee6166");
this->scrollbars.highlights.subscription = QColor("#C466FF");
// this->tabs.newMessage = {
// fg,
// {QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern),
// QBrush(blendColors(themeColor, "#ccc", 0.9), Qt::FDiagPattern),
// QBrush(blendColors(themeColorNoSat, "#ccc", 0.9), Qt::FDiagPattern)}};
// this->tabs.newMessage = {
// fg,
// {QBrush(blendColors(themeColor, "#666", 0.7), Qt::FDiagPattern),
// QBrush(blendColors(themeColor, "#666", 0.5), Qt::FDiagPattern),
// QBrush(blendColors(themeColorNoSat, "#666", 0.7),
// Qt::FDiagPattern)}};
// this->tabs.highlighted = {fg, {QColor("#777"), QColor("#777"),
// QColor("#666")}};
this->tabs.bottomLine = this->tabs.selected.backgrounds.regular.color();
}
@@ -119,7 +143,12 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
this->splits.messageSeperator = isLight ? QColor(127, 127, 127) : QColor(60, 60, 60);
this->splits.background = getColor(0, sat, 1);
this->splits.dropPreview = QColor(0, 148, 255, 0x30);
this->splits.dropPreviewBorder = QColor(0, 148, 255, 0x70);
this->splits.dropPreviewBorder = QColor(0, 148, 255, 0xff);
this->splits.dropTargetRect = QColor(0, 148, 255, 0x00);
this->splits.dropTargetRectBorder = QColor(0, 148, 255, 0x00);
this->splits.resizeHandle = QColor(0, 148, 255, 0x70);
this->splits.resizeHandleBackground = QColor(0, 148, 255, 0x20);
// this->splits.border
// this->splits.borderFocused
@@ -143,7 +172,10 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
this->messages.backgrounds.regular = splits.background;
this->messages.backgrounds.alternate = getColor(0, sat, 0.93);
this->messages.backgrounds.highlighted =
blendColors(themeColor, this->messages.backgrounds.regular, 0.8);
blendColors(themeColor, this->messages.backgrounds.regular, 0.6);
this->messages.backgrounds.subscription =
blendColors(QColor("#C466FF"), this->messages.backgrounds.regular, 0.7);
// this->messages.backgrounds.resub
// this->messages.backgrounds.whisper
this->messages.disabled = getColor(0, sat, 1, 0.6);
@@ -151,7 +183,9 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
// this->messages.seperatorInner =
// Scrollbar
this->scrollbars.background = getColor(0, sat, 0.94);
this->scrollbars.background = QColor(0, 0, 0, 0);
// this->scrollbars.background = splits.background;
// this->scrollbars.background.setAlphaF(qreal(0.2));
this->scrollbars.thumb = getColor(0, sat, 0.80);
this->scrollbars.thumbSelected = getColor(0, sat, 0.7);
@@ -163,13 +197,13 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
this->messages.selection = isLightTheme() ? QColor(0, 0, 0, 64) : QColor(255, 255, 255, 64);
this->updated.invoke();
}
} // namespace singletons
QColor ThemeManager::blendColors(const QColor &color1, const QColor &color2, qreal ratio)
{
int r = color1.red() * (1 - ratio) + color2.red() * ratio;
int g = color1.green() * (1 - ratio) + color2.green() * ratio;
int b = color1.blue() * (1 - ratio) + color2.blue() * ratio;
int r = int(color1.red() * (1 - ratio) + color2.red() * ratio);
int g = int(color1.green() * (1 - ratio) + color2.green() * ratio);
int b = int(color1.blue() * (1 - ratio) + color2.blue() * ratio);
return QColor(r, g, b, 255);
}
@@ -177,22 +211,22 @@ QColor ThemeManager::blendColors(const QColor &color1, const QColor &color2, qre
void ThemeManager::normalizeColor(QColor &color)
{
if (this->isLight) {
if (color.lightnessF() > 0.5f) {
color.setHslF(color.hueF(), color.saturationF(), 0.5f);
if (color.lightnessF() > 0.5) {
color.setHslF(color.hueF(), color.saturationF(), 0.5);
}
if (color.lightnessF() > 0.4f && color.hueF() > 0.1 && color.hueF() < 0.33333) {
if (color.lightnessF() > 0.4 && color.hueF() > 0.1 && color.hueF() < 0.33333) {
color.setHslF(
color.hueF(), color.saturationF(),
color.lightnessF() - sin((color.hueF() - 0.1) / (0.3333 - 0.1) * 3.14159) *
color.saturationF() * 0.2);
}
} else {
if (color.lightnessF() < 0.5f) {
color.setHslF(color.hueF(), color.saturationF(), 0.5f);
if (color.lightnessF() < 0.5) {
color.setHslF(color.hueF(), color.saturationF(), 0.5);
}
if (color.lightnessF() < 0.6f && color.hueF() > 0.54444 && color.hueF() < 0.83333) {
if (color.lightnessF() < 0.6 && color.hueF() > 0.54444 && color.hueF() < 0.83333) {
color.setHslF(
color.hueF(), color.saturationF(),
color.lightnessF() + sin((color.hueF() - 0.54444) / (0.8333 - 0.54444) * 3.14159) *
+17 -6
View File
@@ -25,11 +25,16 @@ public:
struct TabColors {
QColor text;
struct Backgrounds {
struct {
QBrush regular;
QBrush hover;
QBrush unfocused;
} backgrounds;
struct {
QColor regular;
QColor hover;
QColor unfocused;
} line;
};
/// WINDOW
@@ -43,9 +48,9 @@ public:
/// TABS
struct {
TabColors regular;
TabColors selected;
TabColors highlighted;
TabColors newMessage;
TabColors highlighted;
TabColors selected;
QColor border;
QColor bottomLine;
} tabs;
@@ -58,6 +63,10 @@ public:
QColor borderFocused;
QColor dropPreview;
QColor dropPreviewBorder;
QColor dropTargetRect;
QColor dropTargetRectBorder;
QColor resizeHandle;
QColor resizeHandleBackground;
struct {
QColor border;
@@ -89,7 +98,7 @@ public:
QColor regular;
QColor alternate;
QColor highlighted;
// QColor resub;
QColor subscription;
// QColor whisper;
} backgrounds;
@@ -104,8 +113,10 @@ public:
QColor background;
QColor thumb;
QColor thumbSelected;
// const int highlightsCount = 3;
// QColor highlights[3];
struct {
QColor highlight;
QColor subscription;
} highlights;
} scrollbars;
/// TOOLTIP
+33 -6
View File
@@ -7,25 +7,31 @@ namespace chatterino {
namespace singletons {
UpdateManager::UpdateManager()
: currentVersion(CHATTERINO_VERSION)
: currentVersion_(CHATTERINO_VERSION)
{
qDebug() << "init UpdateManager";
}
UpdateManager &UpdateManager::getInstance()
{
// fourtf: don't add this class to the application class
static UpdateManager instance;
return instance;
}
const QString &UpdateManager::getCurrentVersion() const
{
return this->getCurrentVersion();
return currentVersion_;
}
const QString &UpdateManager::getOnlineVersion() const
{
return this->getOnlineVersion();
return onlineVersion_;
}
void UpdateManager::installUpdates()
{
}
void UpdateManager::checkForUpdates()
@@ -33,17 +39,38 @@ void UpdateManager::checkForUpdates()
QString url = "https://notitia.chatterino.com/version/chatterino/" CHATTERINO_OS "/stable";
util::NetworkRequest req(url);
req.setTimeout(20000);
req.setTimeout(30000);
req.getJSON([this](QJsonObject &object) {
QJsonValue version_val = object.value("version");
if (!version_val.isString()) {
this->setStatus_(Error);
qDebug() << "error updating";
return;
}
this->onlineVersion = version_val.toString();
this->onlineVersionUpdated.invoke();
this->onlineVersion_ = version_val.toString();
if (this->currentVersion_ != this->onlineVersion_) {
this->setStatus_(UpdateAvailable);
} else {
this->setStatus_(NoUpdateAvailable);
}
});
this->setStatus_(Searching);
req.execute();
}
UpdateManager::UpdateStatus UpdateManager::getStatus() const
{
return this->status_;
}
void UpdateManager::setStatus_(UpdateStatus status)
{
if (this->status_ != status) {
this->status_ = status;
this->statusUpdated.invoke(status);
}
}
} // namespace singletons
+11 -3
View File
@@ -11,17 +11,25 @@ class UpdateManager
UpdateManager();
public:
enum UpdateStatus { None, Searching, UpdateAvailable, NoUpdateAvailable, Error };
// fourtf: don't add this class to the application class
static UpdateManager &getInstance();
void checkForUpdates();
const QString &getCurrentVersion() const;
const QString &getOnlineVersion() const;
void installUpdates();
UpdateStatus getStatus() const;
pajlada::Signals::NoArgSignal onlineVersionUpdated;
pajlada::Signals::Signal<UpdateStatus> statusUpdated;
private:
QString currentVersion;
QString onlineVersion;
QString currentVersion_;
QString onlineVersion_;
UpdateStatus status_ = None;
void setStatus_(UpdateStatus status);
};
} // namespace singletons
+83 -29
View File
@@ -10,6 +10,7 @@
#include "widgets/accountswitchpopupwidget.hpp"
#include "widgets/settingsdialog.hpp"
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
@@ -20,6 +21,9 @@
namespace chatterino {
namespace singletons {
using SplitNode = widgets::SplitContainer::Node;
using SplitDirection = widgets::SplitContainer::Direction;
void WindowManager::showSettingsDialog()
{
QTimer::singleShot(80, [] { widgets::SettingsDialog::showDialog(); });
@@ -54,11 +58,17 @@ WindowManager::WindowManager()
qDebug() << "init WindowManager";
}
void WindowManager::layoutVisibleChatWidgets(Channel *channel)
void WindowManager::layoutChannelViews(Channel *channel)
{
this->layout.invoke(channel);
}
void WindowManager::forceLayoutChannelViews()
{
this->incGeneration();
this->layoutChannelViews(nullptr);
}
void WindowManager::repaintVisibleChatWidgets(Channel *channel)
{
if (this->mainWindow != nullptr) {
@@ -184,32 +194,40 @@ void WindowManager::initialize()
// load tabs
QJsonArray tabs = window_obj.value("tabs").toArray();
for (QJsonValue tab_val : tabs) {
widgets::SplitContainer *tab = window.getNotebook().addNewPage();
widgets::SplitContainer *page = window.getNotebook().addPage(false);
QJsonObject tab_obj = tab_val.toObject();
// set custom title
QJsonValue title_val = tab_obj.value("title");
if (title_val.isString()) {
tab->getTab()->setTitle(title_val.toString());
tab->getTab()->useDefaultTitle = false;
page->getTab()->setCustomTitle(title_val.toString());
}
// selected
if (tab_obj.value("selected").toBool(false)) {
window.getNotebook().select(tab);
window.getNotebook().select(page);
}
// load splits
QJsonObject splitRoot = tab_obj.value("splits2").toObject();
if (!splitRoot.isEmpty()) {
page->decodeFromJson(splitRoot);
continue;
}
// fallback load splits (old)
int colNr = 0;
for (QJsonValue column_val : tab_obj.value("splits").toArray()) {
for (QJsonValue split_val : column_val.toArray()) {
widgets::Split *split = new widgets::Split(tab);
widgets::Split *split = new widgets::Split(page);
QJsonObject split_obj = split_val.toObject();
split->setChannel(this->decodeChannel(split_obj));
split->setChannel(decodeChannel(split_obj));
tab->addToLayout(split, std::make_pair(colNr, 10000000));
page->appendSplit(split);
}
colNr++;
}
@@ -218,7 +236,7 @@ void WindowManager::initialize()
if (mainWindow == nullptr) {
mainWindow = &createWindow(widgets::Window::Main);
mainWindow->getNotebook().addNewPage(true);
mainWindow->getNotebook().addPage(true);
}
this->initialized = true;
@@ -255,13 +273,15 @@ void WindowManager::save()
// window tabs
QJsonArray tabs_arr;
for (int tab_i = 0; tab_i < window->getNotebook().tabCount(); tab_i++) {
for (int tab_i = 0; tab_i < window->getNotebook().getPageCount(); tab_i++) {
QJsonObject tab_obj;
widgets::SplitContainer *tab = window->getNotebook().tabAt(tab_i);
widgets::SplitContainer *tab =
dynamic_cast<widgets::SplitContainer *>(window->getNotebook().getPageAt(tab_i));
assert(tab != nullptr);
// custom tab title
if (!tab->getTab()->useDefaultTitle) {
tab_obj.insert("title", tab->getTab()->getTitle());
if (tab->getTab()->hasCustomTitle()) {
tab_obj.insert("title", tab->getTab()->getCustomTitle());
}
// selected
@@ -270,23 +290,11 @@ void WindowManager::save()
}
// splits
QJsonArray columns_arr;
std::vector<std::vector<widgets::Split *>> columns = tab->getColumns();
QJsonObject splits;
for (std::vector<widgets::Split *> &cells : columns) {
QJsonArray cells_arr;
this->encodeNodeRecusively(tab->getBaseNode(), splits);
for (widgets::Split *cell : cells) {
QJsonObject cell_obj;
this->encodeChannel(cell->getIndirectChannel(), cell_obj);
cells_arr.append(cell_obj);
}
columns_arr.append(cells_arr);
}
tab_obj.insert("splits", columns_arr);
tab_obj.insert("splits2", splits);
tabs_arr.append(tab_obj);
}
@@ -302,10 +310,46 @@ void WindowManager::save()
QString settingsPath = app->paths->settingsFolderPath + SETTINGS_FILENAME;
QFile file(settingsPath);
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
file.write(document.toJson());
QJsonDocument::JsonFormat format =
#ifdef _DEBUG
QJsonDocument::JsonFormat::Compact
#else
(QJsonDocument::JsonFormat)0
#endif
;
file.write(document.toJson(format));
file.flush();
}
void WindowManager::encodeNodeRecusively(SplitNode *node, QJsonObject &obj)
{
switch (node->getType()) {
case SplitNode::_Split: {
obj.insert("type", "split");
QJsonObject split;
encodeChannel(node->getSplit()->getIndirectChannel(), split);
obj.insert("data", split);
obj.insert("flexh", node->getHorizontalFlex());
obj.insert("flexv", node->getVerticalFlex());
} break;
case SplitNode::HorizontalContainer:
case SplitNode::VerticalContainer: {
obj.insert("type", node->getType() == SplitNode::HorizontalContainer ? "horizontal"
: "vertical");
QJsonArray items_arr;
for (const std::unique_ptr<SplitNode> &n : node->getChildren()) {
QJsonObject subObj;
this->encodeNodeRecusively(n.get(), subObj);
items_arr.append(subObj);
}
obj.insert("items", items_arr);
} break;
}
}
void WindowManager::encodeChannel(IndirectChannel channel, QJsonObject &obj)
{
util::assertInGuiThread();
@@ -356,5 +400,15 @@ void WindowManager::closeAll()
}
}
int WindowManager::getGeneration() const
{
return this->generation;
}
void WindowManager::incGeneration()
{
this->generation++;
}
} // namespace singletons
} // namespace chatterino
+16 -3
View File
@@ -1,8 +1,12 @@
#pragma once
#include "widgets/splitcontainer.hpp"
#include "widgets/window.hpp"
namespace chatterino {
// namespace widgets {
// struct SplitContainer::Node;
//}
namespace singletons {
class WindowManager
@@ -15,7 +19,8 @@ public:
void showSettingsDialog();
void showAccountSelectPopup(QPoint point);
void layoutVisibleChatWidgets(Channel *channel = nullptr);
void layoutChannelViews(Channel *channel = nullptr);
void forceLayoutChannelViews();
void repaintVisibleChatWidgets(Channel *channel = nullptr);
void repaintGifEmotes();
// void updateAll();
@@ -31,19 +36,27 @@ public:
void initialize();
void closeAll();
int getGeneration() const;
void incGeneration();
pajlada::Signals::NoArgSignal repaintGifs;
pajlada::Signals::Signal<Channel *> layout;
private:
bool initialized = false;
std::atomic<int> generation{0};
std::vector<widgets::Window *> windows;
widgets::Window *mainWindow = nullptr;
widgets::Window *selectedWindow = nullptr;
void encodeChannel(IndirectChannel channel, QJsonObject &obj);
IndirectChannel decodeChannel(const QJsonObject &obj);
void encodeNodeRecusively(widgets::SplitContainer::Node *node, QJsonObject &obj);
public:
static void encodeChannel(IndirectChannel channel, QJsonObject &obj);
static IndirectChannel decodeChannel(const QJsonObject &obj);
};
} // namespace singletons