File indexing completed on 2025-01-05 04:26:49

0001 /*
0002  * Replacement fot QT Bindings that were removed from QT5
0003  * Copyright (C) 2020  Pedro de Carvalho Gomes <pedrogomes81@gmail.com>
0004  *
0005  * This program is free software: you can redistribute it and/or modify
0006  * it under the terms of the GNU General Public License as published by
0007  * the Free Software Foundation, either version 3 of the License, or
0008  * (at your option) any later version.
0009  *
0010  * This program is distributed in the hope that it will be useful,
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013  * GNU General Public License for more details.
0014  *
0015  * You should have received a copy of the GNU General Public License
0016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0017  */
0018 
0019 #ifndef QTBINDING_H
0020 #define QTBINDING_H
0021 
0022 #include <QJSEngine>
0023 #include <QMetaMethod>
0024 #include <QRegularExpression>
0025 #include <QSet>
0026 
0027 namespace QtBindings {
0028     /** Base template for QT bindings
0029      * IMPORTANT: for methods do be correctly exported, static methods must be
0030      * annotated with Q_INVOKABLE, and object methods must be declared as public slots
0031      * */
0032     template<class T> class Base
0033     {
0034     public:
0035         /**
0036          * Export type to the JS engine
0037          * @param engine
0038          * @param parentObject
0039          */
0040         static void installJSType( QJSEngine *engine )
0041         {
0042             if (!engine) return;
0043 
0044             // Install type only once along program execution
0045             if ( !QMetaType::isRegistered( QMetaType::type( typeName ) ) ) {
0046                 qRegisterMetaType<T>( typeName );
0047                 qRegisterMetaType<T>( typeNameRef );
0048                 qRegisterMetaType<T*>( typeNamePtr );
0049                 qRegisterMetaType<T>(  "const " + typeName );
0050                 qRegisterMetaType<T>(  "const " + typeNameRef );
0051                 qRegisterMetaType<T*>( "const " + typeNamePtr );
0052 
0053                 /* Converter allows passing parameters to C++ via value and const-ref */
0054                 bool conv = QMetaType::registerConverter<QObject*,T>( [] (QObject* qObjPtr) {
0055                     const T* dataPtr = qobject_cast<T*>( qObjPtr );
0056                     return (dataPtr == nullptr) ? T() : T( *dataPtr ) ;
0057                 });
0058                 Q_ASSERT(conv);
0059             }
0060 
0061             QJSValue scopeObj = engine->globalObject();
0062 
0063             // Export type to each JS engine only once - Test if not defined yet
0064             if ( scopeObj.property( qTypeName ).isUndefined() ) {
0065                 scopeObj.setProperty( qTypeName, engine->newQMetaObject<T>());
0066 
0067                 QJSValue classObj = engine->newQObject( new T() );
0068                 // Add static methods to the associated JS object
0069                 for ( const QString& methodName : getStaticMethods() ) {
0070                     scopeObj.property( qTypeName )
0071                         .setProperty( methodName, classObj.property( methodName ) );
0072                 }
0073             }
0074         }
0075 
0076         static QSet<QString> getStaticMethods()
0077         {
0078             const QMetaObject classObj = T::staticMetaObject;
0079             QSet<QString> methodList;
0080             for (int i = classObj.methodOffset(); i < classObj.methodCount(); i++) {
0081                 /* TODO - non-static methods are filtered out by being slots since
0082                  * tags do not work for Q_INVOKABLE. Change this to tags if fixed */
0083                 if (classObj.method(i).methodType() == QMetaMethod::Method)
0084                     methodList << classObj.method(i).name();
0085             }
0086             return methodList;
0087         }
0088 
0089     protected:
0090         static const QByteArray typeName;
0091         static const QByteArray typeNamePtr;
0092         static const QByteArray typeNameRef;
0093         static const QString qTypeName;
0094 
0095         /**
0096          *  Mirrors a QObject tree into a JS object
0097          *
0098          * @param rootObject top object in the tree
0099          * @param  engine javascript engine object that the tree belongs to
0100          * @return a Javascript object with the top QObject of the tree
0101          */
0102         QJSValue mirrorObjectTree( QObject* rootObject, QJSEngine* engine ) {
0103 
0104             QJSValue rootJSValue = engine->newQObject( rootObject );
0105 
0106             for (QObject* childObject : rootObject->findChildren<QObject*>( QString(), Qt::FindDirectChildrenOnly ) ) {
0107                 QJSValue childJSValue = mirrorObjectTree( childObject, engine ) ;
0108                 rootJSValue.setProperty( childObject->objectName(), childJSValue );
0109             }
0110             return rootJSValue;
0111         }
0112     };
0113 
0114     // Remove namespace
0115     template<class T> const QByteArray Base<T>::typeName = QString( T::staticMetaObject.className() )
0116         .remove(  QRegularExpression( "^.*::" ) ).toLatin1();
0117     template<class T> const QByteArray Base<T>::typeNamePtr = typeName + "*";
0118     template<class T> const QByteArray Base<T>::typeNameRef = typeName + "&";
0119     template<class T> const QString Base<T>::qTypeName( "Q" + typeName );
0120 }
0121 
0122 
0123 #endif //QTBINDING_H