feat(StatusQ): Add cpp model utils to allow handling ListModel and generic QAbstractItemModel uniformly

Closes: #9562
This commit is contained in:
Michał Cieślak 2023-02-17 14:36:58 +01:00 committed by Michał
parent 89b767cf2a
commit 03e1636cff
15 changed files with 277 additions and 145 deletions

View File

@ -30,6 +30,7 @@ SplitView {
store: CommunitiesStore {
id: mockedCommunity
permissionsModel: PermissionsModel.permissionsModel
readonly property var assetsModel: AssetsModel {
@ -83,10 +84,8 @@ SplitView {
anchors.fill: parent
model: mockedCommunity.permissionsModel
assetKeys: ModelUtils.modelToArray(
assetsModel, ["key"]).map(asset => asset.key)
collectibleKeys: ModelUtils.modelToArray(
collectiblesModel, ["key"]).map(collectible => collectible.key)
assetKeys: assetsModel.data.map(asset => asset.key)
collectibleKeys: collectiblesModel.data.map(collectible => collectible.key)
}
}
}

View File

@ -3,42 +3,43 @@ import QtQuick 2.15
import AppLayouts.Chat.controls.community 1.0
ListModel {
Component.onCompleted:
append([
{
key: "socks",
iconSource: ModelsData.assets.socks,
name: "Unisocks",
shortName: "SOCKS",
category: TokenCategories.Category.Community
},
{
key: "zrx",
iconSource: ModelsData.assets.zrx,
name: "Ox",
shortName: "ZRX",
category: TokenCategories.Category.Own
},
{
key: "1inch",
iconSource: ModelsData.assets.inch,
name: "1inch",
shortName: "1INCH",
category: TokenCategories.Category.Own
},
{
key: "Aave",
iconSource: ModelsData.assets.aave,
name: "Aave",
shortName: "AAVE",
category: TokenCategories.Category.Own
},
{
key: "Amp",
iconSource: ModelsData.assets.amp,
name: "Amp",
shortName: "AMP",
category: TokenCategories.Category.Own
}
])
readonly property var data: [
{
key: "socks",
iconSource: ModelsData.assets.socks,
name: "Unisocks",
shortName: "SOCKS",
category: TokenCategories.Category.Community
},
{
key: "zrx",
iconSource: ModelsData.assets.zrx,
name: "Ox",
shortName: "ZRX",
category: TokenCategories.Category.Own
},
{
key: "1inch",
iconSource: ModelsData.assets.inch,
name: "1inch",
shortName: "1INCH",
category: TokenCategories.Category.Own
},
{
key: "Aave",
iconSource: ModelsData.assets.aave,
name: "Aave",
shortName: "AAVE",
category: TokenCategories.Category.Own
},
{
key: "Amp",
iconSource: ModelsData.assets.amp,
name: "Amp",
shortName: "AMP",
category: TokenCategories.Category.Own
}
]
Component.onCompleted: append(data)
}

View File

@ -3,75 +3,76 @@ import QtQuick 2.15
import AppLayouts.Chat.controls.community 1.0
ListModel {
Component.onCompleted:
append([
{
key: "Anniversary",
iconSource: ModelsData.collectibles.anniversary,
name: "Anniversary",
category: TokenCategories.Category.Community
},
{
key: "CryptoKitties",
iconSource: ModelsData.collectibles.cryptoKitties,
name: "CryptoKitties",
category: TokenCategories.Category.Own,
subItems: [
{
key: "Kitty1",
iconSource: ModelsData.collectibles.kitty1,
imageSource: ModelsData.collectibles.kitty1Big,
name: "Furbeard"
},
{
key: "Kitty2",
iconSource: ModelsData.collectibles.kitty2,
imageSource: ModelsData.collectibles.kitty2Big,
name: "Magicat"
},
{
key: "Kitty3",
iconSource: ModelsData.collectibles.kitty3,
imageSource: ModelsData.collectibles.kitty3Big,
name: "Happy Meow"
},
{
key: "Kitty4",
iconSource: ModelsData.collectibles.kitty4,
imageSource: ModelsData.collectibles.kitty4Big,
name: "Furbeard-2"
},
{
key: "Kitty5",
iconSource: ModelsData.collectibles.kitty5,
imageSource: ModelsData.collectibles.kitty5Big,
name: "Magicat-3"
},
{
key: "Kitty5",
iconSource: ModelsData.collectibles.kitty4,
imageSource: ModelsData.collectibles.kitty4Big,
name: "Furbeard-3"
},
{
key: "Kitty6",
iconSource: ModelsData.collectibles.kitty5,
imageSource: ModelsData.collectibles.kitty5Big,
name: "Magicat-4"
}
]
},
{
key: "SuperRare",
iconSource: ModelsData.collectibles.superRare,
name: "SuperRare",
category: TokenCategories.Category.Own
},
{
key: "Custom",
iconSource: ModelsData.collectibles.custom,
name: "Custom Collectible",
category: TokenCategories.Category.General
}
])
readonly property var data: [
{
key: "Anniversary",
iconSource: ModelsData.collectibles.anniversary,
name: "Anniversary",
category: TokenCategories.Category.Community
},
{
key: "CryptoKitties",
iconSource: ModelsData.collectibles.cryptoKitties,
name: "CryptoKitties",
category: TokenCategories.Category.Own,
subItems: [
{
key: "Kitty1",
iconSource: ModelsData.collectibles.kitty1,
imageSource: ModelsData.collectibles.kitty1Big,
name: "Furbeard"
},
{
key: "Kitty2",
iconSource: ModelsData.collectibles.kitty2,
imageSource: ModelsData.collectibles.kitty2Big,
name: "Magicat"
},
{
key: "Kitty3",
iconSource: ModelsData.collectibles.kitty3,
imageSource: ModelsData.collectibles.kitty3Big,
name: "Happy Meow"
},
{
key: "Kitty4",
iconSource: ModelsData.collectibles.kitty4,
imageSource: ModelsData.collectibles.kitty4Big,
name: "Furbeard-2"
},
{
key: "Kitty5",
iconSource: ModelsData.collectibles.kitty5,
imageSource: ModelsData.collectibles.kitty5Big,
name: "Magicat-3"
},
{
key: "Kitty5",
iconSource: ModelsData.collectibles.kitty4,
imageSource: ModelsData.collectibles.kitty4Big,
name: "Furbeard-3"
},
{
key: "Kitty6",
iconSource: ModelsData.collectibles.kitty5,
imageSource: ModelsData.collectibles.kitty5Big,
name: "Magicat-4"
}
]
},
{
key: "SuperRare",
iconSource: ModelsData.collectibles.superRare,
name: "SuperRare",
category: TokenCategories.Category.Own
},
{
key: "Custom",
iconSource: ModelsData.collectibles.custom,
name: "Custom Collectible",
category: TokenCategories.Category.General
}
]
Component.onCompleted: append(data)
}

View File

@ -1,19 +1,21 @@
set(STATUSQ_DIR ${CMAKE_CURRENT_LIST_DIR})
set(STATUSQ_HEADERS
${STATUSQ_DIR}/include/StatusQ/QClipboardProxy.h
${STATUSQ_DIR}/include/StatusQ/modelutilsinternal.h
${STATUSQ_DIR}/include/StatusQ/rxvalidator.h
${STATUSQ_DIR}/include/StatusQ/statussyntaxhighlighter.h
${STATUSQ_DIR}/include/StatusQ/statuswindow.h
${STATUSQ_DIR}/include/StatusQ/typesregistration.h
${STATUSQ_DIR}/include/StatusQ/QClipboardProxy.h
${STATUSQ_DIR}/include/StatusQ/statussyntaxhighlighter.h
${STATUSQ_DIR}/include/StatusQ/rxvalidator.h
)
set(STATUSQ_SOURCES
${STATUSQ_DIR}/src/QClipboardProxy.cpp
${STATUSQ_DIR}/src/modelutilsinternal.cpp
${STATUSQ_DIR}/src/rxvalidator.cpp
${STATUSQ_DIR}/src/statussyntaxhighlighter.cpp
${STATUSQ_DIR}/src/statuswindow.cpp
${STATUSQ_DIR}/src/typesregistration.cpp
${STATUSQ_DIR}/src/QClipboardProxy.cpp
${STATUSQ_DIR}/src/statussyntaxhighlighter.cpp
${STATUSQ_DIR}/src/rxvalidator.cpp
)
if(APPLE)

View File

@ -0,0 +1,34 @@
#pragma once
#include <QObject>
#include <QString>
#include <QVariant>
class QAbstractItemModel;
class QJSEngine;
class QQmlEngine;
class ModelUtilsInternal : public QObject
{
Q_OBJECT
public:
explicit ModelUtilsInternal(QObject* parent = nullptr);
Q_INVOKABLE int roleByName(QAbstractItemModel *model,
const QString &roleName) const;
Q_INVOKABLE QStringList roleNames(QAbstractItemModel *model) const;
Q_INVOKABLE QVariantMap get(QAbstractItemModel *model, int row) const;
Q_INVOKABLE QVariant get(QAbstractItemModel *model, int row,
const QString &roleName) const;
static QObject* qmlInstance(QQmlEngine *engine, QJSEngine *scriptEngine)
{
Q_UNUSED(engine);
Q_UNUSED(scriptEngine);
return new ModelUtilsInternal;
}
};

View File

@ -2,16 +2,25 @@ pragma Singleton
import QtQuick 2.14
import StatusQ.Internal 0.1 as Internal
QtObject {
function get(model, index, role = "") {
if (role)
return Internal.ModelUtils.get(model, index, role)
else
return Internal.ModelUtils.get(model, index)
}
function modelToArray(model, roles) {
if (!model)
return []
const count = model.count
const count = model.rowCount()
const array = []
for (let i = 0; i < count; i++) {
const modelItem = model.get(i)
const modelItem = Internal.ModelUtils.get(model, i)
const arrayItem = {}
roles.forEach(role => {
@ -28,10 +37,10 @@ QtObject {
}
function indexOf(model, role, key) {
const count = model.count
const count = model.rowCount()
for (let i = 0; i < count; i++)
if (model.get(i)[role] === key)
if (Internal.ModelUtils.get(model, i, role) === key)
return i
return -1
@ -55,8 +64,8 @@ QtObject {
return true
for (let i = 0; i < countA; i++) {
const itemA = modelA.get(i)
const itemB = modelB.get(i)
const itemA = Internal.ModelUtils.get(modelA, i)
const itemB = Internal.ModelUtils.get(modelB, i)
if (!checkItemsEquality(itemA, itemB, roles))
return false
@ -79,11 +88,11 @@ QtObject {
return true
for (let i = 0; i < countA; i++) {
const itemA = modelA.get(i)
const itemA = Internal.ModelUtils.get(modelA, i)
let found = false
for (let j = 0; j < countB; j++) {
const itemB = modelB.get(j)
const itemB = Internal.ModelUtils.get(modelB, j)
if (checkItemsEquality(itemA, itemB, roles))
found = true

View File

@ -0,0 +1,56 @@
#include "StatusQ/modelutilsinternal.h"
#include <QAbstractItemModel>
ModelUtilsInternal::ModelUtilsInternal(QObject* parent)
: QObject(parent)
{
}
QStringList ModelUtilsInternal::roleNames(QAbstractItemModel *model) const
{
if (model == nullptr)
return {};
QHash<int, QByteArray> roles = model->roleNames();
QStringList strings;
strings.reserve(roles.size());
for (auto it = roles.begin(); it != roles.end(); ++it)
strings << QString::fromUtf8(it.value());
return strings;
}
int ModelUtilsInternal::roleByName(QAbstractItemModel* model,
const QString &roleName) const
{
if (model == nullptr)
return -1;
return model->roleNames().key(roleName.toUtf8(), -1);
}
QVariantMap ModelUtilsInternal::get(QAbstractItemModel *model, int row) const
{
QVariantMap map;
if (model == nullptr)
return map;
QModelIndex modelIndex = model->index(row, 0);
QHash<int, QByteArray> roles = model->roleNames();
for (auto it = roles.begin(); it != roles.end(); ++it)
map.insert(it.value(), model->data(modelIndex, it.key()));
return map;
}
QVariant ModelUtilsInternal::get(QAbstractItemModel *model,
int row, const QString &roleName) const
{
return model->data(model->index(row, 0), roleByName(model, roleName));
}

View File

@ -1,17 +1,21 @@
#include "StatusQ/typesregistration.h"
#include "StatusQ/QClipboardProxy.h"
#include "StatusQ/modelutilsinternal.h"
#include "StatusQ/rxvalidator.h"
#include "StatusQ/statussyntaxhighlighter.h"
#include "StatusQ/statuswindow.h"
#include "StatusQ/rxvalidator.h"
#include <QQmlEngine>
void registerStatusQTypes()
{
qmlRegisterType<StatusWindow>("StatusQ", 0 , 1, "StatusWindow");
qmlRegisterType<StatusWindow>("StatusQ", 0 , 1, "StatusWindow");
qmlRegisterSingletonType<QClipboardProxy>("StatusQ", 0 , 1, "QClipboardProxy",
&QClipboardProxy::qmlInstance);
qmlRegisterType<StatusSyntaxHighlighter>("StatusQ", 0 , 1, "StatusSyntaxHighlighter");
qmlRegisterType<RXValidator>("StatusQ", 0 , 1, "RXValidator");
qmlRegisterSingletonType<ModelUtilsInternal>("StatusQ.Internal", 0 , 1, "ModelUtils",
&ModelUtilsInternal::qmlInstance);
}

View File

@ -125,7 +125,7 @@ StatusListView {
Item {
id: sectionDelegateRoot
property string section: root.model && root.model.count ?
property string section: root.count ?
qsTr("Search result") :
qsTr("No results")

View File

@ -37,7 +37,7 @@ StatusScrollView {
Layout.leftMargin: 8
Layout.topMargin: 8
visible: !!model ? model.count === 0 : false
visible: repeater.count === 0
Layout.fillWidth: true
@ -56,6 +56,8 @@ StatusScrollView {
columns: d.columns
Repeater {
id: repeater
model: root.model
delegate: ColumnLayout {
spacing: 4

View File

@ -3,28 +3,31 @@ pragma Singleton
import QtQml 2.14
import StatusQ.Core 0.1
import utils 1.0
import StatusQ.Core.Utils 0.1
import AppLayouts.Chat.controls.community 1.0
QtObject {
function getTokenByKey(model, key) {
if (!model)
return null
for (let i = 0; i < model.count; i++) {
const item = model.get(i)
const count = model.rowCount()
for (let i = 0; i < count; i++) {
const item = ModelUtils.get(model, i)
if (item.key === key)
return item
if (item.subItems) {
const subitem = getTokenByKey(item.subItems, key)
if (subitem !== null)
return subitem
}
}
return null
}

View File

@ -170,13 +170,14 @@ SettingsPageLayout {
communityNewPermissionView.dirtyValues.isPrivate
const model = root.store.permissionsModel
const count = model.rowCount()
for (let i = 0; i < model.count; i++) {
for (let i = 0; i < count; i++) {
if (root.state === d.editPermissionViewState
&& d.permissionIndexToEdit === i)
continue
const item = model.get(i)
const item = ModelUtils.get(model, i)
const holdings = item.holdingsListModel
const channels = item.channelsListModel
@ -255,7 +256,7 @@ SettingsPageLayout {
store: root.store
function setInitialValuesFromIndex(index) {
const item = root.store.permissionsModel.get(index)
const item = ModelUtils.get(root.store.permissionsModel, index)
d.holdingsToEditModel = item.holdingsListModel
d.channelsToEditModel = item.channelsListModel

View File

@ -49,6 +49,8 @@ Control {
spacing: d.defaultHoldingsSpacing
Repeater {
id: repeater
model: root.model
ColumnLayout {
@ -88,7 +90,7 @@ Control {
Layout.alignment: Qt.AlignHCenter
text: qsTr("or")
textFormat: Text.StyledText
visible: (index !== root.model.count - 1)
visible: (index !== repeater.count - 1)
}
}
}

View File

@ -12,6 +12,15 @@ SortFilterProxyModel {
property var assetsModel
property var collectiblesModel
readonly property ModelChangeTracker _assetsChanges: ModelChangeTracker {
model: assetsModel
}
readonly property ModelChangeTracker _collectiblesChanges: ModelChangeTracker {
model: collectiblesModel
}
proxyRoles: [
ExpressionRole {
name: "text"
@ -23,10 +32,9 @@ SortFilterProxyModel {
const model = type === HoldingTypes.Type.Asset
? assetsModel
: collectiblesModel
const item = CommunityPermissionsHelpers.getTokenByKey(model, key)
return item ? item.name : ""
return item ? item.shortName || item.name : ""
}
function getText(type, key, amount) {
@ -38,7 +46,11 @@ SortFilterProxyModel {
// Direct call for singleton function is not handled properly by
// SortFilterProxyModel that's why helper function is used instead.
expression: getText(model.type, model.key, model.amount)
expression: {
_assetsChanges.revision
_collectiblesChanges.revision
getText(model.type, model.key, model.amount)
}
},
ExpressionRole {
name: "imageSource"
@ -53,7 +65,11 @@ SortFilterProxyModel {
return CommunityPermissionsHelpers.getTokenIconByKey(model, key)
}
expression: getIcon(model.type, model.key)
expression: {
_assetsChanges.revision
_collectiblesChanges.revision
getIcon(model.type, model.key)
}
},
ExpressionRole {
name: "operator"

View File

@ -73,6 +73,7 @@
#include "DOtherSide/Status/SoundManager.h"
#include "StatusQ/QClipboardProxy.h"
#include "StatusQ/modelutilsinternal.h"
#include "StatusQ/rxvalidator.h"
#include "StatusQ/statussyntaxhighlighter.h"
#include "StatusQ/statuswindow.h"
@ -94,7 +95,8 @@ void register_meta_types()
qmlRegisterType<StatusSyntaxHighlighter>("StatusQ", 0 , 1, "StatusSyntaxHighlighter");
qmlRegisterType<RXValidator>("StatusQ", 0, 1, "RXValidator");
qmlRegisterSingletonType<QClipboardProxy>("StatusQ", 0 , 1, "QClipboardProxy", &QClipboardProxy::qmlInstance);
qmlRegisterSingletonType<ModelUtilsInternal>("StatusQ.Internal", 0 , 1, "ModelUtils",
&ModelUtilsInternal::qmlInstance);
#ifdef MONITORING
qmlRegisterSingletonType<Monitor>("Monitoring", 1 , 0, "Monitor", &Monitor::qmlInstance);
#endif