mirror of
https://github.com/Chatterino/chatterino2.git
synced 2024-11-21 22:24:07 +01:00
Merge branch 'master' of https://github.com/fourtf/chatterino2
This commit is contained in:
commit
431b9a8c1f
76 changed files with 1645 additions and 561 deletions
|
@ -10,7 +10,7 @@ The code is normally formated using clang format in Qt Creator. [.clang-format](
|
|||
To setup automatic code formating with QT Creator, see [this guide](https://gist.github.com/pajlada/0296454198eb8f8789fd6fe7ea660c5b).
|
||||
|
||||
## Building
|
||||
Before building run `git submodule update --init --recursive` to get required submodules. In case you are new to using qt creator or c++ be sure to add -j to your make arguments as shown here [image](https://i.fourtf.com/GreenSweetImage.png) so it uses all your cpu cores to build.
|
||||
Before building run `git submodule update --init --recursive` to get required submodules.
|
||||
|
||||
### Windows
|
||||
#### Using Qt Creator
|
||||
|
@ -23,6 +23,7 @@ download the [boost library](https://sourceforge.net/projects/boost/files/boost/
|
|||
|
||||
#### Using MSYS2
|
||||
Building using MSYS2 can be quite easier process. Check out MSYS2 at [msys2.org](http://www.msys2.org/).
|
||||
Be sure to add "-j <number of cores\*2>" as a make argument so it will use all your cpu cores to build. [example setup](https://i.imgur.com/qlESlS1.png)
|
||||
1. open appropriate MSYS2 terminal and do `pacman -S mingw-w64-<arch>-boost mingw-w64-<arch>-qt5 mingw-w64-<arch>-rapidjson` where `<arch>` is x86_64 or i686
|
||||
2. go into the project directory
|
||||
3. create build folder `mkdir build && cd build`
|
||||
|
|
|
@ -163,7 +163,13 @@ SOURCES += \
|
|||
src/widgets/basewindow.cpp \
|
||||
src/singletons/helper/moderationaction.cpp \
|
||||
src/widgets/streamview.cpp \
|
||||
src/util/networkrequest.cpp
|
||||
src/util/networkrequest.cpp \
|
||||
src/widgets/settingspages/ignoreuserspage.cpp \
|
||||
src/widgets/settingspages/ignoremessagespage.cpp \
|
||||
src/widgets/settingspages/specialchannelspage.cpp \
|
||||
src/widgets/settingspages/keyboardsettingspage.cpp \
|
||||
src/widgets/helper/titlebarbutton.cpp \
|
||||
src/widgets/helper/label.cpp
|
||||
|
||||
HEADERS += \
|
||||
src/precompiled_header.hpp \
|
||||
|
@ -267,7 +273,14 @@ HEADERS += \
|
|||
src/util/networkrequest.hpp \
|
||||
src/util/networkworker.hpp \
|
||||
src/util/networkrequester.hpp \
|
||||
src/util/flagsenum.h
|
||||
src/util/flagsenum.h \
|
||||
src/widgets/settingspages/ignoreuserspage.hpp \
|
||||
src/widgets/settingspages/ignoremessagespage.hpp \
|
||||
src/widgets/settingspages/specialchannelspage.hpp \
|
||||
src/widgets/settingspages/keyboardsettings.hpp \
|
||||
src/widgets/settingspages/keyboardsettingspage.hpp \
|
||||
src/widgets/helper/titlebarbutton.hpp \
|
||||
src/widgets/helper/label.hpp
|
||||
|
||||
RESOURCES += \
|
||||
resources/resources.qrc
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 51 KiB |
|
@ -64,6 +64,6 @@ private:
|
|||
// std::shared_ptr<logging::Channel> loggingChannel;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Channel> SharedChannel;
|
||||
typedef std::shared_ptr<Channel> ChannelPtr;
|
||||
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -89,6 +89,12 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags)
|
|||
layoutRequired |= wordMaskChanged;
|
||||
this->currentWordFlags = flags; // singletons::SettingManager::getInstance().getWordTypeMask();
|
||||
|
||||
// check if timestamp format changed
|
||||
bool timestampFormatChanged =
|
||||
this->timestampFormat != singletons::SettingManager::getInstance().timestampFormat;
|
||||
|
||||
layoutRequired |= timestampFormatChanged;
|
||||
|
||||
// check if dpi changed
|
||||
bool scaleChanged = this->scale != scale;
|
||||
layoutRequired |= scaleChanged;
|
||||
|
@ -98,11 +104,11 @@ bool MessageLayout::layout(int width, float scale, MessageElement::Flags flags)
|
|||
|
||||
// update word sizes if needed
|
||||
if (imagesChanged) {
|
||||
// fourtf: update images
|
||||
// this->container.updateImages();
|
||||
this->addFlags(MessageLayout::RequiresBufferUpdate);
|
||||
}
|
||||
if (textChanged) {
|
||||
// fourtf: update text
|
||||
// this->container.updateText();
|
||||
this->addFlags(MessageLayout::RequiresBufferUpdate);
|
||||
}
|
||||
if (widthChanged || wordMaskChanged) {
|
||||
|
@ -139,7 +145,8 @@ void MessageLayout::actuallyLayout(int width, MessageElement::Flags flags)
|
|||
}
|
||||
|
||||
// Painting
|
||||
void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection &selection)
|
||||
void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection &selection,
|
||||
bool isLastReadMessage, bool isWindowFocused)
|
||||
{
|
||||
QPixmap *pixmap = this->buffer.get();
|
||||
singletons::ThemeManager &themeManager = singletons::ThemeManager::getInstance();
|
||||
|
@ -164,7 +171,8 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection
|
|||
}
|
||||
|
||||
// draw on buffer
|
||||
painter.drawPixmap(0, y, this->container.width, this->container.getHeight(), *pixmap);
|
||||
painter.drawPixmap(0, y, *pixmap);
|
||||
// painter.drawPixmap(0, y, this->container.width, this->container.getHeight(), *pixmap);
|
||||
|
||||
// draw disabled
|
||||
if (this->message->flags & Message::Disabled) {
|
||||
|
@ -174,6 +182,16 @@ void MessageLayout::paint(QPainter &painter, int y, int messageIndex, Selection
|
|||
// draw gif emotes
|
||||
this->container.paintAnimatedElements(painter, y);
|
||||
|
||||
// draw last read message line
|
||||
if (isLastReadMessage) {
|
||||
QColor color = isWindowFocused ? themeManager.tabs.selected.backgrounds.regular.color()
|
||||
: themeManager.tabs.selected.backgrounds.unfocused.color();
|
||||
|
||||
QBrush brush = QBrush(color, Qt::VerPattern);
|
||||
|
||||
painter.fillRect(0, y + this->container.getHeight() - 1, this->container.width, 1, brush);
|
||||
}
|
||||
|
||||
this->bufferValid = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,8 @@ public:
|
|||
bool layout(int width, float scale, MessageElement::Flags flags);
|
||||
|
||||
// Painting
|
||||
void paint(QPainter &painter, int y, int messageIndex, Selection &selection);
|
||||
void paint(QPainter &painter, int y, int messageIndex, Selection &selection,
|
||||
bool isLastReadMessage, bool isWindowFocused);
|
||||
void invalidateBuffer();
|
||||
void deleteBuffer();
|
||||
|
||||
|
@ -67,6 +68,7 @@ private:
|
|||
int currentLayoutWidth = -1;
|
||||
int fontGeneration = -1;
|
||||
int emoteGeneration = -1;
|
||||
QString timestampFormat;
|
||||
float scale = -1;
|
||||
unsigned int bufferUpdatedCount = 0;
|
||||
|
||||
|
|
|
@ -61,45 +61,64 @@ ImageElement::ImageElement(Image *_image, MessageElement::Flags flags)
|
|||
|
||||
void ImageElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags _flags)
|
||||
{
|
||||
QSize size(this->image->getWidth() * this->image->getScale() * container.scale,
|
||||
this->image->getHeight() * this->image->getScale() * container.scale);
|
||||
if (_flags & this->getFlags()) {
|
||||
QSize size(this->image->getWidth() * this->image->getScale() * container.scale,
|
||||
this->image->getHeight() * this->image->getScale() * container.scale);
|
||||
|
||||
container.addElement(
|
||||
(new ImageLayoutElement(*this, this->image, size))->setLink(this->getLink()));
|
||||
container.addElement(
|
||||
(new ImageLayoutElement(*this, this->image, size))->setLink(this->getLink()));
|
||||
}
|
||||
}
|
||||
|
||||
// EMOTE
|
||||
EmoteElement::EmoteElement(const util::EmoteData &_data, MessageElement::Flags flags)
|
||||
: MessageElement(flags)
|
||||
, data(_data)
|
||||
, textElement(nullptr)
|
||||
{
|
||||
if (_data.isValid()) {
|
||||
this->setTooltip(data.image1x->getTooltip());
|
||||
this->textElement = new TextElement(_data.image1x->getName(), MessageElement::Misc);
|
||||
}
|
||||
}
|
||||
|
||||
EmoteElement::~EmoteElement()
|
||||
{
|
||||
if (this->textElement != nullptr) {
|
||||
delete this->textElement;
|
||||
}
|
||||
}
|
||||
|
||||
void EmoteElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags _flags)
|
||||
{
|
||||
if (!this->data.isValid()) {
|
||||
qDebug() << "EmoteElement::data is invalid xD";
|
||||
return;
|
||||
if (_flags & this->getFlags()) {
|
||||
if (_flags & MessageElement::EmoteImages) {
|
||||
if (!this->data.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int quality = singletons::SettingManager::getInstance().preferredEmoteQuality;
|
||||
|
||||
Image *_image;
|
||||
if (quality == 3 && this->data.image3x != nullptr) {
|
||||
_image = this->data.image3x;
|
||||
} else if (quality >= 2 && this->data.image2x != nullptr) {
|
||||
_image = this->data.image2x;
|
||||
} else {
|
||||
_image = this->data.image1x;
|
||||
}
|
||||
|
||||
QSize size((int)(container.scale * _image->getScaledWidth()),
|
||||
(int)(container.scale * _image->getScaledHeight()));
|
||||
|
||||
container.addElement(
|
||||
(new ImageLayoutElement(*this, _image, size))->setLink(this->getLink()));
|
||||
} else {
|
||||
if (this->textElement != nullptr) {
|
||||
this->textElement->addToContainer(container, MessageElement::Misc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int quality = singletons::SettingManager::getInstance().preferredEmoteQuality;
|
||||
|
||||
Image *_image;
|
||||
if (quality == 3 && this->data.image3x != nullptr) {
|
||||
_image = this->data.image3x;
|
||||
} else if (quality >= 2 && this->data.image2x != nullptr) {
|
||||
_image = this->data.image2x;
|
||||
} else {
|
||||
_image = this->data.image1x;
|
||||
}
|
||||
|
||||
QSize size((int)(container.scale * _image->getScaledWidth()),
|
||||
(int)(container.scale * _image->getScaledHeight()));
|
||||
|
||||
container.addElement((new ImageLayoutElement(*this, _image, size))->setLink(this->getLink()));
|
||||
}
|
||||
|
||||
// TEXT
|
||||
|
@ -117,75 +136,79 @@ TextElement::TextElement(const QString &text, MessageElement::Flags flags,
|
|||
|
||||
void TextElement::addToContainer(MessageLayoutContainer &container, MessageElement::Flags _flags)
|
||||
{
|
||||
QFontMetrics &metrics =
|
||||
singletons::FontManager::getInstance().getFontMetrics(this->style, container.scale);
|
||||
singletons::ThemeManager &themeManager = singletons::ThemeManager::ThemeManager::getInstance();
|
||||
if (_flags & this->getFlags()) {
|
||||
QFontMetrics &metrics =
|
||||
singletons::FontManager::getInstance().getFontMetrics(this->style, container.scale);
|
||||
singletons::ThemeManager &themeManager =
|
||||
singletons::ThemeManager::ThemeManager::getInstance();
|
||||
|
||||
for (Word &word : this->words) {
|
||||
auto getTextLayoutElement = [&](QString text, int width, bool trailingSpace) {
|
||||
QColor color = this->color.getColor(themeManager);
|
||||
themeManager.normalizeColor(color);
|
||||
for (Word &word : this->words) {
|
||||
auto getTextLayoutElement = [&](QString text, int width, bool trailingSpace) {
|
||||
QColor color = this->color.getColor(themeManager);
|
||||
themeManager.normalizeColor(color);
|
||||
|
||||
auto e = (new TextLayoutElement(*this, text, QSize(width, metrics.height()), color,
|
||||
this->style, container.scale))
|
||||
->setLink(this->getLink());
|
||||
e->setTrailingSpace(trailingSpace);
|
||||
return e;
|
||||
};
|
||||
auto e = (new TextLayoutElement(*this, text, QSize(width, metrics.height()), color,
|
||||
this->style, container.scale))
|
||||
->setLink(this->getLink());
|
||||
e->setTrailingSpace(trailingSpace);
|
||||
return e;
|
||||
};
|
||||
|
||||
if (word.width == -1) {
|
||||
// fourtf: add again
|
||||
// if (word.width == -1) {
|
||||
word.width = metrics.width(word.text);
|
||||
}
|
||||
|
||||
// see if the text fits in the current line
|
||||
if (container.fitsInLine(word.width)) {
|
||||
container.addElementNoLineBreak(
|
||||
getTextLayoutElement(word.text, word.width, this->hasTrailingSpace()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// see if the text fits in the next line
|
||||
if (!container.atStartOfLine()) {
|
||||
container.breakLine();
|
||||
// }
|
||||
|
||||
// see if the text fits in the current line
|
||||
if (container.fitsInLine(word.width)) {
|
||||
container.addElementNoLineBreak(
|
||||
getTextLayoutElement(word.text, word.width, this->hasTrailingSpace()));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// we done goofed, we need to wrap the text
|
||||
QString text = word.text;
|
||||
int textLength = text.length();
|
||||
int wordStart = 0;
|
||||
int width = metrics.width(text[0]);
|
||||
int lastWidth = 0;
|
||||
|
||||
for (int i = 1; i < textLength; i++) {
|
||||
int charWidth = metrics.width(text[i]);
|
||||
|
||||
if (!container.fitsInLine(width + charWidth)) {
|
||||
container.addElementNoLineBreak(getTextLayoutElement(
|
||||
text.mid(wordStart, i - wordStart), width - lastWidth, false));
|
||||
// see if the text fits in the next line
|
||||
if (!container.atStartOfLine()) {
|
||||
container.breakLine();
|
||||
|
||||
wordStart = i;
|
||||
lastWidth = width;
|
||||
width = 0;
|
||||
if (textLength > i + 2) {
|
||||
width += metrics.width(text[i]);
|
||||
width += metrics.width(text[i + 1]);
|
||||
i += 1;
|
||||
if (container.fitsInLine(word.width)) {
|
||||
container.addElementNoLineBreak(
|
||||
getTextLayoutElement(word.text, word.width, this->hasTrailingSpace()));
|
||||
continue;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
width += charWidth;
|
||||
}
|
||||
|
||||
container.addElement(getTextLayoutElement(text.mid(wordStart), word.width - lastWidth,
|
||||
this->hasTrailingSpace()));
|
||||
container.breakLine();
|
||||
// we done goofed, we need to wrap the text
|
||||
QString text = word.text;
|
||||
int textLength = text.length();
|
||||
int wordStart = 0;
|
||||
int width = metrics.width(text[0]);
|
||||
int lastWidth = 0;
|
||||
|
||||
for (int i = 1; i < textLength; i++) {
|
||||
int charWidth = metrics.width(text[i]);
|
||||
|
||||
if (!container.fitsInLine(width + charWidth)) {
|
||||
container.addElementNoLineBreak(getTextLayoutElement(
|
||||
text.mid(wordStart, i - wordStart), width - lastWidth, false));
|
||||
container.breakLine();
|
||||
|
||||
wordStart = i;
|
||||
lastWidth = width;
|
||||
width = 0;
|
||||
if (textLength > i + 2) {
|
||||
width += metrics.width(text[i]);
|
||||
width += metrics.width(text[i + 1]);
|
||||
i += 1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
width += charWidth;
|
||||
}
|
||||
|
||||
container.addElement(getTextLayoutElement(text.mid(wordStart), word.width - lastWidth,
|
||||
this->hasTrailingSpace()));
|
||||
container.breakLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,13 +234,15 @@ TimestampElement::~TimestampElement()
|
|||
void TimestampElement::addToContainer(MessageLayoutContainer &container,
|
||||
MessageElement::Flags _flags)
|
||||
{
|
||||
if (singletons::SettingManager::getInstance().timestampFormat != this->format) {
|
||||
this->format = singletons::SettingManager::getInstance().timestampFormat.getValue();
|
||||
delete this->element;
|
||||
this->element = TimestampElement::formatTime(this->time);
|
||||
}
|
||||
if (_flags & this->getFlags()) {
|
||||
if (singletons::SettingManager::getInstance().timestampFormat != this->format) {
|
||||
this->format = singletons::SettingManager::getInstance().timestampFormat.getValue();
|
||||
delete this->element;
|
||||
this->element = TimestampElement::formatTime(this->time);
|
||||
}
|
||||
|
||||
this->element->addToContainer(container, _flags);
|
||||
this->element->addToContainer(container, _flags);
|
||||
}
|
||||
}
|
||||
|
||||
TextElement *TimestampElement::formatTime(const QTime &time)
|
||||
|
@ -239,8 +264,6 @@ TwitchModerationElement::TwitchModerationElement()
|
|||
void TwitchModerationElement::addToContainer(MessageLayoutContainer &container,
|
||||
MessageElement::Flags _flags)
|
||||
{
|
||||
// qDebug() << _flags;
|
||||
|
||||
if (_flags & MessageElement::ModeratorTools) {
|
||||
QSize size((int)(container.scale * 16), (int)(container.scale * 16));
|
||||
|
||||
|
|
|
@ -145,20 +145,6 @@ public:
|
|||
MessageElement::Flags flags) override;
|
||||
};
|
||||
|
||||
// contains emote data and will pick the emote based on :
|
||||
// a) are images for the emote type enabled
|
||||
// b) which size it wants
|
||||
class EmoteElement : public MessageElement
|
||||
{
|
||||
const util::EmoteData data;
|
||||
|
||||
public:
|
||||
EmoteElement(const util::EmoteData &data, MessageElement::Flags flags);
|
||||
|
||||
virtual void addToContainer(MessageLayoutContainer &container,
|
||||
MessageElement::Flags flags) override;
|
||||
};
|
||||
|
||||
// contains a text, it will split it into words
|
||||
class TextElement : public MessageElement
|
||||
{
|
||||
|
@ -180,6 +166,22 @@ public:
|
|||
MessageElement::Flags flags) override;
|
||||
};
|
||||
|
||||
// contains emote data and will pick the emote based on :
|
||||
// a) are images for the emote type enabled
|
||||
// b) which size it wants
|
||||
class EmoteElement : public MessageElement
|
||||
{
|
||||
const util::EmoteData data;
|
||||
TextElement *textElement;
|
||||
|
||||
public:
|
||||
EmoteElement(const util::EmoteData &data, MessageElement::Flags flags);
|
||||
~EmoteElement();
|
||||
|
||||
virtual void addToContainer(MessageLayoutContainer &container,
|
||||
MessageElement::Flags flags) override;
|
||||
};
|
||||
|
||||
// contains a text, formated depending on the preferences
|
||||
class TimestampElement : public MessageElement
|
||||
{
|
||||
|
@ -208,12 +210,5 @@ public:
|
|||
virtual void addToContainer(MessageLayoutContainer &container,
|
||||
MessageElement::Flags flags) override;
|
||||
};
|
||||
|
||||
// adds bits as text, static image or animated image
|
||||
// class BitsElement : public MessageElement
|
||||
//{
|
||||
// public:
|
||||
// virtual void addToContainer(LayoutContainer &container) override;
|
||||
//};
|
||||
} // namespace messages
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -8,7 +8,6 @@ public:
|
|||
bool disablePingSoungs = false;
|
||||
bool isReceivedWhisper = false;
|
||||
bool isSentWhisper = false;
|
||||
bool includeChannelName = false;
|
||||
};
|
||||
|
||||
} // namespace messages
|
||||
|
|
|
@ -19,11 +19,11 @@ ChannelManager::ChannelManager()
|
|||
{
|
||||
}
|
||||
|
||||
const std::vector<SharedChannel> ChannelManager::getItems()
|
||||
const std::vector<ChannelPtr> ChannelManager::getItems()
|
||||
{
|
||||
QMutexLocker locker(&this->channelsMutex);
|
||||
|
||||
std::vector<SharedChannel> items;
|
||||
std::vector<ChannelPtr> items;
|
||||
|
||||
for (auto &item : this->twitchChannels.values()) {
|
||||
items.push_back(std::get<0>(item));
|
||||
|
@ -32,7 +32,7 @@ const std::vector<SharedChannel> ChannelManager::getItems()
|
|||
return items;
|
||||
}
|
||||
|
||||
SharedChannel ChannelManager::addTwitchChannel(const QString &rawChannelName)
|
||||
ChannelPtr ChannelManager::addTwitchChannel(const QString &rawChannelName)
|
||||
{
|
||||
QString channelName = rawChannelName.toLower();
|
||||
|
||||
|
@ -63,7 +63,7 @@ SharedChannel ChannelManager::addTwitchChannel(const QString &rawChannelName)
|
|||
return std::get<0>(it.value());
|
||||
}
|
||||
|
||||
SharedChannel ChannelManager::getTwitchChannel(const QString &channel)
|
||||
ChannelPtr ChannelManager::getTwitchChannel(const QString &channel)
|
||||
{
|
||||
QMutexLocker locker(&this->channelsMutex);
|
||||
|
||||
|
@ -128,7 +128,7 @@ const std::string &ChannelManager::getUserID(const std::string &username)
|
|||
return temporary;
|
||||
}
|
||||
|
||||
void ChannelManager::doOnAll(std::function<void(SharedChannel)> func)
|
||||
void ChannelManager::doOnAll(std::function<void(ChannelPtr)> func)
|
||||
{
|
||||
for (const auto &channel : this->twitchChannels) {
|
||||
func(std::get<0>(channel));
|
||||
|
|
|
@ -17,20 +17,20 @@ class ChannelManager
|
|||
public:
|
||||
static ChannelManager &getInstance();
|
||||
|
||||
const std::vector<SharedChannel> getItems();
|
||||
const std::vector<ChannelPtr> getItems();
|
||||
|
||||
SharedChannel addTwitchChannel(const QString &channel);
|
||||
SharedChannel getTwitchChannel(const QString &channel);
|
||||
ChannelPtr addTwitchChannel(const QString &channel);
|
||||
ChannelPtr getTwitchChannel(const QString &channel);
|
||||
void removeTwitchChannel(const QString &channel);
|
||||
|
||||
const std::string &getUserID(const std::string &username);
|
||||
|
||||
void doOnAll(std::function<void(SharedChannel)> func);
|
||||
void doOnAll(std::function<void(ChannelPtr)> func);
|
||||
|
||||
// Special channels
|
||||
const SharedChannel whispersChannel;
|
||||
const SharedChannel mentionsChannel;
|
||||
const SharedChannel emptyChannel;
|
||||
const ChannelPtr whispersChannel;
|
||||
const ChannelPtr mentionsChannel;
|
||||
const ChannelPtr emptyChannel;
|
||||
|
||||
private:
|
||||
std::map<std::string, std::string> usernameToID;
|
||||
|
|
|
@ -88,7 +88,7 @@ QStringList CommandManager::getCommands()
|
|||
return this->commandsStringList;
|
||||
}
|
||||
|
||||
QString CommandManager::execCommand(const QString &text, SharedChannel channel,
|
||||
QString CommandManager::execCommand(const QString &text, ChannelPtr channel,
|
||||
bool dryRun)
|
||||
{
|
||||
QStringList words = text.split(' ', QString::SkipEmptyParts);
|
||||
|
|
|
@ -246,16 +246,23 @@ void IrcManager::privateMessageReceived(Communi::IrcPrivateMessage *message)
|
|||
return;
|
||||
}
|
||||
|
||||
auto xd = message->content();
|
||||
auto xd2 = message->toData();
|
||||
// auto xd = message->content();
|
||||
// auto xd2 = message->toData();
|
||||
|
||||
debug::Log("HEHE: {}", xd2.toStdString());
|
||||
// debug::Log("HEHE: {}", xd2.toStdString());
|
||||
|
||||
messages::MessageParseArgs args;
|
||||
|
||||
twitch::TwitchMessageBuilder builder(c.get(), message, args);
|
||||
|
||||
c->addMessage(builder.parse());
|
||||
if (!builder.isIgnored()) {
|
||||
messages::MessagePtr _message = builder.build();
|
||||
if (_message->flags & messages::Message::Highlighted) {
|
||||
singletons::ChannelManager::getInstance().mentionsChannel->addMessage(_message);
|
||||
}
|
||||
|
||||
c->addMessage(_message);
|
||||
}
|
||||
}
|
||||
|
||||
void IrcManager::messageReceived(Communi::IrcMessage *message)
|
||||
|
@ -384,7 +391,7 @@ void IrcManager::onConnected()
|
|||
MessagePtr connMsg = Message::createSystemMessage("connected to chat");
|
||||
MessagePtr reconnMsg = Message::createSystemMessage("reconnected to chat");
|
||||
|
||||
this->channelManager.doOnAll([connMsg, reconnMsg](SharedChannel channel) {
|
||||
this->channelManager.doOnAll([connMsg, reconnMsg](ChannelPtr channel) {
|
||||
assert(channel);
|
||||
|
||||
LimitedQueueSnapshot<MessagePtr> snapshot = channel->getMessageSnapshot();
|
||||
|
@ -407,7 +414,7 @@ void IrcManager::onDisconnected()
|
|||
MessagePtr msg = Message::createSystemMessage("disconnected from chat");
|
||||
msg->flags &= Message::DisconnectedMessage;
|
||||
|
||||
this->channelManager.doOnAll([msg](SharedChannel channel) {
|
||||
this->channelManager.doOnAll([msg](ChannelPtr channel) {
|
||||
assert(channel);
|
||||
channel->addMessage(msg);
|
||||
});
|
||||
|
|
|
@ -345,7 +345,7 @@ void ResourceManager::loadChannelData(const QString &roomID, bool bypassCache)
|
|||
QString cheermoteURL = "https://api.twitch.tv/kraken/bits/actions?channel_id=" + roomID;
|
||||
|
||||
util::twitch::get2(
|
||||
cheermoteURL, QThread::currentThread(), [this, roomID](const rapidjson::Document &d) {
|
||||
cheermoteURL, QThread::currentThread(), true, [this, roomID](const rapidjson::Document &d) {
|
||||
ResourceManager::Channel &ch = this->channels[roomID];
|
||||
|
||||
ParseCheermoteSets(ch.jsonCheermoteSets, d);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "debug/log.hpp"
|
||||
#include "singletons/pathmanager.hpp"
|
||||
#include "singletons/resourcemanager.hpp"
|
||||
#include "singletons/windowmanager.hpp"
|
||||
|
||||
using namespace chatterino::messages;
|
||||
|
||||
|
@ -17,6 +18,7 @@ void _registerSetting(std::weak_ptr<pajlada::Settings::ISettingData> setting)
|
|||
|
||||
SettingManager::SettingManager()
|
||||
: snapshot(nullptr)
|
||||
, _ignoredKeywords(new std::vector<QString>)
|
||||
{
|
||||
this->wordFlagsListener.addSetting(this->showTimestamps);
|
||||
this->wordFlagsListener.addSetting(this->showBadges);
|
||||
|
@ -29,6 +31,10 @@ SettingManager::SettingManager()
|
|||
};
|
||||
|
||||
this->moderationActions.connect([this](auto, auto) { this->updateModerationActions(); });
|
||||
this->ignoredKeywords.connect([this](auto, auto) { this->updateIgnoredKeywords(); });
|
||||
|
||||
this->timestampFormat.connect(
|
||||
[](auto, auto) { singletons::WindowManager::getInstance().layoutVisibleChatWidgets(); });
|
||||
}
|
||||
|
||||
MessageElement::Flags SettingManager::getWordFlags()
|
||||
|
@ -135,6 +141,11 @@ std::vector<ModerationAction> SettingManager::getModerationActions() const
|
|||
return this->_moderationActions;
|
||||
}
|
||||
|
||||
const std::shared_ptr<std::vector<QString>> SettingManager::getIgnoredKeywords() const
|
||||
{
|
||||
return this->_ignoredKeywords;
|
||||
}
|
||||
|
||||
void SettingManager::updateModerationActions()
|
||||
{
|
||||
auto &resources = singletons::ResourceManager::getInstance();
|
||||
|
@ -202,5 +213,22 @@ void SettingManager::updateModerationActions()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SettingManager::updateIgnoredKeywords()
|
||||
{
|
||||
static QRegularExpression newLineRegex("(\r\n?|\n)+");
|
||||
|
||||
auto items = new std::vector<QString>();
|
||||
|
||||
for (const QString &line : this->ignoredKeywords.getValue().split(newLineRegex)) {
|
||||
QString line2 = line.trimmed();
|
||||
|
||||
if (!line2.isEmpty()) {
|
||||
items->push_back(line2);
|
||||
}
|
||||
}
|
||||
|
||||
this->_ignoredKeywords = std::shared_ptr<std::vector<QString>>(items);
|
||||
}
|
||||
} // namespace singletons
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -75,6 +75,10 @@ public:
|
|||
/// Links
|
||||
BoolSetting linksDoubleClickOnly = {"/links/doubleClickToOpen", false};
|
||||
|
||||
/// Ingored Users
|
||||
BoolSetting enableTwitchIgnoredUsers = {"/ignore/enableTwitchIgnoredUsers", true};
|
||||
QStringSetting ignoredKeywords = {"/ignore/ignoredKeywords", ""};
|
||||
|
||||
/// Moderation
|
||||
QStringSetting moderationActions = {"/moderation/actions", "/ban {user}\n/timeout {user} 300"};
|
||||
|
||||
|
@ -107,6 +111,7 @@ public:
|
|||
void recallSnapshot();
|
||||
|
||||
std::vector<ModerationAction> getModerationActions() const;
|
||||
const std::shared_ptr<std::vector<QString>> getIgnoredKeywords() const;
|
||||
|
||||
signals:
|
||||
void wordFlagsChanged();
|
||||
|
@ -114,10 +119,12 @@ signals:
|
|||
private:
|
||||
std::vector<ModerationAction> _moderationActions;
|
||||
std::unique_ptr<rapidjson::Document> snapshot;
|
||||
std::shared_ptr<std::vector<QString>> _ignoredKeywords;
|
||||
|
||||
SettingManager();
|
||||
|
||||
void updateModerationActions();
|
||||
void updateIgnoredKeywords();
|
||||
|
||||
messages::MessageElement::Flags wordFlags = messages::MessageElement::Default;
|
||||
|
||||
|
|
|
@ -59,24 +59,23 @@ void ThemeManager::actuallyUpdate(double hue, double multiplier)
|
|||
QColor themeColor = QColor::fromHslF(hue, 0.5, 0.5);
|
||||
QColor themeColorNoSat = QColor::fromHslF(hue, 0, 0.5);
|
||||
|
||||
//#ifdef USEWINSDK
|
||||
// isLightTabs = isLight;
|
||||
// QColor tabFg = isLight ? "#000" : "#fff";
|
||||
// this->windowBg = isLight ? "#fff" : "#444";
|
||||
|
||||
//#else
|
||||
isLightTabs = true;
|
||||
QColor tabFg = isLightTabs ? "#000" : "#fff";
|
||||
this->windowBg = "#fff";
|
||||
|
||||
//#endif
|
||||
|
||||
qreal sat = 0.05;
|
||||
qreal sat = 0.1;
|
||||
// 0.05;
|
||||
|
||||
auto getColor = [multiplier](double h, double s, double l, double a = 1.0) {
|
||||
return QColor::fromHslF(h, s, ((l - 0.5) * multiplier) + 0.5, a);
|
||||
};
|
||||
|
||||
//#ifdef USEWINSDK
|
||||
// isLightTabs = isLight;
|
||||
// QColor tabFg = isLight ? "#000" : "#fff";
|
||||
// this->windowBg = isLight ? "#fff" : getColor(0, sat, 0.9);
|
||||
//#else
|
||||
isLightTabs = true;
|
||||
QColor tabFg = isLightTabs ? "#000" : "#fff";
|
||||
this->windowBg = "#fff";
|
||||
//#endif
|
||||
|
||||
// Ubuntu style
|
||||
// TODO: add setting for this
|
||||
// TabText = QColor(210, 210, 210);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include "debug/log.hpp"
|
||||
#include "singletons/fontmanager.hpp"
|
||||
#include "singletons/thememanager.hpp"
|
||||
#include "widgets/accountswitchpopupwidget.hpp"
|
||||
#include "widgets/settingsdialog.hpp"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
@ -14,6 +16,35 @@ WindowManager &WindowManager::getInstance()
|
|||
return instance;
|
||||
}
|
||||
|
||||
void WindowManager::showSettingsDialog()
|
||||
{
|
||||
QTimer::singleShot(80, [] { widgets::SettingsDialog::showDialog(); });
|
||||
}
|
||||
|
||||
void WindowManager::showAccountSelectPopup(QPoint point)
|
||||
{
|
||||
// static QWidget *lastFocusedWidget = nullptr;
|
||||
static widgets::AccountSwitchPopupWidget *w = new widgets::AccountSwitchPopupWidget();
|
||||
|
||||
if (w->hasFocus()) {
|
||||
w->hide();
|
||||
// if (lastFocusedWidget) {
|
||||
// lastFocusedWidget->setFocus();
|
||||
// }
|
||||
return;
|
||||
}
|
||||
|
||||
// lastFocusedWidget = this->focusWidget();
|
||||
|
||||
w->refresh();
|
||||
|
||||
QPoint buttonPos = point;
|
||||
w->move(buttonPos.x(), buttonPos.y());
|
||||
|
||||
w->show();
|
||||
w->setFocus();
|
||||
}
|
||||
|
||||
WindowManager::WindowManager(ThemeManager &_themeManager)
|
||||
: themeManager(_themeManager)
|
||||
{
|
||||
|
|
|
@ -14,6 +14,9 @@ class WindowManager
|
|||
public:
|
||||
static WindowManager &getInstance();
|
||||
|
||||
void showSettingsDialog();
|
||||
void showAccountSelectPopup(QPoint point);
|
||||
|
||||
void initMainWindow();
|
||||
void layoutVisibleChatWidgets(Channel *channel = nullptr);
|
||||
void repaintVisibleChatWidgets(Channel *channel = nullptr);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "twitchchannel.hpp"
|
||||
#include "debug/log.hpp"
|
||||
#include "messages/message.hpp"
|
||||
#include "singletons/channelmanager.hpp"
|
||||
#include "singletons/emotemanager.hpp"
|
||||
#include "singletons/ircmanager.hpp"
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
|
@ -156,8 +158,8 @@ void TwitchChannel::refreshLiveStatus()
|
|||
|
||||
std::weak_ptr<Channel> weak = this->shared_from_this();
|
||||
|
||||
util::twitch::get2(url, QThread::currentThread(), [weak](const rapidjson::Document &d) {
|
||||
SharedChannel shared = weak.lock();
|
||||
util::twitch::get2(url, QThread::currentThread(), false, [weak](const rapidjson::Document &d) {
|
||||
ChannelPtr shared = weak.lock();
|
||||
|
||||
if (!shared) {
|
||||
return;
|
||||
|
@ -218,7 +220,7 @@ void TwitchChannel::fetchRecentMessages()
|
|||
std::weak_ptr<Channel> weak = this->shared_from_this();
|
||||
|
||||
util::twitch::get(genericURL.arg(roomID), QThread::currentThread(), [weak](QJsonObject obj) {
|
||||
SharedChannel shared = weak.lock();
|
||||
ChannelPtr shared = weak.lock();
|
||||
|
||||
if (!shared) {
|
||||
return;
|
||||
|
@ -230,7 +232,6 @@ void TwitchChannel::fetchRecentMessages()
|
|||
auto msgArray = obj.value("messages").toArray();
|
||||
if (msgArray.size() > 0) {
|
||||
std::vector<messages::MessagePtr> messages;
|
||||
messages.resize(msgArray.size());
|
||||
|
||||
for (int i = 0; i < msgArray.size(); i++) {
|
||||
QByteArray content = msgArray[i].toString().toUtf8();
|
||||
|
@ -239,7 +240,9 @@ void TwitchChannel::fetchRecentMessages()
|
|||
|
||||
messages::MessageParseArgs args;
|
||||
twitch::TwitchMessageBuilder builder(channel, privMsg, args);
|
||||
messages.at(i) = builder.parse();
|
||||
if (!builder.isIgnored()) {
|
||||
messages.push_back(builder.build());
|
||||
}
|
||||
}
|
||||
channel->addMessagesAtStart(messages);
|
||||
}
|
||||
|
|
|
@ -27,15 +27,28 @@ TwitchMessageBuilder::TwitchMessageBuilder(Channel *_channel,
|
|||
, tags(this->ircMessage->tags())
|
||||
, usernameColor(singletons::ThemeManager::getInstance().messages.textColors.system)
|
||||
{
|
||||
this->originalMessage = this->ircMessage->content();
|
||||
}
|
||||
|
||||
MessagePtr TwitchMessageBuilder::parse()
|
||||
bool TwitchMessageBuilder::isIgnored() const
|
||||
{
|
||||
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
|
||||
std::shared_ptr<std::vector<QString>> ignoredKeywords = settings.getIgnoredKeywords();
|
||||
|
||||
for (const QString &keyword : *ignoredKeywords) {
|
||||
if (this->originalMessage.contains(keyword, Qt::CaseInsensitive)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
MessagePtr TwitchMessageBuilder::build()
|
||||
{
|
||||
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
|
||||
singletons::EmoteManager &emoteManager = singletons::EmoteManager::getInstance();
|
||||
|
||||
this->originalMessage = this->ircMessage->content();
|
||||
|
||||
// PARSING
|
||||
this->parseUsername();
|
||||
|
||||
|
@ -43,11 +56,14 @@ MessagePtr TwitchMessageBuilder::parse()
|
|||
// this->appendWord(Word(Resources::getInstance().badgeCollapsed, Word::Collapsed, QString(),
|
||||
// QString()));
|
||||
|
||||
// The timestamp is always appended to the builder
|
||||
// Whether or not will be rendered is decided/checked later
|
||||
// PARSING
|
||||
this->parseMessageID();
|
||||
|
||||
// Appends the correct timestamp if the message is a past message
|
||||
this->parseRoomID();
|
||||
|
||||
this->appendChannelName();
|
||||
|
||||
// timestamp
|
||||
bool isPastMsg = this->tags.contains("historical");
|
||||
if (isPastMsg) {
|
||||
// This may be architecture dependent(datatype)
|
||||
|
@ -58,20 +74,11 @@ MessagePtr TwitchMessageBuilder::parse()
|
|||
this->emplace<TimestampElement>();
|
||||
}
|
||||
|
||||
this->parseMessageID();
|
||||
|
||||
this->parseRoomID();
|
||||
|
||||
// TIMESTAMP
|
||||
this->emplace<TwitchModerationElement>();
|
||||
|
||||
this->parseTwitchBadges();
|
||||
this->appendTwitchBadges();
|
||||
|
||||
this->addChatterinoBadges();
|
||||
|
||||
if (this->args.includeChannelName) {
|
||||
this->parseChannelName();
|
||||
}
|
||||
this->appendChatterinoBadges();
|
||||
|
||||
this->appendUsername();
|
||||
|
||||
|
@ -219,7 +226,7 @@ void TwitchMessageBuilder::parseRoomID()
|
|||
}
|
||||
}
|
||||
|
||||
void TwitchMessageBuilder::parseChannelName()
|
||||
void TwitchMessageBuilder::appendChannelName()
|
||||
{
|
||||
QString channelName("#" + this->channel->name);
|
||||
Link link(Link::Url, this->channel->name + "\n" + this->messageID);
|
||||
|
@ -450,39 +457,36 @@ bool TwitchMessageBuilder::tryAppendEmote(QString &emoteString)
|
|||
singletons::EmoteManager &emoteManager = singletons::EmoteManager::getInstance();
|
||||
util::EmoteData emoteData;
|
||||
|
||||
auto appendEmote = [&](MessageElement::Flags flags) {
|
||||
this->emplace<EmoteElement>(emoteData, flags);
|
||||
return true;
|
||||
};
|
||||
|
||||
if (emoteManager.bttvGlobalEmotes.tryGet(emoteString, emoteData)) {
|
||||
// BTTV Global Emote
|
||||
return this->appendEmote(emoteData);
|
||||
return appendEmote(MessageElement::BttvEmote);
|
||||
} else if (this->twitchChannel != nullptr &&
|
||||
this->twitchChannel->bttvChannelEmotes->tryGet(emoteString, emoteData)) {
|
||||
// BTTV Channel Emote
|
||||
return this->appendEmote(emoteData);
|
||||
return appendEmote(MessageElement::BttvEmote);
|
||||
} else if (emoteManager.ffzGlobalEmotes.tryGet(emoteString, emoteData)) {
|
||||
// FFZ Global Emote
|
||||
return this->appendEmote(emoteData);
|
||||
return appendEmote(MessageElement::FfzEmote);
|
||||
} else if (this->twitchChannel != nullptr &&
|
||||
this->twitchChannel->ffzChannelEmotes->tryGet(emoteString, emoteData)) {
|
||||
// FFZ Channel Emote
|
||||
return this->appendEmote(emoteData);
|
||||
return appendEmote(MessageElement::FfzEmote);
|
||||
} else if (emoteManager.getChatterinoEmotes().tryGet(emoteString, emoteData)) {
|
||||
// Chatterino Emote
|
||||
return this->appendEmote(emoteData);
|
||||
return appendEmote(MessageElement::Misc);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TwitchMessageBuilder::appendEmote(const util::EmoteData &emoteData)
|
||||
{
|
||||
this->emplace<EmoteElement>(emoteData, MessageElement::BttvEmote);
|
||||
|
||||
// Perhaps check for ignored emotes here?
|
||||
return true;
|
||||
}
|
||||
|
||||
// fourtf: this is ugly
|
||||
// maybe put the individual badges into a map instead of this mess
|
||||
void TwitchMessageBuilder::parseTwitchBadges()
|
||||
void TwitchMessageBuilder::appendTwitchBadges()
|
||||
{
|
||||
singletons::ResourceManager &resourceManager = singletons::ResourceManager::getInstance();
|
||||
const auto &channelResources = resourceManager.channels[this->roomID];
|
||||
|
@ -639,7 +643,7 @@ void TwitchMessageBuilder::parseTwitchBadges()
|
|||
}
|
||||
}
|
||||
|
||||
void TwitchMessageBuilder::addChatterinoBadges()
|
||||
void TwitchMessageBuilder::appendChatterinoBadges()
|
||||
{
|
||||
auto &badges = singletons::ResourceManager::getInstance().chatterinoBadges;
|
||||
auto it = badges.find(this->userName.toStdString());
|
||||
|
|
|
@ -38,7 +38,8 @@ public:
|
|||
QString messageID;
|
||||
QString userName;
|
||||
|
||||
messages::MessagePtr parse();
|
||||
bool isIgnored() const;
|
||||
messages::MessagePtr build();
|
||||
|
||||
private:
|
||||
QString roomID;
|
||||
|
@ -47,7 +48,7 @@ private:
|
|||
|
||||
void parseMessageID();
|
||||
void parseRoomID();
|
||||
void parseChannelName();
|
||||
void appendChannelName();
|
||||
void parseUsername();
|
||||
void appendUsername();
|
||||
void parseHighlights();
|
||||
|
@ -55,10 +56,9 @@ private:
|
|||
void appendTwitchEmote(const Communi::IrcPrivateMessage *ircMessage, const QString &emote,
|
||||
std::vector<std::pair<long, util::EmoteData>> &vec);
|
||||
bool tryAppendEmote(QString &emoteString);
|
||||
bool appendEmote(const util::EmoteData &emoteData);
|
||||
|
||||
void parseTwitchBadges();
|
||||
void addChatterinoBadges();
|
||||
void appendTwitchBadges();
|
||||
void appendChatterinoBadges();
|
||||
bool tryParseCheermote(const QString &string);
|
||||
};
|
||||
|
||||
|
|
|
@ -47,6 +47,18 @@ public:
|
|||
return LayoutCreator<T2>(t);
|
||||
}
|
||||
|
||||
template <typename T2, typename Q = T,
|
||||
typename std::enable_if<std::is_base_of<QWidget, Q>::value, int>::type = 0,
|
||||
typename std::enable_if<std::is_base_of<QLayout, T2>::value, int>::type = 0>
|
||||
LayoutCreator<T2> setLayoutType()
|
||||
{
|
||||
T2 *layout = new T2;
|
||||
|
||||
this->item->setLayout(layout);
|
||||
|
||||
return LayoutCreator<T2>(layout);
|
||||
}
|
||||
|
||||
LayoutCreator<T> assign(T **ptr)
|
||||
{
|
||||
*ptr = this->item;
|
||||
|
|
|
@ -37,14 +37,14 @@ static void get(QString url, const QObject *caller,
|
|||
});
|
||||
}
|
||||
|
||||
static void get2(QString url, const QObject *caller,
|
||||
static void get2(QString url, const QObject *caller, bool useQuickLoadCache,
|
||||
std::function<void(const rapidjson::Document &)> successCallback)
|
||||
{
|
||||
util::NetworkRequest req(url);
|
||||
req.setCaller(caller);
|
||||
req.setRawHeader("Client-ID", getDefaultClientID());
|
||||
req.setRawHeader("Accept", "application/vnd.twitchtv.v5+json");
|
||||
req.setUseQuickLoadCache(true);
|
||||
req.setUseQuickLoadCache(useQuickLoadCache);
|
||||
|
||||
req.getJSON2([=](const rapidjson::Document &document) {
|
||||
successCallback(document); //
|
||||
|
|
|
@ -18,13 +18,15 @@
|
|||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
||||
AccountPopupWidget::AccountPopupWidget(SharedChannel _channel)
|
||||
AccountPopupWidget::AccountPopupWidget(ChannelPtr _channel)
|
||||
: BaseWindow()
|
||||
, ui(new Ui::AccountPopup)
|
||||
, channel(_channel)
|
||||
{
|
||||
this->ui->setupUi(this);
|
||||
|
||||
this->setStayInScreenRect(true);
|
||||
|
||||
this->layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
|
||||
this->setWindowFlags(Qt::FramelessWindowHint);
|
||||
|
@ -49,7 +51,6 @@ AccountPopupWidget::AccountPopupWidget(SharedChannel _channel)
|
|||
this->loggedInUser.userID = currentTwitchUser->getUserId();
|
||||
|
||||
this->loggedInUser.refreshUserType(this->channel, true);
|
||||
|
||||
});
|
||||
|
||||
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
|
||||
|
@ -154,7 +155,7 @@ AccountPopupWidget::AccountPopupWidget(SharedChannel _channel)
|
|||
this->hide(); //
|
||||
});
|
||||
|
||||
this->dpiMultiplierChanged(this->getDpiMultiplier(), this->getDpiMultiplier());
|
||||
this->scaleChangedEvent(this->getScale());
|
||||
}
|
||||
|
||||
void AccountPopupWidget::setName(const QString &name)
|
||||
|
@ -171,7 +172,7 @@ void AccountPopupWidget::setName(const QString &name)
|
|||
this->popupWidgetUser.refreshUserType(this->channel, false);
|
||||
}
|
||||
|
||||
void AccountPopupWidget::User::refreshUserType(const SharedChannel &channel, bool loggedInUser)
|
||||
void AccountPopupWidget::User::refreshUserType(const ChannelPtr &channel, bool loggedInUser)
|
||||
{
|
||||
if (channel->name == this->username) {
|
||||
this->userType = UserType::Owner;
|
||||
|
@ -182,7 +183,7 @@ void AccountPopupWidget::User::refreshUserType(const SharedChannel &channel, boo
|
|||
}
|
||||
}
|
||||
|
||||
void AccountPopupWidget::setChannel(SharedChannel _channel)
|
||||
void AccountPopupWidget::setChannel(ChannelPtr _channel)
|
||||
{
|
||||
this->channel = _channel;
|
||||
}
|
||||
|
@ -246,7 +247,7 @@ void AccountPopupWidget::loadAvatar(const QUrl &avatarUrl)
|
|||
}
|
||||
}
|
||||
|
||||
void AccountPopupWidget::dpiMultiplierChanged(float /*oldDpi*/, float newDpi)
|
||||
void AccountPopupWidget::scaleChangedEvent(float newDpi)
|
||||
{
|
||||
this->setStyleSheet(QString("* { font-size: <font-size>px; }")
|
||||
.replace("<font-size>", QString::number((int)(12 * newDpi))));
|
||||
|
|
|
@ -23,10 +23,10 @@ class AccountPopupWidget : public BaseWindow
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AccountPopupWidget(SharedChannel _channel);
|
||||
AccountPopupWidget(ChannelPtr _channel);
|
||||
|
||||
void setName(const QString &name);
|
||||
void setChannel(SharedChannel _channel);
|
||||
void setChannel(ChannelPtr _channel);
|
||||
|
||||
public slots:
|
||||
void actuallyRefreshButtons();
|
||||
|
@ -35,7 +35,7 @@ signals:
|
|||
void refreshButtons();
|
||||
|
||||
protected:
|
||||
virtual void dpiMultiplierChanged(float oldDpi, float newDpi) override;
|
||||
virtual void scaleChangedEvent(float newDpi) override;
|
||||
|
||||
private:
|
||||
Ui::AccountPopup *ui;
|
||||
|
@ -52,7 +52,7 @@ private:
|
|||
|
||||
enum class UserType { User, Mod, Owner };
|
||||
|
||||
SharedChannel channel;
|
||||
ChannelPtr channel;
|
||||
|
||||
QPixmap avatar;
|
||||
|
||||
|
@ -63,7 +63,7 @@ private:
|
|||
QString userID;
|
||||
UserType userType = UserType::User;
|
||||
|
||||
void refreshUserType(const SharedChannel &channel, bool loggedInUser);
|
||||
void refreshUserType(const ChannelPtr &channel, bool loggedInUser);
|
||||
};
|
||||
|
||||
User loggedInUser;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "singletons/settingsmanager.hpp"
|
||||
#include "singletons/thememanager.hpp"
|
||||
|
||||
#include <QChildEvent>
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <QLayout>
|
||||
|
@ -25,13 +26,7 @@ BaseWidget::BaseWidget(BaseWidget *parent, Qt::WindowFlags f)
|
|||
this->init();
|
||||
}
|
||||
|
||||
BaseWidget::BaseWidget(QWidget *parent, Qt::WindowFlags f)
|
||||
: QWidget(parent, f)
|
||||
, themeManager(singletons::ThemeManager::getInstance())
|
||||
{
|
||||
}
|
||||
|
||||
float BaseWidget::getDpiMultiplier()
|
||||
float BaseWidget::getScale() const
|
||||
{
|
||||
// return 1.f;
|
||||
BaseWidget *baseWidget = dynamic_cast<BaseWidget *>(this->window());
|
||||
|
@ -39,17 +34,56 @@ float BaseWidget::getDpiMultiplier()
|
|||
if (baseWidget == nullptr) {
|
||||
return 1.f;
|
||||
} else {
|
||||
return baseWidget->dpiMultiplier;
|
||||
// int screenNr = QApplication::desktop()->screenNumber(this);
|
||||
// QScreen *screen = QApplication::screens().at(screenNr);
|
||||
// return screen->logicalDotsPerInch() / 96.f;
|
||||
return baseWidget->scale;
|
||||
}
|
||||
}
|
||||
|
||||
QSize BaseWidget::getScaleIndependantSize() const
|
||||
{
|
||||
return this->scaleIndependantSize;
|
||||
}
|
||||
|
||||
int BaseWidget::getScaleIndependantWidth() const
|
||||
{
|
||||
return this->scaleIndependantSize.width();
|
||||
}
|
||||
|
||||
int BaseWidget::getScaleIndependantHeight() const
|
||||
{
|
||||
return this->scaleIndependantSize.height();
|
||||
}
|
||||
|
||||
void BaseWidget::setScaleIndependantSize(int width, int height)
|
||||
{
|
||||
this->setScaleIndependantSize(QSize(width, height));
|
||||
}
|
||||
|
||||
void BaseWidget::setScaleIndependantSize(QSize size)
|
||||
{
|
||||
this->scaleIndependantSize = size;
|
||||
|
||||
if (size.width() > 0) {
|
||||
this->setFixedWidth((int)(size.width() * this->getScale()));
|
||||
}
|
||||
if (size.height() > 0) {
|
||||
this->setFixedHeight((int)(size.height() * this->getScale()));
|
||||
}
|
||||
}
|
||||
|
||||
void BaseWidget::setScaleIndependantWidth(int value)
|
||||
{
|
||||
this->setScaleIndependantSize(QSize(value, this->scaleIndependantSize.height()));
|
||||
}
|
||||
|
||||
void BaseWidget::setScaleIndependantHeight(int value)
|
||||
{
|
||||
this->setScaleIndependantSize(QSize(this->scaleIndependantSize.height(), value));
|
||||
}
|
||||
|
||||
void BaseWidget::init()
|
||||
{
|
||||
auto connection = this->themeManager.updated.connect([this]() {
|
||||
this->refreshTheme();
|
||||
this->themeRefreshEvent();
|
||||
|
||||
this->update();
|
||||
});
|
||||
|
@ -59,7 +93,69 @@ void BaseWidget::init()
|
|||
});
|
||||
}
|
||||
|
||||
void BaseWidget::refreshTheme()
|
||||
void BaseWidget::childEvent(QChildEvent *event)
|
||||
{
|
||||
if (event->added()) {
|
||||
BaseWidget *widget = dynamic_cast<BaseWidget *>(event->child());
|
||||
|
||||
if (widget) {
|
||||
this->widgets.push_back(widget);
|
||||
}
|
||||
} else if (event->removed()) {
|
||||
for (auto it = this->widgets.begin(); it != this->widgets.end(); it++) {
|
||||
if (*it == event->child()) {
|
||||
this->widgets.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BaseWidget::setScale(float value)
|
||||
{
|
||||
// update scale value
|
||||
this->scale = value;
|
||||
|
||||
this->scaleChangedEvent(value);
|
||||
this->scaleChanged.invoke(value);
|
||||
|
||||
this->setScaleIndependantSize(this->getScaleIndependantSize());
|
||||
|
||||
// set scale for all children
|
||||
BaseWidget::setScaleRecursive(value, this);
|
||||
}
|
||||
|
||||
void BaseWidget::setScaleRecursive(float scale, QObject *object)
|
||||
{
|
||||
for (QObject *child : object->children()) {
|
||||
BaseWidget *widget = dynamic_cast<BaseWidget *>(child);
|
||||
if (widget != nullptr) {
|
||||
widget->setScale(scale);
|
||||
continue;
|
||||
}
|
||||
|
||||
// QLayout *layout = nullptr;
|
||||
// QWidget *widget = dynamic_cast<QWidget *>(child);
|
||||
|
||||
// if (widget != nullptr) {
|
||||
// layout = widget->layout();
|
||||
// }
|
||||
|
||||
// else {
|
||||
QLayout *layout = dynamic_cast<QLayout *>(object);
|
||||
|
||||
if (layout != nullptr) {
|
||||
setScaleRecursive(scale, layout);
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
void BaseWidget::scaleChangedEvent(float newDpi)
|
||||
{
|
||||
}
|
||||
|
||||
void BaseWidget::themeRefreshEvent()
|
||||
{
|
||||
// Do any color scheme updates here
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <QWidget>
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
namespace singletons {
|
||||
|
@ -8,6 +9,7 @@ class ThemeManager;
|
|||
}
|
||||
|
||||
namespace widgets {
|
||||
class BaseWindow;
|
||||
|
||||
class BaseWidget : public QWidget
|
||||
{
|
||||
|
@ -17,23 +19,38 @@ public:
|
|||
explicit BaseWidget(singletons::ThemeManager &_themeManager, QWidget *parent,
|
||||
Qt::WindowFlags f = Qt::WindowFlags());
|
||||
explicit BaseWidget(BaseWidget *parent, Qt::WindowFlags f = Qt::WindowFlags());
|
||||
explicit BaseWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
|
||||
|
||||
singletons::ThemeManager &themeManager;
|
||||
|
||||
float getDpiMultiplier();
|
||||
float getScale() const;
|
||||
pajlada::Signals::Signal<float> scaleChanged;
|
||||
|
||||
QSize getScaleIndependantSize() const;
|
||||
int getScaleIndependantWidth() const;
|
||||
int getScaleIndependantHeight() const;
|
||||
void setScaleIndependantSize(int width, int height);
|
||||
void setScaleIndependantSize(QSize);
|
||||
void setScaleIndependantWidth(int value);
|
||||
void setScaleIndependantHeight(int value);
|
||||
|
||||
protected:
|
||||
virtual void dpiMultiplierChanged(float /*oldDpi*/, float /*newDpi*/)
|
||||
{
|
||||
}
|
||||
virtual void childEvent(QChildEvent *) override;
|
||||
|
||||
float dpiMultiplier = 1.f;
|
||||
virtual void scaleChangedEvent(float newScale);
|
||||
virtual void themeRefreshEvent();
|
||||
|
||||
void setScale(float value);
|
||||
|
||||
private:
|
||||
void init();
|
||||
float scale = 1.f;
|
||||
QSize scaleIndependantSize;
|
||||
|
||||
virtual void refreshTheme();
|
||||
std::vector<BaseWidget *> widgets;
|
||||
|
||||
static void setScaleRecursive(float scale, QObject *object);
|
||||
|
||||
friend class BaseWindow;
|
||||
};
|
||||
|
||||
} // namespace widgets
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <QApplication>
|
||||
#include <QDebug>
|
||||
#include <QDesktopWidget>
|
||||
#include <QIcon>
|
||||
|
||||
#ifdef USEWINSDK
|
||||
|
@ -22,7 +23,7 @@
|
|||
#define WM_DPICHANGED 0x02E0
|
||||
#endif
|
||||
|
||||
#include "widgets/helper/rippleeffectlabel.hpp"
|
||||
#include "widgets/helper/titlebarbutton.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
@ -43,7 +44,7 @@ BaseWindow::BaseWindow(BaseWidget *parent, bool _enableCustomFrame)
|
|||
}
|
||||
|
||||
BaseWindow::BaseWindow(QWidget *parent, bool _enableCustomFrame)
|
||||
: BaseWidget(parent, Qt::Window)
|
||||
: BaseWidget(singletons::ThemeManager::getInstance(), parent, Qt::Window)
|
||||
, enableCustomFrame(_enableCustomFrame)
|
||||
{
|
||||
this->init();
|
||||
|
@ -56,45 +57,57 @@ void BaseWindow::init()
|
|||
#ifdef USEWINSDK
|
||||
if (this->hasCustomWindowFrame()) {
|
||||
// CUSTOM WINDOW FRAME
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
QVBoxLayout *layout = new QVBoxLayout();
|
||||
layout->setMargin(1);
|
||||
layout->setSpacing(0);
|
||||
this->setLayout(layout);
|
||||
{
|
||||
QHBoxLayout *buttons = this->titlebarBox = new QHBoxLayout;
|
||||
buttons->setMargin(0);
|
||||
layout->addLayout(buttons);
|
||||
QHBoxLayout *buttonLayout = this->titlebarBox = new QHBoxLayout();
|
||||
buttonLayout->setMargin(0);
|
||||
layout->addLayout(buttonLayout);
|
||||
|
||||
// title
|
||||
QLabel *titleLabel = new QLabel("Chatterino");
|
||||
buttons->addWidget(titleLabel);
|
||||
this->titleLabel = titleLabel;
|
||||
QLabel *title = new QLabel(" Chatterino");
|
||||
buttonLayout->addWidget(title);
|
||||
this->titleLabel = title;
|
||||
|
||||
// buttons
|
||||
RippleEffectLabel *min = new RippleEffectLabel;
|
||||
min->getLabel().setText("min");
|
||||
min->setFixedSize(46, 30);
|
||||
RippleEffectLabel *max = new RippleEffectLabel;
|
||||
max->setFixedSize(46, 30);
|
||||
max->getLabel().setText("max");
|
||||
RippleEffectLabel *exit = new RippleEffectLabel;
|
||||
exit->setFixedSize(46, 30);
|
||||
exit->getLabel().setText("exit");
|
||||
TitleBarButton *_minButton = new TitleBarButton;
|
||||
_minButton->setScaleIndependantSize(46, 30);
|
||||
_minButton->setButtonStyle(TitleBarButton::Minimize);
|
||||
TitleBarButton *_maxButton = new TitleBarButton;
|
||||
_maxButton->setScaleIndependantSize(46, 30);
|
||||
_maxButton->setButtonStyle(TitleBarButton::Maximize);
|
||||
TitleBarButton *_exitButton = new TitleBarButton;
|
||||
_exitButton->setScaleIndependantSize(46, 30);
|
||||
_exitButton->setButtonStyle(TitleBarButton::Close);
|
||||
|
||||
this->minButton = min;
|
||||
this->maxButton = max;
|
||||
this->exitButton = exit;
|
||||
QObject::connect(_minButton, &TitleBarButton::clicked, this, [this] {
|
||||
this->setWindowState(Qt::WindowMinimized | this->windowState());
|
||||
});
|
||||
QObject::connect(_maxButton, &TitleBarButton::clicked, this, [this] {
|
||||
this->setWindowState(this->windowState() == Qt::WindowMaximized
|
||||
? Qt::WindowActive
|
||||
: Qt::WindowMaximized);
|
||||
});
|
||||
QObject::connect(_exitButton, &TitleBarButton::clicked, this,
|
||||
[this] { this->close(); });
|
||||
|
||||
this->widgets.push_back(min);
|
||||
this->widgets.push_back(max);
|
||||
this->widgets.push_back(exit);
|
||||
this->minButton = _minButton;
|
||||
this->maxButton = _maxButton;
|
||||
this->exitButton = _exitButton;
|
||||
|
||||
buttons->addStretch(1);
|
||||
buttons->addWidget(min);
|
||||
buttons->addWidget(max);
|
||||
buttons->addWidget(exit);
|
||||
this->buttons.push_back(_minButton);
|
||||
this->buttons.push_back(_maxButton);
|
||||
this->buttons.push_back(_exitButton);
|
||||
|
||||
buttonLayout->addStretch(1);
|
||||
buttonLayout->addWidget(_minButton);
|
||||
buttonLayout->addWidget(_maxButton);
|
||||
buttonLayout->addWidget(_exitButton);
|
||||
buttonLayout->setSpacing(0);
|
||||
}
|
||||
this->layoutBase = new QWidget(this);
|
||||
this->widgets.push_back(this->layoutBase);
|
||||
this->layoutBase = new BaseWidget(this);
|
||||
layout->addWidget(this->layoutBase);
|
||||
}
|
||||
|
||||
|
@ -102,10 +115,10 @@ void BaseWindow::init()
|
|||
auto dpi = util::getWindowDpi(this->winId());
|
||||
|
||||
if (dpi) {
|
||||
this->dpiMultiplier = dpi.value() / 96.f;
|
||||
this->scale = dpi.value() / 96.f;
|
||||
}
|
||||
|
||||
this->dpiMultiplierChanged(1, this->dpiMultiplier);
|
||||
this->scaleChangedEvent(this->scale);
|
||||
#endif
|
||||
|
||||
if (singletons::SettingManager::getInstance().windowTopMost.getValue()) {
|
||||
|
@ -113,6 +126,16 @@ void BaseWindow::init()
|
|||
}
|
||||
}
|
||||
|
||||
void BaseWindow::setStayInScreenRect(bool value)
|
||||
{
|
||||
this->stayInScreenRect = value;
|
||||
}
|
||||
|
||||
bool BaseWindow::getStayInScreenRect() const
|
||||
{
|
||||
return this->stayInScreenRect;
|
||||
}
|
||||
|
||||
QWidget *BaseWindow::getLayoutContainer()
|
||||
{
|
||||
if (this->hasCustomWindowFrame()) {
|
||||
|
@ -125,37 +148,96 @@ QWidget *BaseWindow::getLayoutContainer()
|
|||
bool BaseWindow::hasCustomWindowFrame()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
// return this->enableCustomFrame;
|
||||
return false;
|
||||
return this->enableCustomFrame;
|
||||
// return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void BaseWindow::refreshTheme()
|
||||
void BaseWindow::themeRefreshEvent()
|
||||
{
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::Background, this->themeManager.windowBg);
|
||||
palette.setColor(QPalette::Foreground, this->themeManager.windowText);
|
||||
this->setPalette(palette);
|
||||
|
||||
for (RippleEffectButton *button : this->buttons) {
|
||||
button->setMouseEffectColor(this->themeManager.windowText);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseWindow::addTitleBarButton(const QString &text)
|
||||
void BaseWindow::addTitleBarButton(const TitleBarButton::Style &style,
|
||||
std::function<void()> onClicked)
|
||||
{
|
||||
RippleEffectLabel *label = new RippleEffectLabel;
|
||||
label->getLabel().setText(text);
|
||||
this->widgets.push_back(label);
|
||||
this->titlebarBox->insertWidget(2, label);
|
||||
TitleBarButton *button = new TitleBarButton;
|
||||
button->setScaleIndependantSize(30, 30);
|
||||
|
||||
this->buttons.push_back(button);
|
||||
this->titlebarBox->insertWidget(2, button);
|
||||
button->setButtonStyle(style);
|
||||
|
||||
QObject::connect(button, &TitleBarButton::clicked, this, [onClicked] { onClicked(); });
|
||||
}
|
||||
|
||||
void BaseWindow::changeEvent(QEvent *)
|
||||
{
|
||||
// TooltipWidget::getInstance()->hide();
|
||||
TooltipWidget::getInstance()->hide();
|
||||
|
||||
#ifdef USEWINSDK
|
||||
if (this->hasCustomWindowFrame()) {
|
||||
this->maxButton->setButtonStyle(this->windowState() & Qt::WindowMaximized
|
||||
? TitleBarButton::Unmaximize
|
||||
: TitleBarButton::Maximize);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void BaseWindow::leaveEvent(QEvent *)
|
||||
{
|
||||
// TooltipWidget::getInstance()->hide();
|
||||
TooltipWidget::getInstance()->hide();
|
||||
}
|
||||
|
||||
void BaseWindow::moveTo(QWidget *parent, QPoint point)
|
||||
{
|
||||
point.rx() += 16;
|
||||
point.ry() += 16;
|
||||
|
||||
this->move(point);
|
||||
this->moveIntoDesktopRect(parent);
|
||||
}
|
||||
|
||||
void BaseWindow::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
this->moveIntoDesktopRect(this);
|
||||
}
|
||||
|
||||
void BaseWindow::moveIntoDesktopRect(QWidget *parent)
|
||||
{
|
||||
if (!this->stayInScreenRect)
|
||||
return;
|
||||
|
||||
// move the widget into the screen geometry if it's not already in there
|
||||
QDesktopWidget *desktop = QApplication::desktop();
|
||||
|
||||
QRect s = desktop->screenGeometry(parent);
|
||||
QPoint p = this->pos();
|
||||
|
||||
if (p.x() < s.left()) {
|
||||
p.setX(s.left());
|
||||
}
|
||||
if (p.y() < s.top()) {
|
||||
p.setY(s.top());
|
||||
}
|
||||
if (p.x() + this->width() > s.right()) {
|
||||
p.setX(s.right() - this->width());
|
||||
}
|
||||
if (p.y() + this->height() > s.bottom()) {
|
||||
p.setY(s.bottom() - this->height());
|
||||
}
|
||||
|
||||
if (p != this->pos())
|
||||
this->move(p);
|
||||
}
|
||||
|
||||
#ifdef USEWINSDK
|
||||
|
@ -165,17 +247,16 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
|
|||
|
||||
switch (msg->message) {
|
||||
case WM_DPICHANGED: {
|
||||
qDebug() << "dpi changed";
|
||||
int dpi = HIWORD(msg->wParam);
|
||||
|
||||
float oldDpiMultiplier = this->dpiMultiplier;
|
||||
this->dpiMultiplier = dpi / 96.f;
|
||||
float scale = this->dpiMultiplier / oldDpiMultiplier;
|
||||
float oldScale = this->scale;
|
||||
float _scale = dpi / 96.f;
|
||||
float resizeScale = _scale / oldScale;
|
||||
|
||||
this->dpiMultiplierChanged(oldDpiMultiplier, this->dpiMultiplier);
|
||||
this->resize(static_cast<int>(this->width() * resizeScale),
|
||||
static_cast<int>(this->height() * resizeScale));
|
||||
|
||||
this->resize(static_cast<int>(this->width() * scale),
|
||||
static_cast<int>(this->height() * scale));
|
||||
this->setScale(_scale);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -250,12 +331,16 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
|
|||
bool client = false;
|
||||
|
||||
QPoint point(x - winrect.left, y - winrect.top);
|
||||
for (QWidget *widget : this->widgets) {
|
||||
for (QWidget *widget : this->buttons) {
|
||||
if (widget->geometry().contains(point)) {
|
||||
client = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->layoutBase->geometry().contains(point)) {
|
||||
client = true;
|
||||
}
|
||||
|
||||
if (client) {
|
||||
*result = HTCLIENT;
|
||||
} else {
|
||||
|
@ -263,8 +348,6 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
|
|||
}
|
||||
}
|
||||
|
||||
qDebug() << *result;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return QWidget::nativeEvent(eventType, message, result);
|
||||
|
@ -272,9 +355,10 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
|
|||
break;
|
||||
} // end case WM_NCHITTEST
|
||||
case WM_CLOSE: {
|
||||
if (this->enableCustomFrame) {
|
||||
return close();
|
||||
}
|
||||
// if (this->enableCustomFrame) {
|
||||
// this->close();
|
||||
// }
|
||||
return QWidget::nativeEvent(eventType, message, result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -282,9 +366,10 @@ bool BaseWindow::nativeEvent(const QByteArray &eventType, void *message, long *r
|
|||
}
|
||||
}
|
||||
|
||||
void BaseWindow::showEvent(QShowEvent *)
|
||||
void BaseWindow::showEvent(QShowEvent *event)
|
||||
{
|
||||
if (this->isVisible() && this->hasCustomWindowFrame()) {
|
||||
if (!this->shown && this->isVisible() && this->hasCustomWindowFrame()) {
|
||||
this->shown = true;
|
||||
SetWindowLongPtr((HWND)this->winId(), GWL_STYLE,
|
||||
WS_POPUP | WS_CAPTION | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX);
|
||||
|
||||
|
@ -294,22 +379,29 @@ void BaseWindow::showEvent(QShowEvent *)
|
|||
SetWindowPos((HWND)this->winId(), 0, 0, 0, 0, 0,
|
||||
SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
|
||||
}
|
||||
|
||||
BaseWidget::showEvent(event);
|
||||
}
|
||||
|
||||
void BaseWindow::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
BaseWidget::paintEvent(event);
|
||||
|
||||
if (this->hasCustomWindowFrame()) {
|
||||
BaseWidget::paintEvent(event);
|
||||
|
||||
QPainter painter(this);
|
||||
|
||||
bool windowFocused = this->window() == QApplication::activeWindow();
|
||||
|
||||
QLinearGradient gradient(0, 0, 10, 250);
|
||||
gradient.setColorAt(1, this->themeManager.tabs.selected.backgrounds.unfocused.color());
|
||||
|
||||
if (windowFocused) {
|
||||
painter.setPen(this->themeManager.tabs.selected.backgrounds.regular.color());
|
||||
gradient.setColorAt(.4, this->themeManager.tabs.selected.backgrounds.regular.color());
|
||||
} else {
|
||||
painter.setPen(this->themeManager.tabs.selected.backgrounds.unfocused.color());
|
||||
gradient.setColorAt(.4, this->themeManager.tabs.selected.backgrounds.unfocused.color());
|
||||
}
|
||||
painter.setPen(QPen(QBrush(gradient), 1));
|
||||
|
||||
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "basewidget.hpp"
|
||||
#include "widgets/helper/titlebarbutton.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
class QHBoxLayout;
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
class RippleEffectButton;
|
||||
class TitleBarButton;
|
||||
|
||||
class BaseWindow : public BaseWidget
|
||||
{
|
||||
|
@ -17,7 +22,12 @@ public:
|
|||
|
||||
QWidget *getLayoutContainer();
|
||||
bool hasCustomWindowFrame();
|
||||
void addTitleBarButton(const QString &text);
|
||||
void addTitleBarButton(const TitleBarButton::Style &style, std::function<void()> onClicked);
|
||||
|
||||
void setStayInScreenRect(bool value);
|
||||
bool getStayInScreenRect() const;
|
||||
|
||||
void moveTo(QWidget *widget, QPoint point);
|
||||
|
||||
protected:
|
||||
#ifdef USEWINSDK
|
||||
|
@ -28,21 +38,25 @@ protected:
|
|||
|
||||
virtual void changeEvent(QEvent *) override;
|
||||
virtual void leaveEvent(QEvent *) override;
|
||||
virtual void resizeEvent(QResizeEvent *) override;
|
||||
|
||||
virtual void refreshTheme() override;
|
||||
virtual void themeRefreshEvent() override;
|
||||
|
||||
private:
|
||||
void init();
|
||||
void moveIntoDesktopRect(QWidget *parent);
|
||||
|
||||
bool enableCustomFrame;
|
||||
bool stayInScreenRect = false;
|
||||
bool shown = false;
|
||||
|
||||
QHBoxLayout *titlebarBox;
|
||||
QWidget *titleLabel;
|
||||
QWidget *minButton;
|
||||
QWidget *maxButton;
|
||||
QWidget *exitButton;
|
||||
TitleBarButton *minButton;
|
||||
TitleBarButton *maxButton;
|
||||
TitleBarButton *exitButton;
|
||||
QWidget *layoutBase;
|
||||
std::vector<QWidget *> widgets;
|
||||
std::vector<RippleEffectButton *> buttons;
|
||||
};
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -18,6 +18,11 @@ EmotePopup::EmotePopup(singletons::ThemeManager &themeManager)
|
|||
this->viewEmotes = new ChannelView();
|
||||
this->viewEmojis = new ChannelView();
|
||||
|
||||
this->viewEmotes->setOverrideFlags((MessageElement::Flags)(
|
||||
MessageElement::Default | MessageElement::AlwaysShow | MessageElement::EmoteImages));
|
||||
this->viewEmojis->setOverrideFlags((MessageElement::Flags)(
|
||||
MessageElement::Default | MessageElement::AlwaysShow | MessageElement::EmoteImages));
|
||||
|
||||
this->viewEmotes->setEnableScrollingToBottom(false);
|
||||
this->viewEmojis->setEnableScrollingToBottom(false);
|
||||
|
||||
|
@ -30,9 +35,14 @@ EmotePopup::EmotePopup(singletons::ThemeManager &themeManager)
|
|||
tabs->addTab(this->viewEmojis, "Emojis");
|
||||
|
||||
this->loadEmojis();
|
||||
|
||||
this->viewEmotes->linkClicked.connect(
|
||||
[this](const Link &link) { this->linkClicked.invoke(link); });
|
||||
this->viewEmojis->linkClicked.connect(
|
||||
[this](const Link &link) { this->linkClicked.invoke(link); });
|
||||
}
|
||||
|
||||
void EmotePopup::loadChannel(SharedChannel _channel)
|
||||
void EmotePopup::loadChannel(ChannelPtr _channel)
|
||||
{
|
||||
TwitchChannel *channel = dynamic_cast<TwitchChannel *>(_channel.get());
|
||||
|
||||
|
@ -40,7 +50,7 @@ void EmotePopup::loadChannel(SharedChannel _channel)
|
|||
return;
|
||||
}
|
||||
|
||||
SharedChannel emoteChannel(new Channel(""));
|
||||
ChannelPtr emoteChannel(new Channel(""));
|
||||
|
||||
auto addEmotes = [&](util::EmoteMap &map, const QString &title, const QString &emoteDesc) {
|
||||
// TITLE
|
||||
|
@ -57,7 +67,7 @@ void EmotePopup::loadChannel(SharedChannel _channel)
|
|||
builder2.getMessage()->flags &= Message::DisableCompactEmotes;
|
||||
|
||||
map.each([&](const QString &key, const util::EmoteData &value) {
|
||||
builder2.appendElement((new EmoteElement(value, MessageElement::Flags::AlwaysShow)) //
|
||||
builder2.appendElement((new EmoteElement(value, MessageElement::Flags::AlwaysShow))
|
||||
->setLink(Link(Link::InsertText, key)));
|
||||
});
|
||||
|
||||
|
@ -81,7 +91,7 @@ void EmotePopup::loadEmojis()
|
|||
{
|
||||
util::EmoteMap &emojis = singletons::EmoteManager::getInstance().getEmojis();
|
||||
|
||||
SharedChannel emojiChannel(new Channel(""));
|
||||
ChannelPtr emojiChannel(new Channel(""));
|
||||
|
||||
// title
|
||||
messages::MessageBuilder builder1;
|
||||
|
@ -96,7 +106,7 @@ void EmotePopup::loadEmojis()
|
|||
builder.getMessage()->flags &= Message::DisableCompactEmotes;
|
||||
|
||||
emojis.each([this, &builder](const QString &key, const util::EmoteData &value) {
|
||||
builder.appendElement((new EmoteElement(value, MessageElement::Flags::AlwaysShow)) //
|
||||
builder.appendElement((new EmoteElement(value, MessageElement::Flags::AlwaysShow))
|
||||
->setLink(Link(Link::Type::InsertText, key)));
|
||||
});
|
||||
emojiChannel->addMessage(builder.getMessage());
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "widgets/basewindow.hpp"
|
||||
#include "widgets/helper/channelview.hpp"
|
||||
|
||||
#include <pajlada/signals/signal.hpp>
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
||||
|
@ -12,9 +14,11 @@ class EmotePopup : public BaseWindow
|
|||
public:
|
||||
explicit EmotePopup(singletons::ThemeManager &);
|
||||
|
||||
void loadChannel(SharedChannel channel);
|
||||
void loadChannel(ChannelPtr channel);
|
||||
void loadEmojis();
|
||||
|
||||
pajlada::Signals::Signal<messages::Link> linkClicked;
|
||||
|
||||
private:
|
||||
ChannelView *viewEmotes;
|
||||
ChannelView *viewEmojis;
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "widgets/split.hpp"
|
||||
#include "widgets/tooltipwidget.hpp"
|
||||
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QGraphicsBlurEffect>
|
||||
|
@ -24,8 +25,7 @@
|
|||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#define LAYOUT_WIDTH \
|
||||
(this->width() - (this->scrollBar.isVisible() ? 16 : 4) * this->getDpiMultiplier())
|
||||
#define LAYOUT_WIDTH (this->width() - (this->scrollBar.isVisible() ? 16 : 4) * this->getScale())
|
||||
|
||||
using namespace chatterino::messages;
|
||||
|
||||
|
@ -96,6 +96,9 @@ ChannelView::ChannelView(BaseWidget *parent)
|
|||
auto e = new QResizeEvent(this->size(), this->size());
|
||||
this->resizeEvent(e);
|
||||
delete e;
|
||||
|
||||
singletons::SettingManager::getInstance().showLastMessageIndicator.connect(
|
||||
[this](auto, auto) { this->update(); }, this->managedConnections);
|
||||
}
|
||||
|
||||
ChannelView::~ChannelView()
|
||||
|
@ -111,6 +114,13 @@ ChannelView::~ChannelView()
|
|||
this->messageReplacedConnection.disconnect();
|
||||
}
|
||||
|
||||
void ChannelView::themeRefreshEvent()
|
||||
{
|
||||
BaseWidget::themeRefreshEvent();
|
||||
|
||||
this->layoutMessages();
|
||||
}
|
||||
|
||||
void ChannelView::queueUpdate()
|
||||
{
|
||||
if (this->updateTimer.isActive()) {
|
||||
|
@ -164,7 +174,7 @@ void ChannelView::actuallyLayoutMessages()
|
|||
for (size_t i = start; i < messagesSnapshot.getLength(); ++i) {
|
||||
auto message = messagesSnapshot[i];
|
||||
|
||||
redrawRequired |= message->layout(layoutWidth, this->getDpiMultiplier(), flags);
|
||||
redrawRequired |= message->layout(layoutWidth, this->getScale(), flags);
|
||||
|
||||
y += message->getHeight();
|
||||
|
||||
|
@ -180,7 +190,7 @@ void ChannelView::actuallyLayoutMessages()
|
|||
for (int i = (int)messagesSnapshot.getLength() - 1; i >= 0; i--) {
|
||||
auto *message = messagesSnapshot[i].get();
|
||||
|
||||
message->layout(layoutWidth, this->getDpiMultiplier(), flags);
|
||||
message->layout(layoutWidth, this->getScale(), flags);
|
||||
|
||||
h -= message->getHeight();
|
||||
|
||||
|
@ -281,6 +291,16 @@ bool ChannelView::getEnableScrollingToBottom() const
|
|||
return this->enableScrollingToBottom;
|
||||
}
|
||||
|
||||
void ChannelView::setOverrideFlags(boost::optional<messages::MessageElement::Flags> value)
|
||||
{
|
||||
this->overrideFlags = value;
|
||||
}
|
||||
|
||||
const boost::optional<messages::MessageElement::Flags> &ChannelView::getOverrideFlags() const
|
||||
{
|
||||
return this->overrideFlags;
|
||||
}
|
||||
|
||||
messages::LimitedQueueSnapshot<MessageLayoutPtr> ChannelView::getMessagesSnapshot()
|
||||
{
|
||||
if (!this->paused) {
|
||||
|
@ -290,7 +310,7 @@ messages::LimitedQueueSnapshot<MessageLayoutPtr> ChannelView::getMessagesSnapsho
|
|||
return this->snapshot;
|
||||
}
|
||||
|
||||
void ChannelView::setChannel(SharedChannel newChannel)
|
||||
void ChannelView::setChannel(ChannelPtr newChannel)
|
||||
{
|
||||
if (this->channel) {
|
||||
this->detachChannel();
|
||||
|
@ -328,7 +348,6 @@ void ChannelView::setChannel(SharedChannel newChannel)
|
|||
newChannel->messagesAddedAtStart.connect([this](std::vector<MessagePtr> &messages) {
|
||||
std::vector<MessageLayoutPtr> messageRefs;
|
||||
messageRefs.resize(messages.size());
|
||||
qDebug() << messages.size();
|
||||
for (size_t i = 0; i < messages.size(); i++) {
|
||||
messageRefs.at(i) = MessageLayoutPtr(new MessageLayout(messages.at(i)));
|
||||
}
|
||||
|
@ -410,6 +429,17 @@ void ChannelView::pause(int msecTimeout)
|
|||
this->pauseTimeout.start(msecTimeout);
|
||||
}
|
||||
|
||||
void ChannelView::updateLastReadMessage()
|
||||
{
|
||||
auto _snapshot = this->getMessagesSnapshot();
|
||||
|
||||
if (_snapshot.getLength() > 0) {
|
||||
this->lastReadMessage = _snapshot[_snapshot.getLength() - 1];
|
||||
}
|
||||
|
||||
this->update();
|
||||
}
|
||||
|
||||
void ChannelView::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
this->scrollBar.resize(this->scrollBar.width(), height());
|
||||
|
@ -434,6 +464,10 @@ void ChannelView::setSelection(const SelectionItem &start, const SelectionItem &
|
|||
|
||||
messages::MessageElement::Flags ChannelView::getFlags() const
|
||||
{
|
||||
if (this->overrideFlags) {
|
||||
return this->overrideFlags.get();
|
||||
}
|
||||
|
||||
MessageElement::Flags flags = singletons::SettingManager::getInstance().getWordFlags();
|
||||
|
||||
Split *split = dynamic_cast<Split *>(this->parentWidget());
|
||||
|
@ -442,6 +476,9 @@ messages::MessageElement::Flags ChannelView::getFlags() const
|
|||
if (split->getModerationMode()) {
|
||||
flags = (MessageElement::Flags)(flags | MessageElement::ModeratorTools);
|
||||
}
|
||||
if (this->channel == singletons::ChannelManager::getInstance().mentionsChannel) {
|
||||
flags = (MessageElement::Flags)(flags | MessageElement::ChannelName);
|
||||
}
|
||||
}
|
||||
|
||||
return flags;
|
||||
|
@ -477,11 +514,17 @@ void ChannelView::drawMessages(QPainter &painter)
|
|||
(fmod(this->scrollBar.getCurrentValue(), 1)));
|
||||
|
||||
messages::MessageLayout *end = nullptr;
|
||||
bool windowFocused = this->window() == QApplication::activeWindow();
|
||||
|
||||
for (size_t i = start; i < messagesSnapshot.getLength(); ++i) {
|
||||
messages::MessageLayout *layout = messagesSnapshot[i].get();
|
||||
|
||||
layout->paint(painter, y, i, this->selection);
|
||||
bool isLastMessage = false;
|
||||
if (singletons::SettingManager::getInstance().showLastMessageIndicator) {
|
||||
isLastMessage = this->lastReadMessage.get() == layout;
|
||||
}
|
||||
|
||||
layout->paint(painter, y, i, this->selection, isLastMessage, windowFocused);
|
||||
|
||||
y += layout->getHeight();
|
||||
|
||||
|
@ -551,8 +594,7 @@ void ChannelView::wheelEvent(QWheelEvent *event)
|
|||
if (i == 0) {
|
||||
desired = 0;
|
||||
} else {
|
||||
snapshot[i - 1]->layout(LAYOUT_WIDTH, this->getDpiMultiplier(),
|
||||
this->getFlags());
|
||||
snapshot[i - 1]->layout(LAYOUT_WIDTH, this->getScale(), this->getFlags());
|
||||
scrollFactor = 1;
|
||||
currentScrollLeft = snapshot[i - 1]->getHeight();
|
||||
}
|
||||
|
@ -574,8 +616,7 @@ void ChannelView::wheelEvent(QWheelEvent *event)
|
|||
if (i == snapshotLength - 1) {
|
||||
desired = snapshot.getLength();
|
||||
} else {
|
||||
snapshot[i + 1]->layout(LAYOUT_WIDTH, this->getDpiMultiplier(),
|
||||
this->getFlags());
|
||||
snapshot[i + 1]->layout(LAYOUT_WIDTH, this->getScale(), this->getFlags());
|
||||
|
||||
scrollFactor = 1;
|
||||
currentScrollLeft = snapshot[i + 1]->getHeight();
|
||||
|
@ -782,12 +823,50 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
|
|||
}
|
||||
|
||||
auto &link = hoverLayoutElement->getLink();
|
||||
if (event->button() != Qt::LeftButton ||
|
||||
!singletons::SettingManager::getInstance().linksDoubleClickOnly) {
|
||||
this->handleLinkClick(event, link, layout.get());
|
||||
}
|
||||
|
||||
this->linkClicked.invoke(link);
|
||||
}
|
||||
|
||||
void ChannelView::mouseDoubleClickEvent(QMouseEvent *event)
|
||||
{
|
||||
if (singletons::SettingManager::getInstance().linksDoubleClickOnly) {
|
||||
std::shared_ptr<messages::MessageLayout> layout;
|
||||
QPoint relativePos;
|
||||
int messageIndex;
|
||||
|
||||
if (!tryGetMessageAt(event->pos(), layout, relativePos, messageIndex)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// message under cursor is collapsed
|
||||
if (layout->getFlags() & MessageLayout::Collapsed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const messages::MessageLayoutElement *hoverLayoutElement =
|
||||
layout->getElementAt(relativePos);
|
||||
|
||||
if (hoverLayoutElement == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &link = hoverLayoutElement->getLink();
|
||||
this->handleLinkClick(event, link, layout.get());
|
||||
}
|
||||
}
|
||||
|
||||
void ChannelView::handleLinkClick(QMouseEvent *event, const messages::Link &link,
|
||||
messages::MessageLayout *layout)
|
||||
{
|
||||
switch (link.getType()) {
|
||||
case messages::Link::UserInfo: {
|
||||
auto user = link.getValue();
|
||||
this->userPopupWidget.setName(user);
|
||||
this->userPopupWidget.move(event->screenPos().toPoint());
|
||||
this->userPopupWidget.moveTo(this, event->screenPos().toPoint());
|
||||
this->userPopupWidget.show();
|
||||
this->userPopupWidget.setFocus();
|
||||
|
||||
|
@ -795,7 +874,24 @@ void ChannelView::mouseReleaseEvent(QMouseEvent *event)
|
|||
break;
|
||||
}
|
||||
case messages::Link::Url: {
|
||||
QDesktopServices::openUrl(QUrl(link.getValue()));
|
||||
if (event->button() == Qt::RightButton) {
|
||||
static QMenu *menu = nullptr;
|
||||
static QString url;
|
||||
|
||||
if (menu == nullptr) {
|
||||
menu = new QMenu;
|
||||
menu->addAction("Open in browser",
|
||||
[] { QDesktopServices::openUrl(QUrl(url)); });
|
||||
menu->addAction("Copy to clipboard",
|
||||
[] { QApplication::clipboard()->setText(url); });
|
||||
}
|
||||
|
||||
url = link.getValue();
|
||||
menu->move(QCursor::pos());
|
||||
menu->show();
|
||||
} else {
|
||||
QDesktopServices::openUrl(QUrl(link.getValue()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case messages::Link::UserAction: {
|
||||
|
|
|
@ -38,9 +38,12 @@ public:
|
|||
void clearSelection();
|
||||
void setEnableScrollingToBottom(bool);
|
||||
bool getEnableScrollingToBottom() const;
|
||||
void setOverrideFlags(boost::optional<messages::MessageElement::Flags> value);
|
||||
const boost::optional<messages::MessageElement::Flags> &getOverrideFlags() const;
|
||||
void pause(int msecTimeout);
|
||||
void updateLastReadMessage();
|
||||
|
||||
void setChannel(SharedChannel channel);
|
||||
void setChannel(ChannelPtr channel);
|
||||
messages::LimitedQueueSnapshot<messages::MessageLayoutPtr> getMessagesSnapshot();
|
||||
void layoutMessages();
|
||||
|
||||
|
@ -49,8 +52,11 @@ public:
|
|||
boost::signals2::signal<void(QMouseEvent *)> mouseDown;
|
||||
boost::signals2::signal<void()> selectionChanged;
|
||||
pajlada::Signals::NoArgSignal highlightedMessageReceived;
|
||||
pajlada::Signals::Signal<const messages::Link &> linkClicked;
|
||||
|
||||
protected:
|
||||
virtual void themeRefreshEvent() override;
|
||||
|
||||
virtual void resizeEvent(QResizeEvent *) override;
|
||||
|
||||
virtual void paintEvent(QPaintEvent *) override;
|
||||
|
@ -62,6 +68,10 @@ protected:
|
|||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
|
||||
void handleLinkClick(QMouseEvent *event, const messages::Link &link,
|
||||
messages::MessageLayout *layout);
|
||||
|
||||
bool tryGetMessageAt(QPoint p, std::shared_ptr<messages::MessageLayout> &message,
|
||||
QPoint &relativePos, int &index);
|
||||
|
@ -72,6 +82,8 @@ private:
|
|||
bool messageWasAdded = false;
|
||||
bool paused = false;
|
||||
QTimer pauseTimeout;
|
||||
boost::optional<messages::MessageElement::Flags> overrideFlags;
|
||||
messages::MessageLayoutPtr lastReadMessage;
|
||||
|
||||
messages::LimitedQueueSnapshot<messages::MessageLayoutPtr> snapshot;
|
||||
|
||||
|
@ -82,7 +94,7 @@ private:
|
|||
void setSelection(const messages::SelectionItem &start, const messages::SelectionItem &end);
|
||||
messages::MessageElement::Flags getFlags() const;
|
||||
|
||||
SharedChannel channel;
|
||||
ChannelPtr channel;
|
||||
|
||||
Scrollbar scrollBar;
|
||||
RippleEffectLabel *goToBottom;
|
||||
|
|
78
src/widgets/helper/label.cpp
Normal file
78
src/widgets/helper/label.cpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#include "label.hpp"
|
||||
#include "singletons/fontmanager.hpp"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
Label::Label(BaseWidget *parent)
|
||||
: BaseWidget(parent)
|
||||
{
|
||||
singletons::FontManager::getInstance().fontChanged.connect(
|
||||
[this]() { this->scaleChangedEvent(this->getScale()); });
|
||||
}
|
||||
|
||||
const QString &Label::getText() const
|
||||
{
|
||||
return this->text;
|
||||
}
|
||||
|
||||
void Label::setText(const QString &value)
|
||||
{
|
||||
this->text = value;
|
||||
this->scaleChangedEvent(this->getScale());
|
||||
}
|
||||
|
||||
FontStyle Label::getFontStyle() const
|
||||
{
|
||||
return this->fontStyle;
|
||||
}
|
||||
|
||||
void Label::setFontStyle(FontStyle style)
|
||||
{
|
||||
this->fontStyle = style;
|
||||
this->scaleChangedEvent(this->getScale());
|
||||
}
|
||||
|
||||
void Label::scaleChangedEvent(float scale)
|
||||
{
|
||||
QFontMetrics metrics =
|
||||
singletons::FontManager::getInstance().getFontMetrics(this->fontStyle, scale);
|
||||
|
||||
this->preferedSize = QSize(metrics.width(this->text), metrics.height());
|
||||
|
||||
this->updateGeometry();
|
||||
}
|
||||
|
||||
QSize Label::sizeHint() const
|
||||
{
|
||||
return this->preferedSize;
|
||||
}
|
||||
|
||||
QSize Label::minimumSizeHint() const
|
||||
{
|
||||
return this->preferedSize;
|
||||
}
|
||||
|
||||
void Label::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
painter.setFont(singletons::FontManager::getInstance().getFont(
|
||||
this->fontStyle, this->getScale() / painter.device()->devicePixelRatioF()));
|
||||
|
||||
int width = singletons::FontManager::getInstance()
|
||||
.getFontMetrics(this->fontStyle, this->getScale())
|
||||
.width(this->text);
|
||||
|
||||
int flags = Qt::TextSingleLine;
|
||||
|
||||
if (this->width() < width) {
|
||||
flags |= Qt::AlignLeft | Qt::AlignVCenter;
|
||||
} else {
|
||||
flags |= Qt::AlignCenter;
|
||||
}
|
||||
|
||||
painter.drawText(this->rect(), flags, this->text);
|
||||
}
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
33
src/widgets/helper/label.hpp
Normal file
33
src/widgets/helper/label.hpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "singletons/fontmanager.hpp"
|
||||
#include "widgets/basewidget.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
||||
class Label : public BaseWidget
|
||||
{
|
||||
public:
|
||||
Label(BaseWidget *parent);
|
||||
|
||||
const QString &getText() const;
|
||||
void setText(const QString &text);
|
||||
|
||||
FontStyle getFontStyle() const;
|
||||
void setFontStyle(FontStyle style);
|
||||
|
||||
protected:
|
||||
virtual void scaleChangedEvent(float scale) override;
|
||||
virtual void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
virtual QSize sizeHint() const override;
|
||||
virtual QSize minimumSizeHint() const override;
|
||||
|
||||
private:
|
||||
QSize preferedSize;
|
||||
QString text;
|
||||
FontStyle fontStyle = FontStyle::Medium;
|
||||
};
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
|
@ -1,12 +1,16 @@
|
|||
#include "widgets/helper/notebookbutton.hpp"
|
||||
#include "singletons/thememanager.hpp"
|
||||
#include "widgets/helper/rippleeffectbutton.hpp"
|
||||
#include "widgets/notebook.hpp"
|
||||
#include "widgets/splitcontainer.hpp"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QPainterPath>
|
||||
#include <QRadialGradient>
|
||||
|
||||
#define nuuls nullptr
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
|
||||
|
@ -14,6 +18,8 @@ NotebookButton::NotebookButton(BaseWidget *parent)
|
|||
: RippleEffectButton(parent)
|
||||
{
|
||||
setMouseEffectColor(QColor(0, 0, 0));
|
||||
|
||||
this->setAcceptDrops(true);
|
||||
}
|
||||
|
||||
void NotebookButton::paintEvent(QPaintEvent *)
|
||||
|
@ -97,5 +103,46 @@ void NotebookButton::mouseReleaseEvent(QMouseEvent *event)
|
|||
RippleEffectButton::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void NotebookButton::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if (!event->mimeData()->hasFormat("chatterino/split"))
|
||||
return;
|
||||
|
||||
event->acceptProposedAction();
|
||||
|
||||
auto e = new QMouseEvent(QMouseEvent::MouseButtonPress,
|
||||
QPointF(this->width() / 2, this->height() / 2), Qt::LeftButton,
|
||||
Qt::LeftButton, 0);
|
||||
RippleEffectButton::mousePressEvent(e);
|
||||
delete e;
|
||||
}
|
||||
|
||||
void NotebookButton::dragLeaveEvent(QDragLeaveEvent *)
|
||||
{
|
||||
this->mouseDown = true;
|
||||
this->update();
|
||||
|
||||
auto e = new QMouseEvent(QMouseEvent::MouseButtonRelease,
|
||||
QPointF(this->width() / 2, this->height() / 2), Qt::LeftButton,
|
||||
Qt::LeftButton, 0);
|
||||
RippleEffectButton::mouseReleaseEvent(e);
|
||||
delete e;
|
||||
}
|
||||
|
||||
void NotebookButton::dropEvent(QDropEvent *event)
|
||||
{
|
||||
if (SplitContainer::isDraggingSplit) {
|
||||
event->acceptProposedAction();
|
||||
|
||||
Notebook *notebook = dynamic_cast<Notebook *>(this->parentWidget());
|
||||
|
||||
if (notebook != nuuls) {
|
||||
SplitContainer *tab = notebook->addNewPage();
|
||||
|
||||
SplitContainer::draggingSplit->setParent(tab);
|
||||
tab->addToLayout(SplitContainer::draggingSplit);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
||||
|
|
|
@ -21,8 +21,11 @@ public:
|
|||
NotebookButton(BaseWidget *parent);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
virtual void paintEvent(QPaintEvent *) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *) override;
|
||||
virtual void dragEnterEvent(QDragEnterEvent *) override;
|
||||
virtual void dragLeaveEvent(QDragLeaveEvent *) override;
|
||||
virtual void dropEvent(QDropEvent *) override;
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
|
|
|
@ -25,7 +25,6 @@ NotebookTab::NotebookTab(Notebook *_notebook, const std::string &_uuid)
|
|||
, useDefaultBehaviour(fS("{}/useDefaultBehaviour", this->settingRoot), true)
|
||||
, menu(this)
|
||||
{
|
||||
this->calcSize();
|
||||
this->setAcceptDrops(true);
|
||||
|
||||
this->positionChangedAnimation.setEasingCurve(QEasingCurve(QEasingCurve::InCubic));
|
||||
|
@ -65,22 +64,25 @@ NotebookTab::NotebookTab(Notebook *_notebook, const std::string &_uuid)
|
|||
|
||||
this->menu.addAction(enableHighlightsOnNewMessageAction);
|
||||
|
||||
connect(enableHighlightsOnNewMessageAction, &QAction::toggled, [this](bool newValue) {
|
||||
QObject::connect(enableHighlightsOnNewMessageAction, &QAction::toggled, [this](bool newValue) {
|
||||
debug::Log("New value is {}", newValue); //
|
||||
});
|
||||
}
|
||||
|
||||
void NotebookTab::calcSize()
|
||||
void NotebookTab::themeRefreshEvent()
|
||||
{
|
||||
float scale = getDpiMultiplier();
|
||||
this->update();
|
||||
}
|
||||
|
||||
void NotebookTab::updateSize()
|
||||
{
|
||||
float scale = getScale();
|
||||
QString qTitle(qS(this->title));
|
||||
|
||||
if (singletons::SettingManager::getInstance().hideTabX) {
|
||||
this->resize(static_cast<int>((fontMetrics().width(qTitle) + 16) * scale),
|
||||
static_cast<int>(24 * scale));
|
||||
this->resize((int)((fontMetrics().width(qTitle) + 16) * scale), (int)(24 * scale));
|
||||
} else {
|
||||
this->resize(static_cast<int>((fontMetrics().width(qTitle) + 8 + 24) * scale),
|
||||
static_cast<int>(24 * scale));
|
||||
this->resize((int)((fontMetrics().width(qTitle) + 8 + 24) * scale), (int)(24 * scale));
|
||||
}
|
||||
|
||||
if (this->parent() != nullptr) {
|
||||
|
@ -97,7 +99,7 @@ void NotebookTab::setTitle(const QString &newTitle)
|
|||
{
|
||||
this->title = newTitle.toStdString();
|
||||
|
||||
this->calcSize();
|
||||
this->updateSize();
|
||||
}
|
||||
|
||||
bool NotebookTab::isSelected() const
|
||||
|
@ -134,7 +136,7 @@ QRect NotebookTab::getDesiredRect() const
|
|||
|
||||
void NotebookTab::hideTabXChanged(bool)
|
||||
{
|
||||
this->calcSize();
|
||||
this->updateSize();
|
||||
this->update();
|
||||
}
|
||||
|
||||
|
@ -197,7 +199,7 @@ void NotebookTab::paintEvent(QPaintEvent *)
|
|||
painter.setPen(colors.text);
|
||||
|
||||
// set area for text
|
||||
float scale = this->getDpiMultiplier();
|
||||
float scale = this->getScale();
|
||||
int rectW = (settingManager.hideTabX ? 0 : static_cast<int>(16) * scale);
|
||||
QRect rect(0, 0, this->width() - rectW, this->height());
|
||||
|
||||
|
@ -291,14 +293,14 @@ void NotebookTab::mouseMoveEvent(QMouseEvent *event)
|
|||
}
|
||||
}
|
||||
|
||||
if (this->mouseDown && !this->getDesiredRect().contains(event->pos())) {
|
||||
QPoint relPoint = this->mapToParent(event->pos());
|
||||
QPoint relPoint = this->mapToParent(event->pos());
|
||||
|
||||
if (this->mouseDown && !this->getDesiredRect().contains(relPoint)) {
|
||||
int index;
|
||||
SplitContainer *clickedPage = notebook->tabAt(relPoint, index);
|
||||
SplitContainer *clickedPage = notebook->tabAt(relPoint, index, this->width());
|
||||
|
||||
if (clickedPage != nullptr && clickedPage != this->page) {
|
||||
this->notebook->rearrangePage(clickedPage, index);
|
||||
this->notebook->rearrangePage(this->page, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ class NotebookTab : public BaseWidget
|
|||
public:
|
||||
explicit NotebookTab(Notebook *_notebook, const std::string &_uuid);
|
||||
|
||||
void calcSize();
|
||||
void updateSize();
|
||||
|
||||
SplitContainer *page;
|
||||
|
||||
|
@ -42,16 +42,18 @@ public:
|
|||
void hideTabXChanged(bool);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *) override;
|
||||
virtual void themeRefreshEvent() override;
|
||||
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void enterEvent(QEvent *) override;
|
||||
void leaveEvent(QEvent *) override;
|
||||
virtual void paintEvent(QPaintEvent *) override;
|
||||
|
||||
void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
virtual void enterEvent(QEvent *) override;
|
||||
virtual void leaveEvent(QEvent *) override;
|
||||
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
virtual void dragEnterEvent(QDragEnterEvent *event) override;
|
||||
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
std::vector<pajlada::Signals::ScopedConnection> managedConnections;
|
||||
|
@ -80,7 +82,7 @@ private:
|
|||
|
||||
QRect getXRect()
|
||||
{
|
||||
float scale = this->getDpiMultiplier();
|
||||
float scale = this->getScale();
|
||||
return QRect(this->width() - static_cast<int>(20 * scale), static_cast<int>(4 * scale),
|
||||
static_cast<int>(16 * scale), static_cast<int>(16 * scale));
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ void RippleEffectButton::paintEvent(QPaintEvent *)
|
|||
|
||||
if (this->pixmap != nullptr) {
|
||||
QRect rect = this->rect();
|
||||
int xD = 6 * this->getDpiMultiplier();
|
||||
int xD = 6 * this->getScale();
|
||||
|
||||
rect.moveLeft(xD);
|
||||
rect.setRight(rect.right() - xD - xD);
|
||||
|
@ -57,6 +57,8 @@ void RippleEffectButton::paintEvent(QPaintEvent *)
|
|||
|
||||
void RippleEffectButton::fancyPaint(QPainter &painter)
|
||||
{
|
||||
painter.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
QColor c;
|
||||
|
||||
if (this->mouseEffectColor) {
|
||||
|
|
|
@ -58,7 +58,7 @@ void SearchPopup::initLayout()
|
|||
}
|
||||
}
|
||||
|
||||
void SearchPopup::setChannel(SharedChannel channel)
|
||||
void SearchPopup::setChannel(ChannelPtr channel)
|
||||
{
|
||||
this->snapshot = channel->getMessageSnapshot();
|
||||
this->performSearch();
|
||||
|
@ -70,7 +70,7 @@ void SearchPopup::performSearch()
|
|||
{
|
||||
QString text = searchInput->text();
|
||||
|
||||
SharedChannel channel(new Channel("search"));
|
||||
ChannelPtr channel(new Channel("search"));
|
||||
|
||||
for (size_t i = 0; i < this->snapshot.getLength(); i++) {
|
||||
messages::MessagePtr message = this->snapshot[i];
|
||||
|
|
|
@ -10,7 +10,8 @@ namespace widgets {
|
|||
|
||||
SettingsDialogTab::SettingsDialogTab(SettingsDialog *_dialog, settingspages::SettingsPage *_page,
|
||||
QString imageFileName)
|
||||
: dialog(_dialog)
|
||||
: BaseWidget(_dialog)
|
||||
, dialog(_dialog)
|
||||
, page(_page)
|
||||
{
|
||||
this->ui.labelText = page->getName();
|
||||
|
@ -47,8 +48,8 @@ void SettingsDialogTab::paintEvent(QPaintEvent *)
|
|||
|
||||
this->style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
|
||||
|
||||
int a = (this->height() - 20) / 2;
|
||||
QPixmap pixmap = this->ui.icon.pixmap(QSize(20, 20));
|
||||
int a = (this->height() - (20 * this->getScale())) / 2;
|
||||
QPixmap pixmap = this->ui.icon.pixmap(QSize(this->height() - a * 2, this->height() - a * 2));
|
||||
|
||||
painter.drawPixmap(a, a, pixmap);
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <QPaintEvent>
|
||||
#include <QWidget>
|
||||
|
||||
#include "widgets/basewidget.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
namespace settingspages {
|
||||
|
@ -12,7 +14,7 @@ class SettingsPage;
|
|||
|
||||
class SettingsDialog;
|
||||
|
||||
class SettingsDialogTab : public QWidget
|
||||
class SettingsDialogTab : public BaseWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "twitch/twitchchannel.hpp"
|
||||
#include "util/layoutcreator.hpp"
|
||||
#include "util/urlfetch.hpp"
|
||||
#include "widgets/helper/label.hpp"
|
||||
#include "widgets/split.hpp"
|
||||
#include "widgets/splitcontainer.hpp"
|
||||
#include "widgets/tooltipwidget.hpp"
|
||||
|
@ -47,7 +48,9 @@ SplitHeader::SplitHeader(Split *_split)
|
|||
layout->addStretch(1);
|
||||
|
||||
// channel name label
|
||||
// auto title = layout.emplace<Label>(this).assign(&this->titleLabel);
|
||||
auto title = layout.emplace<SignalLabel>().assign(&this->titleLabel);
|
||||
title->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
|
||||
title->setMouseTracking(true);
|
||||
QObject::connect(this->titleLabel, &SignalLabel::mouseDoubleClick, this,
|
||||
&SplitHeader::mouseDoubleClickEvent);
|
||||
|
@ -66,7 +69,8 @@ SplitHeader::SplitHeader(Split *_split)
|
|||
|
||||
// ---- misc
|
||||
this->layout()->setMargin(0);
|
||||
this->refreshTheme();
|
||||
this->themeRefreshEvent();
|
||||
this->scaleChangedEvent(this->getScale());
|
||||
|
||||
this->updateChannelText();
|
||||
|
||||
|
@ -93,7 +97,7 @@ void SplitHeader::addDropdownItems(RippleEffectButton *label)
|
|||
this->dropdownMenu.addSeparator();
|
||||
#ifdef USEWEBENGINE
|
||||
this->dropdownMenu.addAction("Start watching", this, [this]{
|
||||
SharedChannel _channel = this->split->getChannel();
|
||||
ChannelPtr _channel = this->split->getChannel();
|
||||
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(_channel.get());
|
||||
|
||||
if (tc != nullptr) {
|
||||
|
@ -133,13 +137,15 @@ void SplitHeader::initializeChannelSignals()
|
|||
}
|
||||
}
|
||||
|
||||
void SplitHeader::resizeEvent(QResizeEvent *event)
|
||||
void SplitHeader::scaleChangedEvent(float scale)
|
||||
{
|
||||
int w = 28 * getDpiMultiplier();
|
||||
int w = 28 * scale;
|
||||
|
||||
this->setFixedHeight(w);
|
||||
this->dropdownButton->setFixedWidth(w);
|
||||
this->moderationButton->setFixedWidth(w);
|
||||
// this->titleLabel->setFont(
|
||||
// singletons::FontManager::getInstance().getFont(FontStyle::Medium, scale));
|
||||
}
|
||||
|
||||
void SplitHeader::updateChannelText()
|
||||
|
@ -180,7 +186,7 @@ void SplitHeader::updateModerationModeIcon()
|
|||
: resourceManager.moderationmode_disabled->getPixmap());
|
||||
|
||||
bool modButtonVisible = false;
|
||||
SharedChannel channel = this->split->getChannel();
|
||||
ChannelPtr channel = this->split->getChannel();
|
||||
|
||||
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(channel.get());
|
||||
|
||||
|
@ -242,7 +248,7 @@ void SplitHeader::rightButtonClicked()
|
|||
{
|
||||
}
|
||||
|
||||
void SplitHeader::refreshTheme()
|
||||
void SplitHeader::themeRefreshEvent()
|
||||
{
|
||||
QPalette palette;
|
||||
palette.setColor(QPalette::Foreground, this->themeManager.splits.header.text);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/basewidget.hpp"
|
||||
#include "widgets/helper/label.hpp"
|
||||
#include "widgets/helper/rippleeffectlabel.hpp"
|
||||
#include "widgets/helper/signallabel.hpp"
|
||||
|
||||
|
@ -34,12 +35,14 @@ public:
|
|||
void updateModerationModeIcon();
|
||||
|
||||
protected:
|
||||
virtual void scaleChangedEvent(float) override;
|
||||
virtual void themeRefreshEvent() override;
|
||||
|
||||
virtual void paintEvent(QPaintEvent *) override;
|
||||
virtual void mousePressEvent(QMouseEvent *event) override;
|
||||
virtual void mouseMoveEvent(QMouseEvent *event) override;
|
||||
virtual void leaveEvent(QEvent *event) override;
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||
virtual void resizeEvent(QResizeEvent *event) override;
|
||||
|
||||
private:
|
||||
Split *const split;
|
||||
|
@ -50,6 +53,7 @@ private:
|
|||
boost::signals2::connection onlineStatusChangedConnection;
|
||||
|
||||
RippleEffectButton *dropdownButton;
|
||||
// Label *titleLabel;
|
||||
SignalLabel *titleLabel;
|
||||
RippleEffectButton *moderationButton;
|
||||
|
||||
|
@ -57,8 +61,6 @@ private:
|
|||
|
||||
void rightButtonClicked();
|
||||
|
||||
virtual void refreshTheme() override;
|
||||
|
||||
void initializeChannelSignals();
|
||||
|
||||
QString tooltip;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "singletons/ircmanager.hpp"
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
#include "singletons/thememanager.hpp"
|
||||
#include "util/layoutcreator.hpp"
|
||||
#include "widgets/notebook.hpp"
|
||||
#include "widgets/split.hpp"
|
||||
#include "widgets/splitcontainer.hpp"
|
||||
|
@ -17,68 +18,124 @@ namespace widgets {
|
|||
SplitInput::SplitInput(Split *_chatWidget)
|
||||
: BaseWidget(_chatWidget)
|
||||
, chatWidget(_chatWidget)
|
||||
, emotesLabel(this)
|
||||
{
|
||||
this->setLayout(&this->hbox);
|
||||
this->initLayout();
|
||||
|
||||
this->hbox.setMargin(4);
|
||||
// auto completion
|
||||
auto completer = new QCompleter(
|
||||
singletons::CompletionManager::getInstance().createModel(this->chatWidget->channelName));
|
||||
|
||||
this->hbox.addLayout(&this->editContainer);
|
||||
this->hbox.addLayout(&this->vbox);
|
||||
this->ui.textEdit->setCompleter(completer);
|
||||
|
||||
// misc
|
||||
this->installKeyPressedEvent();
|
||||
this->themeRefreshEvent();
|
||||
this->scaleChangedEvent(this->getScale());
|
||||
}
|
||||
|
||||
void SplitInput::initLayout()
|
||||
{
|
||||
auto &fontManager = singletons::FontManager::getInstance();
|
||||
util::LayoutCreator<SplitInput> layoutCreator(this);
|
||||
|
||||
auto layout = layoutCreator.setLayoutType<QHBoxLayout>().withoutMargin().assign(&this->ui.hbox);
|
||||
|
||||
// input
|
||||
auto textEdit = layout.emplace<ResizingTextEdit>().assign(&this->ui.textEdit);
|
||||
connect(textEdit.getElement(), &ResizingTextEdit::textChanged, this,
|
||||
&SplitInput::editTextChanged);
|
||||
|
||||
// right box
|
||||
auto box = layout.emplace<QVBoxLayout>().withoutMargin();
|
||||
box->setSpacing(0);
|
||||
{
|
||||
auto textEditLength = box.emplace<QLabel>().assign(&this->ui.textEditLength);
|
||||
textEditLength->setAlignment(Qt::AlignRight);
|
||||
|
||||
box->addStretch(1);
|
||||
box.emplace<RippleEffectLabel>().assign(&this->ui.emoteButton);
|
||||
}
|
||||
|
||||
this->ui.emoteButton->getLabel().setTextFormat(Qt::RichText);
|
||||
|
||||
// ---- misc
|
||||
|
||||
// set edit font
|
||||
this->ui.textEdit->setFont(
|
||||
fontManager.getFont(singletons::FontManager::Type::Medium, this->getScale()));
|
||||
|
||||
this->textInput.setFont(
|
||||
fontManager.getFont(singletons::FontManager::Type::Medium, this->getDpiMultiplier()));
|
||||
this->managedConnections.emplace_back(fontManager.fontChanged.connect([this, &fontManager]() {
|
||||
this->textInput.setFont(
|
||||
fontManager.getFont(singletons::FontManager::Type::Medium, this->getDpiMultiplier()));
|
||||
this->ui.textEdit->setFont(
|
||||
fontManager.getFont(singletons::FontManager::Type::Medium, this->getScale()));
|
||||
}));
|
||||
|
||||
this->editContainer.addWidget(&this->textInput);
|
||||
this->editContainer.setMargin(2);
|
||||
|
||||
this->emotesLabel.setMinimumHeight(24);
|
||||
|
||||
this->vbox.addWidget(&this->textLengthLabel);
|
||||
this->vbox.addStretch(1);
|
||||
this->vbox.addWidget(&this->emotesLabel);
|
||||
|
||||
this->textLengthLabel.setText("");
|
||||
this->textLengthLabel.setAlignment(Qt::AlignRight);
|
||||
|
||||
this->emotesLabel.getLabel().setTextFormat(Qt::RichText);
|
||||
this->emotesLabel.getLabel().setText("<img src=':/images/emote.svg' width='12' height='12' "
|
||||
"/>");
|
||||
|
||||
connect(&this->emotesLabel, &RippleEffectLabel::clicked, [this] {
|
||||
if (this->emotePopup == nullptr) {
|
||||
this->emotePopup = new EmotePopup(this->themeManager);
|
||||
// open emote popup
|
||||
QObject::connect(this->ui.emoteButton, &RippleEffectLabel::clicked, [this] {
|
||||
if (!this->emotePopup) {
|
||||
this->emotePopup = std::make_unique<EmotePopup>(this->themeManager);
|
||||
this->emotePopup->linkClicked.connect([this](const messages::Link &link) {
|
||||
if (link.getType() == messages::Link::InsertText) {
|
||||
this->insertText(link.getValue() + " ");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this->emotePopup->resize((int)(300 * this->emotePopup->getDpiMultiplier()),
|
||||
(int)(500 * this->emotePopup->getDpiMultiplier()));
|
||||
this->emotePopup->resize((int)(300 * this->emotePopup->getScale()),
|
||||
(int)(500 * this->emotePopup->getScale()));
|
||||
this->emotePopup->loadChannel(this->chatWidget->getChannel());
|
||||
this->emotePopup->show();
|
||||
});
|
||||
|
||||
connect(&textInput, &ResizingTextEdit::textChanged, this, &SplitInput::editTextChanged);
|
||||
// clear channelview selection when selecting in the input
|
||||
QObject::connect(this->ui.textEdit, &QTextEdit::copyAvailable, [this](bool available) {
|
||||
if (available) {
|
||||
this->chatWidget->view.clearSelection();
|
||||
}
|
||||
});
|
||||
|
||||
this->refreshTheme();
|
||||
textLengthLabel.setHidden(!singletons::SettingManager::getInstance().showMessageLength);
|
||||
// textEditLength visibility
|
||||
singletons::SettingManager::getInstance().showMessageLength.connect(
|
||||
[this](const bool &value, auto) { this->ui.textEditLength->setHidden(!value); },
|
||||
this->managedConnections);
|
||||
}
|
||||
|
||||
auto completer = new QCompleter(
|
||||
singletons::CompletionManager::getInstance().createModel(this->chatWidget->channelName));
|
||||
void SplitInput::scaleChangedEvent(float scale)
|
||||
{
|
||||
// update the icon size of the emote button
|
||||
QString text = "<img src=':/images/emote.svg' width='xD' height='xD' />";
|
||||
text.replace("xD", QString::number((int)12 * scale));
|
||||
|
||||
this->textInput.setCompleter(completer);
|
||||
this->ui.emoteButton->getLabel().setText(text);
|
||||
this->ui.emoteButton->setFixedHeight((int)18 * scale);
|
||||
|
||||
this->textInput.keyPressed.connect([this](QKeyEvent *event) {
|
||||
// set maximum height
|
||||
this->setMaximumHeight((int)(150 * this->getScale()));
|
||||
|
||||
this->themeRefreshEvent();
|
||||
}
|
||||
|
||||
void SplitInput::themeRefreshEvent()
|
||||
{
|
||||
QPalette palette;
|
||||
|
||||
palette.setColor(QPalette::Foreground, this->themeManager.splits.input.text);
|
||||
|
||||
this->ui.textEditLength->setPalette(palette);
|
||||
|
||||
this->ui.textEdit->setStyleSheet(this->themeManager.splits.input.styleSheet);
|
||||
|
||||
this->ui.hbox->setMargin((this->themeManager.isLightTheme() ? 4 : 2) * this->getScale());
|
||||
}
|
||||
|
||||
void SplitInput::installKeyPressedEvent()
|
||||
{
|
||||
this->ui.textEdit->keyPressed.connect([this](QKeyEvent *event) {
|
||||
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
|
||||
auto c = this->chatWidget->getChannel();
|
||||
if (c == nullptr) {
|
||||
return;
|
||||
}
|
||||
QString message = textInput.toPlainText();
|
||||
QString message = ui.textEdit->toPlainText();
|
||||
|
||||
QString sendMessage =
|
||||
singletons::CommandManager::getInstance().execCommand(message, c, false);
|
||||
|
@ -89,9 +146,9 @@ SplitInput::SplitInput(Split *_chatWidget)
|
|||
|
||||
event->accept();
|
||||
if (!(event->modifiers() == Qt::ControlModifier)) {
|
||||
this->textInput.setText(QString());
|
||||
this->ui.textEdit->setText(QString());
|
||||
this->prevIndex = 0;
|
||||
} else if (this->textInput.toPlainText() ==
|
||||
} else if (this->ui.textEdit->toPlainText() ==
|
||||
this->prevMsg.at(this->prevMsg.size() - 1)) {
|
||||
this->prevMsg.removeLast();
|
||||
}
|
||||
|
@ -110,7 +167,7 @@ SplitInput::SplitInput(Split *_chatWidget)
|
|||
} else {
|
||||
if (this->prevMsg.size() && this->prevIndex) {
|
||||
this->prevIndex--;
|
||||
this->textInput.setText(this->prevMsg.at(this->prevIndex));
|
||||
this->ui.textEdit->setText(this->prevMsg.at(this->prevIndex));
|
||||
}
|
||||
}
|
||||
} else if (event->key() == Qt::Key_Down) {
|
||||
|
@ -128,10 +185,10 @@ SplitInput::SplitInput(Split *_chatWidget)
|
|||
if (this->prevIndex != (this->prevMsg.size() - 1) &&
|
||||
this->prevIndex != this->prevMsg.size()) {
|
||||
this->prevIndex++;
|
||||
this->textInput.setText(this->prevMsg.at(this->prevIndex));
|
||||
this->ui.textEdit->setText(this->prevMsg.at(this->prevIndex));
|
||||
} else {
|
||||
this->prevIndex = this->prevMsg.size();
|
||||
this->textInput.setText(QString());
|
||||
this->ui.textEdit->setText(QString());
|
||||
}
|
||||
}
|
||||
} else if (event->key() == Qt::Key_Left) {
|
||||
|
@ -183,49 +240,32 @@ SplitInput::SplitInput(Split *_chatWidget)
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
singletons::SettingManager::getInstance().showMessageLength.connect(
|
||||
[this](const bool &value, auto) { this->textLengthLabel.setHidden(!value); },
|
||||
this->managedConnections);
|
||||
|
||||
QObject::connect(&this->textInput, &QTextEdit::copyAvailable, [this](bool available) {
|
||||
if (available) {
|
||||
this->chatWidget->view.clearSelection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SplitInput::clearSelection()
|
||||
{
|
||||
QTextCursor c = this->textInput.textCursor();
|
||||
QTextCursor c = this->ui.textEdit->textCursor();
|
||||
|
||||
c.setPosition(c.position());
|
||||
c.setPosition(c.position(), QTextCursor::KeepAnchor);
|
||||
|
||||
this->textInput.setTextCursor(c);
|
||||
this->ui.textEdit->setTextCursor(c);
|
||||
}
|
||||
|
||||
QString SplitInput::getInputText() const
|
||||
{
|
||||
return this->textInput.toPlainText();
|
||||
return this->ui.textEdit->toPlainText();
|
||||
}
|
||||
|
||||
void SplitInput::refreshTheme()
|
||||
void SplitInput::insertText(const QString &text)
|
||||
{
|
||||
QPalette palette;
|
||||
|
||||
palette.setColor(QPalette::Foreground, this->themeManager.splits.input.text);
|
||||
|
||||
this->textLengthLabel.setPalette(palette);
|
||||
|
||||
this->textInput.setStyleSheet(this->themeManager.splits.input.styleSheet);
|
||||
|
||||
this->hbox.setMargin((this->themeManager.isLightTheme() ? 4 : 2) * this->getDpiMultiplier());
|
||||
this->ui.textEdit->insertPlainText(text);
|
||||
}
|
||||
|
||||
void SplitInput::editTextChanged()
|
||||
{
|
||||
QString text = this->textInput.toPlainText();
|
||||
// set textLengthLabel value
|
||||
QString text = this->ui.textEdit->toPlainText();
|
||||
|
||||
this->textChanged.invoke(text);
|
||||
|
||||
|
@ -244,7 +284,7 @@ void SplitInput::editTextChanged()
|
|||
labelText = QString::number(text.length());
|
||||
}
|
||||
|
||||
this->textLengthLabel.setText(labelText);
|
||||
this->ui.textEditLength->setText(labelText);
|
||||
}
|
||||
|
||||
void SplitInput::paintEvent(QPaintEvent *)
|
||||
|
@ -255,7 +295,7 @@ void SplitInput::paintEvent(QPaintEvent *)
|
|||
|
||||
QPen pen(this->themeManager.splits.input.border);
|
||||
if (this->themeManager.isLightTheme()) {
|
||||
pen.setWidth((int)(6 * this->getDpiMultiplier()));
|
||||
pen.setWidth((int)(6 * this->getScale()));
|
||||
}
|
||||
painter.setPen(pen);
|
||||
painter.drawRect(0, 0, this->width() - 1, this->height() - 1);
|
||||
|
@ -264,14 +304,10 @@ void SplitInput::paintEvent(QPaintEvent *)
|
|||
void SplitInput::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
if (this->height() == this->maximumHeight()) {
|
||||
this->textInput.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
this->ui.textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
||||
} else {
|
||||
this->textInput.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
this->ui.textEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
}
|
||||
|
||||
this->setMaximumHeight((int)(150 * this->getDpiMultiplier()));
|
||||
|
||||
this->refreshTheme();
|
||||
}
|
||||
|
||||
void SplitInput::mousePressEvent(QMouseEvent *)
|
||||
|
|
|
@ -27,10 +27,13 @@ public:
|
|||
|
||||
void clearSelection();
|
||||
QString getInputText() const;
|
||||
void insertText(const QString &text);
|
||||
|
||||
pajlada::Signals::Signal<const QString &> textChanged;
|
||||
|
||||
protected:
|
||||
virtual void scaleChangedEvent(float scale) override;
|
||||
|
||||
virtual void paintEvent(QPaintEvent *) override;
|
||||
virtual void resizeEvent(QResizeEvent *) override;
|
||||
|
||||
|
@ -38,18 +41,29 @@ protected:
|
|||
|
||||
private:
|
||||
Split *const chatWidget;
|
||||
EmotePopup *emotePopup = nullptr;
|
||||
std::unique_ptr<EmotePopup> emotePopup;
|
||||
|
||||
struct {
|
||||
ResizingTextEdit *textEdit;
|
||||
QLabel *textEditLength;
|
||||
RippleEffectLabel *emoteButton;
|
||||
|
||||
QHBoxLayout *hbox;
|
||||
} ui;
|
||||
|
||||
std::vector<pajlada::Signals::ScopedConnection> managedConnections;
|
||||
QHBoxLayout hbox;
|
||||
QVBoxLayout vbox;
|
||||
QHBoxLayout editContainer;
|
||||
ResizingTextEdit textInput;
|
||||
QLabel textLengthLabel;
|
||||
RippleEffectLabel emotesLabel;
|
||||
// QHBoxLayout hbox;
|
||||
// QVBoxLayout vbox;
|
||||
// QHBoxLayout editContainer;
|
||||
// ResizingTextEdit textInput;
|
||||
// QLabel textLengthLabel;
|
||||
// RippleEffectLabel emotesLabel;
|
||||
QStringList prevMsg;
|
||||
int prevIndex = 0;
|
||||
virtual void refreshTheme() override;
|
||||
|
||||
void initLayout();
|
||||
void installKeyPressedEvent();
|
||||
virtual void themeRefreshEvent() override;
|
||||
|
||||
private slots:
|
||||
void editTextChanged();
|
||||
|
|
118
src/widgets/helper/titlebarbutton.cpp
Normal file
118
src/widgets/helper/titlebarbutton.cpp
Normal file
|
@ -0,0 +1,118 @@
|
|||
#include "titlebarbutton.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
TitleBarButton::TitleBarButton()
|
||||
: RippleEffectButton(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
TitleBarButton::Style TitleBarButton::getButtonStyle() const
|
||||
{
|
||||
return this->style;
|
||||
}
|
||||
|
||||
void TitleBarButton::setButtonStyle(Style _style)
|
||||
{
|
||||
this->style = _style;
|
||||
this->update();
|
||||
}
|
||||
|
||||
void TitleBarButton::paintEvent(QPaintEvent *)
|
||||
{
|
||||
QPainter painter(this);
|
||||
|
||||
QColor color = "#000";
|
||||
QColor background = "#fff";
|
||||
|
||||
int xD = this->height() / 3;
|
||||
int centerX = this->width() / 2;
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing, false);
|
||||
|
||||
switch (this->style) {
|
||||
case Minimize: {
|
||||
painter.fillRect(centerX - xD / 2, xD * 3 / 2, xD, 1, color);
|
||||
break;
|
||||
}
|
||||
case Maximize: {
|
||||
painter.setPen(color);
|
||||
painter.drawRect(centerX - xD / 2, xD, xD - 1, xD - 1);
|
||||
break;
|
||||
}
|
||||
case Unmaximize: {
|
||||
int xD2 = xD * 1 / 5;
|
||||
int xD3 = xD * 4 / 5;
|
||||
|
||||
painter.drawRect(centerX - xD / 2 + xD2, xD, xD3, xD3);
|
||||
painter.fillRect(centerX - xD / 2, xD + xD2, xD3, xD3, QColor("#fff"));
|
||||
painter.drawRect(centerX - xD / 2, xD + xD2, xD3, xD3);
|
||||
break;
|
||||
}
|
||||
case Close: {
|
||||
QRect rect(centerX - xD / 2, xD, xD - 1, xD - 1);
|
||||
painter.setPen(QPen(color, 1));
|
||||
|
||||
painter.drawLine(rect.topLeft(), rect.bottomRight());
|
||||
painter.drawLine(rect.topRight(), rect.bottomLeft());
|
||||
break;
|
||||
}
|
||||
case User: {
|
||||
color = QColor("#333");
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
|
||||
auto a = xD / 3;
|
||||
QPainterPath path;
|
||||
|
||||
painter.save();
|
||||
painter.translate(3, 3);
|
||||
|
||||
path.arcMoveTo(a, 4 * a, 6 * a, 6 * a, 0);
|
||||
path.arcTo(a, 4 * a, 6 * a, 6 * a, 0, 180);
|
||||
|
||||
painter.fillPath(path, color);
|
||||
|
||||
painter.setBrush(background);
|
||||
painter.drawEllipse(2 * a, 1 * a, 4 * a, 4 * a);
|
||||
|
||||
painter.setBrush(color);
|
||||
painter.drawEllipse(2.5 * a, 1.5 * a, 3 * a + 1, 3 * a);
|
||||
painter.restore();
|
||||
|
||||
break;
|
||||
}
|
||||
case Settings: {
|
||||
color = QColor("#333");
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
painter.setRenderHint(QPainter::HighQualityAntialiasing);
|
||||
|
||||
painter.save();
|
||||
painter.translate(3, 3);
|
||||
|
||||
auto a = xD / 3;
|
||||
QPainterPath path;
|
||||
|
||||
path.arcMoveTo(a, a, 6 * a, 6 * a, 0 - (360 / 32.0));
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
path.arcTo(a, a, 6 * a, 6 * a, i * (360 / 8.0) - (360 / 32.0), (360 / 32.0));
|
||||
path.arcTo(2 * a, 2 * a, 4 * a, 4 * a, i * (360 / 8.0) + (360 / 32.0),
|
||||
(360 / 32.0));
|
||||
}
|
||||
|
||||
painter.strokePath(path, color);
|
||||
painter.fillPath(path, color);
|
||||
|
||||
painter.setBrush(background);
|
||||
painter.drawEllipse(3 * a, 3 * a, 2 * a, 2 * a);
|
||||
painter.restore();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this->fancyPaint(painter);
|
||||
}
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
24
src/widgets/helper/titlebarbutton.hpp
Normal file
24
src/widgets/helper/titlebarbutton.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/helper/rippleeffectbutton.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
class TitleBarButton : public RippleEffectButton
|
||||
{
|
||||
public:
|
||||
enum Style { Minimize = 1, Maximize = 2, Unmaximize = 4, Close = 8, User = 16, Settings = 32 };
|
||||
|
||||
TitleBarButton();
|
||||
|
||||
Style getButtonStyle() const;
|
||||
void setButtonStyle(Style style);
|
||||
|
||||
protected:
|
||||
virtual void paintEvent(QPaintEvent *) override;
|
||||
|
||||
private:
|
||||
Style style;
|
||||
};
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
|
@ -1,7 +1,7 @@
|
|||
#include "widgets/notebook.hpp"
|
||||
#include "debug/log.hpp"
|
||||
#include "singletons/thememanager.hpp"
|
||||
#include "widgets/accountswitchpopupwidget.hpp"
|
||||
#include "singletons/windowmanager.hpp"
|
||||
#include "widgets/helper/notebookbutton.hpp"
|
||||
#include "widgets/helper/notebooktab.hpp"
|
||||
#include "widgets/settingsdialog.hpp"
|
||||
|
@ -53,6 +53,8 @@ Notebook::Notebook(Window *parent, bool _showButtons, const std::string &setting
|
|||
closeConfirmDialog.setIcon(QMessageBox::Icon::Question);
|
||||
closeConfirmDialog.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
|
||||
closeConfirmDialog.setDefaultButton(QMessageBox::Yes);
|
||||
|
||||
this->scaleChangedEvent(this->getScale());
|
||||
}
|
||||
|
||||
SplitContainer *Notebook::addNewPage()
|
||||
|
@ -130,6 +132,9 @@ void Notebook::select(SplitContainer *page)
|
|||
if (this->selectedPage != nullptr) {
|
||||
this->selectedPage->setHidden(true);
|
||||
this->selectedPage->getTab()->setSelected(false);
|
||||
for (auto split : this->selectedPage->getSplits()) {
|
||||
split->updateLastReadMessage();
|
||||
}
|
||||
}
|
||||
|
||||
this->selectedPage = page;
|
||||
|
@ -151,12 +156,15 @@ int Notebook::tabCount()
|
|||
return this->pages.size();
|
||||
}
|
||||
|
||||
SplitContainer *Notebook::tabAt(QPoint point, int &index)
|
||||
SplitContainer *Notebook::tabAt(QPoint point, int &index, int maxWidth)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (auto *page : this->pages) {
|
||||
if (page->getTab()->getDesiredRect().contains(point)) {
|
||||
QRect rect = page->getTab()->getDesiredRect();
|
||||
rect.setWidth(std::min(maxWidth, rect.width()));
|
||||
|
||||
if (rect.contains(point)) {
|
||||
index = i;
|
||||
return page;
|
||||
}
|
||||
|
@ -206,7 +214,7 @@ void Notebook::performLayout(bool animated)
|
|||
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
|
||||
|
||||
int x = 0, y = 0;
|
||||
float scale = this->getDpiMultiplier();
|
||||
float scale = this->getScale();
|
||||
bool customFrame = this->parentWindow->hasCustomWindowFrame();
|
||||
|
||||
if (!this->showButtons || settings.hidePreferencesButton || customFrame) {
|
||||
|
@ -250,51 +258,37 @@ void Notebook::performLayout(bool animated)
|
|||
if (this->selectedPage != nullptr) {
|
||||
this->selectedPage->move(0, y + tabHeight);
|
||||
this->selectedPage->resize(width(), height() - y - tabHeight);
|
||||
this->selectedPage->raise();
|
||||
}
|
||||
}
|
||||
|
||||
void Notebook::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
float scale = this->getDpiMultiplier();
|
||||
this->performLayout(false);
|
||||
}
|
||||
|
||||
this->settingsButton.resize(static_cast<int>(24 * scale), static_cast<int>(24 * scale));
|
||||
this->userButton.resize(static_cast<int>(24 * scale), static_cast<int>(24 * scale));
|
||||
this->addButton.resize(static_cast<int>(24 * scale), static_cast<int>(24 * scale));
|
||||
void Notebook::scaleChangedEvent(float)
|
||||
{
|
||||
float h = 24 * this->getScale();
|
||||
|
||||
this->settingsButton.setFixedSize(h, h);
|
||||
this->userButton.setFixedSize(h, h);
|
||||
this->addButton.setFixedSize(h, h);
|
||||
|
||||
for (auto &i : this->pages) {
|
||||
i->getTab()->calcSize();
|
||||
i->getTab()->updateSize();
|
||||
}
|
||||
|
||||
this->performLayout(false);
|
||||
}
|
||||
|
||||
void Notebook::settingsButtonClicked()
|
||||
{
|
||||
QTimer::singleShot(80, [this] { SettingsDialog::showDialog(); });
|
||||
singletons::WindowManager::getInstance().showSettingsDialog();
|
||||
}
|
||||
|
||||
void Notebook::usersButtonClicked()
|
||||
{
|
||||
static QWidget *lastFocusedWidget = nullptr;
|
||||
static AccountSwitchPopupWidget *w = new AccountSwitchPopupWidget(this);
|
||||
|
||||
if (w->hasFocus()) {
|
||||
w->hide();
|
||||
if (lastFocusedWidget) {
|
||||
lastFocusedWidget->setFocus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
lastFocusedWidget = this->focusWidget();
|
||||
|
||||
w->refresh();
|
||||
|
||||
QPoint buttonPos = this->userButton.rect().bottomRight();
|
||||
w->move(buttonPos.x(), buttonPos.y());
|
||||
|
||||
w->show();
|
||||
w->setFocus();
|
||||
singletons::WindowManager::getInstance().showAccountSelectPopup(
|
||||
this->mapToGlobal(this->userButton.rect().bottomRight()));
|
||||
}
|
||||
|
||||
void Notebook::addPageButtonClicked()
|
||||
|
|
|
@ -41,13 +41,14 @@ public:
|
|||
void performLayout(bool animate = true);
|
||||
|
||||
int tabCount();
|
||||
SplitContainer *tabAt(QPoint point, int &index);
|
||||
SplitContainer *tabAt(QPoint point, int &index, int maxWidth = 2000000000);
|
||||
void rearrangePage(SplitContainer *page, int index);
|
||||
|
||||
void nextTab();
|
||||
void previousTab();
|
||||
|
||||
protected:
|
||||
void scaleChangedEvent(float scale);
|
||||
void resizeEvent(QResizeEvent *);
|
||||
|
||||
void settingsButtonMouseReleased(QMouseEvent *event);
|
||||
|
|
|
@ -18,7 +18,7 @@ Scrollbar::Scrollbar(ChannelView *parent)
|
|||
, currentValueAnimation(this, "currentValue")
|
||||
, smoothScrollingSetting(singletons::SettingManager::getInstance().enableSmoothScrolling)
|
||||
{
|
||||
resize((int)(16 * this->getDpiMultiplier()), 100);
|
||||
resize((int)(16 * this->getScale()), 100);
|
||||
this->currentValueAnimation.setDuration(150);
|
||||
this->currentValueAnimation.setEasingCurve(QEasingCurve(QEasingCurve::OutCubic));
|
||||
|
||||
|
@ -29,7 +29,7 @@ Scrollbar::Scrollbar(ChannelView *parent)
|
|||
timer->setSingleShot(true);
|
||||
|
||||
connect(timer, &QTimer::timeout, [=]() {
|
||||
resize((int)(16 * this->getDpiMultiplier()), 100);
|
||||
resize((int)(16 * this->getScale()), 100);
|
||||
timer->deleteLater();
|
||||
});
|
||||
|
||||
|
@ -194,7 +194,7 @@ void Scrollbar::printCurrentState(const QString &prefix) const
|
|||
void Scrollbar::paintEvent(QPaintEvent *)
|
||||
{
|
||||
bool mouseOver = this->mouseOverIndex != -1;
|
||||
int xOffset = mouseOver ? 0 : width() - (int)(4 * this->getDpiMultiplier());
|
||||
int xOffset = mouseOver ? 0 : width() - (int)(4 * this->getScale());
|
||||
|
||||
QPainter painter(this);
|
||||
// painter.fillRect(rect(), this->themeManager.ScrollbarBG);
|
||||
|
@ -248,7 +248,7 @@ void Scrollbar::paintEvent(QPaintEvent *)
|
|||
|
||||
void Scrollbar::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
this->resize((int)(16 * this->getDpiMultiplier()), this->height());
|
||||
this->resize((int)(16 * this->getScale()), this->height());
|
||||
}
|
||||
|
||||
void Scrollbar::mouseMoveEvent(QMouseEvent *event)
|
||||
|
|
|
@ -8,8 +8,12 @@
|
|||
#include "widgets/settingspages/commandpage.hpp"
|
||||
#include "widgets/settingspages/emotespage.hpp"
|
||||
#include "widgets/settingspages/highlightingpage.hpp"
|
||||
#include "widgets/settingspages/ignoremessagespage.hpp"
|
||||
#include "widgets/settingspages/ignoreuserspage.hpp"
|
||||
#include "widgets/settingspages/keyboardsettingspage.hpp"
|
||||
#include "widgets/settingspages/logspage.hpp"
|
||||
#include "widgets/settingspages/moderationpage.hpp"
|
||||
#include "widgets/settingspages/specialchannelspage.hpp"
|
||||
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
|
@ -25,7 +29,7 @@ SettingsDialog::SettingsDialog()
|
|||
|
||||
this->addTabs();
|
||||
|
||||
this->dpiMultiplierChanged(this->getDpiMultiplier(), this->getDpiMultiplier());
|
||||
this->scaleChangedEvent(this->getScale());
|
||||
}
|
||||
|
||||
void SettingsDialog::initUi()
|
||||
|
@ -72,14 +76,24 @@ SettingsDialog *SettingsDialog::getHandle()
|
|||
|
||||
void SettingsDialog::addTabs()
|
||||
{
|
||||
this->ui.tabContainer->setSpacing(0);
|
||||
|
||||
this->addTab(new settingspages::AccountsPage);
|
||||
this->addTab(new settingspages::AppearancePage);
|
||||
this->addTab(new settingspages::BehaviourPage);
|
||||
this->addTab(new settingspages::CommandPage);
|
||||
this->addTab(new settingspages::EmotesPage);
|
||||
this->addTab(new settingspages::HighlightingPage);
|
||||
// this->addTab(new settingspages::LogsPage);
|
||||
|
||||
this->ui.tabContainer->addStretch(1);
|
||||
|
||||
this->addTab(new settingspages::IgnoreMessagesPage);
|
||||
this->addTab(new settingspages::IgnoreUsersPage);
|
||||
this->addTab(new settingspages::KeyboardSettingsPage);
|
||||
this->addTab(new settingspages::LogsPage);
|
||||
this->addTab(new settingspages::ModerationPage);
|
||||
this->addTab(new settingspages::SpecialChannelsPage);
|
||||
|
||||
this->ui.tabContainer->addStretch(1);
|
||||
this->addTab(new settingspages::AboutPage, Qt::AlignBottom);
|
||||
}
|
||||
|
@ -135,7 +149,7 @@ void SettingsDialog::refresh()
|
|||
singletons::SettingManager::getInstance().saveSnapshot();
|
||||
}
|
||||
|
||||
void SettingsDialog::dpiMultiplierChanged(float oldDpi, float newDpi)
|
||||
void SettingsDialog::scaleChangedEvent(float newDpi)
|
||||
{
|
||||
QFile file(":/qss/settings.qss");
|
||||
file.open(QFile::ReadOnly);
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
static void showDialog(PreferredTab preferredTab = PreferredTab::NoPreference);
|
||||
|
||||
protected:
|
||||
virtual void dpiMultiplierChanged(float oldDpi, float newDpi) override;
|
||||
virtual void scaleChangedEvent(float newDpi) override;
|
||||
|
||||
private:
|
||||
void refresh();
|
||||
|
|
|
@ -44,8 +44,10 @@ AppearancePage::AppearancePage()
|
|||
form->addRow("Font:", this->createFontChanger());
|
||||
|
||||
form->addRow("Tab bar:", this->createCheckBox(TAB_X, settings.hideTabX));
|
||||
#ifndef USEWINSDK
|
||||
form->addRow("", this->createCheckBox(TAB_PREF, settings.hidePreferencesButton));
|
||||
form->addRow("", this->createCheckBox(TAB_USER, settings.hideUserButton));
|
||||
#endif
|
||||
|
||||
form->addRow("Scrolling:", this->createCheckBox(SCROLL_SMOOTH, settings.enableSmoothScrolling));
|
||||
form->addRow("", this->createCheckBox(SCROLL_NEWMSG, settings.enableSmoothScrollingNewMessages));
|
||||
|
@ -59,6 +61,7 @@ AppearancePage::AppearancePage()
|
|||
{
|
||||
tbox.emplace<QLabel>("timestamp format (a = am/pm):");
|
||||
tbox.append(this->createComboBox({TIMESTAMP_FORMATS}, settings.timestampFormat));
|
||||
tbox->addStretch(1);
|
||||
}
|
||||
messages.append(this->createCheckBox("Show badges", settings.showBadges));
|
||||
messages.append(this->createCheckBox("Seperate messages", settings.seperateMessages));
|
||||
|
@ -77,7 +80,7 @@ QLayout *AppearancePage::createThemeColorChanger()
|
|||
// SLIDER
|
||||
QSlider *slider = new QSlider(Qt::Horizontal);
|
||||
layout->addWidget(slider);
|
||||
slider->setValue(std::min(std::max(themeHue.getValue(), 0.0), 1.0) * 1000);
|
||||
slider->setValue(std::min(std::max(themeHue.getValue(), 0.0), 1.0) * 100);
|
||||
|
||||
// BUTTON
|
||||
QPushButton *button = new QPushButton;
|
||||
|
@ -87,7 +90,7 @@ QLayout *AppearancePage::createThemeColorChanger()
|
|||
|
||||
// SIGNALS
|
||||
QObject::connect(slider, &QSlider::valueChanged, this, [button, &themeHue](int value) mutable {
|
||||
double newValue = value / 1000.0;
|
||||
double newValue = value / 100.0;
|
||||
|
||||
themeHue.setValue(newValue);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "behaviourpage.hpp"
|
||||
|
||||
#include <QFormLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
|
@ -8,7 +9,7 @@
|
|||
|
||||
#define WINDOW_TOPMOST "Window always on top (requires restart)"
|
||||
#define INPUT_EMPTY "Hide input box when empty"
|
||||
#define LAST_MSG "Show last read message indicator"
|
||||
#define LAST_MSG "Show last read message indicator (marks the spot where you left the window)"
|
||||
#define PAUSE_HOVERING "When hovering"
|
||||
|
||||
#define STREAMLINK_QUALITY "Choose", "Source", "High", "Medium", "Low", "Audio only"
|
||||
|
@ -22,7 +23,9 @@ BehaviourPage::BehaviourPage()
|
|||
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
|
||||
util::LayoutCreator<BehaviourPage> layoutCreator(this);
|
||||
|
||||
auto form = layoutCreator.emplace<QFormLayout>().withoutMargin();
|
||||
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
|
||||
|
||||
auto form = layout.emplace<QFormLayout>().withoutMargin();
|
||||
{
|
||||
form->addRow("Window:", this->createCheckBox(WINDOW_TOPMOST, settings.windowTopMost));
|
||||
form->addRow("Messages:", this->createCheckBox(INPUT_EMPTY, settings.hideEmptyInput));
|
||||
|
@ -30,10 +33,21 @@ BehaviourPage::BehaviourPage()
|
|||
form->addRow("Pause chat:", this->createCheckBox(PAUSE_HOVERING, settings.pauseChatHover));
|
||||
|
||||
form->addRow("Mouse scroll speed:", this->createMouseScrollSlider());
|
||||
form->addRow("Streamlink path:", this->createLineEdit(settings.streamlinkPath));
|
||||
form->addRow("Prefered quality:",
|
||||
this->createComboBox({STREAMLINK_QUALITY}, settings.preferredQuality));
|
||||
form->addRow("Links:", this->createCheckBox("Open links only on double click",
|
||||
settings.linksDoubleClickOnly));
|
||||
}
|
||||
|
||||
layout->addSpacing(16);
|
||||
|
||||
auto group = layout.emplace<QGroupBox>("Streamlink");
|
||||
{
|
||||
auto groupLayout = group.setLayoutType<QFormLayout>();
|
||||
groupLayout->addRow("Streamlink path:", this->createLineEdit(settings.streamlinkPath));
|
||||
groupLayout->addRow("Prefered quality:",
|
||||
this->createComboBox({STREAMLINK_QUALITY}, settings.preferredQuality));
|
||||
}
|
||||
|
||||
layout->addStretch(1);
|
||||
}
|
||||
|
||||
QSlider *BehaviourPage::createMouseScrollSlider()
|
||||
|
|
37
src/widgets/settingspages/ignoremessagespage.cpp
Normal file
37
src/widgets/settingspages/ignoremessagespage.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "ignoremessagespage.hpp"
|
||||
|
||||
#include "util/layoutcreator.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QTextEdit>
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
namespace settingspages {
|
||||
IgnoreMessagesPage::IgnoreMessagesPage()
|
||||
: SettingsPage("Ignore Messages", "")
|
||||
{
|
||||
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
|
||||
util::LayoutCreator<IgnoreMessagesPage> layoutCreator(this);
|
||||
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
|
||||
|
||||
layout.emplace<QLabel>("Ignored keywords:");
|
||||
QTextEdit *textEdit = layout.emplace<QTextEdit>().getElement();
|
||||
|
||||
textEdit->setPlainText(settings.ignoredKeywords);
|
||||
|
||||
QObject::connect(textEdit, &QTextEdit::textChanged,
|
||||
[this] { this->keywordsUpdated.start(200); });
|
||||
|
||||
QObject::connect(&this->keywordsUpdated, &QTimer::timeout, [textEdit, &settings] {
|
||||
QString text = textEdit->toPlainText();
|
||||
|
||||
settings.ignoredKeywords = text;
|
||||
});
|
||||
|
||||
// ---- misc
|
||||
this->keywordsUpdated.setSingleShot(true);
|
||||
}
|
||||
} // namespace settingspages
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
19
src/widgets/settingspages/ignoremessagespage.hpp
Normal file
19
src/widgets/settingspages/ignoremessagespage.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <QTimer>
|
||||
#include "widgets/settingspages/settingspage.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
namespace settingspages {
|
||||
|
||||
class IgnoreMessagesPage : public SettingsPage
|
||||
{
|
||||
public:
|
||||
IgnoreMessagesPage();
|
||||
|
||||
QTimer keywordsUpdated;
|
||||
};
|
||||
} // namespace settingspages
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
55
src/widgets/settingspages/ignoreuserspage.cpp
Normal file
55
src/widgets/settingspages/ignoreuserspage.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#include "ignoreuserspage.hpp"
|
||||
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
#include "util/layoutcreator.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QListView>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
// clang-format off
|
||||
#define INFO "/ignore <user> in chat ignores a user\n/unignore <user> in chat unignores a user\n\nChatterino uses the twitch api for ignored users so they are shared with the webchat.\nIf you use your own oauth key make sure that it has the correct permissions.\n"
|
||||
// clang-format on
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
namespace settingspages {
|
||||
IgnoreUsersPage::IgnoreUsersPage()
|
||||
: SettingsPage("Ignore Users", "")
|
||||
{
|
||||
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
|
||||
util::LayoutCreator<IgnoreUsersPage> layoutCreator(this);
|
||||
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
|
||||
|
||||
auto label = layout.emplace<QLabel>(INFO);
|
||||
label->setWordWrap(true);
|
||||
label->setStyleSheet("color: #BBB");
|
||||
|
||||
auto group = layout.emplace<QGroupBox>("Ignored users").setLayoutType<QVBoxLayout>();
|
||||
{
|
||||
group.append(
|
||||
this->createCheckBox("Enable twitch ignored users", settings.enableTwitchIgnoredUsers));
|
||||
|
||||
auto anyways = group.emplace<QHBoxLayout>().withoutMargin();
|
||||
{
|
||||
anyways.emplace<QLabel>("Show anyways if:");
|
||||
anyways.emplace<QComboBox>();
|
||||
anyways->addStretch(1);
|
||||
}
|
||||
|
||||
auto addremove = group.emplace<QHBoxLayout>().withoutMargin();
|
||||
{
|
||||
auto add = addremove.emplace<QPushButton>("Ignore user");
|
||||
auto remove = addremove.emplace<QPushButton>("Unignore User");
|
||||
addremove->addStretch(1);
|
||||
}
|
||||
|
||||
auto userList = group.emplace<QListView>();
|
||||
}
|
||||
}
|
||||
} // namespace settingspages
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
15
src/widgets/settingspages/ignoreuserspage.hpp
Normal file
15
src/widgets/settingspages/ignoreuserspage.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/settingspages/settingspage.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
namespace settingspages {
|
||||
class IgnoreUsersPage : public SettingsPage
|
||||
{
|
||||
public:
|
||||
IgnoreUsersPage();
|
||||
};
|
||||
} // namespace settingspages
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
12
src/widgets/settingspages/keyboardsettingspage.cpp
Normal file
12
src/widgets/settingspages/keyboardsettingspage.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "keyboardsettingspage.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
namespace settingspages {
|
||||
KeyboardSettingsPage::KeyboardSettingsPage()
|
||||
: SettingsPage("Keybindings", "")
|
||||
{
|
||||
}
|
||||
} // namespace settingspages
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
15
src/widgets/settingspages/keyboardsettingspage.hpp
Normal file
15
src/widgets/settingspages/keyboardsettingspage.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/settingspages/settingspage.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
namespace settingspages {
|
||||
class KeyboardSettingsPage : public SettingsPage
|
||||
{
|
||||
public:
|
||||
KeyboardSettingsPage();
|
||||
};
|
||||
} // namespace settingspages
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
|
@ -4,7 +4,7 @@ namespace chatterino {
|
|||
namespace widgets {
|
||||
namespace settingspages {
|
||||
LogsPage::LogsPage()
|
||||
: SettingsPage("Logs", ":/images/VSO_Link_blue_16x.png")
|
||||
: SettingsPage("Logs", "")
|
||||
{
|
||||
}
|
||||
} // namespace settingspages
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "moderationpage.hpp"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QListView>
|
||||
|
@ -18,22 +19,33 @@ ModerationPage::ModerationPage()
|
|||
singletons::SettingManager &settings = singletons::SettingManager::getInstance();
|
||||
util::LayoutCreator<ModerationPage> layoutCreator(this);
|
||||
|
||||
auto layout = layoutCreator.emplace<QVBoxLayout>().withoutMargin();
|
||||
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
|
||||
{
|
||||
// clang-format off
|
||||
auto label = layout.emplace<QLabel>("In channels that you moderate there is a button <insert image of button here> to enable moderation mode.\n\nOne action per line. {user} will be replaced with the username.\nExample `/timeout {user} 120`");
|
||||
auto label = layout.emplace<QLabel>("Click the moderation mod button (<img width='18' height='18' src=':/images/moderatormode_disabled.png'>) in a channel that you moderate to enable moderator mode.<br>");
|
||||
label->setWordWrap(true);
|
||||
label->setStyleSheet("color: #bbb");
|
||||
// clang-format on
|
||||
|
||||
auto text = layout.emplace<QTextEdit>().getElement();
|
||||
auto modButtons =
|
||||
layout.emplace<QGroupBox>("Custom moderator buttons").setLayoutType<QVBoxLayout>();
|
||||
{
|
||||
auto label2 =
|
||||
modButtons.emplace<QLabel>("One action per line. {user} will be replaced with the "
|
||||
"username.<br>Example `/timeout {user} 120`<br>");
|
||||
label2->setWordWrap(true);
|
||||
|
||||
text->setPlainText(settings.moderationActions);
|
||||
auto text = modButtons.emplace<QTextEdit>().getElement();
|
||||
|
||||
QObject::connect(text, &QTextEdit::textChanged, this,
|
||||
[this] { this->itemsChangedTimer.start(200); });
|
||||
text->setPlainText(settings.moderationActions);
|
||||
|
||||
QObject::connect(&this->itemsChangedTimer, &QTimer::timeout, this,
|
||||
[text, &settings]() { settings.moderationActions = text->toPlainText(); });
|
||||
QObject::connect(text, &QTextEdit::textChanged, this,
|
||||
[this] { this->itemsChangedTimer.start(200); });
|
||||
|
||||
QObject::connect(&this->itemsChangedTimer, &QTimer::timeout, this, [text, &settings]() {
|
||||
settings.moderationActions = text->toPlainText();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ---- misc
|
||||
|
|
28
src/widgets/settingspages/specialchannelspage.cpp
Normal file
28
src/widgets/settingspages/specialchannelspage.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
#include "specialchannelspage.hpp"
|
||||
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
#include "util/layoutcreator.hpp"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
namespace settingspages {
|
||||
SpecialChannelsPage::SpecialChannelsPage()
|
||||
: SettingsPage("Special channels", "")
|
||||
{
|
||||
util::LayoutCreator<SpecialChannelsPage> layoutCreator(this);
|
||||
auto layout = layoutCreator.setLayoutType<QVBoxLayout>();
|
||||
|
||||
auto mentions = layout.emplace<QGroupBox>("Mentions channel").setLayoutType<QVBoxLayout>();
|
||||
{
|
||||
mentions.emplace<QLabel>("Join /mentions to view your mentions.");
|
||||
}
|
||||
|
||||
layout->addStretch(1);
|
||||
}
|
||||
} // namespace settingspages
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
15
src/widgets/settingspages/specialchannelspage.hpp
Normal file
15
src/widgets/settingspages/specialchannelspage.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include "widgets/settingspages/settingspage.hpp"
|
||||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
namespace settingspages {
|
||||
class SpecialChannelsPage : public SettingsPage
|
||||
{
|
||||
public:
|
||||
SpecialChannelsPage();
|
||||
};
|
||||
} // namespace settingspages
|
||||
} // namespace widgets
|
||||
} // namespace chatterino
|
|
@ -87,7 +87,7 @@ Split::Split(SplitContainer *parent, const std::string &_uuid)
|
|||
|
||||
this->channelNameUpdated(this->channelName.getValue());
|
||||
|
||||
this->input.textInput.installEventFilter(parent);
|
||||
this->input.ui.textEdit->installEventFilter(parent);
|
||||
|
||||
this->view.mouseDown.connect([this](QMouseEvent *) { this->giveFocus(Qt::MouseFocusReason); });
|
||||
this->view.selectionChanged.connect([this]() {
|
||||
|
@ -132,17 +132,17 @@ const std::string &Split::getUUID() const
|
|||
return this->uuid;
|
||||
}
|
||||
|
||||
SharedChannel Split::getChannel() const
|
||||
ChannelPtr Split::getChannel() const
|
||||
{
|
||||
return this->channel;
|
||||
}
|
||||
|
||||
SharedChannel &Split::getChannelRef()
|
||||
ChannelPtr &Split::getChannelRef()
|
||||
{
|
||||
return this->channel;
|
||||
}
|
||||
|
||||
void Split::setChannel(SharedChannel _newChannel)
|
||||
void Split::setChannel(ChannelPtr _newChannel)
|
||||
{
|
||||
this->view.setChannel(_newChannel);
|
||||
|
||||
|
@ -248,18 +248,22 @@ void Split::layoutMessages()
|
|||
|
||||
void Split::updateGifEmotes()
|
||||
{
|
||||
qDebug() << "this shouldn't even exist";
|
||||
this->view.queueUpdate();
|
||||
}
|
||||
|
||||
void Split::updateLastReadMessage()
|
||||
{
|
||||
this->view.updateLastReadMessage();
|
||||
}
|
||||
|
||||
void Split::giveFocus(Qt::FocusReason reason)
|
||||
{
|
||||
this->input.textInput.setFocus(reason);
|
||||
this->input.ui.textEdit->setFocus(reason);
|
||||
}
|
||||
|
||||
bool Split::hasFocus() const
|
||||
{
|
||||
return this->input.textInput.hasFocus();
|
||||
return this->input.ui.textEdit->hasFocus();
|
||||
}
|
||||
|
||||
void Split::paintEvent(QPaintEvent *)
|
||||
|
@ -350,7 +354,7 @@ void Split::doClearChat()
|
|||
|
||||
void Split::doOpenChannel()
|
||||
{
|
||||
SharedChannel _channel = this->channel;
|
||||
ChannelPtr _channel = this->channel;
|
||||
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(_channel.get());
|
||||
|
||||
if (tc != nullptr) {
|
||||
|
@ -360,7 +364,7 @@ void Split::doOpenChannel()
|
|||
|
||||
void Split::doOpenPopupPlayer()
|
||||
{
|
||||
SharedChannel _channel = this->channel;
|
||||
ChannelPtr _channel = this->channel;
|
||||
twitch::TwitchChannel *tc = dynamic_cast<twitch::TwitchChannel *>(_channel.get());
|
||||
|
||||
if (tc != nullptr) {
|
||||
|
@ -460,6 +464,7 @@ void Split::doOpenViewerList()
|
|||
viewerDock->move(0, this->header.height());
|
||||
|
||||
auto accountPopup = new AccountPopupWidget(this->channel);
|
||||
accountPopup->setAttribute(Qt::WA_DeleteOnClose);
|
||||
auto multiWidget = new QWidget(viewerDock);
|
||||
auto dockVbox = new QVBoxLayout(viewerDock);
|
||||
auto searchBar = new QLineEdit(viewerDock);
|
||||
|
@ -538,9 +543,9 @@ void Split::doOpenViewerList()
|
|||
void Split::doOpenAccountPopupWidget(AccountPopupWidget *widget, QString user)
|
||||
{
|
||||
widget->setName(user);
|
||||
widget->move(QCursor::pos());
|
||||
widget->show();
|
||||
widget->setFocus();
|
||||
widget->moveTo(this, QCursor::pos());
|
||||
}
|
||||
|
||||
void Split::doCopy()
|
||||
|
|
|
@ -55,8 +55,8 @@ public:
|
|||
}
|
||||
|
||||
const std::string &getUUID() const;
|
||||
SharedChannel getChannel() const;
|
||||
SharedChannel &getChannelRef();
|
||||
ChannelPtr getChannel() const;
|
||||
ChannelPtr &getChannelRef();
|
||||
void setFlexSizeX(double x);
|
||||
double getFlexSizeX();
|
||||
void setFlexSizeY(double y);
|
||||
|
@ -70,6 +70,7 @@ public:
|
|||
bool hasFocus() const;
|
||||
void layoutMessages();
|
||||
void updateGifEmotes();
|
||||
void updateLastReadMessage();
|
||||
|
||||
void drag();
|
||||
|
||||
|
@ -82,7 +83,7 @@ protected:
|
|||
|
||||
private:
|
||||
SplitContainer &parentPage;
|
||||
SharedChannel channel;
|
||||
ChannelPtr channel;
|
||||
|
||||
QVBoxLayout vbox;
|
||||
SplitHeader header;
|
||||
|
@ -96,7 +97,7 @@ private:
|
|||
boost::signals2::connection channelIDChangedConnection;
|
||||
boost::signals2::connection usermodeChangedConnection;
|
||||
|
||||
void setChannel(SharedChannel newChannel);
|
||||
void setChannel(ChannelPtr newChannel);
|
||||
void doOpenAccountPopupWidget(AccountPopupWidget *widget, QString user);
|
||||
void channelNameUpdated(const std::string &newChannelName);
|
||||
void handleModifiers(QEvent *event, Qt::KeyboardModifiers modifiers);
|
||||
|
|
|
@ -143,7 +143,7 @@ void SplitContainer::addToLayout(Split *widget, std::pair<int, int> position)
|
|||
this->refreshCurrentFocusCoordinates();
|
||||
}
|
||||
|
||||
const std::vector<Split *> &SplitContainer::getChatWidgets() const
|
||||
const std::vector<Split *> &SplitContainer::getSplits() const
|
||||
{
|
||||
return this->splits;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
std::pair<int, int> removeFromLayout(Split *widget);
|
||||
void addToLayout(Split *widget, std::pair<int, int> position = std::pair<int, int>(-1, -1));
|
||||
|
||||
const std::vector<Split *> &getChatWidgets() const;
|
||||
const std::vector<Split *> &getSplits() const;
|
||||
NotebookTab *getTab() const;
|
||||
|
||||
void addChat(bool openChannelNameDialog = false, std::string chatUUID = std::string());
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
namespace chatterino {
|
||||
namespace widgets {
|
||||
StreamView::StreamView(SharedChannel channel, QUrl url)
|
||||
StreamView::StreamView(ChannelPtr channel, QUrl url)
|
||||
{
|
||||
util::LayoutCreator<StreamView> layoutCreator(this);
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ TooltipWidget::TooltipWidget(BaseWidget *parent)
|
|||
this->setStyleSheet("color: #fff; background: #000");
|
||||
this->setWindowOpacity(0.8);
|
||||
this->updateFont();
|
||||
this->setStayInScreenRect(true);
|
||||
|
||||
this->setAttribute(Qt::WA_ShowWithoutActivating);
|
||||
this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint |
|
||||
|
@ -39,7 +40,7 @@ TooltipWidget::~TooltipWidget()
|
|||
this->fontChangedConnection.disconnect();
|
||||
}
|
||||
|
||||
void TooltipWidget::dpiMultiplierChanged(float, float)
|
||||
void TooltipWidget::scaleChangedEvent(float)
|
||||
{
|
||||
this->updateFont();
|
||||
}
|
||||
|
@ -47,7 +48,7 @@ void TooltipWidget::dpiMultiplierChanged(float, float)
|
|||
void TooltipWidget::updateFont()
|
||||
{
|
||||
this->setFont(singletons::FontManager::getInstance().getFont(
|
||||
singletons::FontManager::Type::MediumSmall, this->getDpiMultiplier()));
|
||||
singletons::FontManager::Type::MediumSmall, this->getScale()));
|
||||
}
|
||||
|
||||
void TooltipWidget::setText(QString text)
|
||||
|
@ -55,45 +56,6 @@ void TooltipWidget::setText(QString text)
|
|||
this->displayText->setText(text);
|
||||
}
|
||||
|
||||
void TooltipWidget::moveTo(QWidget *parent, QPoint point)
|
||||
{
|
||||
point.rx() += 16;
|
||||
point.ry() += 16;
|
||||
|
||||
this->move(point);
|
||||
this->moveIntoDesktopRect(parent);
|
||||
}
|
||||
|
||||
void TooltipWidget::resizeEvent(QResizeEvent *)
|
||||
{
|
||||
this->moveIntoDesktopRect(this);
|
||||
}
|
||||
|
||||
void TooltipWidget::moveIntoDesktopRect(QWidget *parent)
|
||||
{
|
||||
QDesktopWidget *desktop = QApplication::desktop();
|
||||
|
||||
QRect s = desktop->screenGeometry(parent);
|
||||
QPoint p = this->pos();
|
||||
|
||||
if (p.x() < s.left()) {
|
||||
p.setX(s.left());
|
||||
}
|
||||
if (p.y() < s.top()) {
|
||||
p.setY(s.top());
|
||||
}
|
||||
if (p.x() + this->width() > s.right()) {
|
||||
p.setX(s.right() - this->width());
|
||||
}
|
||||
if (p.y() + this->height() > s.bottom()) {
|
||||
p.setY(s.bottom() - this->height());
|
||||
}
|
||||
|
||||
if (p != this->pos()) {
|
||||
this->move(p);
|
||||
}
|
||||
}
|
||||
|
||||
void TooltipWidget::changeEvent(QEvent *)
|
||||
{
|
||||
// clear parents event
|
||||
|
|
|
@ -16,7 +16,6 @@ public:
|
|||
~TooltipWidget();
|
||||
|
||||
void setText(QString text);
|
||||
void moveTo(QWidget *widget, QPoint point);
|
||||
|
||||
static TooltipWidget *getInstance()
|
||||
{
|
||||
|
@ -28,16 +27,14 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
virtual void resizeEvent(QResizeEvent *) override;
|
||||
virtual void changeEvent(QEvent *) override;
|
||||
virtual void leaveEvent(QEvent *) override;
|
||||
virtual void dpiMultiplierChanged(float, float) override;
|
||||
virtual void scaleChangedEvent(float) override;
|
||||
|
||||
private:
|
||||
QLabel *displayText;
|
||||
pajlada::Signals::Connection fontChangedConnection;
|
||||
|
||||
void moveIntoDesktopRect(QWidget *parent);
|
||||
void updateFont();
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "singletons/ircmanager.hpp"
|
||||
#include "singletons/settingsmanager.hpp"
|
||||
#include "singletons/thememanager.hpp"
|
||||
#include "singletons/windowmanager.hpp"
|
||||
#include "widgets/accountswitchpopupwidget.hpp"
|
||||
#include "widgets/helper/shortcut.hpp"
|
||||
#include "widgets/notebook.hpp"
|
||||
#include "widgets/settingsdialog.hpp"
|
||||
|
@ -21,7 +23,7 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
|
|||
: BaseWindow(_themeManager, nullptr, true)
|
||||
, settingRoot(fS("/windows/{}", windowName))
|
||||
, windowGeometry(this->settingRoot)
|
||||
, dpi(this->getDpiMultiplier())
|
||||
, dpi(this->getScale())
|
||||
, themeManager(_themeManager)
|
||||
, notebook(this, _isMainWindow, this->settingRoot)
|
||||
{
|
||||
|
@ -35,8 +37,12 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
|
|||
});
|
||||
|
||||
if (this->hasCustomWindowFrame()) {
|
||||
this->addTitleBarButton("Preferences");
|
||||
this->addTitleBarButton("User");
|
||||
this->addTitleBarButton(TitleBarButton::Settings, [] {
|
||||
singletons::WindowManager::getInstance().showSettingsDialog();
|
||||
});
|
||||
this->addTitleBarButton(TitleBarButton::User, [this] {
|
||||
singletons::WindowManager::getInstance().showAccountSelectPopup(QCursor::pos());
|
||||
});
|
||||
}
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||
|
@ -52,7 +58,7 @@ Window::Window(const QString &windowName, singletons::ThemeManager &_themeManage
|
|||
// set margin
|
||||
layout->setMargin(0);
|
||||
|
||||
this->refreshTheme();
|
||||
this->themeRefreshEvent();
|
||||
|
||||
this->loadGeometry();
|
||||
|
||||
|
@ -107,7 +113,7 @@ void Window::repaintVisibleChatWidgets(Channel *channel)
|
|||
return;
|
||||
}
|
||||
|
||||
const std::vector<Split *> &widgets = page->getChatWidgets();
|
||||
const std::vector<Split *> &widgets = page->getSplits();
|
||||
|
||||
for (auto it = widgets.begin(); it != widgets.end(); ++it) {
|
||||
Split *widget = *it;
|
||||
|
@ -140,6 +146,27 @@ void Window::closeEvent(QCloseEvent *)
|
|||
this->closed();
|
||||
}
|
||||
|
||||
bool Window::event(QEvent *e)
|
||||
{
|
||||
switch (e->type()) {
|
||||
case QEvent::WindowActivate:
|
||||
break;
|
||||
|
||||
case QEvent::WindowDeactivate: {
|
||||
auto page = this->notebook.getSelectedPage();
|
||||
|
||||
if (page != nullptr) {
|
||||
std::vector<Split *> splits = page->getSplits();
|
||||
|
||||
for (Split *split : splits) {
|
||||
split->updateLastReadMessage();
|
||||
}
|
||||
}
|
||||
} break;
|
||||
};
|
||||
return BaseWindow::event(e);
|
||||
}
|
||||
|
||||
void Window::loadGeometry()
|
||||
{
|
||||
bool doSetGeometry = false;
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
|
||||
protected:
|
||||
virtual void closeEvent(QCloseEvent *event) override;
|
||||
virtual bool event(QEvent *event) override;
|
||||
|
||||
private:
|
||||
singletons::ThemeManager &themeManager;
|
||||
|
|
Loading…
Reference in a new issue