diff --git a/.gitignore b/.gitignore index 39352d7..8b778ba 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ build *.o .directory #*# -nimcache \ No newline at end of file +nimcache +*.kdev4 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7b6e417 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,16 @@ +# Version 3.0 +* [NimQml] Added support for QAbstractListModel subclasses +* [NimQml] Fixed QtObject macro wrong reorder of the methods and proc declaration (thanks to Will) +* [NimQml] Added new ContactApp example +* [NimQml] Added optional support for finalizers +* [DOtherSide] Added support for injecting the DynamicQObject as behaviour to QObject subclasses +* [DotherSide] Added support for QAbstractListModel subclasses + +# Version 2.0 +* [DQml] Initial support for properties creation +* [NimQml] Added new macro syntax for creating QObject derived object (thanks to Will) + +# Version 1.0 +* [DOtherSide] Initial version with support for QObject Slot, Signal and Properties creation +* [DQml] Initial support for Slot and Signal creation +* [NimQml] Initial support for Slot, Signal and Properties creation \ No newline at end of file diff --git a/DOtherSide/CMakeLists.txt b/DOtherSide/CMakeLists.txt index 9c43bb6..012e557 100644 --- a/DOtherSide/CMakeLists.txt +++ b/DOtherSide/CMakeLists.txt @@ -1,3 +1,3 @@ add_subdirectory(DOtherSide) -add_subdirectory(DynamicQObject) -add_subdirectory(IntegrationTest) \ No newline at end of file +#add_subdirectory(DynamicQObject) +#add_subdirectory(IntegrationTest) \ No newline at end of file diff --git a/DOtherSide/DOtherSide/BaseQAbstractListModel.h b/DOtherSide/DOtherSide/BaseQAbstractListModel.h new file mode 100644 index 0000000..85448c7 --- /dev/null +++ b/DOtherSide/DOtherSide/BaseQAbstractListModel.h @@ -0,0 +1,202 @@ +#ifndef BASEQABSTRACTLISTMODEL_H +#define BASEQABSTRACTLISTMODEL_H + +#include "DOtherSideTypes.h" +#include "DynamicQObject.h" +#include + +/// This class act as a base class for D and Nim QAbstractListModel +class BaseQAbstractListModel : public DynamicQObject +{ +public: + /// Constructor + BaseQAbstractListModel(void* modelObject, + RowCountCallback rowCountCallback, + ColumnCountCallback columnCountCallback, + DataCallback dataCallback, + SetDataCallback setDataCallback, + RoleNamesCallback roleNamesCallback, + FlagsCallback flagsCallback, + HeaderDataCallback headerDataCallback); + + /// Return the model's row count + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override; + + /// Return the model's column count + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override; + + /// Return the QVariant at the given index + virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + + /// Sets the QVariant value at the given index and role + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; + + /// Return the item flags for the given index + virtual Qt::ItemFlags flags(const QModelIndex& index) const override; + + /// Return the data for the given role and section in the header with the specified orientation + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + /// Return the dModelPointer + void* modelObject(); + + /// Return the roleNames + virtual QHash roleNames() const override; + + /// Expose beginInsertRows + void publicBeginInsertRows(const QModelIndex& index, int first, int last); + + /// Expose endInsertRows + void publicEndInsertRows(); + + /// Expose beginRemoveRows + void publicBeginRemoveRows(const QModelIndex& index, int first, int last); + + /// Expose endInsertRows + void publicEndRemoveRows(); + + /// Expose beginResetModel + void publicBeginResetModel(); + + /// Expose endResetModel + void publicEndResetModel(); + + /// Expose dataChanged + void publicDataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight, + const QVector& roles = QVector()); + +private: + void* m_modelObject; + RowCountCallback m_rowCountCallback; + ColumnCountCallback m_columnCountCallback; + DataCallback m_dataCallback; + SetDataCallback m_setDataCallback; + RoleNamesCallback m_roleNamesCallback; + FlagsCallback m_flagsCallback; + HeaderDataCallback m_headerDataCallback; +}; + +BaseQAbstractListModel::BaseQAbstractListModel(void* modelObject, + RowCountCallback rowCountCallback, + ColumnCountCallback columnCountCallback, + DataCallback dataCallback, + SetDataCallback setDataCallback, + RoleNamesCallback roleNamesCallback, + FlagsCallback flagsCallback, + HeaderDataCallback headerDataCallback) + : m_modelObject(modelObject) + , m_rowCountCallback(rowCountCallback) + , m_columnCountCallback(columnCountCallback) + , m_dataCallback(dataCallback) + , m_setDataCallback(setDataCallback) + , m_roleNamesCallback(roleNamesCallback) + , m_flagsCallback(flagsCallback) + , m_headerDataCallback(headerDataCallback) +{ +} + +int BaseQAbstractListModel::rowCount(const QModelIndex& parent) const +{ + auto parentIndex = new QModelIndex(); + *parentIndex = parent; + int result; + m_rowCountCallback(m_modelObject, parentIndex, &result); + return result; +} + +int BaseQAbstractListModel::columnCount(const QModelIndex& parent) const +{ + auto parentIndex = new QModelIndex(); + *parentIndex = parent; + int result; + m_columnCountCallback(m_modelObject, parentIndex, &result); + return result; +} + +QVariant BaseQAbstractListModel::data(const QModelIndex& index, int role) const +{ + auto newIndex = new QModelIndex(); + *newIndex = index; + QVariant result; + m_dataCallback(m_modelObject, newIndex, role, &result); + return result; +} + +bool BaseQAbstractListModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + auto newIndex = new QModelIndex(index); + *newIndex = index; + auto newValue = new QVariant(); + *newValue = value; + bool result = false; + m_setDataCallback(m_modelObject, newIndex, newValue, role, &result); + return result; +} + +Qt::ItemFlags BaseQAbstractListModel::flags(const QModelIndex& index) const +{ + auto newIndex = new QModelIndex(index); + *newIndex = index; + int result; + m_flagsCallback(m_modelObject, newIndex, &result); + return Qt::ItemFlags(result); +} + +QVariant BaseQAbstractListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + QVariant result; + m_headerDataCallback(m_modelObject, section, orientation, role, &result); + return result; +} + +void* BaseQAbstractListModel::modelObject() +{ + return m_modelObject; +} + +QHash BaseQAbstractListModel::roleNames() const +{ + QHash result; + m_roleNamesCallback(m_modelObject, &result); + return result; +} + +void BaseQAbstractListModel::publicBeginInsertRows(const QModelIndex& index, int first, int last) +{ + beginInsertRows(index, first, last); +} + +void BaseQAbstractListModel::publicEndInsertRows() +{ + return endInsertRows(); +} + +void BaseQAbstractListModel::publicBeginRemoveRows(const QModelIndex& index, int first, int last) +{ + beginRemoveRows(index, first, last); +} + +void BaseQAbstractListModel::publicEndRemoveRows() +{ + return endRemoveRows(); +} + +void BaseQAbstractListModel::publicBeginResetModel() +{ + beginResetModel(); +} + +void BaseQAbstractListModel::publicEndResetModel() +{ + endResetModel(); +} + +void BaseQAbstractListModel::publicDataChanged(const QModelIndex& topLeft, + const QModelIndex& bottomRight, + const QVector& roles) +{ + emit dataChanged(topLeft, bottomRight, roles); +} + +#endif diff --git a/DOtherSide/DOtherSide/BaseQObject.h b/DOtherSide/DOtherSide/BaseQObject.h new file mode 100644 index 0000000..6341610 --- /dev/null +++ b/DOtherSide/DOtherSide/BaseQObject.h @@ -0,0 +1,8 @@ +#ifndef BASEQOBJECT_H +#define BASEQOBJECT_H + +#include "DynamicQObject.h" + +class BaseQObject : public DynamicQObject {}; + +#endif diff --git a/DOtherSide/DOtherSide/CMakeLists.txt b/DOtherSide/DOtherSide/CMakeLists.txt index 08d65d0..7efc71c 100644 --- a/DOtherSide/DOtherSide/CMakeLists.txt +++ b/DOtherSide/DOtherSide/CMakeLists.txt @@ -8,9 +8,13 @@ find_package(Qt5Core) find_package(Qt5Qml) find_package(Qt5Gui) find_package(Qt5Quick) +find_package(Qt5Widgets) set(HEADERS_LIST DOtherSide.h + DOtherSideTypes.h + BaseQAbstractListModel.h + BaseQObject.h ) set(SRC_LIST @@ -18,5 +22,5 @@ set(SRC_LIST ) add_library(${PROJECT_NAME} SHARED ${SRC_LIST} ${HEADERS_LIST}) -target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Gui Qt5::Qml Qt5::Quick DynamicQObject) +target_link_libraries(${PROJECT_NAME} Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Qml Qt5::Quick) target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../DynamicQObject) \ No newline at end of file diff --git a/DOtherSide/DOtherSide/DOtherSide.cpp b/DOtherSide/DOtherSide/DOtherSide.cpp index 8bbbc3d..d75b07d 100644 --- a/DOtherSide/DOtherSide/DOtherSide.cpp +++ b/DOtherSide/DOtherSide/DOtherSide.cpp @@ -1,19 +1,25 @@ #include "DOtherSide.h" -#include -#include -#include +#include + #include #include +#include +#include +#include #include +#include +#include #include "DynamicQObject.h" +#include "BaseQAbstractListModel.h" +#include "BaseQObject.h" -void convert_to_cstring(const QString& source, CharPtr& destination, int& length) +void convert_to_cstring(const QString& source, char** destination, int* length) { QByteArray array = source.toUtf8(); - destination = qstrdup(array.data()); - length = qstrlen(array.data()); + *destination = qstrdup(array.data()); + *length = qstrlen(array.data()); } void dos_qguiapplication_create() @@ -34,12 +40,40 @@ void dos_qguiapplication_exec() qApp->exec(); } -void dos_qqmlapplicationengine_create(void **vptr) +void dos_qguiapplication_quit() +{ + qApp->quit(); +} + +void dos_qapplication_create() +{ + static int argc = 1; + static char empty[1] = {0}; + static char* argv[] = {empty}; + new QApplication(argc, argv); +} + +void dos_qapplication_delete() +{ + delete qApp; +} + +void dos_qapplication_exec() +{ + qApp->exec(); +} + +void dos_qapplication_quit() +{ + qApp->quit(); +} + +void dos_qqmlapplicationengine_create(void** vptr) { *vptr = new QQmlApplicationEngine(); } -void dos_qqmlapplicationengine_load(void *vptr, const char *filename) +void dos_qqmlapplicationengine_load(void* vptr, const char* filename) { QQmlApplicationEngine* engine = reinterpret_cast(vptr); engine->load(QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + QDir::separator() + QString(filename))); @@ -75,7 +109,7 @@ void dos_qquickview_delete(void* vptr) delete view; } -void dos_qquickview_source(void *vptr, CharPtr& result, int& length) +void dos_qquickview_source(void* vptr, char** result, int* length) { QQuickView* view = reinterpret_cast(vptr); QUrl url = view->source(); @@ -94,25 +128,25 @@ void dos_qquickview_rootContext(void* vptr, void** context) *context = view->rootContext(); } -void dos_chararray_create(CharPtr& ptr) +void dos_chararray_create(char** ptr) { - ptr = 0; + *ptr = 0; } -void dos_chararray_create(CharPtr& ptr, int size) +void dos_chararray_create(char** ptr, int size) { if (size > 0) - ptr = new char[size]; + *ptr = new char[size]; else - ptr = 0; + *ptr = 0; } -void dos_chararray_delete(CharPtr ptr) +void dos_chararray_delete(char* ptr) { if (ptr) delete[] ptr; } -void dos_qqmlcontext_baseUrl(void* vptr, CharPtr& result, int& length) +void dos_qqmlcontext_baseUrl(void* vptr, char** result, int* length) { QQmlContext* context = reinterpret_cast(vptr); QUrl url = context->baseUrl(); @@ -126,17 +160,17 @@ void dos_qqmlcontext_setcontextproperty(void* vptr, const char* name, void* valu context->setContextProperty(QString::fromUtf8(name), *variant); } -void dos_qvariant_create(void **vptr) +void dos_qvariant_create(void** vptr) { *vptr = new QVariant(); } -void dos_qvariant_create_int(void **vptr, int value) +void dos_qvariant_create_int(void** vptr, int value) { *vptr = new QVariant(value); } -void dos_qvariant_create_bool(void **vptr, bool value) +void dos_qvariant_create_bool(void** vptr, bool value) { *vptr = new QVariant(value); } @@ -146,7 +180,15 @@ void dos_qvariant_create_string(void** vptr, const char* value) *vptr = new QVariant(value); } -void dos_qvariant_create_qobject(void **vptr, void* value) +void dos_qvariant_create_qvariant(void** vptr, void* other) +{ + auto newQVariant = new QVariant(); + auto otherQVariant = reinterpret_cast(other); + *newQVariant = *otherQVariant; + *vptr = newQVariant; +} + +void dos_qvariant_create_qobject(void** vptr, void* value) { auto qobject = reinterpret_cast(value); auto variant = new QVariant(); @@ -154,31 +196,68 @@ void dos_qvariant_create_qobject(void **vptr, void* value) *vptr = variant; } -void dos_qvariant_isnull(void* vptr, bool& isNull) +void dos_qvariant_create_float(void** vptr, float value) { - auto variant = reinterpret_cast(vptr); - isNull = variant->isNull(); + *vptr = new QVariant(value); } -void dos_qvariant_delete(void *vptr) +void dos_qvariant_create_double(void** vptr, double value) +{ + *vptr = new QVariant(value); +} + +void dos_qvariant_create_qabstractlistmodel(void** vptr, void* value) +{ + auto qobject = reinterpret_cast(value); + auto variant = new QVariant(); + variant->setValue(qobject); + *vptr = variant; +} + +void dos_qvariant_isnull(void* vptr, bool* isNull) +{ + auto variant = reinterpret_cast(vptr); + *isNull = variant->isNull(); +} + +void dos_qvariant_delete(void* vptr) { auto variant = reinterpret_cast(vptr); delete variant; } -void dos_qvariant_toInt(void* vptr, int& value) +void dos_qvariant_assign(void* vptr, void* other) { - auto variant = reinterpret_cast(vptr); - value = variant->toInt(); + auto leftQVariant = reinterpret_cast(vptr); + auto rightQVariant = reinterpret_cast(other); + *leftQVariant = *rightQVariant; } -void dos_qvariant_toBool(void* vptr, bool& value) +void dos_qvariant_toInt(void* vptr, int* value) { auto variant = reinterpret_cast(vptr); - value = variant->toBool(); + *value = variant->toInt(); } -void dos_qvariant_toString(void* vptr, CharPtr& ptr, int& size) +void dos_qvariant_toBool(void* vptr, bool* value) +{ + auto variant = reinterpret_cast(vptr); + *value = variant->toBool(); +} + +void dos_qvariant_toFloat(void* vptr, float* value) +{ + auto variant = reinterpret_cast(vptr); + *value = variant->toFloat(); +} + +void dos_qvariant_toDouble(void* vptr, double* value) +{ + auto variant = reinterpret_cast(vptr); + *value = variant->toDouble(); +} + +void dos_qvariant_toString(void* vptr, char** ptr, int* size) { auto variant = reinterpret_cast(vptr); convert_to_cstring(variant->toString(), ptr, size); @@ -196,24 +275,50 @@ void dos_qvariant_setBool(void* vptr, bool value) *variant = value; } +void dos_qvariant_setFloat(void* vptr, float value) +{ + auto variant = reinterpret_cast(vptr); + *variant = value; +} + +void dos_qvariant_setDouble(void* vptr, double value) +{ + auto variant = reinterpret_cast(vptr); + *variant = value; +} + void dos_qvariant_setString(void* vptr, const char* value) { auto variant = reinterpret_cast(vptr); *variant = value; } +void dos_qvariant_setQObject(void* vptr, void* value) +{ + auto variant = reinterpret_cast(vptr); + auto qobject = reinterpret_cast(value); + variant->setValue(qobject); +} + +void dos_qvariant_setQAbstractListModel(void* vptr, void* value) +{ + auto variant = reinterpret_cast(vptr); + auto qobject = reinterpret_cast(value); + variant->setValue(qobject); +} + void dos_qobject_create(void** vptr, void* dObjectPointer, DObjectCallback dObjectCallback) { - auto dynamicQObject = new DynamicQObject(); + auto dynamicQObject = new BaseQObject(); QQmlEngine::setObjectOwnership(dynamicQObject, QQmlEngine::CppOwnership); dynamicQObject->setDObjectPointer(dObjectPointer); dynamicQObject->setDObjectCallback(dObjectCallback); *vptr = dynamicQObject; } -void dos_qobject_delete(void *vptr) +void dos_qobject_delete(void* vptr) { - auto dynamicQObject = reinterpret_cast(vptr); + auto dynamicQObject = reinterpret_cast(vptr); dynamicQObject->disconnect(); delete dynamicQObject; } @@ -223,7 +328,8 @@ void dos_qobject_slot_create(void* vptr, const char* name, int parametersCount, if (parametersCount <= 0) return; - auto dynamicQObject = reinterpret_cast(vptr); + auto qobject = reinterpret_cast(vptr); + auto dynamicQObject = dynamic_cast(qobject); QMetaType::Type returnType = static_cast(parametersMetaTypes[0]); QList argumentsTypes; @@ -238,7 +344,8 @@ void dos_qobject_signal_create(void* vptr, const char* name, int parametersCount if (parametersCount <= 0) return; - auto dynamicQObject = reinterpret_cast(vptr); + auto qobject = reinterpret_cast(vptr); + auto dynamicQObject = dynamic_cast(qobject); QList argumentsTypes; for (int i = 0; i < parametersCount; ++i) @@ -249,14 +356,16 @@ void dos_qobject_signal_create(void* vptr, const char* name, int parametersCount void dos_qobject_signal_emit(void* vptr, const char* name, int parametersCount, void** parameters) { - auto dynamicQObject = reinterpret_cast(vptr); + auto qobject = reinterpret_cast(vptr); + auto dynamicQObject = dynamic_cast(qobject); + QVariantList arguments; for (int i = 0; i < parametersCount; ++i) arguments << *(reinterpret_cast(parameters[i])); + dynamicQObject->emitSignal(QString::fromStdString(name), arguments); } - void dos_qobject_property_create(void* vptr, const char* name, int type, @@ -264,10 +373,175 @@ void dos_qobject_property_create(void* vptr, const char* writeSlot, const char* notifySignal) { - auto dynamicQObject = reinterpret_cast(vptr); + auto qobject = reinterpret_cast(vptr); + auto dynamicQObject = dynamic_cast(qobject); dynamicQObject->registerProperty(QString(name), QMetaType::Type(type), QString(readSlot), QString(writeSlot), QString(notifySignal)); } + +void dos_qmodelindex_create(void** vptr) +{ + auto index = new QModelIndex(); + *vptr = index; +} + +void dos_qmodelindex_delete(void* vptr) +{ + auto index = reinterpret_cast(vptr); + delete index; +} + +void dos_qmodelindex_row(void* vptr, int* row) +{ + auto index = reinterpret_cast(vptr); + *row = index->row(); +} + +void dos_qmodelindex_column(void* vptr, int* column) +{ + auto index = reinterpret_cast(vptr); + *column = index->column(); +} + +void dos_qmodelindex_isValid(void* vptr, bool* isValid) +{ + auto index = reinterpret_cast(vptr); + *isValid = index->isValid(); +} + +void dos_qmodelindex_data(void* vptr, int role, void* data) +{ + auto index = reinterpret_cast(vptr); + auto result = reinterpret_cast(data); + *result = index->data(role); +} + +void dos_qmodelindex_parent(void* vptr, void* parent) +{ + auto index = reinterpret_cast(vptr); + auto parentIndex = reinterpret_cast(parent); + *parentIndex = index->parent(); +} + +void dos_qmodelindex_child(void* vptr, int row, int column, void* child) +{ + auto index = reinterpret_cast(vptr); + auto childIndex = reinterpret_cast(child); + *childIndex = index->child(row, column); +} + +void dos_qmodelindex_sibling(void* vptr, int row, int column, void* sibling) +{ + auto index = reinterpret_cast(vptr); + auto siblingIndex = reinterpret_cast(sibling); + *siblingIndex = index->sibling(row, column); +} + +void dos_qabstractlistmodel_create(void** vptr, + void* modelObject, + DObjectCallback dObjectCallback, + RowCountCallback rowCountCallback, + ColumnCountCallback columnCountCallback, + DataCallback dataCallback, + SetDataCallback setDataCallback, + RoleNamesCallback roleNamesCallaback, + FlagsCallback flagsCallback, + HeaderDataCallback headerDataCallback) +{ + auto model = new BaseQAbstractListModel(modelObject, + rowCountCallback, + columnCountCallback, + dataCallback, + setDataCallback, + roleNamesCallaback, + flagsCallback, + headerDataCallback); + model->setDObjectPointer(modelObject); + model->setDObjectCallback(dObjectCallback); + *vptr = model; +} + +void dos_qabstractlistmodel_delete(void* vptr) +{ + auto model = reinterpret_cast(vptr); + delete model; +} + +void dos_qabstractlistmodel_beginInsertRows(void* vptr, QModelIndexVoidPtr parentIndex, int first, int last) +{ + auto model = reinterpret_cast(vptr); + auto index = reinterpret_cast(parentIndex); + model->publicBeginInsertRows(*index, first, last); +} + +void dos_qabstractlistmodel_endInsertRows(void* vptr) +{ + auto model = reinterpret_cast(vptr); + model->publicEndInsertRows(); +} + +void dos_qabstractlistmodel_beginRemoveRows(void* vptr, QModelIndexVoidPtr parentIndex, int first, int last) +{ + auto model = reinterpret_cast(vptr); + auto index = reinterpret_cast(parentIndex); + model->publicBeginRemoveRows(*index, first, last); +} + +void dos_qabstractlistmodel_endRemoveRows(void* vptr) +{ + auto model = reinterpret_cast(vptr); + model->publicEndRemoveRows(); +} + +void dos_qabstractlistmodel_beginResetModel(void* vptr) +{ + auto model = reinterpret_cast(vptr); + model->publicBeginResetModel(); +} + +void dos_qabstractlistmodel_endResetModel(void* vptr) +{ + auto model = reinterpret_cast(vptr); + model->publicEndResetModel(); +} + +void dos_qabstractlistmodel_dataChanged(void* vptr, + QModelIndexVoidPtr topLeftIndex, + QModelIndexVoidPtr bottomRightIndex, + int* rolesArrayPtr, + int rolesArrayLength) +{ + auto model = reinterpret_cast(vptr); + auto topLeft = reinterpret_cast(topLeftIndex); + auto bottomRight = reinterpret_cast(bottomRightIndex); + auto roles = QVector::fromStdVector(std::vector(rolesArrayPtr, rolesArrayPtr + rolesArrayLength)); + model->publicDataChanged(*topLeft, *bottomRight, roles); +} + +void dos_qhash_int_qbytearray_create(QHashIntQByteArrayVoidPtr* vptr) +{ + *vptr = new QHash(); +} + +void dos_qhash_int_qbytearray_delete(QHashIntQByteArrayVoidPtr vptr) +{ + auto qHash = reinterpret_cast*>(vptr); + delete qHash; +} + +void dos_qhash_int_qbytearray_insert(QHashIntQByteArrayVoidPtr vptr, int key, const char* value) +{ + auto qHash = reinterpret_cast*>(vptr); + qHash->insert(key, QByteArray(value)); +} + +void dos_qhash_int_qbytearray_value(QHashIntQByteArrayVoidPtr vptr, int key, char** result) +{ + auto qHash = reinterpret_cast*>(vptr); + QByteArray value = qHash->value(key); + *result = qstrdup(value.data()); +} + diff --git a/DOtherSide/DOtherSide/DOtherSide.h b/DOtherSide/DOtherSide/DOtherSide.h index cb1abe7..54a90ea 100644 --- a/DOtherSide/DOtherSide/DOtherSide.h +++ b/DOtherSide/DOtherSide/DOtherSide.h @@ -1,86 +1,154 @@ #ifndef DOTHERSIDE_H #define DOTHERSIDE_H +#include "DOtherSideTypes.h" + #ifdef __cplusplus extern "C" { #endif - typedef char* CharPtr; - typedef void(*Function)(void*); - typedef void(*DObjectCallback)(void*, void*, int, void**); +typedef char* CharPtr; +typedef void(*Function)(void*); +typedef void(*DObjectCallback)(void*, void*, int, void**); - // QGuiApplication - void dos_qguiapplication_create(); - void dos_qguiapplication_exec(); - void dos_qguiapplication_delete(); +// QGuiApplication +void dos_qguiapplication_create(); +void dos_qguiapplication_exec(); +void dos_qguiapplication_quit(); +void dos_qguiapplication_delete(); - /// QQmlApplicationEngine - void dos_qqmlapplicationengine_create(void** vptr); - void dos_qqmlapplicationengine_load(void* vptr, const char* filename); - void dos_qqmlapplicationengine_context(void* vptr, void** context); - void dos_qqmlapplicationengine_delete(void* vptr); +// QApplication +void dos_qapplication_create(); +void dos_qapplication_exec(); +void dos_qapplication_quit(); +void dos_qapplication_delete(); - // QQuickView - void dos_qquickview_create(void** vptr); - void dos_qquickview_show(void* vptr); - void dos_qquickview_source(void* vptr, CharPtr& result, int& length); - void dos_qquickview_set_source(void* vptr, const char* filename); - void dos_qquickview_delete(void* vptr); - void dos_qquickview_rootContext(void* vptr, void** context); +// QQmlApplicationEngine +void dos_qqmlapplicationengine_create(void** vptr); +void dos_qqmlapplicationengine_load(void* vptr, const char* filename); +void dos_qqmlapplicationengine_context(void* vptr, void** context); +void dos_qqmlapplicationengine_delete(void* vptr); - // QQmlContext - void dos_qqmlcontext_baseUrl(void* vptr, CharPtr& result, int& length); - void dos_qqmlcontext_setcontextproperty(void* vptr, const char* name, void* value); +// QQuickView +void dos_qquickview_create(void** vptr); +void dos_qquickview_show(void* vptr); +void dos_qquickview_source(void* vptr, char** result, int* length); +void dos_qquickview_set_source(void* vptr, const char* filename); +void dos_qquickview_delete(void* vptr); +void dos_qquickview_rootContext(void* vptr, void** context); - // CharArray - void dos_chararray_create(CharPtr& ptr, int size); - void dos_chararray_delete(CharPtr ptr); +// QQmlContext +void dos_qqmlcontext_baseUrl(void* vptr, char** result, int* length); +void dos_qqmlcontext_setcontextproperty(void* vptr, const char* name, void* value); - // QVariant - void dos_qvariant_create(void **vptr); - void dos_qvariant_create_int(void **vptr, int value); - void dos_qvariant_create_bool(void **vptr, bool value); - void dos_qvariant_create_string(void **vptr, const char* value); - void dos_qvariant_create_qobject(void **vptr, void* value); - void dos_qvariant_toInt(void* vptr, int& value); - void dos_qvariant_setInt(void* vptr, int value); - void dos_qvariant_toBool(void* vptr, bool& value); - void dos_qvariant_setBool(void* vptr, bool value); - void dos_qvariant_toString(void* vptr, CharPtr& ptr, int& size); - void dos_qvariant_setString(void* vptr, const char* value); - void dos_qvariant_isnull(void *vptr, bool& isNull); - void dos_qvariant_delete(void *vptr); +// CharArray +void dos_chararray_create(char** ptr, int size); +void dos_chararray_delete(char* ptr); - // QObject - void dos_qobject_create(void **vptr, - void *dObjectPointer, - DObjectCallback dObjectCallback); +// QVariant +void dos_qvariant_create(void** vptr); +void dos_qvariant_create_int(void** vptr, int value); +void dos_qvariant_create_bool(void** vptr, bool value); +void dos_qvariant_create_string(void** vptr, const char* value); +void dos_qvariant_create_qobject(void** vptr, void* value); +void dos_qvariant_create_qvariant(void** vptr, void* value); +void dos_qvariant_create_float(void** vptr, float value); +void dos_qvariant_create_double(void** vptr, double value); +void dos_qvariant_create_qabstractlistmodel(void** vptr, void* value); +void dos_qvariant_toInt(void* vptr, int* value); +void dos_qvariant_setInt(void* vptr, int value); +void dos_qvariant_toBool(void* vptr, bool* value); +void dos_qvariant_setBool(void* vptr, bool value); +void dos_qvariant_toFloat(void* vptr, float* value); +void dos_qvariant_setFloat(void* vptr, float value); +void dos_qvariant_toDouble(void* vptr, double* value); +void dos_qvariant_setDouble(void* vptr, double value); +void dos_qvariant_toString(void* vptr, char** ptr, int* size); +void dos_qvariant_setString(void* vptr, const char* value); +void dos_qvariant_setQObject(void* vptr, void* value); +void dos_qvariant_setQAbstractListModel(void* vptr, void* value); +void dos_qvariant_isnull(void* vptr, bool* isNull); +void dos_qvariant_delete(void* vptr); +void dos_qvariant_assign(void* vptr, void* other); - void dos_qobject_slot_create(void* vptr, - const char* name, - int parametersCount, - int* parametersMetaTypes, - int* slotIndex); +// QObject +void dos_qobject_create(void** vptr, + void* dObjectPointer, + DObjectCallback dObjectCallback); - void dos_qobject_signal_create(void* vptr, - const char* name, - int parametersCount, - int* parametersMetaTypes, - int* signalIndex); +void dos_qobject_slot_create(void* vptr, + const char* name, + int parametersCount, + int* parametersMetaTypes, + int* slotIndex); - void dos_qobject_signal_emit(void* vptr, - const char* name, - int parametersCount, - void** parameters); +void dos_qobject_signal_create(void* vptr, + const char* name, + int parametersCount, + int* parametersMetaTypes, + int* signalIndex); - void dos_qobject_property_create(void* vptr, - const char* name, - int propertyMetaType, - const char* readSlot, - const char* writeSlot, - const char* notifySignal); - void dos_qobject_delete(void *vptr); +void dos_qobject_signal_emit(void* vptr, + const char* name, + int parametersCount, + void** parameters); + +void dos_qobject_property_create(void* vptr, + const char* name, + int propertyMetaType, + const char* readSlot, + const char* writeSlot, + const char* notifySignal); +void dos_qobject_delete(void* vptr); + +// QModelIndex +void dos_qmodelindex_create(void** vptr); +void dos_qmodelindex_delete(void* vptr); +void dos_qmodelindex_row(void* vptr, int* row); +void dos_qmodelindex_column(void* vptr, int* column); +void dos_qmodelindex_isValid(void* vptr, bool* isValid); +void dos_qmodelindex_data(void* vptr, int role, void* data); +void dos_qmodelindex_parent(void* vptr, void* parent); +void dos_qmodelindex_child(void* vptr, int row, int column, void* child); +void dos_qmodelindex_sibling(void* vptr, int row, int column, void* sibling); + +// QHash +void dos_qhash_int_qbytearray_create(QHashIntQByteArrayVoidPtr* vptr); +void dos_qhash_int_qbytearray_delete(QHashIntQByteArrayVoidPtr vptr); +void dos_qhash_int_qbytearray_insert(QHashIntQByteArrayVoidPtr vptr, int key, const char* value); +void dos_qhash_int_qbytearray_value(QHashIntQByteArrayVoidPtr vptr, int key, char** result); + +// QAbstractListModel +void dos_qabstractlistmodel_create(void** vptr, + void* callbackObject, + DObjectCallback dObjectCallback, + RowCountCallback rowCountCallback, + ColumnCountCallback columnCountCallback, + DataCallback dataCallback, + SetDataCallback setDataCallback, + RoleNamesCallback roleNamesCallback, + FlagsCallback flagsCallback, + HeaderDataCallback headerDataCallback); +void dos_qabstractlistmodel_beginInsertRows(void* vptr, + QModelIndexVoidPtr parentIndex, + int first, + int last); +void dos_qabstractlistmodel_endInsertRows(void* vptr); +void dos_qabstractlistmodel_beginRemoveRows(void* vptr, + QModelIndexVoidPtr parentIndex, + int first, + int last); +void dos_qabstractlistmodel_endRemoveRows(void* vptr); +void dos_qabstractlistmodel_beginResetModel(void* vptr); +void dos_qabstractlistmodel_endResetModel(void* vptr); +void dos_qabstractlistmodel_dataChanged(void* vptr, + QModelIndexVoidPtr topLeftIndex, + QModelIndexVoidPtr bottomRightIndex, + int* rolesArrayPtr, + int rolesArrayLength); + +void dos_qabstractlistmodel_delete(void* vptr); #ifdef __cplusplus } diff --git a/DOtherSide/DOtherSide/DOtherSideTypes.h b/DOtherSide/DOtherSide/DOtherSideTypes.h new file mode 100644 index 0000000..0eb6a40 --- /dev/null +++ b/DOtherSide/DOtherSide/DOtherSideTypes.h @@ -0,0 +1,27 @@ +#ifndef DOTHERSIDETYPES_H +#define DOTHERSIDETYPES_H + +// Raw data types +typedef bool* BoolPtr; +typedef int* IntPtr; +typedef char* CharPtr; +typedef const char* ConstCharPtr; +typedef void* QVariantVoidPtr; +typedef void* QModelIndexVoidPtr; +typedef void* QAbstractListModelVoidPtr; +typedef void* QQmlApplicationEngineVoidPtr; +typedef void* QQuickViewVoidPtr; +typedef void* QQmlContextVoidPtr; +typedef void* QHashIntQByteArrayVoidPtr; + +// Raw function types +typedef void (*Function)(void*); +typedef void (*DObjectCallback)(void*, void*, int, void**); +typedef void (*RowCountCallback)(void* model, QModelIndexVoidPtr index, IntPtr result); +typedef void (*ColumnCountCallback)(void* model, QModelIndexVoidPtr index, IntPtr result); +typedef void (*DataCallback)(void* model, QModelIndexVoidPtr index, int role, QVariantVoidPtr result); +typedef void (*SetDataCallback) (void* model, QModelIndexVoidPtr index, QVariantVoidPtr value, int role, BoolPtr result); +typedef void (*RoleNamesCallback)(void* model, QHashIntQByteArrayVoidPtr result); +typedef void (*FlagsCallback) (void* model, QModelIndexVoidPtr index, IntPtr result); +typedef void (*HeaderDataCallback) (void* model, int section, int orientation, int role, QVariantVoidPtr result); +#endif \ No newline at end of file diff --git a/DOtherSide/DynamicQObject/CMakeLists.txt b/DOtherSide/DynamicQObject/CMakeLists.txt index 5dfc420..8a996d9 100644 --- a/DOtherSide/DynamicQObject/CMakeLists.txt +++ b/DOtherSide/DynamicQObject/CMakeLists.txt @@ -15,7 +15,6 @@ set(HEADERS_LIST set(SRC_LIST DynamicProperty.cpp - DynamicQObject.cpp DynamicSignal.cpp DynamicSlot.cpp ) diff --git a/DOtherSide/DynamicQObject/DynamicProperty.cpp b/DOtherSide/DynamicQObject/DynamicProperty.cpp deleted file mode 100644 index 7682490..0000000 --- a/DOtherSide/DynamicQObject/DynamicProperty.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "DynamicProperty.h" -#include - -struct PropertyData -{ - PropertyData(const QString& name, - QMetaType::Type type, - const QString& readSlotName, - const QString& writeSlotName, - const QString& notifySignalName) - : name(name) - , type(type) - , readSlotName(readSlotName) - , writeSlotName(writeSlotName) - , notifySignalName(notifySignalName) - {} - - PropertyData(const PropertyData& other) - : name(other.name) - , type(other.type) - , readSlotName(other.readSlotName) - , writeSlotName(other.writeSlotName) - , notifySignalName(other.notifySignalName) - {} - - PropertyData& operator=(const PropertyData& other) - { - name = other.name; - type = other.type; - readSlotName = other.readSlotName; - writeSlotName = other.writeSlotName; - notifySignalName = other.notifySignalName; - return *this; - } - - QString name; - QMetaType::Type type; - QString readSlotName; - QString writeSlotName; - QString notifySignalName; -}; - -DynamicProperty::DynamicProperty() - : d(nullptr) -{} - -DynamicProperty::DynamicProperty(const QString& name, QMetaType::Type type, const QString& readSlotName, const QString& writeSlotName, const QString& notifySignalName) - : d(new PropertyData(name, type, readSlotName, writeSlotName, notifySignalName)) -{ -} - -DynamicProperty::DynamicProperty(const DynamicProperty& other) - : d(nullptr) -{ - if (other.d) - d.reset(new PropertyData(*other.d)); -} - -DynamicProperty&DynamicProperty::operator=(const DynamicProperty& other) -{ - if (!other.d && d) - { - d.reset(); - } - else if (other.d && !d) - { - d.reset(new PropertyData(*other.d)); - } - else if (other.d && d) - { - *d = *other.d; - } - - return *this; -} - -DynamicProperty::~DynamicProperty() -{} - -QString DynamicProperty::name() const -{ - return d->name; -} - -QMetaType::Type DynamicProperty::type() const -{ - return d->type; -} - -bool DynamicProperty::isReadable() const -{ - return !d->readSlotName.isEmpty(); -} - -bool DynamicProperty::isWriteable() const -{ - return !d->writeSlotName.isEmpty(); -} - -bool DynamicProperty::hasNotifySignal() const -{ - return !d->notifySignalName.isEmpty(); -} - -QString DynamicProperty::readSlot() const -{ - return d->readSlotName; -} - -QString DynamicProperty::writeSlot() const -{ - return d->writeSlotName; -} - -QString DynamicProperty::notifySignal() const -{ - return d->notifySignalName; -} - diff --git a/DOtherSide/DynamicQObject/DynamicProperty.h b/DOtherSide/DynamicQObject/DynamicProperty.h index b99056a..b3b3dd0 100644 --- a/DOtherSide/DynamicQObject/DynamicProperty.h +++ b/DOtherSide/DynamicQObject/DynamicProperty.h @@ -1,10 +1,10 @@ -#pragma once +#ifndef DYNAMICPROPERTY_H +#define DYNAMICPROPERTY_H +#include #include #include -#include -class QString; class PropertyData; class DynamicProperty @@ -23,7 +23,9 @@ public: QString name() const; QMetaType::Type type() const; - bool isValid() const { return d != nullptr; } + bool isValid() const { + return d != nullptr; + } bool isReadable() const; bool isWriteable() const; @@ -36,3 +38,115 @@ public: private: std::unique_ptr d; }; + +struct PropertyData +{ + PropertyData(const QString& name, + QMetaType::Type type, + const QString& readSlotName, + const QString& writeSlotName, + const QString& notifySignalName) + : name(name) + , type(type) + , readSlotName(readSlotName) + , writeSlotName(writeSlotName) + , notifySignalName(notifySignalName) + {} + + PropertyData(const PropertyData& other) + : name(other.name) + , type(other.type) + , readSlotName(other.readSlotName) + , writeSlotName(other.writeSlotName) + , notifySignalName(other.notifySignalName) + {} + + PropertyData& operator=(const PropertyData& other) + { + name = other.name; + type = other.type; + readSlotName = other.readSlotName; + writeSlotName = other.writeSlotName; + notifySignalName = other.notifySignalName; + return *this; + } + + QString name; + QMetaType::Type type; + QString readSlotName; + QString writeSlotName; + QString notifySignalName; +}; + +DynamicProperty::DynamicProperty() + : d(nullptr) +{} + +DynamicProperty::DynamicProperty(const QString& name, QMetaType::Type type, const QString& readSlotName, const QString& writeSlotName, const QString& notifySignalName) + : d(new PropertyData(name, type, readSlotName, writeSlotName, notifySignalName)) +{ +} + +DynamicProperty::DynamicProperty(const DynamicProperty& other) + : d(nullptr) +{ + if (other.d) + d.reset(new PropertyData(*other.d)); +} + +DynamicProperty& DynamicProperty::operator=(const DynamicProperty& other) +{ + if (!other.d && d) + d.reset(); + else if (other.d && !d) + d.reset(new PropertyData(*other.d)); + else if (other.d && d) + *d = *other.d; + + return *this; +} + +DynamicProperty::~DynamicProperty() +{} + +QString DynamicProperty::name() const +{ + return d->name; +} + +QMetaType::Type DynamicProperty::type() const +{ + return d->type; +} + +bool DynamicProperty::isReadable() const +{ + return !d->readSlotName.isEmpty(); +} + +bool DynamicProperty::isWriteable() const +{ + return !d->writeSlotName.isEmpty(); +} + +bool DynamicProperty::hasNotifySignal() const +{ + return !d->notifySignalName.isEmpty(); +} + +QString DynamicProperty::readSlot() const +{ + return d->readSlotName; +} + +QString DynamicProperty::writeSlot() const +{ + return d->writeSlotName; +} + +QString DynamicProperty::notifySignal() const +{ + return d->notifySignalName; +} + +#endif \ No newline at end of file diff --git a/DOtherSide/DynamicQObject/DynamicQObject.cpp b/DOtherSide/DynamicQObject/DynamicQObject.cpp deleted file mode 100644 index dfd3f26..0000000 --- a/DOtherSide/DynamicQObject/DynamicQObject.cpp +++ /dev/null @@ -1,345 +0,0 @@ -#include "DynamicQObject.h" -#include -#include -#include -#include "private/qmetaobjectbuilder_p.h" - -DynamicQObject::DynamicQObject() - : m_dObjectPointer(nullptr) - , m_dObjectCallback(nullptr) -{ - QMetaObjectBuilder builder; - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder.setClassName("DynamicQObject"); - builder.setSuperClass(&QObject::staticMetaObject); - m_metaObject.reset(builder.toMetaObject()); -} - -DynamicQObject::~DynamicQObject() = default; - -bool DynamicQObject::registerSlot(const QString& name, - const QMetaType::Type returnType, - const QList& argumentsTypes, - int& slotIndex) -{ - DynamicSlot slot (name, returnType, argumentsTypes); - - if (m_slotsBySignature.contains(slot.signature())) - return false; - - m_slotsByName.insertMulti(slot.name(), slot); - m_slotsBySignature[slot.signature()] = slot; - - auto afterSignalAdded = [](QMetaObjectBuilder&) {}; - auto afterPropertyAdded = afterSignalAdded; - - auto afterSlotAdded = [&slot, returnType](QMetaObjectBuilder& metaObjectBuilder) { - QMetaMethodBuilder methodBuilder = metaObjectBuilder.addSlot(slot.signature()); - methodBuilder.setReturnType(QMetaType::typeName(returnType)); - methodBuilder.setAttributes(QMetaMethod::Scriptable); - }; - - auto newMetaObject = recreateMetaObjectBuilder(m_metaObject.data() - , afterSignalAdded - , afterSlotAdded - , afterPropertyAdded); - - m_metaObject.reset(newMetaObject); - - slotIndex = m_metaObject->indexOfSlot(QMetaObject::normalizedSignature(slot.signature())); - - return slotIndex != -1; -} - -bool DynamicQObject::registerSignal(const QString& name, const QList& arguments, int& signalIndex) -{ - DynamicSignal signal(name, arguments); - - if (m_signalsBySignature.contains(signal.signature())) - return false; - - m_signalsByName.insertMulti(signal.name(), signal); - m_signalsBySignature[signal.signature()] = signal; - - auto afterSignalAdded = [&signal](QMetaObjectBuilder& metaObjectBuilder) { - QMetaMethodBuilder methodBuilder = metaObjectBuilder.addSignal(signal.signature()); - methodBuilder.setReturnType(QMetaType::typeName(QMetaType::Void)); - methodBuilder.setAccess(QMetaMethod::Public); - }; - - auto afterSlotAdded = [](QMetaObjectBuilder&) { }; - auto afterPropertyAdded = afterSlotAdded; - - auto newMetaObject = recreateMetaObjectBuilder(m_metaObject.data() - , afterSignalAdded - , afterSlotAdded - , afterPropertyAdded); - - m_metaObject.reset(newMetaObject); - - signalIndex = m_metaObject->indexOfSignal(QMetaObject::normalizedSignature(signal.signature())); - - return signalIndex != -1; -} - -bool DynamicQObject::registerProperty(const QString& name, - QMetaType::Type type, - const QString& readSlotName, - const QString& writeSlotName, - const QString& notifySignalName) -{ - DynamicProperty property(name, type, readSlotName, writeSlotName, notifySignalName); - - DynamicSignal notifySignal; - - if (!notifySignalName.isEmpty()) - { - notifySignal = m_signalsByName.value(notifySignalName, DynamicSignal()); - if (!notifySignal.isValid()) - return false; - } - - m_propertiesByName.insert(name.toUtf8(), property); - - auto afterSignalAdded = [](QMetaObjectBuilder& metaObjectBuilder) {}; - auto afterSlotAdded = [](QMetaObjectBuilder& metaObjectBuilder) {}; - auto afterPropertyAdded = [name, type, notifySignal](QMetaObjectBuilder& metaObjectBuilder) - { - int signalIndex = -1; - if (notifySignal.isValid()) - { - for (int i = 0; i < metaObjectBuilder.methodCount(); ++i) - { - QMetaMethodBuilder methodBuilder = metaObjectBuilder.method(i); - if (methodBuilder.methodType() == QMetaMethod::Signal) - { - if (methodBuilder.signature() == QMetaObject::normalizedSignature(notifySignal.signature())); - { - signalIndex = i; - break; - } - } - } - - } - - auto typeName = QMetaType::typeName(type); - auto builder = metaObjectBuilder.addProperty(name.toUtf8(), - QMetaObject::normalizedType(typeName), - signalIndex); - }; - - auto newMetaObject = recreateMetaObjectBuilder(m_metaObject.data() - , afterSignalAdded - , afterSlotAdded - , afterPropertyAdded); - m_metaObject.reset(newMetaObject); - return true; -} - -bool DynamicQObject::emitSignal(const QString& name, const QList& args) -{ - DynamicSignal signal; - - for (DynamicSignal currentSignal : m_signalsByName.values(name)) - { - if (currentSignal.validate(args)) - { - signal = currentSignal; - break; - } - } - - if (!signal.isValid()) - return false; - - int index = m_metaObject->indexOfSignal(QMetaObject::normalizedSignature(signal.signature())); - if (index < 0) - return false; - - QVariantList argsCopy = args; - - QVector arguments(argsCopy.size() + 1 ,0); - arguments[0] = 0; - for (int i = 0; i < argsCopy.size(); ++i) - arguments[i + 1] = &argsCopy[i]; - - QMetaObject::activate(this, index, arguments.data()); - - return true; -} - -const QMetaObject* DynamicQObject::metaObject() const -{ - return m_metaObject.data(); -} - -bool DynamicQObject::executeSlot(const DynamicSlot& slot, void** args) -{ - if (!slot.isValid()) - return false; - - QList arguments; - for (int i = 0; i < slot.argumentsTypes().count(); ++i) - arguments << QVariant(slot.argumentTypeAt(i), args[i+1]); - - QVariant result = executeSlot(slot, arguments); - - if (slot.returnType() != QMetaType::Void && result.isValid()) - { - QMetaType metatype(slot.returnType()); - metatype.construct(args[0], result.constData()); - } - - return true; -} - -QVariant DynamicQObject::executeSlot(const DynamicSlot& slot, const QList& args) -{ - QVariant slotName(slot.name()); - - const int numParametersPlusReturn = slot.argumentsTypes().count() + 1; - std::vector argumentsAsVariants(numParametersPlusReturn); - std::vector argumentsAsVoidPointers(numParametersPlusReturn); - - for (int i = 0; i < numParametersPlusReturn; ++i) { - argumentsAsVariants[i] = i == 0 ? QVariant() : args[i-1]; - argumentsAsVoidPointers[i] = &argumentsAsVariants[i]; - } - - if (m_dObjectCallback && m_dObjectPointer) - m_dObjectCallback(m_dObjectPointer, &slotName, numParametersPlusReturn, &argumentsAsVoidPointers[0]); - - return argumentsAsVariants[0]; -} - -bool DynamicQObject::readProperty(const DynamicProperty& property, void** args) -{ - if (!property.isValid()) - return false; - - if (!property.isReadable()) - return false; - - DynamicSlot readSlot = m_slotsByName.value(property.readSlot(), DynamicSlot()); - - if (!readSlot.isValid()) - return false; - - if (readSlot.argumentsTypes().count() > 0) - return false; - - if (readSlot.returnType() != property.type()) - return false; - - return executeSlot(readSlot, args); -} - -bool DynamicQObject::writeProperty(const DynamicProperty& property, void** args) -{ - if (!property.isValid()) - return false; - - if (!property.isWriteable()) - return false; - - DynamicSlot writeSlot = m_slotsByName.value(property.writeSlot(), DynamicSlot()); - - if (!writeSlot.isValid()) - return false; - - if (writeSlot.argumentsTypes().count() != 1) - return false; - - if (writeSlot.returnType() != QMetaType::Void) - return false; - - QVariant newValue (writeSlot.argumentTypeAt(0), args[0]); - executeSlot(writeSlot, {newValue}); -} - -int DynamicQObject::qt_metacall(QMetaObject::Call callType, int index, void** args) -{ - if (callType == QMetaObject::InvokeMetaMethod) - { - QMetaMethod method = m_metaObject->method(index); - - if (!method.isValid()) - return -1; - - DynamicSlot slot = m_slotsBySignature[method.methodSignature()]; - return executeSlot(slot, args) ? 1 : -1; - } - else if (callType == QMetaObject::ReadProperty) - { - QMetaProperty metaProperty = m_metaObject->property(index); - - if (!metaProperty.isValid()) - return -1; - - DynamicProperty dynamicProperty = m_propertiesByName.value(metaProperty.name(), DynamicProperty()); - return readProperty(dynamicProperty, args) ? 1 : -1; - } - else if (callType == QMetaObject::WriteProperty) - { - QMetaProperty metaProperty = m_metaObject->property(index); - - if (!metaProperty.isValid()) - return -1; - - DynamicProperty dynamicProperty = m_propertiesByName.value(metaProperty.name(), DynamicProperty()); - return writeProperty(dynamicProperty, args) ? 1 : -1; - } - - return -1; -} - -QMetaObject* DynamicQObject::recreateMetaObjectBuilder(QMetaObject* currentMetaObject, - const std::function& afterSignalAdded, - const std::function& afterSlotAdded, - const std::function& afterPropertyAdded) -{ - // Collect the current methods and signals - QList signalsList; - QList methodsList; - QList propertiesList; - - for (int i = currentMetaObject->methodOffset(); i < currentMetaObject->methodCount(); ++i) - { - QMetaMethod method = currentMetaObject->method(i); - if (method.methodType() == QMetaMethod::Signal) - signalsList.append(method); - else - methodsList.append(method); - } - - for (int i = currentMetaObject->propertyOffset(); i < currentMetaObject->propertyCount(); ++i) - { - QMetaProperty property = currentMetaObject->property(i); - propertiesList.append(property); - } - - QMetaObjectBuilder metaObjectBuilder; - metaObjectBuilder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - metaObjectBuilder.setClassName(currentMetaObject->className()); - metaObjectBuilder.setSuperClass(currentMetaObject->superClass()); - - foreach(auto& method, signalsList) - metaObjectBuilder.addMethod(method); - - // Call custom code to be executed after signal have been added - afterSignalAdded(metaObjectBuilder); - - foreach (auto& method, methodsList) - metaObjectBuilder.addMethod(method); - - // Call custom code to be executed after slots have been added - afterSlotAdded(metaObjectBuilder); - - foreach (auto& property, propertiesList) - metaObjectBuilder.addProperty(property); - - afterPropertyAdded(metaObjectBuilder); - - return metaObjectBuilder.toMetaObject(); -} diff --git a/DOtherSide/DynamicQObject/DynamicQObject.h b/DOtherSide/DynamicQObject/DynamicQObject.h index 00bf636..6f5c3a2 100644 --- a/DOtherSide/DynamicQObject/DynamicQObject.h +++ b/DOtherSide/DynamicQObject/DynamicQObject.h @@ -1,19 +1,22 @@ -#pragma once +#ifndef DYNAMICQOBJECT_H +#define DYNAMICQOBJECT_H -#include -#include +#include +#include +#include +#include +#include +#include "private/qmetaobjectbuilder_p.h" #include "DynamicSignal.h" #include "DynamicSlot.h" #include "DynamicProperty.h" - -class QMetaObjectBuilder; +#include "IDynamicQObject.h" /// This class implements a QObject to which signals, slots and properties can be added dynamically -class DynamicQObject : public QObject +template +class DynamicQObject : public T, public IDynamicQObject { - typedef void (*Callback)(void*, void*, int, void **); - public: /// Constructor DynamicQObject(); @@ -22,37 +25,37 @@ public: virtual ~DynamicQObject(); /// Sets the function to be called from C++ to D or Nimrod - void setDObjectCallback(Callback callback) { m_dObjectCallback = callback; } + virtual void setDObjectCallback(IDynamicQObject::Callback callback) override; /// Sets the D or Nimrod object that owns this DynamicQObject - void setDObjectPointer(void* dObjectPointer) { m_dObjectPointer = dObjectPointer; } + virtual void setDObjectPointer(void* dObjectPointer) override; /// Register a new signal - bool registerSignal(const QString& name, - const QList& argumentsTypes, - int& signalIndex); + virtual bool registerSignal(const QString& name, + const QList& argumentsTypes, + int& signalIndex) override; /// Register a new slot - bool registerSlot(const QString& name, - const QMetaType::Type returnType, - const QList& argumentsTypes, - int& slotIndex); + virtual bool registerSlot(const QString& name, + const QMetaType::Type returnType, + const QList& argumentsTypes, + int& slotIndex) override; /// Register a new property - bool registerProperty(const QString& name, - QMetaType::Type type, - const QString& readSlotName, - const QString& writeSlotName = "", - const QString& notifySignalName = ""); + virtual bool registerProperty(const QString& name, + QMetaType::Type type, + const QString& readSlotName, + const QString& writeSlotName = "", + const QString& notifySignalName = "") override; /// Emit the signal with the given name and arguments - bool emitSignal(const QString& name, const QList& argumentsValues); + virtual bool emitSignal(const QString& name, const QList& argumentsValues) override; /// Return the QMetaObject for this DynamicQObject - virtual const QMetaObject *metaObject() const; + virtual const QMetaObject* metaObject() const; /// The qt metacall. Called from Qt when a signals, slot or property is invoked - int qt_metacall(QMetaObject::Call, int, void **); + int qt_metacall(QMetaObject::Call, int, void**); private: bool executeSlot(const DynamicSlot& slot, void** args); @@ -67,9 +70,9 @@ private: /// The creation is customizable by injecting custom code after signals and slots have /// been added static QMetaObject* recreateMetaObjectBuilder(QMetaObject* currentMetaObject, - const std::function& afterSignalAdded, - const std::function& afterSlotAdded, - const std::function& afterPropertyAdded); + const std::function& afterSignalAdded, + const std::function& afterSlotAdded, + const std::function& afterPropertyAdded); QHash m_signalsByName; QHash m_signalsBySignature; @@ -78,5 +81,380 @@ private: QHash m_propertiesByName; QScopedPointer m_metaObject; void* m_dObjectPointer; - Callback m_dObjectCallback; + IDynamicQObject::Callback m_dObjectCallback; }; + +template +DynamicQObject::DynamicQObject() + : T() + , m_dObjectPointer(nullptr) + , m_dObjectCallback(nullptr) +{ + QMetaObjectBuilder builder; + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + builder.setClassName("DynamicQObject"); + builder.setSuperClass(&T::staticMetaObject); + m_metaObject.reset(builder.toMetaObject()); +} + +template +void DynamicQObject::setDObjectCallback(IDynamicQObject::Callback callback) +{ + m_dObjectCallback = callback; +} + +template +void DynamicQObject::setDObjectPointer(void* dObjectPointer) +{ + m_dObjectPointer = dObjectPointer; +} + +template +DynamicQObject::~DynamicQObject() = default; + +template +bool DynamicQObject::registerSlot(const QString& name, + const QMetaType::Type returnType, + const QList& argumentsTypes, + int& slotIndex) +{ + DynamicSlot slot(name, returnType, argumentsTypes); + + if (m_slotsBySignature.contains(slot.signature())) + return false; + + m_slotsByName.insertMulti(slot.name(), slot); + m_slotsBySignature[slot.signature()] = slot; + + auto afterSignalAdded = [](QMetaObjectBuilder&) {}; + auto afterPropertyAdded = afterSignalAdded; + + auto afterSlotAdded = [&slot, returnType](QMetaObjectBuilder & metaObjectBuilder) { + QMetaMethodBuilder methodBuilder = metaObjectBuilder.addSlot(slot.signature()); + methodBuilder.setReturnType(QMetaType::typeName(returnType)); + methodBuilder.setAttributes(QMetaMethod::Scriptable); + }; + + auto newMetaObject = recreateMetaObjectBuilder(m_metaObject.data(), + afterSignalAdded, + afterSlotAdded, + afterPropertyAdded); + + m_metaObject.reset(newMetaObject); + + slotIndex = m_metaObject->indexOfSlot(QMetaObject::normalizedSignature(slot.signature())); + + return slotIndex != -1; +} + +template +bool DynamicQObject::registerSignal(const QString& name, const QList& arguments, int& signalIndex) +{ + DynamicSignal signal(name, arguments); + + if (m_signalsBySignature.contains(signal.signature())) + return false; + + m_signalsByName.insertMulti(signal.name(), signal); + m_signalsBySignature[signal.signature()] = signal; + + auto afterSignalAdded = [&signal](QMetaObjectBuilder & metaObjectBuilder) { + QMetaMethodBuilder methodBuilder = metaObjectBuilder.addSignal(signal.signature()); + methodBuilder.setReturnType(QMetaType::typeName(QMetaType::Void)); + methodBuilder.setAccess(QMetaMethod::Public); + }; + + auto afterSlotAdded = [](QMetaObjectBuilder&) {}; + auto afterPropertyAdded = afterSlotAdded; + + auto newMetaObject = recreateMetaObjectBuilder(m_metaObject.data(), + afterSignalAdded, + afterSlotAdded, + afterPropertyAdded); + + m_metaObject.reset(newMetaObject); + + signalIndex = m_metaObject->indexOfSignal(QMetaObject::normalizedSignature(signal.signature())); + + return signalIndex != -1; +} + +template +bool DynamicQObject::registerProperty(const QString& name, + QMetaType::Type type, + const QString& readSlotName, + const QString& writeSlotName, + const QString& notifySignalName) +{ + DynamicProperty property(name, type, readSlotName, writeSlotName, notifySignalName); + + DynamicSignal notifySignal; + + if (!notifySignalName.isEmpty()) + { + notifySignal = m_signalsByName.value(notifySignalName, DynamicSignal()); + if (!notifySignal.isValid()) + return false; + } + + m_propertiesByName.insert(name.toUtf8(), property); + + auto afterSignalAdded = [](QMetaObjectBuilder & metaObjectBuilder) {}; + auto afterSlotAdded = [](QMetaObjectBuilder & metaObjectBuilder) {}; + auto afterPropertyAdded = [name, type, notifySignal](QMetaObjectBuilder & metaObjectBuilder) + { + int signalIndex = -1; + if (notifySignal.isValid()) + { + for (int i = 0; i < metaObjectBuilder.methodCount(); ++i) + { + QMetaMethodBuilder methodBuilder = metaObjectBuilder.method(i); + if (methodBuilder.methodType() == QMetaMethod::Signal) + { + if (methodBuilder.signature() == QMetaObject::normalizedSignature(notifySignal.signature())); + { + signalIndex = i; + break; + } + } + } + + } + + auto typeName = QMetaType::typeName(type); + auto builder = metaObjectBuilder.addProperty(name.toUtf8(), + QMetaObject::normalizedType(typeName), + signalIndex); + if (signalIndex == -1) + builder.setConstant(true); + }; + + auto newMetaObject = recreateMetaObjectBuilder(m_metaObject.data() + , afterSignalAdded + , afterSlotAdded + , afterPropertyAdded); + m_metaObject.reset(newMetaObject); + return true; +} + +template +bool DynamicQObject::emitSignal(const QString& name, const QList& args) +{ + DynamicSignal signal; + + for (DynamicSignal currentSignal : m_signalsByName.values(name)) + { + if (currentSignal.validate(args)) + { + signal = currentSignal; + break; + } + } + + if (!signal.isValid()) + return false; + + int index = m_metaObject->indexOfSignal(QMetaObject::normalizedSignature(signal.signature())); + if (index < 0) + return false; + + QVariantList argsCopy = args; + + QVector arguments(argsCopy.size() + 1 , 0); + arguments[0] = 0; + for (int i = 0; i < argsCopy.size(); ++i) + arguments[i + 1] = &argsCopy[i]; + + QMetaObject::activate(this, index, arguments.data()); + + return true; +} + +template +const QMetaObject* DynamicQObject::metaObject() const +{ + return m_metaObject.data(); +} + +template +bool DynamicQObject::executeSlot(const DynamicSlot& slot, void** args) +{ + if (!slot.isValid()) + return false; + + QList arguments; + for (int i = 0; i < slot.argumentsTypes().count(); ++i) + arguments << QVariant(slot.argumentTypeAt(i), args[i + 1]); + + QVariant result = executeSlot(slot, arguments); + + if (slot.returnType() != QMetaType::Void && result.isValid()) + { + QMetaType metatype(slot.returnType()); + metatype.construct(args[0], result.constData()); + } + + return true; +} + +template +QVariant DynamicQObject::executeSlot(const DynamicSlot& slot, const QList& args) +{ + QVariant slotName(slot.name()); + + const int numParametersPlusReturn = slot.argumentsTypes().count() + 1; + std::vector argumentsAsVariants(numParametersPlusReturn); + std::vector argumentsAsVoidPointers(numParametersPlusReturn); + + for (int i = 0; i < numParametersPlusReturn; ++i) { + argumentsAsVariants[i] = i == 0 ? QVariant() : args[i - 1]; + argumentsAsVoidPointers[i] = &argumentsAsVariants[i]; + } + + if (m_dObjectCallback && m_dObjectPointer) + m_dObjectCallback(m_dObjectPointer, &slotName, numParametersPlusReturn, &argumentsAsVoidPointers[0]); + + return argumentsAsVariants[0]; +} + +template +bool DynamicQObject::readProperty(const DynamicProperty& property, void** args) +{ + if (!property.isValid()) + return false; + + if (!property.isReadable()) + return false; + + DynamicSlot readSlot = m_slotsByName.value(property.readSlot(), DynamicSlot()); + + if (!readSlot.isValid()) + return false; + + if (readSlot.argumentsTypes().count() > 0) + return false; + + if (readSlot.returnType() != property.type()) + return false; + + return executeSlot(readSlot, args); +} + +template +bool DynamicQObject::writeProperty(const DynamicProperty& property, void** args) +{ + if (!property.isValid()) + return false; + + if (!property.isWriteable()) + return false; + + DynamicSlot writeSlot = m_slotsByName.value(property.writeSlot(), DynamicSlot()); + + if (!writeSlot.isValid()) + return false; + + if (writeSlot.argumentsTypes().count() != 1) + return false; + + if (writeSlot.returnType() != QMetaType::Void) + return false; + + QVariant newValue(writeSlot.argumentTypeAt(0), args[0]); + executeSlot(writeSlot, {newValue}); +} + +template +int DynamicQObject::qt_metacall(QMetaObject::Call callType, int index, void** args) +{ + switch (callType) + { + case QMetaObject::InvokeMetaMethod: { + QMetaMethod method = m_metaObject->method(index); + + if (!method.isValid()) + return -1; + + DynamicSlot slot = m_slotsBySignature[method.methodSignature()]; + return executeSlot(slot, args) ? 1 : -1; + } + + case QMetaObject::ReadProperty: { + QMetaProperty metaProperty = m_metaObject->property(index); + + if (!metaProperty.isValid()) + return -1; + + DynamicProperty dynamicProperty = m_propertiesByName.value(metaProperty.name(), DynamicProperty()); + return readProperty(dynamicProperty, args) ? 1 : -1; + } + + case QMetaObject::WriteProperty: { + QMetaProperty metaProperty = m_metaObject->property(index); + + if (!metaProperty.isValid()) + return -1; + + DynamicProperty dynamicProperty = m_propertiesByName.value(metaProperty.name(), DynamicProperty()); + return writeProperty(dynamicProperty, args) ? 1 : -1; + } + + default: + return T::qt_metacall(callType, index, args); + } + + return -1; +} + +template +QMetaObject* DynamicQObject::recreateMetaObjectBuilder(QMetaObject* currentMetaObject, + const std::function& afterSignalAdded, + const std::function& afterSlotAdded, + const std::function& afterPropertyAdded) +{ + // Collect the current methods and signals + QList signalsList; + QList methodsList; + QList propertiesList; + + for (int i = currentMetaObject->methodOffset(); i < currentMetaObject->methodCount(); ++i) + { + QMetaMethod method = currentMetaObject->method(i); + if (method.methodType() == QMetaMethod::Signal) + signalsList.append(method); + else + methodsList.append(method); + } + + for (int i = currentMetaObject->propertyOffset(); i < currentMetaObject->propertyCount(); ++i) + { + QMetaProperty property = currentMetaObject->property(i); + propertiesList.append(property); + } + + QMetaObjectBuilder metaObjectBuilder; + metaObjectBuilder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + metaObjectBuilder.setClassName(currentMetaObject->className()); + metaObjectBuilder.setSuperClass(currentMetaObject->superClass()); + + for (auto& method : signalsList) + metaObjectBuilder.addMethod(method); + + // Call custom code to be executed after signal have been added + afterSignalAdded(metaObjectBuilder); + + for (auto& method : methodsList) + metaObjectBuilder.addMethod(method); + + // Call custom code to be executed after slots have been added + afterSlotAdded(metaObjectBuilder); + + for (auto& property : propertiesList) + metaObjectBuilder.addProperty(property); + + afterPropertyAdded(metaObjectBuilder); + + return metaObjectBuilder.toMetaObject(); +} + +#endif diff --git a/DOtherSide/DynamicQObject/DynamicSignal.cpp b/DOtherSide/DynamicQObject/DynamicSignal.cpp deleted file mode 100644 index 34ad6bf..0000000 --- a/DOtherSide/DynamicQObject/DynamicSignal.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "DynamicSignal.h" -#include "DynamicQObject.h" - -struct SignalData -{ - QString name; - QList argumentsTypes; - QByteArray signature; -}; - -DynamicSignal::DynamicSignal() - : d(nullptr) -{ -} - -DynamicSignal::DynamicSignal(const QString& name, const QList& arguments) - : d(new SignalData()) -{ - d->name = name; - d->signature = QByteArray(); - d->argumentsTypes = arguments; - _initSignature(); -} - -DynamicSignal::DynamicSignal(const DynamicSignal& signal) -{ - if (signal.isValid()) - { - d.reset(new SignalData()); - *d = *signal.d; - } - else - d.reset(nullptr); -} - -DynamicSignal& DynamicSignal::operator=(const DynamicSignal& signal) -{ - if (signal.isValid()) - { - d.reset(new SignalData()); - *d = *signal.d; - } - else - d.reset(nullptr); - - return *this; -} - -DynamicSignal::~DynamicSignal() -{ -} - -bool DynamicSignal::isValid() const -{ - return d != nullptr; -} - -QString DynamicSignal::name() const -{ - return isValid() ? d->name : QString(); -} - -QByteArray DynamicSignal::signature() const -{ - return isValid() ? d->signature : QByteArray(); -} - -bool DynamicSignal::validate(const QVariantList& arguments) -{ - return true; -} - -bool DynamicSignal::validate(const QList& argumentsTypes, const QVariantList& argumentsValues) -{ - return true; -} - -void DynamicSignal::_initSignature() -{ - QString signature("%1(%2)"); - QString arguments; - for (int i = 0; i < d->argumentsTypes.size(); ++i) - { - if (i != 0) - arguments += ','; - arguments += QMetaType::typeName(d->argumentsTypes[i]); - } - - d->signature = signature.arg(d->name, arguments).toUtf8(); -} diff --git a/DOtherSide/DynamicQObject/DynamicSignal.h b/DOtherSide/DynamicQObject/DynamicSignal.h index 3396489..58faaee 100644 --- a/DOtherSide/DynamicQObject/DynamicSignal.h +++ b/DOtherSide/DynamicQObject/DynamicSignal.h @@ -1,10 +1,14 @@ -#pragma once +#ifndef DYNAMICSIGNAL_H +#define DYNAMICSIGNAL_H -#include #include +#include +#include +#include +#include + class SignalData; -class DynamicQObject; class DynamicSignal { @@ -28,3 +32,93 @@ private: std::unique_ptr d; }; + +struct SignalData +{ + QString name; + QList argumentsTypes; + QByteArray signature; +}; + +DynamicSignal::DynamicSignal() + : d(nullptr) +{ +} + +DynamicSignal::DynamicSignal(const QString& name, const QList& arguments) + : d(new SignalData()) +{ + d->name = name; + d->signature = QByteArray(); + d->argumentsTypes = arguments; + _initSignature(); +} + +DynamicSignal::DynamicSignal(const DynamicSignal& signal) +{ + if (signal.isValid()) + { + d.reset(new SignalData()); + *d = *signal.d; + } + else + d.reset(nullptr); +} + +DynamicSignal& DynamicSignal::operator=(const DynamicSignal& signal) +{ + if (signal.isValid()) + { + d.reset(new SignalData()); + *d = *signal.d; + } + else + d.reset(nullptr); + + return *this; +} + +DynamicSignal::~DynamicSignal() +{ +} + +bool DynamicSignal::isValid() const +{ + return d != nullptr; +} + +QString DynamicSignal::name() const +{ + return isValid() ? d->name : QString(); +} + +QByteArray DynamicSignal::signature() const +{ + return isValid() ? d->signature : QByteArray(); +} + +bool DynamicSignal::validate(const QVariantList& arguments) +{ + return true; +} + +bool DynamicSignal::validate(const QList& argumentsTypes, const QVariantList& argumentsValues) +{ + return true; +} + +void DynamicSignal::_initSignature() +{ + QString signature("%1(%2)"); + QString arguments; + for (int i = 0; i < d->argumentsTypes.size(); ++i) + { + if (i != 0) + arguments += ','; + arguments += QMetaType::typeName(d->argumentsTypes[i]); + } + + d->signature = signature.arg(d->name, arguments).toUtf8(); +} + +#endif \ No newline at end of file diff --git a/DOtherSide/DynamicQObject/DynamicSlot.cpp b/DOtherSide/DynamicQObject/DynamicSlot.cpp deleted file mode 100644 index a8a7bcc..0000000 --- a/DOtherSide/DynamicQObject/DynamicSlot.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "DynamicSlot.h" -#include -#include -#include -#include -#include -#include "private/qmetaobjectbuilder_p.h" - -struct SlotData -{ - QString name; - QMetaType::Type returnType; - QList argumentsTypes; - QByteArray signature; -}; - -DynamicSlot::DynamicSlot() - : d(nullptr) -{ - -} - -DynamicSlot::DynamicSlot(const QString& name, - QMetaType::Type returnType, - const QList& argumentsTypes) - : d(new SlotData()) -{ - d->name = name; - d->returnType = returnType; - d->argumentsTypes = argumentsTypes; - _initSignature(); -} - -DynamicSlot::DynamicSlot(const DynamicSlot& slot) -{ - if (slot.isValid()) - { - d.reset(new SlotData()); - *d = *slot.d; - } - else - d.reset(nullptr); -} - -DynamicSlot& DynamicSlot::operator=(const DynamicSlot& slot) -{ - if (slot.isValid()) - { - d.reset(new SlotData()); - *d = *slot.d; - } - else - d.reset(nullptr); - - return *this; -} - -DynamicSlot::~DynamicSlot() -{ -} - -QString DynamicSlot::name() const -{ - return isValid() ? d->name : QString(); -} - -bool DynamicSlot::isValid() const -{ - return d != nullptr; -} - -QByteArray DynamicSlot::signature() const -{ - return isValid() ? d->signature : QByteArray(); -} - -bool DynamicSlot::validate(const QVariantList& argumentsValues) -{ - return true; -} - -QMetaType::Type DynamicSlot::returnType() const -{ - return isValid() ? d->returnType : QMetaType::UnknownType; -} - -QList DynamicSlot::argumentsTypes() const -{ - return isValid() ? d->argumentsTypes : QList(); -} - -QMetaType::Type DynamicSlot::argumentTypeAt(int i) const -{ - return isValid() ? d->argumentsTypes.at(i) : QMetaType::UnknownType; -} - -void DynamicSlot::_initSignature() -{ - QString signature("%1(%2)"); - QString arguments = ""; - for (int i = 0; i < d->argumentsTypes.size(); ++i) - { - if (i != 0) - arguments += ','; - arguments += QMetaType::typeName(d->argumentsTypes[i]); - } - - d->signature = signature.arg(d->name, arguments).toUtf8(); - - qDebug() << "C++: slot signature is " << d->signature; -} diff --git a/DOtherSide/DynamicQObject/DynamicSlot.h b/DOtherSide/DynamicQObject/DynamicSlot.h index 187ea2c..5c45cf6 100644 --- a/DOtherSide/DynamicQObject/DynamicSlot.h +++ b/DOtherSide/DynamicQObject/DynamicSlot.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef DYNAMICSLOT_H +#define DYNAMICSLOT_H #include #include @@ -6,6 +7,12 @@ #include #include #include +#include +#include +#include +#include + +#include "private/qmetaobjectbuilder_p.h" class SlotData; class QString; @@ -34,3 +41,107 @@ private: std::unique_ptr d; }; + +struct SlotData +{ + QString name; + QMetaType::Type returnType; + QList argumentsTypes; + QByteArray signature; +}; + +DynamicSlot::DynamicSlot() + : d(nullptr) +{ + +} + +DynamicSlot::DynamicSlot(const QString& name, + QMetaType::Type returnType, + const QList& argumentsTypes) + : d(new SlotData()) +{ + d->name = name; + d->returnType = returnType; + d->argumentsTypes = argumentsTypes; + _initSignature(); +} + +DynamicSlot::DynamicSlot(const DynamicSlot& slot) +{ + if (slot.isValid()) + { + d.reset(new SlotData()); + *d = *slot.d; + } + else + d.reset(nullptr); +} + +DynamicSlot& DynamicSlot::operator=(const DynamicSlot& slot) +{ + if (slot.isValid()) + { + d.reset(new SlotData()); + *d = *slot.d; + } + else + d.reset(nullptr); + + return *this; +} + +DynamicSlot::~DynamicSlot() +{ +} + +QString DynamicSlot::name() const +{ + return isValid() ? d->name : QString(); +} + +bool DynamicSlot::isValid() const +{ + return d != nullptr; +} + +QByteArray DynamicSlot::signature() const +{ + return isValid() ? d->signature : QByteArray(); +} + +bool DynamicSlot::validate(const QVariantList& argumentsValues) +{ + return true; +} + +QMetaType::Type DynamicSlot::returnType() const +{ + return isValid() ? d->returnType : QMetaType::UnknownType; +} + +QList DynamicSlot::argumentsTypes() const +{ + return isValid() ? d->argumentsTypes : QList(); +} + +QMetaType::Type DynamicSlot::argumentTypeAt(int i) const +{ + return isValid() ? d->argumentsTypes.at(i) : QMetaType::UnknownType; +} + +void DynamicSlot::_initSignature() +{ + QString signature("%1(%2)"); + QString arguments = ""; + for (int i = 0; i < d->argumentsTypes.size(); ++i) + { + if (i != 0) + arguments += ','; + arguments += QMetaType::typeName(d->argumentsTypes[i]); + } + + d->signature = signature.arg(d->name, arguments).toUtf8(); +} + +#endif \ No newline at end of file diff --git a/DOtherSide/DynamicQObject/IDynamicQObject.h b/DOtherSide/DynamicQObject/IDynamicQObject.h new file mode 100644 index 0000000..e7abc48 --- /dev/null +++ b/DOtherSide/DynamicQObject/IDynamicQObject.h @@ -0,0 +1,40 @@ +#ifndef IDYNAMICQOBJECT_H +#define IDYNAMICQOBJECT_H + +class IDynamicQObject +{ +public: + typedef void (*Callback)(void*, void*, int, void**); + + /// Destructor + virtual ~IDynamicQObject() = default; + + /// Sets the function to be called from C++ to D or Nimrod + virtual void setDObjectCallback(Callback callback) = 0; + + /// Sets the D or Nimrod object that owns this DynamicQObject + virtual void setDObjectPointer(void* dObjectPointer) = 0; + + /// Register a new signal + virtual bool registerSignal(const QString& name, + const QList& argumentsTypes, + int& signalIndex) = 0; + + /// Register a new slot + virtual bool registerSlot(const QString& name, + const QMetaType::Type returnType, + const QList& argumentsTypes, + int& slotIndex) = 0; + + /// Register a new property + virtual bool registerProperty(const QString& name, + QMetaType::Type type, + const QString& readSlotName, + const QString& writeSlotName = "", + const QString& notifySignalName = "") = 0; + + /// Emit the signal with the given name and arguments + virtual bool emitSignal(const QString& name, const QList& argumentsValues) = 0; +}; + +#endif \ No newline at end of file diff --git a/DOtherSide/DynamicQObject/private/qmetaobject_p.h b/DOtherSide/DynamicQObject/private/qmetaobject_p.h index ef66927..b3c04d5 100644 --- a/DOtherSide/DynamicQObject/private/qmetaobject_p.h +++ b/DOtherSide/DynamicQObject/private/qmetaobject_p.h @@ -114,7 +114,7 @@ enum MetaDataFlags { TypeNameIndexMask = 0x7FFFFFFF }; -extern int qMetaTypeTypeInternal(const char *); +extern int qMetaTypeTypeInternal(const char*); class QArgumentType { @@ -122,21 +122,23 @@ public: QArgumentType(int type) : _type(type) {} - QArgumentType(const QByteArray &name) + QArgumentType(const QByteArray& name) : _type(qMetaTypeTypeInternal(name.constData())), _name(name) {} QArgumentType() : _type(0) {} int type() const - { return _type; } + { + return _type; + } QByteArray name() const { if (_type && _name.isEmpty()) - const_cast(this)->_name = QMetaType::typeName(_type); + const_cast(this)->_name = QMetaType::typeName(_type); return _name; } - bool operator==(const QArgumentType &other) const + bool operator==(const QArgumentType& other) const { if (_type) return _type == other._type; @@ -145,7 +147,7 @@ public: else return _name == other._name; } - bool operator!=(const QArgumentType &other) const + bool operator!=(const QArgumentType& other) const { if (_type) return _type != other._type; @@ -182,55 +184,57 @@ struct QMetaObjectPrivate // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself // revision 7 is Qt 5 - static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) - { return reinterpret_cast(metaobject->d.data); } + static inline const QMetaObjectPrivate* get(const QMetaObject* metaobject) + { + return reinterpret_cast(metaobject->d.data); + } - static int originalClone(const QMetaObject *obj, int local_method_index); + static int originalClone(const QMetaObject* obj, int local_method_index); - static QByteArray decodeMethodSignature(const char *signature, - QArgumentTypeArray &types); - static int indexOfSignalRelative(const QMetaObject **baseObject, - const QByteArray &name, int argc, - const QArgumentType *types); - static int indexOfSlotRelative(const QMetaObject **m, - const QByteArray &name, int argc, - const QArgumentType *types); - static int indexOfSignal(const QMetaObject *m, const QByteArray &name, - int argc, const QArgumentType *types); - static int indexOfSlot(const QMetaObject *m, const QByteArray &name, - int argc, const QArgumentType *types); - static int indexOfMethod(const QMetaObject *m, const QByteArray &name, - int argc, const QArgumentType *types); - static int indexOfConstructor(const QMetaObject *m, const QByteArray &name, - int argc, const QArgumentType *types); - Q_CORE_EXPORT static QMetaMethod signal(const QMetaObject *m, int signal_index); - Q_CORE_EXPORT static int signalOffset(const QMetaObject *m); - Q_CORE_EXPORT static int absoluteSignalCount(const QMetaObject *m); - Q_CORE_EXPORT static int signalIndex(const QMetaMethod &m); - static bool checkConnectArgs(int signalArgc, const QArgumentType *signalTypes, - int methodArgc, const QArgumentType *methodTypes); - static bool checkConnectArgs(const QMetaMethodPrivate *signal, - const QMetaMethodPrivate *method); + static QByteArray decodeMethodSignature(const char* signature, + QArgumentTypeArray& types); + static int indexOfSignalRelative(const QMetaObject** baseObject, + const QByteArray& name, int argc, + const QArgumentType* types); + static int indexOfSlotRelative(const QMetaObject** m, + const QByteArray& name, int argc, + const QArgumentType* types); + static int indexOfSignal(const QMetaObject* m, const QByteArray& name, + int argc, const QArgumentType* types); + static int indexOfSlot(const QMetaObject* m, const QByteArray& name, + int argc, const QArgumentType* types); + static int indexOfMethod(const QMetaObject* m, const QByteArray& name, + int argc, const QArgumentType* types); + static int indexOfConstructor(const QMetaObject* m, const QByteArray& name, + int argc, const QArgumentType* types); + Q_CORE_EXPORT static QMetaMethod signal(const QMetaObject* m, int signal_index); + Q_CORE_EXPORT static int signalOffset(const QMetaObject* m); + Q_CORE_EXPORT static int absoluteSignalCount(const QMetaObject* m); + Q_CORE_EXPORT static int signalIndex(const QMetaMethod& m); + static bool checkConnectArgs(int signalArgc, const QArgumentType* signalTypes, + int methodArgc, const QArgumentType* methodTypes); + static bool checkConnectArgs(const QMetaMethodPrivate* signal, + const QMetaMethodPrivate* method); - static QList parameterTypeNamesFromSignature(const char *signature); + static QList parameterTypeNamesFromSignature(const char* signature); #ifndef QT_NO_QOBJECT //defined in qobject.cpp enum DisconnectType { DisconnectAll, DisconnectOne }; - static void memberIndexes(const QObject *obj, const QMetaMethod &member, - int *signalIndex, int *methodIndex); - static QObjectPrivate::Connection *connect(const QObject *sender, int signal_index, - const QMetaObject *smeta, - const QObject *receiver, int method_index_relative, - const QMetaObject *rmeta = 0, - int type = 0, int *types = 0); - static bool disconnect(const QObject *sender, int signal_index, - const QMetaObject *smeta, - const QObject *receiver, int method_index, void **slot, + static void memberIndexes(const QObject* obj, const QMetaMethod& member, + int* signalIndex, int* methodIndex); + static QObjectPrivate::Connection* connect(const QObject* sender, int signal_index, + const QMetaObject* smeta, + const QObject* receiver, int method_index_relative, + const QMetaObject* rmeta = 0, + int type = 0, int* types = 0); + static bool disconnect(const QObject* sender, int signal_index, + const QMetaObject* smeta, + const QObject* receiver, int method_index, void** slot, DisconnectType = DisconnectAll); - static inline bool disconnectHelper(QObjectPrivate::Connection *c, - const QObject *receiver, int method_index, void **slot, - QMutex *senderMutex, DisconnectType = DisconnectAll); + static inline bool disconnectHelper(QObjectPrivate::Connection* c, + const QObject* receiver, int method_index, void** slot, + QMutex* senderMutex, DisconnectType = DisconnectAll); #endif }; @@ -246,7 +250,7 @@ static inline bool is_ident_char(char s) || (s >= 'A' && s <= 'Z') || (s >= '0' && s <= '9') || s == '_' - ); + ); } static inline bool is_space(char s) diff --git a/DOtherSide/DynamicQObject/private/qmetaobjectbuilder_p.h b/DOtherSide/DynamicQObject/private/qmetaobjectbuilder_p.h index 3a1b43c..ed76f53 100644 --- a/DOtherSide/DynamicQObject/private/qmetaobjectbuilder_p.h +++ b/DOtherSide/DynamicQObject/private/qmetaobjectbuilder_p.h @@ -100,14 +100,14 @@ public: Q_DECLARE_FLAGS(MetaObjectFlags, MetaObjectFlag) QMetaObjectBuilder(); - explicit QMetaObjectBuilder(const QMetaObject *prototype, QMetaObjectBuilder::AddMembers members = AllMembers); + explicit QMetaObjectBuilder(const QMetaObject* prototype, QMetaObjectBuilder::AddMembers members = AllMembers); virtual ~QMetaObjectBuilder(); QByteArray className() const; void setClassName(const QByteArray& name); - const QMetaObject *superClass() const; - void setSuperClass(const QMetaObject *meta); + const QMetaObject* superClass() const; + void setSuperClass(const QMetaObject* meta); MetaObjectFlags flags() const; void setFlags(MetaObjectFlags); @@ -129,7 +129,7 @@ public: QMetaMethodBuilder addConstructor(const QByteArray& signature); QMetaMethodBuilder addConstructor(const QMetaMethod& prototype); - QMetaPropertyBuilder addProperty(const QByteArray& name, const QByteArray& type, int notifierId=-1); + QMetaPropertyBuilder addProperty(const QByteArray& name, const QByteArray& type, int notifierId = -1); QMetaPropertyBuilder addProperty(const QMetaProperty& prototype); QMetaEnumBuilder addEnumerator(const QByteArray& name); @@ -137,15 +137,15 @@ public: int addClassInfo(const QByteArray& name, const QByteArray& value); - int addRelatedMetaObject(const QMetaObject *meta); + int addRelatedMetaObject(const QMetaObject* meta); - void addMetaObject(const QMetaObject *prototype, QMetaObjectBuilder::AddMembers members = AllMembers); + void addMetaObject(const QMetaObject* prototype, QMetaObjectBuilder::AddMembers members = AllMembers); QMetaMethodBuilder method(int index) const; QMetaMethodBuilder constructor(int index) const; QMetaPropertyBuilder property(int index) const; QMetaEnumBuilder enumerator(int index) const; - const QMetaObject *relatedMetaObject(int index) const; + const QMetaObject* relatedMetaObject(int index) const; QByteArray classInfoName(int index) const; QByteArray classInfoValue(int index) const; @@ -165,26 +165,26 @@ public: int indexOfEnumerator(const QByteArray& name); int indexOfClassInfo(const QByteArray& name); - typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **); + typedef void (*StaticMetacallFunction)(QObject*, QMetaObject::Call, int, void**); QMetaObjectBuilder::StaticMetacallFunction staticMetacallFunction() const; void setStaticMetacallFunction(QMetaObjectBuilder::StaticMetacallFunction value); - QMetaObject *toMetaObject() const; - QByteArray toRelocatableData(bool * = 0) const; - static void fromRelocatableData(QMetaObject *, const QMetaObject *, const QByteArray &); + QMetaObject* toMetaObject() const; + QByteArray toRelocatableData(bool* = 0) const; + static void fromRelocatableData(QMetaObject*, const QMetaObject*, const QByteArray&); #ifndef QT_NO_DATASTREAM void serialize(QDataStream& stream) const; void deserialize - (QDataStream& stream, - const QMap& references); + (QDataStream& stream, + const QMap& references); #endif private: Q_DISABLE_COPY(QMetaObjectBuilder) - QMetaObjectBuilderPrivate *d; + QMetaObjectBuilderPrivate* d; friend class QMetaMethodBuilder; friend class QMetaPropertyBuilder; @@ -221,16 +221,16 @@ public: void setRevision(int revision); private: - const QMetaObjectBuilder *_mobj; + const QMetaObjectBuilder* _mobj; int _index; friend class QMetaObjectBuilder; friend class QMetaPropertyBuilder; - QMetaMethodBuilder(const QMetaObjectBuilder *mobj, int index) + QMetaMethodBuilder(const QMetaObjectBuilder* mobj, int index) : _mobj(mobj), _index(index) {} - QMetaMethodBuilderPrivate *d_func() const; + QMetaMethodBuilderPrivate* d_func() const; }; class Q_CORE_EXPORT QMetaPropertyBuilder @@ -238,7 +238,9 @@ class Q_CORE_EXPORT QMetaPropertyBuilder public: QMetaPropertyBuilder() : _mobj(0), _index(0) {} - int index() const { return _index; } + int index() const { + return _index; + } QByteArray name() const; QByteArray type() const; @@ -278,15 +280,15 @@ public: void setRevision(int revision); private: - const QMetaObjectBuilder *_mobj; + const QMetaObjectBuilder* _mobj; int _index; friend class QMetaObjectBuilder; - QMetaPropertyBuilder(const QMetaObjectBuilder *mobj, int index) + QMetaPropertyBuilder(const QMetaObjectBuilder* mobj, int index) : _mobj(mobj), _index(index) {} - QMetaPropertyBuilderPrivate *d_func() const; + QMetaPropertyBuilderPrivate* d_func() const; }; class Q_CORE_EXPORT QMetaEnumBuilder @@ -294,7 +296,9 @@ class Q_CORE_EXPORT QMetaEnumBuilder public: QMetaEnumBuilder() : _mobj(0), _index(0) {} - int index() const { return _index; } + int index() const { + return _index; + } QByteArray name() const; @@ -309,27 +313,27 @@ public: void removeKey(int index); private: - const QMetaObjectBuilder *_mobj; + const QMetaObjectBuilder* _mobj; int _index; friend class QMetaObjectBuilder; - QMetaEnumBuilder(const QMetaObjectBuilder *mobj, int index) + QMetaEnumBuilder(const QMetaObjectBuilder* mobj, int index) : _mobj(mobj), _index(index) {} - QMetaEnumBuilderPrivate *d_func() const; + QMetaEnumBuilderPrivate* d_func() const; }; class Q_CORE_EXPORT QMetaStringTable { public: - explicit QMetaStringTable(const QByteArray &className); + explicit QMetaStringTable(const QByteArray& className); - int enter(const QByteArray &value); + int enter(const QByteArray& value); static int preferredAlignment(); int blobSize() const; - void writeBlob(char *out) const; + void writeBlob(char* out) const; private: typedef QHash Entries; // string --> index mapping diff --git a/DOtherSide/DynamicQObject/private/qobject_p.h b/DOtherSide/DynamicQObject/private/qobject_p.h index 180887b..1236a21 100644 --- a/DOtherSide/DynamicQObject/private/qobject_p.h +++ b/DOtherSide/DynamicQObject/private/qobject_p.h @@ -68,19 +68,21 @@ QT_BEGIN_NAMESPACE class QVariant; class QThreadData; class QObjectConnectionListVector; -namespace QtSharedPointer { struct ExternalRefCountData; } +namespace QtSharedPointer { + struct ExternalRefCountData; +} /* for Qt Test */ struct QSignalSpyCallbackSet { - typedef void (*BeginCallback)(QObject *caller, int signal_or_method_index, void **argv); - typedef void (*EndCallback)(QObject *caller, int signal_or_method_index); + typedef void (*BeginCallback)(QObject* caller, int signal_or_method_index, void** argv); + typedef void (*EndCallback)(QObject* caller, int signal_or_method_index); BeginCallback signal_begin_callback, - slot_begin_callback; + slot_begin_callback; EndCallback signal_end_callback, slot_end_callback; }; -void Q_CORE_EXPORT qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet &callback_set); +void Q_CORE_EXPORT qt_register_signal_spy_callbacks(const QSignalSpyCallbackSet& callback_set); extern QSignalSpyCallbackSet Q_CORE_EXPORT qt_signal_spy_callback_set; @@ -89,19 +91,19 @@ enum { QObjectPrivateVersion = QT_VERSION }; class Q_CORE_EXPORT QAbstractDeclarativeData { public: - static void (*destroyed)(QAbstractDeclarativeData *, QObject *); - static void (*destroyed_qml1)(QAbstractDeclarativeData *, QObject *); - static void (*parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *); - static void (*signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **); - static int (*receivers)(QAbstractDeclarativeData *, const QObject *, int); - static bool (*isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int); + static void (*destroyed)(QAbstractDeclarativeData*, QObject*); + static void (*destroyed_qml1)(QAbstractDeclarativeData*, QObject*); + static void (*parentChanged)(QAbstractDeclarativeData*, QObject*, QObject*); + static void (*signalEmitted)(QAbstractDeclarativeData*, QObject*, int, void**); + static int (*receivers)(QAbstractDeclarativeData*, const QObject*, int); + static bool (*isSignalConnected)(QAbstractDeclarativeData*, const QObject*, int); }; // This is an implementation of QAbstractDeclarativeData that is identical with // the implementation in QtDeclarative and QtQml for the first bit struct QAbstractDeclarativeDataImpl : public QAbstractDeclarativeData { - quint32 ownedByQml1:1; + quint32 ownedByQml1: 1; quint32 unused: 31; }; @@ -113,9 +115,9 @@ public: struct ExtraData { ExtraData() {} - #ifndef QT_NO_USERDATA - QVector userData; - #endif +#ifndef QT_NO_USERDATA + QVector userData; +#endif QList propertyNames; QList propertyValues; QVector runningTimers; @@ -123,20 +125,20 @@ public: QString objectName; }; - typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **); + typedef void (*StaticMetaCallFunction)(QObject*, QMetaObject::Call, int, void**); struct Connection { - QObject *sender; - QObject *receiver; + QObject* sender; + QObject* receiver; union { StaticMetaCallFunction callFunction; - QtPrivate::QSlotObjectBase *slotObj; + QtPrivate::QSlotObjectBase* slotObj; }; // The next pointer for the singly-linked ConnectionList - Connection *nextConnectionList; + Connection* nextConnectionList; //senders linked list - Connection *next; - Connection **prev; + Connection* next; + Connection** prev; QAtomicPointer argumentTypes; QAtomicInt ref_; ushort method_offset; @@ -149,8 +151,12 @@ public: //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection } ~Connection(); - int method() const { return method_offset + method_relative; } - void ref() { ref_.ref(); } + int method() const { + return method_offset + method_relative; + } + void ref() { + ref_.ref(); + } void deref() { if (!ref_.deref()) { Q_ASSERT(!receiver); @@ -161,13 +167,13 @@ public: // ConnectionList is a singly-linked list struct ConnectionList { ConnectionList() : first(0), last(0) {} - Connection *first; - Connection *last; + Connection* first; + Connection* last; }; struct Sender { - QObject *sender; + QObject* sender; int signal; int ref; }; @@ -177,64 +183,64 @@ public: virtual ~QObjectPrivate(); void deleteChildren(); - void setParent_helper(QObject *); + void setParent_helper(QObject*); void moveToThread_helper(); - void setThreadData_helper(QThreadData *currentData, QThreadData *targetData); - void _q_reregisterTimers(void *pointer); + void setThreadData_helper(QThreadData* currentData, QThreadData* targetData); + void _q_reregisterTimers(void* pointer); - bool isSender(const QObject *receiver, const char *signal) const; - QObjectList receiverList(const char *signal) const; + bool isSender(const QObject* receiver, const char* signal) const; + QObjectList receiverList(const char* signal) const; QObjectList senderList() const; - void addConnection(int signal, Connection *c); + void addConnection(int signal, Connection* c); void cleanConnectionLists(); - static inline Sender *setCurrentSender(QObject *receiver, - Sender *sender); - static inline void resetCurrentSender(QObject *receiver, - Sender *currentSender, - Sender *previousSender); + static inline Sender* setCurrentSender(QObject* receiver, + Sender* sender); + static inline void resetCurrentSender(QObject* receiver, + Sender* currentSender, + Sender* previousSender); - static QObjectPrivate *get(QObject *o) { + static QObjectPrivate* get(QObject* o) { return o->d_func(); } - int signalIndex(const char *signalName, const QMetaObject **meta = 0) const; + int signalIndex(const char* signalName, const QMetaObject** meta = 0) const; inline bool isSignalConnected(uint signalIdx) const; // To allow abitrary objects to call connectNotify()/disconnectNotify() without making // the API public in QObject. This is used by QQmlNotifierEndpoint. - inline void connectNotify(const QMetaMethod &signal); - inline void disconnectNotify(const QMetaMethod &signal); + inline void connectNotify(const QMetaMethod& signal); + inline void disconnectNotify(const QMetaMethod& signal); template - static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, - const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot, - Qt::ConnectionType type = Qt::AutoConnection); + static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer::Object* sender, Func1 signal, + const typename QtPrivate::FunctionPointer::Object* receiverPrivate, Func2 slot, + Qt::ConnectionType type = Qt::AutoConnection); template - static inline bool disconnect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, - const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot); + static inline bool disconnect(const typename QtPrivate::FunctionPointer::Object* sender, Func1 signal, + const typename QtPrivate::FunctionPointer::Object* receiverPrivate, Func2 slot); - static QMetaObject::Connection connectImpl(const QObject *sender, int signal_index, - const QObject *receiver, void **slot, - QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type, - const int *types, const QMetaObject *senderMetaObject); - static QMetaObject::Connection connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type); - static bool disconnect(const QObject *sender, int signal_index, void **slot); + static QMetaObject::Connection connectImpl(const QObject* sender, int signal_index, + const QObject* receiver, void** slot, + QtPrivate::QSlotObjectBase* slotObj, Qt::ConnectionType type, + const int* types, const QMetaObject* senderMetaObject); + static QMetaObject::Connection connect(const QObject* sender, int signal_index, QtPrivate::QSlotObjectBase* slotObj, Qt::ConnectionType type); + static bool disconnect(const QObject* sender, int signal_index, void** slot); public: - ExtraData *extraData; // extra data set by the user - QThreadData *threadData; // id of the thread that owns the object + ExtraData* extraData; // extra data set by the user + QThreadData* threadData; // id of the thread that owns the object - QObjectConnectionListVector *connectionLists; + QObjectConnectionListVector* connectionLists; - Connection *senders; // linked list of connections connected to this object - Sender *currentSender; // object currently activating the object + Connection* senders; // linked list of connections connected to this object + Sender* currentSender; // object currently activating the object mutable quint32 connectedSignals[2]; union { - QObject *currentChildBeingDeleted; - QAbstractDeclarativeData *declarativeData; //extra data used by the declarative module + QObject* currentChildBeingDeleted; + QAbstractDeclarativeData* declarativeData; //extra data used by the declarative module }; // these objects are all used to indicate that a QObject was deleted @@ -254,22 +260,22 @@ public: inline bool QObjectPrivate::isSignalConnected(uint signal_index) const { return signal_index >= sizeof(connectedSignals) * 8 - || (connectedSignals[signal_index >> 5] & (1 << (signal_index & 0x1f)) - || (declarativeData && QAbstractDeclarativeData::isSignalConnected - && QAbstractDeclarativeData::isSignalConnected(declarativeData, q_func(), signal_index))); + || (connectedSignals[signal_index >> 5] & (1 << (signal_index & 0x1f)) + || (declarativeData && QAbstractDeclarativeData::isSignalConnected + && QAbstractDeclarativeData::isSignalConnected(declarativeData, q_func(), signal_index))); } -inline QObjectPrivate::Sender *QObjectPrivate::setCurrentSender(QObject *receiver, - Sender *sender) +inline QObjectPrivate::Sender* QObjectPrivate::setCurrentSender(QObject* receiver, + Sender* sender) { - Sender *previousSender = receiver->d_func()->currentSender; + Sender* previousSender = receiver->d_func()->currentSender; receiver->d_func()->currentSender = sender; return previousSender; } -inline void QObjectPrivate::resetCurrentSender(QObject *receiver, - Sender *currentSender, - Sender *previousSender) +inline void QObjectPrivate::resetCurrentSender(QObject* receiver, + Sender* currentSender, + Sender* previousSender) { // ref is set to zero when this object is deleted during the metacall if (currentSender->ref == 1) @@ -279,46 +285,47 @@ inline void QObjectPrivate::resetCurrentSender(QObject *receiver, previousSender->ref = currentSender->ref; } -inline void QObjectPrivate::connectNotify(const QMetaMethod &signal) +inline void QObjectPrivate::connectNotify(const QMetaMethod& signal) { q_ptr->connectNotify(signal); } -inline void QObjectPrivate::disconnectNotify(const QMetaMethod &signal) +inline void QObjectPrivate::disconnectNotify(const QMetaMethod& signal) { q_ptr->disconnectNotify(signal); } namespace QtPrivate { -template class QPrivateSlotObject : public QSlotObjectBase -{ - typedef QtPrivate::FunctionPointer FuncType; - Func function; - static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) + template class QPrivateSlotObject : public QSlotObjectBase { - switch (which) { + typedef QtPrivate::FunctionPointer FuncType; + Func function; + static void impl(int which, QSlotObjectBase* this_, QObject* r, void** a, bool* ret) + { + switch (which) { case Destroy: delete static_cast(this_); break; case Call: FuncType::template call(static_cast(this_)->function, - static_cast(QObjectPrivate::get(r)), a); + static_cast(QObjectPrivate::get(r)), a); break; case Compare: - *ret = *reinterpret_cast(a) == static_cast(this_)->function; + *ret = *reinterpret_cast(a) == static_cast(this_)->function; break; - case NumOperations: ; + case NumOperations: + ; + } } - } -public: - explicit QPrivateSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {} -}; + public: + explicit QPrivateSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {} + }; } //namespace QtPrivate template -inline QMetaObject::Connection QObjectPrivate::connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal, - const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot, - Qt::ConnectionType type) +inline QMetaObject::Connection QObjectPrivate::connect(const typename QtPrivate::FunctionPointer::Object* sender, Func1 signal, + const typename QtPrivate::FunctionPointer::Object* receiverPrivate, Func2 slot, + Qt::ConnectionType type) { typedef QtPrivate::FunctionPointer SignalType; typedef QtPrivate::FunctionPointer SlotType; @@ -333,15 +340,15 @@ inline QMetaObject::Connection QObjectPrivate::connect(const typename QtPrivate: Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible::value), "Return type of the slot is not compatible with the return type of the signal."); - const int *types = 0; + const int* types = 0; if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) types = QtPrivate::ConnectionTypes::types(); - return QObject::connectImpl(sender, reinterpret_cast(&signal), - receiverPrivate->q_ptr, reinterpret_cast(&slot), - new QtPrivate::QPrivateSlotObject::Value, - typename SignalType::ReturnType>(slot), - type, types, &SignalType::Object::staticMetaObject); + return QObject::connectImpl(sender, reinterpret_cast(&signal), + receiverPrivate->q_ptr, reinterpret_cast(&slot), + new QtPrivate::QPrivateSlotObject::Value, + typename SignalType::ReturnType>(slot), + type, types, &SignalType::Object::staticMetaObject); } template @@ -355,9 +362,9 @@ bool QObjectPrivate::disconnect(const typename QtPrivate::FunctionPointer< Func1 //compilation error if the arguments does not match. Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments::value), "Signal and slot arguments are not compatible."); - return QObject::disconnectImpl(sender, reinterpret_cast(&signal), - receiverPrivate->q_ptr, reinterpret_cast(&slot), - &SignalType::Object::staticMetaObject); + return QObject::disconnectImpl(sender, reinterpret_cast(&signal), + receiverPrivate->q_ptr, reinterpret_cast(&slot), + &SignalType::Object::staticMetaObject); } Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_MOVABLE_TYPE); @@ -367,31 +374,39 @@ class QSemaphore; class Q_CORE_EXPORT QMetaCallEvent : public QEvent { public: - QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction , const QObject *sender, int signalId, - int nargs = 0, int *types = 0, void **args = 0, QSemaphore *semaphore = 0); + QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction , const QObject* sender, int signalId, + int nargs = 0, int* types = 0, void** args = 0, QSemaphore* semaphore = 0); /*! \internal \a signalId is in the signal index range (see QObjectPrivate::signalIndex()). */ - QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj, const QObject *sender, int signalId, - int nargs = 0, int *types = 0, void **args = 0, QSemaphore *semaphore = 0); + QMetaCallEvent(QtPrivate::QSlotObjectBase* slotObj, const QObject* sender, int signalId, + int nargs = 0, int* types = 0, void** args = 0, QSemaphore* semaphore = 0); ~QMetaCallEvent(); - inline int id() const { return method_offset_ + method_relative_; } - inline const QObject *sender() const { return sender_; } - inline int signalId() const { return signalId_; } - inline void **args() const { return args_; } + inline int id() const { + return method_offset_ + method_relative_; + } + inline const QObject* sender() const { + return sender_; + } + inline int signalId() const { + return signalId_; + } + inline void** args() const { + return args_; + } - virtual void placeMetaCall(QObject *object); + virtual void placeMetaCall(QObject* object); private: - QtPrivate::QSlotObjectBase *slotObj_; - const QObject *sender_; + QtPrivate::QSlotObjectBase* slotObj_; + const QObject* sender_; int signalId_; int nargs_; - int *types_; - void **args_; - QSemaphore *semaphore_; + int* types_; + void** args_; + QSemaphore* semaphore_; QObjectPrivate::StaticMetaCallFunction callFunction_; ushort method_offset_; ushort method_relative_; @@ -401,32 +416,46 @@ class QBoolBlocker { Q_DISABLE_COPY(QBoolBlocker) public: - explicit inline QBoolBlocker(bool &b, bool value=true):block(b), reset(b){block = value;} - inline ~QBoolBlocker(){block = reset; } + explicit inline QBoolBlocker(bool& b, bool value = true): block(b), reset(b) { + block = value; + } + inline ~QBoolBlocker() { + block = reset; + } private: - bool █ + bool& block; bool reset; }; -void Q_CORE_EXPORT qDeleteInEventHandler(QObject *o); +void Q_CORE_EXPORT qDeleteInEventHandler(QObject* o); struct QAbstractDynamicMetaObject; struct Q_CORE_EXPORT QDynamicMetaObjectData { virtual ~QDynamicMetaObjectData() {} - virtual void objectDestroyed(QObject *) { delete this; } + virtual void objectDestroyed(QObject*) { + delete this; + } - virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *) = 0; - virtual int metaCall(QObject *, QMetaObject::Call, int _id, void **) = 0; + virtual QAbstractDynamicMetaObject* toDynamicMetaObject(QObject*) = 0; + virtual int metaCall(QObject*, QMetaObject::Call, int _id, void**) = 0; }; struct Q_CORE_EXPORT QAbstractDynamicMetaObject : public QDynamicMetaObjectData, public QMetaObject { - virtual QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *) { return this; } - virtual int createProperty(const char *, const char *) { return -1; } - virtual int metaCall(QObject *, QMetaObject::Call c, int _id, void **a) - { return metaCall(c, _id, a); } - virtual int metaCall(QMetaObject::Call, int _id, void **) { return _id; } // Compat overload + virtual QAbstractDynamicMetaObject* toDynamicMetaObject(QObject*) { + return this; + } + virtual int createProperty(const char*, const char*) { + return -1; + } + virtual int metaCall(QObject*, QMetaObject::Call c, int _id, void** a) + { + return metaCall(c, _id, a); + } + virtual int metaCall(QMetaObject::Call, int _id, void**) { + return _id; // Compat overload + } }; QT_END_NAMESPACE diff --git a/DOtherSide/IntegrationTest/main.cpp b/DOtherSide/IntegrationTest/main.cpp index 11357ee..f0ac270 100644 --- a/DOtherSide/IntegrationTest/main.cpp +++ b/DOtherSide/IntegrationTest/main.cpp @@ -5,7 +5,7 @@ #include #include -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { QGuiApplication app(argc, argv); diff --git a/Nim/Docs/NimQml.txt b/Nim/Docs/NimQml.txt index ec1e322..e330f2f 100644 --- a/Nim/Docs/NimQml.txt +++ b/Nim/Docs/NimQml.txt @@ -2,8 +2,8 @@ Filippo Cucchetto Will Szumski -:Version: 0.2.0 -:Date: 2015/01/02 +:Version: 0.3.0 +:Date: 2015/02/15 Introduction ----------- @@ -126,6 +126,7 @@ At the time of writing the QVariant class support the following types: * int * string * bool +* float * QObject derived classes Example 3: exposing complex data and procedures to Qml @@ -247,3 +248,57 @@ The ``QtProperty`` macro has the following syntax .. code-block:: nim QtProperty[typeOfProperty] nameOfProperty +Example 5: ContactApp +------------------------- +The last example tries to show you all the stuff presented +in the previous chapters and gives you an introduction to how +to expose lists to qml. + +Qt models are a huge topic and explaining how they work is +out of scope. For further information please read the official +Qt documentation. + +The main file follows the basic logic of creating a qml +engine and exposing a QObject derived object "ApplicationLogic" +through a global "logic" property + +``Examples/ContactApp/main.nim`` + +.. code-block:: nim + :file: ../Examples/ContactApp/main.nim + +The qml file shows a simple app with a central tableview + +``Examples/ContactApp/main.qml`` + +.. code-block:: qml + :file: ../Examples/ContactApp/main.qml + +The important things to notice are: +1. The menubar load, save and exit items handlers call the logic load, save and exit slots +2. The TableView model is retrieved by the logic.contactList property +3. The delete and add buttons call the del and add slots of the logic.contactList model + +The ApplicationLogic object is as follows: + +``Examples/ContactApp/ApplicationLogic.nim`` + +.. code-block:: nim + :file: ../Examples/ContactApp/ApplicationLogic.nim + +The ApplicationLogic object, +1. expose some slots for handling the qml menubar triggered signals +2. expose a contactList property that return a QAbstractListModel derived object that manage the list of contacts + +The ContactList object is as follows: + +``Examples/ContactApp/ContactList.nim`` + +.. code-block:: nim + :file: ../Examples/ContactApp/ContactList.nim + +The ContactList object: +1. overrides the ``rowCount`` method for returning the number of rows stored in the model +2. overrides the ``data`` method for returning the value for the exported roles +3. overrides the ``roleNames`` method for returning the names of the roles of the model. This name are then available in the qml item delegates +4. defines two slots ``add`` and ``del`` that add or delete a Contact. During this operations the model execute the ``beginInsertRows`` and ``beginRemoveRows`` for notifing the view of an upcoming change. Once the add or delete operations are done the model execute the ``endInsertRows`` and ``endRemoveRows``. diff --git a/Nim/Examples/AbstractItemModel/CMakeLists.txt b/Nim/Examples/AbstractItemModel/CMakeLists.txt new file mode 100644 index 0000000..227c09f --- /dev/null +++ b/Nim/Examples/AbstractItemModel/CMakeLists.txt @@ -0,0 +1,2 @@ +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/main.qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +add_nim_executable(TARGET AbstractItemModel SOURCES main.nim PATHS ../../NimQml) \ No newline at end of file diff --git a/Nim/Examples/AbstractItemModel/main.nim b/Nim/Examples/AbstractItemModel/main.nim new file mode 100644 index 0000000..7be20f0 --- /dev/null +++ b/Nim/Examples/AbstractItemModel/main.nim @@ -0,0 +1,64 @@ +import NimQml +import macros +import typeinfo +import tables + +type + Roles {.pure.} = enum + Name = cint(0) + + MyQAbstractListModel = ref object of QAbstractListModel + m_roleNames: Table[int, cstring] + m_names: seq[string] + +proc create(self: MyQAbstractListModel) = + var qAbstractListModel = self.QAbstractListModel + qAbstractListModel.create + self.m_names = @["John", "Max", "Paul", "Anna"] + self.m_roleNames = initTable[int, cstring]() + self.m_roleNames[0] = "name" + +proc delete(self: MyQAbstractListModel) = + var qAbstractListModel = self.QAbstractListModel + qAbstractListModel.delete + +proc newMyQAbstractListModel(): MyQAbstractListModel = + new(result, delete) + result.create + +method rowCount(self: MyQAbstractListModel, index: QModelIndex): cint = + return self.m_names.len.cint + +method data(self: MyQAbstractListModel, index: QModelIndex, role: cint): QVariant = + if not index.isValid: + return + if index.row < 0 or index.row >= self.m_names.len: + return + if role == Roles.Name.cint: + return newQVariant(self.m_names[index.row]) + +method roleNames(self: MyQAbstractListModel): Table[int, cstring] = + return self.m_roleNames + +proc mainProc() = + var app = newQApplication() + defer: app.delete + + var myListModel = newMyQAbstractListModel() + defer: myListModel.delete + + var engine = newQQmlApplicationEngine() + defer: engine.delete + + var variant = newQVariant(myListModel) + defer: variant.delete + + engine.rootContext.setContextProperty("myListModel", variant) + engine.load("main.qml") + + app.exec() + +when isMainModule: + mainProc() + GC_fullcollect() + diff --git a/Nim/Examples/AbstractItemModel/main.qml b/Nim/Examples/AbstractItemModel/main.qml new file mode 100644 index 0000000..d3aa3a3 --- /dev/null +++ b/Nim/Examples/AbstractItemModel/main.qml @@ -0,0 +1,25 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +ApplicationWindow +{ + width: 400 + height: 300 + title: "AbstractItemModel" + Component.onCompleted: visible = true + + Component + { + id: myListModelDelegate + Label { text: "Name:" + name } + } + + ListView + { + anchors.fill: parent + model: myListModel + delegate: myListModelDelegate + } +} \ No newline at end of file diff --git a/Nim/Examples/CMakeLists.txt b/Nim/Examples/CMakeLists.txt index d9f665a..a6d896a 100644 --- a/Nim/Examples/CMakeLists.txt +++ b/Nim/Examples/CMakeLists.txt @@ -1,4 +1,6 @@ add_subdirectory(HelloWorld) add_subdirectory(SimpleData) add_subdirectory(SlotsAndProperties) -add_subdirectory(QtObjectMacro) \ No newline at end of file +add_subdirectory(QtObjectMacro) +add_subdirectory(ContactApp) +add_subdirectory(AbstractItemModel) \ No newline at end of file diff --git a/Nim/Examples/ContactApp/ApplicationLogic.nim b/Nim/Examples/ContactApp/ApplicationLogic.nim new file mode 100644 index 0000000..ed1242d --- /dev/null +++ b/Nim/Examples/ContactApp/ApplicationLogic.nim @@ -0,0 +1,33 @@ +import NimQml, NimQmlMacros, ContactList + +QtObject: + type ApplicationLogic* = ref object of QObject + contactList: ContactList + app: QApplication + + proc delete*(self: ApplicationLogic) = + let qobject = self.QObject + qobject.delete + self.contactList.delete + + proc newApplicationLogic*(app: QApplication): ApplicationLogic = + new(result) + result.contactList = newContactList() + result.app = app + result.create() + + method getContactList(self: ApplicationLogic): QVariant {.slot.} = + return newQVariant(self.contactList) + + method onLoadTriggered(self: ApplicationLogic) {.slot.} = + echo "Load Triggered" + self.contactList.add("John", "Doo") + + method onSaveTriggered(self: ApplicationLogic) {.slot.} = + echo "Save Triggered" + + method onExitTriggered(self: ApplicationLogic) {.slot.} = + self.app.quit + + QtProperty[QVariant] contactList: + read = getContactList diff --git a/Nim/Examples/ContactApp/CMakeLists.txt b/Nim/Examples/ContactApp/CMakeLists.txt new file mode 100644 index 0000000..a6c189e --- /dev/null +++ b/Nim/Examples/ContactApp/CMakeLists.txt @@ -0,0 +1,2 @@ +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/main.qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) +add_nim_executable(TARGET ContactApp SOURCES main.nim PATHS ../../NimQml) \ No newline at end of file diff --git a/Nim/Examples/ContactApp/Contact.nim b/Nim/Examples/ContactApp/Contact.nim new file mode 100644 index 0000000..ec5f2b2 --- /dev/null +++ b/Nim/Examples/ContactApp/Contact.nim @@ -0,0 +1,51 @@ +## Please note we are using templates where ordinarily we would like to use procedures +## due to bug: https://github.com/Araq/Nim/issues/1821 +import NimQml, NimQmlMacros + +QtObject: + type Contact* = ref object of QObject + name: string + surname: string + + proc delete*(self: Contact) = + let qobject = self.QObject + qobject.delete + + proc newContact*(): Contact = + new(result) + result.name = "" + result.create + + method firstName*(self: Contact): string {.slot.} = + result = self.name + + method firstNameChanged*(self: Contact) {.signal.} + + method setFirstName(self: Contact, name: string) {.slot.} = + if self.name != name: + self.name = name + self.firstNameChanged() + + proc `firstName=`*(self: Contact, name: string) = self.setFirstName(name) + + QtProperty[string] firstName: + read = firstName + write = setFirstName + notify = firstNameChanged + + method surname*(self: Contact): string {.slot.} = + result = self.surname + + method surnameChanged*(self: Contact) {.signal.} + + method setSurname(self: Contact, surname: string) {.slot.} = + if self.surname != surname: + self.surname = surname + self.surnameChanged() + + proc `surname=`*(self: Contact, surname: string) = self.setSurname(surname) + + QtProperty[string] surname: + read = surname + write = setSurname + notify = surnameChanged diff --git a/Nim/Examples/ContactApp/ContactList.nim b/Nim/Examples/ContactApp/ContactList.nim new file mode 100644 index 0000000..c1ccb7e --- /dev/null +++ b/Nim/Examples/ContactApp/ContactList.nim @@ -0,0 +1,62 @@ +import NimQml, NimQmlMacros, Contact, Tables + +QtObject: + type + ContactList* = ref object of QAbstractListModel + contacts*: seq[Contact] + ContactRoles {.pure.} = enum + FirstName = 0 + Surname = 1 + + converter toCInt(value: ContactRoles): cint = return value.cint + converter toCInt(value: int): cint = return value.cint + converter toInt(value: ContactRoles): int = return value.int + converter toInt(value: cint): int = return value.int + converter toQVariant(value: string): QVariant = return value.newQVariant + + proc delete(self: ContactList) = + let model = self.QAbstractListModel + model.delete + for contact in self.contacts: + contact.delete + self.contacts = @[] + + proc newContactList*(): ContactList = + new(result, delete) + result.contacts = @[] + result.create + + method rowCount(self: ContactList, index: QModelIndex = nil): cint = + return self.contacts.len + + method data(self: ContactList, index: QModelIndex, role: cint): QVariant = + if not index.isValid: + return + if index.row < 0 or index.row >= self.contacts.len: + return + let contact = self.contacts[index.row] + let contactRole = role.ContactRoles + case contactRole: + of ContactRoles.FirstName: return contact.firstName + of ContactRoles.Surname: return contact.surname + else: return + + method roleNames(self: ContactList): Table[cint, cstring] = + result = initTable[cint, cstring]() + result[ContactRoles.FirstName] = "firstName" + result[ContactRoles.Surname] = "surname" + + method add*(self: ContactList, name: string, surname: string) {.slot.} = + let contact = newContact() + contact.firstName = name + contact.surname = surname + self.beginInsertRows(newQModelIndex(), self.contacts.len, self.contacts.len) + self.contacts.add(contact) + self.endInsertRows() + + method del*(self: ContactList, pos: int) {.slot.} = + if pos < 0 or pos >= self.contacts.len: + return + self.beginRemoveRows(newQModelIndex(), pos, pos) + self.contacts.del(pos) + self.endRemoveRows diff --git a/Nim/Examples/ContactApp/main.nim b/Nim/Examples/ContactApp/main.nim new file mode 100644 index 0000000..d5761fb --- /dev/null +++ b/Nim/Examples/ContactApp/main.nim @@ -0,0 +1,17 @@ +import NimQml, ApplicationLogic + +proc mainProc() = + let app = newQApplication() + defer: app.delete + let logic = newApplicationLogic(app) + defer: logic.delete + let engine = newQQmlApplicationEngine() + defer: engine.delete + let logicVariant = newQVariant(logic) + defer: logicVariant.delete + engine.rootContext.setContextProperty("logic", logicVariant) + engine.load("main.qml") + app.exec() + +when isMainModule: + mainProc() diff --git a/Nim/Examples/ContactApp/main.qml b/Nim/Examples/ContactApp/main.qml new file mode 100644 index 0000000..e9c73c1 --- /dev/null +++ b/Nim/Examples/ContactApp/main.qml @@ -0,0 +1,93 @@ +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.1 +import QtQuick.Window 2.1 + +ApplicationWindow { + + width: 500 + height: 300 + title: "ContactApp" + visible: true + + menuBar: MenuBar { + Menu { + title: "&File" + MenuItem { text: "&Load"; onTriggered: logic.onLoadTriggered() } + MenuItem { text: "&Save"; onTriggered: logic.onSaveTriggered() } + MenuItem { text: "&Exit"; onTriggered: logic.onExitTriggered() } + } + } + + ColumnLayout { + anchors.fill: parent + + Component { + id: tableTextDelegate + Label { + id: tableTextDelegateInstance + property var styleData: undefined + states: State { + when: styleData !== undefined + PropertyChanges { + target: tableTextDelegateInstance; + text: styleData.value; + color: styleData.textColor + } + } + } + } + + Component { + id: tableButtonDelegate + Button { + id: tableButtonDelegateInstance + property var styleData: undefined + text: "Delete" + onClicked: logic.contactList.del(styleData.row) + } + } + + Component { + id: tableItemDelegate + Loader { + id: tableItemDelegateInstance + sourceComponent: { + if (styleData.column === 0 || styleData.column === 1) + return tableTextDelegate + else if (styleData.column === 2) + return tableButtonDelegate + else + return tableTextDelegate + } + Binding { + target: tableItemDelegateInstance.item + property: "styleData" + value: styleData + } + } + } + + TableView { + model: logic.contactList + Layout.fillWidth: true + Layout.fillHeight: true + TableViewColumn { role: "firstName"; title: "FirstName"; width: 200 } + TableViewColumn { role: "surname"; title: "Surname"; width: 200} + TableViewColumn { width: 100; } + itemDelegate: tableItemDelegate + } + + RowLayout { + Label { text: "Name" } + TextField { id: nameTextField; Layout.fillWidth: true; text: "" } + Label { text: "Surname" } + TextField { id: surnameTextField; Layout.fillWidth: true; text: "" } + Button { + text: "Add" + onClicked: logic.contactList.add(nameTextField.text, surnameTextField.text) + enabled: nameTextField.text !== "" && surnameTextField.text !== "" + } + } + } +} diff --git a/Nim/Examples/HelloWorld/main.nim b/Nim/Examples/HelloWorld/main.nim index 897a160..1936404 100644 --- a/Nim/Examples/HelloWorld/main.nim +++ b/Nim/Examples/HelloWorld/main.nim @@ -3,12 +3,10 @@ import macros import typeinfo proc mainProc() = - var app: QApplication - app.create() + var app = newQApplication() defer: app.delete() - var engine: QQmlApplicationEngine - engine.create() + var engine = newQQmlApplicationEngine() defer: engine.delete() engine.load("main.qml") @@ -16,4 +14,5 @@ proc mainProc() = when isMainModule: mainProc() + GC_fullcollect() diff --git a/Nim/Examples/QtObjectMacro/Contact.nim b/Nim/Examples/QtObjectMacro/Contact.nim index 7b9c79e..380d18b 100644 --- a/Nim/Examples/QtObjectMacro/Contact.nim +++ b/Nim/Examples/QtObjectMacro/Contact.nim @@ -6,20 +6,24 @@ QtObject: type Contact* = ref object of QObject m_name: string - template newContact*(): Contact = - var result = Contact(m_name: "initialName") - result.create - result - - method getName*(contact: Contact): string {.slot.} = - result = contact.m_name + proc delete*(self: Contact) = + var qobject = self.QObject + qobject.delete() - method nameChanged*(contact: Contact) {.signal.} + proc newContact*(): Contact = + new(result, delete) + result.m_name = "InitialName" + result.create - method setName*(contact: Contact, name: string) {.slot.} = - if contact.m_name != name: - contact.m_name = name - contact.nameChanged() + method getName*(self: Contact): string {.slot.} = + result = self.m_name + + method nameChanged*(self: Contact) {.signal.} + + method setName*(self: Contact, name: string) {.slot.} = + if self.m_name != name: + self.m_name = name + self.nameChanged() QtProperty[string] name: read = getName diff --git a/Nim/Examples/QtObjectMacro/main.nim b/Nim/Examples/QtObjectMacro/main.nim index 9c45926..784a67d 100644 --- a/Nim/Examples/QtObjectMacro/main.nim +++ b/Nim/Examples/QtObjectMacro/main.nim @@ -2,26 +2,22 @@ import NimQml import Contact proc mainProc() = - var app: QApplication - app.create() + var app = newQApplication() defer: app.delete() var contact = newContact() defer: contact.delete() - var engine: QQmlApplicationEngine - engine.create() + var engine = newQQmlApplicationEngine() defer: engine.delete() - var variant: QVariant - variant.create(contact) + var variant = newQVariant(contact) defer: variant.delete() - var rootContext: QQmlContext = engine.rootContext() - rootContext.setContextProperty("contact", variant) + engine.rootContext.setContextProperty("contact", variant) engine.load("main.qml") app.exec() when isMainModule: mainProc() - + GC_fullcollect() diff --git a/Nim/Examples/SimpleData/main.nim b/Nim/Examples/SimpleData/main.nim index 01f5eb9..d20e464 100644 --- a/Nim/Examples/SimpleData/main.nim +++ b/Nim/Examples/SimpleData/main.nim @@ -3,36 +3,32 @@ import macros import typeinfo proc mainProc() = - var app: QApplication - app.create() + var app = newQApplication() defer: app.delete() - var engine: QQmlApplicationEngine - engine.create() + var engine = newQQmlApplicationEngine() defer: engine.delete() - var qVar1: QVariant - qVar1.create() + var qVar1 = newQVariant(10) defer: qVar1.delete() - qVar1.intVal = 10 - var qVar2: QVariant - qVar2.create() + var qVar2 = newQVariant("Hello World") defer: qVar2.delete() - qVar2.stringVal = "Hello World" - var qVar3: QVariant - qVar3.create() + var qVar3 = newQVariant(false) defer: qVar3.delete() - qVar3.boolVal = false + + var qVar4 = newQVariant(3.5.float) + defer: qVar4.delete() engine.rootContext.setContextProperty("qVar1", qVar1) engine.rootContext.setContextProperty("qVar2", qVar2) - engine.rootContext.setContextProperty("qVar3", qVar2) - + engine.rootContext.setContextProperty("qVar3", qVar3) + engine.rootContext.setContextProperty("qVar4", qVar4) engine.load("main.qml") app.exec() when isMainModule: mainProc() + GC_fullcollect() diff --git a/Nim/Examples/SimpleData/main.qml b/Nim/Examples/SimpleData/main.qml index 8f32b75..99ecb91 100644 --- a/Nim/Examples/SimpleData/main.qml +++ b/Nim/Examples/SimpleData/main.qml @@ -16,5 +16,6 @@ ApplicationWindow SpinBox { value: qVar1} TextField { text: qVar2} CheckBox { checked: qVar3} + SpinBox { value: qVar4; decimals: 1 } } } diff --git a/Nim/Examples/SlotsAndProperties/Contact.nim b/Nim/Examples/SlotsAndProperties/Contact.nim index 797d82c..3310b0b 100644 --- a/Nim/Examples/SlotsAndProperties/Contact.nim +++ b/Nim/Examples/SlotsAndProperties/Contact.nim @@ -4,17 +4,24 @@ import NimQml type Contact = ref object of QObject m_name: string - -template newContact*(): Contact = - var result = Contact(m_name: "initialName") - result.create() - result.m_name = "InitialName" - result.registerSlot("getName", [QMetaType.QString]) - result.registerSlot("setName", [QMetaType.Void, QMetaType.QString]) - result.registerSignal("nameChanged", [QMetaType.Void]) - result.registerProperty("name", QMetaType.QString, "getName", "setName", "nameChanged") - result +proc delete*(self: Contact) = + var qobject = self.QObject + qobject.delete() + +proc create*(self: Contact) = + var qobject = self.QObject + qobject.create() + self.m_name = "InitialName" + self.registerSlot("getName", [QMetaType.QString]) + self.registerSlot("setName", [QMetaType.Void, QMetaType.QString]) + self.registerSignal("nameChanged", [QMetaType.Void]) + self.registerProperty("name", QMetaType.QString, "getName", "setName", "nameChanged") + +proc newContact*(): Contact = + new(result, delete) + result.create() + method getName*(self: Contact): string = result = self.m_name @@ -30,4 +37,4 @@ method onSlotCalled(self: Contact, slotName: string, args: openarray[QVariant]) of "setName": self.setName(args[1].stringVal) else: - discard() \ No newline at end of file + discard() diff --git a/Nim/Examples/SlotsAndProperties/main.nim b/Nim/Examples/SlotsAndProperties/main.nim index 3e85e9e..e395a41 100644 --- a/Nim/Examples/SlotsAndProperties/main.nim +++ b/Nim/Examples/SlotsAndProperties/main.nim @@ -2,26 +2,23 @@ import NimQml import Contact proc mainProc() = - var app: QApplication - app.create() + var app = newQApplication() defer: app.delete() var contact = newContact() defer: contact.delete() - var engine: QQmlApplicationEngine - engine.create() + var engine = newQQmlApplicationEngine() defer: engine.delete() - var variant: QVariant - variant.create(contact) + var variant = newQVariant(contact) defer: variant.delete() - var rootContext: QQmlContext = engine.rootContext() - rootContext.setContextProperty("contact", variant) + engine.rootContext.setContextProperty("contact", variant) engine.load("main.qml") app.exec() when isMainModule: mainProc() + GC_fullcollect() diff --git a/Nim/NimQml/NimQml.nim b/Nim/NimQml/NimQml.nim index cd40e6c..a9f8914 100644 --- a/Nim/NimQml/NimQml.nim +++ b/Nim/NimQml/NimQml.nim @@ -1,13 +1,14 @@ -import NimQmlTypes -import tables +include NimQmlTypes ## NimQml aims to provide binding to the QML for the Nim programming language - -export QObject -export QApplication -export QVariant -export QQmlApplicationEngine -export QQmlContext +## +## Optional finalizers +## ------------------- +## To enable finalizers you must define ``nimqml_use_finalizers`` by passing +## the option, ``-d:nimqml_use_finalizers``, to the Nim compiler. The relevant +## delete method will then be called automatically by the garbage collector. +## Care should be taken when using this approach as there are no guarantees +## when a finalzier will be run, or if, indeed, it will run at all. type QMetaType* {.pure.} = enum ## \ ## Qt metatypes values used for specifing the @@ -19,276 +20,770 @@ type QMetaType* {.pure.} = enum ## \ Int = cint(2), QString = cint(10), VoidStar = cint(31), + QObjectStar = cint(39), QVariant = cint(41), - Void = cint(43) + Void = cint(43), -proc debugMsg(message: string) = - echo "NimQml: ", message +var qobjectRegistry = initTable[ptr QObjectObj, bool]() -proc debugMsg(typeName: string, procName: string) = - var message = typeName - message &= ": " - message &= procName - debugMsg(message) +template debugMsg(message: string) = + {.push warning[user]: off.} # workaround to remove warnings; this won't be needed soon + when defined(debug): + {.pop.} + echo "NimQml: ", message + else: + {.pop.} + +template debugMsg(typeName: string, procName: string) = + {.push warning[user]: off.} # workaround to remove warnings; this won't be needed soon + when defined(debug): + {.pop.} + var message = typeName + message &= ": " + message &= procName + debugMsg(message) + else: + {.pop.} -proc debugMsg(typeName: string, procName: string, userMessage: string) = - var message = typeName - message &= ": " - message &= procName - message &= " " - message &= userMessage - debugMsg(message) +template debugMsg(typeName: string, procName: string, userMessage: string) = + {.push warning[user]: off.} # workaround to remove warnings; this won't be needed soon + when defined(debug): + {.pop.} + var message = typeName + message &= ": " + message &= procName + message &= " " + message &= userMessage + debugMsg(message) + else: + {.pop.} + +template newWithCondFinalizer(variable: expr, finalizer: expr) = + ## calls ``new`` but only setting a finalizer when ``nimqml_use_finalizers`` + ## is defined + {.push warning[user]: off.} # workaround to remove warnings; this won't be needed soon + when defined(nimqml_use_finalizers): + {.pop.} + new(variable, finalizer) + else: + {.pop.} + new(variable) # QVariant -proc dos_qvariant_create(variant: var pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_create_int(variant: var pointer, value: cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_create_bool(variant: var pointer, value: bool) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_create_string(variant: var pointer, value: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_create_qobject(variant: var pointer, value: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_delete(variant: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_isnull(variant: pointer, isNull: var bool) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_toInt(variant: pointer, value: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_toBool(variant: pointer, value: var bool) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_toString(variant: pointer, value: var cstring, length: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_setInt(variant: pointer, value: cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_setBool(variant: pointer, value: bool) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qvariant_setString(variant: pointer, value: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_create(variant: var RawQVariant) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_create_int(variant: var RawQVariant, value: cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_create_bool(variant: var RawQVariant, value: bool) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_create_string(variant: var RawQVariant, value: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_create_qobject(variant: var RawQVariant, value: RawQObject) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_create_qvariant(variant: var RawQVariant, value: RawQVariant) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_create_float(variant: var RawQVariant, value: cfloat) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_create_double(variant: var RawQVariant, value: cdouble) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_delete(variant: RawQVariant) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_isnull(variant: RawQVariant, isNull: var bool) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_toInt(variant: RawQVariant, value: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_toBool(variant: RawQVariant, value: var bool) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_toString(variant: RawQVariant, value: var cstring, length: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_setInt(variant: RawQVariant, value: cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_setBool(variant: RawQVariant, value: bool) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_setString(variant: RawQVariant, value: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_assign(leftValue: RawQVariant, rightValue: RawQVariant) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_toFloat(variant: RawQVariant, value: var cfloat) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_setFloat(variant: RawQVariant, value: float) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_toDouble(variant: RawQVariant, value: var cdouble) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_setDouble(variant: RawQVariant, value: cdouble) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qvariant_setQObject(variant: RawQVariant, value: RawQObject) {.cdecl, dynlib:"libDOtherSide.so", importc.} proc dos_chararray_delete(rawCString: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc create*(variant: var QVariant) = +proc create*(variant: QVariant) = ## Create a new QVariant - var data: pointer - dos_qvariant_create(data) - variant = QVariant(data) + dos_qvariant_create(variant.data) + variant.deleted = false -proc create*(variant: var QVariant, value: cint) = +proc create*(variant: QVariant, value: cint) = ## Create a new QVariant given a cint value - var data: pointer - dos_qvariant_create_int(data, value) - variant = QVariant(data) + dos_qvariant_create_int(variant.data, value) + variant.deleted = false -proc create*(variant: var QVariant, value: bool) = +proc create*(variant: QVariant, value: bool) = ## Create a new QVariant given a bool value - var data: pointer - dos_qvariant_create_bool(data, value) - variant = QVariant(data) + dos_qvariant_create_bool(variant.data, value) + variant.deleted = false -proc create*(variant: var QVariant, value: string) = +proc create*(variant: QVariant, value: string) = ## Create a new QVariant given a string value - var data: pointer - dos_qvariant_create_string(data, value) - variant = QVariant(data) + dos_qvariant_create_string(variant.data, value) + variant.deleted = false -proc create*(variant: var QVariant, value: QObject) = +proc create*(variant: QVariant, value: QObject) = ## Create a new QVariant given a QObject - var data: pointer - dos_qvariant_create_qobject(data, value.data) - variant = QVariant(data) + dos_qvariant_create_qobject(variant.data, value.data.RawQObject) + variant.deleted = false +proc create*(variant: QVariant, value: RawQVariant) = + ## Create a new QVariant given another QVariant. + ## The inner value of the QVariant is copied + dos_qvariant_create_qvariant(variant.data, value) + variant.deleted = false + +proc create*(variant: QVariant, value: cfloat) = + ## Create a new QVariant given a cfloat value + dos_qvariant_create_float(variant.data, value) + variant.deleted = false + +proc create*(variant: QVariant, value: QVariant) = + ## Create a new QVariant given another QVariant. + ## The inner value of the QVariant is copied + create(variant, value.data) + proc delete*(variant: QVariant) = ## Delete a QVariant - debugMsg("QVariant", "delete") - dos_qvariant_delete(pointer(variant)) + if not variant.deleted: + debugMsg("QVariant", "delete") + dos_qvariant_delete(variant.data) + variant.data = nil.RawQVariant + variant.deleted = true +proc newQVariant*(): QVariant = + ## Return a new QVariant + newWithCondFinalizer(result, delete) + result.create() + +proc newQVariant*(value: cint): QVariant = + ## Return a new QVariant given a cint + newWithCondFinalizer(result, delete) + result.create(value) + +proc newQVariant*(value: bool): QVariant = + ## Return a new QVariant given a bool + newWithCondFinalizer(result, delete) + result.create(value) + +proc newQVariant*(value: string): QVariant = + ## Return a new QVariant given a string + newWithCondFinalizer(result, delete) + result.create(value) + +proc newQVariant*(value: QObject): QVariant = + ## Return a new QVariant given a QObject + newWithCondFinalizer(result, delete) + result.create(value) + +proc newQVariant*(value: RawQVariant): QVariant = + ## Return a new QVariant given a raw QVariant pointer + newWithCondFinalizer(result, delete) + result.create(value) + +proc newQVariant*(value: QVariant): QVariant = + ## Return a new QVariant given another QVariant + newWithCondFinalizer(result, delete) + result.create(value) + +proc newQVariant*(value: float): QVariant = + ## Return a new QVariant given a float + newWithCondFinalizer(result, delete) + result.create(value) + proc isNull*(variant: QVariant): bool = ## Return true if the QVariant value is null, false otherwise - dos_qvariant_isnull(pointer(variant), result) + dos_qvariant_isnull(variant.data, result) proc intVal*(variant: QVariant): int = ## Return the QVariant value as int var rawValue: cint - dos_qvariant_toInt(pointer(variant), rawValue) - result = cast[int](rawValue) + dos_qvariant_toInt(variant.data, rawValue) + result = rawValue.cint proc `intVal=`*(variant: QVariant, value: int) = ## Sets the QVariant value int value - var rawValue = cast[cint](value) - dos_qvariant_setInt(pointer(variant), rawValue) + var rawValue = value.cint + dos_qvariant_setInt(variant.data, rawValue) proc boolVal*(variant: QVariant): bool = ## Return the QVariant value as bool - dos_qvariant_toBool(pointer(variant), result) + dos_qvariant_toBool(variant.data, result) proc `boolVal=`*(variant: QVariant, value: bool) = ## Sets the QVariant bool value - dos_qvariant_setBool(pointer(variant), value) + dos_qvariant_setBool(variant.data, value) +proc floatVal*(variant: QVariant): float = + ## Return the QVariant value as float + var rawValue: cfloat + dos_qvariant_toFloat(variant.data, rawValue) + result = rawValue.cfloat + +proc `floatVal=`*(variant: QVariant, value: float) = + ## Sets the QVariant float value + dos_qvariant_setFloat(variant.data, value.cfloat) + +proc doubleVal*(variant: QVariant): cdouble = + ## Return the QVariant value as double + var rawValue: cdouble + dos_qvariant_toDouble(variant.data, rawValue) + result = rawValue + +proc `doubleVal=`*(variant: QVariant, value: cdouble) = + ## Sets the QVariant double value + dos_qvariant_setDouble(variant.data, value) + proc stringVal*(variant: QVariant): string = ## Return the QVariant value as string var rawCString: cstring var rawCStringLength: cint - dos_qvariant_toString(pointer(variant), rawCString, rawCStringLength) + dos_qvariant_toString(variant.data, rawCString, rawCStringLength) result = $rawCString dos_chararray_delete(rawCString) proc `stringVal=`*(variant: QVariant, value: string) = ## Sets the QVariant string value - dos_qvariant_setString(pointer(variant), value) + dos_qvariant_setString(variant.data, value) +proc `qobjectVal=`*(variant: QVariant, value: QObject) = + ## Sets the QVariant qobject value + dos_qvariant_setQObject(variant.data, value.data.RawQObject) + +proc assign*(leftValue: QVariant, rightValue: QVariant) = + ## Assign a QVariant with another. The inner value of the QVariant is copied + dos_qvariant_assign(leftValue.data, rightValue.data) # QQmlApplicationEngine -proc dos_qqmlapplicationengine_create(engine: var pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qqmlapplicationengine_load(engine: pointer, filename: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qqmlapplicationengine_context(engine: pointer, context: var pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qqmlapplicationengine_delete(engine: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qqmlapplicationengine_create(engine: var RawQQmlApplicationEngine) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qqmlapplicationengine_load(engine: RawQQmlApplicationEngine, filename: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qqmlapplicationengine_context(engine: RawQQmlApplicationEngine, context: var QQmlContext) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qqmlapplicationengine_delete(engine: RawQQmlApplicationEngine) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc create*(engine: var QQmlApplicationEngine) = +proc create*(engine: QQmlApplicationEngine) = ## Create an new QQmlApplicationEngine - var temp: pointer - dos_qqmlapplicationengine_create(temp) - engine = QQmlApplicationEngine(temp) - + dos_qqmlapplicationengine_create(engine.data) + engine.deleted = false + proc load*(engine: QQmlApplicationEngine, filename: cstring) = ## Load the given Qml file - dos_qqmlapplicationengine_load(pointer(engine), filename) + dos_qqmlapplicationengine_load(engine.data, filename) proc rootContext*(engine: QQmlApplicationEngine): QQmlContext = ## Return the engine root context - var context: pointer - dos_qqmlapplicationengine_context(pointer(engine), context) - result = cast[QQmlContext](context) + dos_qqmlapplicationengine_context(engine.data, result) proc delete*(engine: QQmlApplicationEngine) = ## Delete the given QQmlApplicationEngine - debugMsg("QQmlApplicationEngine", "delete") - dos_qqmlapplicationengine_delete(pointer(engine)) + if not engine.deleted: + debugMsg("QQmlApplicationEngine", "delete") + dos_qqmlapplicationengine_delete(engine.data) + engine.data = nil.RawQQmlApplicationEngine + engine.deleted = true + +proc newQQmlApplicationEngine*(): QQmlApplicationEngine = + ## Return a new QQmlApplicationEngine + newWithCondFinalizer(result, delete) + result.create() # QQmlContext -proc dos_qqmlcontext_setcontextproperty(context: pointer, propertyName: cstring, propertyValue: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qqmlcontext_setcontextproperty(context: QQmlContext, propertyName: cstring, propertyValue: RawQVariant) {.cdecl, dynlib:"libDOtherSide.so", importc.} proc setContextProperty*(context: QQmlContext, propertyName: string, propertyValue: QVariant) = ## Sets a new property with the given value - dos_qqmlcontext_setcontextproperty(pointer(context), propertyName, pointer(propertyValue)) + dos_qqmlcontext_setcontextproperty(context, propertyName, propertyValue.data) # QApplication -proc dos_qguiapplication_create() {.cdecl, dynlib: "libDOtherSide.so", importc.} -proc dos_qguiapplication_exec() {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qguiapplication_delete() {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qapplication_create() {.cdecl, dynlib: "libDOtherSide.so", importc.} +proc dos_qapplication_exec() {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qapplication_quit() {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qapplication_delete() {.cdecl, dynlib:"libDOtherSide.so", importc.} proc create*(application: QApplication) = ## Create a new QApplication - dos_qguiapplication_create() + dos_qapplication_create() + application.deleted = false proc exec*(application: QApplication) = ## Start the Qt event loop - dos_qguiapplication_exec() + dos_qapplication_exec() +proc quit*(application: QApplication) = + ## Quit the Qt event loop + dos_qapplication_quit() + proc delete*(application: QApplication) = ## Delete the given QApplication - dos_qguiapplication_delete() + if not application.deleted: + debugMsg("QApplication", "delete") + dos_qapplication_delete() + application.deleted = true +proc newQApplication*(): QApplication = + ## Return a new QApplication + newWithCondFinalizer(result, delete) + result.create() + +# QGuiApplication +proc dos_qguiapplication_create() {.cdecl, dynlib: "libDOtherSide.so", importc.} +proc dos_qguiapplication_exec() {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qguiapplication_quit() {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qguiapplication_delete() {.cdecl, dynlib:"libDOtherSide.so", importc.} + +proc create*(application: QGuiApplication) = + ## Create a new QApplication + dos_qguiapplication_create() + application.deleted = false + +proc exec*(application: QGuiApplication) = + ## Start the Qt event loop + dos_qguiapplication_exec() + +proc quit*(application: QGuiApplication) = + ## Quit the Qt event loop + dos_qguiapplication_quit() + +proc delete*(application: QGuiApplication) = + ## Delete the given QApplication + if not application.deleted: + debugMsg("QApplication", "delete") + dos_qguiapplication_delete() + application.deleted = true + +proc newQGuiApplication*(): QGuiApplication = + ## Return a new QApplication + newWithCondFinalizer(result, delete) + result.create() + # QObject -type QVariantArray {.unchecked.} = array[0..0, QVariant] -type QVariantArrayPtr = ptr QVariantArray +type RawQVariantArray {.unchecked.} = array[0..0, RawQVariant] +type RawQVariantArrayPtr = ptr RawQVariantArray +type RawQVariantSeq = seq[RawQVariant] -proc toVariantSeq(args: QVariantArrayPtr, numArgs: cint): seq[QVariant] = +proc toVariantSeq(args: RawQVariantArrayPtr, numArgs: cint): seq[QVariant] = result = @[] for i in 0..numArgs-1: - result.add(args[i]) + result.add(newQVariant(args[i])) + +proc toRawVariantSeq(args: openarray[QVariant]): RawQVariantSeq = + result = @[] + for variant in args: + result.add(variant.data) + +proc delete(sequence: seq[QVariant]) = + for variant in sequence: + variant.delete proc toCIntSeq(metaTypes: openarray[QMetaType]): seq[cint] = result = @[] for metaType in metaTypes: result.add(cint(metaType)) + +type QObjectCallBack = proc(nimobject: ptr QObjectObj, slotName: RawQVariant, numArguments: cint, arguments: RawQVariantArrayPtr) {.cdecl.} -proc dos_qobject_create(qobject: var pointer, nimobject: pointer, qobjectCallback: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qobject_delete(qobject: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qobject_slot_create(qobject: pointer, slotName: cstring, argumentsCount: cint, argumentsMetaTypes: ptr cint, slotIndex: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qobject_signal_create(qobject: pointer, signalName: cstring, argumentsCount: cint, argumentsMetaTypes: ptr cint, signalIndex: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qobject_signal_emit(qobject: pointer, signalName: cstring, argumentsCount: cint, arguments: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qobject_property_create(qobject: pointer, propertyName: cstring, propertyType: cint, readSlot: cstring, writeSlot: cstring, notifySignal: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qobject_create(qobject: var RawQObject, nimobject: ptr QObjectObj, qobjectCallback: QObjectCallBack) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qobject_delete(qobject: RawQObject) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qobject_slot_create(qobject: RawQObject, slotName: cstring, argumentsCount: cint, argumentsMetaTypes: ptr cint, slotIndex: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qobject_signal_create(qobject: RawQObject, signalName: cstring, argumentsCount: cint, argumentsMetaTypes: ptr cint, signalIndex: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qobject_signal_emit(qobject: RawQObject, signalName: cstring, argumentsCount: cint, arguments: ptr RawQVariant) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qobject_property_create(qobject: RawQObject, propertyName: cstring, propertyType: cint, readSlot: cstring, writeSlot: cstring, notifySignal: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} method onSlotCalled*(nimobject: QObject, slotName: string, args: openarray[QVariant]) = ## Called from the NimQml bridge when a slot is called from Qml. ## Subclasses can override the given method for handling the slot call discard() -proc qobjectCallback(nimobject: pointer, slotName: QVariant, numArguments: cint, arguments: QVariantArrayPtr) {.exportc.} = - var nimQObjectCasted = cast[ptr QObject](nimobject) - # forward to the QObject subtype instance - nimQObjectCasted[].onSlotCalled(slotName.stringVal, arguments.toVariantSeq(numArguments)) +proc qobjectCallback(nimObject: ptr QObjectObj, slotName: RawQVariant, numArguments: cint, arguments: RawQVariantArrayPtr) {.cdecl, exportc.} = + if qobjectRegistry[nimObject]: + let qobject = cast[QObject](nimObject) + GC_ref(qobject) + let slotNameAsQVariant = newQVariant(slotName) + defer: slotNameAsQVariant.delete + let argumentsAsQVariant = arguments.toVariantSeq(numArguments) + defer: argumentsAsQVariant.delete + # Forward to args to the slot + qobject.onSlotCalled(slotNameAsQVariant.stringVal, argumentsAsQVariant) + # Update the slot return value + dos_qvariant_assign(arguments[0], argumentsAsQVariant[0].data) + GC_unref(qobject) -proc create*(qobject: var QObject) = +proc create*(qobject: QObject) = ## Create a new QObject - qobject.name = "QObject" + debugMsg("QObject", "create") + qobject.deleted = false qobject.slots = initTable[string,cint]() qobject.signals = initTable[string, cint]() - dos_qobject_create(qobject.data, addr(qobject), qobjectCallback) - + qobject.properties = initTable[string, cint]() + let qobjectPtr = addr(qobject[]) + dos_qobject_create(qobject.data, qobjectPtr, qobjectCallback) + qobjectRegistry[qobjectPtr] = true + proc delete*(qobject: QObject) = ## Delete the given QObject - dos_qobject_delete(qobject.data) + if not qobject.deleted: + debugMsg("QObject", "delete") + let qobjectPtr = addr(qobject[]) + qobjectRegistry.del qobjectPtr + dos_qobject_delete(qobject.data) + qobject.data = nil.RawQObject + qobject.deleted = true + +proc newQObject*(): QObject = + ## Return a new QObject + newWithCondFinalizer(result, delete) + result.create() -proc registerSlot*(qobject: var QObject, +proc registerSlot*(qobject: QObject, slotName: string, metaTypes: openarray[QMetaType]) = ## Register a slot in the QObject with the given name and signature # Copy the metatypes array var copy = toCIntSeq(metatypes) var index: cint - dos_qobject_slot_create(qobject.data, slotName, cint(copy.len), cast[ptr cint](addr(copy[0])), index) + dos_qobject_slot_create(qobject.data, slotName, cint(copy.len), addr(copy[0].cint), index) qobject.slots[slotName] = index -proc registerSignal*(qobject: var QObject, +proc registerSignal*(qobject: QObject, signalName: string, metatypes: openarray[QMetaType]) = ## Register a signal in the QObject with the given name and signature var index: cint if metatypes.len > 0: var copy = toCIntSeq(metatypes) - dos_qobject_signal_create(qobject.data, signalName, cast[cint](copy.len), cast[ptr cint](addr(copy[0])), index) + dos_qobject_signal_create(qobject.data, signalName, copy.len.cint, addr(copy[0].cint), index) else: - dos_qobject_signal_create(qobject.data, signalName, 0, cast[ptr cint](0), index) + dos_qobject_signal_create(qobject.data, signalName, 0, nil, index) qobject.signals[signalName] = index -proc registerProperty*(qobject: var QObject, +proc registerProperty*(qobject: QObject, propertyName: string, propertyType: QMetaType, readSlot: string, writeSlot: string, notifySignal: string) = ## Register a property in the QObject with the given name and type. - dos_qobject_property_create(qobject.data, propertyName, cast[cint](propertyType), readSlot, writeSlot, notifySignal) + assert propertyName != nil, "property name cannot be nil" + # don't convert a nil string, else we get a strange memory address + let cReadSlot: cstring = if readSlot == nil: cast[cstring](nil) else: readSlot + let cWriteSlot: cstring = if writeSlot == nil: cast[cstring](nil) else: writeSlot + let cNotifySignal: cstring = if notifySignal == nil: cast[cstring](nil) else: notifySignal + dos_qobject_property_create(qobject.data, propertyName, propertyType.cint, cReadSlot, cWriteSlot, cNotifySignal) proc emit*(qobject: QObject, signalName: string, args: openarray[QVariant] = []) = ## Emit the signal with the given name and values if args.len > 0: - var copy: seq[QVariant] - for i in 0..args.len-1: - copy.add(args[i]) - dos_qobject_signal_emit(qobject.data, signalName, cast[cint](args.len), cast[pointer](addr(copy[0]))) + var copy = args.toRawVariantSeq + dos_qobject_signal_emit(qobject.data, signalName, copy.len.cint, addr(copy[0])) else: - dos_qobject_signal_emit(qobject.data, signalName, 0, cast[pointer](0)) + dos_qobject_signal_emit(qobject.data, signalName, 0, nil) # QQuickView -proc dos_qquickview_create(view: var pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qquickview_delete(view: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qquickview_show(view: pointer) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qquickview_source(view: pointer, filename: var cstring, length: var int) {.cdecl, dynlib:"libDOtherSide.so", importc.} -proc dos_qquickview_set_source(view: pointer, filename: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qquickview_create(view: var RawQQuickView) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qquickview_delete(view: RawQQuickView) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qquickview_show(view: RawQQuickView) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qquickview_source(view: RawQQuickView, filename: var cstring, length: var int) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qquickview_set_source(view: RawQQuickView, filename: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} proc create(view: var QQuickView) = ## Create a new QQuickView - var temp: pointer - dos_qquickview_create(temp) - view = QQuickView(temp) + dos_qquickview_create(view.data) + view.deleted = false proc source(view: QQuickView): cstring = ## Return the source Qml file loaded by the view var length: int - dos_qquickview_source(pointer(view), result, length) + dos_qquickview_source(view.data, result, length) proc `source=`(view: QQuickView, filename: cstring) = ## Sets the source Qml file laoded by the view - dos_qquickview_set_source(pointer(view), filename) + dos_qquickview_set_source(view.data, filename) proc show(view: QQuickView) = ## Sets the view visible - dos_qquickview_show(pointer(view)) + dos_qquickview_show(view.data) proc delete(view: QQuickView) = ## Delete the given QQuickView - dos_qquickview_delete(pointer(view)) + if not view.deleted: + debugMsg("QQuickView", "delete") + dos_qquickview_delete(view.data) + view.data = nil.RawQQuickView + view.deleted = true +proc newQQuickView*(): QQuickView = + ## Return a new QQuickView + newWithCondFinalizer(result, delete) + result.create() +# QModelIndex +proc dos_qmodelindex_create(modelIndex: var RawQModelIndex) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qmodelindex_delete(modelIndex: RawQModelIndex) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qmodelindex_row(modelIndex: RawQModelIndex, row: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qmodelindex_column(modelIndex: RawQModelIndex, column: var cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qmodelindex_isValid(modelIndex: RawQModelIndex, column: var bool) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qmodelindex_data(modelIndex: RawQModelIndex, role: cint, data: RawQVariant) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qmodelindex_parent(modelIndex: RawQModelIndex, parent: RawQModelIndex) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qmodelindex_child(modelIndex: RawQModelIndex, row: cint, column: cint, parent: RawQModelIndex) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qmodelindex_sibling(modelIndex: RawQModelIndex, row: cint, column: cint, sibling: RawQModelIndex) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc create*(modelIndex: var QModelIndex) = + ## Create a new QModelIndex + dos_qmodelindex_create(modelIndex.data) + modelIndex.deleted = false + +proc create*(modelIndex: var QModelIndex, rawQModelIndex: RawQModelIndex) = + ## Create a new QModelIndex + modelIndex.data = rawQModelIndex + modelIndex.deleted = false + +proc delete*(modelIndex: QModelIndex) = + ## Delete the given QModelIndex + if not modelIndex.deleted: + debugMsg("QModelIndex", "delete") + dos_qmodelindex_delete(modelIndex.data) + modelIndex.data = nil.RawQModelIndex + modelIndex.deleted = true + +proc newQModelIndex*(): QModelIndex = + ## Return a new QModelIndex + newWithCondFinalizer(result, delete) + result.create() + +proc newQModelIndex*(rawQModelIndex: RawQModelIndex): QModelIndex = + ## Return a new QModelIndex given a raw index + newWithCondFinalizer(result, delete) + result.create(rawQModelIndex) + +proc row*(modelIndex: QModelIndex): cint = + ## Return the index row + dos_qmodelindex_row(modelIndex.data, result) + +proc column*(modelIndex: QModelIndex): cint = + ## Return the index column + dos_qmodelindex_column(modelIndex.data, result) + +proc isValid*(modelIndex: QModelIndex): bool = + ## Return true if the index is valid, false otherwise + dos_qmodelindex_isValid(modelIndex.data, result) + +proc data*(modelIndex: QModelIndex, role: cint): QVariant = + ## Return the model data associated to the given role + result = newQVariant() + dos_qmodelindex_data(modelIndex.data, role, result.data) + +proc parent*(modelIndex: QModelIndex): QModelIndex = + ## Return the parent index + result = newQModelIndex() + dos_qmodelindex_parent(modelIndex.data, result.data) + +proc child*(modelIndex: QModelIndex, row: cint, column: cint): QModelIndex = + ## Return the child index associated to the given row and column + result = newQModelIndex() + dos_qmodelindex_child(modelIndex.data, row, column, result.data) + +proc sibling*(modelIndex: QModelIndex, row: cint, column: cint): QModelIndex = + ## Return the sibling index associated to the given row and column + result = newQModelIndex() + dos_qmodelindex_sibling(modelIndex.data, row, column, result.data) + +# QHashIntByteArray +proc dos_qhash_int_qbytearray_create(qHash: var RawQHashIntByteArray) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qhash_int_qbytearray_delete(qHash: RawQHashIntByteArray) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qhash_int_qbytearray_insert(qHash: RawQHashIntByteArray, key: int, value: cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qhash_int_qbytearray_value(qHash: RawQHashIntByteArray, key: int, value: var cstring) {.cdecl, dynlib:"libDOtherSide.so", importc.} + +proc create*(qHash: var QHashIntByteArray) = + ## Create the QHash + debugMsg("QHashIntByteArray", "create") + dos_qhash_int_qbytearray_create(qHash.data) + qHash.deleted = false + +proc delete*(qHash: QHashIntByteArray) = + ## Delete the QHash + if not qHash.deleted: + debugMsg("QHashIntByteArray", "delete") + dos_qhash_int_qbytearray_delete(qHash.data) + qHash.deleted = true + +proc insert*(qHash: QHashIntByteArray, key: int, value: cstring) = + ## Insert the value at the given key + dos_qhash_int_qbytearray_insert(qHash.data, key, value) + +proc value*(qHash: QHashIntByteArray, key: int): string = + ## Return the value associated at the given key + var rawString: cstring + dos_qhash_int_qbytearray_value(qHash.data, key, rawString) + result = $rawString + dos_chararray_delete(rawString) + +proc newQHashIntQByteArray*(): QHashIntByteArray = + ## Create a new QHashIntQByteArray + newWithCondFinalizer(result, delete) + result.create() + +# QAbstractListModel +type + RowCountCallback = proc(modelObject: ptr QAbstractListModelObj, rawIndex: RawQModelIndex, result: var cint) {.cdecl.} + ColumnCountCallback = proc(modelObject: ptr QAbstractListModelObj, rawIndex: RawQModelIndex, result: var cint) {.cdecl.} + DataCallback = proc(modelObject: ptr QAbstractListModelObj, rawIndex: RawQModelIndex, role: cint, result: RawQVariant) {.cdecl.} + SetDataCallback = proc(modelObject: ptr QAbstractListModelObj, rawIndex: RawQModelIndex, value: RawQVariant, role: cint, result: var bool) {.cdecl.} + RoleNamesCallback = proc(modelObject: ptr QAbstractListModelObj, result: RawQHashIntByteArray) {.cdecl.} + FlagsCallback = proc(modelObject: ptr QAbstractListModelObj, index: RawQModelIndex, result: var cint) {.cdecl.} + HeaderDataCallback = proc(modelObject: ptr QAbstractListModelObj, section: cint, orientation: cint, role: cint, result: RawQVariant) {.cdecl.} + +proc dos_qabstractlistmodel_create(model: var RawQAbstractListModel, + modelPtr: ptr QAbstractListModelObj, + qobjectCallback: QObjectCallBack, + rowCountCallback: RowCountCallback, + columnCountCallback: ColumnCountCallback, + dataCallback: DataCallback, + setDataCallback: SetDataCallBack, + roleNamesCallback: RoleNamesCallback, + flagsCallback: FlagsCallback, + headerDataCallback: HeaderDataCallback) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qabstractlistmodel_delete(model: RawQAbstractListModel) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qabstractlistmodel_beginInsertRows(model: RawQAbstractListModel, + parentIndex: RawQModelIndex, + first: cint, + last: cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qabstractlistmodel_endInsertRows(model: RawQAbstractListModel) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qabstractlistmodel_beginRemoveRows(model: RawQAbstractListModel, + parentIndex: RawQModelIndex, + first: cint, + last: cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qabstractlistmodel_endRemoveRows(model: RawQAbstractListModel) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qabstractlistmodel_beginResetModel(model: RawQAbstractListModel) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qabstractlistmodel_endResetModel(model: RawQAbstractListModel) {.cdecl, dynlib:"libDOtherSide.so", importc.} +proc dos_qabstractlistmodel_dataChanged(model: RawQAbstractListModel, + parentLeft: RawQModelIndex, + bottomRight: RawQModelIndex, + rolesArrayPtr: ptr cint, + rolesArrayLength: cint) {.cdecl, dynlib:"libDOtherSide.so", importc.} + +method rowCount*(model: QAbstractListModel, index: QModelIndex): cint = + ## Return the model's row count + return 0 + +proc rowCountCallback(modelObject: ptr QAbstractListModelObj, rawIndex: RawQModelIndex, result: var cint) {.cdecl, exportc.} = + debugMsg("QAbstractListModel", "rowCountCallback") + let model = cast[QAbstractListModel](modelObject) + let index = newQModelIndex(rawIndex) + result = model.rowCount(index) + +method columnCount*(model: QAbstractListModel, index: QModelIndex): cint = + ## Return the model's column count + return 1 + +proc columnCountCallback(modelObject: ptr QAbstractListModelObj, rawIndex: RawQModelIndex, result: var cint) {.cdecl, exportc.} = + debugMsg("QAbstractListModel", "columnCountCallback") + let model = cast[QAbstractListModel](modelObject) + let index = newQModelIndex(rawIndex) + result = model.columnCount(index) + +method data*(model: QAbstractListModel, index: QModelIndex, role: cint): QVariant = + ## Return the data at the given model index and role + return nil + +proc dataCallback(modelObject: ptr QAbstractListModelObj, rawIndex: RawQModelIndex, role: cint, result: RawQVariant) {.cdecl, exportc.} = + debugMsg("QAbstractListModel", "dataCallback") + let model = cast[QAbstractListModel](modelObject) + let index = newQModelIndex(rawIndex) + let variant = data(model, index, role) + if variant != nil: + dos_qvariant_assign(result, variant.data) + variant.delete + +method setData*(model: QAbstractListModel, index: QModelIndex, value: QVariant, role: cint): bool = + ## Sets the data at the given index and role. Return true on success, false otherwise + return false + +proc setDataCallback(modelObject: ptr QAbstractListModelObj, rawIndex: RawQModelIndex, rawQVariant: RawQVariant, role: cint, result: var bool) {.cdecl, exportc.} = + debugMsg("QAbstractListModel", "setDataCallback") + let model = cast[QAbstractListModel](modelObject) + let index = newQModelIndex(rawIndex) + let variant = newQVariant(rawQVariant) + result = model.setData(index, variant, role) + +method roleNames*(model: QAbstractListModel): Table[cint, cstring] = + ## Return the model role names + result = initTable[cint, cstring]() + +proc roleNamesCallback(modelObject: ptr QAbstractListModelObj, hash: RawQHashIntByteArray) {.cdecl, exportc.} = + debugMsg("QAbstractListModel", "roleNamesCallback") + let model = cast[QAbstractListModel](modelObject) + let table = model.roleNames() + for pair in table.pairs: + dos_qhash_int_qbytearray_insert(hash, pair.key, pair.val) + +method flags*(model: QAbstractListModel, index: QModelIndex): QtItemFlag = + ## Return the item flags and the given index + return QtItemFlag.None + +proc flagsCallback(modelObject: ptr QAbstractListModelObj, rawIndex: RawQModelIndex, result: var cint) {.cdecl, exportc.} = + debugMsg("QAbstractListModel", "flagsCallback") + let model = cast[QAbstractListModel](modelObject) + let index = newQModelIndex(rawIndex) + result = model.flags(index).cint + +method headerData*(model: QAbstractListModel, section: cint, orientation: QtOrientation, role: cint): QVariant = + ## Returns the data for the given role and section in the header with the specified orientation + return nil + +proc headerDataCallback(modelObject: ptr QAbstractListModelObj, section: cint, orientation: cint, role: cint, result: RawQVariant) {.cdecl, exportc.} = + debugMsg("QAbstractListModel", "headerDataCallback") + let model = cast[QAbstractListModel](modelObject) + let variant = model.headerData(section, orientation.QtOrientation, role) + if variant != nil: + dos_qvariant_assign(result, variant.data) + variant.delete + +proc create*(model: var QAbstractListModel) = + ## Create a new QAbstractListModel + debugMsg("QAbstractListModel", "create") + model.slots = initTable[string,cint]() + model.signals = initTable[string, cint]() + model.properties = initTable[string, cint]() + model.deleted = false + let modelPtr = addr(model[]) + dos_qabstractlistmodel_create(model.data.RawQAbstractListModel, modelPtr, qobjectCallback, rowCountCallback, columnCountCallback, dataCallback, setDataCallback, roleNamesCallback, flagsCallback, headerDataCallback) + qobjectRegistry[modelPtr] = true + +proc delete*(model: QAbstractListModel) = + ## Delete the given QAbstractListModel + if not model.deleted: + debugMsg("QAbstractListModel", "delete") + let modelPtr = addr(model[]) + qobjectRegistry.del modelPtr + dos_qabstractlistmodel_delete(model.data.RawQAbstractListModel) + model.data = nil.RawQObject + model.deleted = true + +proc newQAbstractListModel*(): QAbstractListModel = + ## Return a new QAbstractListModel + newWithCondFinalizer(result, delete) + result.create() + +proc beginInsertRows*(model: QAbstractListModel, parentIndex: QModelIndex, first: int, last: int) = + ## Notify the view that the model is about to inserting the given number of rows + dos_qabstractlistmodel_beginInsertRows(model.data.RawQAbstractListModel, parentIndex.data, first.cint, last.cint) + +proc endInsertRows*(model: QAbstractListModel) = + ## Notify the view that the rows have been inserted + dos_qabstractlistmodel_endInsertRows(model.data.RawQAbstractListModel) + +proc beginRemoveRows*(model: QAbstractListModel, parentIndex: QModelIndex, first: int, last: int) = + ## Notify the view that the model is about to remove the given number of rows + dos_qabstractlistmodel_beginRemoveRows(model.data.RawQAbstractListModel, parentIndex.data, first.cint, last.cint) + +proc endRemoveRows*(model: QAbstractListModel) = + ## Notify the view that the rows have been removed + dos_qabstractlistmodel_endRemoveRows(model.data.RawQAbstractListModel) + +proc beginResetModel*(model: QAbstractListModel) = + ## Notify the view that the model is about to resetting + dos_qabstractlistmodel_beginResetModel(model.data.RawQAbstractListModel) + +proc endResetModel*(model: QAbstractListModel) = + ## Notify the view that model has finished resetting + dos_qabstractlistmodel_endResetModel(model.data.RawQAbstractListModel) + +proc dataChanged*(model: QAbstractListModel, + topLeft: QModelIndex, + bottomRight: QModelIndex, + roles: seq[cint]) = + ## Notify the view that the model data changed + var copy = roles + dos_qabstractlistmodel_dataChanged(model.data.RawQAbstractListModel, topLeft.data, bottomRight.data, copy[0].addr, copy.len.cint) + diff --git a/Nim/NimQml/NimQml.nimble b/Nim/NimQml/NimQml.nimble index ef79830..ab57634 100644 --- a/Nim/NimQml/NimQml.nimble +++ b/Nim/NimQml/NimQml.nimble @@ -1,6 +1,6 @@ [Package] name = "NimQml" -version = "0.2.0" +version = "0.3.0" author = "Filippo Cucchetto, Will Szumski" description = "QML bindings for Nimrod" license = "GPLv3" diff --git a/Nim/NimQml/NimQmlMacros.nim b/Nim/NimQml/NimQmlMacros.nim index 40372d7..fe6fc39 100644 --- a/Nim/NimQml/NimQmlMacros.nim +++ b/Nim/NimQml/NimQmlMacros.nim @@ -17,6 +17,8 @@ let nimFromQtVariant {.compileTime.} = { "int" : "intVal", "string" : "stringVal", "bool" : "boolVal", + "float" : "floatVal", + "QObject" : "qobjectVal", }.toTable let nim2QtMeta {.compileTime.} = { @@ -24,6 +26,7 @@ let nim2QtMeta {.compileTime.} = { "int" : "Int", "string" : "QString", "pointer" : "VoidStar", + "QObject" : "QObjectStar", "QVariant": "QVariant", "" : "Void", # no return, which is represented by an nnkEmpty node }.toTable @@ -210,7 +213,7 @@ proc getArgName*(arg: PNimrodNode): PNimrodNode {.compileTime.} = proc addSignalBody(signal: PNimrodNode): PNimrodNode {.compileTime.} = # e.g: produces: emit(MyQObject, "nameChanged") - expectKind signal, nnkMethodDef + assert signal.kind in {nnkMethodDef, nnkProcDef} result = newStmtList() # if exported, will use postfix let name = if signal.name.kind == nnkIdent: signal.name else: signal.name[1] @@ -225,14 +228,18 @@ proc addSignalBody(signal: PNimrodNode): PNimrodNode {.compileTime.} = args.add getArgName params[i] result.add newCall("emit", args) -#FIXME: changed typ from typedesc to expr to workaround Nim issue #1874 -template declareOnSlotCalled(typ: expr): stmt = +#FIXME: changed typ from typedesc to expr to workaround Nim issue #1874 +# This is declared dirty so that identifers are not bound to symbols. +# The alternative is to use `removeOpenSym` as we did for `prototypeCreate`. +# We should decide which method is preferable. +template prototypeOnSlotCalled(typ: expr): stmt {.dirty.} = method onSlotCalled(myQObject: typ, slotName: string, args: openarray[QVariant]) = - discard + var super = (typ.superType())(myQObject) + procCall onSlotCalled(super, slotName, args) #FIXME: changed parent, typ from typedesc to expr to workaround Nim issue #1874 template prototypeCreate(typ: expr): stmt = - template create*(myQObject: var typ) = + proc create*(myQObject: var typ) = var super = (typ.superType())(myQObject) procCall create(super) @@ -266,137 +273,55 @@ proc getIdentDefName*(a: PNimrodNode): PNimrodNode {.compileTime.} = elif a[0].kind == nnkPostFix: return a[0][1] -macro QtObject*(qtDecl: stmt): stmt {.immediate.} = - ## Generates boiler plate code for registering signals, slots - ## and properties. - ## - ## Currently generates: - ## - create: a method to register signal, slots and properties - ## - superType: a template that returns the super type of the - ## object defined within the macro body - ## - onSlotCalled: a method to dispatch an on slot call to the - ## appropiate method. - ## - ## Current limitations: - ## - only one type can be defined within the body of code sent to the - ## the macro. It is assumed, but not checked, that somewhere in the - ## inheritance hierarchy this object derives from ``QObject``. - ## - generics are not currently supported - expectKind(qtDecl, nnkStmtList) - #echo treeRepr qtDecl - result = newStmtList() - var slots = newSeq[PNimrodNode]() - var properties = newSeq[PNimrodNode]() - var signals = newSeq[PNimrodNode]() - # holds all user defined procedures so we can add them after create - var userDefined = newSeq[PNimrodNode]() - # assume only one type per section for now - var typ: PNimrodNode - for it in qtDecl.children(): - if it.kind == nnkTypeSection: - let typeDecl = it.findChild(it.kind == nnkTypeDef) - let superType = typeDecl.getSuperType() - if superType.kind == nnkEmpty: - # allow simple types and type aliases - result.add it - else: - # may come in useful if we want to check objects inherit from QObject - #let superName = if superType.kind == nnkIdent: superType - # else: superType.getNodeOf(nnkIdent) - if typ != nil: - error("you may not define more than one type " & - "within the code block passed to this macro") - else: # without this else, it fails to compile - typ = typeDecl - result.add it - result.add genSuperTemplate(typeDecl) - elif it.kind == nnkMethodDef: - if it.hasPragma("slot"): - let pragma = it.pragma() - it.pragma = pragma.removePragma("slot") - slots.add it # we need to gensome code later - result.add it - elif it.hasPragma("signal"): - let pragma = it.pragma() - it.pragma = pragma.removePragma("signal") - it.body = addSignalBody(it) - result.add it - signals.add it - else: - userDefined.add it - elif it.kind == nnkProcDef: - userDefined.add it - elif it.kind == nnkCommand: - let bracket = it[0] - if bracket.kind != nnkBracketExpr: - error("do not know how to handle: \n" & repr(it)) - # BracketExpr - # Ident !"QtProperty" - # Ident !"string" - let cmdIdent = bracket[0] - if cmdIdent == nil or cmdIdent.kind != nnkIdent or - ($cmdIdent).toLower() != "qtproperty": - error("do not know how to handle: \n" & repr(it)) - properties.add it - else: - # everything else should pass through unchanged - result.add it - if typ == nil: - error("you must declare an object that inherits from QObject") +proc tryHandleSigSlot(def: PNimrodNode, signals: var seq[PNimrodNode], slots: var seq[PNimrodNode], + generatedCode: var PNimrodNode): bool {.compileTime.} = + ## Checks a method/proc definition for signals and slots. On finding a method/proc with + ## a {.signal.} or {.slot.} pragma, it will strip the pragma, add the definition to the + ## appropriate seq, append the stripped version to the generated code block and return true + ## indicating the definition was a signal or a slot. If the method/proc is not a slot + ## or a slot it will return false. + expectKind generatedCode, nnkStmtList + assert (not signals.isNil) + assert (not slots.isNil) + assert def.kind in {nnkMethodDef, nnkProcDef} + result = false + if def.hasPragma("slot"): + let pragma = def.pragma() + def.pragma = pragma.removePragma("slot") + slots.add def # we need to gensome code later + generatedCode.add def + result = true + elif def.hasPragma("signal"): + let pragma = def.pragma() + def.pragma = pragma.removePragma("signal") + def.body = addSignalBody(def) + generatedCode.add def + signals.add def + result = true + +proc genCreateDecl(typ: PNimrodNode): PNimrodNode {.compileTime.} = + ## generates forward declaration for ``create`` procedure + expectKind typ, nnkTypeDef let typeName = typ.getTypeName() - - ## define onSlotCalled - var slotProto = (getAst declareOnSlotCalled(typeName))[0] - var caseStmt = newNimNode(nnkCaseStmt) - caseStmt.add ident("slotName") - for slot in slots: - var ofBranch = newNimNode(nnkOfBranch) - # for exported procedures - strip * marker - let slotName = ($slot.name).replace("*","") - ofBranch.add newLit slotName - let params = slot.params - let hasReturn = not (params[0].kind == nnkEmpty) - var branchStmts = newStmtList() - var args = newSeq[PNimrodNode]() - # first params always the object - args.add ident "myQObject" - for i in 2.. + + QtItemFlag* {.pure.} = enum + None = 0.cint, + IsSelectable = 1.cint, + IsEditable = 2.cint, + IsDragEnabled = 4.cint, + IsDropEnabled = 8.cint, + IsUserCheckable = 16.cint, + IsEnabled = 32.cint, + IsTristate = 64.cint, + NeverHasChildren = 128.cint + + QtOrientation {.pure.} = enum + Horizontal = 1.cint, + Vertical = 2.cint diff --git a/README.md b/README.md index b74beba..19b1cb7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,9 @@ THIS IS UNSTABLE AND ALPHA SOFTWARE ## Description Qml bindings for both D and Nim programming languages +## Change log +The project change log can be read [here](./CHANGELOG.md). + ## Documentation The documentation for the Nim programming language can be read [here](http://filcuc.github.io/DOtherSide/ "").