diff --git a/src/messages/limitedqueue.hpp b/src/messages/limitedqueue.hpp index 44e5b8c6..70f60ac2 100644 --- a/src/messages/limitedqueue.hpp +++ b/src/messages/limitedqueue.hpp @@ -132,7 +132,7 @@ public: return acceptedItems; } - // replaces a single item, return true if successful + // replace an single item, return index if successful, -1 if unsuccessful int replaceItem(const T &item, const T &replacement) { std::lock_guard lock(this->mutex); @@ -166,6 +166,38 @@ public: return -1; } + // replace an item at index, return true if worked + bool replaceItem(size_t index, const T &replacement) + { + std::lock_guard lock(this->mutex); + + int x = 0; + + for (size_t i = 0; i < this->chunks->size(); i++) { + Chunk &chunk = this->chunks->at(i); + + size_t start = i == 0 ? this->firstChunkOffset : 0; + size_t end = i == chunk->size() - 1 ? this->lastChunkEnd : chunk->size(); + + for (size_t j = start; j < end; j++) { + if (x == index) { + Chunk newChunk = std::make_shared>(); + newChunk->resize(chunk->size()); + + for (size_t k = 0; k < chunk->size(); k++) { + newChunk->at(k) = chunk->at(k); + } + + newChunk->at(j) = replacement; + this->chunks->at(i) = newChunk; + + return x; + } + x++; + } + } + } + // void insertAfter(const std::vector &items, const T &index) messages::LimitedQueueSnapshot getSnapshot() diff --git a/src/messages/message.cpp b/src/messages/message.cpp index 26b01750..44e76202 100644 --- a/src/messages/message.cpp +++ b/src/messages/message.cpp @@ -13,6 +13,8 @@ #include #include +typedef chatterino::widgets::ScrollbarHighlight SBHighlight; + namespace chatterino { namespace messages { @@ -128,6 +130,14 @@ void Message::updateContent() const this->content = _content; } +SBHighlight Message::getScrollBarHighlight() const +{ + if (this->getFlags() & Message::Highlighted) { + return SBHighlight(SBHighlight::Highlight); + } + return SBHighlight(); +} + namespace { void AddCurrentTimestamp(Message *message) diff --git a/src/messages/message.hpp b/src/messages/message.hpp index aa520ccf..bc26a815 100644 --- a/src/messages/message.hpp +++ b/src/messages/message.hpp @@ -1,6 +1,7 @@ #pragma once #include "messages/word.hpp" +#include "widgets/helper/scrollbarhighlight.hpp" #include #include @@ -23,6 +24,7 @@ public: None = 0, System = (1 << 1), Timeout = (1 << 2), + Highlighted = (1 << 3), }; bool containsHighlightedPhrase() const; @@ -44,6 +46,7 @@ public: bool getDisableCompactEmotes() const; void setDisableCompactEmotes(bool value); void updateContent() const; + widgets::ScrollbarHighlight getScrollBarHighlight() const; QString loginName; QString displayName; diff --git a/src/twitch/twitchmessagebuilder.cpp b/src/twitch/twitchmessagebuilder.cpp index 5ea5793a..1a754bbf 100644 --- a/src/twitch/twitchmessagebuilder.cpp +++ b/src/twitch/twitchmessagebuilder.cpp @@ -460,6 +460,10 @@ void TwitchMessageBuilder::parseHighlights() QApplication::alert(singletons::WindowManager::getInstance().getMainWindow().window(), 2500); } + + if (doHighlight) { + this->message->addFlags(Message::Highlighted); + } } } diff --git a/src/widgets/helper/channelview.cpp b/src/widgets/helper/channelview.cpp index 11a33484..bf7422af 100644 --- a/src/widgets/helper/channelview.cpp +++ b/src/widgets/helper/channelview.cpp @@ -233,7 +233,7 @@ void ChannelView::updateGifEmotes() } } -ScrollBar &ChannelView::getScrollBar() +Scrollbar &ChannelView::getScrollBar() { return this->scrollBar; } @@ -395,6 +395,8 @@ void ChannelView::setChannel(std::shared_ptr newChannel) this->highlightedMessageReceived.invoke(); } + this->scrollBar.addHighlight(message->getScrollBarHighlight()); + this->messageWasAdded = true; this->layoutMessages(); }); @@ -416,6 +418,14 @@ void ChannelView::setChannel(std::shared_ptr newChannel) } } + std::vector highlights; + highlights.reserve(messages.size()); + for (int i = 0; i < messages.size(); i++) { + highlights.push_back(messages.at(i)->getScrollBarHighlight()); + } + + this->scrollBar.addHighlightsAtStart(highlights); + this->messageWasAdded = true; this->layoutMessages(); }); @@ -436,6 +446,8 @@ void ChannelView::setChannel(std::shared_ptr newChannel) newChannel->messageReplaced.connect([this](size_t index, SharedMessage replacement) { SharedMessageRef newItem(new MessageRef(replacement)); + this->scrollBar.replaceHighlight(index, replacement->getScrollBarHighlight()); + this->messages.replaceItem(this->messages.getSnapshot()[index], newItem); this->layoutMessages(); }); diff --git a/src/widgets/helper/channelview.hpp b/src/widgets/helper/channelview.hpp index 3ea1f0eb..f0c29663 100644 --- a/src/widgets/helper/channelview.hpp +++ b/src/widgets/helper/channelview.hpp @@ -33,7 +33,7 @@ public: void updateGifEmotes(); void queueUpdate(); - ScrollBar &getScrollBar(); + Scrollbar &getScrollBar(); QString getSelectedText(); bool hasSelection(); void clearSelection(); @@ -94,7 +94,7 @@ private: std::vector gifEmotes; - ScrollBar scrollBar; + Scrollbar scrollBar; RippleEffectLabel *goToBottom; // This variable can be used to decide whether or not we should render the "Show latest diff --git a/src/widgets/helper/scrollbarhighlight.cpp b/src/widgets/helper/scrollbarhighlight.cpp index fa00987d..239273ec 100644 --- a/src/widgets/helper/scrollbarhighlight.cpp +++ b/src/widgets/helper/scrollbarhighlight.cpp @@ -5,16 +5,32 @@ namespace chatterino { namespace widgets { -ScrollBarHighlight::ScrollBarHighlight(double _position, int _colorIndex, ScrollBar *parent, - Style _style, QString _tag) - : themeManager(parent->themeManager) - , position(_position) - // , colorIndex(std::max(0, std::min(this->themeManager.HighlightColorCount, _colorIndex))) - , colorIndex(0) - , style(_style) - , tag(_tag) +ScrollbarHighlight::ScrollbarHighlight() + : color(Color::Highlight) + , style(Style::None) { } +ScrollbarHighlight::ScrollbarHighlight(Color _color, Style _style) + : color(_color) + , style(_style) +{ +} + +ScrollbarHighlight::Color ScrollbarHighlight::getColor() const +{ + return this->color; +} + +ScrollbarHighlight::Style ScrollbarHighlight::getStyle() const +{ + return this->style; +} + +bool ScrollbarHighlight::isNull() const +{ + return this->style == None; +} + } // namespace widgets } // namespace chatterino diff --git a/src/widgets/helper/scrollbarhighlight.hpp b/src/widgets/helper/scrollbarhighlight.hpp index 1eb972de..a387617f 100644 --- a/src/widgets/helper/scrollbarhighlight.hpp +++ b/src/widgets/helper/scrollbarhighlight.hpp @@ -3,51 +3,23 @@ #include "QString" namespace chatterino { -namespace singletons { -class ThemeManager; -} - namespace widgets { - -class ScrollBar; - -class ScrollBarHighlight +class ScrollbarHighlight { public: - enum Style { Default, Left, Right, SingleLine }; + enum Style : char { None, Default, Line }; + enum Color : char { Highlight }; - ScrollBarHighlight(double _position, int _colorIndex, ScrollBar *parent, Style _style = Default, - QString _tag = ""); + ScrollbarHighlight(); + ScrollbarHighlight(Color _color, Style _style = Default); - singletons::ThemeManager &themeManager; - - double getPosition() - { - return this->position; - } - - int getColorIndex() - { - return this->colorIndex; - } - - Style getStyle() - { - return this->style; - } - - QString getTag() - { - return this->tag; - } - - ScrollBarHighlight *next = nullptr; + Color getColor() const; + Style getStyle() const; + bool isNull() const; private: - double position; - int colorIndex; + Color color; Style style; - QString tag; }; } // namespace widgets diff --git a/src/widgets/scrollbar.cpp b/src/widgets/scrollbar.cpp index fd0d50b8..5169d294 100644 --- a/src/widgets/scrollbar.cpp +++ b/src/widgets/scrollbar.cpp @@ -13,10 +13,9 @@ namespace chatterino { namespace widgets { -ScrollBar::ScrollBar(ChannelView *parent) +Scrollbar::Scrollbar(ChannelView *parent) : BaseWidget(parent) , currentValueAnimation(this, "currentValue") - , highlights(nullptr) , smoothScrollingSetting(singletons::SettingManager::getInstance().enableSmoothScrolling) { resize((int)(16 * this->getDpiMultiplier()), 100); @@ -37,97 +36,61 @@ ScrollBar::ScrollBar(ChannelView *parent) timer->start(10); } -ScrollBar::~ScrollBar() +void Scrollbar::addHighlight(ScrollbarHighlight highlight) { - auto highlight = this->highlights; - - while (highlight != nullptr) { - auto tmp = highlight->next; - delete highlight; - highlight = tmp; - } + ScrollbarHighlight deleted; + this->highlights.pushBack(highlight, deleted); } -void ScrollBar::removeHighlightsWhere(std::function func) +void Scrollbar::addHighlightsAtStart(const std::vector &_highlights) { - this->mutex.lock(); - - ScrollBarHighlight *last = nullptr; - ScrollBarHighlight *current = this->highlights; - - while (current != nullptr) { - if (func(*current)) { - if (last == nullptr) { - this->highlights = current->next; - } else { - last->next = current->next; - } - - auto oldCurrent = current; - - current = current->next; - last = current; - - delete oldCurrent; - } - } - - this->mutex.unlock(); + this->highlights.pushFront(_highlights); } -void ScrollBar::addHighlight(ScrollBarHighlight *highlight) +void Scrollbar::replaceHighlight(size_t index, ScrollbarHighlight replacement) { - this->mutex.lock(); - - if (this->highlights == nullptr) { - this->highlights = highlight; - } else { - highlight->next = this->highlights->next; - this->highlights->next = highlight; - } - - this->mutex.unlock(); + this->highlights.replaceItem(index, replacement); } -void ScrollBar::scrollToBottom(bool animate) +void Scrollbar::scrollToBottom(bool animate) { this->setDesiredValue(this->maximum - this->getLargeChange(), animate); } -bool ScrollBar::isAtBottom() const +bool Scrollbar::isAtBottom() const { return this->atBottom; } -void ScrollBar::setMaximum(qreal value) +void Scrollbar::setMaximum(qreal value) { this->maximum = value; updateScroll(); } -void ScrollBar::setMinimum(qreal value) +void Scrollbar::setMinimum(qreal value) { this->minimum = value; updateScroll(); } -void ScrollBar::setLargeChange(qreal value) +void Scrollbar::setLargeChange(qreal value) { this->largeChange = value; updateScroll(); } -void ScrollBar::setSmallChange(qreal value) +void Scrollbar::setSmallChange(qreal value) { this->smallChange = value; updateScroll(); } -void ScrollBar::setDesiredValue(qreal value, bool animated) +void Scrollbar::setDesiredValue(qreal value, bool animated) { animated &= this->smoothScrollingSetting.getValue(); value = std::max(this->minimum, std::min(this->maximum - this->largeChange, value)); @@ -160,37 +123,37 @@ void ScrollBar::setDesiredValue(qreal value, bool animated) this->desiredValue = value; } -qreal ScrollBar::getMaximum() const +qreal Scrollbar::getMaximum() const { return this->maximum; } -qreal ScrollBar::getMinimum() const +qreal Scrollbar::getMinimum() const { return this->minimum; } -qreal ScrollBar::getLargeChange() const +qreal Scrollbar::getLargeChange() const { return this->largeChange; } -qreal ScrollBar::getSmallChange() const +qreal Scrollbar::getSmallChange() const { return this->smallChange; } -qreal ScrollBar::getDesiredValue() const +qreal Scrollbar::getDesiredValue() const { return this->desiredValue + this->smoothScrollingOffset; } -qreal ScrollBar::getCurrentValue() const +qreal Scrollbar::getCurrentValue() const { return this->currentValue; } -void ScrollBar::offset(qreal value) +void Scrollbar::offset(qreal value) { if (this->currentValueAnimation.state() == QPropertyAnimation::Running) { this->smoothScrollingOffset += value; @@ -199,12 +162,12 @@ void ScrollBar::offset(qreal value) } } -boost::signals2::signal &ScrollBar::getCurrentValueChanged() +boost::signals2::signal &Scrollbar::getCurrentValueChanged() { return this->currentValueChanged; } -void ScrollBar::setCurrentValue(qreal value) +void Scrollbar::setCurrentValue(qreal value) { value = std::max(this->minimum, std::min(this->maximum - this->largeChange, value + this->smoothScrollingOffset)); @@ -219,7 +182,7 @@ void ScrollBar::setCurrentValue(qreal value) } } -void ScrollBar::printCurrentState(const QString &prefix) const +void Scrollbar::printCurrentState(const QString &prefix) const { qDebug() << prefix // << "Current value: " << this->getCurrentValue() // @@ -228,7 +191,7 @@ void ScrollBar::printCurrentState(const QString &prefix) const << ". Large change: " << this->getLargeChange(); // } -void ScrollBar::paintEvent(QPaintEvent *) +void Scrollbar::paintEvent(QPaintEvent *) { bool mouseOver = this->mouseOverIndex != -1; int xOffset = mouseOver ? 0 : width() - (int)(4 * this->getDpiMultiplier()); @@ -253,23 +216,37 @@ void ScrollBar::paintEvent(QPaintEvent *) painter.fillRect(this->thumbRect, this->themeManager.scrollbars.thumb); } - // ScrollBarHighlight *highlight = highlights; + // draw highlights + auto snapshot = this->highlights.getSnapshot(); + int snapshotLength = snapshot.getLength(); + int w = this->width(); + float y = 0; + float dY = (this->height() - MIN_THUMB_HEIGHT) / snapshotLength; + int highlightHeight = std::ceilf(dY); - this->mutex.lock(); + for (int i = 0; i < snapshotLength; i++) { + ScrollbarHighlight const &highlight = snapshot[i]; - // do { - // painter.fillRect(); - // } while ((highlight = highlight->next()) != nullptr); + if (!highlight.isNull()) { + if (highlight.getStyle() == ScrollbarHighlight::Default) { + painter.fillRect(w / 8 * 3, (int)y, w / 4, highlightHeight, + this->themeManager.tabs.selected.backgrounds.regular.color()); + } else { + painter.fillRect(0, (int)y, w, 1, + this->themeManager.tabs.selected.backgrounds.regular.color()); + } + } - this->mutex.unlock(); + y += dY; + } } -void ScrollBar::resizeEvent(QResizeEvent *) +void Scrollbar::resizeEvent(QResizeEvent *) { this->resize((int)(16 * this->getDpiMultiplier()), this->height()); } -void ScrollBar::mouseMoveEvent(QMouseEvent *event) +void Scrollbar::mouseMoveEvent(QMouseEvent *event) { if (this->mouseDownIndex == -1) { int y = event->pos().y(); @@ -300,7 +277,7 @@ void ScrollBar::mouseMoveEvent(QMouseEvent *event) this->lastMousePosition = event->pos(); } -void ScrollBar::mousePressEvent(QMouseEvent *event) +void Scrollbar::mousePressEvent(QMouseEvent *event) { int y = event->pos().y(); @@ -317,7 +294,7 @@ void ScrollBar::mousePressEvent(QMouseEvent *event) } } -void ScrollBar::mouseReleaseEvent(QMouseEvent *event) +void Scrollbar::mouseReleaseEvent(QMouseEvent *event) { int y = event->pos().y(); @@ -345,14 +322,14 @@ void ScrollBar::mouseReleaseEvent(QMouseEvent *event) update(); } -void ScrollBar::leaveEvent(QEvent *) +void Scrollbar::leaveEvent(QEvent *) { this->mouseOverIndex = -1; update(); } -void ScrollBar::updateScroll() +void Scrollbar::updateScroll() { this->trackHeight = height() - this->buttonHeight - this->buttonHeight - MIN_THUMB_HEIGHT - 1; diff --git a/src/widgets/scrollbar.hpp b/src/widgets/scrollbar.hpp index c5151711..478b40a3 100644 --- a/src/widgets/scrollbar.hpp +++ b/src/widgets/scrollbar.hpp @@ -1,5 +1,6 @@ #pragma once +#include "messages/limitedqueue.hpp" #include "singletons/settingsmanager.hpp" #include "widgets/basewidget.hpp" #include "widgets/helper/scrollbarhighlight.hpp" @@ -15,21 +16,18 @@ namespace widgets { class ChannelView; -class ScrollBar : public BaseWidget +class Scrollbar : public BaseWidget { Q_OBJECT public: - ScrollBar(ChannelView *parent = 0); - ~ScrollBar(); + Scrollbar(ChannelView *parent = 0); - void removeHighlightsWhere(std::function func); - void addHighlight(ScrollBarHighlight *highlight); - - Q_PROPERTY(qreal desiredValue READ getDesiredValue WRITE setDesiredValue) + void addHighlight(ScrollbarHighlight highlight); + void addHighlightsAtStart(const std::vector &highlights); + void replaceHighlight(size_t index, ScrollbarHighlight replacement); void scrollToBottom(bool animate = false); - bool isAtBottom() const; void setMaximum(qreal value); @@ -50,6 +48,8 @@ public: void printCurrentState(const QString &prefix = QString()) const; + Q_PROPERTY(qreal desiredValue READ getDesiredValue WRITE setDesiredValue) + protected: void paintEvent(QPaintEvent *) override; void resizeEvent(QResizeEvent *) override; @@ -65,7 +65,7 @@ private: QPropertyAnimation currentValueAnimation; - ScrollBarHighlight *highlights; + messages::LimitedQueue highlights; bool atBottom = false; @@ -73,7 +73,6 @@ private: int mouseDownIndex = -1; QPoint lastMousePosition; - // int buttonHeight = 16; int buttonHeight = 0; int trackHeight = 100;