Remove getApp and getSettings calls from Message-Rendering (#4535)

* refactor: remove singletons from message rendering

* chore: add changelog entry

* Disable the `cppcoreguidelines-avoid-const-or-ref-data-members` clang-tidy check

* auto *app

* Selection is a struct, not a class

* Use ChannelView's `signalHolder_` instead of `channelConnections_`

* Remove `applySettings` step, instead just connect & set each setting individually

* rename & constify some context values

* Handle empty "last message color" setting value better (as it was
        originally in this pr before I removed that change :-)

* unrelated mini refactor cleanup

* let painSelection handle size_t instead of int

* Add some more comments to the MessageLayoutContext structs

---------

Co-authored-by: Rasmus Karlsson <rasmus.karlsson@pajlada.com>
This commit is contained in:
nerix 2023-07-30 14:52:39 +02:00 committed by GitHub
parent 95aee044e2
commit 71594ad0d8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 312 additions and 131 deletions

View file

@ -18,6 +18,7 @@ Checks: "-*,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay, -cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-cppcoreguidelines-owning-memory, -cppcoreguidelines-owning-memory,
-cppcoreguidelines-avoid-magic-numbers, -cppcoreguidelines-avoid-magic-numbers,
-cppcoreguidelines-avoid-const-or-ref-data-members,
-readability-magic-numbers, -readability-magic-numbers,
-performance-noexcept-move-constructor, -performance-noexcept-move-constructor,
-misc-non-private-member-variables-in-classes, -misc-non-private-member-variables-in-classes,

View file

@ -44,6 +44,7 @@
- Dev: Moved preprocessor Git and date definitions to executables only. (#4681) - Dev: Moved preprocessor Git and date definitions to executables only. (#4681)
- Dev: Refactored tests to be able to use `ctest` and run in debug builds. (#4700) - Dev: Refactored tests to be able to use `ctest` and run in debug builds. (#4700)
- Dev: Added the ability to use an alternate linker using the `-DUSE_ALTERNATE_LINKER=...` CMake parameter. (#4711) - Dev: Added the ability to use an alternate linker using the `-DUSE_ALTERNATE_LINKER=...` CMake parameter. (#4711)
- Dev: Removed `getApp` and `getSettings` calls from message rendering. (#4535)
## 2.4.4 ## 2.4.4

View file

@ -209,6 +209,8 @@ set(SOURCE_FILES
messages/layouts/MessageLayout.hpp messages/layouts/MessageLayout.hpp
messages/layouts/MessageLayoutContainer.cpp messages/layouts/MessageLayoutContainer.cpp
messages/layouts/MessageLayoutContainer.hpp messages/layouts/MessageLayoutContainer.hpp
messages/layouts/MessageLayoutContext.cpp
messages/layouts/MessageLayoutContext.hpp
messages/layouts/MessageLayoutElement.cpp messages/layouts/MessageLayoutElement.cpp
messages/layouts/MessageLayoutElement.hpp messages/layouts/MessageLayoutElement.hpp
messages/search/AuthorPredicate.cpp messages/search/AuthorPredicate.cpp

View file

@ -24,7 +24,6 @@ namespace chatterino::literals {
namespace detail { namespace detail {
// NOLINTBEGIN(modernize-avoid-c-arrays) // NOLINTBEGIN(modernize-avoid-c-arrays)
// NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays) // NOLINTBEGIN(cppcoreguidelines-avoid-c-arrays)
// NOLINTBEGIN(cppcoreguidelines-avoid-const-or-ref-data-members)
template <size_t N> template <size_t N>
struct LiteralResolver { struct LiteralResolver {
@ -95,7 +94,6 @@ namespace detail {
} }
}; };
// NOLINTEND(cppcoreguidelines-avoid-const-or-ref-data-members)
// NOLINTEND(cppcoreguidelines-avoid-c-arrays) // NOLINTEND(cppcoreguidelines-avoid-c-arrays)
// NOLINTEND(modernize-avoid-c-arrays) // NOLINTEND(modernize-avoid-c-arrays)

View file

@ -1,16 +1,14 @@
#include "messages/layouts/MessageLayout.hpp" #include "messages/layouts/MessageLayout.hpp"
#include "Application.hpp" #include "Application.hpp"
#include "debug/Benchmark.hpp"
#include "messages/layouts/MessageLayoutContainer.hpp" #include "messages/layouts/MessageLayoutContainer.hpp"
#include "messages/layouts/MessageLayoutContext.hpp"
#include "messages/layouts/MessageLayoutElement.hpp" #include "messages/layouts/MessageLayoutElement.hpp"
#include "messages/Message.hpp" #include "messages/Message.hpp"
#include "messages/MessageElement.hpp" #include "messages/MessageElement.hpp"
#include "messages/Selection.hpp" #include "messages/Selection.hpp"
#include "providers/colors/ColorProvider.hpp" #include "providers/colors/ColorProvider.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Settings.hpp" #include "singletons/Settings.hpp"
#include "singletons/Theme.hpp"
#include "singletons/WindowManager.hpp" #include "singletons/WindowManager.hpp"
#include "util/DebugCount.hpp" #include "util/DebugCount.hpp"
#include "util/StreamerMode.hpp" #include "util/StreamerMode.hpp"
@ -198,84 +196,77 @@ void MessageLayout::actuallyLayout(int width, MessageElementFlags flags)
} }
// Painting // Painting
void MessageLayout::paint(QPainter &painter, int width, int y, int messageIndex, void MessageLayout::paint(const MessagePaintContext &ctx)
Selection &selection, bool isLastReadMessage,
bool isWindowFocused, bool isMentions)
{ {
auto app = getApp(); QPixmap *pixmap = this->ensureBuffer(ctx.painter, ctx.canvasWidth);
QPixmap *pixmap = this->ensureBuffer(painter, width);
if (!this->bufferValid_ || !selection.isEmpty()) if (!this->bufferValid_ || !ctx.selection.isEmpty())
{ {
this->updateBuffer(pixmap, messageIndex, selection); this->updateBuffer(pixmap, ctx);
} }
// draw on buffer // draw on buffer
painter.drawPixmap(0, y, *pixmap); ctx.painter.drawPixmap(0, ctx.y, *pixmap);
// painter.drawPixmap(0, y, this->container.width,
// this->container.getHeight(), *pixmap);
// draw gif emotes // draw gif emotes
this->container_.paintAnimatedElements(painter, y); this->container_.paintAnimatedElements(ctx.painter, ctx.y);
// draw disabled // draw disabled
if (this->message_->flags.has(MessageFlag::Disabled)) if (this->message_->flags.has(MessageFlag::Disabled))
{ {
painter.fillRect(0, y, pixmap->width(), pixmap->height(), ctx.painter.fillRect(0, ctx.y, pixmap->width(), pixmap->height(),
app->themes->messages.disabled); ctx.messageColors.disabled);
// painter.fillRect(0, y, pixmap->width(), pixmap->height(),
// QBrush(QColor(64, 64, 64, 64)));
} }
if (this->message_->flags.has(MessageFlag::RecentMessage)) if (this->message_->flags.has(MessageFlag::RecentMessage))
{ {
painter.fillRect(0, y, pixmap->width(), pixmap->height(), ctx.painter.fillRect(0, ctx.y, pixmap->width(), pixmap->height(),
app->themes->messages.disabled); ctx.messageColors.disabled);
} }
if (!isMentions && if (!ctx.isMentions &&
(this->message_->flags.has(MessageFlag::RedeemedChannelPointReward) || (this->message_->flags.has(MessageFlag::RedeemedChannelPointReward) ||
this->message_->flags.has(MessageFlag::RedeemedHighlight)) && this->message_->flags.has(MessageFlag::RedeemedHighlight)) &&
getSettings()->enableRedeemedHighlight.getValue()) ctx.preferences.enableRedeemedHighlight)
{ {
painter.fillRect( ctx.painter.fillRect(
0, y, this->scale_ * 4, pixmap->height(), 0, ctx.y, int(this->scale_ * 4), pixmap->height(),
*ColorProvider::instance().color(ColorType::RedeemedHighlight)); *ColorProvider::instance().color(ColorType::RedeemedHighlight));
} }
// draw selection // draw selection
if (!selection.isEmpty()) if (!ctx.selection.isEmpty())
{ {
this->container_.paintSelection(painter, messageIndex, selection, y); this->container_.paintSelection(ctx.painter, ctx.messageIndex,
ctx.selection, ctx.y);
} }
// draw message seperation line // draw message seperation line
if (getSettings()->separateMessages.getValue()) if (ctx.preferences.separateMessages)
{ {
painter.fillRect(0, y, this->container_.getWidth() + 64, 1, ctx.painter.fillRect(0, ctx.y, this->container_.getWidth() + 64, 1,
app->themes->splits.messageSeperator); ctx.messageColors.messageSeperator);
} }
// draw last read message line // draw last read message line
if (isLastReadMessage) if (ctx.isLastReadMessage)
{ {
QColor color; QColor color;
if (getSettings()->lastMessageColor != QStringLiteral("")) if (ctx.preferences.lastMessageColor.isValid())
{ {
color = QColor(getSettings()->lastMessageColor.getValue()); color = ctx.preferences.lastMessageColor;
} }
else else
{ {
color = isWindowFocused color = ctx.isWindowFocused
? app->themes->tabs.selected.backgrounds.regular ? ctx.messageColors.focusedLastMessageLine
: app->themes->tabs.selected.backgrounds.unfocused; : ctx.messageColors.unfocusedLastMessageLine;
} }
QBrush brush(color, static_cast<Qt::BrushStyle>( QBrush brush(color, ctx.preferences.lastMessagePattern);
getSettings()->lastMessagePattern.getValue()));
painter.fillRect(0, y + this->container_.getHeight() - 1, ctx.painter.fillRect(0, ctx.y + this->container_.getHeight() - 1,
pixmap->width(), 1, brush); pixmap->width(), 1, brush);
} }
this->bufferValid_ = true; this->bufferValid_ = true;
@ -305,45 +296,42 @@ QPixmap *MessageLayout::ensureBuffer(QPainter &painter, int width)
return this->buffer_.get(); return this->buffer_.get();
} }
void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/, void MessageLayout::updateBuffer(QPixmap *buffer,
Selection & /*selection*/) const MessagePaintContext &ctx)
{ {
if (buffer->isNull()) if (buffer->isNull())
{
return; return;
}
auto app = getApp();
auto settings = getSettings();
QPainter painter(buffer); QPainter painter(buffer);
painter.setRenderHint(QPainter::SmoothPixmapTransform); painter.setRenderHint(QPainter::SmoothPixmapTransform);
// draw background // draw background
QColor backgroundColor = [this, &app] { QColor backgroundColor = [&] {
if (getSettings()->alternateMessages.getValue() && if (ctx.preferences.alternateMessages &&
this->flags.has(MessageLayoutFlag::AlternateBackground)) this->flags.has(MessageLayoutFlag::AlternateBackground))
{ {
return app->themes->messages.backgrounds.alternate; return ctx.messageColors.alternate;
}
else
{
return app->themes->messages.backgrounds.regular;
} }
return ctx.messageColors.regular;
}(); }();
if (this->message_->flags.has(MessageFlag::ElevatedMessage) && if (this->message_->flags.has(MessageFlag::ElevatedMessage) &&
getSettings()->enableElevatedMessageHighlight.getValue()) ctx.preferences.enableElevatedMessageHighlight)
{
backgroundColor = blendColors(backgroundColor,
*ColorProvider::instance().color(
ColorType::ElevatedMessageHighlight));
}
else if (this->message_->flags.has(MessageFlag::FirstMessage) &&
getSettings()->enableFirstMessageHighlight.getValue())
{ {
backgroundColor = blendColors( backgroundColor = blendColors(
backgroundColor, backgroundColor,
*ColorProvider::instance().color(ColorType::FirstMessageHighlight)); *ctx.colorProvider.color(ColorType::ElevatedMessageHighlight));
}
else if (this->message_->flags.has(MessageFlag::FirstMessage) &&
ctx.preferences.enableFirstMessageHighlight)
{
backgroundColor = blendColors(
backgroundColor,
*ctx.colorProvider.color(ColorType::FirstMessageHighlight));
} }
else if ((this->message_->flags.has(MessageFlag::Highlighted) || else if ((this->message_->flags.has(MessageFlag::Highlighted) ||
this->message_->flags.has(MessageFlag::HighlightedWhisper)) && this->message_->flags.has(MessageFlag::HighlightedWhisper)) &&
@ -354,22 +342,21 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/,
blendColors(backgroundColor, *this->message_->highlightColor); blendColors(backgroundColor, *this->message_->highlightColor);
} }
else if (this->message_->flags.has(MessageFlag::Subscription) && else if (this->message_->flags.has(MessageFlag::Subscription) &&
getSettings()->enableSubHighlight) ctx.preferences.enableSubHighlight)
{ {
// Blend highlight color with usual background color // Blend highlight color with usual background color
backgroundColor = blendColors( backgroundColor = blendColors(
backgroundColor, backgroundColor, *ctx.colorProvider.color(ColorType::Subscription));
*ColorProvider::instance().color(ColorType::Subscription));
} }
else if ((this->message_->flags.has(MessageFlag::RedeemedHighlight) || else if ((this->message_->flags.has(MessageFlag::RedeemedHighlight) ||
this->message_->flags.has( this->message_->flags.has(
MessageFlag::RedeemedChannelPointReward)) && MessageFlag::RedeemedChannelPointReward)) &&
settings->enableRedeemedHighlight.getValue()) ctx.preferences.enableRedeemedHighlight)
{ {
// Blend highlight color with usual background color // Blend highlight color with usual background color
backgroundColor = blendColors( backgroundColor =
backgroundColor, blendColors(backgroundColor,
*ColorProvider::instance().color(ColorType::RedeemedHighlight)); *ctx.colorProvider.color(ColorType::RedeemedHighlight));
} }
else if (this->message_->flags.has(MessageFlag::AutoMod)) else if (this->message_->flags.has(MessageFlag::AutoMod))
{ {
@ -383,7 +370,7 @@ void MessageLayout::updateBuffer(QPixmap *buffer, int /*messageIndex*/,
painter.fillRect(buffer->rect(), backgroundColor); painter.fillRect(buffer->rect(), backgroundColor);
// draw message // draw message
this->container_.paintElements(painter); this->container_.paintElements(painter, ctx);
#ifdef FOURTF #ifdef FOURTF
// debug // debug

View file

@ -18,6 +18,7 @@ using MessagePtr = std::shared_ptr<const Message>;
struct Selection; struct Selection;
struct MessageLayoutContainer; struct MessageLayoutContainer;
class MessageLayoutElement; class MessageLayoutElement;
struct MessagePaintContext;
enum class MessageElementFlag : int64_t; enum class MessageElementFlag : int64_t;
using MessageElementFlags = FlagsEnum<MessageElementFlag>; using MessageElementFlags = FlagsEnum<MessageElementFlag>;
@ -49,9 +50,7 @@ public:
bool layout(int width, float scale_, MessageElementFlags flags); bool layout(int width, float scale_, MessageElementFlags flags);
// Painting // Painting
void paint(QPainter &painter, int width, int y, int messageIndex, void paint(const MessagePaintContext &ctx);
Selection &selection, bool isLastReadMessage,
bool isWindowFocused, bool isMentions);
void invalidateBuffer(); void invalidateBuffer();
void deleteBuffer(); void deleteBuffer();
void deleteCache(); void deleteCache();
@ -72,7 +71,7 @@ public:
private: private:
// methods // methods
void actuallyLayout(int width, MessageElementFlags flags); void actuallyLayout(int width, MessageElementFlags flags);
void updateBuffer(QPixmap *pixmap, int messageIndex, Selection &selection); void updateBuffer(QPixmap *buffer, const MessagePaintContext &ctx);
// Create new buffer if required, returning the buffer // Create new buffer if required, returning the buffer
QPixmap *ensureBuffer(QPainter &painter, int width); QPixmap *ensureBuffer(QPainter &painter, int width);

View file

@ -1,6 +1,7 @@
#include "MessageLayoutContainer.hpp" #include "MessageLayoutContainer.hpp"
#include "Application.hpp" #include "Application.hpp"
#include "messages/layouts/MessageLayoutContext.hpp"
#include "messages/layouts/MessageLayoutElement.hpp" #include "messages/layouts/MessageLayoutElement.hpp"
#include "messages/Message.hpp" #include "messages/Message.hpp"
#include "messages/MessageElement.hpp" #include "messages/MessageElement.hpp"
@ -151,7 +152,7 @@ void MessageLayoutContainer::_addElement(MessageLayoutElement *element,
} }
// top margin // top margin
if (this->elements_.size() == 0) if (this->elements_.empty())
{ {
this->currentY_ = int(this->margin.top * this->scale_); this->currentY_ = int(this->margin.top * this->scale_);
} }
@ -386,7 +387,7 @@ void MessageLayoutContainer::breakLine()
element->getRect().y() + this->lineHeight_ + yExtra)); element->getRect().y() + this->lineHeight_ + yExtra));
} }
if (this->lines_.size() != 0) if (!this->lines_.empty())
{ {
this->lines_.back().endIndex = this->lineStart_; this->lines_.back().endIndex = this->lineStart_;
this->lines_.back().endCharIndex = this->charIndex_; this->lines_.back().endCharIndex = this->charIndex_;
@ -395,7 +396,7 @@ void MessageLayoutContainer::breakLine()
{(int)lineStart_, 0, this->charIndex_, 0, {(int)lineStart_, 0, this->charIndex_, 0,
QRect(-100000, this->currentY_, 200000, lineHeight_)}); QRect(-100000, this->currentY_, 200000, lineHeight_)});
for (int i = this->lineStart_; i < this->elements_.size(); i++) for (auto i = this->lineStart_; i < this->elements_.size(); i++)
{ {
this->charIndex_ += this->elements_[i]->getSelectionIndexCount(); this->charIndex_ += this->elements_[i]->getSelectionIndexCount();
} }
@ -465,7 +466,7 @@ void MessageLayoutContainer::end()
this->height_ += this->lineHeight_; this->height_ += this->lineHeight_;
if (this->lines_.size() != 0) if (!this->lines_.empty())
{ {
this->lines_[0].rect.setTop(-100000); this->lines_[0].rect.setTop(-100000);
this->lines_.back().rect.setBottom(100000); this->lines_.back().rect.setBottom(100000);
@ -480,7 +481,7 @@ bool MessageLayoutContainer::canCollapse()
this->flags_.has(MessageFlag::Collapsed); this->flags_.has(MessageFlag::Collapsed);
} }
bool MessageLayoutContainer::isCollapsed() bool MessageLayoutContainer::isCollapsed() const
{ {
return this->isCollapsed_; return this->isCollapsed_;
} }
@ -499,7 +500,8 @@ MessageLayoutElement *MessageLayoutContainer::getElementAt(QPoint point)
} }
// painting // painting
void MessageLayoutContainer::paintElements(QPainter &painter) void MessageLayoutContainer::paintElements(QPainter &painter,
const MessagePaintContext &ctx)
{ {
for (const std::unique_ptr<MessageLayoutElement> &element : this->elements_) for (const std::unique_ptr<MessageLayoutElement> &element : this->elements_)
{ {
@ -508,7 +510,7 @@ void MessageLayoutContainer::paintElements(QPainter &painter)
painter.drawRect(element->getRect()); painter.drawRect(element->getRect());
#endif #endif
element->paint(painter); element->paint(painter, ctx.messageColors);
} }
} }
@ -521,10 +523,12 @@ void MessageLayoutContainer::paintAnimatedElements(QPainter &painter,
} }
} }
void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex, void MessageLayoutContainer::paintSelection(QPainter &painter,
Selection &selection, int yOffset) size_t messageIndex,
const Selection &selection,
int yOffset)
{ {
auto app = getApp(); auto *app = getApp();
QColor selectionColor = app->themes->messages.selection; QColor selectionColor = app->themes->messages.selection;
// don't draw anything // don't draw anything
@ -713,7 +717,7 @@ void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex,
// selection // selection
int MessageLayoutContainer::getSelectionIndex(QPoint point) int MessageLayoutContainer::getSelectionIndex(QPoint point)
{ {
if (this->elements_.size() == 0) if (this->elements_.empty())
{ {
return 0; return 0;
} }
@ -774,7 +778,7 @@ int MessageLayoutContainer::getSelectionIndex(QPoint point)
// fourtf: no idea if this is acurate LOL // fourtf: no idea if this is acurate LOL
int MessageLayoutContainer::getLastCharacterIndex() const int MessageLayoutContainer::getLastCharacterIndex() const
{ {
if (this->lines_.size() == 0) if (this->lines_.empty())
{ {
return 0; return 0;
} }
@ -791,7 +795,7 @@ int MessageLayoutContainer::getFirstMessageCharacterIndex() const
// Get the index of the first character of the real message // Get the index of the first character of the real message
int index = 0; int index = 0;
for (auto &element : this->elements_) for (const auto &element : this->elements_)
{ {
if (element->getFlags().hasAny(skippedFlags)) if (element->getFlags().hasAny(skippedFlags))
{ {
@ -853,10 +857,8 @@ void MessageLayoutContainer::addSelectionText(QString &str, uint32_t from,
element->addCopyTextToString(str, 0, to - index); element->addCopyTextToString(str, 0, to - index);
break; break;
} }
else
{ element->addCopyTextToString(str);
element->addCopyTextToString(str);
}
} }
index += indexCount; index += indexCount;

View file

@ -18,6 +18,7 @@ enum class FirstWord { Neutral, RTL, LTR };
using MessageFlags = FlagsEnum<MessageFlag>; using MessageFlags = FlagsEnum<MessageFlag>;
class MessageLayoutElement; class MessageLayoutElement;
struct Selection; struct Selection;
struct MessagePaintContext;
struct Margin { struct Margin {
int top; int top;
@ -73,10 +74,10 @@ struct MessageLayoutContainer {
MessageLayoutElement *getElementAt(QPoint point); MessageLayoutElement *getElementAt(QPoint point);
// painting // painting
void paintElements(QPainter &painter); void paintElements(QPainter &painter, const MessagePaintContext &ctx);
void paintAnimatedElements(QPainter &painter, int yOffset); void paintAnimatedElements(QPainter &painter, int yOffset);
void paintSelection(QPainter &painter, int messageIndex, void paintSelection(QPainter &painter, size_t messageIndex,
Selection &selection, int yOffset); const Selection &selection, int yOffset);
// selection // selection
int getSelectionIndex(QPoint point); int getSelectionIndex(QPoint point);
@ -85,7 +86,7 @@ struct MessageLayoutContainer {
void addSelectionText(QString &str, uint32_t from, uint32_t to, void addSelectionText(QString &str, uint32_t from, uint32_t to,
CopyMode copymode); CopyMode copymode);
bool isCollapsed(); bool isCollapsed() const;
private: private:
struct Line { struct Line {

View file

@ -0,0 +1,82 @@
#include "messages/layouts/MessageLayoutContext.hpp"
#include "singletons/Settings.hpp"
#include "singletons/Theme.hpp"
namespace chatterino {
void MessageColors::applyTheme(Theme *theme)
{
this->regular = theme->messages.backgrounds.regular;
this->alternate = theme->messages.backgrounds.alternate;
this->disabled = theme->messages.disabled;
this->selection = theme->messages.selection;
this->system = theme->messages.textColors.system;
this->messageSeperator = theme->splits.messageSeperator;
this->focusedLastMessageLine = theme->tabs.selected.backgrounds.regular;
this->unfocusedLastMessageLine = theme->tabs.selected.backgrounds.unfocused;
}
void MessagePreferences::connectSettings(Settings *settings,
pajlada::Signals::SignalHolder &holder)
{
settings->enableRedeemedHighlight.connect(
[this](const auto &newValue) {
this->enableRedeemedHighlight = newValue;
},
holder);
settings->enableElevatedMessageHighlight.connect(
[this](const auto &newValue) {
this->enableElevatedMessageHighlight = newValue;
},
holder);
settings->enableFirstMessageHighlight.connect(
[this](const auto &newValue) {
this->enableFirstMessageHighlight = newValue;
},
holder);
settings->enableSubHighlight.connect(
[this](const auto &newValue) {
this->enableSubHighlight = newValue;
},
holder);
settings->alternateMessages.connect(
[this](const auto &newValue) {
this->alternateMessages = newValue;
},
holder);
settings->separateMessages.connect(
[this](const auto &newValue) {
this->separateMessages = newValue;
},
holder);
settings->lastMessageColor.connect(
[this](const auto &newValue) {
if (newValue.isEmpty())
{
this->lastMessageColor = QColor();
}
else
{
this->lastMessageColor = QColor(newValue);
}
},
holder);
settings->lastMessagePattern.connect(
[this](const auto &newValue) {
this->lastMessagePattern = static_cast<Qt::BrushStyle>(newValue);
},
holder);
}
} // namespace chatterino

View file

@ -0,0 +1,74 @@
#pragma once
#include <QColor>
#include <QPainter>
namespace pajlada::Signals {
class SignalHolder;
} // namespace pajlada::Signals
namespace chatterino {
class ColorProvider;
class Theme;
class Settings;
struct Selection;
// TODO: Figure out if this could be a subset of Theme instead (e.g. Theme::MessageColors)
struct MessageColors {
QColor regular;
QColor alternate;
QColor disabled;
QColor selection;
QColor system;
QColor messageSeperator;
QColor focusedLastMessageLine;
QColor unfocusedLastMessageLine;
void applyTheme(Theme *theme);
};
// TODO: Explore if we can let settings own this
struct MessagePreferences {
QColor lastMessageColor;
Qt::BrushStyle lastMessagePattern{};
bool enableRedeemedHighlight{};
bool enableElevatedMessageHighlight{};
bool enableFirstMessageHighlight{};
bool enableSubHighlight{};
bool alternateMessages{};
bool separateMessages{};
void connectSettings(Settings *settings,
pajlada::Signals::SignalHolder &holder);
};
struct MessagePaintContext {
QPainter &painter;
const Selection &selection;
const ColorProvider &colorProvider;
const MessageColors &messageColors;
const MessagePreferences &preferences;
// width of the area we have to draw on
const int canvasWidth{};
// whether the painting should be treated as if this view's window is focused
const bool isWindowFocused{};
// whether the painting should be treated as if this view is the special mentions view
const bool isMentions{};
// y coordinate we're currently painting at
int y{};
// Index of the message that is currently being painted
// This index refers to the snapshot being used in the painting
size_t messageIndex{};
bool isLastReadMessage{};
};
} // namespace chatterino

View file

@ -3,9 +3,9 @@
#include "Application.hpp" #include "Application.hpp"
#include "messages/Emote.hpp" #include "messages/Emote.hpp"
#include "messages/Image.hpp" #include "messages/Image.hpp"
#include "messages/layouts/MessageLayoutContext.hpp"
#include "messages/MessageElement.hpp" #include "messages/MessageElement.hpp"
#include "providers/twitch/TwitchEmotes.hpp" #include "providers/twitch/TwitchEmotes.hpp"
#include "singletons/Theme.hpp"
#include "util/DebugCount.hpp" #include "util/DebugCount.hpp"
#include <QDebug> #include <QDebug>
@ -137,7 +137,8 @@ int ImageLayoutElement::getSelectionIndexCount() const
return this->trailingSpace ? 2 : 1; return this->trailingSpace ? 2 : 1;
} }
void ImageLayoutElement::paint(QPainter &painter) void ImageLayoutElement::paint(QPainter &painter,
const MessageColors & /*messageColors*/)
{ {
if (this->image_ == nullptr) if (this->image_ == nullptr)
{ {
@ -228,7 +229,8 @@ int LayeredImageLayoutElement::getSelectionIndexCount() const
return this->trailingSpace ? 2 : 1; return this->trailingSpace ? 2 : 1;
} }
void LayeredImageLayoutElement::paint(QPainter &painter) void LayeredImageLayoutElement::paint(QPainter &painter,
const MessageColors & /*messageColors*/)
{ {
auto fullRect = QRectF(this->getRect()); auto fullRect = QRectF(this->getRect());
@ -329,7 +331,8 @@ ImageWithBackgroundLayoutElement::ImageWithBackgroundLayoutElement(
{ {
} }
void ImageWithBackgroundLayoutElement::paint(QPainter &painter) void ImageWithBackgroundLayoutElement::paint(
QPainter &painter, const MessageColors & /*messageColors*/)
{ {
if (this->image_ == nullptr) if (this->image_ == nullptr)
{ {
@ -360,7 +363,8 @@ ImageWithCircleBackgroundLayoutElement::ImageWithCircleBackgroundLayoutElement(
{ {
} }
void ImageWithCircleBackgroundLayoutElement::paint(QPainter &painter) void ImageWithCircleBackgroundLayoutElement::paint(
QPainter &painter, const MessageColors & /*messageColors*/)
{ {
if (this->image_ == nullptr) if (this->image_ == nullptr)
{ {
@ -423,7 +427,8 @@ int TextLayoutElement::getSelectionIndexCount() const
return this->getText().length() + (this->trailingSpace ? 1 : 0); return this->getText().length() + (this->trailingSpace ? 1 : 0);
} }
void TextLayoutElement::paint(QPainter &painter) void TextLayoutElement::paint(QPainter &painter,
const MessageColors & /*messageColors*/)
{ {
auto app = getApp(); auto app = getApp();
QString text = this->getText(); QString text = this->getText();
@ -532,13 +537,14 @@ int TextIconLayoutElement::getSelectionIndexCount() const
return this->trailingSpace ? 2 : 1; return this->trailingSpace ? 2 : 1;
} }
void TextIconLayoutElement::paint(QPainter &painter) void TextIconLayoutElement::paint(QPainter &painter,
const MessageColors &messageColors)
{ {
auto app = getApp(); auto *app = getApp();
QFont font = app->fonts->getFont(FontStyle::Tiny, this->scale); QFont font = app->fonts->getFont(FontStyle::Tiny, this->scale);
painter.setPen(app->themes->messages.textColors.system); painter.setPen(messageColors.system);
painter.setFont(font); painter.setFont(font);
QTextOption option; QTextOption option;
@ -598,7 +604,8 @@ ReplyCurveLayoutElement::ReplyCurveLayoutElement(MessageElement &creator,
{ {
} }
void ReplyCurveLayoutElement::paint(QPainter &painter) void ReplyCurveLayoutElement::paint(QPainter &painter,
const MessageColors & /*messageColors*/)
{ {
QRectF paintRect(this->getRect()); QRectF paintRect(this->getRect());
QPainterPath path; QPainterPath path;

View file

@ -21,6 +21,7 @@ class Image;
using ImagePtr = std::shared_ptr<Image>; using ImagePtr = std::shared_ptr<Image>;
enum class FontStyle : uint8_t; enum class FontStyle : uint8_t;
enum class MessageElementFlag : int64_t; enum class MessageElementFlag : int64_t;
struct MessageColors;
class MessageLayoutElement : boost::noncopyable class MessageLayoutElement : boost::noncopyable
{ {
@ -44,7 +45,8 @@ public:
virtual void addCopyTextToString(QString &str, uint32_t from = 0, virtual void addCopyTextToString(QString &str, uint32_t from = 0,
uint32_t to = UINT32_MAX) const = 0; uint32_t to = UINT32_MAX) const = 0;
virtual int getSelectionIndexCount() const = 0; virtual int getSelectionIndexCount() const = 0;
virtual void paint(QPainter &painter) = 0; virtual void paint(QPainter &painter,
const MessageColors &messageColors) = 0;
virtual void paintAnimated(QPainter &painter, int yOffset) = 0; virtual void paintAnimated(QPainter &painter, int yOffset) = 0;
virtual int getMouseOverIndex(const QPoint &abs) const = 0; virtual int getMouseOverIndex(const QPoint &abs) const = 0;
virtual int getXFromIndex(int index) = 0; virtual int getXFromIndex(int index) = 0;
@ -75,7 +77,7 @@ protected:
void addCopyTextToString(QString &str, uint32_t from = 0, void addCopyTextToString(QString &str, uint32_t from = 0,
uint32_t to = UINT32_MAX) const override; uint32_t to = UINT32_MAX) const override;
int getSelectionIndexCount() const override; int getSelectionIndexCount() const override;
void paint(QPainter &painter) override; void paint(QPainter &painter, const MessageColors &messageColors) override;
void paintAnimated(QPainter &painter, int yOffset) override; void paintAnimated(QPainter &painter, int yOffset) override;
int getMouseOverIndex(const QPoint &abs) const override; int getMouseOverIndex(const QPoint &abs) const override;
int getXFromIndex(int index) override; int getXFromIndex(int index) override;
@ -94,7 +96,7 @@ protected:
void addCopyTextToString(QString &str, uint32_t from = 0, void addCopyTextToString(QString &str, uint32_t from = 0,
uint32_t to = UINT32_MAX) const override; uint32_t to = UINT32_MAX) const override;
int getSelectionIndexCount() const override; int getSelectionIndexCount() const override;
void paint(QPainter &painter) override; void paint(QPainter &painter, const MessageColors &messageColors) override;
void paintAnimated(QPainter &painter, int yOffset) override; void paintAnimated(QPainter &painter, int yOffset) override;
int getMouseOverIndex(const QPoint &abs) const override; int getMouseOverIndex(const QPoint &abs) const override;
int getXFromIndex(int index) override; int getXFromIndex(int index) override;
@ -110,7 +112,7 @@ public:
const QSize &size, QColor color); const QSize &size, QColor color);
protected: protected:
void paint(QPainter &painter) override; void paint(QPainter &painter, const MessageColors &messageColors) override;
private: private:
QColor color_; QColor color_;
@ -125,7 +127,7 @@ public:
int padding); int padding);
protected: protected:
void paint(QPainter &painter) override; void paint(QPainter &painter, const MessageColors &messageColors) override;
private: private:
const QColor color_; const QColor color_;
@ -147,7 +149,7 @@ protected:
void addCopyTextToString(QString &str, uint32_t from = 0, void addCopyTextToString(QString &str, uint32_t from = 0,
uint32_t to = UINT32_MAX) const override; uint32_t to = UINT32_MAX) const override;
int getSelectionIndexCount() const override; int getSelectionIndexCount() const override;
void paint(QPainter &painter) override; void paint(QPainter &painter, const MessageColors &messageColors) override;
void paintAnimated(QPainter &painter, int yOffset) override; void paintAnimated(QPainter &painter, int yOffset) override;
int getMouseOverIndex(const QPoint &abs) const override; int getMouseOverIndex(const QPoint &abs) const override;
int getXFromIndex(int index) override; int getXFromIndex(int index) override;
@ -171,7 +173,7 @@ protected:
void addCopyTextToString(QString &str, uint32_t from = 0, void addCopyTextToString(QString &str, uint32_t from = 0,
uint32_t to = UINT32_MAX) const override; uint32_t to = UINT32_MAX) const override;
int getSelectionIndexCount() const override; int getSelectionIndexCount() const override;
void paint(QPainter &painter) override; void paint(QPainter &painter, const MessageColors &messageColors) override;
void paintAnimated(QPainter &painter, int yOffset) override; void paintAnimated(QPainter &painter, int yOffset) override;
int getMouseOverIndex(const QPoint &abs) const override; int getMouseOverIndex(const QPoint &abs) const override;
int getXFromIndex(int index) override; int getXFromIndex(int index) override;
@ -189,7 +191,7 @@ public:
float radius, float neededMargin); float radius, float neededMargin);
protected: protected:
void paint(QPainter &painter) override; void paint(QPainter &painter, const MessageColors &messageColors) override;
void paintAnimated(QPainter &painter, int yOffset) override; void paintAnimated(QPainter &painter, int yOffset) override;
int getMouseOverIndex(const QPoint &abs) const override; int getMouseOverIndex(const QPoint &abs) const override;
int getXFromIndex(int index) override; int getXFromIndex(int index) override;

View file

@ -11,12 +11,14 @@
#include "messages/Emote.hpp" #include "messages/Emote.hpp"
#include "messages/Image.hpp" #include "messages/Image.hpp"
#include "messages/layouts/MessageLayout.hpp" #include "messages/layouts/MessageLayout.hpp"
#include "messages/layouts/MessageLayoutContext.hpp"
#include "messages/layouts/MessageLayoutElement.hpp" #include "messages/layouts/MessageLayoutElement.hpp"
#include "messages/LimitedQueueSnapshot.hpp" #include "messages/LimitedQueueSnapshot.hpp"
#include "messages/Message.hpp" #include "messages/Message.hpp"
#include "messages/MessageBuilder.hpp" #include "messages/MessageBuilder.hpp"
#include "messages/MessageElement.hpp" #include "messages/MessageElement.hpp"
#include "messages/MessageThread.hpp" #include "messages/MessageThread.hpp"
#include "providers/colors/ColorProvider.hpp"
#include "providers/LinkResolver.hpp" #include "providers/LinkResolver.hpp"
#include "providers/twitch/TwitchAccount.hpp" #include "providers/twitch/TwitchAccount.hpp"
#include "providers/twitch/TwitchChannel.hpp" #include "providers/twitch/TwitchChannel.hpp"
@ -61,7 +63,6 @@
#include <functional> #include <functional>
#include <memory> #include <memory>
#define DRAW_WIDTH (this->width())
#define SELECTION_RESUME_SCROLLING_MSG_THRESHOLD 3 #define SELECTION_RESUME_SCROLLING_MSG_THRESHOLD 3
#define CHAT_HOVER_PAUSE_DURATION 1000 #define CHAT_HOVER_PAUSE_DURATION 1000
#define TOOLTIP_EMOTE_ENTRIES_LIMIT 7 #define TOOLTIP_EMOTE_ENTRIES_LIMIT 7
@ -201,6 +202,10 @@ ChannelView::ChannelView(BaseWidget *parent, Split *split, Context context,
auto curve = QEasingCurve(); auto curve = QEasingCurve();
curve.setCustomType(highlightEasingFunction); curve.setCustomType(highlightEasingFunction);
this->highlightAnimation_.setEasingCurve(curve); this->highlightAnimation_.setEasingCurve(curve);
this->messageColors_.applyTheme(getTheme());
this->messagePreferences_.connectSettings(getSettings(),
this->signalHolder_);
} }
void ChannelView::initializeLayout() void ChannelView::initializeLayout()
@ -378,6 +383,7 @@ void ChannelView::themeChangedEvent()
this->setupHighlightAnimationColors(); this->setupHighlightAnimationColors();
this->queueLayout(); this->queueLayout();
this->messageColors_.applyTheme(getTheme());
} }
void ChannelView::setupHighlightAnimationColors() void ChannelView::setupHighlightAnimationColors()
@ -1247,32 +1253,47 @@ void ChannelView::drawMessages(QPainter &painter)
return; return;
} }
int y = int(-(messagesSnapshot[start].get()->getHeight() *
(fmod(this->scrollBar_->getRelativeCurrentValue(), 1))));
MessageLayout *end = nullptr; MessageLayout *end = nullptr;
bool windowFocused = this->window() == QApplication::activeWindow();
auto app = getApp(); MessagePaintContext ctx = {
bool isMentions = this->underlyingChannel_ == app->twitch->mentionsChannel; .painter = painter,
.selection = this->selection_,
.colorProvider = ColorProvider::instance(),
.messageColors = this->messageColors_,
.preferences = this->messagePreferences_,
for (size_t i = start; i < messagesSnapshot.size(); ++i) .canvasWidth = this->width(),
.isWindowFocused = this->window() == QApplication::activeWindow(),
.isMentions =
this->underlyingChannel_ == getApp()->twitch->mentionsChannel,
.y = int(-(messagesSnapshot[start]->getHeight() *
(fmod(this->scrollBar_->getRelativeCurrentValue(), 1)))),
.messageIndex = start,
.isLastReadMessage = false,
};
bool showLastMessageIndicator = getSettings()->showLastMessageIndicator;
for (; ctx.messageIndex < messagesSnapshot.size(); ++ctx.messageIndex)
{ {
MessageLayout *layout = messagesSnapshot[i].get(); MessageLayout *layout = messagesSnapshot[ctx.messageIndex].get();
bool isLastMessage = false; if (showLastMessageIndicator)
if (getSettings()->showLastMessageIndicator)
{ {
isLastMessage = this->lastReadMessage_.get() == layout; ctx.isLastReadMessage = this->lastReadMessage_.get() == layout;
}
else
{
ctx.isLastReadMessage = false;
} }
layout->paint(painter, DRAW_WIDTH, y, i, this->selection_, layout->paint(ctx);
isLastMessage, windowFocused, isMentions);
if (this->highlightedMessage_ == layout) if (this->highlightedMessage_ == layout)
{ {
painter.fillRect( painter.fillRect(
0, y, layout->getWidth(), layout->getHeight(), 0, ctx.y, layout->getWidth(), layout->getHeight(),
this->highlightAnimation_.currentValue().value<QColor>()); this->highlightAnimation_.currentValue().value<QColor>());
if (this->highlightAnimation_.state() == QVariantAnimation::Stopped) if (this->highlightAnimation_.state() == QVariantAnimation::Stopped)
{ {
@ -1280,10 +1301,10 @@ void ChannelView::drawMessages(QPainter &painter)
} }
} }
y += layout->getHeight(); ctx.y += layout->getHeight();
end = layout; end = layout;
if (y > this->height()) if (ctx.y > this->height())
{ {
break; break;
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "common/FlagsEnum.hpp" #include "common/FlagsEnum.hpp"
#include "messages/layouts/MessageLayoutContext.hpp"
#include "messages/LimitedQueue.hpp" #include "messages/LimitedQueue.hpp"
#include "messages/LimitedQueueSnapshot.hpp" #include "messages/LimitedQueueSnapshot.hpp"
#include "messages/Selection.hpp" #include "messages/Selection.hpp"
@ -343,6 +344,9 @@ private:
std::unordered_set<std::shared_ptr<MessageLayout>> messagesOnScreen_; std::unordered_set<std::shared_ptr<MessageLayout>> messagesOnScreen_;
MessageColors messageColors_;
MessagePreferences messagePreferences_;
static constexpr int leftPadding = 8; static constexpr int leftPadding = 8;
static constexpr int scrollbarPadding = 8; static constexpr int scrollbarPadding = 8;