Browse Source

Add QQmlListProperty for classes that contains regular properties

Alexey Edelev 5 years ago
parent
commit
6f65c43f12

+ 18 - 1
src/generator/protobufclassgenerator.cpp

@@ -246,10 +246,16 @@ bool ProtobufClassGenerator::producePropertyMap(const FieldDescriptor *field, Pr
     std::string capProperty = field->camelcase_name();
     capProperty[0] = ::toupper(capProperty[0]);
 
+    std::string typeNameNoList = typeName;
+    if (field->is_repeated() && !field->is_map()) {
+        typeNameNoList.resize(typeNameNoList.size() - strlen("List"));
+    }
     propertyMap = {{"type", typeName},
                    {"type_lower", typeNameLower},
                    {"property_name", field->camelcase_name()},
-                   {"property_name_cap", capProperty}};
+                   {"property_name_cap", capProperty},
+                   {"type_nolist", typeNameNoList}
+                  };
     return true;
 }
 
@@ -384,6 +390,14 @@ void ProtobufClassGenerator::printProperties()
         printField(field, propertyTemplate);
     }
 
+    //Generate extra QmlListProperty that is mapped to list
+    for (int i = 0; i < mMessage->field_count(); i++) {
+        const FieldDescriptor* field = mMessage->field(i);
+        if (field->type() == FieldDescriptor::TYPE_MESSAGE && field->is_repeated() && !field->is_map()) {
+            printField(field, Templates::QmlListPropertyTemplate);
+        }
+    }
+
     Outdent();
 
     printQEnums(mMessage);
@@ -407,6 +421,9 @@ void ProtobufClassGenerator::printProperties()
             }
         }
         printField(field, Templates::GetterTemplate);
+        if (field->type() == FieldDescriptor::TYPE_MESSAGE && field->is_repeated() && !field->is_map()) {
+             printField(field, Templates::QmlListGetterTemplate);
+        }
     }
     for (int i = 0; i < mMessage->field_count(); i++) {
         auto field = mMessage->field(i);

+ 8 - 1
src/generator/protobufsourcegenerator.cpp

@@ -42,8 +42,14 @@ ProtobufSourceGenerator::ProtobufSourceGenerator(const google::protobuf::Descrip
 
 void ProtobufSourceGenerator::printRegisterBody()
 {
-    mPrinter.Print({{"classname", mClassName}, {"namespaces", mNamespacesColonDelimited}},
+    const std::map<std::string, std::string> registrationProperties = {{"classname", mClassName}, {"namespaces", mNamespacesColonDelimited}};
+    mPrinter.Print(registrationProperties,
                    Templates::ComplexTypeRegistrationTemplate);
+    Indent();
+    Indent();
+    mPrinter.Print(registrationProperties, Templates::RegisterQmlListPropertyMetaTypeTemplate);
+    Outdent();
+    Outdent();
 
     Indent();
     Indent();
@@ -72,6 +78,7 @@ void ProtobufSourceGenerator::printRegisterBody()
                              Templates::MapSerializationRegisterTemplate);
         }
     }
+
     mPrinter.Print({{"classname", mClassName}}, Templates::RegisterSerializersTemplate);
     Outdent();
     mPrinter.Print(Templates::SimpleBlockEnclosureTemplate);

+ 13 - 1
src/generator/templates.cpp

@@ -29,6 +29,7 @@ using namespace qtprotobuf::generator;
 
 const char *Templates::DefaultProtobufIncludesTemplate = "#include <QMetaType>\n"
                                                          "#include <QList>\n"
+                                                         "#include <QtQml/QQmlListProperty>\n"
                                                          "#include <qprotobufobject.h>\n"
                                                          "#include <unordered_map>\n"
                                                          "#include <QSharedPointer>\n\n";
@@ -70,6 +71,8 @@ const char *Templates::ProtoClassDefinitionTemplate = "\nclass $classname$ final
 
 const char *Templates::PropertyTemplate = "Q_PROPERTY($type$ $property_name$ READ $property_name$ WRITE set$property_name_cap$ NOTIFY $property_name$Changed)\n";
 const char *Templates::MessagePropertyTemplate = "Q_PROPERTY($type$ *$property_name$ READ $property_name$_p WRITE set$property_name_cap$_p NOTIFY $property_name$Changed)\n";
+const char *Templates::QmlListPropertyTemplate = "Q_PROPERTY(QQmlListProperty<$type_nolist$> $property_name$Data READ $property_name$_l NOTIFY $property_name$Changed)\n";
+
 const char *Templates::MemberTemplate = "$type$ m_$property_name$;\n";
 const char *Templates::EnumMemberTemplate = "::$type$ m_$property_name$;\n";
 const char *Templates::PublicBlockTemplate = "\npublic:\n";
@@ -102,6 +105,10 @@ const char *Templates::GetterTemplate = "$type$ $property_name$() const {\n"
                                         "    return m_$property_name$;\n"
                                         "}\n\n";
 
+const char *Templates::QmlListGetterTemplate = "QQmlListProperty<$type_nolist$> $property_name$_l() {\n"
+                                               "    return qtprotobuf::ProtobufObjectPrivate::constructQmlListProperty<$type_nolist$>(this, &m_$property_name$);\n"
+                                               "}\n\n";
+
 const char *Templates::SetterTemplateMessageType = "void set$property_name_cap$_p($type$ *$property_name$) {\n"
                                                    "    if ($property_name$ == nullptr) {\n"
                                                    "        m_$property_name$ = {};\n"
@@ -147,10 +154,15 @@ const char *Templates::ConstructorContentTemplate = "\n{\n    registerTypes();\n
 const char *Templates::DeclareMetaTypeTemplate = "Q_DECLARE_METATYPE($namespaces$::$classname$)\n";
 const char *Templates::DeclareMessageMetaTypeTemplate = "Q_DECLARE_METATYPE($namespaces$::$classname$)\n"
                                                         "Q_DECLARE_OPAQUE_POINTER($namespaces$::$classname$)\n";
-const char *Templates::DeclareComplexListTypeTemplate = "Q_DECLARE_METATYPE($namespaces$::$classname$List)\n";
+
+const char *Templates::DeclareComplexListTypeTemplate = "Q_DECLARE_METATYPE($namespaces$::$classname$List)\n"
+                                                        "Q_DECLARE_METATYPE(QQmlListProperty<$namespaces$::$classname$>)\n";
 const char *Templates::RegisterMetaTypeDefaultTemplate = "qRegisterMetaType<$namespaces$::$type$>();\n";
 const char *Templates::RegisterMetaTypeTemplateNoNamespace = "qRegisterMetaType<$namespaces$::$type$>(\"$type$\");\n";
 const char *Templates::RegisterMetaTypeTemplate = "qRegisterMetaType<$namespaces$::$type$>(\"$namespaces$::$type$\");\n";
+const char *Templates::RegisterQmlListPropertyMetaTypeTemplate = "qRegisterMetaType<QQmlListProperty<$namespaces$::$classname$>>(\"QQmlListProperty<$namespaces$::$classname$>\");\n"
+                                                                 "qRegisterMetaType<QQmlListProperty<$namespaces$::$classname$>>(\"QQmlListProperty<$classname$>\");\n";
+
 
 const char *Templates::QEnumTemplate = "Q_ENUM($type$)\n";
 

+ 3 - 0
src/generator/templates.h

@@ -54,6 +54,8 @@ public:
     static const char *ClassDefinitionTemplate;
     static const char *PropertyTemplate;
     static const char *MessagePropertyTemplate;
+    static const char *QmlListPropertyTemplate;
+    static const char *QmlListGetterTemplate;
     static const char *MemberTemplate;
     static const char *EnumMemberTemplate;
     static const char *PublicBlockTemplate;
@@ -96,6 +98,7 @@ public:
     static const char *RegisterMetaTypeDefaultTemplate;
     static const char *RegisterMetaTypeTemplate;
     static const char *RegisterMetaTypeTemplateNoNamespace;
+    static const char *RegisterQmlListPropertyMetaTypeTemplate;
     static const char *QEnumTemplate;
     static const char *MapSerializationRegisterTemplate;
     static const char *SerializersTemplate;

+ 35 - 1
src/protobuf/qprotobufobject_p.h

@@ -28,6 +28,7 @@
 #include <QObject>
 #include <QSharedPointer>
 #include <QMetaProperty>
+#include <QtQml/QQmlListProperty>
 
 #include <unordered_map>
 #include <memory>
@@ -660,9 +661,42 @@ public:
             ProtobufObjectPrivate::deserializeProperty(object, wireType, metaProperty, it);
         }
     }
-};
 
+    //###########################################################################
+    //                                Qml support
+    //###########################################################################
+
+    template<typename T>
+    static void qmllistpropertyAppend(QQmlListProperty<T> *, T *) {
+        Q_ASSERT_X(false, "", "Access to append functionality of protobuf lists is restricted");
+    }
+
+    template<typename T>
+    static int qmllistpropertyCount(QQmlListProperty<T> *p) {
+        return reinterpret_cast<QList<QSharedPointer<T>> *>(p->data)->count();
+    }
+
+    template<typename T>
+    static T * qmllistpropertyAt(QQmlListProperty<T> *p, int index) {
+        return reinterpret_cast<QList<QSharedPointer<T>> *>(p->data)->at(index).data();
+    }
+
+    template<typename T>
+    static void qmllistpropertyReset(QQmlListProperty<T> *){
+        Q_ASSERT_X(false, "", "Access to reset functionality of protobuf lists is restricted");
+    }
+
+    template<typename T>
+    static QQmlListProperty<T> constructQmlListProperty(QObject* p, QList<QSharedPointer<T>> *data)
+    {
+        return QQmlListProperty<T>(p, data, qmllistpropertyAppend<T>, qmllistpropertyCount<T>,
+                                   qmllistpropertyAt<T>, qmllistpropertyReset<T>);
+    }
+};
 
+//###########################################################################
+//                             Common functions
+//###########################################################################
 /*  Header byte
  *  bits    | 7  6  5  4  3 | 2  1  0
  *  -----------------------------------

+ 3 - 3
tests/CMakeLists.txt

@@ -202,7 +202,7 @@ if(WIN32)
 
     #Set  path to GTest include directory
     include_directories(${GTEST_INCLUDE_PATHS} "/")
-    find_package(Qt5 COMPONENTS Core Quick Network REQUIRED)
+    find_package(Qt5 COMPONENTS Core Quick Network Qml REQUIRED)
 endif()
 
 file(GLOB SOURCES main.cpp
@@ -217,8 +217,8 @@ set(testtarget "qtprotobuf_test")
 add_executable(${testtarget} ${SOURCES} ${GENERATED_SOURCES})
 
 if(WIN32)
-    target_link_libraries(${testtarget} qtgrpc qtprotobufsupport "${GTEST_BOTH_LIBRARIES}/gmock_main.lib" "${GTEST_BOTH_LIBRARIES}/gmock.lib" Qt5::Quick Qt5::Core Qt5::Network)
+    target_link_libraries(${testtarget} qtgrpc qtprotobufsupport "${GTEST_BOTH_LIBRARIES}/gmock_main.lib" "${GTEST_BOTH_LIBRARIES}/gmock.lib" Qt5::Quick Qt5::Core Qt5::Qml Qt5::Network)
 elseif(UNIX)
-    target_link_libraries(${testtarget} gtest qtgrpc)
+    target_link_libraries(${testtarget} gtest qtgrpc qtprotobufsupport)
 endif()
 add_dependencies(${testtarget} ${testgeneration})