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