#pragma once #include #include #include #include #include #include "debug/AssertInGuiThread.hpp" namespace chatterino { template struct SignalVectorItemArgs { const TVectorItem &item; int index; void *caller; }; template class ReadOnlySignalVector : boost::noncopyable { public: ReadOnlySignalVector() { QObject::connect(&this->itemsChangedTimer, &QTimer::timeout, [this] { this->delayedItemsChanged.invoke(); }); this->itemsChangedTimer.setInterval(100); this->itemsChangedTimer.setSingleShot(true); } virtual ~ReadOnlySignalVector() = default; pajlada::Signals::Signal> itemInserted; pajlada::Signals::Signal> itemRemoved; pajlada::Signals::NoArgSignal delayedItemsChanged; const std::vector &getVector() const { assertInGuiThread(); return this->vector; } void invokeDelayedItemsChanged() { assertInGuiThread(); if (!this->itemsChangedTimer.isActive()) { itemsChangedTimer.start(); } } virtual bool isSorted() const = 0; protected: std::vector vector; QTimer itemsChangedTimer; }; template class BaseSignalVector : public ReadOnlySignalVector { public: // returns the actual index of the inserted item virtual int insertItem(const TVectorItem &item, int proposedIndex = -1, void *caller = nullptr) = 0; void removeItem(int index, void *caller = nullptr) { assertInGuiThread(); assert(index >= 0 && index < this->vector.size()); TVectorItem item = this->vector[index]; this->vector.erase(this->vector.begin() + index); SignalVectorItemArgs args{item, index, caller}; this->itemRemoved.invoke(args); this->invokeDelayedItemsChanged(); } int appendItem(const TVectorItem &item, void *caller = nullptr) { return this->insertItem(item, -1, caller); } }; template class UnsortedSignalVector : public BaseSignalVector { public: virtual int insertItem(const TVectorItem &item, int index = -1, void *caller = nullptr) override { assertInGuiThread(); if (index == -1) { index = this->vector.size(); } else { assert(index >= 0 && index <= this->vector.size()); } this->vector.insert(this->vector.begin() + index, item); SignalVectorItemArgs args{item, index, caller}; this->itemInserted.invoke(args); this->invokeDelayedItemsChanged(); return index; } virtual bool isSorted() const override { return false; } }; template class SortedSignalVector : public BaseSignalVector { public: virtual int insertItem(const TVectorItem &item, int = -1, void *caller = nullptr) override { assertInGuiThread(); auto it = std::lower_bound(this->vector.begin(), this->vector.end(), item, Compare{}); int index = it - this->vector.begin(); this->vector.insert(it, item); SignalVectorItemArgs args{item, index, caller}; this->itemInserted.invoke(args); this->invokeDelayedItemsChanged(); return index; } virtual bool isSorted() const override { return true; } }; } // namespace chatterino