aboutsummaryrefslogtreecommitdiffstats
path: root/sources/pyside6/libpysideqml/pysideqmlattached.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sources/pyside6/libpysideqml/pysideqmlattached.cpp')
-rw-r--r--sources/pyside6/libpysideqml/pysideqmlattached.cpp216
1 files changed, 216 insertions, 0 deletions
diff --git a/sources/pyside6/libpysideqml/pysideqmlattached.cpp b/sources/pyside6/libpysideqml/pysideqmlattached.cpp
new file mode 100644
index 000000000..d484257e2
--- /dev/null
+++ b/sources/pyside6/libpysideqml/pysideqmlattached.cpp
@@ -0,0 +1,216 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "pysideqmlattached.h"
+#include "pysideqmlattached_p.h"
+#include "pysideqmltypeinfo_p.h"
+#include "pysideqmlregistertype_p.h"
+
+#include <signalmanager.h>
+#include <pyside_p.h>
+#include <pysideclassdecorator_p.h>
+
+#include <shiboken.h>
+#include <signature.h>
+#include <sbkstring.h>
+
+#include <QtQml/qqml.h>
+
+#include <algorithm>
+
+// The QmlAttached decorator modifies QmlElement to register an attached property
+// type. Due to the (reverse) execution order of decorators, it needs to follow
+// QmlElement.
+class PySideQmlAttachedPrivate : public PySide::ClassDecorator::TypeDecoratorPrivate
+{
+public:
+ PyObject *tp_call(PyObject *self, PyObject *args, PyObject * /* kw */) override;
+ const char *name() const override;
+};
+
+// The call operator is passed the class type and registers the type
+// in QmlTypeInfo.
+PyObject *PySideQmlAttachedPrivate::tp_call(PyObject *self, PyObject *args, PyObject * /* kw */)
+{
+ PyObject *klass = tp_call_check(args, CheckMode::WrappedType);
+ if (klass == nullptr)
+ return nullptr;
+
+ auto *data = DecoratorPrivate::get<PySideQmlAttachedPrivate>(self);
+ PySide::Qml::ensureQmlTypeInfo(klass)->attachedType = data->type();
+
+ Py_INCREF(klass);
+ return klass;
+}
+
+const char *PySideQmlAttachedPrivate::name() const
+{
+ return "QmlAttached";
+}
+
+extern "C" {
+
+static PyTypeObject *createPySideQmlAttachedType(void)
+{
+ auto typeSlots =
+ PySide::ClassDecorator::Methods<PySideQmlAttachedPrivate>::typeSlots();
+
+ PyType_Spec PySideQmlAttachedType_spec = {
+ "2:PySide6.QtCore.qmlAttached",
+ sizeof(PySideClassDecorator),
+ 0,
+ Py_TPFLAGS_DEFAULT,
+ typeSlots.data()
+ };
+ return SbkType_FromSpec(&PySideQmlAttachedType_spec);
+}
+
+PyTypeObject *PySideQmlAttached_TypeF(void)
+{
+ static auto *type = createPySideQmlAttachedType();
+ return type;
+}
+
+} // extern "C"
+
+static const char *qmlAttached_SignatureStrings[] = {
+ "PySide6.QtQml.QmlAttached(self,type:type)",
+ nullptr // Sentinel
+};
+
+namespace PySide::Qml {
+
+static QObject *attachedFactoryHelper(PyTypeObject *attachingType, QObject *o)
+{
+ // Call static qmlAttachedProperties() on type. If there is an error
+ // and nullptr is returned, a crash occurs. So, errors should at least be
+ // printed.
+
+ Shiboken::GilState gilState;
+ Shiboken::Conversions::SpecificConverter converter("QObject");
+ Q_ASSERT(converter);
+
+ static const char methodName[] = "qmlAttachedProperties";
+ static PyObject *const pyMethodName = Shiboken::String::createStaticString(methodName);
+ PyObject *attachingTypeObj = reinterpret_cast<PyObject *>(attachingType);
+ Shiboken::AutoDecRef pyResult(PyObject_CallMethodObjArgs(attachingTypeObj, pyMethodName,
+ attachingTypeObj /* self */,
+ converter.toPython(&o),
+ nullptr));
+ if (pyResult.isNull() || PyErr_Occurred()) {
+ PyErr_Print();
+ return nullptr;
+ }
+
+ if (PyType_IsSubtype(pyResult->ob_type, qObjectType()) == 0) {
+ qWarning("QmlAttached: Attached objects must inherit QObject, got %s.",
+ pyResult->ob_type->tp_name);
+ return nullptr;
+ }
+
+ QObject *result = nullptr;
+ converter.toCpp(pyResult.object(), &result);
+ return result;
+}
+
+// Since the required attached factory signature does not have a void *user
+// parameter to store the attaching type, we employ a template trick, storing
+// the attaching types in an array and create non-type-template (int) functions
+// taking the array index as template parameter.
+// We initialize the attachedFactories array with factory functions
+// accessing the attachingTypes[N] using template metaprogramming.
+
+enum { MAX_ATTACHING_TYPES = 50};
+
+using AttachedFactory = QObject *(*)(QObject *);
+
+static int nextAttachingType = 0;
+static PyTypeObject *attachingTypes[MAX_ATTACHING_TYPES];
+static AttachedFactory attachedFactories[MAX_ATTACHING_TYPES];
+
+template <int N>
+static QObject *attachedFactory(QObject *o)
+{
+ return attachedFactoryHelper(attachingTypes[N], o);
+}
+
+template<int N>
+struct AttachedFactoryInitializerBase
+{
+};
+
+template<int N>
+struct AttachedFactoryInitializer : AttachedFactoryInitializerBase<N>
+{
+ static void init()
+ {
+ attachedFactories[N] = attachedFactory<N>;
+ AttachedFactoryInitializer<N-1>::init();
+ }
+};
+
+template<>
+struct AttachedFactoryInitializer<0> : AttachedFactoryInitializerBase<0>
+{
+ static void init()
+ {
+ attachedFactories[0] = attachedFactory<0>;
+ }
+};
+
+void initQmlAttached(PyObject *module)
+{
+ std::fill(attachingTypes, attachingTypes + MAX_ATTACHING_TYPES, nullptr);
+ AttachedFactoryInitializer<MAX_ATTACHING_TYPES - 1>::init();
+
+ if (InitSignatureStrings(PySideQmlAttached_TypeF(), qmlAttached_SignatureStrings) < 0)
+ return;
+
+ Py_INCREF(PySideQmlAttached_TypeF());
+ PyModule_AddObject(module, "QmlAttached",
+ reinterpret_cast<PyObject *>(PySideQmlAttached_TypeF()));
+}
+
+PySide::Qml::QmlExtensionInfo qmlAttachedInfo(PyTypeObject *t,
+ const std::shared_ptr<QmlTypeInfo> &info)
+{
+ PySide::Qml::QmlExtensionInfo result{nullptr, nullptr};
+ if (!info || info->attachedType == nullptr)
+ return result;
+
+ auto *name = reinterpret_cast<PyTypeObject *>(t)->tp_name;
+ if (nextAttachingType >= MAX_ATTACHING_TYPES) {
+ qWarning("Unable to initialize attached type \"%s\": "
+ "The limit %d of attached types has been reached.",
+ name, MAX_ATTACHING_TYPES);
+ return result;
+ }
+
+ result.metaObject = PySide::retrieveMetaObject(info->attachedType);
+ if (result.metaObject == nullptr) {
+ qWarning("Unable to retrieve meta object for %s", name);
+ return result;
+ }
+
+ attachingTypes[nextAttachingType] = t;
+ result.factory = attachedFactories[nextAttachingType];
+ ++nextAttachingType;
+
+ return result;
+}
+
+QObject *qmlAttachedPropertiesObject(PyObject *typeObject, QObject *obj, bool create)
+{
+ auto *type = reinterpret_cast<PyTypeObject *>(typeObject);
+ auto *end = attachingTypes + nextAttachingType;
+ auto *typePtr = std::find(attachingTypes, end, type);
+ if (typePtr == end) {
+ qWarning("%s: Attaching type \"%s\" not found.", __FUNCTION__, type->tp_name);
+ return nullptr;
+ }
+
+ auto func = attachedFactories[std::uintptr_t(typePtr - attachingTypes)];
+ return ::qmlAttachedPropertiesObject(obj, func, create);
+}
+
+} // namespace PySide::Qml