A lot of changes

Remove unused constructor of messages::Message
Fixed LimitedQueueSnapshot _-prefixes
Changed LimitedQueueSnapshot's usage of int to std::size_t
ColorScheme is no longer a singleton
Created a "BaseWidget" class which is pretty much a QWidget except it
has a reference of ColorScheme since most widgets will need a reference
to the style they should use.
BaseWidget can be implemented either with a BaseWidget parent (which
will copy the ColorScheme reference from the parent) or with a
normal QWidget parent and an explicit ColorScheme reference.
Save main window geometry on close
Fix font changing in the Settings Dialog
Update settings library version
This commit is contained in:
Rasmus Karlsson 2017-06-26 16:41:20 +02:00
parent c2e67e4b90
commit 7df7da70cb
56 changed files with 933 additions and 650 deletions

@ -1 +1 @@
Subproject commit 4bf19ff04c77c51883886af2586753df0638ffa4 Subproject commit c05bec45f3ccb829d5014b46b6a02ee9e9c6b81f

View file

@ -1,18 +1,25 @@
#include "application.hpp" #include "application.hpp"
#include "colorscheme.hpp" #include "colorscheme.hpp"
#include "logging/loggingmanager.hpp"
#include "settingsmanager.hpp" #include "settingsmanager.hpp"
namespace chatterino { namespace chatterino {
// this class is responsible for handling the workflow of Chatterino
// It will create the instances of the major classes, and connect their signals to each other
Application::Application() Application::Application()
: windowManager(this->channelManager) : windowManager(this->channelManager, this->colorScheme)
, colorScheme(this->windowManager)
, emoteManager(this->windowManager, this->resources) , emoteManager(this->windowManager, this->resources)
, resources(this->emoteManager, this->windowManager) , resources(this->emoteManager, this->windowManager)
, channelManager(this->windowManager, this->emoteManager, this->ircManager) , channelManager(this->windowManager, this->emoteManager, this->ircManager)
, ircManager(this->channelManager, this->resources, this->emoteManager, this->windowManager) , ircManager(this->channelManager, this->resources, this->emoteManager, this->windowManager)
, messageFactory(this->resources, this->emoteManager, this->windowManager)
{ {
// TODO(pajlada): Get rid of all singletons // TODO(pajlada): Get rid of all singletons
ColorScheme::getInstance().init(this->windowManager); logging::init();
SettingsManager::getInstance().load();
// Initialize everything we need // Initialize everything we need
this->emoteManager.loadGlobalEmotes(); this->emoteManager.loadGlobalEmotes();
@ -21,11 +28,28 @@ Application::Application()
SettingsManager::getInstance().updateWordTypeMask(); SettingsManager::getInstance().updateWordTypeMask();
this->windowManager.load(); this->windowManager.load();
this->ircManager.onPrivateMessage.connect([=](Communi::IrcPrivateMessage *message) {
QString channelName = message->target().mid(1);
auto channel = this->channelManager.getChannel(channelName);
if (channel == nullptr) {
// The message doesn't have a channel we listen to
return;
}
messages::MessageParseArgs args;
this->messageFactory.buildMessage(message, *channel.get(), args);
});
} }
Application::~Application() Application::~Application()
{ {
this->windowManager.save(); this->windowManager.save();
chatterino::SettingsManager::getInstance().save();
} }
int Application::run(QApplication &qtApp) int Application::run(QApplication &qtApp)

View file

@ -1,8 +1,10 @@
#pragma once #pragma once
#include "channelmanager.hpp" #include "channelmanager.hpp"
#include "colorscheme.hpp"
#include "emotemanager.hpp" #include "emotemanager.hpp"
#include "ircmanager.hpp" #include "ircmanager.hpp"
#include "messagefactory.hpp"
#include "resources.hpp" #include "resources.hpp"
#include "windowmanager.hpp" #include "windowmanager.hpp"
@ -19,10 +21,12 @@ public:
int run(QApplication &qtApp); int run(QApplication &qtApp);
WindowManager windowManager; WindowManager windowManager;
ColorScheme colorScheme;
EmoteManager emoteManager; EmoteManager emoteManager;
Resources resources; Resources resources;
ChannelManager channelManager; ChannelManager channelManager;
IrcManager ircManager; IrcManager ircManager;
MessageFactory messageFactory;
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -8,62 +8,65 @@
namespace chatterino { namespace chatterino {
void ColorScheme::init(WindowManager &windowManager) namespace detail {
double getMultiplierByTheme(const std::string &themeName)
{ {
static bool initiated = false; if (themeName == "Light") {
return 0.8;
} else if (themeName == "White") {
return 1.0;
} else if (themeName == "Black") {
return -1.0;
} else if (themeName == "Dark") {
return -0.8;
}
if (!initiated) { return -0.8;
initiated = true; }
ColorScheme::getInstance().update();
SettingsManager::getInstance().theme.valueChanged.connect([](const QString &) { } // namespace detail
ColorScheme::getInstance().update(); //
ColorScheme::ColorScheme(WindowManager &windowManager)
{
this->update();
SettingsManager::getInstance().themeName.getValueChangedSignal().connect([=](const auto &) {
this->update(); //
}); });
SettingsManager::getInstance().themeHue.valueChanged.connect([](const float &) { SettingsManager::getInstance().themeHue.getValueChangedSignal().connect([=](const auto &) {
ColorScheme::getInstance().update(); // this->update(); //
}); });
ColorScheme::getInstance().updated.connect([&windowManager] { this->updated.connect([&windowManager] {
windowManager.repaintVisibleChatWidgets(); // windowManager.repaintVisibleChatWidgets(); //
}); });
} }
}
void ColorScheme::update() void ColorScheme::update()
{ {
QString theme = SettingsManager::getInstance().theme.get(); SettingsManager &settings = SettingsManager::getInstance();
theme = theme.toLower();
qreal hue = SettingsManager::getInstance().themeHue.get(); this->setColors(settings.themeHue, detail::getMultiplierByTheme(settings.themeName));
if (theme == "light") {
setColors(hue, 0.8);
} else if (theme == "white") {
setColors(hue, 1);
} else if (theme == "black") {
setColors(hue, -1);
} else {
setColors(hue, -0.8);
}
} }
// hue: theme color (0 - 1) // hue: theme color (0 - 1)
// multiplyer: 1 = white, 0.8 = light, -0.8 dark, -1 black // multiplier: 1 = white, 0.8 = light, -0.8 dark, -1 black
void ColorScheme::setColors(float hue, float multiplyer) void ColorScheme::setColors(double hue, double multiplier)
{ {
IsLightTheme = multiplyer > 0; lightTheme = multiplier > 0;
bool hasDarkBorder = false; bool hasDarkBorder = false;
SystemMessageColor = QColor(140, 127, 127); SystemMessageColor = QColor(140, 127, 127);
auto getColor = [multiplyer](qreal h, qreal s, qreal l, qreal a = 1.0) { auto getColor = [multiplier](double h, double s, double l, double a = 1.0) {
return QColor::fromHslF(h, s, (((l - 0.5) * multiplyer) + 0.5), a); return QColor::fromHslF(h, s, (((l - 0.5) * multiplier) + 0.5), a);
}; };
DropPreviewBackground = getColor(hue, 0.5, 0.5, 0.6); DropPreviewBackground = getColor(hue, 0.5, 0.5, 0.6);
Text = TextCaret = IsLightTheme ? QColor(0, 0, 0) : QColor(255, 255, 255); Text = TextCaret = lightTheme ? QColor(0, 0, 0) : QColor(255, 255, 255);
// tab // tab
if (hasDarkBorder) { if (hasDarkBorder) {
@ -119,26 +122,26 @@ void ColorScheme::setColors(float hue, float multiplyer)
updated(); updated();
} }
void ColorScheme::fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, qreal fromValue, void ColorScheme::fillLookupTableValues(double (&array)[360], double from, double to,
qreal toValue) double fromValue, double toValue)
{ {
qreal diff = toValue - fromValue; double diff = toValue - fromValue;
int start = from * LOOKUP_COLOR_COUNT; int start = from * LOOKUP_COLOR_COUNT;
int end = to * LOOKUP_COLOR_COUNT; int end = to * LOOKUP_COLOR_COUNT;
int length = end - start; int length = end - start;
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
array[start + i] = fromValue + (diff * ((qreal)i / length)); array[start + i] = fromValue + (diff * ((double)i / length));
} }
} }
void ColorScheme::normalizeColor(QColor &color) void ColorScheme::normalizeColor(QColor &color)
{ {
// qreal l = color.lightnessF(); // double l = color.lightnessF();
// qreal s = color.saturationF(); // double s = color.saturationF();
// qreal x = this->colorLookupTable[std::max(0, color.hue())]; // double x = this->colorLookupTable[std::max(0, color.hue())];
// qreal newL = (l - 1) * x + 1; // double newL = (l - 1) * x + 1;
// newL = s * newL + (1 - s) * l; // newL = s * newL + (1 - s) * l;
@ -146,16 +149,16 @@ void ColorScheme::normalizeColor(QColor &color)
// color.setHslF(color.hueF(), s, newL); // color.setHslF(color.hueF(), s, newL);
qreal l = color.lightnessF(); double l = color.lightnessF();
qreal s = color.saturationF(); double s = color.saturationF();
int h = std::max(0, color.hue()); int h = std::max(0, color.hue());
qreal x = this->middleLookupTable[h]; double x = this->middleLookupTable[h];
x = s * 0.5 + (1 - s) * x; x = s * 0.5 + (1 - s) * x;
qreal min = this->minLookupTable[h]; double min = this->minLookupTable[h];
min = (1 - s) * 0.5 + s * min; min = (1 - s) * 0.5 + s * min;
qreal newL; double newL;
if (l < x) { if (l < x) {
newL = l * ((x - min) / x) + min; newL = l * ((x - min) / x) + min;
@ -168,7 +171,7 @@ void ColorScheme::normalizeColor(QColor &color)
color.setHslF(color.hueF(), s, newL); color.setHslF(color.hueF(), s, newL);
// qreal newL = (l - 1) * x + 1; // double newL = (l - 1) * x + 1;
// newL = s * newL + (1 - s) * l; // newL = s * newL + (1 - s) * l;

View file

@ -11,7 +11,12 @@ class WindowManager;
class ColorScheme class ColorScheme
{ {
public: public:
bool IsLightTheme; explicit ColorScheme(WindowManager &windowManager);
inline bool isLightTheme() const
{
return this->lightTheme;
}
QString InputStyleSheet; QString InputStyleSheet;
@ -62,13 +67,6 @@ public:
const int HighlightColorCount = 3; const int HighlightColorCount = 3;
QColor HighlightColors[3]; QColor HighlightColors[3];
static ColorScheme &getInstance()
{
static ColorScheme instance;
return instance;
}
void init(WindowManager &windowManager); void init(WindowManager &windowManager);
void normalizeColor(QColor &color); void normalizeColor(QColor &color);
@ -77,18 +75,15 @@ public:
boost::signals2::signal<void()> updated; boost::signals2::signal<void()> updated;
private: private:
ColorScheme() void setColors(double hue, double multiplier);
: updated()
{
}
void setColors(float hue, float multiplyer); double middleLookupTable[360] = {};
double minLookupTable[360] = {};
qreal middleLookupTable[360] = {}; void fillLookupTableValues(double (&array)[360], double from, double to, double fromValue,
qreal minLookupTable[360] = {}; double toValue);
void fillLookupTableValues(qreal (&array)[360], qreal from, qreal to, qreal fromValue, bool lightTheme;
qreal toValue);
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -9,116 +9,4 @@ namespace chatterino {
QRegularExpression Emojis::findShortCodesRegex(":([-+\\w]+):"); QRegularExpression Emojis::findShortCodesRegex(":([-+\\w]+):");
QMap<QString, Emojis::EmojiData> Emojis::shortCodeToEmoji;
QMap<QString, QString> Emojis::emojiToShortCode;
QMap<QChar, QMap<QString, QString>> Emojis::firstEmojiChars;
ConcurrentMap<QString, messages::LazyLoadedImage *> Emojis::imageCache;
QString Emojis::replaceShortCodes(const QString &text)
{
// TODO: Implement this xD
return text;
}
void Emojis::parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
const QString &text)
{
long lastSlice = 0;
for (auto i = 0; i < text.length() - 1; i++) {
if (!text.at(i).isLowSurrogate()) {
auto iter = firstEmojiChars.find(text.at(i));
if (iter != firstEmojiChars.end()) {
for (auto j = std::min(8, text.length() - i); j > 0; j--) {
QString emojiString = text.mid(i, 2);
auto emojiIter = iter.value().find(emojiString);
if (emojiIter != iter.value().end()) {
QString url = "https://cdnjs.cloudflare.com/ajax/libs/"
"emojione/2.2.6/assets/png/" +
emojiIter.value() + ".png";
if (i - lastSlice != 0) {
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
nullptr, text.mid(lastSlice, i - lastSlice)));
}
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
imageCache.getOrAdd(url,
[/*&url*/] {
/* TODO: re-implement
return new messages::LazyLoadedImage(url,
0.35); //
*/
return nullptr;
}),
QString()));
i += j - 1;
lastSlice = i + 1;
break;
}
}
}
}
}
if (lastSlice < text.length()) {
vector.push_back(
std::tuple<messages::LazyLoadedImage *, QString>(nullptr, text.mid(lastSlice)));
}
}
void Emojis::loadEmojis()
{
QFile file(":/emojidata.txt");
file.open(QFile::ReadOnly);
QTextStream in(&file);
uint emotes[4];
while (!in.atEnd()) {
QString line = in.readLine();
if (line.length() < 3 || line.at(0) == '#')
continue;
QStringList a = line.split(' ');
if (a.length() < 2)
continue;
QStringList b = a.at(1).split('-');
if (b.length() < 1)
continue;
int i = 0;
for (const QString &item : b) {
emotes[i++] = QString(item).toUInt(nullptr, 16);
}
shortCodeToEmoji.insert(a.at(0), Emojis::EmojiData{QString::fromUcs4(emotes, i), a.at(1)});
}
for (auto const &emoji : shortCodeToEmoji.toStdMap()) {
emojiToShortCode.insert(emoji.second.value, emoji.first);
}
for (auto const &emoji : shortCodeToEmoji.toStdMap()) {
auto iter = firstEmojiChars.find(emoji.first.at(0));
if (iter != firstEmojiChars.end()) {
iter.value().insert(emoji.second.value, emoji.second.value);
continue;
}
firstEmojiChars.insert(emoji.first.at(0),
QMap<QString, QString>{{emoji.second.value, emoji.second.code}});
}
}
} // namespace chatterino } // namespace chatterino

View file

@ -11,33 +11,18 @@
namespace chatterino { namespace chatterino {
class Emojis
{
public:
static void parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
const QString &text);
static void loadEmojis();
static QString replaceShortCodes(const QString &text);
struct EmojiData { struct EmojiData {
QString value; QString value;
QString code; QString code;
}; };
class Emojis
{
public:
static QString replaceShortCodes(const QString &text);
private: private:
static QRegularExpression findShortCodesRegex; static QRegularExpression findShortCodesRegex;
static QMap<QString, EmojiData> shortCodeToEmoji;
static QMap<QString, QString> emojiToShortCode;
static QMap<QChar, QMap<QString, QString>> firstEmojiChars;
static ConcurrentMap<QString, messages::LazyLoadedImage *> imageCache;
Emojis()
{
}
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -29,6 +29,7 @@ EmoteManager::EmoteManager(WindowManager &_windowManager, Resources &_resources)
void EmoteManager::loadGlobalEmotes() void EmoteManager::loadGlobalEmotes()
{ {
this->loadEmojis();
this->loadBTTVEmotes(); this->loadBTTVEmotes();
this->loadFFZEmotes(); this->loadFFZEmotes();
} }
@ -39,7 +40,7 @@ void EmoteManager::reloadBTTVChannelEmotes(const QString &channelName,
printf("[EmoteManager] Reload BTTV Channel Emotes for channel %s\n", qPrintable(channelName)); printf("[EmoteManager] Reload BTTV Channel Emotes for channel %s\n", qPrintable(channelName));
QString url("https://api.betterttv.net/2/channels/" + channelName); QString url("https://api.betterttv.net/2/channels/" + channelName);
util::urlJsonFetch(url, [this, &channelEmoteMap](QJsonObject &rootNode) { util::urlFetchJSON(url, [this, &channelEmoteMap](QJsonObject &rootNode) {
channelEmoteMap.clear(); channelEmoteMap.clear();
auto emotesNode = rootNode.value("emotes").toArray(); auto emotesNode = rootNode.value("emotes").toArray();
@ -77,7 +78,7 @@ void EmoteManager::reloadFFZChannelEmotes(
QString url("http://api.frankerfacez.com/v1/room/" + channelName); QString url("http://api.frankerfacez.com/v1/room/" + channelName);
util::urlJsonFetch(url, [this, &channelEmoteMap](QJsonObject &rootNode) { util::urlFetchJSON(url, [this, &channelEmoteMap](QJsonObject &rootNode) {
channelEmoteMap.clear(); channelEmoteMap.clear();
auto setsNode = rootNode.value("sets").toObject(); auto setsNode = rootNode.value("sets").toObject();
@ -148,6 +149,116 @@ ConcurrentMap<QString, messages::LazyLoadedImage *> &EmoteManager::getMiscImageF
return _miscImageFromCache; return _miscImageFromCache;
} }
void EmoteManager::loadEmojis()
{
QFile file(":/emojidata.txt");
file.open(QFile::ReadOnly);
QTextStream in(&file);
uint unicodeBytes[4];
while (!in.atEnd()) {
// Line example: sunglasses 1f60e
QString line = in.readLine();
if (line.at(0) == '#') {
// Ignore lines starting with # (comments)
continue;
}
QStringList parts = line.split(' ');
if (parts.length() < 2) {
continue;
}
QString shortCode = parts[0];
QString code = parts[1];
QStringList unicodeCharacters = code.split('-');
if (unicodeCharacters.length() < 1) {
continue;
}
int numUnicodeBytes = 0;
for (const QString &unicodeCharacter : unicodeCharacters) {
unicodeBytes[numUnicodeBytes++] = QString(unicodeCharacter).toUInt(nullptr, 16);
}
EmojiData emojiData{
QString::fromUcs4(unicodeBytes, numUnicodeBytes), //
code, //
};
shortCodeToEmoji.insert(shortCode, emojiData);
emojiToShortCode.insert(emojiData.value, shortCode);
}
/*
for (auto const &emoji : shortCodeToEmoji.toStdMap()) {
auto iter = firstEmojiChars.find(emoji.first.at(0));
if (iter != firstEmojiChars.end()) {
iter.value().insert(emoji.second.value, emoji.second.value);
continue;
}
firstEmojiChars.insert(emoji.first.at(0),
QMap<QString, QString>{{emoji.second.value, emoji.second.code}});
}
*/
}
void EmoteManager::parseEmojis(
std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector, const QString &text)
{
// TODO(pajlada): Add this method to EmoteManager instead
long lastSlice = 0;
for (auto i = 0; i < text.length() - 1; i++) {
if (!text.at(i).isLowSurrogate()) {
auto iter = firstEmojiChars.find(text.at(i));
if (iter != firstEmojiChars.end()) {
for (auto j = std::min(8, text.length() - i); j > 0; j--) {
QString emojiString = text.mid(i, 2);
auto emojiIter = iter.value().find(emojiString);
if (emojiIter != iter.value().end()) {
QString url = "https://cdnjs.cloudflare.com/ajax/libs/"
"emojione/2.2.6/assets/png/" +
emojiIter.value() + ".png";
if (i - lastSlice != 0) {
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
nullptr, text.mid(lastSlice, i - lastSlice)));
}
vector.push_back(std::tuple<messages::LazyLoadedImage *, QString>(
emojis.getOrAdd(url,
[this, &url] {
return new LazyLoadedImage(
*this, this->windowManager, url, 0.35); //
}),
QString()));
i += j - 1;
lastSlice = i + 1;
break;
}
}
}
}
}
if (lastSlice < text.length()) {
vector.push_back(
std::tuple<messages::LazyLoadedImage *, QString>(nullptr, text.mid(lastSlice)));
}
}
void EmoteManager::loadBTTVEmotes() void EmoteManager::loadBTTVEmotes()
{ {
// bttv // bttv

View file

@ -3,11 +3,13 @@
#define GIF_FRAME_LENGTH 33 #define GIF_FRAME_LENGTH 33
#include "concurrentmap.hpp" #include "concurrentmap.hpp"
#include "emojis.hpp"
#include "messages/lazyloadedimage.hpp" #include "messages/lazyloadedimage.hpp"
#include "twitch/emotevalue.hpp" #include "twitch/emotevalue.hpp"
#include <QMap> #include <QMap>
#include <QMutex> #include <QMutex>
#include <QString>
#include <QTimer> #include <QTimer>
#include <boost/signals2.hpp> #include <boost/signals2.hpp>
@ -60,16 +62,47 @@ private:
WindowManager &windowManager; WindowManager &windowManager;
Resources &resources; Resources &resources;
ConcurrentMap<QString, messages::LazyLoadedImage *> bttvChannelEmotes; // Emojis
ConcurrentMap<QString, messages::LazyLoadedImage *> ffzChannelEmotes; // shortCodeToEmoji maps strings like ":sunglasses:" to the unicode character
QMap<QString, EmojiData> shortCodeToEmoji;
// emojiToShortCode maps the unicode character to the shortcode like ":sunglasses:"
QMap<QString, QString> emojiToShortCode;
// TODO(pajlada): Figure out what this is for
QMap<QChar, QMap<QString, QString>> firstEmojiChars;
ConcurrentMap<QString, messages::LazyLoadedImage *> emojis;
void loadEmojis();
public:
void parseEmojis(std::vector<std::tuple<messages::LazyLoadedImage *, QString>> &vector,
const QString &text);
private:
// Twitch emotes
ConcurrentMap<QString, twitch::EmoteValue *> _twitchEmotes; ConcurrentMap<QString, twitch::EmoteValue *> _twitchEmotes;
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvEmotes;
ConcurrentMap<QString, messages::LazyLoadedImage *> _ffzEmotes;
ConcurrentMap<QString, messages::LazyLoadedImage *> _chatterinoEmotes;
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvChannelEmoteFromCaches;
ConcurrentMap<int, messages::LazyLoadedImage *> _ffzChannelEmoteFromCaches;
ConcurrentMap<long, messages::LazyLoadedImage *> _twitchEmoteFromCache; ConcurrentMap<long, messages::LazyLoadedImage *> _twitchEmoteFromCache;
// BTTV emotes
ConcurrentMap<QString, messages::LazyLoadedImage *> bttvChannelEmotes;
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvEmotes;
ConcurrentMap<QString, messages::LazyLoadedImage *> _bttvChannelEmoteFromCaches;
void loadBTTVEmotes();
// FFZ emotes
ConcurrentMap<QString, messages::LazyLoadedImage *> ffzChannelEmotes;
ConcurrentMap<QString, messages::LazyLoadedImage *> _ffzEmotes;
ConcurrentMap<int, messages::LazyLoadedImage *> _ffzChannelEmoteFromCaches;
void loadFFZEmotes();
// Chatterino emotes
ConcurrentMap<QString, messages::LazyLoadedImage *> _chatterinoEmotes;
// ???
ConcurrentMap<QString, messages::LazyLoadedImage *> _miscImageFromCache; ConcurrentMap<QString, messages::LazyLoadedImage *> _miscImageFromCache;
boost::signals2::signal<void()> _gifUpdateTimerSignal; boost::signals2::signal<void()> _gifUpdateTimerSignal;
@ -80,9 +113,6 @@ private:
// methods // methods
static QString getTwitchEmoteLink(long id, qreal &scale); static QString getTwitchEmoteLink(long id, qreal &scale);
void loadFFZEmotes();
void loadBTTVEmotes();
}; };
} // namespace chatterino } // namespace chatterino

View file

@ -9,10 +9,10 @@ FontManager::FontManager()
, currentFontSize("/appearance/currentFontSize", 14) , currentFontSize("/appearance/currentFontSize", 14)
, currentFont(this->currentFontFamily.getValue().c_str(), currentFontSize.getValue()) , currentFont(this->currentFontFamily.getValue().c_str(), currentFontSize.getValue())
{ {
this->currentFontFamily.valueChanged.connect([this](const std::string &newValue) { this->currentFontFamily.getValueChangedSignal().connect([this](const std::string &newValue) {
this->currentFont.setFamily(newValue.c_str()); // this->currentFont.setFamily(newValue.c_str()); //
}); });
this->currentFontSize.valueChanged.connect([this](const int &newValue) { this->currentFontSize.getValueChangedSignal().connect([this](const int &newValue) {
this->currentFont.setSize(newValue); // this->currentFont.setSize(newValue); //
}); });
} }

View file

@ -8,6 +8,7 @@
#include "twitch/twitchmessagebuilder.hpp" #include "twitch/twitchmessagebuilder.hpp"
#include "twitch/twitchparsemessage.hpp" #include "twitch/twitchparsemessage.hpp"
#include "twitch/twitchuser.hpp" #include "twitch/twitchuser.hpp"
#include "util/urlfetch.hpp"
#include "windowmanager.hpp" #include "windowmanager.hpp"
#include <irccommand.h> #include <irccommand.h>
@ -132,15 +133,12 @@ void IrcManager::refreshIgnoredUsers(const QString &username, const QString &oau
void IrcManager::refreshTwitchEmotes(const QString &username, const QString &oauthClient, void IrcManager::refreshTwitchEmotes(const QString &username, const QString &oauthClient,
const QString &oauthToken) const QString &oauthToken)
{ {
QNetworkRequest req(QUrl("https://api.twitch.tv/kraken/users/" + username + QString url("https://api.twitch.tv/kraken/users/" + username +
"/emotes?oauth_token=" + oauthToken + "&client_id=" + oauthClient)); "/emotes?oauth_token=" + oauthToken + "&client_id=" + oauthClient);
QNetworkReply *reply = _accessManager.get(req);
QObject::connect(reply, &QNetworkReply::finished, [=] {
QByteArray data = reply->readAll();
QJsonDocument jsonDoc(QJsonDocument::fromJson(data));
QJsonObject root = jsonDoc.object();
if (true) {
util::urlFetchJSONTimeout(url,
[=](QJsonObject &root) {
// nextLink = // nextLink =
// root.value("_links").toObject().value("next").toString(); // root.value("_links").toObject().value("next").toString();
@ -150,10 +148,14 @@ void IrcManager::refreshTwitchEmotes(const QString &username, const QString &oau
for (QJsonValue block : blocks) { for (QJsonValue block : blocks) {
QJsonObject user = block.toObject().value("user").toObject(); QJsonObject user = block.toObject().value("user").toObject();
// display_name // display_name
_twitchBlockedUsers.insert(user.value("name").toString().toLower(), true); _twitchBlockedUsers.insert(
user.value("name").toString().toLower(), true);
} }
_twitchBlockedUsersMutex.unlock(); _twitchBlockedUsersMutex.unlock();
}); qDebug() << "XD";
},
3000, &this->_accessManager);
}
} }
void IrcManager::beginConnecting() void IrcManager::beginConnecting()
@ -209,14 +211,13 @@ void IrcManager::sendMessage(const QString &channelName, const QString &message)
this->connectionMutex.unlock(); this->connectionMutex.unlock();
// DEBUGGING // DEBUGGING
#if 0 /*
Communi::IrcPrivateMessage msg(this->readConnection.get()); Communi::IrcPrivateMessage msg(this->readConnection.get());
QStringList params{"#pajlada", message}; QStringList params{"#pajlada", message};
qDebug() << params; qDebug() << params;
/*
if (message == "COMIC SANS LOL") { if (message == "COMIC SANS LOL") {
FontManager::getInstance().currentFontFamily = "Comic Sans MS"; FontManager::getInstance().currentFontFamily = "Comic Sans MS";
} else if (message == "ARIAL LOL") { } else if (message == "ARIAL LOL") {
@ -224,14 +225,13 @@ void IrcManager::sendMessage(const QString &channelName, const QString &message)
} else if (message == "WINGDINGS LOL") { } else if (message == "WINGDINGS LOL") {
FontManager::getInstance().currentFontFamily = "Wingdings"; FontManager::getInstance().currentFontFamily = "Wingdings";
} }
*/
msg.setParameters(params); msg.setParameters(params);
msg.setPrefix("pajlada!pajlada@pajlada"); msg.setPrefix("pajlada!pajlada@pajlada");
this->privateMessageReceived(&msg); this->privateMessageReceived(&msg);
#endif */
} }
void IrcManager::joinChannel(const QString &channelName) void IrcManager::joinChannel(const QString &channelName)
@ -260,6 +260,7 @@ void IrcManager::partChannel(const QString &channelName)
void IrcManager::privateMessageReceived(Communi::IrcPrivateMessage *message) void IrcManager::privateMessageReceived(Communi::IrcPrivateMessage *message)
{ {
this->onPrivateMessage.invoke(message);
auto c = this->channelManager.getChannel(message->target().mid(1)); auto c = this->channelManager.getChannel(message->target().mid(1));
if (c != nullptr) { if (c != nullptr) {

View file

@ -10,6 +10,7 @@
#include <QMutex> #include <QMutex>
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QString> #include <QString>
#include <pajlada/signals/signal.hpp>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@ -50,6 +51,8 @@ public:
const twitch::TwitchUser &getUser() const; const twitch::TwitchUser &getUser() const;
void setUser(const twitch::TwitchUser &account); void setUser(const twitch::TwitchUser &account);
pajlada::Signals::Signal<Communi::IrcPrivateMessage *> onPrivateMessage;
private: private:
ChannelManager &channelManager; ChannelManager &channelManager;
Resources &resources; Resources &resources;

View file

@ -1,20 +1,8 @@
#include "application.hpp" #include "application.hpp"
#include "channelmanager.hpp"
#include "colorscheme.hpp"
#include "emojis.hpp"
#include "emotemanager.hpp"
#include "ircmanager.hpp"
#include "logging/loggingmanager.hpp"
#include "resources.hpp"
#include "settingsmanager.hpp"
#include "widgets/mainwindow.hpp"
#include "windowmanager.hpp"
#include <QApplication> #include <QApplication>
#include <QClipboard>
#include <QDir> #include <QDir>
#include <QStandardPaths> #include <QStandardPaths>
#include <boost/signals2.hpp>
#include <pajlada/settings/settingmanager.hpp> #include <pajlada/settings/settingmanager.hpp>
namespace { namespace {
@ -67,10 +55,6 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
chatterino::logging::init();
chatterino::SettingsManager::getInstance().load();
chatterino::Emojis::loadEmojis();
int ret = 0; int ret = 0;
{ {
@ -83,8 +67,6 @@ int main(int argc, char *argv[])
// Application will go out of scope here and deinitialize itself // Application will go out of scope here and deinitialize itself
} }
chatterino::SettingsManager::getInstance().save();
// Save settings // Save settings
pajlada::Settings::SettingManager::save(); pajlada::Settings::SettingManager::save();

View file

@ -1,6 +1,20 @@
#include "messagefactory.hpp" #include "messagefactory.hpp"
MessageFactory::MessageFactory() namespace chatterino {
{
MessageFactory::MessageFactory(Resources &_resources, EmoteManager &_emoteManager,
WindowManager &_windowManager)
: resources(_resources)
, emoteManager(_emoteManager)
, windowManager(_windowManager)
{
} }
messages::SharedMessage MessageFactory::buildMessage(Communi::IrcPrivateMessage *message,
Channel &channel,
const messages::MessageParseArgs &args)
{
return nullptr;
}
} // namespace chatterino

View file

@ -1,11 +1,26 @@
#ifndef MESSAGEFACTORY_HPP #pragma once
#define MESSAGEFACTORY_HPP
#include "messages/message.hpp"
namespace chatterino {
class Resources;
class EmoteManager;
class WindowManager;
class MessageFactory class MessageFactory
{ {
public: public:
MessageFactory(); explicit MessageFactory(Resources &_resources, EmoteManager &_emoteManager,
WindowManager &_windowManager);
messages::SharedMessage buildMessage(Communi::IrcPrivateMessage *message, Channel &channel,
const messages::MessageParseArgs &args);
private:
Resources &resources;
EmoteManager &emoteManager;
WindowManager &windowManager;
}; };
#endif // MESSAGEFACTORY_HPP } // namespace chatterino

View file

@ -10,28 +10,29 @@ template <typename T>
class LimitedQueueSnapshot class LimitedQueueSnapshot
{ {
public: public:
LimitedQueueSnapshot(std::shared_ptr<std::vector<T>> vector, int offset, int size) LimitedQueueSnapshot(std::shared_ptr<std::vector<T>> _vector, std::size_t _offset,
: _vector(vector) std::size_t _size)
, _offset(offset) : vector(_vector)
, _length(size) , offset(_offset)
, length(_size)
{ {
} }
int getSize() std::size_t getLength()
{ {
return _length; return length;
} }
T const &operator[](int index) const T const &operator[](std::size_t index) const
{ {
return _vector->at(index + _offset); return vector->at(index + offset);
} }
private: private:
std::shared_ptr<std::vector<T>> _vector; std::shared_ptr<std::vector<T>> vector;
int _offset; std::size_t offset;
int _length; std::size_t length;
}; };
} // namespace messages } // namespace messages

View file

@ -20,12 +20,14 @@
namespace chatterino { namespace chatterino {
namespace messages { namespace messages {
/*
Message::Message(const QString &text) Message::Message(const QString &text)
: text(text) : text(text)
{ {
this->words.push_back( this->words.push_back(
Word(text, Word::Text, ColorScheme::getInstance().SystemMessageColor, text, QString())); Word(text, Word::Text, ColorScheme::getInstance().SystemMessageColor, text, QString()));
} }
*/
Message::Message(const QString &text, const std::vector<Word> &words) Message::Message(const QString &text, const std::vector<Word> &words)
: text(text) : text(text)

View file

@ -23,7 +23,7 @@ typedef std::shared_ptr<Message> SharedMessage;
class Message class Message
{ {
public: public:
explicit Message(const QString &text); // explicit Message(const QString &text);
explicit Message(const QString &text, const std::vector<messages::Word> &words); explicit Message(const QString &text, const std::vector<messages::Word> &words);
bool getCanHighlightTab() const; bool getCanHighlightTab() const;

View file

@ -33,17 +33,21 @@ void MessageBuilder::appendTimestamp(time_t time)
{ {
char timeStampBuffer[69]; char timeStampBuffer[69];
// TODO(pajlada): Fix this
QColor systemMessageColor(140, 127, 127);
// QColor &systemMessageColor = ColorScheme::getInstance().SystemMessageColor;
// Add word for timestamp with no seconds // Add word for timestamp with no seconds
strftime(timeStampBuffer, 69, "%H:%M", localtime(&time)); strftime(timeStampBuffer, 69, "%H:%M", localtime(&time));
QString timestampNoSeconds(timeStampBuffer); QString timestampNoSeconds(timeStampBuffer);
appendWord(Word(timestampNoSeconds, Word::TimestampNoSeconds, appendWord(Word(timestampNoSeconds, Word::TimestampNoSeconds, systemMessageColor, QString(),
ColorScheme::getInstance().SystemMessageColor, QString(), QString())); QString()));
// Add word for timestamp with seconds // Add word for timestamp with seconds
strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&time)); strftime(timeStampBuffer, 69, "%H:%M:%S", localtime(&time));
QString timestampWithSeconds(timeStampBuffer); QString timestampWithSeconds(timeStampBuffer);
appendWord(Word(timestampWithSeconds, Word::TimestampWithSeconds, appendWord(Word(timestampWithSeconds, Word::TimestampWithSeconds, systemMessageColor, QString(),
ColorScheme::getInstance().SystemMessageColor, QString(), QString())); QString()));
} }
QString MessageBuilder::matchLink(const QString &string) QString MessageBuilder::matchLink(const QString &string)

View file

@ -75,7 +75,7 @@ public:
ButtonTimeout = (1 << 22), ButtonTimeout = (1 << 22),
EmojiImage = (1 << 23), EmojiImage = (1 << 23),
EmojiText = (1 << 34), EmojiText = (1 << 24),
Default = TimestampNoSeconds | Badges | Username | BitsStatic | FfzEmoteImage | Default = TimestampNoSeconds | Badges | Username | BitsStatic | FfzEmoteImage |
BttvEmoteImage | BttvGifEmoteImage | TwitchEmoteImage | BitsAmount | Text | BttvEmoteImage | BttvGifEmoteImage | TwitchEmoteImage | BitsAmount | Text |
@ -85,6 +85,7 @@ public:
Word() Word()
{ {
} }
explicit Word(LazyLoadedImage *_image, Type getType, const QString &copytext, explicit Word(LazyLoadedImage *_image, Type getType, const QString &copytext,
const QString &getTooltip, const Link &getLink = Link()); const QString &getTooltip, const Link &getLink = Link());
explicit Word(const QString &_text, Type getType, const QColor &getColor, explicit Word(const QString &_text, Type getType, const QColor &getColor,

View file

@ -40,7 +40,7 @@ Resources::Resources(EmoteManager &em, WindowManager &wm)
{ {
QString badgesUrl("https://badges.twitch.tv/v1/badges/global/display?language=en"); QString badgesUrl("https://badges.twitch.tv/v1/badges/global/display?language=en");
util::urlJsonFetch(badgesUrl, [this](QJsonObject &root) { util::urlFetchJSON(badgesUrl, [this](QJsonObject &root) {
QJsonObject sets = root.value("badge_sets").toObject(); QJsonObject sets = root.value("badge_sets").toObject();
for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) { for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) {
@ -86,7 +86,7 @@ void Resources::loadChannelData(const std::string &roomID, bool bypassCache)
QString url = "https://badges.twitch.tv/v1/badges/channels/" + QString::fromStdString(roomID) + QString url = "https://badges.twitch.tv/v1/badges/channels/" + QString::fromStdString(roomID) +
"/display?language=en"; "/display?language=en";
util::urlJsonFetch(url, [this](QJsonObject &root) { util::urlFetchJSON(url, [this](QJsonObject &root) {
QJsonObject sets = root.value("badge_sets").toObject(); QJsonObject sets = root.value("badge_sets").toObject();
for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) { for (QJsonObject::iterator it = sets.begin(); it != sets.end(); ++it) {

View file

@ -11,15 +11,15 @@ namespace chatterino {
SettingsManager::SettingsManager() SettingsManager::SettingsManager()
: _settings(Path::getAppdataPath() + "settings.ini", QSettings::IniFormat) : _settings(Path::getAppdataPath() + "settings.ini", QSettings::IniFormat)
, theme(_settingsItems, "theme", "dark") , showTimestamps("/appearance/messages/showTimestamps", true)
, themeHue(_settingsItems, "themeHue", 0) , showTimestampSeconds("/appearance/messages/showTimestampSeconds", true)
, showBadges("/appearance/messages/showBadges", true)
, themeName("/appearance/theme/name", "Dark")
, themeHue("/appearance/theme/hue", 0.0)
, selectedUser(_settingsItems, "selectedUser", "") , selectedUser(_settingsItems, "selectedUser", "")
, emoteScale(_settingsItems, "emoteScale", 1.0) , emoteScale(_settingsItems, "emoteScale", 1.0)
, mouseScrollMultiplier(_settingsItems, "mouseScrollMultiplier", 1.0) , mouseScrollMultiplier(_settingsItems, "mouseScrollMultiplier", 1.0)
, scaleEmotesByLineHeight(_settingsItems, "scaleEmotesByLineHeight", false) , scaleEmotesByLineHeight(_settingsItems, "scaleEmotesByLineHeight", false)
, showTimestamps("/appearance/messages/showTimestamps", true)
, showTimestampSeconds("/appearance/messages/showTimestampSeconds", true)
, showBadges("/appearance/messages/showBadges", true)
, showLastMessageIndicator(_settingsItems, "showLastMessageIndicator", false) , showLastMessageIndicator(_settingsItems, "showLastMessageIndicator", false)
, allowDouplicateMessages(_settingsItems, "allowDouplicateMessages", true) , allowDouplicateMessages(_settingsItems, "allowDouplicateMessages", true)
, linksDoubleClickOnly(_settingsItems, "linksDoubleClickOnly", false) , linksDoubleClickOnly(_settingsItems, "linksDoubleClickOnly", false)

View file

@ -42,10 +42,10 @@ public:
pajlada::Settings::Setting<bool> showTimestamps; pajlada::Settings::Setting<bool> showTimestamps;
pajlada::Settings::Setting<bool> showTimestampSeconds; pajlada::Settings::Setting<bool> showTimestampSeconds;
pajlada::Settings::Setting<bool> showBadges; pajlada::Settings::Setting<bool> showBadges;
pajlada::Settings::Setting<std::string> themeName;
pajlada::Settings::Setting<double> themeHue;
// Settings // Settings
Setting<QString> theme;
Setting<float> themeHue;
Setting<QString> selectedUser; Setting<QString> selectedUser;
Setting<float> emoteScale; Setting<float> emoteScale;
Setting<float> mouseScrollMultiplier; Setting<float> mouseScrollMultiplier;

View file

@ -60,6 +60,8 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
// badges // badges
iterator = tags.find("badges"); iterator = tags.find("badges");
ColorScheme &colorScheme = windowManager.colorScheme;
const auto &channelResources = resources.channels[roomID]; const auto &channelResources = resources.channels[roomID];
if (iterator != tags.end()) { if (iterator != tags.end()) {
@ -69,7 +71,7 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
} }
// color // color
QColor usernameColor = ColorScheme::getInstance().SystemMessageColor; QColor &usernameColor = colorScheme.SystemMessageColor;
iterator = tags.find("color"); iterator = tags.find("color");
if (iterator != tags.end()) { if (iterator != tags.end()) {
@ -79,7 +81,7 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
// channel name // channel name
if (args.includeChannelName) { if (args.includeChannelName) {
QString channelName("#" + channel->getName()); QString channelName("#" + channel->getName());
b.appendWord(Word(channelName, Word::Misc, ColorScheme::getInstance().SystemMessageColor, b.appendWord(Word(channelName, Word::Misc, colorScheme.SystemMessageColor,
QString(channelName), QString(), QString(channelName), QString(),
Link(Link::Url, channel->getName() + "\n" + b.messageId))); Link(Link::Url, channel->getName() + "\n" + b.messageId)));
} }
@ -157,7 +159,7 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
auto currentTwitchEmote = twitchEmotes.begin(); auto currentTwitchEmote = twitchEmotes.begin();
// words // words
QColor textColor = ircMessage->isAction() ? usernameColor : ColorScheme::getInstance().Text; QColor textColor = ircMessage->isAction() ? usernameColor : colorScheme.Text;
const QString &originalMessage = ircMessage->content(); const QString &originalMessage = ircMessage->content();
b.originalMessage = originalMessage; b.originalMessage = originalMessage;
@ -184,7 +186,7 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
// split words // split words
std::vector<std::tuple<LazyLoadedImage *, QString>> parsed; std::vector<std::tuple<LazyLoadedImage *, QString>> parsed;
Emojis::parseEmojis(parsed, split); emoteManager.parseEmojis(parsed, split);
for (const std::tuple<LazyLoadedImage *, QString> &tuple : parsed) { for (const std::tuple<LazyLoadedImage *, QString> &tuple : parsed) {
LazyLoadedImage *image = std::get<0>(tuple); LazyLoadedImage *image = std::get<0>(tuple);
@ -257,7 +259,11 @@ SharedMessage TwitchMessageBuilder::parse(const Communi::IrcPrivateMessage *ircM
// bttv / ffz emotes // bttv / ffz emotes
LazyLoadedImage *bttvEmote; LazyLoadedImage *bttvEmote;
// TODO: Implement this (ignored emotes) // TODO: Implement ignored emotes
// Format of ignored emotes:
// Emote name: "forsenPuke" - if string in ignoredEmotes
// Will match emote regardless of source (i.e. bttv, ffz)
// Emote source + name: "bttv:nyanPls"
if (emoteManager.getBTTVEmotes().tryGet(string, bttvEmote) || if (emoteManager.getBTTVEmotes().tryGet(string, bttvEmote) ||
channel->getBttvChannelEmotes().tryGet(string, bttvEmote) || channel->getBttvChannelEmotes().tryGet(string, bttvEmote) ||
emoteManager.getFFZEmotes().tryGet(string, bttvEmote) || emoteManager.getFFZEmotes().tryGet(string, bttvEmote) ||

View file

@ -1,4 +1,34 @@
#ifndef BASEWIDGET_HPP #pragma once
#define BASEWIDGET_HPP
#endif // BASEWIDGET_HPP #include "colorscheme.hpp"
#include <QWidget>
namespace chatterino {
class ColorScheme;
namespace widgets {
class BaseWidget : public QWidget
{
Q_OBJECT
public:
explicit BaseWidget(ColorScheme &_colorScheme, QWidget *parent)
: QWidget(parent)
, colorScheme(_colorScheme)
{
}
explicit BaseWidget(BaseWidget *parent)
: QWidget(parent)
, colorScheme(parent->colorScheme)
{
}
ColorScheme &colorScheme;
};
} // namespace widgets
} // namespace chatterino

View file

@ -30,8 +30,8 @@ inline void ezShortcut(ChatWidget *w, const char *key, T t)
} // namespace } // namespace
ChatWidget::ChatWidget(ChannelManager &_channelManager, QWidget *parent) ChatWidget::ChatWidget(ChannelManager &_channelManager, NotebookPage *parent)
: QWidget(parent) : BaseWidget(parent)
, channelManager(_channelManager) , channelManager(_channelManager)
, channel(_channelManager.getEmpty()) , channel(_channelManager.getEmpty())
, vbox(this) , vbox(this)
@ -142,7 +142,7 @@ void ChatWidget::setChannel(std::shared_ptr<Channel> _newChannel)
auto snapshot = this->channel->getMessageSnapshot(); auto snapshot = this->channel->getMessageSnapshot();
for (int i = 0; i < snapshot.getSize(); i++) { for (int i = 0; i < snapshot.getLength(); i++) {
SharedMessageRef deleted; SharedMessageRef deleted;
auto messageRef = new MessageRef(snapshot[i]); auto messageRef = new MessageRef(snapshot[i]);
@ -199,7 +199,7 @@ void ChatWidget::paintEvent(QPaintEvent *)
// color the background of the chat // color the background of the chat
QPainter painter(this); QPainter painter(this);
painter.fillRect(this->rect(), ColorScheme::getInstance().ChatBackground); painter.fillRect(this->rect(), this->colorScheme.ChatBackground);
} }
void ChatWidget::load(const boost::property_tree::ptree &tree) void ChatWidget::load(const boost::property_tree::ptree &tree)
@ -241,7 +241,8 @@ void ChatWidget::doChangeChannel()
void ChatWidget::doPopup() void ChatWidget::doPopup()
{ {
// TODO: Copy signals and stuff too // TODO: Copy signals and stuff too
auto widget = new ChatWidget(this->channelManager); auto widget =
new ChatWidget(this->channelManager, static_cast<NotebookPage *>(this->parentWidget()));
widget->setChannelName(this->getChannelName()); widget->setChannelName(this->getChannelName());
widget->show(); widget->show();
} }

View file

@ -5,6 +5,7 @@
#include "messages/messageref.hpp" #include "messages/messageref.hpp"
#include "messages/word.hpp" #include "messages/word.hpp"
#include "messages/wordpart.hpp" #include "messages/wordpart.hpp"
#include "widgets/basewidget.hpp"
#include "widgets/chatwidgetheader.hpp" #include "widgets/chatwidgetheader.hpp"
#include "widgets/chatwidgetinput.hpp" #include "widgets/chatwidgetinput.hpp"
#include "widgets/chatwidgetview.hpp" #include "widgets/chatwidgetview.hpp"
@ -19,9 +20,12 @@
namespace chatterino { namespace chatterino {
class ChannelManager; class ChannelManager;
class ColorScheme;
namespace widgets { namespace widgets {
class NotebookPage;
// Each ChatWidget consists of three sub-elements that handle their own part of the chat widget: // Each ChatWidget consists of three sub-elements that handle their own part of the chat widget:
// ChatWidgetHeader // ChatWidgetHeader
// - Responsible for rendering which channel the ChatWidget is in, and the menu in the top-left of // - Responsible for rendering which channel the ChatWidget is in, and the menu in the top-left of
@ -32,12 +36,12 @@ namespace widgets {
// - Responsible for rendering and handling user text input // - Responsible for rendering and handling user text input
// //
// Each sub-element has a reference to the parent Chat Widget // Each sub-element has a reference to the parent Chat Widget
class ChatWidget : public QWidget class ChatWidget : public BaseWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
ChatWidget(ChannelManager &_channelManager, QWidget *parent = nullptr); ChatWidget(ChannelManager &_channelManager, NotebookPage *parent);
~ChatWidget(); ~ChatWidget();
std::shared_ptr<Channel> getChannel() const; std::shared_ptr<Channel> getChannel() const;

View file

@ -12,9 +12,11 @@ namespace chatterino {
namespace widgets { namespace widgets {
ChatWidgetHeader::ChatWidgetHeader(ChatWidget *_chatWidget) ChatWidgetHeader::ChatWidgetHeader(ChatWidget *_chatWidget)
: QWidget(_chatWidget) : BaseWidget(_chatWidget)
, chatWidget(_chatWidget) , chatWidget(_chatWidget)
, leftLabel(this)
, leftMenu(this) , leftMenu(this)
, rightLabel(this)
, rightMenu(this) , rightMenu(this)
{ {
this->setFixedHeight(32); this->setFixedHeight(32);
@ -68,7 +70,7 @@ ChatWidgetHeader::ChatWidgetHeader(ChatWidget *_chatWidget)
void ChatWidgetHeader::updateColors() void ChatWidgetHeader::updateColors()
{ {
QPalette palette; QPalette palette;
palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text); palette.setColor(QPalette::Foreground, this->colorScheme.Text);
this->leftLabel.setPalette(palette); this->leftLabel.setPalette(palette);
this->channelNameLabel.setPalette(palette); this->channelNameLabel.setPalette(palette);
@ -86,8 +88,8 @@ void ChatWidgetHeader::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
painter.fillRect(rect(), ColorScheme::getInstance().ChatHeaderBackground); painter.fillRect(rect(), this->colorScheme.ChatHeaderBackground);
painter.setPen(ColorScheme::getInstance().ChatHeaderBorder); painter.setPen(this->colorScheme.ChatHeaderBorder);
painter.drawRect(0, 0, width() - 1, height() - 1); painter.drawRect(0, 0, width() - 1, height() - 1);
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "signallabel.hpp" #include "signallabel.hpp"
#include "widgets/basewidget.hpp"
#include "widgets/chatwidgetheaderbutton.hpp" #include "widgets/chatwidgetheaderbutton.hpp"
#include <QAction> #include <QAction>
@ -13,10 +14,14 @@
#include <QWidget> #include <QWidget>
namespace chatterino { namespace chatterino {
class ColorScheme;
namespace widgets { namespace widgets {
class ChatWidget; class ChatWidget;
class ChatWidgetHeader : public QWidget class ChatWidgetHeader : public BaseWidget
{ {
Q_OBJECT Q_OBJECT

View file

@ -1,5 +1,6 @@
#include "widgets/chatwidgetheaderbutton.hpp" #include "widgets/chatwidgetheaderbutton.hpp"
#include "colorscheme.hpp" #include "colorscheme.hpp"
#include "widgets/chatwidgetheader.hpp"
#include <QBrush> #include <QBrush>
#include <QPainter> #include <QPainter>
@ -7,24 +8,23 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
ChatWidgetHeaderButton::ChatWidgetHeaderButton(int spacing) ChatWidgetHeaderButton::ChatWidgetHeaderButton(BaseWidget *parent, int spacing)
: QWidget() : BaseWidget(parent)
, _hbox() , mouseOver(false)
, _label() , mouseDown(false)
, _mouseOver(false)
, _mouseDown(false)
{ {
setLayout(&_hbox); setLayout(&this->ui.hbox);
_label.setAlignment(Qt::AlignCenter); this->ui.label.setAlignment(Qt::AlignCenter);
_hbox.setMargin(0); this->ui.hbox.setMargin(0);
_hbox.addSpacing(spacing); this->ui.hbox.addSpacing(spacing);
_hbox.addWidget(&_label); this->ui.hbox.addWidget(&this->ui.label);
_hbox.addSpacing(spacing); this->ui.hbox.addSpacing(spacing);
QObject::connect(&_label, &SignalLabel::mouseUp, this, &ChatWidgetHeaderButton::labelMouseUp); QObject::connect(&this->ui.label, &SignalLabel::mouseUp, this,
QObject::connect(&_label, &SignalLabel::mouseDown, this, &ChatWidgetHeaderButton::labelMouseUp);
QObject::connect(&this->ui.label, &SignalLabel::mouseDown, this,
&ChatWidgetHeaderButton::labelMouseDown); &ChatWidgetHeaderButton::labelMouseDown);
} }
@ -32,14 +32,14 @@ void ChatWidgetHeaderButton::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
QBrush brush(ColorScheme::getInstance().IsLightTheme ? QColor(0, 0, 0, 32) QBrush brush(this->colorScheme.isLightTheme() ? QColor(0, 0, 0, 32)
: QColor(255, 255, 255, 32)); : QColor(255, 255, 255, 32));
if (_mouseDown) { if (mouseDown) {
painter.fillRect(rect(), brush); painter.fillRect(rect(), brush);
} }
if (_mouseOver) { if (mouseOver) {
painter.fillRect(rect(), brush); painter.fillRect(rect(), brush);
} }
} }
@ -47,7 +47,7 @@ void ChatWidgetHeaderButton::paintEvent(QPaintEvent *)
void ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event) void ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
_mouseDown = true; mouseDown = true;
update(); update();
} }
@ -56,7 +56,7 @@ void ChatWidgetHeaderButton::mousePressEvent(QMouseEvent *event)
void ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event) void ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
_mouseDown = false; mouseDown = false;
update(); update();
@ -66,21 +66,21 @@ void ChatWidgetHeaderButton::mouseReleaseEvent(QMouseEvent *event)
void ChatWidgetHeaderButton::enterEvent(QEvent *) void ChatWidgetHeaderButton::enterEvent(QEvent *)
{ {
_mouseOver = true; mouseOver = true;
update(); update();
} }
void ChatWidgetHeaderButton::leaveEvent(QEvent *) void ChatWidgetHeaderButton::leaveEvent(QEvent *)
{ {
_mouseOver = false; mouseOver = false;
update(); update();
} }
void ChatWidgetHeaderButton::labelMouseUp() void ChatWidgetHeaderButton::labelMouseUp()
{ {
_mouseDown = false; mouseDown = false;
update(); update();
@ -89,7 +89,7 @@ void ChatWidgetHeaderButton::labelMouseUp()
void ChatWidgetHeaderButton::labelMouseDown() void ChatWidgetHeaderButton::labelMouseDown()
{ {
_mouseDown = true; mouseDown = true;
update(); update();
} }

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "widgets/basewidget.hpp"
#include "widgets/signallabel.hpp" #include "widgets/signallabel.hpp"
#include <QHBoxLayout> #include <QHBoxLayout>
@ -8,38 +9,45 @@
#include <QWidget> #include <QWidget>
namespace chatterino { namespace chatterino {
class ColorScheme;
namespace widgets { namespace widgets {
class ChatWidgetHeaderButton : public QWidget class ChatWidgetHeader;
class ChatWidgetHeaderButton : public BaseWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ChatWidgetHeaderButton(int spacing = 6); explicit ChatWidgetHeaderButton(BaseWidget *parent, int spacing = 6);
SignalLabel &getLabel() SignalLabel &getLabel()
{ {
return _label; return this->ui.label;
} }
signals: signals:
void clicked(); void clicked();
protected: protected:
void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; virtual void paintEvent(QPaintEvent *) override;
void enterEvent(QEvent *) Q_DECL_OVERRIDE; virtual void enterEvent(QEvent *) override;
void leaveEvent(QEvent *) Q_DECL_OVERRIDE; virtual void leaveEvent(QEvent *) override;
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; virtual void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) Q_DECL_OVERRIDE; virtual void mouseReleaseEvent(QMouseEvent *event) override;
private: private:
QHBoxLayout _hbox; struct {
SignalLabel _label; QHBoxLayout hbox;
SignalLabel label;
} ui;
bool _mouseOver; bool mouseOver = false;
bool _mouseDown; bool mouseDown = false;
void labelMouseUp(); void labelMouseUp();
void labelMouseDown(); void labelMouseDown();

View file

@ -12,8 +12,9 @@ namespace chatterino {
namespace widgets { namespace widgets {
ChatWidgetInput::ChatWidgetInput(ChatWidget *_chatWidget) ChatWidgetInput::ChatWidgetInput(ChatWidget *_chatWidget)
: QWidget(_chatWidget) : BaseWidget(_chatWidget)
, chatWidget(_chatWidget) , chatWidget(_chatWidget)
, emotesLabel(this)
{ {
this->setMaximumHeight(150); this->setMaximumHeight(150);
@ -93,11 +94,11 @@ void ChatWidgetInput::refreshTheme()
{ {
QPalette palette; QPalette palette;
palette.setColor(QPalette::Foreground, ColorScheme::getInstance().Text); palette.setColor(QPalette::Foreground, this->colorScheme.Text);
this->textLengthLabel.setPalette(palette); this->textLengthLabel.setPalette(palette);
this->textInput.setStyleSheet(ColorScheme::getInstance().InputStyleSheet); this->textInput.setStyleSheet(this->colorScheme.InputStyleSheet);
} }
void ChatWidgetInput::editTextChanged() void ChatWidgetInput::editTextChanged()
@ -118,8 +119,8 @@ void ChatWidgetInput::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
painter.fillRect(this->rect(), ColorScheme::getInstance().ChatInputBackground); painter.fillRect(this->rect(), this->colorScheme.ChatInputBackground);
painter.setPen(ColorScheme::getInstance().ChatInputBorder); painter.setPen(this->colorScheme.ChatInputBorder);
painter.drawRect(0, 0, this->width() - 1, this->height() - 1); painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "resizingtextedit.hpp" #include "resizingtextedit.hpp"
#include "widgets/basewidget.hpp"
#include "widgets/chatwidgetheaderbutton.hpp" #include "widgets/chatwidgetheaderbutton.hpp"
#include <QHBoxLayout> #include <QHBoxLayout>
@ -16,7 +17,7 @@ namespace widgets {
class ChatWidget; class ChatWidget;
class ChatWidgetInput : public QWidget class ChatWidgetInput : public BaseWidget
{ {
Q_OBJECT Q_OBJECT

View file

@ -20,7 +20,7 @@ namespace chatterino {
namespace widgets { namespace widgets {
ChatWidgetView::ChatWidgetView(ChatWidget *_chatWidget) ChatWidgetView::ChatWidgetView(ChatWidget *_chatWidget)
: QWidget(_chatWidget) : BaseWidget(_chatWidget)
, chatWidget(_chatWidget) , chatWidget(_chatWidget)
, scrollBar(this) , scrollBar(this)
, userPopupWidget(_chatWidget->getChannelRef()) , userPopupWidget(_chatWidget->getChannelRef())
@ -47,7 +47,7 @@ bool ChatWidgetView::layoutMessages()
{ {
auto messages = this->chatWidget->getMessagesSnapshot(); auto messages = this->chatWidget->getMessagesSnapshot();
if (messages.getSize() == 0) { if (messages.getLength() == 0) {
this->scrollBar.setVisible(false); this->scrollBar.setVisible(false);
return false; return false;
} }
@ -65,10 +65,10 @@ bool ChatWidgetView::layoutMessages()
int layoutWidth = this->scrollBar.isVisible() ? width() - this->scrollBar.width() : width(); int layoutWidth = this->scrollBar.isVisible() ? width() - this->scrollBar.width() : width();
// layout the visible messages in the view // layout the visible messages in the view
if (messages.getSize() > start) { if (messages.getLength() > start) {
int y = -(messages[start]->getHeight() * (fmod(this->scrollBar.getCurrentValue(), 1))); int y = -(messages[start]->getHeight() * (fmod(this->scrollBar.getCurrentValue(), 1)));
for (int i = start; i < messages.getSize(); ++i) { for (int i = start; i < messages.getLength(); ++i) {
auto message = messages[i]; auto message = messages[i];
redraw |= message->layout(layoutWidth, true); redraw |= message->layout(layoutWidth, true);
@ -84,7 +84,7 @@ bool ChatWidgetView::layoutMessages()
// layout the messages at the bottom to determine the scrollbar thumb size // layout the messages at the bottom to determine the scrollbar thumb size
int h = height() - 8; int h = height() - 8;
for (int i = messages.getSize() - 1; i >= 0; i--) { for (std::size_t i = messages.getLength() - 1; i > 0; i--) {
auto *message = messages[i].get(); auto *message = messages[i].get();
message->layout(layoutWidth, true); message->layout(layoutWidth, true);
@ -92,7 +92,7 @@ bool ChatWidgetView::layoutMessages()
h -= message->getHeight(); h -= message->getHeight();
if (h < 0) { if (h < 0) {
this->scrollBar.setLargeChange((messages.getSize() - i) + this->scrollBar.setLargeChange((messages.getLength() - i) +
(qreal)h / message->getHeight()); (qreal)h / message->getHeight());
this->scrollBar.setDesiredValue(this->scrollBar.getDesiredValue()); this->scrollBar.setDesiredValue(this->scrollBar.getDesiredValue());
@ -107,7 +107,7 @@ bool ChatWidgetView::layoutMessages()
this->scrollBar.setDesiredValue(0); this->scrollBar.setDesiredValue(0);
} }
this->scrollBar.setMaximum(messages.getSize()); this->scrollBar.setMaximum(messages.getLength());
if (this->showingLatestMessages && showScrollbar) { if (this->showingLatestMessages && showScrollbar) {
// If we were showing the latest messages and the scrollbar now wants to be rendered, scroll // If we were showing the latest messages and the scrollbar now wants to be rendered, scroll
@ -148,14 +148,12 @@ void ChatWidgetView::paintEvent(QPaintEvent * /*event*/)
_painter.setRenderHint(QPainter::SmoothPixmapTransform); _painter.setRenderHint(QPainter::SmoothPixmapTransform);
ColorScheme &scheme = ColorScheme::getInstance();
// only update gif emotes // only update gif emotes
if (this->onlyUpdateEmotes) { if (this->onlyUpdateEmotes) {
this->onlyUpdateEmotes = false; this->onlyUpdateEmotes = false;
for (const GifEmoteData &item : this->gifEmotes) { for (const GifEmoteData &item : this->gifEmotes) {
_painter.fillRect(item.rect, scheme.ChatBackground); _painter.fillRect(item.rect, this->colorScheme.ChatBackground);
_painter.drawPixmap(item.rect, *item.image->getPixmap()); _painter.drawPixmap(item.rect, *item.image->getPixmap());
} }
@ -166,7 +164,7 @@ void ChatWidgetView::paintEvent(QPaintEvent * /*event*/)
// update all messages // update all messages
this->gifEmotes.clear(); this->gifEmotes.clear();
_painter.fillRect(rect(), scheme.ChatBackground); _painter.fillRect(rect(), this->colorScheme.ChatBackground);
// code for tesing colors // code for tesing colors
/* /*
@ -205,13 +203,13 @@ void ChatWidgetView::paintEvent(QPaintEvent * /*event*/)
int start = this->scrollBar.getCurrentValue(); int start = this->scrollBar.getCurrentValue();
if (start >= messages.getSize()) { if (start >= messages.getLength()) {
return; return;
} }
int y = -(messages[start].get()->getHeight() * (fmod(this->scrollBar.getCurrentValue(), 1))); int y = -(messages[start].get()->getHeight() * (fmod(this->scrollBar.getCurrentValue(), 1)));
for (int i = start; i < messages.getSize(); ++i) { for (int i = start; i < messages.getLength(); ++i) {
messages::MessageRef *messageRef = messages[i].get(); messages::MessageRef *messageRef = messages[i].get();
std::shared_ptr<QPixmap> bufferPtr = messageRef->buffer; std::shared_ptr<QPixmap> bufferPtr = messageRef->buffer;
@ -228,7 +226,7 @@ void ChatWidgetView::paintEvent(QPaintEvent * /*event*/)
// update messages that have been changed // update messages that have been changed
if (updateBuffer) { if (updateBuffer) {
QPainter painter(buffer); QPainter painter(buffer);
painter.fillRect(buffer->rect(), scheme.ChatBackground); painter.fillRect(buffer->rect(), this->colorScheme.ChatBackground);
for (messages::WordPart const &wordPart : messageRef->getWordParts()) { for (messages::WordPart const &wordPart : messageRef->getWordParts()) {
// image // image
@ -247,7 +245,7 @@ void ChatWidgetView::paintEvent(QPaintEvent * /*event*/)
else { else {
QColor color = wordPart.getWord().getColor(); QColor color = wordPart.getWord().getColor();
ColorScheme::getInstance().normalizeColor(color); this->colorScheme.normalizeColor(color);
painter.setPen(color); painter.setPen(color);
painter.setFont(wordPart.getWord().getFont()); painter.setFont(wordPart.getWord().getFont());
@ -266,14 +264,14 @@ void ChatWidgetView::paintEvent(QPaintEvent * /*event*/)
messages::LazyLoadedImage &lli = wordPart.getWord().getImage(); messages::LazyLoadedImage &lli = wordPart.getWord().getImage();
if (lli.getAnimated()) { if (lli.getAnimated()) {
GifEmoteData data; GifEmoteData gifEmoteData;
data.image = &lli; gifEmoteData.image = &lli;
QRect rect(wordPart.getX(), wordPart.getY() + y, wordPart.getWidth(), QRect rect(wordPart.getX(), wordPart.getY() + y, wordPart.getWidth(),
wordPart.getHeight()); wordPart.getHeight());
data.rect = rect; gifEmoteData.rect = rect;
this->gifEmotes.push_back(data); this->gifEmotes.push_back(gifEmoteData);
} }
} }
} }
@ -290,7 +288,7 @@ void ChatWidgetView::paintEvent(QPaintEvent * /*event*/)
} }
for (GifEmoteData &item : this->gifEmotes) { for (GifEmoteData &item : this->gifEmotes) {
_painter.fillRect(item.rect, scheme.ChatBackground); _painter.fillRect(item.rect, this->colorScheme.ChatBackground);
_painter.drawPixmap(item.rect, *item.image->getPixmap()); _painter.drawPixmap(item.rect, *item.image->getPixmap());
} }
@ -401,13 +399,13 @@ bool ChatWidgetView::tryGetMessageAt(QPoint p, std::shared_ptr<messages::Message
int start = this->scrollBar.getCurrentValue(); int start = this->scrollBar.getCurrentValue();
if (start >= messages.getSize()) { if (start >= messages.getLength()) {
return false; return false;
} }
int y = -(messages[start]->getHeight() * (fmod(this->scrollBar.getCurrentValue(), 1))); int y = -(messages[start]->getHeight() * (fmod(this->scrollBar.getCurrentValue(), 1)));
for (int i = start; i < messages.getSize(); ++i) { for (int i = start; i < messages.getLength(); ++i) {
auto message = messages[i]; auto message = messages[i];
if (p.y() < y + message->getHeight()) { if (p.y() < y + message->getHeight()) {

View file

@ -5,6 +5,7 @@
#include "messages/messageref.hpp" #include "messages/messageref.hpp"
#include "messages/word.hpp" #include "messages/word.hpp"
#include "widgets/accountpopup.hpp" #include "widgets/accountpopup.hpp"
#include "widgets/basewidget.hpp"
#include "widgets/scrollbar.hpp" #include "widgets/scrollbar.hpp"
#include <QPaintEvent> #include <QPaintEvent>
@ -17,7 +18,7 @@ namespace widgets {
class ChatWidget; class ChatWidget;
class ChatWidgetView : public QWidget class ChatWidgetView : public BaseWidget
{ {
public: public:
explicit ChatWidgetView(ChatWidget *_chatWidget); explicit ChatWidgetView(ChatWidget *_chatWidget);

View file

@ -6,49 +6,42 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
FancyButton::FancyButton(QWidget *parent) FancyButton::FancyButton(BaseWidget *parent)
: QWidget(parent) : BaseWidget(parent)
, _selected()
, _mouseOver()
, _mouseDown()
, _mousePos()
, _hoverMultiplier()
, _effectTimer()
, _mouseEffectColor(QColor(255, 255, 255))
{ {
connect(&_effectTimer, &QTimer::timeout, this, &FancyButton::onMouseEffectTimeout); connect(&effectTimer, &QTimer::timeout, this, &FancyButton::onMouseEffectTimeout);
_effectTimer.setInterval(20); this->effectTimer.setInterval(20);
_effectTimer.start(); this->effectTimer.start();
} }
void FancyButton::setMouseEffectColor(QColor color) void FancyButton::setMouseEffectColor(QColor color)
{ {
_mouseEffectColor = color; this->mouseEffectColor = color;
} }
void FancyButton::paintEvent(QPaintEvent *) void FancyButton::paintEvent(QPaintEvent *)
{ {
QPainter painter; QPainter painter;
fancyPaint(painter); this->fancyPaint(painter);
} }
void FancyButton::fancyPaint(QPainter &painter) void FancyButton::fancyPaint(QPainter &painter)
{ {
QColor &c = _mouseEffectColor; QColor &c = this->mouseEffectColor;
if (_hoverMultiplier > 0) { if (this->hoverMultiplier > 0) {
QRadialGradient gradient(_mousePos.x(), _mousePos.y(), 50, _mousePos.x(), _mousePos.y()); QRadialGradient gradient(mousePos.x(), mousePos.y(), 50, mousePos.x(), mousePos.y());
gradient.setColorAt(0, QColor(c.red(), c.green(), c.blue(), (int)(24 * _hoverMultiplier))); gradient.setColorAt(0, QColor(c.red(), c.green(), c.blue(), (int)(24 * this->hoverMultiplier)));
gradient.setColorAt(1, QColor(c.red(), c.green(), c.blue(), (int)(12 * _hoverMultiplier))); gradient.setColorAt(1, QColor(c.red(), c.green(), c.blue(), (int)(12 * this->hoverMultiplier)));
painter.fillRect(this->rect(), gradient); painter.fillRect(this->rect(), gradient);
} }
for (auto effect : _clickEffects) { for (auto effect : this->clickEffects) {
QRadialGradient gradient(effect.position.x(), effect.position.y(), QRadialGradient gradient(effect.position.x(), effect.position.y(),
effect.progress * (float)width() * 2, effect.position.x(), effect.progress * (float)width() * 2, effect.position.x(),
effect.position.y()); effect.position.y());
@ -65,12 +58,12 @@ void FancyButton::fancyPaint(QPainter &painter)
void FancyButton::enterEvent(QEvent *) void FancyButton::enterEvent(QEvent *)
{ {
_mouseOver = true; this->mouseOver = true;
} }
void FancyButton::leaveEvent(QEvent *) void FancyButton::leaveEvent(QEvent *)
{ {
_mouseOver = false; this->mouseOver = false;
} }
void FancyButton::mousePressEvent(QMouseEvent *event) void FancyButton::mousePressEvent(QMouseEvent *event)
@ -79,9 +72,9 @@ void FancyButton::mousePressEvent(QMouseEvent *event)
return; return;
} }
_clickEffects.push_back(ClickEffect(event->pos())); this->clickEffects.push_back(ClickEffect(event->pos()));
_mouseDown = true; this->mouseDown = true;
} }
void FancyButton::mouseReleaseEvent(QMouseEvent *event) void FancyButton::mouseReleaseEvent(QMouseEvent *event)
@ -90,43 +83,43 @@ void FancyButton::mouseReleaseEvent(QMouseEvent *event)
return; return;
} }
_mouseDown = false; this->mouseDown = false;
} }
void FancyButton::mouseMoveEvent(QMouseEvent *event) void FancyButton::mouseMoveEvent(QMouseEvent *event)
{ {
_mousePos = event->pos(); this->mousePos = event->pos();
} }
void FancyButton::onMouseEffectTimeout() void FancyButton::onMouseEffectTimeout()
{ {
bool performUpdate = false; bool performUpdate = false;
if (_selected) { if (selected) {
if (_hoverMultiplier != 0) { if (this->hoverMultiplier != 0) {
_hoverMultiplier = std::max(0.0, _hoverMultiplier - 0.1); this->hoverMultiplier = std::max(0.0, this->hoverMultiplier - 0.1);
performUpdate = true; performUpdate = true;
} }
} else if (_mouseOver) { } else if (mouseOver) {
if (_hoverMultiplier != 1) { if (this->hoverMultiplier != 1) {
_hoverMultiplier = std::min(1.0, _hoverMultiplier + 0.5); this->hoverMultiplier = std::min(1.0, this->hoverMultiplier + 0.5);
performUpdate = true; performUpdate = true;
} }
} else { } else {
if (_hoverMultiplier != 0) { if (this->hoverMultiplier != 0) {
_hoverMultiplier = std::max(0.0, _hoverMultiplier - 0.3); this->hoverMultiplier = std::max(0.0, this->hoverMultiplier - 0.3);
performUpdate = true; performUpdate = true;
} }
} }
if (_clickEffects.size() != 0) { if (this->clickEffects.size() != 0) {
performUpdate = true; performUpdate = true;
for (auto it = _clickEffects.begin(); it != _clickEffects.end();) { for (auto it = this->clickEffects.begin(); it != this->clickEffects.end();) {
(*it).progress += _mouseDown ? 0.02 : 0.07; (*it).progress += mouseDown ? 0.02 : 0.07;
if ((*it).progress >= 1.0) { if ((*it).progress >= 1.0) {
it = _clickEffects.erase(it); it = this->clickEffects.erase(it);
} else { } else {
it++; it++;
} }

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "widgets/basewidget.hpp"
#include <QMouseEvent> #include <QMouseEvent>
#include <QPainter> #include <QPainter>
#include <QPoint> #include <QPoint>
@ -9,43 +11,42 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
class FancyButton : public QWidget class FancyButton : public BaseWidget
{ {
struct ClickEffect { struct ClickEffect {
float progress; double progress = 0.0;
QPoint position; QPoint position;
ClickEffect(QPoint position) ClickEffect(QPoint _position)
: progress() : position(_position)
, position(position)
{ {
} }
}; };
public: public:
FancyButton(QWidget *parent = nullptr); FancyButton(BaseWidget *parent);
void setMouseEffectColor(QColor color); void setMouseEffectColor(QColor color);
protected: protected:
void paintEvent(QPaintEvent *) override; virtual void paintEvent(QPaintEvent *) override;
void enterEvent(QEvent *) override; virtual void enterEvent(QEvent *) override;
void leaveEvent(QEvent *) override; virtual void leaveEvent(QEvent *) override;
void mousePressEvent(QMouseEvent *event) override; virtual void mousePressEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override; virtual void mouseReleaseEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override; virtual void mouseMoveEvent(QMouseEvent *event) override;
void fancyPaint(QPainter &painter); void fancyPaint(QPainter &painter);
private: private:
bool _selected; bool selected = false;
bool _mouseOver; bool mouseOver = false;
bool _mouseDown; bool mouseDown = false;
QPoint _mousePos; QPoint mousePos;
float _hoverMultiplier; double hoverMultiplier = 0.0;
QTimer _effectTimer; QTimer effectTimer;
std::vector<ClickEffect> _clickEffects; std::vector<ClickEffect> clickEffects;
QColor _mouseEffectColor; QColor mouseEffectColor = {255, 255, 255};
void onMouseEffectTimeout(); void onMouseEffectTimeout();
}; };

View file

@ -19,12 +19,12 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
MainWindow::MainWindow(ChannelManager &_channelManager, QWidget *parent) MainWindow::MainWindow(ChannelManager &_channelManager, ColorScheme &_colorScheme)
: QWidget(parent) : BaseWidget(_colorScheme, nullptr)
, channelManager(_channelManager) , channelManager(_channelManager)
, colorScheme(_colorScheme)
, notebook(this->channelManager, this) , notebook(this->channelManager, this)
, _loaded(false) , windowGeometry("/windows/0/geometry")
, _titleBar()
{ {
QVBoxLayout *layout = new QVBoxLayout(this); QVBoxLayout *layout = new QVBoxLayout(this);
@ -44,10 +44,17 @@ MainWindow::MainWindow(ChannelManager &_channelManager, QWidget *parent)
// } // }
QPalette palette; QPalette palette;
palette.setColor(QPalette::Background, ColorScheme::getInstance().TabPanelBackground); palette.setColor(QPalette::Background, this->colorScheme.TabPanelBackground);
setPalette(palette); setPalette(palette);
resize(1280, 800); if (this->windowGeometry->isFilled()) {
// Load geometry from settings file
this->setGeometry(this->windowGeometry.getValueRef());
} else {
// Set default geometry
// Default position is in the middle of the current monitor or the primary monitor
this->resize(1280, 800);
}
// Initialize program-wide hotkeys // Initialize program-wide hotkeys
{ {
@ -122,7 +129,7 @@ void MainWindow::load(const boost::property_tree::ptree &tree)
{ {
this->notebook.load(tree); this->notebook.load(tree);
_loaded = true; loaded = true;
} }
boost::property_tree::ptree MainWindow::save() boost::property_tree::ptree MainWindow::save()
@ -140,12 +147,12 @@ void MainWindow::loadDefaults()
{ {
this->notebook.loadDefaults(); this->notebook.loadDefaults();
_loaded = true; loaded = true;
} }
bool MainWindow::isLoaded() const bool MainWindow::isLoaded() const
{ {
return _loaded; return loaded;
} }
Notebook &MainWindow::getNotebook() Notebook &MainWindow::getNotebook()
@ -153,5 +160,11 @@ Notebook &MainWindow::getNotebook()
return this->notebook; return this->notebook;
} }
void MainWindow::closeEvent(QCloseEvent *event)
{
// Save closing window position
this->windowGeometry = this->geometry();
}
} // namespace widgets } // namespace widgets
} // namespace chatterino } // namespace chatterino

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "widgets/basewidget.hpp"
#include "widgets/notebook.hpp" #include "widgets/notebook.hpp"
#include "widgets/titlebar.hpp" #include "widgets/titlebar.hpp"
@ -9,19 +10,22 @@
#include <QMainWindow> #include <QMainWindow>
#include <boost/property_tree/ptree.hpp> #include <boost/property_tree/ptree.hpp>
#include <pajlada/settings/serialize.hpp>
#include <pajlada/settings/settingdata.hpp>
namespace chatterino { namespace chatterino {
class ChannelManager; class ChannelManager;
class ColorScheme;
namespace widgets { namespace widgets {
class MainWindow : public QWidget class MainWindow : public BaseWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit MainWindow(ChannelManager &_channelManager, QWidget *parent = nullptr); explicit MainWindow(ChannelManager &_channelManager, ColorScheme &_colorScheme);
~MainWindow(); ~MainWindow();
void layoutVisibleChatWidgets(Channel *channel = nullptr); void layoutVisibleChatWidgets(Channel *channel = nullptr);
@ -36,12 +40,108 @@ public:
Notebook &getNotebook(); Notebook &getNotebook();
protected:
virtual void closeEvent(QCloseEvent *event) override;
private: private:
ChannelManager &channelManager; ChannelManager &channelManager;
ColorScheme &colorScheme;
Notebook notebook; Notebook notebook;
bool _loaded; bool loaded = false;
TitleBar _titleBar; TitleBar titleBar;
class QRectWrapper : public pajlada::Settings::ISettingData, public QRect
{
public:
QRectWrapper()
: QRect(-1, -1, -1, -1)
{
}
pajlada::Signals::Signal<const QRectWrapper &> valueChanged;
/*
operator const QRect &() const
{
return static_cast<const QRect &>(*this);
// return this->getValue();
}
*/
const QRectWrapper &getValueRef() const
{
return *this;
}
virtual rapidjson::Value marshalInto(rapidjson::Document &d) override
{
using namespace pajlada::Settings;
rapidjson::Value obj(rapidjson::kObjectType);
auto _x = serializeToJSON<int>::serialize(this->x(), d.GetAllocator());
auto _y = serializeToJSON<int>::serialize(this->y(), d.GetAllocator());
auto _width = serializeToJSON<int>::serialize(this->width(), d.GetAllocator());
auto _height = serializeToJSON<int>::serialize(this->height(), d.GetAllocator());
obj.AddMember("x", _x, d.GetAllocator());
obj.AddMember("y", _y, d.GetAllocator());
obj.AddMember("width", _width, d.GetAllocator());
obj.AddMember("height", _height, d.GetAllocator());
return obj;
}
virtual bool unmarshalFrom(rapidjson::Document &document) override
{
using namespace pajlada::Settings;
auto vXp = this->getValueWithSuffix("/x", document);
auto vYp = this->getValueWithSuffix("/y", document);
auto vWidthp = this->getValueWithSuffix("/width", document);
auto vHeightp = this->getValueWithSuffix("/height", document);
if (vXp != nullptr) {
this->setX(deserializeJSON<int>::deserialize(*vXp));
this->filled = true;
}
if (vYp != nullptr) {
this->setY(deserializeJSON<int>::deserialize(*vYp));
this->filled = true;
}
if (vWidthp != nullptr) {
this->setWidth(deserializeJSON<int>::deserialize(*vWidthp));
this->filled = true;
}
if (vHeightp != nullptr) {
this->setHeight(deserializeJSON<int>::deserialize(*vHeightp));
this->filled = true;
}
return true;
}
virtual void registerDocument(rapidjson::Document &d) override
{
this->valueChanged.connect([this, &d](const auto &) {
this->marshalInto(d); //
});
}
QRectWrapper &operator=(const QRect &rhs)
{
static_cast<QRect &>(*this) = rhs;
return *this;
}
void setValue(const QRect &rhs)
{
static_cast<QRect &>(*this) = rhs;
}
};
pajlada::Settings::Setting<QRectWrapper, QRectWrapper> windowGeometry;
}; };
} // namespace widgets } // namespace widgets

View file

@ -18,26 +18,26 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
Notebook::Notebook(ChannelManager &_channelManager, QWidget *parent) Notebook::Notebook(ChannelManager &_channelManager, BaseWidget *parent)
: QWidget(parent) : BaseWidget(parent)
, channelManager(_channelManager) , channelManager(_channelManager)
, _addButton(this) , addButton(this)
, _settingsButton(this) , settingsButton(this)
, _userButton(this) , userButton(this)
, _selectedPage(nullptr) , selectedPage(nullptr)
{ {
connect(&_settingsButton, SIGNAL(clicked()), this, SLOT(settingsButtonClicked())); connect(&settingsButton, SIGNAL(clicked()), this, SLOT(settingsButtonClicked()));
connect(&_userButton, SIGNAL(clicked()), this, SLOT(usersButtonClicked())); connect(&userButton, SIGNAL(clicked()), this, SLOT(usersButtonClicked()));
connect(&_addButton, SIGNAL(clicked()), this, SLOT(addPageButtonClicked())); connect(&addButton, SIGNAL(clicked()), this, SLOT(addPageButtonClicked()));
_settingsButton.resize(24, 24); settingsButton.resize(24, 24);
_settingsButton.icon = NotebookButton::IconSettings; settingsButton.icon = NotebookButton::IconSettings;
_userButton.resize(24, 24); userButton.resize(24, 24);
_userButton.move(24, 0); userButton.move(24, 0);
_userButton.icon = NotebookButton::IconUser; userButton.icon = NotebookButton::IconUser;
_addButton.resize(24, 24); addButton.resize(24, 24);
SettingsManager::getInstance().hidePreferencesButton.valueChanged.connect( SettingsManager::getInstance().hidePreferencesButton.valueChanged.connect(
[this](const bool &) { performLayout(); }); [this](const bool &) { performLayout(); });
@ -52,11 +52,11 @@ NotebookPage *Notebook::addPage(bool select)
tab->show(); tab->show();
if (select || _pages.count() == 0) { if (select || pages.count() == 0) {
this->select(page); this->select(page);
} }
_pages.append(page); pages.append(page);
performLayout(); performLayout();
@ -65,22 +65,22 @@ NotebookPage *Notebook::addPage(bool select)
void Notebook::removePage(NotebookPage *page) void Notebook::removePage(NotebookPage *page)
{ {
int index = _pages.indexOf(page); int index = pages.indexOf(page);
if (_pages.size() == 1) { if (pages.size() == 1) {
select(nullptr); select(nullptr);
} else if (index == _pages.count() - 1) { } else if (index == pages.count() - 1) {
select(_pages[index - 1]); select(pages[index - 1]);
} else { } else {
select(_pages[index + 1]); select(pages[index + 1]);
} }
delete page->getTab(); delete page->getTab();
delete page; delete page;
_pages.removeOne(page); pages.removeOne(page);
if (_pages.size() == 0) { if (pages.size() == 0) {
addPage(); addPage();
} }
@ -89,7 +89,7 @@ void Notebook::removePage(NotebookPage *page)
void Notebook::select(NotebookPage *page) void Notebook::select(NotebookPage *page)
{ {
if (page == _selectedPage) if (page == selectedPage)
return; return;
if (page != nullptr) { if (page != nullptr) {
@ -98,12 +98,12 @@ void Notebook::select(NotebookPage *page)
page->getTab()->raise(); page->getTab()->raise();
} }
if (_selectedPage != nullptr) { if (selectedPage != nullptr) {
_selectedPage->setHidden(true); selectedPage->setHidden(true);
_selectedPage->getTab()->setSelected(false); selectedPage->getTab()->setSelected(false);
} }
_selectedPage = page; selectedPage = page;
performLayout(); performLayout();
} }
@ -112,7 +112,7 @@ NotebookPage *Notebook::tabAt(QPoint point, int &index)
{ {
int i = 0; int i = 0;
for (auto *page : _pages) { for (auto *page : pages) {
if (page->getTab()->getDesiredRect().contains(point)) { if (page->getTab()->getDesiredRect().contains(point)) {
index = i; index = i;
return page; return page;
@ -127,7 +127,7 @@ NotebookPage *Notebook::tabAt(QPoint point, int &index)
void Notebook::rearrangePage(NotebookPage *page, int index) void Notebook::rearrangePage(NotebookPage *page, int index)
{ {
_pages.move(_pages.indexOf(page), index); pages.move(pages.indexOf(page), index);
performLayout(); performLayout();
} }
@ -137,26 +137,26 @@ void Notebook::performLayout(bool animated)
int x = 0, y = 0; int x = 0, y = 0;
if (SettingsManager::getInstance().hidePreferencesButton.get()) { if (SettingsManager::getInstance().hidePreferencesButton.get()) {
_settingsButton.hide(); settingsButton.hide();
} else { } else {
_settingsButton.show(); settingsButton.show();
x += 24; x += 24;
} }
if (SettingsManager::getInstance().hideUserButton.get()) { if (SettingsManager::getInstance().hideUserButton.get()) {
_userButton.hide(); userButton.hide();
} else { } else {
_userButton.move(x, 0); userButton.move(x, 0);
_userButton.show(); userButton.show();
x += 24; x += 24;
} }
int tabHeight = 16; int tabHeight = 16;
bool first = true; bool first = true;
for (auto &i : _pages) { for (auto &i : pages) {
tabHeight = i->getTab()->height(); tabHeight = i->getTab()->height();
if (!first && (i == _pages.last() ? tabHeight : 0) + x + i->getTab()->width() > width()) { if (!first && (i == pages.last() ? tabHeight : 0) + x + i->getTab()->width() > width()) {
y += i->getTab()->height(); y += i->getTab()->height();
i->getTab()->moveAnimated(QPoint(0, y), animated); i->getTab()->moveAnimated(QPoint(0, y), animated);
x = i->getTab()->width(); x = i->getTab()->width();
@ -168,11 +168,11 @@ void Notebook::performLayout(bool animated)
first = false; first = false;
} }
_addButton.move(x, y); addButton.move(x, y);
if (_selectedPage != nullptr) { if (selectedPage != nullptr) {
_selectedPage->move(0, y + tabHeight); selectedPage->move(0, y + tabHeight);
_selectedPage->resize(width(), height() - y - tabHeight); selectedPage->resize(width(), height() - y - tabHeight);
} }
} }
@ -211,7 +211,7 @@ void Notebook::load(const boost::property_tree::ptree &tree)
// can't read tabs // can't read tabs
} }
if (_pages.size() == 0) { if (pages.size() == 0) {
// No pages saved, show default stuff // No pages saved, show default stuff
loadDefaults(); loadDefaults();
} }
@ -222,7 +222,7 @@ void Notebook::save(boost::property_tree::ptree &tree)
boost::property_tree::ptree tabs; boost::property_tree::ptree tabs;
// Iterate through all tabs and add them to our tabs property thing // Iterate through all tabs and add them to our tabs property thing
for (const auto &page : _pages) { for (const auto &page : pages) {
boost::property_tree::ptree pTab = page->getTab()->save(); boost::property_tree::ptree pTab = page->getTab()->save();
boost::property_tree::ptree pChats = page->save(); boost::property_tree::ptree pChats = page->save();

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "widgets/basewidget.hpp"
#include "widgets/notebookbutton.hpp" #include "widgets/notebookbutton.hpp"
#include "widgets/notebookpage.hpp" #include "widgets/notebookpage.hpp"
#include "widgets/notebooktab.hpp" #include "widgets/notebooktab.hpp"
@ -11,17 +12,18 @@
namespace chatterino { namespace chatterino {
class ChannelManager; class ChannelManager;
class ColorScheme;
namespace widgets { namespace widgets {
class Notebook : public QWidget class Notebook : public BaseWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
enum HighlightType { none, highlighted, newMessage }; enum HighlightType { none, highlighted, newMessage };
Notebook(ChannelManager &_channelManager, QWidget *parent); explicit Notebook(ChannelManager &_channelManager, BaseWidget *parent);
NotebookPage *addPage(bool select = false); NotebookPage *addPage(bool select = false);
@ -30,7 +32,7 @@ public:
NotebookPage *getSelectedPage() NotebookPage *getSelectedPage()
{ {
return _selectedPage; return selectedPage;
} }
void performLayout(bool animate = true); void performLayout(bool animate = true);
@ -51,13 +53,13 @@ public slots:
private: private:
ChannelManager &channelManager; ChannelManager &channelManager;
QList<NotebookPage *> _pages; QList<NotebookPage *> pages;
NotebookButton _addButton; NotebookButton addButton;
NotebookButton _settingsButton; NotebookButton settingsButton;
NotebookButton _userButton; NotebookButton userButton;
NotebookPage *_selectedPage; NotebookPage *selectedPage;
public: public:
void load(const boost::property_tree::ptree &tree); void load(const boost::property_tree::ptree &tree);

View file

@ -10,7 +10,7 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
NotebookButton::NotebookButton(QWidget *parent) NotebookButton::NotebookButton(BaseWidget *parent)
: FancyButton(parent) : FancyButton(parent)
{ {
setMouseEffectColor(QColor(0, 0, 0)); setMouseEffectColor(QColor(0, 0, 0));
@ -23,17 +23,15 @@ void NotebookButton::paintEvent(QPaintEvent *)
QColor background; QColor background;
QColor foreground; QColor foreground;
auto &colorScheme = ColorScheme::getInstance(); if (mouseDown) {
background = this->colorScheme.TabSelectedBackground;
if (_mouseDown) { foreground = this->colorScheme.TabSelectedText;
background = colorScheme.TabSelectedBackground; } else if (mouseOver) {
foreground = colorScheme.TabSelectedText; background = this->colorScheme.TabHoverBackground;
} else if (_mouseOver) { foreground = this->colorScheme.TabSelectedBackground;
background = colorScheme.TabHoverBackground;
foreground = colorScheme.TabSelectedBackground;
} else { } else {
background = colorScheme.TabPanelBackground; background = this->colorScheme.TabPanelBackground;
// foreground = colorScheme.TabSelectedBackground; // foreground = this->colorScheme.TabSelectedBackground;
foreground = QColor(230, 230, 230); foreground = QColor(230, 230, 230);
} }
@ -93,7 +91,7 @@ void NotebookButton::paintEvent(QPaintEvent *)
void NotebookButton::mouseReleaseEvent(QMouseEvent *event) void NotebookButton::mouseReleaseEvent(QMouseEvent *event)
{ {
if (event->button() == Qt::LeftButton) { if (event->button() == Qt::LeftButton) {
_mouseDown = false; mouseDown = false;
update(); update();

View file

@ -18,7 +18,7 @@ public:
int icon = 0; int icon = 0;
NotebookButton(QWidget *parent); NotebookButton(BaseWidget *parent);
protected: protected:
void paintEvent(QPaintEvent *) override; void paintEvent(QPaintEvent *) override;
@ -28,9 +28,9 @@ signals:
void clicked(); void clicked();
private: private:
bool _mouseOver = false; bool mouseOver = false;
bool _mouseDown = false; bool mouseDown = false;
QPoint _mousePos; QPoint mousePos;
}; };
} // namespace widgets } // namespace widgets

View file

@ -1,6 +1,7 @@
#include "widgets/notebookpage.hpp" #include "widgets/notebookpage.hpp"
#include "colorscheme.hpp" #include "colorscheme.hpp"
#include "widgets/chatwidget.hpp" #include "widgets/chatwidget.hpp"
#include "widgets/notebook.hpp"
#include "widgets/notebooktab.hpp" #include "widgets/notebooktab.hpp"
#include <QDebug> #include <QDebug>
@ -19,8 +20,8 @@ bool NotebookPage::isDraggingSplit = false;
ChatWidget *NotebookPage::draggingSplit = nullptr; ChatWidget *NotebookPage::draggingSplit = nullptr;
std::pair<int, int> NotebookPage::dropPosition = std::pair<int, int>(-1, -1); std::pair<int, int> NotebookPage::dropPosition = std::pair<int, int>(-1, -1);
NotebookPage::NotebookPage(ChannelManager &_channelManager, QWidget *parent, NotebookTab *_tab) NotebookPage::NotebookPage(ChannelManager &_channelManager, Notebook *parent, NotebookTab *_tab)
: QWidget(parent) : BaseWidget(parent->colorScheme, parent)
, channelManager(_channelManager) , channelManager(_channelManager)
, tab(_tab) , tab(_tab)
, _parentbox(this) , _parentbox(this)
@ -52,7 +53,7 @@ NotebookTab *NotebookPage::getTab() const
void NotebookPage::addChat(bool openChannelNameDialog) void NotebookPage::addChat(bool openChannelNameDialog)
{ {
ChatWidget *w = new ChatWidget(this->channelManager); ChatWidget *w = this->createChatWidget();
if (openChannelNameDialog) { if (openChannelNameDialog) {
w->showChangeChannelPopup(); w->showChangeChannelPopup();
@ -130,9 +131,9 @@ void NotebookPage::addToLayout(ChatWidget *widget,
void NotebookPage::enterEvent(QEvent *) void NotebookPage::enterEvent(QEvent *)
{ {
if (_hbox.count() == 0) { if (_hbox.count() == 0) {
setCursor(QCursor(Qt::PointingHandCursor)); this->setCursor(QCursor(Qt::PointingHandCursor));
} else { } else {
setCursor(QCursor(Qt::ArrowCursor)); this->setCursor(QCursor(Qt::ArrowCursor));
} }
} }
@ -144,9 +145,9 @@ void NotebookPage::mouseReleaseEvent(QMouseEvent *event)
{ {
if (_hbox.count() == 0 && event->button() == Qt::LeftButton) { if (_hbox.count() == 0 && event->button() == Qt::LeftButton) {
// "Add Chat" was clicked // "Add Chat" was clicked
addToLayout(new ChatWidget(this->channelManager), std::pair<int, int>(-1, -1)); this->addToLayout(this->createChatWidget(), std::pair<int, int>(-1, -1));
setCursor(QCursor(Qt::ArrowCursor)); this->setCursor(QCursor(Qt::ArrowCursor));
} }
} }
@ -236,16 +237,16 @@ void NotebookPage::paintEvent(QPaintEvent *)
QPainter painter(this); QPainter painter(this);
if (_hbox.count() == 0) { if (_hbox.count() == 0) {
painter.fillRect(rect(), ColorScheme::getInstance().ChatBackground); painter.fillRect(rect(), this->colorScheme.ChatBackground);
painter.fillRect(0, 0, width(), 2, ColorScheme::getInstance().TabSelectedBackground); painter.fillRect(0, 0, width(), 2, this->colorScheme.TabSelectedBackground);
painter.setPen(ColorScheme::getInstance().Text); painter.setPen(this->colorScheme.Text);
painter.drawText(rect(), "Add Chat", QTextOption(Qt::AlignCenter)); painter.drawText(rect(), "Add Chat", QTextOption(Qt::AlignCenter));
} else { } else {
painter.fillRect(rect(), ColorScheme::getInstance().TabSelectedBackground); painter.fillRect(rect(), this->colorScheme.TabSelectedBackground);
painter.fillRect(0, 0, width(), 2, ColorScheme::getInstance().TabSelectedBackground); painter.fillRect(0, 0, width(), 2, this->colorScheme.TabSelectedBackground);
} }
} }
@ -269,6 +270,11 @@ std::pair<int, int> NotebookPage::getChatPosition(const ChatWidget *chatWidget)
return getWidgetPositionInLayout(layout, chatWidget); return getWidgetPositionInLayout(layout, chatWidget);
} }
ChatWidget *NotebookPage::createChatWidget()
{
return new ChatWidget(this->channelManager, this);
}
void NotebookPage::load(const boost::property_tree::ptree &tree) void NotebookPage::load(const boost::property_tree::ptree &tree)
{ {
try { try {
@ -276,7 +282,7 @@ void NotebookPage::load(const boost::property_tree::ptree &tree)
for (const auto &v : tree.get_child("columns.")) { for (const auto &v : tree.get_child("columns.")) {
int row = 0; int row = 0;
for (const auto &innerV : v.second.get_child("")) { for (const auto &innerV : v.second.get_child("")) {
auto widget = new ChatWidget(this->channelManager); auto widget = this->createChatWidget();
widget->load(innerV.second); widget->load(innerV.second);
addToLayout(widget, std::pair<int, int>(column, row)); addToLayout(widget, std::pair<int, int>(column, row));
++row; ++row;

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "widgets/basewidget.hpp"
#include "widgets/chatwidget.hpp" #include "widgets/chatwidget.hpp"
#include "widgets/notebookpage.hpp" #include "widgets/notebookpage.hpp"
#include "widgets/notebookpagedroppreview.hpp" #include "widgets/notebookpagedroppreview.hpp"
@ -20,12 +21,14 @@ class ChannelManager;
namespace widgets { namespace widgets {
class NotebookPage : public QWidget class NotebookPage : public BaseWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
NotebookPage(ChannelManager &_channelManager, QWidget *parent, NotebookTab *_tab); NotebookPage(ChannelManager &_channelManager, Notebook *parent, NotebookTab *_tab);
ChannelManager &channelManager;
std::pair<int, int> removeFromLayout(ChatWidget *widget); std::pair<int, int> removeFromLayout(ChatWidget *widget);
void addToLayout(ChatWidget *widget, std::pair<int, int> position); void addToLayout(ChatWidget *widget, std::pair<int, int> position);
@ -52,8 +55,6 @@ protected:
void dropEvent(QDropEvent *event) override; void dropEvent(QDropEvent *event) override;
private: private:
ChannelManager &channelManager;
struct DropRegion { struct DropRegion {
QRect rect; QRect rect;
std::pair<int, int> position; std::pair<int, int> position;
@ -79,6 +80,8 @@ private:
std::pair<int, int> getChatPosition(const ChatWidget *chatWidget); std::pair<int, int> getChatPosition(const ChatWidget *chatWidget);
ChatWidget *createChatWidget();
public: public:
void load(const boost::property_tree::ptree &tree); void load(const boost::property_tree::ptree &tree);
boost::property_tree::ptree save(); boost::property_tree::ptree save();

View file

@ -1,5 +1,4 @@
#include "widgets/notebookpagedroppreview.hpp" #include "widgets/notebookpagedroppreview.hpp"
#include "colorscheme.hpp"
#include <QDebug> #include <QDebug>
#include <QPainter> #include <QPainter>
@ -7,11 +6,9 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
NotebookPageDropPreview::NotebookPageDropPreview(QWidget *parent) NotebookPageDropPreview::NotebookPageDropPreview(BaseWidget *parent)
: QWidget(parent) : BaseWidget(parent)
, positionAnimation(this, "geometry") , positionAnimation(this, "geometry")
, desiredGeometry()
, animate(false)
{ {
this->positionAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic)); this->positionAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
this->setHidden(true); this->setHidden(true);
@ -21,13 +18,12 @@ void NotebookPageDropPreview::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
painter.fillRect(8, 8, width() - 17, height() - 17, painter.fillRect(8, 8, width() - 17, height() - 17, this->colorScheme.DropPreviewBackground);
ColorScheme::getInstance().DropPreviewBackground);
} }
void NotebookPageDropPreview::hideEvent(QHideEvent *) void NotebookPageDropPreview::hideEvent(QHideEvent *)
{ {
animate = false; this->animate = false;
} }
void NotebookPageDropPreview::setBounds(const QRect &rect) void NotebookPageDropPreview::setBounds(const QRect &rect)
@ -36,7 +32,7 @@ void NotebookPageDropPreview::setBounds(const QRect &rect)
return; return;
} }
if (animate) { if (this->animate) {
this->positionAnimation.stop(); this->positionAnimation.stop();
this->positionAnimation.setDuration(50); this->positionAnimation.setDuration(50);
this->positionAnimation.setStartValue(this->geometry()); this->positionAnimation.setStartValue(this->geometry());
@ -48,7 +44,7 @@ void NotebookPageDropPreview::setBounds(const QRect &rect)
this->desiredGeometry = rect; this->desiredGeometry = rect;
animate = true; this->animate = true;
} }
} // namespace widgets } // namespace widgets

View file

@ -1,26 +1,27 @@
#pragma once #pragma once
#include "widgets/basewidget.hpp"
#include <QPropertyAnimation> #include <QPropertyAnimation>
#include <QWidget> #include <QWidget>
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
class NotebookPageDropPreview : public QWidget class NotebookPageDropPreview : public BaseWidget
{ {
public: public:
NotebookPageDropPreview(QWidget *parent); NotebookPageDropPreview(BaseWidget *parent);
void setBounds(const QRect &rect); void setBounds(const QRect &rect);
protected: protected:
void paintEvent(QPaintEvent *); virtual void paintEvent(QPaintEvent *) override;
virtual void hideEvent(QHideEvent *) override;
void hideEvent(QHideEvent *);
QPropertyAnimation positionAnimation; QPropertyAnimation positionAnimation;
QRect desiredGeometry; QRect desiredGeometry;
bool animate; bool animate = false;
}; };
} // namespace widgets } // namespace widgets

View file

@ -10,17 +10,9 @@ namespace widgets {
NotebookTab::NotebookTab(Notebook *notebook) NotebookTab::NotebookTab(Notebook *notebook)
: QWidget(notebook) : QWidget(notebook)
, colorScheme(notebook->colorScheme)
, _posAnimation(this, "pos") , _posAnimation(this, "pos")
, _posAnimated(false)
, _posAnimationDesired()
, _notebook(notebook) , _notebook(notebook)
, _title("<no title>")
, _selected(false)
, _mouseOver(false)
, _mouseDown(false)
, _mouseOverX(false)
, _mouseDownX(false)
, _highlightStyle(HighlightNone)
{ {
this->calcSize(); this->calcSize();
this->setAcceptDrops(true); this->setAcceptDrops(true);
@ -122,23 +114,21 @@ void NotebookTab::paintEvent(QPaintEvent *)
QColor fg = QColor(0, 0, 0); QColor fg = QColor(0, 0, 0);
auto &colorScheme = ColorScheme::getInstance();
if (_selected) { if (_selected) {
painter.fillRect(rect(), colorScheme.TabSelectedBackground); painter.fillRect(rect(), this->colorScheme.TabSelectedBackground);
fg = colorScheme.TabSelectedText; fg = this->colorScheme.TabSelectedText;
} else if (_mouseOver) { } else if (_mouseOver) {
painter.fillRect(rect(), colorScheme.TabHoverBackground); painter.fillRect(rect(), this->colorScheme.TabHoverBackground);
fg = colorScheme.TabHoverText; fg = this->colorScheme.TabHoverText;
} else if (_highlightStyle == HighlightHighlighted) { } else if (_highlightStyle == HighlightHighlighted) {
painter.fillRect(rect(), colorScheme.TabHighlightedBackground); painter.fillRect(rect(), this->colorScheme.TabHighlightedBackground);
fg = colorScheme.TabHighlightedText; fg = this->colorScheme.TabHighlightedText;
} else if (_highlightStyle == HighlightNewMessage) { } else if (_highlightStyle == HighlightNewMessage) {
painter.fillRect(rect(), colorScheme.TabNewMessageBackground); painter.fillRect(rect(), this->colorScheme.TabNewMessageBackground);
fg = colorScheme.TabHighlightedText; fg = this->colorScheme.TabHighlightedText;
} else { } else {
painter.fillRect(rect(), colorScheme.TabBackground); painter.fillRect(rect(), this->colorScheme.TabBackground);
fg = colorScheme.TabText; fg = this->colorScheme.TabText;
} }
painter.setPen(fg); painter.setPen(fg);

View file

@ -7,6 +7,9 @@
#include <boost/signals2/connection.hpp> #include <boost/signals2/connection.hpp>
namespace chatterino { namespace chatterino {
class ColorScheme;
namespace widgets { namespace widgets {
class Notebook; class Notebook;
@ -22,6 +25,8 @@ public:
explicit NotebookTab(Notebook *_notebook); explicit NotebookTab(Notebook *_notebook);
~NotebookTab(); ~NotebookTab();
ColorScheme &colorScheme;
void calcSize(); void calcSize();
NotebookPage *page; NotebookPage *page;
@ -55,20 +60,20 @@ private:
boost::signals2::connection _hideXConnection; boost::signals2::connection _hideXConnection;
QPropertyAnimation _posAnimation; QPropertyAnimation _posAnimation;
bool _posAnimated; bool _posAnimated = false;
QPoint _posAnimationDesired; QPoint _posAnimationDesired;
Notebook *_notebook; Notebook *_notebook;
QString _title; QString _title = "<no title>";
bool _selected; bool _selected = false;
bool _mouseOver; bool _mouseOver = false;
bool _mouseDown; bool _mouseDown = false;
bool _mouseOverX; bool _mouseOverX = false;
bool _mouseDownX; bool _mouseDownX = false;
HighlightStyle _highlightStyle; HighlightStyle _highlightStyle = HighlightStyle::HighlightNone;
QRect getXRect() QRect getXRect()
{ {

View file

@ -1,5 +1,6 @@
#include "widgets/scrollbar.hpp" #include "widgets/scrollbar.hpp"
#include "colorscheme.hpp" #include "colorscheme.hpp"
#include "widgets/chatwidgetview.hpp"
#include <QDebug> #include <QDebug>
#include <QMouseEvent> #include <QMouseEvent>
@ -10,8 +11,8 @@
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
ScrollBar::ScrollBar(QWidget *widget) ScrollBar::ScrollBar(ChatWidgetView *parent)
: QWidget(widget) : BaseWidget(parent)
, _currentValueAnimation(this, "currentValue") , _currentValueAnimation(this, "currentValue")
, _highlights(nullptr) , _highlights(nullptr)
{ {
@ -197,7 +198,7 @@ void ScrollBar::printCurrentState(const QString &prefix) const
void ScrollBar::paintEvent(QPaintEvent *) void ScrollBar::paintEvent(QPaintEvent *)
{ {
QPainter painter(this); QPainter painter(this);
painter.fillRect(rect(), ColorScheme::getInstance().ScrollbarBG); painter.fillRect(rect(), this->colorScheme.ScrollbarBG);
painter.fillRect(QRect(0, 0, width(), _buttonHeight), QColor(255, 0, 0)); painter.fillRect(QRect(0, 0, width(), _buttonHeight), QColor(255, 0, 0));
painter.fillRect(QRect(0, height() - _buttonHeight, width(), _buttonHeight), QColor(255, 0, 0)); painter.fillRect(QRect(0, height() - _buttonHeight, width(), _buttonHeight), QColor(255, 0, 0));

View file

@ -1,5 +1,6 @@
#pragma once #pragma once
#include "widgets/basewidget.hpp"
#include "widgets/scrollbarhighlight.hpp" #include "widgets/scrollbarhighlight.hpp"
#include <QMutex> #include <QMutex>
@ -8,14 +9,19 @@
#include <boost/signals2.hpp> #include <boost/signals2.hpp>
namespace chatterino { namespace chatterino {
class ColorScheme;
namespace widgets { namespace widgets {
class ScrollBar : public QWidget class ChatWidgetView;
class ScrollBar : public BaseWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
ScrollBar(QWidget *parent = 0); ScrollBar(ChatWidgetView *parent = 0);
~ScrollBar(); ~ScrollBar();
void removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func); void removeHighlightsWhere(std::function<bool(ScrollBarHighlight &)> func);

View file

@ -1,14 +1,17 @@
#include "widgets/scrollbarhighlight.hpp" #include "widgets/scrollbarhighlight.hpp"
#include "colorscheme.hpp" #include "colorscheme.hpp"
#include "widgets/scrollbar.hpp"
namespace chatterino { namespace chatterino {
namespace widgets { namespace widgets {
ScrollBarHighlight::ScrollBarHighlight(float position, int colorIndex, Style style, QString tag) ScrollBarHighlight::ScrollBarHighlight(double _position, int _colorIndex, ScrollBar *parent,
: _style(style) Style _style, QString _tag)
, _position(position) : colorScheme(parent->colorScheme)
, _colorIndex(std::max(0, std::min(ColorScheme::getInstance().HighlightColorCount, colorIndex))) , position(_position)
, _tag(tag) , colorIndex(std::max(0, std::min(this->colorScheme.HighlightColorCount, _colorIndex)))
, style(_style)
, tag(_tag)
{ {
} }

View file

@ -3,43 +3,50 @@
#include "QString" #include "QString"
namespace chatterino { namespace chatterino {
class ColorScheme;
namespace widgets { namespace widgets {
class ScrollBar;
class ScrollBarHighlight class ScrollBarHighlight
{ {
public: public:
enum Style { Default, Left, Right, SingleLine }; enum Style { Default, Left, Right, SingleLine };
ScrollBarHighlight(float getPosition, int getColorIndex, Style getStyle = Default, ScrollBarHighlight(double _position, int _colorIndex, ScrollBar *parent, Style _style = Default,
QString _tag = ""); QString _tag = "");
Style getStyle() ColorScheme &colorScheme;
{
return _style;
}
float getPosition() double getPosition()
{ {
return _position; return this->position;
} }
int getColorIndex() int getColorIndex()
{ {
return _colorIndex; return this->colorIndex;
}
Style getStyle()
{
return this->style;
} }
QString getTag() QString getTag()
{ {
return _tag; return this->tag;
} }
ScrollBarHighlight *next = nullptr; ScrollBarHighlight *next = nullptr;
private: private:
Style _style; double position;
float _position; int colorIndex;
int _colorIndex; Style style;
QString _tag; QString tag;
}; };
} // namespace widgets } // namespace widgets

View file

@ -108,10 +108,31 @@ void SettingsDialog::addTabs()
auto form = new QFormLayout(); auto form = new QFormLayout();
auto combo = new QComboBox(); auto combo = new QComboBox();
auto slider = new QSlider(Qt::Horizontal);
auto font = new QPushButton("select"); auto fontLayout = new QHBoxLayout();
font->connect(font, &QPushButton::clicked, []() { auto fontFamilyLabel = new QLabel("Current font family");
auto fontSizeLabel = new QLabel("Current font size");
auto fontButton = new QPushButton("Select");
fontLayout->addWidget(fontButton);
fontLayout->addWidget(fontFamilyLabel);
fontLayout->addWidget(fontSizeLabel);
{
auto fontManager = FontManager::getInstance();
fontManager.currentFontFamily.getValueChangedSignal().connect(
[fontFamilyLabel](const std::string &newValue) {
fontFamilyLabel->setText(QString::fromStdString(newValue)); //
});
fontManager.currentFontSize.getValueChangedSignal().connect(
[fontSizeLabel](const int &newValue) {
fontSizeLabel->setText(QString(QString::number(newValue))); //
});
}
fontButton->connect(fontButton, &QPushButton::clicked, []() {
auto fontManager = FontManager::getInstance(); auto fontManager = FontManager::getInstance();
QFontDialog dialog(fontManager.getFont(FontManager::Medium)); QFontDialog dialog(fontManager.getFont(FontManager::Medium));
@ -131,47 +152,60 @@ void SettingsDialog::addTabs()
auto hideUserButton = createCheckbox("Hide user button", settings.hideUserButton); auto hideUserButton = createCheckbox("Hide user button", settings.hideUserButton);
form->addRow("Theme:", combo); form->addRow("Theme:", combo);
form->addRow("Theme color:", slider);
form->addRow("Font:", font); {
auto hbox = new QHBoxLayout();
auto slider = new QSlider(Qt::Horizontal);
// Theme hue
slider->setMinimum(0);
slider->setMaximum(1000);
slider->setValue(std::min(std::max(settings.themeHue.getValue(), 0.0), 1.0) * 1000);
hbox->addWidget(slider);
auto button = new QPushButton();
button->setFlat(true);
hbox->addWidget(button);
form->addRow("Theme color:", hbox);
QObject::connect(slider, &QSlider::valueChanged, this, [&settings, button](int value) {
settings.themeHue.setValue(value / 1000.0);
QPalette pal = button->palette();
QColor color;
color.setHsvF(settings.themeHue.getValue(), 1.0, 1.0, 1.0);
pal.setColor(QPalette::Button, color);
button->setAutoFillBackground(true);
button->setPalette(pal);
button->update();
// TODO(pajlada): re-implement
// this->windowManager.updateAll();
});
}
form->addRow("Font:", fontLayout);
form->addRow("Tab bar:", compactTabs); form->addRow("Tab bar:", compactTabs);
form->addRow("", hidePreferencesButton); form->addRow("", hidePreferencesButton);
form->addRow("", hideUserButton); form->addRow("", hideUserButton);
// theme // Theme name
combo->addItem("White"); combo->addItem("White");
combo->addItem("Light"); combo->addItem("Light");
combo->addItem("Dark"); combo->addItem("Dark");
combo->addItem("Black"); combo->addItem("Black");
QString theme = settings.theme.get(); auto xD = QString::fromStdString(settings.themeName);
theme = theme.toLower();
if (theme == "light") { combo->setCurrentText(xD);
combo->setCurrentIndex(0);
} else if (theme == "white") {
combo->setCurrentIndex(1);
} else if (theme == "black") {
combo->setCurrentIndex(3);
} else {
combo->setCurrentIndex(2);
}
QObject::connect(combo, &QComboBox::currentTextChanged, this, QObject::connect(combo, &QComboBox::currentTextChanged, this,
[&settings](const QString &value) { settings.theme.set(value); }); [&settings](const QString &value) {
settings.themeName.setValue(value.toStdString()); //
// theme hue
slider->setMinimum(0);
slider->setMaximum(1000);
float hue = settings.themeHue.get();
slider->setValue(std::min(std::max(hue, (float)0.0), (float)1.0) * 1000);
QObject::connect(slider, &QSlider::valueChanged, this, [&settings](int value) {
settings.themeHue.set(value / 1000.0);
// TODO(pajlada): re-implement
// this->windowManager.updateAll();
}); });
group->setLayout(form); group->setLayout(form);

View file

@ -1,6 +1,7 @@
#include "windowmanager.hpp" #include "windowmanager.hpp"
#include "appdatapath.hpp" #include "appdatapath.hpp"
#include "channelmanager.hpp" #include "channelmanager.hpp"
#include "colorscheme.hpp"
#include <QDebug> #include <QDebug>
#include <QStandardPaths> #include <QStandardPaths>
@ -9,8 +10,9 @@
namespace chatterino { namespace chatterino {
WindowManager::WindowManager(ChannelManager &_channelManager) WindowManager::WindowManager(ChannelManager &_channelManager, ColorScheme &_colorScheme)
: channelManager(_channelManager) : channelManager(_channelManager)
, colorScheme(_colorScheme)
{ {
} }
@ -54,7 +56,7 @@ widgets::MainWindow &WindowManager::getMainWindow()
std::lock_guard<std::mutex> lock(this->windowMutex); std::lock_guard<std::mutex> lock(this->windowMutex);
if (this->mainWindow == nullptr) { if (this->mainWindow == nullptr) {
this->mainWindow = new widgets::MainWindow(this->channelManager); this->mainWindow = new widgets::MainWindow(this->channelManager, this->colorScheme);
} }
return *this->mainWindow; return *this->mainWindow;

View file

@ -7,11 +7,15 @@
namespace chatterino { namespace chatterino {
class ChannelManager; class ChannelManager;
class ColorScheme;
class WindowManager class WindowManager
{ {
public: public:
explicit WindowManager(ChannelManager &_channelManager); explicit WindowManager(ChannelManager &_channelManager, ColorScheme &_colorScheme);
ChannelManager &channelManager;
ColorScheme &colorScheme;
void layoutVisibleChatWidgets(Channel *channel = nullptr); void layoutVisibleChatWidgets(Channel *channel = nullptr);
void repaintVisibleChatWidgets(Channel *channel = nullptr); void repaintVisibleChatWidgets(Channel *channel = nullptr);
@ -24,8 +28,6 @@ public:
void save(); void save();
private: private:
ChannelManager &channelManager;
std::mutex windowMutex; std::mutex windowMutex;
// TODO(pajlada): Store as a value instead of a pointer // TODO(pajlada): Store as a value instead of a pointer