feat(StatusQ.Models): Adding SingleModelItemData helper component (#14891)
SingleModelItemData is a generic component that can provide a live object extract from an arbitrary QAbstractItemModel*
This commit is contained in:
parent
f1308f3b28
commit
4e81f8f220
|
@ -0,0 +1,154 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ 0.1
|
||||
import StatusQ.Core.Utils 0.1
|
||||
|
||||
import Models 1.0
|
||||
import Storybook 1.0
|
||||
|
||||
Control {
|
||||
id: root
|
||||
|
||||
UsersModel {
|
||||
id: usersModel
|
||||
}
|
||||
|
||||
ModelEntry {
|
||||
id: itemData
|
||||
sourceModel: usersModel
|
||||
key: "pubKey"
|
||||
value: pubKeySelector.currentText
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
anchors.fill: parent
|
||||
Pane {
|
||||
Layout.fillWidth: true
|
||||
background: Rectangle {
|
||||
border.width: 1
|
||||
border.color: "lightgray"
|
||||
}
|
||||
contentItem: ColumnLayout {
|
||||
Label {
|
||||
text: "User with pubKey " + itemData.value
|
||||
font.bold: true
|
||||
}
|
||||
Label {
|
||||
text: "Data available: " + itemData.available
|
||||
font.bold: true
|
||||
}
|
||||
Label {
|
||||
text: "Keys: " + itemData.roles
|
||||
font.bold: true
|
||||
}
|
||||
Label {
|
||||
text: "Item removed from model: " + itemData.itemRemovedFromModel
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.fillWidth: true
|
||||
active: itemData.available
|
||||
sourceComponent: Pane {
|
||||
background: Rectangle {
|
||||
border.width: 1
|
||||
border.color: "lightgray"
|
||||
}
|
||||
contentItem: ColumnLayout {
|
||||
Repeater {
|
||||
model: itemData.roles
|
||||
delegate: Label {
|
||||
text: modelData + ": " + itemData.item[modelData]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GenericListView {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
model: usersModel
|
||||
insetComponent: RowLayout {
|
||||
Button {
|
||||
height: 20
|
||||
font.pixelSize: 11
|
||||
text: "remove"
|
||||
highlighted: model.index === itemData.row
|
||||
|
||||
onClicked: {
|
||||
usersModel.remove(model.index)
|
||||
}
|
||||
}
|
||||
Button {
|
||||
height: 20
|
||||
font.pixelSize: 11
|
||||
text: "edit"
|
||||
highlighted: model.index === itemData.row
|
||||
|
||||
onClicked: {
|
||||
menu.row = model.index
|
||||
menu.popup()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Pane {
|
||||
contentItem: RowLayout {
|
||||
ComboBox {
|
||||
id: pubKeySelector
|
||||
model: [...ModelUtils.modelToFlatArray(usersModel, "pubKey"), "none"]
|
||||
}
|
||||
CheckBox {
|
||||
text: "Cache item on removal"
|
||||
checked: itemData.cacheOnRemoval
|
||||
onCheckedChanged: {
|
||||
itemData.cacheOnRemoval = checked
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: menu
|
||||
|
||||
property int row: -1
|
||||
|
||||
readonly property var modelItem: usersModel.get(row)
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
Label {
|
||||
text: "Edit user"
|
||||
font.bold: true
|
||||
}
|
||||
TextField {
|
||||
id: pubKeyField
|
||||
placeholderText: "pubKey"
|
||||
enabled: !!menu.modelItem
|
||||
text: !!menu.modelItem ? menu.modelItem.pubKey : ""
|
||||
onAccepted: usersModel.setProperty(menu.row, "pubKey", pubKeyField.text)
|
||||
}
|
||||
TextField {
|
||||
id: displayNameField
|
||||
placeholderText: "displayName"
|
||||
enabled: !!menu.modelItem
|
||||
text: !!menu.modelItem ? menu.modelItem.displayName : ""
|
||||
onAccepted: usersModel.setProperty(menu.row, "displayName", displayNameField.text)
|
||||
}
|
||||
TextField {
|
||||
id: ensNameField
|
||||
placeholderText: "ensName"
|
||||
enabled: !!menu.modelItem
|
||||
text: !!menu.modelItem ? menu.modelItem.ensName : ""
|
||||
onAccepted: usersModel.setProperty(menu.row, "ensName", ensNameField.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// category: Models
|
|
@ -207,3 +207,5 @@ Item {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// category: Models
|
|
@ -101,12 +101,15 @@ add_library(StatusQ SHARED
|
|||
include/StatusQ/formatteddoubleproperty.h
|
||||
include/StatusQ/functionaggregator.h
|
||||
include/StatusQ/leftjoinmodel.h
|
||||
include/StatusQ/modelentry.h
|
||||
include/StatusQ/modelsyncedcontainer.h
|
||||
include/StatusQ/modelutilsinternal.h
|
||||
include/StatusQ/movablemodel.h
|
||||
include/StatusQ/permissionutilsinternal.h
|
||||
include/StatusQ/rolesrenamingmodel.h
|
||||
include/StatusQ/rxvalidator.h
|
||||
include/StatusQ/snapshotmodel.h
|
||||
include/StatusQ/snapshotobject.h
|
||||
include/StatusQ/singleroleaggregator.h
|
||||
include/StatusQ/statussyntaxhighlighter.h
|
||||
include/StatusQ/statuswindow.h
|
||||
|
@ -124,6 +127,7 @@ add_library(StatusQ SHARED
|
|||
src/formatteddoubleproperty.cpp
|
||||
src/functionaggregator.cpp
|
||||
src/leftjoinmodel.cpp
|
||||
src/modelentry.cpp
|
||||
src/modelutilsinternal.cpp
|
||||
src/movablemodel.cpp
|
||||
src/permissionutilsinternal.cpp
|
||||
|
@ -131,6 +135,8 @@ add_library(StatusQ SHARED
|
|||
src/rolesrenamingmodel.cpp
|
||||
src/rxvalidator.cpp
|
||||
src/singleroleaggregator.cpp
|
||||
src/snapshotmodel.cpp
|
||||
src/snapshotobject.cpp
|
||||
src/statussyntaxhighlighter.cpp
|
||||
src/statuswindow.cpp
|
||||
src/stringutilsinternal.cpp
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
#pragma once
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QPointer>
|
||||
#include <QQmlEngine>
|
||||
#include <QQmlPropertyMap>
|
||||
|
||||
class ModelEntry : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
////////////// input
|
||||
// the source model to get the item from
|
||||
Q_PROPERTY(QAbstractItemModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged REQUIRED)
|
||||
// the key role used to search for the item
|
||||
Q_PROPERTY(QString key READ key WRITE setKey NOTIFY keyChanged REQUIRED)
|
||||
// the value role used to cache the item
|
||||
Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged REQUIRED)
|
||||
// whether to cache the item when it is removed from the model
|
||||
// if true, the item will be cached and available until another source model is used or the cacheOnRemoval is set to false
|
||||
Q_PROPERTY(bool cacheOnRemoval READ cacheOnRemoval WRITE setCacheOnRemoval NOTIFY cacheOnRemovalChanged)
|
||||
|
||||
///////////// output
|
||||
// the item found in the source model
|
||||
Q_PROPERTY(QQmlPropertyMap* item READ item NOTIFY itemChanged)
|
||||
// whether the item is available
|
||||
Q_PROPERTY(bool available READ available NOTIFY availableChanged)
|
||||
// the roles of the item
|
||||
Q_PROPERTY(QStringList roles READ roles NOTIFY rolesChanged)
|
||||
// the row of the item in the source model, -1 if not available or removed
|
||||
Q_PROPERTY(int row READ row NOTIFY rowChanged)
|
||||
// whether the item was removed from the model. This flag is only set when cacheOnRemoval is true
|
||||
Q_PROPERTY(bool itemRemovedFromModel READ itemRemovedFromModel NOTIFY itemRemovedFromModelChanged)
|
||||
|
||||
public:
|
||||
explicit ModelEntry(QObject* parent = nullptr);
|
||||
|
||||
QAbstractItemModel* sourceModel() const;
|
||||
QString key() const;
|
||||
QVariant value() const;
|
||||
bool cacheOnRemoval() const;
|
||||
|
||||
QQmlPropertyMap* item() const;
|
||||
bool available() const;
|
||||
const QStringList& roles() const;
|
||||
int row() const;
|
||||
bool itemRemovedFromModel() const;
|
||||
|
||||
protected:
|
||||
void setSourceModel(QAbstractItemModel* sourceModel);
|
||||
void setKey(const QString& key);
|
||||
void setValue(const QVariant& value);
|
||||
void setIndex(const QModelIndex& index);
|
||||
void setAvailable(bool available);
|
||||
void setRoles(const QStringList& roles);
|
||||
void setRow(int row);
|
||||
void setCacheOnRemoval(bool cacheOnRemoval);
|
||||
void setItemRemovedFromModel(bool itemRemovedFromModel);
|
||||
|
||||
void resetIndex();
|
||||
void tryItemResetOrUpdate();
|
||||
void resetItem();
|
||||
void updateItem(const QList<int>& roles = {});
|
||||
|
||||
QModelIndex findIndexInRange(int start, int end, const QList<int>& roles = {}) const;
|
||||
bool itemHasCorrectRoles() const;
|
||||
void cacheItem();
|
||||
void resetCachedItem();
|
||||
|
||||
signals:
|
||||
void sourceModelChanged();
|
||||
void keyChanged();
|
||||
void valueChanged();
|
||||
void itemChanged();
|
||||
void availableChanged();
|
||||
void rolesChanged();
|
||||
void rowChanged();
|
||||
void cacheOnRemovalChanged();
|
||||
void itemRemovedFromModelChanged();
|
||||
|
||||
private:
|
||||
QScopedPointer<QQmlPropertyMap> m_item{nullptr};
|
||||
QPointer<QAbstractItemModel> m_sourceModel{nullptr};
|
||||
QPersistentModelIndex m_index;
|
||||
bool m_available{false};
|
||||
QStringList m_roles;
|
||||
int m_row{-1};
|
||||
bool m_cacheOnRemoval{false};
|
||||
bool m_itemRemovedFromModel{false};
|
||||
QVariant m_value;
|
||||
QString m_key;
|
||||
};
|
|
@ -2,21 +2,26 @@
|
|||
|
||||
#include <QAbstractListModel>
|
||||
|
||||
class SnapshotModel : public QAbstractListModel {
|
||||
|
||||
class SnapshotModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SnapshotModel(QObject* parent = nullptr);
|
||||
explicit SnapshotModel(const QAbstractItemModel& model, bool recursive = true,
|
||||
QObject* parent = nullptr);
|
||||
explicit SnapshotModel(const QAbstractItemModel& model, bool recursive = true, QObject* parent = nullptr);
|
||||
|
||||
~SnapshotModel();
|
||||
|
||||
int rowCount(const QModelIndex& parent = {}) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
QVariant data(const QModelIndex& index, int role) const override;
|
||||
|
||||
void grabSnapshot(const QAbstractItemModel& model, bool recursive = true);
|
||||
void clearSnapshot();
|
||||
|
||||
QVariant data(int row, int role) const;
|
||||
|
||||
private:
|
||||
|
||||
QHash<int, QList<QVariant>> m_data;
|
||||
QHash<int, QByteArray> m_roles;
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
|
||||
class QAbstractItemModel;
|
||||
class SnapshotObject : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QVariant snapshot READ snapshot NOTIFY snapshotChanged)
|
||||
Q_PROPERTY(bool available READ available NOTIFY availableChanged)
|
||||
|
||||
public:
|
||||
explicit SnapshotObject(QObject* parent = nullptr);
|
||||
explicit SnapshotObject(const QObject* object, QObject* parent);
|
||||
|
||||
QVariant snapshot() const;
|
||||
bool available() const;
|
||||
|
||||
Q_INVOKABLE void grabSnapshot(const QObject* object);
|
||||
|
||||
signals:
|
||||
void snapshotChanged();
|
||||
void availableChanged();
|
||||
|
||||
private:
|
||||
void setAvailable(bool available);
|
||||
void setSnapshot(const QVariant& snapshot);
|
||||
|
||||
QVariantMap objectToVariantMap(const QObject* object);
|
||||
QVariant objectToVariant(const QObject* object);
|
||||
QVariant modelToVariant(const QAbstractItemModel* model);
|
||||
void insertIntoVariantMap(QVariantMap& map, const QString& key, const QVariant& value);
|
||||
|
||||
QVariant m_snapshot;
|
||||
bool m_available{false};
|
||||
};
|
|
@ -0,0 +1,346 @@
|
|||
#include "StatusQ/modelentry.h"
|
||||
|
||||
#include "StatusQ/snapshotmodel.h"
|
||||
#include "StatusQ/snapshotobject.h"
|
||||
|
||||
ModelEntry::ModelEntry(QObject* parent)
|
||||
: QObject(parent)
|
||||
, m_item(new QQmlPropertyMap(this))
|
||||
{ }
|
||||
|
||||
QQmlPropertyMap* ModelEntry::item() const
|
||||
{
|
||||
return m_item.data();
|
||||
}
|
||||
|
||||
QAbstractItemModel* ModelEntry::sourceModel() const
|
||||
{
|
||||
return m_sourceModel.data();
|
||||
}
|
||||
|
||||
QString ModelEntry::key() const
|
||||
{
|
||||
return m_key;
|
||||
}
|
||||
|
||||
QVariant ModelEntry::value() const
|
||||
{
|
||||
return m_value;
|
||||
}
|
||||
|
||||
bool ModelEntry::available() const
|
||||
{
|
||||
return m_available;
|
||||
}
|
||||
|
||||
const QStringList& ModelEntry::roles() const
|
||||
{
|
||||
return m_roles;
|
||||
}
|
||||
|
||||
int ModelEntry::row() const
|
||||
{
|
||||
return m_row;
|
||||
}
|
||||
|
||||
bool ModelEntry::cacheOnRemoval() const
|
||||
{
|
||||
return m_cacheOnRemoval;
|
||||
}
|
||||
|
||||
bool ModelEntry::itemRemovedFromModel() const
|
||||
{
|
||||
return m_itemRemovedFromModel;
|
||||
}
|
||||
|
||||
void ModelEntry::setSourceModel(QAbstractItemModel* sourceModel)
|
||||
{
|
||||
if(m_sourceModel == sourceModel) return;
|
||||
|
||||
if(m_sourceModel)
|
||||
{
|
||||
disconnect(m_sourceModel, nullptr, this, nullptr);
|
||||
}
|
||||
m_sourceModel = sourceModel;
|
||||
|
||||
resetCachedItem();
|
||||
resetIndex();
|
||||
|
||||
if(!m_sourceModel)
|
||||
{
|
||||
emit sourceModelChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
connect(m_sourceModel, &QAbstractItemModel::modelReset, this, [this]() { resetIndex(); });
|
||||
connect(m_sourceModel, &QAbstractItemModel::rowsInserted, this, [this]() {
|
||||
if(!m_index.isValid())
|
||||
{
|
||||
resetIndex();
|
||||
}
|
||||
});
|
||||
connect(m_sourceModel,
|
||||
&QAbstractItemModel::rowsMoved,
|
||||
this,
|
||||
[this](const QModelIndex& parent, int start, int end, const QModelIndex& destination, int row) {
|
||||
if(!m_index.isValid()) return;
|
||||
|
||||
if(m_index.row() >= destination.row() && m_index.row() <= destination.row() + (end - start))
|
||||
{
|
||||
emit rowChanged();
|
||||
}
|
||||
});
|
||||
connect(m_sourceModel,
|
||||
&QAbstractItemModel::rowsAboutToBeRemoved,
|
||||
this,
|
||||
[this](const QModelIndex& parent, int first, int last) {
|
||||
if(!m_index.isValid()) return;
|
||||
|
||||
if(m_index.row() < first || m_index.row() > last) return;
|
||||
|
||||
if(m_cacheOnRemoval)
|
||||
{
|
||||
cacheItem();
|
||||
setItemRemovedFromModel(true);
|
||||
setRow(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
setIndex({});
|
||||
});
|
||||
connect(m_sourceModel,
|
||||
&QAbstractItemModel::dataChanged,
|
||||
this,
|
||||
[this](const QModelIndex& topLeft, const QModelIndex& bottomRight, const QList<int>& roles = QList<int>()) {
|
||||
if(!m_index.isValid())
|
||||
{
|
||||
auto index = findIndexInRange(topLeft.row(), bottomRight.row() + 1);
|
||||
setIndex(index);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the index is still valid
|
||||
auto index = findIndexInRange(m_index.row(), m_index.row() + 1, roles);
|
||||
if(index != m_index)
|
||||
{
|
||||
setIndex(index);
|
||||
return;
|
||||
}
|
||||
|
||||
if(topLeft.row() <= m_index.row() && m_index.row() <= bottomRight.row())
|
||||
{
|
||||
updateItem(roles);
|
||||
}
|
||||
});
|
||||
connect(m_sourceModel, &QAbstractItemModel::layoutChanged, this, [this]() {
|
||||
if(!m_index.isValid())
|
||||
{
|
||||
// Resetting just to cover cases where the rows are removed after the layout change
|
||||
resetItem();
|
||||
}
|
||||
setRow(m_index.row());
|
||||
});
|
||||
|
||||
emit sourceModelChanged();
|
||||
}
|
||||
|
||||
void ModelEntry::setKey(const QString& key)
|
||||
{
|
||||
if(m_key == key) return;
|
||||
|
||||
m_key = key;
|
||||
resetIndex();
|
||||
emit keyChanged();
|
||||
}
|
||||
|
||||
void ModelEntry::setValue(const QVariant& value)
|
||||
{
|
||||
if(m_value == value) return;
|
||||
|
||||
m_value = value;
|
||||
resetIndex();
|
||||
emit valueChanged();
|
||||
}
|
||||
|
||||
void ModelEntry::setIndex(const QModelIndex& index)
|
||||
{
|
||||
if(m_index == index) return;
|
||||
|
||||
m_index = index;
|
||||
|
||||
tryItemResetOrUpdate();
|
||||
setRow(m_index.row());
|
||||
}
|
||||
|
||||
void ModelEntry::setAvailable(bool available)
|
||||
{
|
||||
if(available == m_available) return;
|
||||
|
||||
m_available = available;
|
||||
emit availableChanged();
|
||||
}
|
||||
|
||||
void ModelEntry::setRoles(const QStringList& roles)
|
||||
{
|
||||
if(m_roles.size() == roles.size() && !m_roles.empty() &&
|
||||
std::all_of(roles.begin(), roles.end(), [this](const QString& role) { return m_roles.contains(role); }))
|
||||
return;
|
||||
|
||||
m_roles = roles;
|
||||
emit rolesChanged();
|
||||
}
|
||||
|
||||
void ModelEntry::setRow(int row)
|
||||
{
|
||||
if(m_row == row) return;
|
||||
|
||||
m_row = row;
|
||||
emit rowChanged();
|
||||
}
|
||||
|
||||
void ModelEntry::setCacheOnRemoval(bool cacheOnRemoval)
|
||||
{
|
||||
if(m_cacheOnRemoval == cacheOnRemoval) return;
|
||||
|
||||
resetCachedItem();
|
||||
|
||||
m_cacheOnRemoval = cacheOnRemoval;
|
||||
emit cacheOnRemovalChanged();
|
||||
}
|
||||
|
||||
void ModelEntry::setItemRemovedFromModel(bool itemRemovedFromModel)
|
||||
{
|
||||
if(m_itemRemovedFromModel == itemRemovedFromModel) return;
|
||||
|
||||
m_itemRemovedFromModel = itemRemovedFromModel;
|
||||
emit itemRemovedFromModelChanged();
|
||||
}
|
||||
|
||||
QModelIndex ModelEntry::findIndexInRange(int start, int end, const QList<int>& roles) const
|
||||
{
|
||||
if(!m_sourceModel || m_key.isEmpty()) return {};
|
||||
|
||||
auto keysForRole = m_sourceModel->roleNames().keys(m_key.toUtf8());
|
||||
|
||||
// no matching roles found
|
||||
if(keysForRole.isEmpty() || (!roles.isEmpty() && !roles.contains(keysForRole.first()))) return {};
|
||||
|
||||
for(int i = start; i < end; i++)
|
||||
{
|
||||
auto index = m_sourceModel->index(i, 0);
|
||||
auto data = index.data(keysForRole.first());
|
||||
if(data == m_value) return index;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void ModelEntry::resetIndex()
|
||||
{
|
||||
auto index = QModelIndex();
|
||||
if(m_sourceModel) index = findIndexInRange(0, m_sourceModel->rowCount());
|
||||
|
||||
setIndex(index);
|
||||
}
|
||||
|
||||
void ModelEntry::tryItemResetOrUpdate()
|
||||
{
|
||||
if(!m_index.isValid() || !itemHasCorrectRoles())
|
||||
{
|
||||
resetItem();
|
||||
return;
|
||||
}
|
||||
|
||||
updateItem();
|
||||
setAvailable(true);
|
||||
}
|
||||
|
||||
void ModelEntry::resetItem()
|
||||
{
|
||||
// Signal order is important here
|
||||
if(!m_index.isValid())
|
||||
{
|
||||
setAvailable(false);
|
||||
}
|
||||
|
||||
m_item.reset(new QQmlPropertyMap());
|
||||
|
||||
updateItem();
|
||||
|
||||
if(!m_index.isValid())
|
||||
{
|
||||
setRoles(m_item->keys());
|
||||
}
|
||||
|
||||
emit itemChanged();
|
||||
|
||||
if(m_index.isValid())
|
||||
{
|
||||
setRoles(m_item->keys());
|
||||
setAvailable(true);
|
||||
}
|
||||
}
|
||||
|
||||
void ModelEntry::updateItem(const QList<int>& roles /*{}*/)
|
||||
{
|
||||
if(!m_index.isValid() || !m_sourceModel) return;
|
||||
|
||||
const auto& rolesRef = roles.isEmpty() ? m_sourceModel->roleNames().keys() : roles;
|
||||
|
||||
for(auto role : rolesRef)
|
||||
{
|
||||
auto roleName = m_sourceModel->roleNames().value(role);
|
||||
auto roleValue = m_index.data(role);
|
||||
|
||||
if(roleValue == m_item->value(roleName)) continue;
|
||||
|
||||
m_item->insert(roleName, roleValue);
|
||||
emit m_item->valueChanged(roleName, roleValue);
|
||||
}
|
||||
setItemRemovedFromModel(false);
|
||||
}
|
||||
|
||||
bool ModelEntry::itemHasCorrectRoles() const
|
||||
{
|
||||
if(!m_sourceModel || !m_item) return false;
|
||||
|
||||
auto itemKeys = m_item->keys();
|
||||
auto modelRoles = m_sourceModel->roleNames().values();
|
||||
|
||||
return std::all_of(modelRoles.cbegin(),
|
||||
modelRoles.cend(),
|
||||
[itemKeys](const QByteArray& role) { return itemKeys.contains(role); }) &&
|
||||
itemKeys.size() == modelRoles.size();
|
||||
}
|
||||
|
||||
void ModelEntry::cacheItem()
|
||||
{
|
||||
if(!m_cacheOnRemoval) return;
|
||||
|
||||
for(const auto& role : qAsConst(m_roles))
|
||||
{
|
||||
auto roleName = m_sourceModel->roleNames().key(role.toUtf8());
|
||||
auto roleValue = m_index.data(roleName);
|
||||
|
||||
if(roleValue.canConvert<QAbstractItemModel*>())
|
||||
{
|
||||
m_item->insert(role,
|
||||
QVariant::fromValue(new SnapshotModel(*roleValue.value<QAbstractItemModel*>(), true, m_item.data())));
|
||||
}
|
||||
else if(roleValue.canConvert<QObject*>())
|
||||
{
|
||||
const auto obj = roleValue.value<QObject*>();
|
||||
const auto snapshot = new SnapshotObject(obj, m_item.data());
|
||||
m_item->insert(role, QVariant::fromValue(snapshot->snapshot()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelEntry::resetCachedItem()
|
||||
{
|
||||
if(!m_cacheOnRemoval || !m_itemRemovedFromModel) return;
|
||||
|
||||
resetIndex();
|
||||
tryItemResetOrUpdate();
|
||||
setItemRemovedFromModel(false);
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
#include "StatusQ/permissionutilsinternal.h"
|
||||
#include "StatusQ/rolesrenamingmodel.h"
|
||||
#include "StatusQ/rxvalidator.h"
|
||||
#include "StatusQ/modelentry.h"
|
||||
#include "StatusQ/snapshotobject.h"
|
||||
#include "StatusQ/statussyntaxhighlighter.h"
|
||||
#include "StatusQ/statuswindow.h"
|
||||
#include "StatusQ/stringutilsinternal.h"
|
||||
|
@ -62,6 +64,8 @@ public:
|
|||
qmlRegisterType<FormattedDoubleProperty>("StatusQ", 0, 1, "FormattedDoubleProperty");
|
||||
|
||||
qmlRegisterSingletonType<QClipboardProxy>("StatusQ", 0, 1, "QClipboardProxy", &QClipboardProxy::qmlInstance);
|
||||
qmlRegisterType<ModelEntry>("StatusQ", 0, 1, "ModelEntry");
|
||||
qmlRegisterType<SnapshotObject>("StatusQ", 0, 1, "SnapshotObject");
|
||||
|
||||
qmlRegisterSingletonType<ModelUtilsInternal>(
|
||||
"StatusQ.Internal", 0, 1, "ModelUtils", &ModelUtilsInternal::qmlInstance);
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
#include "StatusQ/snapshotmodel.h"
|
||||
|
||||
#include "StatusQ/snapshotobject.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
SnapshotModel::SnapshotModel(QObject* parent)
|
||||
: QAbstractListModel(parent)
|
||||
{ }
|
||||
|
||||
SnapshotModel::SnapshotModel(const QAbstractItemModel& model, bool recursive, QObject* parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
grabSnapshot(model, recursive);
|
||||
}
|
||||
|
||||
SnapshotModel::~SnapshotModel()
|
||||
{
|
||||
clearSnapshot();
|
||||
}
|
||||
|
||||
int SnapshotModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
if(parent.isValid()) return 0;
|
||||
|
||||
return m_data.size() ? m_data.begin()->size() : 0;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> SnapshotModel::roleNames() const
|
||||
{
|
||||
return m_roles;
|
||||
}
|
||||
|
||||
QVariant SnapshotModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if(!index.isValid() || !m_roles.contains(role) || index.row() >= rowCount())
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return m_data[role][index.row()];
|
||||
}
|
||||
|
||||
void SnapshotModel::grabSnapshot(const QAbstractItemModel& model, bool recursive)
|
||||
{
|
||||
beginResetModel();
|
||||
clearSnapshot();
|
||||
|
||||
m_roles = model.roleNames();
|
||||
|
||||
auto roles = m_roles.keys();
|
||||
auto count = model.rowCount();
|
||||
|
||||
for(auto role : roles)
|
||||
{
|
||||
for(int i = 0; i < count; i++)
|
||||
{
|
||||
QVariant data = model.data(model.index(i, 0), role);
|
||||
|
||||
if(recursive && data.canConvert<QAbstractItemModel*>())
|
||||
{
|
||||
const auto submodel = data.value<QAbstractItemModel*>();
|
||||
|
||||
m_data[role].push_back(QVariant::fromValue(new SnapshotModel(*submodel, true, this)));
|
||||
}
|
||||
else if(recursive && data.canConvert<QObject*>())
|
||||
{
|
||||
const auto submodelObject = data.value<QObject*>();
|
||||
const auto snapshot = new SnapshotObject(submodelObject, this);
|
||||
connect(this, &SnapshotModel::modelAboutToBeReset, snapshot, &SnapshotObject::deleteLater);
|
||||
m_data[role].push_back(snapshot->snapshot());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_data[role].push_back(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
void SnapshotModel::clearSnapshot()
|
||||
{
|
||||
for (auto& data : m_data.values())
|
||||
{
|
||||
for (auto& item : data)
|
||||
{
|
||||
if (item.canConvert<SnapshotModel*>())
|
||||
{
|
||||
item.value<SnapshotModel*>()->deleteLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
m_data.clear();
|
||||
}
|
||||
|
||||
QVariant SnapshotModel::data(int row, int role) const
|
||||
{
|
||||
return data(index(row), role);
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
#include "StatusQ/snapshotobject.h"
|
||||
|
||||
#include "StatusQ/snapshotmodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMetaProperty>
|
||||
|
||||
SnapshotObject::SnapshotObject(QObject* parent)
|
||||
: QObject(parent)
|
||||
{ }
|
||||
|
||||
SnapshotObject::SnapshotObject(const QObject* object, QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
grabSnapshot(object);
|
||||
}
|
||||
|
||||
QVariant SnapshotObject::snapshot() const
|
||||
{
|
||||
return m_snapshot;
|
||||
}
|
||||
|
||||
bool SnapshotObject::available() const
|
||||
{
|
||||
return m_available;
|
||||
}
|
||||
|
||||
void SnapshotObject::setAvailable(bool available)
|
||||
{
|
||||
if(m_available == available) return;
|
||||
|
||||
m_available = available;
|
||||
emit availableChanged();
|
||||
}
|
||||
|
||||
void SnapshotObject::setSnapshot(const QVariant& snapshot)
|
||||
{
|
||||
if(m_snapshot == snapshot) return;
|
||||
|
||||
m_snapshot = snapshot;
|
||||
|
||||
// available emit order is important
|
||||
if (!m_snapshot.isValid()) setAvailable(false);
|
||||
|
||||
emit snapshotChanged();
|
||||
|
||||
if (m_snapshot.isValid()) setAvailable(true);
|
||||
}
|
||||
|
||||
void SnapshotObject::grabSnapshot(const QObject* object)
|
||||
{
|
||||
if(!object)
|
||||
{
|
||||
setSnapshot({});
|
||||
return;
|
||||
}
|
||||
|
||||
// try cast to QAbstractItemModel
|
||||
if(const auto model = qobject_cast<const QAbstractItemModel*>(object))
|
||||
{
|
||||
setSnapshot(modelToVariant(model));
|
||||
return;
|
||||
}
|
||||
|
||||
setSnapshot(QVariant::fromValue(objectToVariantMap(object)));
|
||||
}
|
||||
|
||||
QVariantMap SnapshotObject::objectToVariantMap(const QObject* object)
|
||||
{
|
||||
if(!object)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
QVariantMap item;
|
||||
|
||||
const auto metaObject = object->metaObject();
|
||||
const auto count = metaObject->propertyCount();
|
||||
const auto propertyOffset = metaObject->propertyOffset();
|
||||
|
||||
for(int i = propertyOffset; i < propertyOffset + count; i++)
|
||||
{
|
||||
const auto property = metaObject->property(i);
|
||||
const auto name = property.name();
|
||||
const auto value = property.read(object);
|
||||
|
||||
insertIntoVariantMap(item, name, value);
|
||||
}
|
||||
|
||||
const auto dynamicPropertyNames = object->dynamicPropertyNames();
|
||||
for(const auto& name : dynamicPropertyNames)
|
||||
{
|
||||
const auto value = object->property(name);
|
||||
insertIntoVariantMap(item, name, value);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
QVariant SnapshotObject::objectToVariant(const QObject* object)
|
||||
{
|
||||
if(auto model = qobject_cast<const QAbstractItemModel*>(object))
|
||||
{
|
||||
return modelToVariant(model);
|
||||
}
|
||||
|
||||
return {objectToVariantMap(object)};
|
||||
}
|
||||
|
||||
QVariant SnapshotObject::modelToVariant(const QAbstractItemModel* model)
|
||||
{
|
||||
if(!model)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto modelSnapshot = new SnapshotModel(*model, true, this);
|
||||
connect(this, &SnapshotObject::snapshotChanged, modelSnapshot, [modelSnapshot]() { modelSnapshot->deleteLater(); });
|
||||
return QVariant::fromValue(modelSnapshot);
|
||||
}
|
||||
|
||||
|
||||
void SnapshotObject::insertIntoVariantMap(QVariantMap& map, const QString& key, const QVariant& value)
|
||||
{
|
||||
if(value.canConvert<QObject*>())
|
||||
{
|
||||
map.insert(key, objectToVariant(value.value<QObject*>()));
|
||||
return;
|
||||
}
|
||||
|
||||
map.insert(key, value);
|
||||
}
|
|
@ -23,13 +23,11 @@ add_library(StatusQTestLib
|
|||
src/TestHelpers/modeltestutils.h
|
||||
src/TestHelpers/persistentindexestester.cpp
|
||||
src/TestHelpers/persistentindexestester.h
|
||||
src/TestHelpers/snapshotmodel.cpp
|
||||
src/TestHelpers/snapshotmodel.h
|
||||
src/TestHelpers/testmodel.cpp
|
||||
src/TestHelpers/testmodel.h
|
||||
)
|
||||
|
||||
target_link_libraries(StatusQTestLib PUBLIC Qt5::Core Qt5::Quick Qt5::Test)
|
||||
target_link_libraries(StatusQTestLib PUBLIC Qt5::Core Qt5::Quick Qt5::Test StatusQ)
|
||||
target_include_directories(StatusQTestLib PUBLIC src)
|
||||
|
||||
enable_testing()
|
||||
|
@ -101,3 +99,11 @@ add_test(NAME MovableModelTest COMMAND MovableModelTest)
|
|||
add_executable(ModelSyncedContainerTest tst_ModelSyncedContainer.cpp)
|
||||
target_link_libraries(ModelSyncedContainerTest PRIVATE StatusQ StatusQTestLib)
|
||||
add_test(NAME ModelSyncedContainerTest COMMAND ModelSyncedContainerTest)
|
||||
|
||||
add_executable(ModelEntryTest tst_ModelEntry.cpp)
|
||||
target_link_libraries(ModelEntryTest PRIVATE StatusQ StatusQTestLib)
|
||||
add_test(NAME ModelEntryTest COMMAND ModelEntryTest)
|
||||
|
||||
add_executable(SnapshotObjectTest tst_SnapshotObject.cpp)
|
||||
target_link_libraries(SnapshotObjectTest PRIVATE StatusQ StatusQTestLib)
|
||||
add_test(NAME SnapshotObjectTest COMMAND SnapshotObjectTest)
|
|
@ -1,6 +1,5 @@
|
|||
#include "persistentindexestester.h"
|
||||
|
||||
#include <TestHelpers/snapshotmodel.h>
|
||||
#include "StatusQ/snapshotmodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
#include "snapshotmodel.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
SnapshotModel::SnapshotModel(QObject* parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
}
|
||||
|
||||
SnapshotModel::SnapshotModel(const QAbstractItemModel& model, bool recursive,
|
||||
QObject* parent)
|
||||
: QAbstractListModel(parent)
|
||||
{
|
||||
grabSnapshot(model, recursive);
|
||||
}
|
||||
|
||||
int SnapshotModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
if(parent.isValid())
|
||||
return 0;
|
||||
|
||||
return m_data.size() ? m_data.begin()->size() : 0;
|
||||
}
|
||||
|
||||
QHash<int, QByteArray> SnapshotModel::roleNames() const
|
||||
{
|
||||
return m_roles;
|
||||
}
|
||||
|
||||
QVariant SnapshotModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (!index.isValid() || !m_roles.contains(role)
|
||||
|| index.row() >= rowCount()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return m_data[role][index.row()];
|
||||
}
|
||||
|
||||
void SnapshotModel::grabSnapshot(const QAbstractItemModel& model, bool recursive)
|
||||
{
|
||||
m_roles = model.roleNames();
|
||||
m_data.clear();
|
||||
|
||||
auto roles = m_roles.keys();
|
||||
auto count = model.rowCount();
|
||||
|
||||
for (auto role : roles) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
QVariant data = model.data(model.index(i, 0), role);
|
||||
|
||||
if (recursive && data.canConvert<QAbstractItemModel*>()) {
|
||||
const auto submodel = data.value<QAbstractItemModel*>();
|
||||
|
||||
m_data[role].push_back(
|
||||
QVariant::fromValue(
|
||||
new SnapshotModel(*submodel, true, this)));
|
||||
} else {
|
||||
m_data[role].push_back(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariant SnapshotModel::data(int row, int role) const
|
||||
{
|
||||
return data(index(row), role);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -7,12 +7,12 @@
|
|||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <StatusQ/movablemodel.h>
|
||||
#include <StatusQ/snapshotmodel.h>
|
||||
|
||||
#include <TestHelpers/listmodelwrapper.h>
|
||||
#include <TestHelpers/modelsignalsspy.h>
|
||||
#include <TestHelpers/modeltestutils.h>
|
||||
#include <TestHelpers/persistentindexestester.h>
|
||||
#include <TestHelpers/snapshotmodel.h>
|
||||
|
||||
class TestMovableModel : public QObject
|
||||
{
|
||||
|
|
|
@ -0,0 +1,201 @@
|
|||
#include "StatusQ/snapshotobject.h"
|
||||
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
#include <QQmlListProperty>
|
||||
#include <QQmlPropertyMap>
|
||||
#include <QStandardItemModel>
|
||||
#include <QScopedPointer>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
class SimpleObject : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool boolProperty MEMBER m_boolProperty)
|
||||
Q_PROPERTY(int intProperty MEMBER m_intProperty)
|
||||
Q_PROPERTY(QString stringProperty MEMBER m_stringProperty)
|
||||
Q_PROPERTY(QVariant variantProperty MEMBER m_variantProperty)
|
||||
public:
|
||||
SimpleObject(bool boolProperty = true,
|
||||
int intProperty = 5,
|
||||
const QString& stringProperty = "string",
|
||||
const QVariant& variantProperty = "variant",
|
||||
QObject* parent = nullptr)
|
||||
: QObject(parent)
|
||||
, m_boolProperty(boolProperty)
|
||||
, m_intProperty(intProperty)
|
||||
, m_stringProperty(stringProperty)
|
||||
, m_variantProperty(variantProperty)
|
||||
{ }
|
||||
bool m_boolProperty;
|
||||
int m_intProperty;
|
||||
QString m_stringProperty;
|
||||
QVariant m_variantProperty;
|
||||
};
|
||||
|
||||
class QObjectTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool boolProperty MEMBER m_boolProperty)
|
||||
Q_PROPERTY(int intProperty MEMBER m_intProperty)
|
||||
Q_PROPERTY(QString stringProperty MEMBER m_stringProperty)
|
||||
Q_PROPERTY(QVariant variantProperty MEMBER m_variantProperty)
|
||||
Q_PROPERTY(QVariantList variantListProperty MEMBER m_variantListProperty)
|
||||
Q_PROPERTY(QVariantMap variantMapProperty MEMBER m_variantMapProperty)
|
||||
Q_PROPERTY(QObject* objectProperty MEMBER m_objectProperty)
|
||||
Q_PROPERTY(QStandardItemModel* standardItemModel MEMBER m_standardItemModel)
|
||||
|
||||
public:
|
||||
bool m_boolProperty{true};
|
||||
int m_intProperty{5};
|
||||
QString m_stringProperty{"string"};
|
||||
QVariant m_variantProperty{"variant"};
|
||||
QVariantList m_variantListProperty{"variant1", "variant2"};
|
||||
QVariantMap m_variantMapProperty{{"key1", "value1"}, {"key2", "value2"}};
|
||||
QObject* m_objectProperty{new SimpleObject(true, 45, "stringVal", "variantVal", this)};
|
||||
QStandardItemModel* m_standardItemModel = nullptr;
|
||||
};
|
||||
|
||||
class SnapshotObjectTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void snapshotQObjectTest()
|
||||
{
|
||||
QScopedPointer<SnapshotObject> snapshotObject {new SnapshotObject()};
|
||||
|
||||
QSignalSpy snapshotChangedSpy(snapshotObject.data(), &SnapshotObject::snapshotChanged);
|
||||
QSignalSpy availableChangedSpy(snapshotObject.data(), &SnapshotObject::availableChanged);
|
||||
|
||||
QVERIFY(snapshotObject->snapshot().isNull());
|
||||
QVERIFY(!snapshotObject->available());
|
||||
|
||||
// grabSnapshot(nullptr) should clear the snapshot and set available to false
|
||||
snapshotObject->grabSnapshot(nullptr);
|
||||
|
||||
QCOMPARE(snapshotChangedSpy.count(), 0);
|
||||
QCOMPARE(availableChangedSpy.count(), 0);
|
||||
QVERIFY(snapshotObject->snapshot().isNull());
|
||||
QVERIFY(!snapshotObject->available());
|
||||
|
||||
{
|
||||
// grabSnapshot(new SimpleObject) should set the snapshot and set available to true
|
||||
QScopedPointer<SimpleObject> testObject {new SimpleObject(true, 45, "stringVal", "variantVal")};
|
||||
const auto snapshotObjPtr = snapshotObject.data();
|
||||
auto connection = connect(snapshotObjPtr, &SnapshotObject::availableChanged, [snapshotObjPtr]() {
|
||||
// the snapshot object must change after the available property
|
||||
QVERIFY(snapshotObjPtr->snapshot().isValid());
|
||||
});
|
||||
snapshotObject->grabSnapshot(testObject.data());
|
||||
|
||||
QCOMPARE(snapshotChangedSpy.count(), 1);
|
||||
QCOMPARE(availableChangedSpy.count(), 1);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["boolProperty"].toBool(), true);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["intProperty"].toInt(), 45);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["stringProperty"].toString(), "stringVal");
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["variantProperty"].toString(), "variantVal");
|
||||
|
||||
QVERIFY(snapshotObject->available());
|
||||
|
||||
disconnect(connection);
|
||||
// delete the test object and check that the snapshot is still available
|
||||
}
|
||||
|
||||
QCOMPARE(snapshotChangedSpy.count(), 1);
|
||||
QCOMPARE(availableChangedSpy.count(), 1);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["boolProperty"].toBool(), true);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["intProperty"].toInt(), 45);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["stringProperty"].toString(), "stringVal");
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["variantProperty"].toString(), "variantVal");
|
||||
|
||||
QVERIFY(snapshotObject->available());
|
||||
|
||||
{
|
||||
// grabshapshot(new QObjectTest) should set the snapshot and set available to true
|
||||
auto snapshotObjPtr = snapshotObject.data();
|
||||
auto connection = connect(snapshotObject.data(), &SnapshotObject::availableChanged, [snapshotObjPtr]() {
|
||||
// the snapshot object must change after the available property
|
||||
QVERIFY(snapshotObjPtr->snapshot().isValid());
|
||||
});
|
||||
QScopedPointer<QObjectTest> testObject {new QObjectTest()};
|
||||
testObject->m_standardItemModel = new QStandardItemModel(this);
|
||||
testObject->m_standardItemModel->insertRow(0, new QStandardItem("item1"));
|
||||
testObject->m_standardItemModel->insertRow(1, new QStandardItem("item2"));
|
||||
|
||||
snapshotObject->grabSnapshot(testObject.data());
|
||||
disconnect(connection);
|
||||
|
||||
// the testObject and the model is destroyed here
|
||||
}
|
||||
|
||||
QCOMPARE(snapshotChangedSpy.count(), 2);
|
||||
QCOMPARE(availableChangedSpy.count(), 1);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["boolProperty"].toBool(), true);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["intProperty"].toInt(), 5);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["stringProperty"].toString(), "string");
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["variantProperty"].toString(), "variant");
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["variantListProperty"].toList().size(), 2);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["variantListProperty"].toList().at(0).toString(), "variant1");
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["variantListProperty"].toList().at(1).toString(), "variant2");
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["variantMapProperty"].toMap().size(), 2);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["variantMapProperty"].toMap()["key1"].toString(), "value1");
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["variantMapProperty"].toMap()["key2"].toString(), "value2");
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["objectProperty"].toMap()["boolProperty"].toBool(), true);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["objectProperty"].toMap()["intProperty"].toInt(), 45);
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["objectProperty"].toMap()["stringProperty"].toString(),
|
||||
"stringVal");
|
||||
QCOMPARE(snapshotObject->snapshot().toMap()["objectProperty"].toMap()["variantProperty"].toString(),
|
||||
"variantVal");
|
||||
|
||||
auto standardItemModel = snapshotObject->snapshot().toMap()["standardItemModel"].value<QAbstractItemModel*>();
|
||||
QSignalSpy modelDestroyedSpy(standardItemModel, &QObject::destroyed);
|
||||
|
||||
QCOMPARE(standardItemModel->rowCount(), 2);
|
||||
QCOMPARE(standardItemModel->data(standardItemModel->index(0, 0)), "item1");
|
||||
QCOMPARE(standardItemModel->data(standardItemModel->index(1, 0)).toString(), "item2");
|
||||
|
||||
QVERIFY(snapshotObject->available());
|
||||
|
||||
snapshotObject->grabSnapshot(nullptr);
|
||||
|
||||
QCOMPARE(snapshotChangedSpy.count(), 3);
|
||||
QCOMPARE(availableChangedSpy.count(), 2);
|
||||
QVERIFY(snapshotObject->snapshot().isNull());
|
||||
QVERIFY(!snapshotObject->available());
|
||||
// check if the memory is released after grabbing another snapshot
|
||||
QTRY_COMPARE(modelDestroyedSpy.count(), 1);
|
||||
}
|
||||
|
||||
void snapshotModelTest()
|
||||
{
|
||||
QScopedPointer<SnapshotObject> snapshotObject {new SnapshotObject()};
|
||||
|
||||
{
|
||||
QScopedPointer<QStandardItemModel> model {new QStandardItemModel()};
|
||||
model->insertRow(0, new QStandardItem("item1"));
|
||||
model->insertRow(1, new QStandardItem("item2"));
|
||||
|
||||
snapshotObject->grabSnapshot(model.data());
|
||||
}
|
||||
|
||||
auto snapshot = snapshotObject->snapshot();
|
||||
auto snapshotModel = snapshot.value<QAbstractItemModel*>();
|
||||
|
||||
QSignalSpy modelDestroyedSpy(snapshotModel, &QObject::destroyed);
|
||||
|
||||
QVERIFY(snapshotModel);
|
||||
QCOMPARE(snapshotModel->rowCount(), 2);
|
||||
QCOMPARE(snapshotModel->data(snapshotModel->index(0, 0)).toString(), "item1");
|
||||
QCOMPARE(snapshotModel->data(snapshotModel->index(1, 0)).toString(), "item2");
|
||||
QVERIFY(snapshotObject->available());
|
||||
|
||||
snapshotObject->grabSnapshot(nullptr);
|
||||
QTRY_COMPARE(modelDestroyedSpy.count(), 1);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(SnapshotObjectTest)
|
||||
#include "tst_SnapshotObject.moc"
|
|
@ -1,17 +1,19 @@
|
|||
#include <QAbstractItemModelTester>
|
||||
#include <QAbstractListModel>
|
||||
#include <QTest>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
#include "StatusQ/snapshotmodel.h"
|
||||
#include "StatusQ/writableproxymodel.h"
|
||||
|
||||
#include <TestHelpers/persistentindexestester.h>
|
||||
#include <TestHelpers/snapshotmodel.h>
|
||||
#include <TestHelpers/modeltestutils.h>
|
||||
#include <TestHelpers/persistentindexestester.h>
|
||||
|
||||
namespace {
|
||||
namespace
|
||||
{
|
||||
|
||||
class TestSourceModel : public QAbstractListModel {
|
||||
class TestSourceModel : public QAbstractListModel
|
||||
{
|
||||
|
||||
public:
|
||||
explicit TestSourceModel(QList<QPair<QString, QVariantList>> data)
|
||||
|
|
Loading…
Reference in New Issue