diff --git a/CHANGELOG.md b/CHANGELOG.md index 96f7865a2..98d20fd2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,13 +17,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Added - Support for container types: `QSet`, `QHash`, `QList`, `QMap`, `QVector` -- Support for further types: `QByteArray`, `QCoreApplication`, `QGuiApplication`, `QModelIndex`, `QPersistentModelIndex`, `QStringList`, `QVector2D`, `QVector3D`, `QVector4D` +- Support for further types: `QByteArray`, `QCoreApplication`, `QGuiApplication`, `QModelIndex`, `QPersistentModelIndex`, `QQmlEngine`, `QStringList`, `QVector2D`, `QVector3D`, `QVector4D` - Support for nesting objects in properties, invokables, and signals with `*mut T` ### Changed - `QVariant` now has a uses a `QVariantValue` trait for supported types, allowing custom types to be used with QVariant -- `QtGui` types in cxx-qt-lib are now behind a feature `qt_gui` +- `QtGui` and `QtQml` types in cxx-qt-lib are now behind the features `qt_gui` and `qt_qml` ### Fixed diff --git a/crates/cxx-qt-build/Cargo.toml b/crates/cxx-qt-build/Cargo.toml index b7bebf6e9..5c68da075 100644 --- a/crates/cxx-qt-build/Cargo.toml +++ b/crates/cxx-qt-build/Cargo.toml @@ -23,5 +23,6 @@ quote.workspace = true qt-build-utils.workspace = true [features] -default = ["qt_gui"] +default = ["qt_gui", "qt_qml"] qt_gui = ["cxx-qt-lib-headers/qt_gui"] +qt_qml = ["cxx-qt-lib-headers/qt_qml"] diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index f7dca7572..05028bbfd 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -258,6 +258,8 @@ impl CxxQtBuilder { qt_modules.insert("Core".to_owned()); #[cfg(feature = "qt_gui")] qt_modules.insert("Gui".to_owned()); + #[cfg(feature = "qt_qml")] + qt_modules.insert("Qml".to_owned()); Self { rust_sources: vec![], qobject_headers: vec![], @@ -360,6 +362,9 @@ impl CxxQtBuilder { // Enable Qt Gui in C++ if the feature is enabled #[cfg(feature = "qt_gui")] self.cc_builder.define("CXX_QT_GUI_FEATURE", None); + // Enable Qt Gui in C++ if the feature is enabled + #[cfg(feature = "qt_qml")] + self.cc_builder.define("CXX_QT_QML_FEATURE", None); let mut qtbuild = qt_build_utils::QtBuild::new(self.qt_modules.into_iter().collect()) .expect("Could not find Qt installation"); diff --git a/crates/cxx-qt-lib-headers/Cargo.toml b/crates/cxx-qt-lib-headers/Cargo.toml index 5584abfd1..f464a198a 100644 --- a/crates/cxx-qt-lib-headers/Cargo.toml +++ b/crates/cxx-qt-lib-headers/Cargo.toml @@ -14,3 +14,4 @@ repository.workspace = true [features] default = [] qt_gui = [] +qt_qml = [] diff --git a/crates/cxx-qt-lib-headers/include/qml/qqmlengine.h b/crates/cxx-qt-lib-headers/include/qml/qqmlengine.h new file mode 100644 index 000000000..cb078998e --- /dev/null +++ b/crates/cxx-qt-lib-headers/include/qml/qqmlengine.h @@ -0,0 +1,24 @@ +// clang-format off +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#pragma once + +#ifdef CXX_QT_QML_FEATURE + +#include + +#include + +namespace rust { +namespace cxxqtlib1 { + +::std::unique_ptr +qqmlengineNew(); + +} +} + +#endif diff --git a/crates/cxx-qt-lib-headers/src/lib.rs b/crates/cxx-qt-lib-headers/src/lib.rs index dc003490d..f7fab7eb2 100644 --- a/crates/cxx-qt-lib-headers/src/lib.rs +++ b/crates/cxx-qt-lib-headers/src/lib.rs @@ -66,6 +66,8 @@ pub fn write_headers(directory: impl AsRef) { (include_str!("../include/gui/qvector3d.h"), "qvector3d.h"), #[cfg(feature = "qt_gui")] (include_str!("../include/gui/qvector4d.h"), "qvector4d.h"), + #[cfg(feature = "qt_qml")] + (include_str!("../include/qml/qqmlengine.h"), "qqmlengine.h"), (include_str!("../include/common.h"), "common.h"), (include_str!("../include/convert.h"), "convert.h"), (include_str!("../include/cxxqt_thread.h"), "cxxqt_thread.h"), diff --git a/crates/cxx-qt-lib/Cargo.toml b/crates/cxx-qt-lib/Cargo.toml index 8de837ffa..48edaac71 100644 --- a/crates/cxx-qt-lib/Cargo.toml +++ b/crates/cxx-qt-lib/Cargo.toml @@ -24,5 +24,6 @@ cxx-qt-lib-headers.workspace = true qt-build-utils.workspace = true [features] -default = ["qt_gui"] +default = ["qt_gui", "qt_qml"] qt_gui = ["cxx-qt-lib-headers/qt_gui"] +qt_qml = ["cxx-qt-lib-headers/qt_qml"] diff --git a/crates/cxx-qt-lib/build.rs b/crates/cxx-qt-lib/build.rs index f3262422b..d3b308007 100644 --- a/crates/cxx-qt-lib/build.rs +++ b/crates/cxx-qt-lib/build.rs @@ -5,11 +5,15 @@ fn main() { let feature_qt_gui_enabled = std::env::var("CARGO_FEATURE_QT_GUI").is_ok(); + let feature_qt_qml_enabled = std::env::var("CARGO_FEATURE_QT_QML").is_ok(); let mut qt_modules = vec!["Core".to_owned()]; if feature_qt_gui_enabled { qt_modules.push("Gui".to_owned()); } + if feature_qt_qml_enabled { + qt_modules.push("Qml".to_owned()); + } let qtbuild = qt_build_utils::QtBuild::new(qt_modules).expect("Could not find Qt installation"); qtbuild.cargo_link_libraries(); @@ -144,6 +148,12 @@ fn main() { ]); } + if feature_qt_qml_enabled { + rust_bridges.extend([ + "qml/qqmlengine", + ]); + } + for bridge in &rust_bridges { println!("cargo:rerun-if-changed=src/{bridge}.rs"); } @@ -192,6 +202,12 @@ fn main() { ]); } + if feature_qt_qml_enabled { + cpp_files.extend([ + "qml/qqmlengine", + ]); + } + for cpp_file in &cpp_files { builder.file(format!("src/{cpp_file}.cpp")); println!("cargo:rerun-if-changed=src/{cpp_file}.cpp"); @@ -210,6 +226,11 @@ fn main() { builder.define("CXX_QT_GUI_FEATURE", None); } + // Enable Qt Qml in C++ if the feature is enabled + if feature_qt_gui_enabled { + builder.define("CXX_QT_QML_FEATURE", None); + } + // MSVC builder.flag_if_supported("/std:c++17"); builder.flag_if_supported("/Zc:__cplusplus"); diff --git a/crates/cxx-qt-lib/src/lib.rs b/crates/cxx-qt-lib/src/lib.rs index 3021a21c2..1c9202d48 100644 --- a/crates/cxx-qt-lib/src/lib.rs +++ b/crates/cxx-qt-lib/src/lib.rs @@ -14,3 +14,8 @@ pub use crate::core::*; mod gui; #[cfg(feature = "qt_gui")] pub use crate::gui::*; + +#[cfg(feature = "qt_qml")] +mod qml; +#[cfg(feature = "qt_qml")] +pub use crate::qml::*; diff --git a/crates/cxx-qt-lib/src/qml/mod.rs b/crates/cxx-qt-lib/src/qml/mod.rs new file mode 100644 index 000000000..545c58a11 --- /dev/null +++ b/crates/cxx-qt-lib/src/qml/mod.rs @@ -0,0 +1,7 @@ +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +mod qqmlengine; +pub use qqmlengine::QQmlEngine; diff --git a/crates/cxx-qt-lib/src/qml/qqmlengine.cpp b/crates/cxx-qt-lib/src/qml/qqmlengine.cpp new file mode 100644 index 000000000..932432c7f --- /dev/null +++ b/crates/cxx-qt-lib/src/qml/qqmlengine.cpp @@ -0,0 +1,22 @@ +// clang-format off +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#ifdef CXX_QT_GUI_FEATURE +#include "cxx-qt-lib/qqmlengine.h" + +namespace rust { +namespace cxxqtlib1 { + +::std::unique_ptr +qqmlengineNew() +{ + return ::std::make_unique(); +} + +} +} +#endif diff --git a/crates/cxx-qt-lib/src/qml/qqmlengine.rs b/crates/cxx-qt-lib/src/qml/qqmlengine.rs new file mode 100644 index 000000000..9ae12efe1 --- /dev/null +++ b/crates/cxx-qt-lib/src/qml/qqmlengine.rs @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#[cxx::bridge] +mod ffi { + unsafe extern "C++" { + include!("cxx-qt-lib/qstring.h"); + type QString = crate::QString; + include!("cxx-qt-lib/qstringlist.h"); + type QStringList = crate::QStringList; + include!("cxx-qt-lib/qurl.h"); + type QUrl = crate::QUrl; + + include!("cxx-qt-lib/qqmlengine.h"); + type QQmlEngine; + + /// Adds path as a directory where the engine searches for installed modules in a URL-based directory structure. + #[rust_name = "add_import_path"] + fn addImportPath(self: Pin<&mut QQmlEngine>, path: &QString); + + /// Adds path as a directory where the engine searches for native plugins for imported modules (referenced in the qmldir file). + #[rust_name = "add_plugin_path"] + fn addPluginPath(self: Pin<&mut QQmlEngine>, path: &QString); + + /// Return the base URL for this engine. + /// The base URL is only used to resolve components when a relative URL is passed to the QQmlComponent constructor. + #[rust_name = "base_url"] + fn baseUrl(self: &QQmlEngine) -> QUrl; + + /// Returns the list of directories where the engine searches for installed modules in a URL-based directory structure. + #[rust_name = "import_path_list"] + fn importPathList(self: &QQmlEngine) -> QStringList; + + /// Returns the list of directories where the engine searches for native plugins for imported modules (referenced in the qmldir file). + #[rust_name = "plugin_path_list"] + fn pluginPathList(self: &QQmlEngine) -> QStringList; + + /// Set the base URL for this engine to url. + #[rust_name = "set_base_url"] + fn setBaseUrl(self: Pin<&mut QQmlEngine>, url: &QUrl); + + /// Sets paths as the list of directories where the engine searches for installed modules in a URL-based directory structure. + #[rust_name = "set_import_path_list"] + fn setImportPathList(self: Pin<&mut QQmlEngine>, paths: &QStringList); + + /// Sets the list of directories where the engine searches for native plugins for imported modules (referenced in the qmldir file) to paths. + #[rust_name = "set_plugin_path_list"] + fn setPluginPathList(self: Pin<&mut QQmlEngine>, paths: &QStringList); + } + + #[namespace = "rust::cxxqtlib1"] + unsafe extern "C++" { + #[doc(hidden)] + #[rust_name = "qqmlengine_new"] + fn qqmlengineNew() -> UniquePtr; + } + + impl UniquePtr {} +} + +pub use ffi::QQmlEngine; + +impl QQmlEngine { + /// Create a new QQmlEngine + pub fn new() -> cxx::UniquePtr { + ffi::qqmlengine_new() + } +} diff --git a/examples/cargo_without_cmake/build.rs b/examples/cargo_without_cmake/build.rs index 394e0de7b..5f45a0203 100644 --- a/examples/cargo_without_cmake/build.rs +++ b/examples/cargo_without_cmake/build.rs @@ -8,9 +8,8 @@ use cxx_qt_build::CxxQtBuilder; fn main() { CxxQtBuilder::new() - // Link Qt's Qml and Network libraries. Qt Core and Gui are always + // Link Qt's Network libraries. Qt Core, Gui, and Qml are always // linked, so there is no need to specify them here. - .qt_module("Qml") .qt_module("Network") // Generate C++ from the `#[cxx_qt::bridge]` module .file("src/cxxqt_object.rs") diff --git a/tests/basic_cxx_only/CMakeLists.txt b/tests/basic_cxx_only/CMakeLists.txt index 52b12c83c..de8d50f9f 100644 --- a/tests/basic_cxx_only/CMakeLists.txt +++ b/tests/basic_cxx_only/CMakeLists.txt @@ -18,10 +18,10 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT USE_QT5) - find_package(Qt6 COMPONENTS Core Gui Test) + find_package(Qt6 COMPONENTS Core Gui Qml Test) endif() if(NOT Qt6_FOUND) - find_package(Qt5 5.15 COMPONENTS Core Gui Test REQUIRED) + find_package(Qt5 5.15 COMPONENTS Core Gui Qml Test REQUIRED) endif() get_target_property(QMAKE Qt::qmake IMPORTED_LOCATION) @@ -36,6 +36,7 @@ target_include_directories(${CRATE} INTERFACE "${CXXQT_EXPORT_DIR}/${CRATE}") target_link_libraries(${CRATE} INTERFACE Qt::Core Qt::Gui + Qt::Qml ) add_executable(${APP_NAME} diff --git a/tests/basic_cxx_qt/CMakeLists.txt b/tests/basic_cxx_qt/CMakeLists.txt index 029fb102d..ff08653b7 100644 --- a/tests/basic_cxx_qt/CMakeLists.txt +++ b/tests/basic_cxx_qt/CMakeLists.txt @@ -17,10 +17,10 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT USE_QT5) - find_package(Qt6 COMPONENTS Core Gui Test) + find_package(Qt6 COMPONENTS Core Gui Qml Test) endif() if(NOT Qt6_FOUND) - find_package(Qt5 5.15 COMPONENTS Core Gui Test REQUIRED) + find_package(Qt5 5.15 COMPONENTS Core Gui Qml Test REQUIRED) endif() get_target_property(QMAKE Qt::qmake IMPORTED_LOCATION) @@ -35,6 +35,7 @@ target_include_directories(${CRATE} INTERFACE "${CXXQT_EXPORT_DIR}/${CRATE}") target_link_libraries(${CRATE} INTERFACE Qt::Core Qt::Gui + Qt::Qml ) add_executable(${APP_NAME} cpp/main.cpp) diff --git a/tests/qt_types_standalone/CMakeLists.txt b/tests/qt_types_standalone/CMakeLists.txt index f5c1bf22f..f188b680b 100644 --- a/tests/qt_types_standalone/CMakeLists.txt +++ b/tests/qt_types_standalone/CMakeLists.txt @@ -18,10 +18,10 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT USE_QT5) - find_package(Qt6 COMPONENTS Core Gui Test) + find_package(Qt6 COMPONENTS Core Gui Qml Test) endif() if(NOT Qt6_FOUND) - find_package(Qt5 5.15 COMPONENTS Core Gui Test REQUIRED) + find_package(Qt5 5.15 COMPONENTS Core Gui Qml Test REQUIRED) endif() get_target_property(QMAKE Qt::qmake IMPORTED_LOCATION) @@ -36,6 +36,7 @@ target_include_directories(${CRATE} INTERFACE "${CXXQT_EXPORT_DIR}/${CRATE}") target_link_libraries(${CRATE} INTERFACE Qt::Core Qt::Gui + Qt::Qml ) add_executable(${APP_NAME}