mirror-chatterino2/src/common/SignalVector.hpp

175 lines
4.3 KiB
C++
Raw Normal View History

#pragma once
#include <QStandardItemModel>
#include <QTimer>
#include <boost/noncopyable.hpp>
#include <pajlada/signals/signal.hpp>
#include <vector>
#include "debug/AssertInGuiThread.hpp"
namespace chatterino {
2020-02-23 17:10:49 +01:00
template <typename T>
struct SignalVectorItemEvent {
const T &item;
int index;
void *caller;
};
2020-02-23 17:10:49 +01:00
template <typename T>
class SignalVector : boost::noncopyable
{
public:
2020-02-23 17:10:49 +01:00
pajlada::Signals::Signal<SignalVectorItemEvent<T>> itemInserted;
pajlada::Signals::Signal<SignalVectorItemEvent<T>> itemRemoved;
pajlada::Signals::NoArgSignal delayedItemsChanged;
2020-02-23 17:10:49 +01:00
SignalVector()
: readOnly_(new std::vector<T>())
{
QObject::connect(&this->itemsChangedTimer_, &QTimer::timeout,
[this] { this->delayedItemsChanged.invoke(); });
this->itemsChangedTimer_.setInterval(100);
this->itemsChangedTimer_.setSingleShot(true);
}
2020-02-23 17:10:49 +01:00
SignalVector(std::function<bool(const T &, const T &)> &&compare)
: SignalVector()
{
itemCompare_ = std::move(compare);
}
2020-02-23 17:10:49 +01:00
virtual bool isSorted() const
{
return bool(this->itemCompare_);
}
2020-02-23 17:10:49 +01:00
/// A read-only version of the vector which can be used concurrently.
std::shared_ptr<const std::vector<T>> readOnly()
{
return this->readOnly_;
}
2020-02-23 17:10:49 +01:00
/// This may only be called from the GUI thread.
///
/// @param item
/// Item to be inserted.
/// @param proposedIndex
/// Index to insert at. `-1` will append at the end.
/// Will be ignored if the vector is sorted.
/// @param caller
/// Caller id which will be passed in the itemInserted and itemRemoved
/// signals.
int insert(const T &item, int index = -1, void *caller = nullptr)
{
assertInGuiThread();
2020-02-23 17:10:49 +01:00
if (this->isSorted())
{
2020-02-23 17:10:49 +01:00
auto it = std::lower_bound(this->items_.begin(), this->items_.end(),
item, this->itemCompare_);
index = it - this->items_.begin();
this->items_.insert(it, item);
}
2020-02-23 17:10:49 +01:00
else
{
2020-02-23 17:10:49 +01:00
if (index == -1)
index = this->items_.size();
else
assert(index >= 0 && index <= this->items_.size());
2020-02-23 17:10:49 +01:00
this->items_.insert(this->items_.begin() + index, item);
}
2020-02-23 17:10:49 +01:00
SignalVectorItemEvent<T> args{item, index, caller};
this->itemInserted.invoke(args);
this->itemsChanged_();
2020-02-23 17:10:49 +01:00
return index;
}
2020-02-23 17:10:49 +01:00
/// This may only be called from the GUI thread.
///
/// @param item
/// Item to be appended.
/// @param caller
/// Caller id which will be passed in the itemInserted and itemRemoved
/// signals.
int append(const T &item, void *caller = nullptr)
{
return this->insert(item, -1, caller);
2020-02-23 17:10:49 +01:00
}
2020-02-23 17:10:49 +01:00
void removeAt(int index, void *caller = nullptr)
{
2020-02-23 17:10:49 +01:00
assertInGuiThread();
assert(index >= 0 && index < int(this->items_.size()));
T item = this->items_[index];
this->items_.erase(this->items_.begin() + index);
SignalVectorItemEvent<T> args{item, index, caller};
this->itemRemoved.invoke(args);
this->itemsChanged_();
}
2020-02-23 17:45:59 +01:00
const std::vector<T> &raw() const
{
assertInGuiThread();
return this->items_;
}
2020-02-23 17:10:49 +01:00
[[deprecated]] std::vector<T> cloneVector()
{
2020-02-23 17:10:49 +01:00
return *this->readOnly();
}
2020-02-23 17:10:49 +01:00
// mirror vector functions
auto begin() const
{
assertInGuiThread();
2020-02-23 17:10:49 +01:00
return this->items_.begin();
}
2020-02-23 17:10:49 +01:00
auto end() const
{
assertInGuiThread();
2020-02-23 17:10:49 +01:00
return this->items_.end();
}
2020-02-23 17:10:49 +01:00
decltype(auto) operator[](size_t index)
{
2020-02-23 17:10:49 +01:00
assertInGuiThread();
return this->items[index];
}
2020-02-23 17:10:49 +01:00
auto empty()
{
assertInGuiThread();
2020-02-23 17:10:49 +01:00
return this->items_.empty();
}
2020-02-23 17:10:49 +01:00
private:
void itemsChanged_()
{
// emit delayed event
if (!this->itemsChangedTimer_.isActive())
{
2020-02-23 17:10:49 +01:00
this->itemsChangedTimer_.start();
}
2020-02-23 17:10:49 +01:00
// update concurrent version
this->readOnly_ = std::make_shared<const std::vector<T>>(this->items_);
}
2020-02-23 17:10:49 +01:00
std::vector<T> items_;
std::shared_ptr<const std::vector<T>> readOnly_;
QTimer itemsChangedTimer_;
std::function<bool(const T &, const T &)> itemCompare_;
};
} // namespace chatterino