added template model/view magic for commands

This commit is contained in:
fourtf
2018-04-29 23:25:49 +02:00
parent e31dc09e91
commit 6bd787423d
14 changed files with 326 additions and 165 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
namespace chatterino {
namespace util {
void assertInGuiThread()
static void assertInGuiThread()
{
#ifdef _DEBUG
assert(QCoreApplication::instance()->thread() == QThread::currentThread());
+35 -13
View File
@@ -1,6 +1,8 @@
#pragma once
#include <QStandardItemModel>
#include <QTimer>
#include <boost/noncopyable.hpp>
#include <pajlada/signals/signal.hpp>
#include <vector>
@@ -10,18 +12,24 @@ namespace chatterino {
namespace util {
template <typename TVectorItem>
class ReadOnlySignalVector
class ReadOnlySignalVector : boost::noncopyable
{
public:
ReadOnlySignalVector()
{
QObject::connect(&this->itemsChangedTimer, &QTimer::timeout,
[this] { this->delayedItemsChanged.invoke(); });
}
virtual ~ReadOnlySignalVector() = default;
struct ItemInsertedArgs {
struct ItemArgs {
const TVectorItem &item;
int index;
void *caller;
};
pajlada::Signals::Signal<ItemInsertedArgs> itemInserted;
pajlada::Signals::Signal<int> itemRemoved;
pajlada::Signals::Signal<ItemArgs> itemInserted;
pajlada::Signals::Signal<ItemArgs> itemRemoved;
pajlada::Signals::NoArgSignal delayedItemsChanged;
const std::vector<TVectorItem> &getVector() const
@@ -31,42 +39,56 @@ public:
return this->vector;
}
void invokeDelayedItemsChanged()
{
util::assertInGuiThread();
if (!this->itemsChangedTimer.isActive()) {
itemsChangedTimer.start();
}
}
protected:
std::vector<TVectorItem> vector;
QTimer itemsChangedTimer;
};
template <typename TVectorItem>
class BaseSignalVector : public ReadOnlySignalVector<TVectorItem>
{
public:
void removeItem(int index)
virtual void appendItem(const TVectorItem &item, void *caller = 0) = 0;
void removeItem(int index, void *caller = 0)
{
util::assertInGuiThread();
assert(index >= 0 && index < this->vector.size());
TVectorItem item = this->vector[index];
this->vector.erase(this->vector.begin() + index);
this->itemRemoved.invoke(index);
ItemArgs args{item, args, caller};
this->itemRemoved.invoke(args);
}
};
template <typename TVectorItem>
class SignalVector2 : public BaseSignalVector<TVectorItem>
class UnsortedSignalVector : public BaseSignalVector<TVectorItem>
{
public:
void insertItem(const TVectorItem &item, int index)
void insertItem(const TVectorItem &item, int index, void *caller = 0)
{
util::assertInGuiThread();
assert(index >= 0 && index <= this->vector.size());
this->vector.insert(this->vector.begin() + index, item);
ItemInsertedArgs args{item, index};
ItemArgs args{item, index, caller};
this->itemInserted.invoke(args);
}
void appendItem(const TVectorItem &item)
virtual void appendItem(const TVectorItem &item, void *caller = 0) override
{
this->insertItem(item, this->vector.size());
this->insertItem(item, this->vector.size(), caller);
}
};
@@ -74,14 +96,14 @@ template <typename TVectorItem>
class SortedSignalVector : public BaseSignalVector<TVectorItem>
{
public:
void addItem(const TVectorItem &item)
virtual void appendItem(const TVectorItem &item, void *caller = 0) override
{
util::assertInGuiThread();
int index = this->vector.insert(
std::lower_bound(this->vector.begin(), this->vector.end(), item), item) -
this->vector.begin();
ItemInsertedArgs args{item, index};
ItemArgs args{item, index, caller};
this->itemInserted.invoke(args);
}
};
+1
View File
@@ -0,0 +1 @@
#include "signalvectormodel.hpp"
+112
View File
@@ -0,0 +1,112 @@
#pragma once
#include <QAbstractTableModel>
#include <QStandardItem>
#include <util/signalvector2.hpp>
#include <pajlada/signals/signalholder.hpp>
namespace chatterino {
namespace util {
template <typename TVectorItem>
class SignalVectorModel : public QAbstractTableModel, pajlada::Signals::SignalHolder
{
public:
SignalVectorModel(util::BaseSignalVector<TVectorItem> *vec, int columnCount,
QObject *parent = nullptr)
: QAbstractTableModel(parent)
, _columnCount(columnCount)
{
this->managedConnect(vec->itemInserted, [this](auto args) {
std::vector<QStandardItem *> items;
for (int i = 0; i < this->_columnCount; i++) {
items.push_back(new QStandardItem());
}
int row = this->prepareInsert(args.item, args.index, items);
assert(row >= 0 && row <= this->rows.size());
// insert row
this->beginInsertRows(QModelIndex(), row, row);
this->rows.insert(this->rows.begin() + row, Row(items));
this->endInsertRows();
});
this->managedConnect(vec->itemRemoved, [this](auto args) {
int row = this->prepareRemove(args.item, args.index);
assert(row >= 0 && row <= this->rows.size());
// remove row
this->beginRemoveRows(QModelIndex(), row, row);
for (QStandardItem *item : this->rows[row].items) {
delete item;
}
this->rows.erase(this->rows.begin() + row);
this->endRemoveRows();
});
}
virtual ~SignalVectorModel()
{
for (Row &row : this->rows) {
for (QStandardItem *item : row.items) {
delete item;
}
}
}
int rowCount(const QModelIndex &parent) const
{
return this->rows.size();
}
int columnCount(const QModelIndex &parent) const
{
return this->_columnCount;
}
QVariant data(const QModelIndex &index, int role) const
{
int row = index.row(), column = index.column();
assert(row >= 0 && row < this->rows.size() && column >= 0 && column < this->_columnCount);
return rows[row].items[column]->data(role);
}
bool setData(const QModelIndex &index, const QVariant &value, int role)
{
this->rows[index.row()].items[index.column()]->setData(value, role);
return true;
}
QStandardItem *getItem(int row, int column)
{
assert(row >= 0 && row < this->rows.size() && column >= 0 && column < this->_columnCount);
return rows[row][column];
}
protected:
virtual int prepareInsert(const TVectorItem &item, int index,
std::vector<QStandardItem *> &rowToAdd) = 0;
virtual int prepareRemove(const TVectorItem &item, int index) = 0;
private:
struct Row {
std::vector<QStandardItem *> items;
bool isCustomRow;
Row(const std::vector<QStandardItem *> _items, bool _isCustomRow = false)
: items(_items)
, isCustomRow(_isCustomRow)
{
}
};
std::vector<Row> rows;
int _columnCount;
};
} // namespace util
} // namespace chatterino