diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fa4e18..eb9ede5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.16.0) project(JlQML) -set(JlQML_VERSION 0.10.0) +set(JlQML_VERSION 0.10.1) message(STATUS "Project version: v${JlQML_VERSION}") set(CMAKE_MACOSX_RPATH 1) @@ -76,7 +76,9 @@ qt6_add_qml_module(jlqml opengl_viewport.hpp opengl_viewport.cpp jlqml.hpp - wrap_qml.cpp) + wrap_qml.cpp + wrap_qml_part_a.cpp + wrap_qml_part_b.cpp) set_property(TARGET jlqml PROPERTY VERSION ${JlQML_VERSION}) if(WIN32) diff --git a/wrap_qml.cpp b/wrap_qml.cpp index fddafd2..a4d45b2 100644 --- a/wrap_qml.cpp +++ b/wrap_qml.cpp @@ -1,290 +1,4 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "application_manager.hpp" -#include "foreign_thread_manager.hpp" -#include "julia_api.hpp" -#include "julia_canvas.hpp" -#include "julia_display.hpp" -#include "julia_imageprovider.hpp" -#include "julia_itemmodel.hpp" -#include "julia_painteditem.hpp" -#include "julia_property_map.hpp" -#include "julia_signals.hpp" -#include "opengl_viewport.hpp" -#include "makie_viewport.hpp" - -#include "jlqml.hpp" - -#include "jlcxx/stl.hpp" - -namespace jlcxx -{ - -template<> struct SuperType { using type = QQmlEngine; }; -template<> struct SuperType { using type = QObject; }; -template<> struct SuperType { using type = QObject; }; -template<> struct SuperType { using type = QObject; }; -template<> struct SuperType { using type = QQmlPropertyMap; }; -template<> struct SuperType { using type = QQuickWindow; }; -template<> struct SuperType { using type = QObject; }; -template<> struct SuperType { using type = QQuickItem; }; -template<> struct SuperType { using type = QObject; }; -template<> struct SuperType { using type = QAbstractItemModel; }; -template<> struct SuperType { using type = QObject; }; -template<> struct SuperType { using type = QObject; }; -template<> struct SuperType { using type = QWindow; }; -template<> struct SuperType { using type = QAbstractTableModel; }; -template<> struct SuperType { using type = QPaintDevice; }; -template<> struct SuperType { using type = QPaintDevice; }; -template<> struct SuperType { using type = QObject; }; -template<> struct SuperType { using type = QQmlImageProviderBase; }; -template<> struct SuperType { using type = QQuickImageProvider; }; - -} - -namespace qmlwrap -{ - -QVariantAny::QVariantAny(jl_value_t* v) : value(v) -{ - assert(v != nullptr); - jlcxx::protect_from_gc(value); -} -QVariantAny::~QVariantAny() -{ - jlcxx::unprotect_from_gc(value); -} - -using qvariant_types = jlcxx::ParameterList, JuliaDisplay*, JuliaCanvas*, JuliaPropertyMap*, QObject*>; - -inline std::map g_variant_type_map; - -jl_datatype_t* julia_type_from_qt_id(int id) -{ - if(qmlwrap::g_variant_type_map.count(id) == 0) - { - qWarning() << "invalid variant type " << QMetaType(id).name(); - } - assert(qmlwrap::g_variant_type_map.count(id) == 1); - return qmlwrap::g_variant_type_map[id]; -} - -jl_datatype_t* julia_variant_type(const QVariant& v) -{ - if(!v.isValid()) - { - static jl_datatype_t* nothing_type = (jl_datatype_t*)jlcxx::julia_type("Nothing"); - return nothing_type; - } - const int usertype = v.userType(); - if(usertype == qMetaTypeId()) - { - return julia_variant_type(v.value().toVariant()); - } - // Convert to some known, specific type if necessary - if(v.canConvert()) - { - QObject* obj = v.value(); - if(obj != nullptr) - { - if(qobject_cast(obj) != nullptr) - { - return jlcxx::julia_base_type(); - } - if(qobject_cast(obj) != nullptr) - { - return jlcxx::julia_base_type(); - } - if(dynamic_cast(obj) != nullptr) - { - return (jl_datatype_t*)jlcxx::julia_type("JuliaPropertyMap"); - } - } - } - - return julia_type_from_qt_id(usertype); -} - -template -struct ApplyQVariant -{ - void operator()(jlcxx::TypeWrapper& wrapper) - { - g_variant_type_map[qMetaTypeId()] = jlcxx::julia_base_type(); - wrapper.module().method("value", [] (jlcxx::SingletonType, const QVariant& v) - { - if(v.userType() == qMetaTypeId()) - { - return v.value().toVariant().value(); - } - return v.value(); - }); - wrapper.module().method("setValue", [] (jlcxx::SingletonType, QVariant& v, T val) - { - v.setValue(val); - }); - wrapper.module().method("QVariant", [] (jlcxx::SingletonType, T val) - { - return QVariant::fromValue(val); - }); - } -}; - -template<> -struct ApplyQVariant -{ - void operator()(jlcxx::TypeWrapper& wrapper) - { - g_variant_type_map[qMetaTypeId()] = jl_any_type; - wrapper.module().method("value", [] (jlcxx::SingletonType, const QVariant& v) - { - if(v.userType() == qMetaTypeId()) - { - return v.value().toVariant().value()->value; - } - return v.value()->value; - }); - wrapper.module().method("setValue", [] (jlcxx::SingletonType, QVariant& v, jl_value_t* val) - { - v.setValue(std::make_shared(val)); - }); - wrapper.module().method("QVariant", [] (jlcxx::SingletonType, jl_value_t* val) - { - return QVariant::fromValue(std::make_shared(val)); - }); - } -}; - -// These are created in QML when passing a JuliaPropertyMap back to Julia by calling a Julia function from QML -template<> -struct ApplyQVariant -{ - void operator()(jlcxx::TypeWrapper& wrapper) - { - wrapper.module().method("getpropertymap", [] (QVariant& v) { return dynamic_cast(v.value())->julia_value(); }); - } -}; - -struct WrapQVariant -{ - WrapQVariant(jlcxx::TypeWrapper& w) : m_wrapper(w) - { - } - - template - void apply() - { - ApplyQVariant()(m_wrapper); - } - - jlcxx::TypeWrapper& m_wrapper; -}; - -struct WrapQList -{ - template - void operator()(TypeWrapperT&& wrapped) - { - using WrappedT = typename TypeWrapperT::type; - wrapped.method("cppsize", &WrappedT::size); - wrapped.method("cppgetindex", [] (const WrappedT& list, const int i) -> typename WrappedT::const_reference { return list[i]; }); - wrapped.method("cppsetindex!", [] (WrappedT& list, const typename WrappedT::value_type& v, const int i) { list[i] = v; }); - wrapped.method("push_back", static_cast(&WrappedT::push_back)); - wrapped.method("clear", &WrappedT::clear); - wrapped.method("removeAt", &WrappedT::removeAt); - } -}; - -// Wrap a ContainerT::iterator in a templated struct so it can be added as a parametric type in Julia -template typename ContainerT, typename KeyT, typename ValueT> -struct QtIteratorWrapper -{ - using iterator_type = typename ContainerT::iterator; - using value_type = ValueT; - using key_type = KeyT; - iterator_type value; -}; - -template struct QHashIteratorWrapper : QtIteratorWrapper {}; -template struct QMapIteratorWrapper : QtIteratorWrapper {}; - -template -void validate_iterator(T it) -{ - using IteratorT = typename T::iterator_type; - if(it.value == IteratorT()) - { - throw std::runtime_error("Invalid iterator"); - } -} - -struct WrapQtIterator -{ - template - void operator()(TypeWrapperT&& wrapped) - { - using WrappedT = typename TypeWrapperT::type; - using KeyT = typename WrappedT::key_type; - using ValueT = typename WrappedT::value_type; - - wrapped.method("iteratornext", [] (WrappedT it) -> WrappedT { ++(it.value); return it; }); - wrapped.method("iteratorkey", [] (WrappedT it) -> KeyT { validate_iterator(it); return it.value.key();} ); - wrapped.method("iteratorvalue", [] (WrappedT it) -> ValueT& { validate_iterator(it); return it.value.value(); } ); - wrapped.method("iteratorisequal", [] (WrappedT it1, WrappedT it2) -> bool { return it1.value == it2.value; } ); - } -}; - -template typename IteratorWrapperT> -struct WrapQtAssociativeContainer -{ - template - void operator()(TypeWrapperT&& wrapped) - { - using WrappedT = typename TypeWrapperT::type; - using ValueT = typename WrappedT::mapped_type; - using KeyT = typename WrappedT::key_type; - - wrapped.method("cppsize", &WrappedT::size); - wrapped.method("cppgetindex", [] (WrappedT& hash, const KeyT& k) -> ValueT& { return hash[k]; }); - wrapped.method("cppsetindex!", [] (WrappedT& hash, const ValueT& v, const KeyT& k) { hash[k] = v; }); - wrapped.method("insert", [] (WrappedT& hash, const KeyT& k, const ValueT& v) { hash.insert(k,v); }); - wrapped.method("clear", &WrappedT::clear); - wrapped.method("remove", [] (WrappedT& hash, const KeyT& k) -> bool { return hash.remove(k); }); - wrapped.method("empty", &WrappedT::empty); - wrapped.method("iteratorbegin", [] (WrappedT& hash) { return IteratorWrapperT{hash.begin()}; }); - wrapped.method("iteratorend", [] (WrappedT& hash) { return IteratorWrapperT{hash.end()}; }); - wrapped.method("keys", [] (const WrappedT& hash) { return hash.keys(); }); - wrapped.method("values", &WrappedT::values); - wrapped.method("contains", [] (WrappedT& hash, const KeyT& k) -> bool { return hash.contains(k); }); - } -}; - -struct WrapImageResult -{ - template - void operator()(TypeWrapperT&& wrapped) - { - using WrappedT = typename TypeWrapperT::type; - using image_type = typename WrappedT::image_type; - wrapped.template constructor(); - } -}; - -} +#include "wrap_qml.hpp" JLCXX_MODULE define_julia_module(jlcxx::Module& qml_module) { @@ -560,472 +274,6 @@ JLCXX_MODULE define_julia_module(jlcxx::Module& qml_module) }) ); - qml_module.add_type("QObject") - .method("deleteLater", &QObject::deleteLater); - qml_module.method("connect_destroyed_signal", [] (QObject& obj, jl_value_t* jl_f) - { - QObject::connect(&obj, &QObject::destroyed, [jl_f](QObject* o) - { - static JuliaFunction f(jl_f); - qmlwrap::GCGuard gc_guard; - f(o); - }); - }); - - qml_module.add_type("QSize") - .constructor() - .method("width", &QSize::width) - .method("height", &QSize::height); - - qml_module.add_type("QCoreApplication", julia_base_type()); - qml_module.add_type("QGuiApplication", julia_base_type()) - .constructor(); - qml_module.method("quit", [] () { QGuiApplication::instance()->quit(); }); - - qml_module.add_type("QString", julia_type("AbstractString")) - .method("cppsize", &QString::size); - qml_module.method("uint16char", [] (const QString& s, int i) { return static_cast(s[i].unicode()); }); - qml_module.method("fromStdWString", QString::fromStdWString); - qml_module.method("isvalidindex", [] (const QString& s, int i) - { - if(i < 0 || i >= s.size()) - { - return false; - } - QTextBoundaryFinder bf(QTextBoundaryFinder::Grapheme, s); - bf.setPosition(i); - return bf.isAtBoundary(); - }); - - qml_module.method("get_iterate", [] (const QString& s, int i) - { - if(i < 0 || i >= s.size()) - { - return std::make_tuple(uint32_t(0),-1); - } - QTextBoundaryFinder bf(QTextBoundaryFinder::Grapheme, s); - bf.setPosition(i); - if(bf.toNextBoundary() != -1) - { - const int nexti = bf.position(); - if((nexti - i) == 1) - { - return std::make_tuple(uint32_t(s[i].unicode()), nexti); - } - return std::make_tuple(uint32_t(QChar::surrogateToUcs4(s[i],s[i+1])),nexti); - } - return std::make_tuple(uint32_t(0),-1); - }); - - qml_module.add_type("JuliaCanvas"); - - qml_module.add_type("JuliaDisplay", julia_type("AbstractDisplay", "Base")) - .method("load_png", &qmlwrap::JuliaDisplay::load_png) - .method("load_svg", &qmlwrap::JuliaDisplay::load_svg); - - qml_module.add_type("QUrl") - .constructor() - .method("toString", [] (const QUrl& url) { return url.toString(); }) - .method("toLocalFile", &QUrl::toLocalFile); - qml_module.method("QUrlFromLocalFile", QUrl::fromLocalFile); - - auto qvar_type = qml_module.add_type("QVariant"); - qvar_type.method("toString", &QVariant::toString); - - qml_module.add_type("QByteArray").constructor() - .method("to_string", &QByteArray::toStdString); - - qml_module.add_type("QByteArrayView"); - - qml_module.add_type>>("QList", julia_type("AbstractVector")) - .apply, QList, QList, QList, QList>(qmlwrap::WrapQList()); - - // QMap (= QVariantMap for the given type) - qml_module.add_type,TypeVar<2>>>("QMapIterator") - .apply>(qmlwrap::WrapQtIterator()); - qml_module.add_type,TypeVar<2>>>("QMap", julia_type("AbstractDict")) - .apply>(qmlwrap::WrapQtAssociativeContainer()); - - // QHash - qml_module.add_type,TypeVar<2>>>("QHashIterator") - .apply>(qmlwrap::WrapQtIterator()); - qml_module.add_type,TypeVar<2>>>("QHash", julia_type("AbstractDict")) - .apply>(qmlwrap::WrapQtAssociativeContainer()); - - qml_module.add_type("QQmlPropertyMap", julia_base_type()) - .constructor(jlcxx::finalize_policy::no) - .method("clear", &QQmlPropertyMap::clear) - .method("contains", &QQmlPropertyMap::contains) - .method("insert", static_cast(&QQmlPropertyMap::insert)) - .method("size", &QQmlPropertyMap::size) - .method("value", &QQmlPropertyMap::value) - .method("connect_value_changed", [] (QQmlPropertyMap& propmap, jl_value_t* julia_property_map, jl_value_t* callback) - { - auto conn = QObject::connect(&propmap, &QQmlPropertyMap::valueChanged, [=](const QString& key, const QVariant& newvalue) - { - const jlcxx::JuliaFunction on_value_changed(callback); - jl_value_t* julia_propmap = julia_property_map; - qmlwrap::GCGuard gc_guard; - on_value_changed(julia_propmap, key, newvalue); - }); - }); - qml_module.add_type("_JuliaPropertyMap", julia_base_type()) - .method("julia_value", &qmlwrap::JuliaPropertyMap::julia_value) - .method("set_julia_value", &qmlwrap::JuliaPropertyMap::set_julia_value); - - jlcxx::for_each_parameter_type(qmlwrap::WrapQVariant(qvar_type)); - qml_module.method("type", qmlwrap::julia_variant_type); - - qml_module.method("make_qvariant_map", [] () - { - QVariantMap m; - m[QString("test")] = QVariant::fromValue(5); - return QVariant::fromValue(m); - }); - - auto qqmlcontext_wrapper = qml_module.add_type("QQmlContext", julia_base_type()) - .constructor() - .constructor() - .method("context_property", &QQmlContext::contextProperty) - .method("set_context_object", &QQmlContext::setContextObject) - .method("_set_context_property", static_cast(&QQmlContext::setContextProperty)) - .method("_set_context_property", static_cast(&QQmlContext::setContextProperty)) - .method("context_object", &QQmlContext::contextObject); - - qml_module.add_type("QQmlImageProviderBase", julia_base_type()); - - qml_module.add_type("QQmlEngine", julia_base_type()) - .method("addImageProvider", &QQmlEngine::addImageProvider) - .method("clearComponentCache", &QQmlEngine::clearComponentCache) - .method("clearSingletons", &QQmlEngine::clearSingletons) - .method("root_context", &QQmlEngine::rootContext) - .method("quit", &QQmlEngine::quit); - - qqmlcontext_wrapper.method("engine", &QQmlContext::engine); - - qml_module.add_type("QQmlApplicationEngine", julia_base_type()) - .constructor() // Construct with path to QML - .method("rootObjects", &QQmlApplicationEngine::rootObjects) - .method("load_into_engine", [] (QQmlApplicationEngine* e, const QString& qmlpath) - { - bool success = false; - auto conn = QObject::connect(e, &QQmlApplicationEngine::objectCreated, [&] (QObject* obj, const QUrl& url) { success = (obj != nullptr); }); - e->load(qmlpath); - QObject::disconnect(conn); - if(!success) - { - e->exit(1); - } - return success; - }); - - qml_module.method("qt_prefix_path", []() { return QLibraryInfo::path(QLibraryInfo::PrefixPath); }); - - auto qquickitem_type = qml_module.add_type("QQuickItem", julia_base_type()); - - qml_module.add_type("QWindow", julia_base_type()) - .method("destroy", &QWindow::destroy); - qml_module.add_type("QQuickWindow", julia_base_type()) - .method("content_item", &QQuickWindow::contentItem); - - qquickitem_type.method("window", &QQuickItem::window); - - qml_module.method("effectiveDevicePixelRatio", [] (QQuickWindow& w) - { - return w.effectiveDevicePixelRatio(); - }); - - qml_module.add_type("QQuickView", julia_base_type()) - .method("set_source", &QQuickView::setSource) - .method("show", &QQuickView::show) // not exported: conflicts with Base.show - .method("engine", &QQuickView::engine) - .method("root_object", &QQuickView::rootObject); - - qml_module.add_type("JuliaPaintedItem", julia_base_type()); - - qml_module.add_type("QQmlComponent", julia_base_type()) - .method("set_data", &QQmlComponent::setData); - // We manually add this constructor, since no finalizer will be added here. The component is destroyed together with the engine. - qml_module.method("QQmlComponent", [] (QQmlEngine* e) { return new QQmlComponent(e,e); }); - qml_module.method("create", [](QQmlComponent* comp, QQmlContext* context) - { - if(!comp->isReady()) - { - qWarning() << "QQmlComponent is not ready, aborting create. Errors were: " << comp->errors(); - return; - } - - QObject* obj = comp->create(context); - if(context != nullptr) - { - obj->setParent(context); // setting this makes sure the new object gets deleted - } - }); - -#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) - qml_module.method("qputenv", [] (const char* varName, QByteArray value) { qputenv(varName, value); }); -#else - qml_module.method("qputenv", qputenv); -#endif - qml_module.method("qgetenv", qgetenv); - qml_module.method("qunsetenv", qunsetenv); - - // App manager functions - qml_module.add_type("ApplicationManager"); - qml_module.method("init_qmlapplicationengine", []() { return qmlwrap::ApplicationManager::instance().init_qmlapplicationengine(); }); - qml_module.method("init_qmlengine", []() { return qmlwrap::ApplicationManager::instance().init_qmlengine(); }); - qml_module.method("get_qmlengine", []() { return qmlwrap::ApplicationManager::instance().get_qmlengine(); }); - qml_module.method("init_qquickview", []() { return qmlwrap::ApplicationManager::instance().init_qquickview(); }); - qml_module.method("cleanup", []() { qmlwrap::ApplicationManager::instance().cleanup(); }); - qml_module.method("qmlcontext", []() { return qmlwrap::ApplicationManager::instance().root_context(); }); - qml_module.method("app_exec", []() { qmlwrap::ApplicationManager::instance().exec(); }); - qml_module.method("process_events", qmlwrap::ApplicationManager::process_events); - qml_module.method("add_import_path", [](std::string path) { qmlwrap::ApplicationManager::instance().add_import_path(path); }); - qml_module.method("queue_process_eventloop_updates", []() { qmlwrap::ApplicationManager::instance().queue_process_eventloop_updates(); }); - - qml_module.method("yield", []() { qmlwrap::ForeignThreadManager::instance().yield(); }); - - qml_module.add_type("QTimer", julia_base_type()) - .constructor() - .method("setInterval", static_cast(&QTimer::setInterval)) - .method("start", [] (QTimer& t) { t.start(); } ) - .method("stop", &QTimer::stop) - .method("callOnTimeout", [] (QTimer& t, jl_value_t* julia_function) - { - JuliaFunction f(julia_function); - t.callOnTimeout([f] () { f(); }); - }); - - // Emit signals helper - qml_module.method("emit", [](const char *signal_name, const QVariantList& args) { - using namespace qmlwrap; - JuliaSignals *julia_signals = ApplicationManager::instance().julia_api()->juliaSignals(); - if (julia_signals == nullptr) - { - throw std::runtime_error("No signals available"); - } - julia_signals->emit_signal(signal_name, args); - }); - - // Function to register a function - qml_module.method("qmlfunction", [](const QString &name, jl_value_t *f) { - qmlwrap::ApplicationManager::instance().julia_api()->register_function(name, f); - }); - - qml_module.add_type("QPaintDevice") - .method("width", &QPaintDevice::width) - .method("height", &QPaintDevice::height) - .method("logicalDpiX", &QPaintDevice::logicalDpiX) - .method("logicalDpiY", &QPaintDevice::logicalDpiY); - qml_module.add_type("QPainter") - .method("device", &QPainter::device); - - qml_module.add_type("QAbstractItemModel", julia_base_type()); - qml_module.add_type("QAbstractTableModel", julia_base_type()); - qml_module.add_type("JuliaItemModel", julia_base_type()) - .method("emit_data_changed", &qmlwrap::JuliaItemModel::emit_data_changed) - .method("emit_header_data_changed", &qmlwrap::JuliaItemModel::emit_header_data_changed) - .method("begin_reset_model", &qmlwrap::JuliaItemModel::begin_reset_model) - .method("end_reset_model", &qmlwrap::JuliaItemModel::end_reset_model) - .method("begin_insert_rows", &qmlwrap::JuliaItemModel::begin_insert_rows) - .method("end_insert_rows", &qmlwrap::JuliaItemModel::end_insert_rows) - .method("begin_move_rows", &qmlwrap::JuliaItemModel::begin_move_rows) - .method("end_move_rows", &qmlwrap::JuliaItemModel::end_move_rows) - .method("begin_remove_rows", &qmlwrap::JuliaItemModel::begin_remove_rows) - .method("end_remove_rows", &qmlwrap::JuliaItemModel::end_remove_rows) - .method("begin_insert_columns", &qmlwrap::JuliaItemModel::begin_insert_columns) - .method("end_insert_columns", &qmlwrap::JuliaItemModel::end_insert_columns) - .method("begin_move_columns", &qmlwrap::JuliaItemModel::begin_move_columns) - .method("end_move_columns", &qmlwrap::JuliaItemModel::end_move_columns) - .method("begin_remove_columns", &qmlwrap::JuliaItemModel::begin_remove_columns) - .method("end_remove_columns", &qmlwrap::JuliaItemModel::end_remove_columns) - .method("default_role_names", &qmlwrap::JuliaItemModel::default_role_names) - .method("get_julia_data", &qmlwrap::JuliaItemModel::get_julia_data); - - qml_module.method("new_item_model", [] (jl_value_t* modeldata) { return jlcxx::create(modeldata); }); - - qml_module.set_override_module(jl_base_module); - qml_module.method("getindex", [](const QVariantMap& m, const QString& key) { return m[key]; }); - qml_module.unset_override_module(); - - qml_module.add_type("QOpenGLFramebufferObjectFormat") - .method("internalTextureFormat", &QOpenGLFramebufferObjectFormat::internalTextureFormat) - .method("textureTarget", &QOpenGLFramebufferObjectFormat::textureTarget); - - qml_module.add_type("QOpenGLFramebufferObject") - .method("size", &QOpenGLFramebufferObject::size) - .method("handle", &QOpenGLFramebufferObject::handle) - .method("isValid", &QOpenGLFramebufferObject::isValid) - .method("bind", &QOpenGLFramebufferObject::bind) - .method("release", &QOpenGLFramebufferObject::release) - .method("format", &QOpenGLFramebufferObject::format) - .method("texture", &QOpenGLFramebufferObject::texture) - .method("addColorAttachment", static_cast(&QOpenGLFramebufferObject::addColorAttachment)) - .method("textures", [] (const QOpenGLFramebufferObject& fbo) - { - auto v = fbo.textures(); - return std::vector(v.begin(), v.end()); - }); - - qml_module.method("graphicsApi", QQuickWindow::graphicsApi); - qml_module.method("setGraphicsApi", QQuickWindow::setGraphicsApi); - - qml_module.method("__test_add_double!", [] (double& result, QVariant var) - { - result += var.value(); - }); - - qml_module.method("__test_add_double_ref!", [] (double& result, const QVariant& var) - { - result += var.value(); - }); - - qml_module.method("__test_return_qvariant", [] (double val) - { - static QVariant var; - var.setValue(val); - return var; - }); - - qml_module.method("__test_return_qvariant_ref", [] (double val) -> const QVariant& - { - static QVariant var; - var.setValue(val); - return var; - }); - - qml_module.add_type("QFileSystemWatcher") - .constructor(jlcxx::finalize_policy::no) - .method("addPath", &QFileSystemWatcher::addPath); - qml_module.method("connect_file_changed_signal", [] (QFileSystemWatcher& watcher, jl_value_t* jl_f) - { - QObject::connect(&watcher, &QFileSystemWatcher::fileChanged, [jl_f](const QString& path) - { - static JuliaFunction f(jl_f); - qmlwrap::GCGuard gc_guard; - f(path); - }); - }); - - qml_module.add_enum("Format", - std::vector({ - "Format_Invalid", - "Format_Mono", - "Format_MonoLSB", - "Format_Indexed8", - "Format_RGB32", - "Format_ARGB32", - "Format_ARGB32_Premultiplied", - "Format_RGB16", - "Format_ARGB8565_Premultiplied", - "Format_RGB666", - "Format_ARGB6666_Premultiplied", - "Format_RGB555", - "Format_ARGB8555_Premultiplied", - "Format_RGB888", - "Format_RGB444", - "Format_ARGB4444_Premultiplied", - "Format_RGBX8888", - "Format_RGBA8888", - "Format_RGBA8888_Premultiplied", - "Format_BGR30", - "Format_A2BGR30_Premultiplied", - "Format_RGB30", - "Format_A2RGB30_Premultiplied", - "Format_Alpha8", - "Format_Grayscale8", - "Format_RGBX64", - "Format_RGBA64", - "Format_RGBA64_Premultiplied", - "Format_Grayscale16", - "Format_BGR888", - "Format_RGBX16FPx4", - "Format_RGBA16FPx4", - "Format_RGBA16FPx4_Premultiplied", - "Format_RGBX32FPx4", - "Format_RGBA32FPx4", - "Format_RGBA32FPx4_Premultiplied", - "Format_CMYK8888" - }), - std::vector({ - QImage::Format_Invalid, - QImage::Format_Mono, - QImage::Format_MonoLSB, - QImage::Format_Indexed8, - QImage::Format_RGB32, - QImage::Format_ARGB32, - QImage::Format_ARGB32_Premultiplied, - QImage::Format_RGB16, - QImage::Format_ARGB8565_Premultiplied, - QImage::Format_RGB666, - QImage::Format_ARGB6666_Premultiplied, - QImage::Format_RGB555, - QImage::Format_ARGB8555_Premultiplied, - QImage::Format_RGB888, - QImage::Format_RGB444, - QImage::Format_ARGB4444_Premultiplied, - QImage::Format_RGBX8888, - QImage::Format_RGBA8888, - QImage::Format_RGBA8888_Premultiplied, - QImage::Format_BGR30, - QImage::Format_A2BGR30_Premultiplied, - QImage::Format_RGB30, - QImage::Format_A2RGB30_Premultiplied, - QImage::Format_Alpha8, - QImage::Format_Grayscale8, - QImage::Format_RGBX64, - QImage::Format_RGBA64, - QImage::Format_RGBA64_Premultiplied, - QImage::Format_Grayscale16, - QImage::Format_BGR888, - QImage::Format_RGBX16FPx4, - QImage::Format_RGBA16FPx4, - QImage::Format_RGBA16FPx4_Premultiplied, - QImage::Format_RGBX32FPx4, - QImage::Format_RGBA32FPx4, - QImage::Format_RGBA32FPx4_Premultiplied, - QImage::Format_CMYK8888 - }) - ); - - qml_module.add_type("QColor") - .constructor() - .constructor() - .constructor() - .constructor(); - - qml_module.add_type("QImage", julia_base_type()) - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .constructor() - .method("copy", static_cast(&QImage::copy)) - .method("copy", [] (const QImage& i) { return i.copy(); } ) - .method("height", &QImage::height) - .method("width", &QImage::width) - .method("fill", static_cast(&QImage::fill)); - - qml_module.add_type("QPixmap", julia_base_type()) - .constructor() - .constructor() - .constructor() - .constructor() - .method("fill", &QPixmap::fill); - qml_module.method("fromImage", [] (const QImage& image) { return QPixmap::fromImage(image); } ); - - qml_module.add_type("QQuickImageProvider", julia_base_type()); - qml_module.add_type("JuliaImageProvider", julia_base_type()) - .constructor(jlcxx::finalize_policy::no) // No finalizer because the engine takes ownership - .method("set_callback", &qmlwrap::JuliaImageProvider::set_callback); - - qml_module.add_type>>("ImageResult") - .apply, qmlwrap::ImageResult>(qmlwrap::WrapImageResult()); + wrap_part_a(qml_module); + wrap_part_b(qml_module); } diff --git a/wrap_qml.hpp b/wrap_qml.hpp new file mode 100644 index 0000000..12e8704 --- /dev/null +++ b/wrap_qml.hpp @@ -0,0 +1,298 @@ +#ifndef QML_wrap_qml_H +#define QML_wrap_qml_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "application_manager.hpp" +#include "foreign_thread_manager.hpp" +#include "julia_api.hpp" +#include "julia_canvas.hpp" +#include "julia_display.hpp" +#include "julia_imageprovider.hpp" +#include "julia_itemmodel.hpp" +#include "julia_painteditem.hpp" +#include "julia_property_map.hpp" +#include "julia_signals.hpp" +#include "opengl_viewport.hpp" +#include "makie_viewport.hpp" + +#include "jlqml.hpp" + +#include "jlcxx/stl.hpp" + +namespace jlcxx +{ + +template<> struct SuperType { using type = QQmlEngine; }; +template<> struct SuperType { using type = QObject; }; +template<> struct SuperType { using type = QCoreApplication; }; +template<> struct SuperType { using type = QObject; }; +template<> struct SuperType { using type = QObject; }; +template<> struct SuperType { using type = QObject; }; +template<> struct SuperType { using type = QObject; }; +template<> struct SuperType { using type = QQmlPropertyMap; }; +template<> struct SuperType { using type = QQuickWindow; }; +template<> struct SuperType { using type = QObject; }; +template<> struct SuperType { using type = QQuickItem; }; +template<> struct SuperType { using type = QObject; }; +template<> struct SuperType { using type = QAbstractItemModel; }; +template<> struct SuperType { using type = QObject; }; +template<> struct SuperType { using type = QObject; }; +template<> struct SuperType { using type = QWindow; }; +template<> struct SuperType { using type = QAbstractTableModel; }; +template<> struct SuperType { using type = QPaintDevice; }; +template<> struct SuperType { using type = QPaintDevice; }; +template<> struct SuperType { using type = QObject; }; +template<> struct SuperType { using type = QQmlImageProviderBase; }; +template<> struct SuperType { using type = QQuickImageProvider; }; + +} + +namespace qmlwrap +{ + +inline QVariantAny::QVariantAny(jl_value_t* v) : value(v) +{ + assert(v != nullptr); + jlcxx::protect_from_gc(value); +} +inline QVariantAny::~QVariantAny() +{ + jlcxx::unprotect_from_gc(value); +} + +using qvariant_types = jlcxx::ParameterList, JuliaDisplay*, JuliaCanvas*, JuliaPropertyMap*, QObject*>; + +inline std::map g_variant_type_map; + +inline jl_datatype_t* julia_type_from_qt_id(int id) +{ + if(qmlwrap::g_variant_type_map.count(id) == 0) + { + qWarning() << "invalid variant type " << QMetaType(id).name(); + } + assert(qmlwrap::g_variant_type_map.count(id) == 1); + return qmlwrap::g_variant_type_map[id]; +} + +inline jl_datatype_t* julia_variant_type(const QVariant& v) +{ + if(!v.isValid()) + { + static jl_datatype_t* nothing_type = (jl_datatype_t*)jlcxx::julia_type("Nothing"); + return nothing_type; + } + const int usertype = v.userType(); + if(usertype == qMetaTypeId()) + { + return julia_variant_type(v.value().toVariant()); + } + // Convert to some known, specific type if necessary + if(v.canConvert()) + { + QObject* obj = v.value(); + if(obj != nullptr) + { + if(qobject_cast(obj) != nullptr) + { + return jlcxx::julia_base_type(); + } + if(qobject_cast(obj) != nullptr) + { + return jlcxx::julia_base_type(); + } + if(dynamic_cast(obj) != nullptr) + { + return (jl_datatype_t*)jlcxx::julia_type("JuliaPropertyMap"); + } + } + } + + return julia_type_from_qt_id(usertype); +} + +template +struct ApplyQVariant +{ + void operator()(jlcxx::TypeWrapper& wrapper) + { + g_variant_type_map[qMetaTypeId()] = jlcxx::julia_base_type(); + wrapper.module().method("value", [] (jlcxx::SingletonType, const QVariant& v) + { + if(v.userType() == qMetaTypeId()) + { + return v.value().toVariant().value(); + } + return v.value(); + }); + wrapper.module().method("setValue", [] (jlcxx::SingletonType, QVariant& v, T val) + { + v.setValue(val); + }); + wrapper.module().method("QVariant", [] (jlcxx::SingletonType, T val) + { + return QVariant::fromValue(val); + }); + } +}; + +template<> +struct ApplyQVariant +{ + void operator()(jlcxx::TypeWrapper& wrapper) + { + g_variant_type_map[qMetaTypeId()] = jl_any_type; + wrapper.module().method("value", [] (jlcxx::SingletonType, const QVariant& v) + { + if(v.userType() == qMetaTypeId()) + { + return v.value().toVariant().value()->value; + } + return v.value()->value; + }); + wrapper.module().method("setValue", [] (jlcxx::SingletonType, QVariant& v, jl_value_t* val) + { + v.setValue(std::make_shared(val)); + }); + wrapper.module().method("QVariant", [] (jlcxx::SingletonType, jl_value_t* val) + { + return QVariant::fromValue(std::make_shared(val)); + }); + } +}; + +// These are created in QML when passing a JuliaPropertyMap back to Julia by calling a Julia function from QML +template<> +struct ApplyQVariant +{ + void operator()(jlcxx::TypeWrapper& wrapper) + { + wrapper.module().method("getpropertymap", [] (QVariant& v) { return dynamic_cast(v.value())->julia_value(); }); + } +}; + +struct WrapQVariant +{ + WrapQVariant(jlcxx::TypeWrapper& w) : m_wrapper(w) + { + } + + template + void apply() + { + ApplyQVariant()(m_wrapper); + } + + jlcxx::TypeWrapper& m_wrapper; +}; + +struct WrapQList +{ + template + void operator()(TypeWrapperT&& wrapped) + { + using WrappedT = typename TypeWrapperT::type; + wrapped.method("cppsize", &WrappedT::size); + wrapped.method("cppgetindex", [] (const WrappedT& list, const int i) -> typename WrappedT::const_reference { return list[i]; }); + wrapped.method("cppsetindex!", [] (WrappedT& list, const typename WrappedT::value_type& v, const int i) { list[i] = v; }); + wrapped.method("push_back", static_cast(&WrappedT::push_back)); + wrapped.method("clear", &WrappedT::clear); + wrapped.method("removeAt", &WrappedT::removeAt); + } +}; + +// Wrap a ContainerT::iterator in a templated struct so it can be added as a parametric type in Julia +template typename ContainerT, typename KeyT, typename ValueT> +struct QtIteratorWrapper +{ + using iterator_type = typename ContainerT::iterator; + using value_type = ValueT; + using key_type = KeyT; + iterator_type value; +}; + +template struct QHashIteratorWrapper : QtIteratorWrapper {}; +template struct QMapIteratorWrapper : QtIteratorWrapper {}; + +template +void validate_iterator(T it) +{ + using IteratorT = typename T::iterator_type; + if(it.value == IteratorT()) + { + throw std::runtime_error("Invalid iterator"); + } +} + +struct WrapQtIterator +{ + template + void operator()(TypeWrapperT&& wrapped) + { + using WrappedT = typename TypeWrapperT::type; + using KeyT = typename WrappedT::key_type; + using ValueT = typename WrappedT::value_type; + + wrapped.method("iteratornext", [] (WrappedT it) -> WrappedT { ++(it.value); return it; }); + wrapped.method("iteratorkey", [] (WrappedT it) -> KeyT { validate_iterator(it); return it.value.key();} ); + wrapped.method("iteratorvalue", [] (WrappedT it) -> ValueT& { validate_iterator(it); return it.value.value(); } ); + wrapped.method("iteratorisequal", [] (WrappedT it1, WrappedT it2) -> bool { return it1.value == it2.value; } ); + } +}; + +template typename IteratorWrapperT> +struct WrapQtAssociativeContainer +{ + template + void operator()(TypeWrapperT&& wrapped) + { + using WrappedT = typename TypeWrapperT::type; + using ValueT = typename WrappedT::mapped_type; + using KeyT = typename WrappedT::key_type; + + wrapped.method("cppsize", &WrappedT::size); + wrapped.method("cppgetindex", [] (WrappedT& hash, const KeyT& k) -> ValueT& { return hash[k]; }); + wrapped.method("cppsetindex!", [] (WrappedT& hash, const ValueT& v, const KeyT& k) { hash[k] = v; }); + wrapped.method("insert", [] (WrappedT& hash, const KeyT& k, const ValueT& v) { hash.insert(k,v); }); + wrapped.method("clear", &WrappedT::clear); + wrapped.method("remove", [] (WrappedT& hash, const KeyT& k) -> bool { return hash.remove(k); }); + wrapped.method("empty", &WrappedT::empty); + wrapped.method("iteratorbegin", [] (WrappedT& hash) { return IteratorWrapperT{hash.begin()}; }); + wrapped.method("iteratorend", [] (WrappedT& hash) { return IteratorWrapperT{hash.end()}; }); + wrapped.method("keys", [] (const WrappedT& hash) { return hash.keys(); }); + wrapped.method("values", &WrappedT::values); + wrapped.method("contains", [] (WrappedT& hash, const KeyT& k) -> bool { return hash.contains(k); }); + } +}; + +struct WrapImageResult +{ + template + void operator()(TypeWrapperT&& wrapped) + { + using WrappedT = typename TypeWrapperT::type; + using image_type = typename WrappedT::image_type; + wrapped.template constructor(); + } +}; + +} + +void wrap_part_a(jlcxx::Module& qml_module); +void wrap_part_b(jlcxx::Module& qml_module); + +#endif diff --git a/wrap_qml_part_a.cpp b/wrap_qml_part_a.cpp new file mode 100644 index 0000000..be9e2cd --- /dev/null +++ b/wrap_qml_part_a.cpp @@ -0,0 +1,132 @@ +#include "wrap_qml.hpp" + +void wrap_part_a(jlcxx::Module& qml_module) +{ + using namespace jlcxx; + + qml_module.add_type("QObject") + .method("deleteLater", &QObject::deleteLater); + qml_module.method("connect_destroyed_signal", [] (QObject& obj, jl_value_t* jl_f) + { + QObject::connect(&obj, &QObject::destroyed, [jl_f](QObject* o) + { + static JuliaFunction f(jl_f); + qmlwrap::GCGuard gc_guard; + f(o); + }); + }); + + qml_module.add_type("QSize") + .constructor() + .method("width", &QSize::width) + .method("height", &QSize::height); + + qml_module.add_type("QCoreApplication", julia_base_type()); + qml_module.add_type("QGuiApplication", julia_base_type()) + .constructor(); + qml_module.method("quit", [] () { QGuiApplication::instance()->quit(); }); + + qml_module.add_type("QString", julia_type("AbstractString")) + .method("cppsize", &QString::size); + qml_module.method("uint16char", [] (const QString& s, int i) { return static_cast(s[i].unicode()); }); + qml_module.method("fromStdWString", QString::fromStdWString); + qml_module.method("isvalidindex", [] (const QString& s, int i) + { + if(i < 0 || i >= s.size()) + { + return false; + } + QTextBoundaryFinder bf(QTextBoundaryFinder::Grapheme, s); + bf.setPosition(i); + return bf.isAtBoundary(); + }); + + qml_module.method("get_iterate", [] (const QString& s, int i) + { + if(i < 0 || i >= s.size()) + { + return std::make_tuple(uint32_t(0),-1); + } + QTextBoundaryFinder bf(QTextBoundaryFinder::Grapheme, s); + bf.setPosition(i); + if(bf.toNextBoundary() != -1) + { + const int nexti = bf.position(); + if((nexti - i) == 1) + { + return std::make_tuple(uint32_t(s[i].unicode()), nexti); + } + return std::make_tuple(uint32_t(QChar::surrogateToUcs4(s[i],s[i+1])),nexti); + } + return std::make_tuple(uint32_t(0),-1); + }); + + qml_module.add_type("JuliaCanvas"); + + qml_module.add_type("JuliaDisplay", julia_type("AbstractDisplay", "Base")) + .method("load_png", &qmlwrap::JuliaDisplay::load_png) + .method("load_svg", &qmlwrap::JuliaDisplay::load_svg); + + qml_module.add_type("QUrl") + .constructor() + .method("toString", [] (const QUrl& url) { return url.toString(); }) + .method("toLocalFile", &QUrl::toLocalFile); + qml_module.method("QUrlFromLocalFile", QUrl::fromLocalFile); + + auto qvar_type = qml_module.add_type("QVariant"); + qvar_type.method("toString", &QVariant::toString); + + qml_module.add_type("QByteArray").constructor() + .method("to_string", &QByteArray::toStdString); + + qml_module.add_type("QByteArrayView"); + + qml_module.add_type>>("QList", julia_type("AbstractVector")) + .apply, QList, QList, QList, QList>(qmlwrap::WrapQList()); + + qml_module.method("libraryPaths", QCoreApplication::libraryPaths); + qml_module.method("addLibraryPath", QCoreApplication::addLibraryPath); + + // QMap (= QVariantMap for the given type) + qml_module.add_type,TypeVar<2>>>("QMapIterator") + .apply>(qmlwrap::WrapQtIterator()); + qml_module.add_type,TypeVar<2>>>("QMap", julia_type("AbstractDict")) + .apply>(qmlwrap::WrapQtAssociativeContainer()); + + // QHash + qml_module.add_type,TypeVar<2>>>("QHashIterator") + .apply>(qmlwrap::WrapQtIterator()); + qml_module.add_type,TypeVar<2>>>("QHash", julia_type("AbstractDict")) + .apply>(qmlwrap::WrapQtAssociativeContainer()); + + qml_module.add_type("QQmlPropertyMap", julia_base_type()) + .constructor(jlcxx::finalize_policy::no) + .method("clear", &QQmlPropertyMap::clear) + .method("contains", &QQmlPropertyMap::contains) + .method("insert", static_cast(&QQmlPropertyMap::insert)) + .method("size", &QQmlPropertyMap::size) + .method("value", &QQmlPropertyMap::value) + .method("connect_value_changed", [] (QQmlPropertyMap& propmap, jl_value_t* julia_property_map, jl_value_t* callback) + { + auto conn = QObject::connect(&propmap, &QQmlPropertyMap::valueChanged, [=](const QString& key, const QVariant& newvalue) + { + const jlcxx::JuliaFunction on_value_changed(callback); + jl_value_t* julia_propmap = julia_property_map; + qmlwrap::GCGuard gc_guard; + on_value_changed(julia_propmap, key, newvalue); + }); + }); + qml_module.add_type("_JuliaPropertyMap", julia_base_type()) + .method("julia_value", &qmlwrap::JuliaPropertyMap::julia_value) + .method("set_julia_value", &qmlwrap::JuliaPropertyMap::set_julia_value); + + jlcxx::for_each_parameter_type(qmlwrap::WrapQVariant(qvar_type)); + qml_module.method("type", qmlwrap::julia_variant_type); + + qml_module.method("make_qvariant_map", [] () + { + QVariantMap m; + m[QString("test")] = QVariant::fromValue(5); + return QVariant::fromValue(m); + }); +} diff --git a/wrap_qml_part_b.cpp b/wrap_qml_part_b.cpp new file mode 100644 index 0000000..5dd81bb --- /dev/null +++ b/wrap_qml_part_b.cpp @@ -0,0 +1,354 @@ +#include "wrap_qml.hpp" + + +void wrap_part_b(jlcxx::Module& qml_module) +{ + using namespace jlcxx; + + + auto qqmlcontext_wrapper = qml_module.add_type("QQmlContext", julia_base_type()) + .constructor() + .constructor() + .method("context_property", &QQmlContext::contextProperty) + .method("set_context_object", &QQmlContext::setContextObject) + .method("_set_context_property", static_cast(&QQmlContext::setContextProperty)) + .method("_set_context_property", static_cast(&QQmlContext::setContextProperty)) + .method("context_object", &QQmlContext::contextObject); + + qml_module.add_type("QQmlImageProviderBase", julia_base_type()); + + qml_module.add_type("QQmlEngine", julia_base_type()) + .method("addImageProvider", &QQmlEngine::addImageProvider) + .method("clearComponentCache", &QQmlEngine::clearComponentCache) + .method("clearSingletons", &QQmlEngine::clearSingletons) + .method("root_context", &QQmlEngine::rootContext) + .method("quit", &QQmlEngine::quit); + + qqmlcontext_wrapper.method("engine", &QQmlContext::engine); + + qml_module.add_type("QQmlApplicationEngine", julia_base_type()) + .constructor() // Construct with path to QML + .method("rootObjects", &QQmlApplicationEngine::rootObjects) + .method("load_into_engine", [] (QQmlApplicationEngine* e, const QString& qmlpath) + { + bool success = false; + auto conn = QObject::connect(e, &QQmlApplicationEngine::objectCreated, [&] (QObject* obj, const QUrl& url) { success = (obj != nullptr); }); + e->load(qmlpath); + QObject::disconnect(conn); + if(!success) + { + e->exit(1); + } + return success; + }); + + qml_module.method("qt_prefix_path", []() { return QLibraryInfo::path(QLibraryInfo::PrefixPath); }); + + auto qquickitem_type = qml_module.add_type("QQuickItem", julia_base_type()); + + qml_module.add_type("QWindow", julia_base_type()) + .method("destroy", &QWindow::destroy); + qml_module.add_type("QQuickWindow", julia_base_type()) + .method("content_item", &QQuickWindow::contentItem); + + qquickitem_type.method("window", &QQuickItem::window); + + qml_module.method("effectiveDevicePixelRatio", [] (QQuickWindow& w) + { + return w.effectiveDevicePixelRatio(); + }); + + qml_module.add_type("QQuickView", julia_base_type()) + .method("set_source", &QQuickView::setSource) + .method("show", &QQuickView::show) // not exported: conflicts with Base.show + .method("engine", &QQuickView::engine) + .method("root_object", &QQuickView::rootObject); + + qml_module.add_type("JuliaPaintedItem", julia_base_type()); + + qml_module.add_type("QQmlComponent", julia_base_type()) + .method("set_data", &QQmlComponent::setData); + // We manually add this constructor, since no finalizer will be added here. The component is destroyed together with the engine. + qml_module.method("QQmlComponent", [] (QQmlEngine* e) { return new QQmlComponent(e,e); }); + qml_module.method("create", [](QQmlComponent* comp, QQmlContext* context) + { + if(!comp->isReady()) + { + qWarning() << "QQmlComponent is not ready, aborting create. Errors were: " << comp->errors(); + return; + } + + QObject* obj = comp->create(context); + if(context != nullptr) + { + obj->setParent(context); // setting this makes sure the new object gets deleted + } + }); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)) + qml_module.method("qputenv", [] (const char* varName, QByteArray value) { qputenv(varName, value); }); +#else + qml_module.method("qputenv", qputenv); +#endif + qml_module.method("qgetenv", qgetenv); + qml_module.method("qunsetenv", qunsetenv); + + // App manager functions + qml_module.add_type("ApplicationManager"); + qml_module.method("init_qmlapplicationengine", []() { return qmlwrap::ApplicationManager::instance().init_qmlapplicationengine(); }); + qml_module.method("init_qmlengine", []() { return qmlwrap::ApplicationManager::instance().init_qmlengine(); }); + qml_module.method("get_qmlengine", []() { return qmlwrap::ApplicationManager::instance().get_qmlengine(); }); + qml_module.method("init_qquickview", []() { return qmlwrap::ApplicationManager::instance().init_qquickview(); }); + qml_module.method("cleanup", []() { qmlwrap::ApplicationManager::instance().cleanup(); }); + qml_module.method("qmlcontext", []() { return qmlwrap::ApplicationManager::instance().root_context(); }); + qml_module.method("app_exec", []() { qmlwrap::ApplicationManager::instance().exec(); }); + qml_module.method("process_events", qmlwrap::ApplicationManager::process_events); + qml_module.method("add_import_path", [](std::string path) { qmlwrap::ApplicationManager::instance().add_import_path(path); }); + qml_module.method("queue_process_eventloop_updates", []() { qmlwrap::ApplicationManager::instance().queue_process_eventloop_updates(); }); + + qml_module.method("yield", []() { qmlwrap::ForeignThreadManager::instance().yield(); }); + + qml_module.add_type("QTimer", julia_base_type()) + .constructor() + .method("setInterval", static_cast(&QTimer::setInterval)) + .method("start", [] (QTimer& t) { t.start(); } ) + .method("stop", &QTimer::stop) + .method("callOnTimeout", [] (QTimer& t, jl_value_t* julia_function) + { + JuliaFunction f(julia_function); + t.callOnTimeout([f] () { f(); }); + }); + + // Emit signals helper + qml_module.method("emit", [](const char *signal_name, const QVariantList& args) { + using namespace qmlwrap; + JuliaSignals *julia_signals = ApplicationManager::instance().julia_api()->juliaSignals(); + if (julia_signals == nullptr) + { + throw std::runtime_error("No signals available"); + } + julia_signals->emit_signal(signal_name, args); + }); + + // Function to register a function + qml_module.method("qmlfunction", [](const QString &name, jl_value_t *f) { + qmlwrap::ApplicationManager::instance().julia_api()->register_function(name, f); + }); + + qml_module.add_type("QPaintDevice") + .method("width", &QPaintDevice::width) + .method("height", &QPaintDevice::height) + .method("logicalDpiX", &QPaintDevice::logicalDpiX) + .method("logicalDpiY", &QPaintDevice::logicalDpiY); + qml_module.add_type("QPainter") + .method("device", &QPainter::device); + + qml_module.add_type("QAbstractItemModel", julia_base_type()); + qml_module.add_type("QAbstractTableModel", julia_base_type()); + qml_module.add_type("JuliaItemModel", julia_base_type()) + .method("emit_data_changed", &qmlwrap::JuliaItemModel::emit_data_changed) + .method("emit_header_data_changed", &qmlwrap::JuliaItemModel::emit_header_data_changed) + .method("begin_reset_model", &qmlwrap::JuliaItemModel::begin_reset_model) + .method("end_reset_model", &qmlwrap::JuliaItemModel::end_reset_model) + .method("begin_insert_rows", &qmlwrap::JuliaItemModel::begin_insert_rows) + .method("end_insert_rows", &qmlwrap::JuliaItemModel::end_insert_rows) + .method("begin_move_rows", &qmlwrap::JuliaItemModel::begin_move_rows) + .method("end_move_rows", &qmlwrap::JuliaItemModel::end_move_rows) + .method("begin_remove_rows", &qmlwrap::JuliaItemModel::begin_remove_rows) + .method("end_remove_rows", &qmlwrap::JuliaItemModel::end_remove_rows) + .method("begin_insert_columns", &qmlwrap::JuliaItemModel::begin_insert_columns) + .method("end_insert_columns", &qmlwrap::JuliaItemModel::end_insert_columns) + .method("begin_move_columns", &qmlwrap::JuliaItemModel::begin_move_columns) + .method("end_move_columns", &qmlwrap::JuliaItemModel::end_move_columns) + .method("begin_remove_columns", &qmlwrap::JuliaItemModel::begin_remove_columns) + .method("end_remove_columns", &qmlwrap::JuliaItemModel::end_remove_columns) + .method("default_role_names", &qmlwrap::JuliaItemModel::default_role_names) + .method("get_julia_data", &qmlwrap::JuliaItemModel::get_julia_data); + + qml_module.method("new_item_model", [] (jl_value_t* modeldata) { return jlcxx::create(modeldata); }); + + qml_module.set_override_module(jl_base_module); + qml_module.method("getindex", [](const QVariantMap& m, const QString& key) { return m[key]; }); + qml_module.unset_override_module(); + + qml_module.add_type("QOpenGLFramebufferObjectFormat") + .method("internalTextureFormat", &QOpenGLFramebufferObjectFormat::internalTextureFormat) + .method("textureTarget", &QOpenGLFramebufferObjectFormat::textureTarget); + + qml_module.add_type("QOpenGLFramebufferObject") + .method("size", &QOpenGLFramebufferObject::size) + .method("handle", &QOpenGLFramebufferObject::handle) + .method("isValid", &QOpenGLFramebufferObject::isValid) + .method("bind", &QOpenGLFramebufferObject::bind) + .method("release", &QOpenGLFramebufferObject::release) + .method("format", &QOpenGLFramebufferObject::format) + .method("texture", &QOpenGLFramebufferObject::texture) + .method("addColorAttachment", static_cast(&QOpenGLFramebufferObject::addColorAttachment)) + .method("textures", [] (const QOpenGLFramebufferObject& fbo) + { + auto v = fbo.textures(); + return std::vector(v.begin(), v.end()); + }); + + qml_module.method("graphicsApi", QQuickWindow::graphicsApi); + qml_module.method("setGraphicsApi", QQuickWindow::setGraphicsApi); + + qml_module.method("__test_add_double!", [] (double& result, QVariant var) + { + result += var.value(); + }); + + qml_module.method("__test_add_double_ref!", [] (double& result, const QVariant& var) + { + result += var.value(); + }); + + qml_module.method("__test_return_qvariant", [] (double val) + { + static QVariant var; + var.setValue(val); + return var; + }); + + qml_module.method("__test_return_qvariant_ref", [] (double val) -> const QVariant& + { + static QVariant var; + var.setValue(val); + return var; + }); + + qml_module.add_type("QFileSystemWatcher") + .constructor(jlcxx::finalize_policy::no) + .method("addPath", &QFileSystemWatcher::addPath); + qml_module.method("connect_file_changed_signal", [] (QFileSystemWatcher& watcher, jl_value_t* jl_f) + { + QObject::connect(&watcher, &QFileSystemWatcher::fileChanged, [jl_f](const QString& path) + { + static JuliaFunction f(jl_f); + qmlwrap::GCGuard gc_guard; + f(path); + }); + }); + + qml_module.add_enum("Format", + std::vector({ + "Format_Invalid", + "Format_Mono", + "Format_MonoLSB", + "Format_Indexed8", + "Format_RGB32", + "Format_ARGB32", + "Format_ARGB32_Premultiplied", + "Format_RGB16", + "Format_ARGB8565_Premultiplied", + "Format_RGB666", + "Format_ARGB6666_Premultiplied", + "Format_RGB555", + "Format_ARGB8555_Premultiplied", + "Format_RGB888", + "Format_RGB444", + "Format_ARGB4444_Premultiplied", + "Format_RGBX8888", + "Format_RGBA8888", + "Format_RGBA8888_Premultiplied", + "Format_BGR30", + "Format_A2BGR30_Premultiplied", + "Format_RGB30", + "Format_A2RGB30_Premultiplied", + "Format_Alpha8", + "Format_Grayscale8", + "Format_RGBX64", + "Format_RGBA64", + "Format_RGBA64_Premultiplied", + "Format_Grayscale16", + "Format_BGR888", + "Format_RGBX16FPx4", + "Format_RGBA16FPx4", + "Format_RGBA16FPx4_Premultiplied", + "Format_RGBX32FPx4", + "Format_RGBA32FPx4", + "Format_RGBA32FPx4_Premultiplied", + "Format_CMYK8888" + }), + std::vector({ + QImage::Format_Invalid, + QImage::Format_Mono, + QImage::Format_MonoLSB, + QImage::Format_Indexed8, + QImage::Format_RGB32, + QImage::Format_ARGB32, + QImage::Format_ARGB32_Premultiplied, + QImage::Format_RGB16, + QImage::Format_ARGB8565_Premultiplied, + QImage::Format_RGB666, + QImage::Format_ARGB6666_Premultiplied, + QImage::Format_RGB555, + QImage::Format_ARGB8555_Premultiplied, + QImage::Format_RGB888, + QImage::Format_RGB444, + QImage::Format_ARGB4444_Premultiplied, + QImage::Format_RGBX8888, + QImage::Format_RGBA8888, + QImage::Format_RGBA8888_Premultiplied, + QImage::Format_BGR30, + QImage::Format_A2BGR30_Premultiplied, + QImage::Format_RGB30, + QImage::Format_A2RGB30_Premultiplied, + QImage::Format_Alpha8, + QImage::Format_Grayscale8, + QImage::Format_RGBX64, + QImage::Format_RGBA64, + QImage::Format_RGBA64_Premultiplied, + QImage::Format_Grayscale16, + QImage::Format_BGR888, + QImage::Format_RGBX16FPx4, + QImage::Format_RGBA16FPx4, + QImage::Format_RGBA16FPx4_Premultiplied, + QImage::Format_RGBX32FPx4, + QImage::Format_RGBA32FPx4, + QImage::Format_RGBA32FPx4_Premultiplied, + QImage::Format_CMYK8888 + }) + ); + + qml_module.add_type("QColor") + .constructor() + .constructor() + .constructor() + .constructor(); + + qml_module.add_type("QImage", julia_base_type()) + .constructor() + .constructor() + .constructor() + .constructor() + .constructor() + .constructor() + .constructor() + .constructor() + .constructor() + .constructor() + .constructor() + .constructor() + .method("copy", static_cast(&QImage::copy)) + .method("copy", [] (const QImage& i) { return i.copy(); } ) + .method("height", &QImage::height) + .method("width", &QImage::width) + .method("fill", static_cast(&QImage::fill)); + + qml_module.add_type("QPixmap", julia_base_type()) + .constructor() + .constructor() + .constructor() + .constructor() + .method("fill", &QPixmap::fill); + qml_module.method("fromImage", [] (const QImage& image) { return QPixmap::fromImage(image); } ); + + qml_module.add_type("QQuickImageProvider", julia_base_type()); + qml_module.add_type("JuliaImageProvider", julia_base_type()) + .constructor(jlcxx::finalize_policy::no) // No finalizer because the engine takes ownership + .method("set_callback", &qmlwrap::JuliaImageProvider::set_callback); + + qml_module.add_type>>("ImageResult") + .apply, qmlwrap::ImageResult>(qmlwrap::WrapImageResult()); +}