diff --git a/CHANGELOG.md b/CHANGELOG.md index e6159721..151ac959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,7 +99,7 @@ - Dev: BREAKING: Replace custom `import()` with normal Lua `require()`. (#5014) - Dev: Fixed most compiler warnings. (#5028) - Dev: Added the ability to show `ChannelView`s without a `Split`. (#4747) -- Dev: Channels without any animated elements on screen will skip updates from the GIF timer. (#5042) +- Dev: Channels without any animated elements on screen will skip updates from the GIF timer. (#5042, #5043) ## 2.4.6 diff --git a/src/widgets/helper/ChannelView.cpp b/src/widgets/helper/ChannelView.cpp index fa5e1bb9..6978e361 100644 --- a/src/widgets/helper/ChannelView.cpp +++ b/src/widgets/helper/ChannelView.cpp @@ -350,6 +350,10 @@ ChannelView::ChannelView(InternalCtor /*tag*/, QWidget *parent, Split *split, auto curve = QEasingCurve(); curve.setCustomType(highlightEasingFunction); this->highlightAnimation_.setEasingCurve(curve); + QObject::connect(&this->highlightAnimation_, + &QVariantAnimation::valueChanged, this, [this] { + this->queueUpdate(); + }); this->messageColors_.applyTheme(getTheme()); this->messagePreferences_.connectSettings(getSettings(), @@ -404,13 +408,13 @@ void ChannelView::initializeSignals() }, this->signalHolder_); - this->signalHolder_.managedConnect(getApp()->windows->gifRepaintRequested, - [&] { - if (this->anyAnimationShown_) - { - this->queueUpdate(); - } - }); + this->signalHolder_.managedConnect( + getApp()->windows->gifRepaintRequested, [&] { + if (!this->animationArea_.isEmpty()) + { + this->queueUpdate(this->animationArea_); + } + }); this->signalHolder_.managedConnect( getApp()->windows->layoutRequested, [&](Channel *channel) { @@ -580,6 +584,11 @@ void ChannelView::queueUpdate() this->update(); } +void ChannelView::queueUpdate(const QRect &area) +{ + this->update(area); +} + void ChannelView::queueLayout() { if (this->isVisible()) @@ -1418,7 +1427,7 @@ void ChannelView::scrollToMessageLayout(MessageLayout *layout, } } -void ChannelView::paintEvent(QPaintEvent * /*event*/) +void ChannelView::paintEvent(QPaintEvent *event) { // BenchmarkGuard benchmark("paint"); @@ -1427,7 +1436,7 @@ void ChannelView::paintEvent(QPaintEvent * /*event*/) painter.fillRect(rect(), this->theme->splits.background); // draw messages - this->drawMessages(painter); + this->drawMessages(painter, event->rect()); // draw paused sign if (this->paused()) @@ -1441,7 +1450,7 @@ void ChannelView::paintEvent(QPaintEvent * /*event*/) // if overlays is false then it draws the message, if true then it draws things // such as the grey overlay when a message is disabled -void ChannelView::drawMessages(QPainter &painter) +void ChannelView::drawMessages(QPainter &painter, const QRect &area) { auto &messagesSnapshot = this->getMessagesSnapshot(); @@ -1474,7 +1483,10 @@ void ChannelView::drawMessages(QPainter &painter) }; bool showLastMessageIndicator = getSettings()->showLastMessageIndicator; - bool anyAnimation = false; + QRect animationArea; + auto areaContainsY = [&area](auto y) { + return y >= area.y() && y < area.y() + area.height(); + }; for (; ctx.messageIndex < messagesSnapshot.size(); ++ctx.messageIndex) { @@ -1489,16 +1501,34 @@ void ChannelView::drawMessages(QPainter &painter) ctx.isLastReadMessage = false; } - anyAnimation |= layout->paint(ctx).hasAnimatedElements; - - if (this->highlightedMessage_ == layout) + if (areaContainsY(ctx.y) || areaContainsY(ctx.y + layout->getHeight())) { - painter.fillRect( - 0, ctx.y, layout->getWidth(), layout->getHeight(), - this->highlightAnimation_.currentValue().value()); - if (this->highlightAnimation_.state() == QVariantAnimation::Stopped) + auto paintResult = layout->paint(ctx); + if (paintResult.hasAnimatedElements) { - this->highlightedMessage_ = nullptr; + if (animationArea.isNull()) + { + animationArea = QRect{0, ctx.y, layout->getWidth(), + layout->getHeight()}; + } + else + { + animationArea.setBottom(ctx.y + layout->getHeight()); + animationArea.setWidth( + std::max(layout->getWidth(), animationArea.width())); + } + } + + if (this->highlightedMessage_ == layout) + { + painter.fillRect( + 0, ctx.y, layout->getWidth(), layout->getHeight(), + this->highlightAnimation_.currentValue().value()); + if (this->highlightAnimation_.state() == + QVariantAnimation::Stopped) + { + this->highlightedMessage_ = nullptr; + } } } @@ -1510,7 +1540,23 @@ void ChannelView::drawMessages(QPainter &painter) break; } } - this->anyAnimationShown_ = anyAnimation; + + // Only update on a full repaint as some messages with animated elements + // might get left out in partial repaints. + // This happens for example when hovering over the go-to-bottom button. + if (this->height() <= area.height()) + { + this->animationArea_ = animationArea; + } +#ifdef FOURTF + else + { + // shows the updated area on partial repaints + painter.setPen(Qt::red); + painter.drawRect(area.x(), area.y(), area.width() - 1, + area.height() - 1); + } +#endif if (end == nullptr) { diff --git a/src/widgets/helper/ChannelView.hpp b/src/widgets/helper/ChannelView.hpp index 522f3353..f1956cf9 100644 --- a/src/widgets/helper/ChannelView.hpp +++ b/src/widgets/helper/ChannelView.hpp @@ -96,6 +96,7 @@ public: size_t messagesLimit = 1000); void queueUpdate(); + void queueUpdate(const QRect &area); Scrollbar &getScrollBar(); QString getSelectedText(); @@ -232,7 +233,7 @@ private: void updateScrollbar(const LimitedQueueSnapshot &messages, bool causedByScrollbar, bool causedByShow); - void drawMessages(QPainter &painter); + void drawMessages(QPainter &painter, const QRect &area); void setSelection(const SelectionItem &start, const SelectionItem &end); void setSelection(const Selection &newSelection); void selectWholeMessage(MessageLayout *layout, int &messageIndex); @@ -273,8 +274,9 @@ private: bool lastMessageHasAlternateBackground_ = false; bool lastMessageHasAlternateBackgroundReverse_ = true; - /// Tracks if this view has painted any animated element in the last #paintEvent(). - bool anyAnimationShown_ = false; + /// Tracks the area of animated elements in the last full repaint. + /// If this is empty (QRect::isEmpty()), no animated element is shown. + QRect animationArea_; bool pausable_ = false; QTimer pauseTimer_;