From e3bfa85849a9348926dde03629e2b9f16364889b Mon Sep 17 00:00:00 2001 From: Jos van den Oever Date: Sat, 19 May 2018 14:01:11 +0200 Subject: [PATCH] Make the roles property in the bindings.json optional --- src/cpp.cpp | 2 +- src/parseJson.cpp | 1 + tests/rust_list/src/implementation.rs | 36 ++++ tests/rust_list/src/interface.rs | 175 ++++++++++++++++++++ tests/test_list.cpp | 27 +++ tests/test_list.json | 13 ++ tests/test_list_rust.cpp | 230 ++++++++++++++++++++++++++ tests/test_list_rust.h | 44 +++++ 8 files changed, 527 insertions(+), 1 deletion(-) diff --git a/src/cpp.cpp b/src/cpp.cpp index b704bc5..829f2da 100644 --- a/src/cpp.cpp +++ b/src/cpp.cpp @@ -121,7 +121,7 @@ private: bool isColumnWrite(const Object& o, int col) { for (auto ip: o.itemProperties) { - if (ip.write && ip.roles.size() > col && ip.roles[col].size() > 0) { + if (ip.write && (col == 0 || (ip.roles.size() > col && ip.roles[col].size() > 0))) { return true; } } diff --git a/src/parseJson.cpp b/src/parseJson.cpp index edcad34..da9111a 100644 --- a/src/parseJson.cpp +++ b/src/parseJson.cpp @@ -296,6 +296,7 @@ parseObject(const QString& name, const QJsonObject& json) { o.columnCount = qMax(o.columnCount, p.roles.size()); o.itemProperties.append(p); } + o.columnCount = qMax(1, o.columnCount); return o; } diff --git a/tests/rust_list/src/implementation.rs b/tests/rust_list/src/implementation.rs index b230cb1..6149563 100644 --- a/tests/rust_list/src/implementation.rs +++ b/tests/rust_list/src/implementation.rs @@ -6,6 +6,7 @@ use interface::*; #[derive(Default, Clone)] struct PersonsItem { user_name: String, + age: u8, } pub struct Persons { @@ -37,3 +38,38 @@ impl PersonsTrait for Persons { } } +pub struct NoRole { + emit: NoRoleEmitter, + model: NoRoleList, + list: Vec +} + +impl NoRoleTrait for NoRole { + fn new(emit: NoRoleEmitter, model: NoRoleList) -> NoRole { + NoRole { + emit: emit, + model: model, + list: vec![PersonsItem::default(); 10], + } + } + fn emit(&self) -> &NoRoleEmitter { + &self.emit + } + fn row_count(&self) -> usize { + self.list.len() + } + fn user_name(&self, item: usize) -> &str { + &self.list[item].user_name + } + fn set_user_name(&mut self, item: usize, v: String) -> bool { + self.list[item].user_name = v; + true + } + fn user_age(&self, item: usize) -> u8 { + self.list[item].age + } + fn set_user_age(&mut self, item: usize, v: u8) -> bool { + self.list[item].age = v; + true + } +} diff --git a/tests/rust_list/src/interface.rs b/tests/rust_list/src/interface.rs index cd2adff..972e14e 100644 --- a/tests/rust_list/src/interface.rs +++ b/tests/rust_list/src/interface.rs @@ -79,6 +79,181 @@ fn to_c_int(n: usize) -> c_int { } +pub struct NoRoleQObject {} + +#[derive(Clone)] +pub struct NoRoleEmitter { + qobject: Arc>, + new_data_ready: fn(*const NoRoleQObject), +} + +unsafe impl Send for NoRoleEmitter {} + +impl NoRoleEmitter { + fn clear(&self) { + *self.qobject.lock().unwrap() = null(); + } + pub fn new_data_ready(&self) { + let ptr = *self.qobject.lock().unwrap(); + if !ptr.is_null() { + (self.new_data_ready)(ptr); + } + } +} + +pub struct NoRoleList { + qobject: *const NoRoleQObject, + data_changed: fn(*const NoRoleQObject, usize, usize), + begin_reset_model: fn(*const NoRoleQObject), + end_reset_model: fn(*const NoRoleQObject), + begin_insert_rows: fn(*const NoRoleQObject, usize, usize), + end_insert_rows: fn(*const NoRoleQObject), + begin_remove_rows: fn(*const NoRoleQObject, usize, usize), + end_remove_rows: fn(*const NoRoleQObject), +} + +impl NoRoleList { + pub fn data_changed(&self, first: usize, last: usize) { + (self.data_changed)(self.qobject, first, last); + } + pub fn begin_reset_model(&self) { + (self.begin_reset_model)(self.qobject); + } + pub fn end_reset_model(&self) { + (self.end_reset_model)(self.qobject); + } + pub fn begin_insert_rows(&self, first: usize, last: usize) { + (self.begin_insert_rows)(self.qobject, first, last); + } + pub fn end_insert_rows(&self) { + (self.end_insert_rows)(self.qobject); + } + pub fn begin_remove_rows(&self, first: usize, last: usize) { + (self.begin_remove_rows)(self.qobject, first, last); + } + pub fn end_remove_rows(&self) { + (self.end_remove_rows)(self.qobject); + } +} + +pub trait NoRoleTrait { + fn new(emit: NoRoleEmitter, model: NoRoleList) -> Self; + fn emit(&self) -> &NoRoleEmitter; + fn row_count(&self) -> usize; + fn insert_rows(&mut self, _row: usize, _count: usize) -> bool { false } + fn remove_rows(&mut self, _row: usize, _count: usize) -> bool { false } + fn can_fetch_more(&self) -> bool { + false + } + fn fetch_more(&mut self) {} + fn sort(&mut self, u8, SortOrder) {} + fn user_age(&self, item: usize) -> u8; + fn set_user_age(&mut self, item: usize, u8) -> bool; + fn user_name(&self, item: usize) -> &str; + fn set_user_name(&mut self, item: usize, String) -> bool; +} + +#[no_mangle] +pub extern "C" fn no_role_new( + no_role: *mut NoRoleQObject, + no_role_new_data_ready: fn(*const NoRoleQObject), + no_role_data_changed: fn(*const NoRoleQObject, usize, usize), + no_role_begin_reset_model: fn(*const NoRoleQObject), + no_role_end_reset_model: fn(*const NoRoleQObject), + no_role_begin_insert_rows: fn(*const NoRoleQObject, usize, usize), + no_role_end_insert_rows: fn(*const NoRoleQObject), + no_role_begin_remove_rows: fn(*const NoRoleQObject, usize, usize), + no_role_end_remove_rows: fn(*const NoRoleQObject), +) -> *mut NoRole { + let no_role_emit = NoRoleEmitter { + qobject: Arc::new(Mutex::new(no_role)), + new_data_ready: no_role_new_data_ready, + }; + let model = NoRoleList { + qobject: no_role, + data_changed: no_role_data_changed, + begin_reset_model: no_role_begin_reset_model, + end_reset_model: no_role_end_reset_model, + begin_insert_rows: no_role_begin_insert_rows, + end_insert_rows: no_role_end_insert_rows, + begin_remove_rows: no_role_begin_remove_rows, + end_remove_rows: no_role_end_remove_rows, + }; + let d_no_role = NoRole::new(no_role_emit, model); + Box::into_raw(Box::new(d_no_role)) +} + +#[no_mangle] +pub unsafe extern "C" fn no_role_free(ptr: *mut NoRole) { + Box::from_raw(ptr).emit().clear(); +} + +#[no_mangle] +pub unsafe extern "C" fn no_role_row_count(ptr: *const NoRole) -> c_int { + to_c_int((&*ptr).row_count()) +} +#[no_mangle] +pub unsafe extern "C" fn no_role_insert_rows(ptr: *mut NoRole, row: c_int, count: c_int) -> bool { + (&mut *ptr).insert_rows(to_usize(row), to_usize(count)) +} +#[no_mangle] +pub unsafe extern "C" fn no_role_remove_rows(ptr: *mut NoRole, row: c_int, count: c_int) -> bool { + (&mut *ptr).remove_rows(to_usize(row), to_usize(count)) +} +#[no_mangle] +pub unsafe extern "C" fn no_role_can_fetch_more(ptr: *const NoRole) -> bool { + (&*ptr).can_fetch_more() +} +#[no_mangle] +pub unsafe extern "C" fn no_role_fetch_more(ptr: *mut NoRole) { + (&mut *ptr).fetch_more() +} +#[no_mangle] +pub unsafe extern "C" fn no_role_sort( + ptr: *mut NoRole, + column: u8, + order: SortOrder, +) { + (&mut *ptr).sort(column, order) +} + +#[no_mangle] +pub extern "C" fn no_role_data_user_age(ptr: *const NoRole, row: c_int) -> u8 { + let o = unsafe { &*ptr }; + o.user_age(to_usize(row)).into() +} + +#[no_mangle] +pub unsafe extern "C" fn no_role_set_data_user_age( + ptr: *mut NoRole, row: c_int, + v: u8, +) -> bool { + (&mut *ptr).set_user_age(to_usize(row), v) +} + +#[no_mangle] +pub extern "C" fn no_role_data_user_name( + ptr: *const NoRole, row: c_int, + d: *mut QString, + set: fn(*mut QString, *const c_char, len: c_int), +) { + let o = unsafe { &*ptr }; + let data = o.user_name(to_usize(row)); + let s: *const c_char = data.as_ptr() as (*const c_char); + set(d, s, to_c_int(data.len())); +} + +#[no_mangle] +pub extern "C" fn no_role_set_data_user_name( + ptr: *mut NoRole, row: c_int, + s: *const c_ushort, len: c_int, +) -> bool { + let o = unsafe { &mut *ptr }; + let mut v = String::new(); + set_string_from_utf16(&mut v, s, len); + o.set_user_name(to_usize(row), v) +} + pub struct PersonsQObject {} #[derive(Clone)] diff --git a/tests/test_list.cpp b/tests/test_list.cpp index e1f06a3..91abe08 100644 --- a/tests/test_list.cpp +++ b/tests/test_list.cpp @@ -29,6 +29,7 @@ private slots: void testConstructor(); void testStringGetter(); void testStringSetter(); + void testAccessByDefaultRole(); }; void TestRustList::testConstructor() @@ -58,6 +59,7 @@ void TestRustList::testStringSetter() const bool set = persons.setData(index, "Konqi"); // THEN + QCOMPARE(persons.columnCount(), 1); QVERIFY(set); QVERIFY(spy.isValid()); QCOMPARE(spy.count(), 1); @@ -65,5 +67,30 @@ void TestRustList::testStringSetter() QCOMPARE(value.toString(), QString("Konqi")); } +void TestRustList::testAccessByDefaultRole() +{ + // GIVEN + NoRole norole; + QSignalSpy spy(&norole, &NoRole::dataChanged); + auto ageRole = Qt::UserRole + 0; + auto nameRole = Qt::UserRole + 1; + + // WHEN + const QModelIndex index(norole.index(0,0)); + const bool setName = norole.setData(index, "Konqi", nameRole); + const bool setAge = norole.setData(index, 21, ageRole); + + // THEN + QCOMPARE(norole.columnCount(), 1); + QVERIFY(setName); + QVERIFY(setAge); + QVERIFY(spy.isValid()); + QCOMPARE(spy.count(), 2); + QVariant name = norole.data(norole.index(0,0), nameRole); + QCOMPARE(name.toString(), QString("Konqi")); + QVariant age = norole.data(norole.index(0,0), ageRole); + QCOMPARE(age.value(), (quint8)21); +} + QTEST_MAIN(TestRustList) #include "test_list.moc" diff --git a/tests/test_list.json b/tests/test_list.json index 2e9c9db..26e8bac 100644 --- a/tests/test_list.json +++ b/tests/test_list.json @@ -15,6 +15,19 @@ "roles": [ [ "display", "edit" ] ] } } + }, + "NoRole": { + "type": "List", + "itemProperties": { + "userName": { + "type": "QString", + "write": true + }, + "userAge": { + "type": "quint8", + "write": true + } + } } } } diff --git a/tests/test_list_rust.cpp b/tests/test_list_rust.cpp index bb9491f..c7d0c92 100644 --- a/tests/test_list_rust.cpp +++ b/tests/test_list_rust.cpp @@ -29,6 +29,184 @@ namespace { return (v.isNull()) ?QVariant() :v; } } +extern "C" { + quint8 no_role_data_user_age(const NoRole::Private*, int); + bool no_role_set_data_user_age(NoRole::Private*, int, quint8); + void no_role_data_user_name(const NoRole::Private*, int, QString*, qstring_set); + bool no_role_set_data_user_name(NoRole::Private*, int, const ushort* s, int len); + void no_role_sort(NoRole::Private*, unsigned char column, Qt::SortOrder order = Qt::AscendingOrder); + + int no_role_row_count(const NoRole::Private*); + bool no_role_insert_rows(NoRole::Private*, int, int); + bool no_role_remove_rows(NoRole::Private*, int, int); + bool no_role_can_fetch_more(const NoRole::Private*); + void no_role_fetch_more(NoRole::Private*); +} +int NoRole::columnCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : 1; +} + +bool NoRole::hasChildren(const QModelIndex &parent) const +{ + return rowCount(parent) > 0; +} + +int NoRole::rowCount(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : no_role_row_count(m_d); +} + +bool NoRole::insertRows(int row, int count, const QModelIndex &) +{ + return no_role_insert_rows(m_d, row, count); +} + +bool NoRole::removeRows(int row, int count, const QModelIndex &) +{ + return no_role_remove_rows(m_d, row, count); +} + +QModelIndex NoRole::index(int row, int column, const QModelIndex &parent) const +{ + if (!parent.isValid() && row >= 0 && row < rowCount(parent) && column >= 0 && column < 1) { + return createIndex(row, column, (quintptr)row); + } + return QModelIndex(); +} + +QModelIndex NoRole::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +bool NoRole::canFetchMore(const QModelIndex &parent) const +{ + return (parent.isValid()) ? 0 : no_role_can_fetch_more(m_d); +} + +void NoRole::fetchMore(const QModelIndex &parent) +{ + if (!parent.isValid()) { + no_role_fetch_more(m_d); + } +} + +void NoRole::sort(int column, Qt::SortOrder order) +{ + no_role_sort(m_d, column, order); +} +Qt::ItemFlags NoRole::flags(const QModelIndex &i) const +{ + auto flags = QAbstractItemModel::flags(i); + if (i.column() == 0) { + flags |= Qt::ItemIsEditable; + } + return flags; +} + +quint8 NoRole::userAge(int row) const +{ + return no_role_data_user_age(m_d, row); +} + +bool NoRole::setUserAge(int row, quint8 value) +{ + bool set = false; + set = no_role_set_data_user_age(m_d, row, value); + if (set) { + QModelIndex index = createIndex(row, 0, row); + emit dataChanged(index, index); + } + return set; +} + +QString NoRole::userName(int row) const +{ + QString s; + no_role_data_user_name(m_d, row, &s, set_qstring); + return s; +} + +bool NoRole::setUserName(int row, const QString& value) +{ + bool set = false; + set = no_role_set_data_user_name(m_d, row, value.utf16(), value.length()); + if (set) { + QModelIndex index = createIndex(row, 0, row); + emit dataChanged(index, index); + } + return set; +} + +QVariant NoRole::data(const QModelIndex &index, int role) const +{ + Q_ASSERT(rowCount(index.parent()) > index.row()); + switch (index.column()) { + case 0: + switch (role) { + case Qt::UserRole + 0: + return QVariant::fromValue(userAge(index.row())); + case Qt::UserRole + 1: + return QVariant::fromValue(userName(index.row())); + } + } + return QVariant(); +} + +QHash NoRole::roleNames() const { + QHash names = QAbstractItemModel::roleNames(); + names.insert(Qt::UserRole + 0, "userAge"); + names.insert(Qt::UserRole + 1, "userName"); + return names; +} +QVariant NoRole::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation != Qt::Horizontal) { + return QVariant(); + } + return m_headerData.value(qMakePair(section, (Qt::ItemDataRole)role), role == Qt::DisplayRole ?QString::number(section + 1) :QVariant()); +} + +bool NoRole::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role) +{ + if (orientation != Qt::Horizontal) { + return false; + } + m_headerData.insert(qMakePair(section, (Qt::ItemDataRole)role), value); + return true; +} + +bool NoRole::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.column() == 0) { + if (role == Qt::UserRole + 0) { + if (value.canConvert(qMetaTypeId())) { + return setUserAge(index.row(), value.value()); + } + } + if (role == Qt::UserRole + 1) { + if (value.canConvert(qMetaTypeId())) { + return setUserName(index.row(), value.value()); + } + } + } + return false; +} + +extern "C" { + NoRole::Private* no_role_new(NoRole*, + void (*)(const NoRole*), + void (*)(NoRole*, quintptr, quintptr), + void (*)(NoRole*), + void (*)(NoRole*), + void (*)(NoRole*, int, int), + void (*)(NoRole*), + void (*)(NoRole*, int, int), + void (*)(NoRole*)); + void no_role_free(NoRole::Private*); +}; + extern "C" { void persons_data_user_name(const Persons::Private*, int, QString*, qstring_set); bool persons_set_data_user_name(Persons::Private*, int, const ushort* s, int len); @@ -183,6 +361,58 @@ extern "C" { void persons_free(Persons::Private*); }; +NoRole::NoRole(bool /*owned*/, QObject *parent): + QAbstractItemModel(parent), + m_d(0), + m_ownsPrivate(false) +{ + initHeaderData(); +} + +NoRole::NoRole(QObject *parent): + QAbstractItemModel(parent), + m_d(no_role_new(this, + [](const NoRole* o) { + emit o->newDataReady(QModelIndex()); + }, + [](NoRole* o, quintptr first, quintptr last) { + o->dataChanged(o->createIndex(first, 0, first), + o->createIndex(last, 0, last)); + }, + [](NoRole* o) { + o->beginResetModel(); + }, + [](NoRole* o) { + o->endResetModel(); + }, + [](NoRole* o, int first, int last) { + o->beginInsertRows(QModelIndex(), first, last); + }, + [](NoRole* o) { + o->endInsertRows(); + }, + [](NoRole* o, int first, int last) { + o->beginRemoveRows(QModelIndex(), first, last); + }, + [](NoRole* o) { + o->endRemoveRows(); + } +)), + m_ownsPrivate(true) +{ + connect(this, &NoRole::newDataReady, this, [this](const QModelIndex& i) { + this->fetchMore(i); + }, Qt::QueuedConnection); + initHeaderData(); +} + +NoRole::~NoRole() { + if (m_ownsPrivate) { + no_role_free(m_d); + } +} +void NoRole::initHeaderData() { +} Persons::Persons(bool /*owned*/, QObject *parent): QAbstractItemModel(parent), m_d(0), diff --git a/tests/test_list_rust.h b/tests/test_list_rust.h index 5e62b16..6630ef1 100644 --- a/tests/test_list_rust.h +++ b/tests/test_list_rust.h @@ -5,8 +5,52 @@ #include #include +class NoRole; class Persons; +class NoRole : public QAbstractItemModel +{ + Q_OBJECT +public: + class Private; +private: + Private * m_d; + bool m_ownsPrivate; + explicit NoRole(bool owned, QObject *parent); +public: + explicit NoRole(QObject *parent = nullptr); + ~NoRole(); + + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &index) const override; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + bool canFetchMore(const QModelIndex &parent) const override; + void fetchMore(const QModelIndex &parent) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; + QHash roleNames() const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + bool setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) override; + Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + Q_INVOKABLE quint8 userAge(int row) const; + Q_INVOKABLE bool setUserAge(int row, quint8 value); + Q_INVOKABLE QString userName(int row) const; + Q_INVOKABLE bool setUserName(int row, const QString& value); + +signals: + // new data is ready to be made available to the model with fetchMore() + void newDataReady(const QModelIndex &parent) const; +private: + QHash, QVariant> m_headerData; + void initHeaderData(); +signals: +}; + class Persons : public QAbstractItemModel { Q_OBJECT