diff --git a/.gitignore b/.gitignore index c4da92783..f89b07a01 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ compile_commands.json # clangd cache .cache/ + +*.qmlls.ini diff --git a/crates/cxx-qt-build/cpp/builtins.h b/crates/cxx-qt-build/cpp/builtins.h new file mode 100644 index 000000000..ec987bb7a --- /dev/null +++ b/crates/cxx-qt-build/cpp/builtins.h @@ -0,0 +1,94 @@ +// clang-format off +// SPDX-FileCopyrightText: 2025 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 + +#include + +#include + +// This is similar to the builtins file in qtdeclarative +// https://code.qt.io/cgit/qt/qtdeclarative.git/tree/src/qml/qqmlbuiltins_p.h?h=v6.9.3 +// +// We need this to be able to alias namespaced std numerics to types that QML +// understands. +// +// We do not need to build this file but the moc JSON output to give to +// qmltyperegistrar so that qmllint and qmlls can understand the types. +// +// If Qt ever registered qualified versions of the numerics this could be +// removed. +// +// qqmlbuiltins uses the following values for QML_USING so we should copy +// i8, u8 -> qint8, quint8 +// i16, u16 -> short, ushort +// i32, u32 -> int, uint +// i64, u64 -> qlonglong, qulonglong + +struct QQmlCxxQtStdInt8TForeign +{ + Q_GADGET + QML_FOREIGN(::std::int8_t) + QML_USING(qint8) +}; +static_assert(sizeof(::std::int8_t) == sizeof(qint8)); + +struct QQmlCxxQtStdUInt8TForeign +{ + Q_GADGET + QML_FOREIGN(::std::uint8_t) + QML_USING(quint8) +}; +static_assert(sizeof(::std::uint8_t) == sizeof(quint8)); + +struct QQmlCxxQtStdInt16TForeign +{ + Q_GADGET + QML_FOREIGN(::std::int16_t) + QML_USING(short) +}; +static_assert(sizeof(::std::int16_t) == sizeof(short)); + +struct QQmlCxxQtStdUInt16TForeign +{ + Q_GADGET + QML_FOREIGN(::std::uint16_t) + QML_USING(ushort) +}; +static_assert(sizeof(::std::uint16_t) == sizeof(ushort)); + +struct QQmlCxxQtStdInt32TForeign +{ + Q_GADGET + QML_FOREIGN(::std::int32_t) + QML_USING(int) +}; +static_assert(sizeof(::std::int32_t) == sizeof(int)); + +struct QQmlCxxQtStdUInt32TForeign +{ + Q_GADGET + QML_FOREIGN(::std::uint32_t) + QML_USING(uint) +}; +static_assert(sizeof(::std::uint32_t) == sizeof(uint)); + +struct QQmlCxxQtStdInt64TForeign +{ + Q_GADGET + QML_FOREIGN(::std::int64_t) + QML_USING(qlonglong) +}; +static_assert(sizeof(::std::int64_t) == sizeof(qlonglong)); + +struct QQmlCxxQtStdUInt64TForeign +{ + Q_GADGET + QML_FOREIGN(::std::uint64_t) + QML_USING(qulonglong) +}; +static_assert(sizeof(::std::uint64_t) == sizeof(qulonglong)); diff --git a/crates/cxx-qt-build/src/dir.rs b/crates/cxx-qt-build/src/dir.rs index 6e0546173..b08886b2e 100644 --- a/crates/cxx-qt-build/src/dir.rs +++ b/crates/cxx-qt-build/src/dir.rs @@ -5,7 +5,7 @@ //! This modules contains information about the paths where artifacts are placed by cxx-qt-build. -use crate::{crate_name, module_name_from_uri}; +use crate::crate_name; use std::io::Result; use std::{ env, fs, @@ -57,32 +57,35 @@ pub(crate) fn crate_target() -> PathBuf { } /// The target directory, namespaced by QML module -pub(crate) fn module_target(module_uri: &str) -> PathBuf { +pub(crate) fn module_target(module_uri: &qt_build_utils::QmlUri) -> PathBuf { module_export(module_uri).unwrap_or_else(|| { out() // Use a short name due to the Windows file path limit! .join("cxxqtqml") - .join(module_name_from_uri(module_uri)) + .join(module_uri.as_dirs()) }) } /// The export directory, namespaced by QML module /// -/// In conctrast to the crate_export directory, this is `Some` for downstream dependencies as well. +/// In contrast to the crate_export directory, this is `Some` for downstream dependencies as well. /// This allows CMake to import QML modules from dependencies. /// /// TODO: This may conflict if two dependencies are building QML modules with the same name! /// We should probably include a lockfile here to avoid this. -pub(crate) fn module_export(module_uri: &str) -> Option { +pub(crate) fn module_export(module_uri: &qt_build_utils::QmlUri) -> Option { + // In contrast to crate_export, we don't need to check for the specific crate here. + // QML modules should always be exported. + module_export_qml_modules().map(|dir| dir.join(module_uri.as_dirs())) +} + +pub(crate) fn module_export_qml_modules() -> Option { // In contrast to crate_export, we don't need to check for the specific crate here. // QML modules should always be exported. env::var("CXX_QT_EXPORT_DIR") .ok() .map(PathBuf::from) - .map(|dir| { - dir.join("qml_modules") - .join(module_name_from_uri(module_uri)) - }) + .map(|dir| dir.join("qml_modules")) } /// The target directory or another directory where we can write files that will be shared diff --git a/crates/cxx-qt-build/src/lib.rs b/crates/cxx-qt-build/src/lib.rs index e32fb57ec..92b9605b1 100644 --- a/crates/cxx-qt-build/src/lib.rs +++ b/crates/cxx-qt-build/src/lib.rs @@ -36,6 +36,7 @@ pub use qml_modules::QmlModule; pub use qt_build_utils::MocArguments; use qt_build_utils::MocProducts; +use qt_build_utils::QmlLsIniBuilder; use quote::ToTokens; use semver::Version; use std::{ @@ -732,6 +733,10 @@ impl CxxQtBuilder { { let mut moc_arguments = moc_arguments.clone(); + // Ensure that header root is in the include path of moc + // otherwise it cannot read the .cxx.h paths + moc_arguments = moc_arguments.include_path(dir::header_root()); + if let Some(qml_module) = &self.qml_module { // Ensure that the generated QObject header is in the include path // so that qmltyperegistar can include them later @@ -814,8 +819,14 @@ impl CxxQtBuilder { // Extract qml_modules out of self so we don't have to hold onto `self` for the duration of // the loop. if let Some(qml_module) = self.qml_module.take() { - dir::clean(dir::module_target(&qml_module.uri)) - .expect("Failed to clean qml module export directory!"); + // TODO: likely qml_module will have a QmlUri already + let qml_uri = qt_build_utils::QmlUri::from(qml_module.uri.as_str()); + + // TODO: clean the old module target + // however if there is a sub uri this cleans that too + // so we should only remove files and not sub folders? + // dir::clean(dir::module_target(&qml_uri)) + // .expect("Failed to clean qml module export directory!"); // Check that all rust files are within the same directory // @@ -853,11 +864,22 @@ impl CxxQtBuilder { let cc_builder = &mut self.cc_builder; qtbuild.cargo_link_libraries(cc_builder); - let qml_metatypes_json: Vec = moc_products + let mut qml_metatypes_json: Vec = moc_products .iter() .map(|products| products.metatypes_json.clone()) .collect(); + // Inject CXX-Qt builtin meta types + let builtins_path = dir::out().join("builtins.h"); + std::fs::write(&builtins_path, include_str!("../cpp/builtins.h")) + .expect("Failed to write builtins.h"); + qml_metatypes_json.push( + qtbuild + .moc() + .compile(builtins_path, MocArguments::default()) + .metatypes_json, + ); + let qml_module_registration_files = qtbuild.register_qml_module( &qml_metatypes_json, &qml_module.uri, @@ -869,6 +891,7 @@ impl CxxQtBuilder { &module_name_from_uri(&qml_module.uri), &qml_module.qml_files, &qml_module.qrc_files, + &qml_module.depends, ); if let Some(qmltyperegistrar) = qml_module_registration_files.qmltyperegistrar { cc_builder.file(qmltyperegistrar); @@ -890,7 +913,7 @@ impl CxxQtBuilder { for qmlcachegen_file in qml_module_registration_files.qmlcachegen { cc_builder.file(qmlcachegen_file); } - // This is required, as described here: plugin_builder + // This is required, as described here: https://doc.qt.io/qt-6/plugins-howto.html#creating-static-plugins cc_builder.define("QT_STATICPLUGIN", None); // If any of the files inside the qml module change, then trigger a rerun @@ -902,6 +925,39 @@ impl CxxQtBuilder { println!("cargo::rerun-if-changed={}", path.display()); } + // Export the .qmltypes and qmldir files into a stable path, so that tools like + // qmllint/qmlls can find them. + let plugin_dir = dir::module_export(&qml_uri); + if let Some(plugin_dir) = &plugin_dir { + std::fs::create_dir_all(plugin_dir).expect("Could not create plugin directory"); + std::fs::copy( + qml_module_registration_files.qmltypes, + plugin_dir.join("plugin.qmltypes"), + ) + .expect("Could not copy plugin.qmltypes to export directory"); + std::fs::copy( + qml_module_registration_files.qmldir, + plugin_dir.join("qmldir"), + ) + .expect("Could not copy qmldir to export directory"); + } + + // Create a .qmlls.ini file with the build dir set similar to QT_QML_GENERATE_QMLLS_INI + if let (Some(qml_modules_dir), Some(manifest_dir)) = + (dir::module_export_qml_modules(), dir::manifest()) + { + // TODO: manifest dir is not enough as QML files might be in a parent + // so this should likely be an argument given to cxx-qt-build + // that optionally exports? + let mut file = File::create(manifest_dir.parent().unwrap().join(".qmlls.ini")) + .expect("Could not create qmlls.ini file"); + QmlLsIniBuilder::new() + .build_dir(qml_modules_dir) + .no_cmake_calls(true) + .write(&mut file) + .expect("Could not write qmlls.ini") + } + let module_init_key = qml_module_init_key(&qml_module.uri); let private_initializers = [qml_module_registration_files.plugin_init]; let public_initializer = @@ -909,7 +965,7 @@ impl CxxQtBuilder { self.build_initializers( &private_initializers, &public_initializer, - dir::module_export(&qml_module.uri).map(|dir| dir.join("plugin_init.o")), + plugin_dir.map(|dir| dir.join("plugin_init.o")), &module_init_key, ); diff --git a/crates/cxx-qt-build/src/qml_modules.rs b/crates/cxx-qt-build/src/qml_modules.rs index e6e9904bf..1ed411d1d 100644 --- a/crates/cxx-qt-build/src/qml_modules.rs +++ b/crates/cxx-qt-build/src/qml_modules.rs @@ -31,6 +31,8 @@ where // and an empty slice is likely desired in most cases; most users probably don't // care about this field. pub qrc_files: &'a [A], + /// Dependencies of the QML module + pub depends: &'a [&'a str], } impl Default for QmlModule<'_, A, B> @@ -45,6 +47,7 @@ where version_minor: 0, qml_files: &[], qrc_files: &[], + depends: &[], } } } @@ -58,6 +61,7 @@ pub(crate) struct OwningQmlModule { pub version_minor: usize, pub qml_files: Vec, pub qrc_files: Vec, + pub depends: Vec, } fn collect_pathbuf_vec(asref: &[impl AsRef]) -> Vec { @@ -72,6 +76,11 @@ impl, B: AsRef> From> for OwningQmlModu version_minor: other.version_minor, qml_files: collect_pathbuf_vec(other.qml_files), qrc_files: collect_pathbuf_vec(other.qrc_files), + depends: other + .depends + .into_iter() + .map(|depend| depend.to_string()) + .collect(), } } } diff --git a/crates/cxx-qt-gen/src/generator/cpp/cxxqttype.rs b/crates/cxx-qt-gen/src/generator/cpp/cxxqttype.rs index 0fbe10c17..d2c0c9535 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/cxxqttype.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/cxxqttype.rs @@ -17,7 +17,7 @@ pub fn generate(qobject_idents: &QObjectNames) -> Result")); + .push(format!("private ::rust::cxxqt1::CxxQtType<{rust_struct}>")); Ok(result) } @@ -42,7 +42,7 @@ mod tests { assert_eq!(generated.base_classes.len(), 1); assert_eq!( generated.base_classes[0], - "::rust::cxxqt1::CxxQtType" + "private ::rust::cxxqt1::CxxQtType" ); } } diff --git a/crates/cxx-qt-gen/src/generator/cpp/qobject.rs b/crates/cxx-qt-gen/src/generator/cpp/qobject.rs index 8f1b2d3d7..24e55bfa6 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/qobject.rs @@ -63,16 +63,13 @@ impl GeneratedCppQObjectBlocks { pub fn from(qobject: &ParsedQObject) -> GeneratedCppQObjectBlocks { let mut qml_specifiers = Vec::new(); if let Some(qml_metadata) = &qobject.qml_metadata { - // Somehow moc doesn't include the info in metatypes.json that qmltyperegistrar needs - // when using the QML_ELEMENT/QML_NAMED_ELEMENT macros, but moc works when using what - // those macros expand to. - qml_specifiers.push(format!( - "Q_CLASSINFO(\"QML.Element\", \"{}\")", - qml_metadata.name - )); + // Note ensure that the header moc processes has QtQml/QQmlEngine + // this is done via generator/rust/qobject + + qml_specifiers.push(format!("QML_NAMED_ELEMENT({})", qml_metadata.name)); if qml_metadata.uncreatable { - qml_specifiers.push("Q_CLASSINFO(\"QML.Creatable\", \"false\")".to_owned()); + qml_specifiers.push("QML_UNCREATABLE(\"Not creatable\")".to_owned()); } if qml_metadata.singleton { @@ -128,7 +125,10 @@ impl GeneratedCppQObject { // CODECOV_EXCLUDE_STOP }; - generated.blocks.base_classes.push(base_class.clone()); + generated + .blocks + .base_classes + .push(format!("public {base_class}")); // Add the CxxQtType rust and rust_mut methods generated @@ -229,10 +229,10 @@ mod tests { assert_eq!(cpp.namespace_internals, "cxx_qt_MyObject"); assert_eq!(cpp.blocks.base_classes.len(), 2); - assert_eq!(cpp.blocks.base_classes[0], "QObject"); + assert_eq!(cpp.blocks.base_classes[0], "public QObject"); assert_eq!( cpp.blocks.base_classes[1], - "::rust::cxxqt1::CxxQtType" + "private ::rust::cxxqt1::CxxQtType" ); assert_eq!(cpp.blocks.metaobjects.len(), 0); } @@ -268,10 +268,10 @@ mod tests { .unwrap(); assert_eq!(cpp.namespace_internals, "cxx_qt::cxx_qt_MyObject"); assert_eq!(cpp.blocks.base_classes.len(), 2); - assert_eq!(cpp.blocks.base_classes[0], "QStringListModel"); + assert_eq!(cpp.blocks.base_classes[0], "public QStringListModel"); assert_eq!( cpp.blocks.base_classes[1], - "::rust::cxxqt1::CxxQtType" + "private ::rust::cxxqt1::CxxQtType" ); assert_eq!(cpp.blocks.metaobjects.len(), 0); } @@ -302,10 +302,7 @@ mod tests { .unwrap(); assert_eq!(cpp.name.cxx_unqualified(), "MyNamedObject"); assert_eq!(cpp.blocks.metaobjects.len(), 1); - assert_eq!( - cpp.blocks.metaobjects[0], - "Q_CLASSINFO(\"QML.Element\", \"MyQmlElement\")" - ); + assert_eq!(cpp.blocks.metaobjects[0], "QML_NAMED_ELEMENT(MyQmlElement)"); } #[test] @@ -322,10 +319,7 @@ mod tests { .unwrap(); assert_eq!(cpp.name.cxx_unqualified(), "MyObject"); assert_eq!(cpp.blocks.metaobjects.len(), 2); - assert_eq!( - cpp.blocks.metaobjects[0], - "Q_CLASSINFO(\"QML.Element\", \"MyObject\")" - ); + assert_eq!(cpp.blocks.metaobjects[0], "QML_NAMED_ELEMENT(MyObject)"); assert_eq!(cpp.blocks.metaobjects[1], "QML_SINGLETON"); } @@ -353,13 +347,10 @@ mod tests { .unwrap(); assert_eq!(cpp.name.cxx_unqualified(), "MyObject"); assert_eq!(cpp.blocks.metaobjects.len(), 2); - assert_eq!( - cpp.blocks.metaobjects[0], - "Q_CLASSINFO(\"QML.Element\", \"MyObject\")" - ); + assert_eq!(cpp.blocks.metaobjects[0], "QML_NAMED_ELEMENT(MyObject)"); assert_eq!( cpp.blocks.metaobjects[1], - "Q_CLASSINFO(\"QML.Creatable\", \"false\")" + "QML_UNCREATABLE(\"Not creatable\")" ); } } diff --git a/crates/cxx-qt-gen/src/generator/cpp/threading.rs b/crates/cxx-qt-gen/src/generator/cpp/threading.rs index 52eb3d1b6..af9a0d7b2 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/threading.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/threading.rs @@ -32,9 +32,10 @@ pub fn generate(qobject_idents: &QObjectNames) -> Result<(String, GeneratedCppQO .includes .insert("#include ".to_owned()); - result - .base_classes - .push(format!("::rust::cxxqt1::CxxQtThreading<{cpp_class}>")); + // TODO: this should probably be private too? + result.base_classes.push(format!( + "public ::rust::cxxqt1::CxxQtThreading<{cpp_class}>" + )); let class_initializer = format!("::rust::cxxqt1::CxxQtThreading<{cpp_class}>(this)"); @@ -90,7 +91,7 @@ mod tests { assert_eq!(generated.base_classes.len(), 1); assert_eq!( generated.base_classes[0], - "::rust::cxxqt1::CxxQtThreading" + "public ::rust::cxxqt1::CxxQtThreading" ); } } diff --git a/crates/cxx-qt-gen/src/generator/rust/qobject.rs b/crates/cxx-qt-gen/src/generator/rust/qobject.rs index 9305456c0..d426ef8e7 100644 --- a/crates/cxx-qt-gen/src/generator/rust/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/rust/qobject.rs @@ -40,15 +40,13 @@ impl GeneratedRustFragment { generate_rust_signals(&structured_qobject.signals, &qobject_names, type_names)?, ]; - // If this type is a singleton then we need to add an include - if let Some(qml_metadata) = &qobject.qml_metadata { - if qml_metadata.singleton { - generated.push(GeneratedRustFragment::from_cxx_item(parse_quote! { - unsafe extern "C++" { - include!(); - } - })) - } + // If this type is using QML declarative macros then ensure we have the right include + if qobject.qml_metadata.is_some() { + generated.push(GeneratedRustFragment::from_cxx_item(parse_quote! { + unsafe extern "C++" { + include!(); + } + })) } // If this type has threading enabled then add generation diff --git a/crates/cxx-qt-gen/src/writer/cpp/header.rs b/crates/cxx-qt-gen/src/writer/cpp/header.rs index 4931bec08..141b5854f 100644 --- a/crates/cxx-qt-gen/src/writer/cpp/header.rs +++ b/crates/cxx-qt-gen/src/writer/cpp/header.rs @@ -91,11 +91,18 @@ fn qobjects_header(generated: &GeneratedCppBlocks) -> Vec { {public_methods} {private_methods} + + private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }}; {qobject_assert}"#, // Note that there is always a base class as we always have CxxQtType - base_classes = qobject.blocks.base_classes.iter().map(|base| format!("public {base}")).collect::>().join(", "), + base_classes = qobject.blocks.base_classes.join(", "), metaobjects = qobject.blocks.metaobjects.join("\n "), public_methods = create_block("public", &qobject.blocks.methods.iter().filter_map(pair_as_header).collect::>()), private_methods = create_block("private", &qobject.blocks.private_methods.iter().filter_map(pair_as_header).collect::>()), @@ -276,7 +283,7 @@ class MyObject; -class MyObject : public MyBase, public ::rust::cxxqt1::CxxQtType +class MyObject : public MyBase, private ::rust::cxxqt1::CxxQtType { public: @@ -287,7 +294,12 @@ public: public: explicit MyObject(); +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; diff --git a/crates/cxx-qt-gen/src/writer/cpp/mod.rs b/crates/cxx-qt-gen/src/writer/cpp/mod.rs index 69de8d404..55b3a83b6 100644 --- a/crates/cxx-qt-gen/src/writer/cpp/mod.rs +++ b/crates/cxx-qt-gen/src/writer/cpp/mod.rs @@ -111,7 +111,7 @@ mod tests { }, has_qobject_macro: true, blocks: GeneratedCppQObjectBlocks { - base_classes: vec!["QStringListModel".to_owned()], + base_classes: vec!["public QStringListModel".to_owned()], includes: { let mut includes = BTreeSet::::default(); includes.insert("#include ".to_owned()); @@ -233,7 +233,7 @@ mod tests { namespace_internals: "cxx_qt::cxx_qt_first_object".to_owned(), has_qobject_macro: true, blocks: GeneratedCppQObjectBlocks { - base_classes: vec!["QStringListModel".to_owned()], + base_classes: vec!["public QStringListModel".to_owned()], includes: { let mut includes = BTreeSet::::default(); includes.insert("#include ".to_owned()); @@ -276,7 +276,7 @@ mod tests { namespace_internals: "cxx_qt::cxx_qt_second_object".to_owned(), has_qobject_macro: true, blocks: GeneratedCppQObjectBlocks { - base_classes: vec!["QStringListModel".to_owned()], + base_classes: vec!["public QStringListModel".to_owned()], includes: { let mut includes = BTreeSet::::default(); includes.insert("#include ".to_owned()); @@ -374,6 +374,13 @@ mod tests { void privateMethod() const; void privateMethod(); + + private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, "MyObject must inherit from QObject"); @@ -428,6 +435,13 @@ mod tests { Q_SIGNAL void countChanged(); + + private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, "FirstObject must inherit from QObject"); @@ -455,6 +469,13 @@ mod tests { private: void privateMethod() const; + + private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, "SecondObject must inherit from QObject"); @@ -506,6 +527,13 @@ mod tests { void privateMethod() const; void privateMethod(); + + private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, "MyObject must inherit from QObject"); diff --git a/crates/cxx-qt-gen/test_outputs/cfgs.h b/crates/cxx-qt-gen/test_outputs/cfgs.h index 7221df6fc..b5d6818aa 100644 --- a/crates/cxx-qt-gen/test_outputs/cfgs.h +++ b/crates/cxx-qt-gen/test_outputs/cfgs.h @@ -55,7 +55,7 @@ QObjectEnabled_signal_enabledConnect( class QObjectEnabled : public QObject - , public ::rust::cxxqt1::CxxQtType + , private ::rust::cxxqt1::CxxQtType { Q_OBJECT public: @@ -76,6 +76,13 @@ class QObjectEnabled return QObject::inherit_enabled(args...); } explicit QObjectEnabled(QObject* parent = nullptr); + +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, diff --git a/crates/cxx-qt-gen/test_outputs/inheritance.h b/crates/cxx-qt-gen/test_outputs/inheritance.h index 6e7700a3f..2ef2055d8 100644 --- a/crates/cxx-qt-gen/test_outputs/inheritance.h +++ b/crates/cxx-qt-gen/test_outputs/inheritance.h @@ -9,7 +9,7 @@ class MyObject; class MyObject : public QAbstractItemModel - , public ::rust::cxxqt1::CxxQtType + , private ::rust::cxxqt1::CxxQtType { Q_OBJECT public: @@ -36,6 +36,13 @@ class MyObject return QAbstractItemModel::fetch_more(args...); } explicit MyObject(QObject* parent = nullptr); + +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, diff --git a/crates/cxx-qt-gen/test_outputs/invokables.h b/crates/cxx-qt-gen/test_outputs/invokables.h index ad7cd7e04..ab1391d2a 100644 --- a/crates/cxx-qt-gen/test_outputs/invokables.h +++ b/crates/cxx-qt-gen/test_outputs/invokables.h @@ -15,7 +15,7 @@ using MyObjectCxxQtThread = ::rust::cxxqt1::CxxQtThread; namespace cxx_qt::my_object { class MyObject : public QObject - , public ::rust::cxxqt1::CxxQtType + , private ::rust::cxxqt1::CxxQtType , public ::rust::cxxqt1::CxxQtThreading { Q_OBJECT @@ -46,6 +46,13 @@ class MyObject ::cxx_qt::my_object::cxx_qt_MyObject::CxxQtConstructorArguments0&& args); explicit MyObject( ::cxx_qt::my_object::cxx_qt_MyObject::CxxQtConstructorArguments1&& args); + +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, diff --git a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h index a3cfd34be..6f61a6302 100644 --- a/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h +++ b/crates/cxx-qt-gen/test_outputs/passthrough_and_naming.h @@ -107,7 +107,7 @@ MyObject_readyConnect( namespace cxx_qt::multi_object { class MyObject : public QStringListModel - , public ::rust::cxxqt1::CxxQtType + , private ::rust::cxxqt1::CxxQtType { Q_OBJECT public: @@ -123,6 +123,13 @@ class MyObject Q_INVOKABLE void invokable_name() noexcept; Q_SIGNAL void ready(); explicit MyObject(QObject* parent = nullptr); + +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, @@ -151,7 +158,7 @@ SecondObject_readyConnect( namespace second_object { class SecondObject : public QObject - , public ::rust::cxxqt1::CxxQtType + , private ::rust::cxxqt1::CxxQtType { Q_OBJECT public: @@ -168,6 +175,13 @@ class SecondObject void myRenamedFunction(::std::int32_t param) const noexcept; Q_SIGNAL void ready(); explicit SecondObject(QObject* parent = nullptr); + +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, @@ -179,7 +193,7 @@ Q_DECLARE_METATYPE(second_object::SecondObject*) namespace my_namespace { class MyCxxName : public QObject - , public ::rust::cxxqt1::CxxQtType + , private ::rust::cxxqt1::CxxQtType { Q_OBJECT public: @@ -187,6 +201,13 @@ class MyCxxName public: explicit MyCxxName(QObject* parent = nullptr); + +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, diff --git a/crates/cxx-qt-gen/test_outputs/properties.h b/crates/cxx-qt-gen/test_outputs/properties.h index d64c13b73..044a4f8b7 100644 --- a/crates/cxx-qt-gen/test_outputs/properties.h +++ b/crates/cxx-qt-gen/test_outputs/properties.h @@ -117,7 +117,7 @@ MyObject_my_on_changedConnect( namespace cxx_qt::my_object { class MyObject : public QObject - , public ::rust::cxxqt1::CxxQtType + , private ::rust::cxxqt1::CxxQtType { Q_OBJECT public: @@ -182,6 +182,13 @@ class MyObject void myResetFn() noexcept; Q_SIGNAL void my_on_changed(); explicit MyObject(QObject* parent = nullptr); + +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, diff --git a/crates/cxx-qt-gen/test_outputs/qenum.h b/crates/cxx-qt-gen/test_outputs/qenum.h index f52d67353..fa2cc60c3 100644 --- a/crates/cxx-qt-gen/test_outputs/qenum.h +++ b/crates/cxx-qt-gen/test_outputs/qenum.h @@ -51,7 +51,7 @@ Q_ENUM_NS(MyOtherNamespacedEnum) namespace cxx_qt::my_object { class MyObject : public QObject - , public ::rust::cxxqt1::CxxQtType + , private ::rust::cxxqt1::CxxQtType { Q_OBJECT public: @@ -75,6 +75,13 @@ class MyObject cxx_qt::my_object::MyEnum qenum, my_namespace::MyOtherEnum other_qenum) const noexcept; explicit MyObject(QObject* parent = nullptr); + +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, @@ -94,7 +101,7 @@ Q_DECLARE_METATYPE(cxx_qt::my_object::MyObject*) namespace cxx_qt::my_object { class CxxName : public QObject - , public ::rust::cxxqt1::CxxQtType + , private ::rust::cxxqt1::CxxQtType { Q_OBJECT public: @@ -110,6 +117,13 @@ class CxxName public: explicit CxxName(QObject* parent = nullptr); + +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, diff --git a/crates/cxx-qt-gen/test_outputs/signals.h b/crates/cxx-qt-gen/test_outputs/signals.h index 450a86c85..8e9c65450 100644 --- a/crates/cxx-qt-gen/test_outputs/signals.h +++ b/crates/cxx-qt-gen/test_outputs/signals.h @@ -82,7 +82,7 @@ MyObject_newDataConnect( namespace cxx_qt::my_object { class MyObject : public QObject - , public ::rust::cxxqt1::CxxQtType + , private ::rust::cxxqt1::CxxQtType { Q_OBJECT public: @@ -96,6 +96,13 @@ class MyObject QPoint third, QPoint const& fourth); explicit MyObject(QObject* parent = nullptr); + +private: + template + friend Inner& ::rust::cxxqt1::unsafeRustMut(Outer& outer); + + template + friend const Inner& ::rust::cxxqt1::unsafeRust(const Outer& outer); }; static_assert(::std::is_base_of::value, diff --git a/crates/qt-build-utils/src/lib.rs b/crates/qt-build-utils/src/lib.rs index 847304649..1b9b016cc 100644 --- a/crates/qt-build-utils/src/lib.rs +++ b/crates/qt-build-utils/src/lib.rs @@ -34,7 +34,7 @@ mod platform; pub use platform::QtPlatformLinker; mod qml; -pub use qml::{QmlDirBuilder, QmlPluginCppBuilder, QmlUri}; +pub use qml::{QmlDirBuilder, QmlLsIniBuilder, QmlPluginCppBuilder, QmlUri}; mod qrc; pub use qrc::{QResource, QResourceFile, QResources}; @@ -65,6 +65,12 @@ pub struct QmlModuleRegistrationFiles { pub qmlcachegen: Vec, /// File generated by [qmltyperegistrar](https://www.qt.io/blog/qml-type-registration-in-qt-5.15) CLI tool. pub qmltyperegistrar: Option, + /// The .qmltypes file generated by [qmltyperegistrar](https://www.qt.io/blog/qml-type-registration-in-qt-5.15) CLI tool. + /// Mostly used for IDE support (e.g. qmllint/qmlls). + pub qmltypes: PathBuf, + /// qmldir file path. + /// Mostly used for better qmllint/qmlls support. + pub qmldir: PathBuf, /// File with generated [QQmlEngineExtensionPlugin](https://doc.qt.io/qt-6/qqmlengineextensionplugin.html) that calls the function generated by qmltyperegistrar. pub plugin: PathBuf, /// Initializer that automatically registers the QQmlExtensionPlugin at startup. @@ -154,6 +160,7 @@ impl QtBuild { plugin_name: &str, qml_files: &[impl AsRef], qrc_files: &[impl AsRef], + depends: impl IntoIterator>, ) -> QmlModuleRegistrationFiles { let qml_uri = QmlUri::new(uri.split('.')); let qml_uri_dirs = qml_uri.as_dirs(); @@ -175,6 +182,7 @@ impl QtBuild { { let mut file = File::create(&qmldir_file_path).expect("Could not create qmldir file"); QmlDirBuilder::new(qml_uri.clone()) + .depends(depends) .plugin(plugin_name, true) .class_name(&plugin_class_name) .type_info(plugin_type_info) @@ -232,7 +240,7 @@ impl QtBuild { if self.qt_installation.version().major >= 6 { let qml_cache_args = QmlCacheArguments { uri: uri.to_owned(), - qmldir_path: qmldir_file_path, + qmldir_path: qmldir_file_path.clone(), qmldir_qrc_path: qrc_path.clone(), }; let mut qml_resource_paths = Vec::new(); @@ -258,7 +266,7 @@ impl QtBuild { // Run qmltyperegistrar over the meta types let qmltyperegistrar_path = self.qmltyperegistrar().compile( metatypes_json, - qmltypes_path, + &qmltypes_path, uri, Version::new(version_major as u64, version_minor as u64, 0), ); @@ -300,6 +308,8 @@ Q_IMPORT_PLUGIN({plugin_class_name}); rcc: rcc.file.unwrap(), qmlcachegen: qmlcachegen_file_paths, qmltyperegistrar: qmltyperegistrar_path, + qmltypes: qmltypes_path.into(), + qmldir: qmldir_file_path.into(), plugin: qml_plugin_cpp_path, plugin_init, include_path, diff --git a/crates/qt-build-utils/src/qml/mod.rs b/crates/qt-build-utils/src/qml/mod.rs index 0992522e7..6e972e2e7 100644 --- a/crates/qt-build-utils/src/qml/mod.rs +++ b/crates/qt-build-utils/src/qml/mod.rs @@ -6,6 +6,9 @@ mod qmldir; pub use qmldir::QmlDirBuilder; +mod qmlls; +pub use qmlls::QmlLsIniBuilder; + mod qmlplugincpp; pub use qmlplugincpp::QmlPluginCppBuilder; diff --git a/crates/qt-build-utils/src/qml/qmldir.rs b/crates/qt-build-utils/src/qml/qmldir.rs index c32d252a5..29ae81eec 100644 --- a/crates/qt-build-utils/src/qml/qmldir.rs +++ b/crates/qt-build-utils/src/qml/qmldir.rs @@ -12,6 +12,7 @@ use std::io; /// A qmldir file is a plain-text file that contains the commands pub struct QmlDirBuilder { class_name: Option, + depends: Vec, plugin: Option<(bool, String)>, type_info: Option, uri: QmlUri, @@ -23,6 +24,7 @@ impl QmlDirBuilder { pub fn new(uri: QmlUri) -> Self { Self { class_name: None, + depends: vec![], plugin: None, type_info: None, uri, @@ -51,6 +53,10 @@ impl QmlDirBuilder { writeln!(writer, "typeinfo {file}")?; } + for depend in self.depends { + writeln!(writer, "depends {depend}")?; + } + // Prefer is always specified for now writeln!(writer, "prefer :/qt/qml/{}/", self.uri.as_dirs()) } @@ -68,6 +74,18 @@ impl QmlDirBuilder { self } + /// Declares that this module depends on another + pub fn depend(mut self, depend: impl Into) -> Self { + self.depends.push(depend.into()); + self + } + + /// Declares that this module depends on another + pub fn depends>(mut self, depends: impl IntoIterator) -> Self { + self.depends.extend(depends.into_iter().map(Into::into)); + self + } + /// Declares a plugin to be made available by the module. /// /// optional denotes that the plugin itself does not contain any relevant code @@ -105,7 +123,6 @@ impl QmlDirBuilder { // object type declaration // internal object type declaration // javascript resource definition - // module dependencies declaration // module import declaration // designer support declaration } @@ -119,6 +136,7 @@ mod test { let mut result = Vec::new(); QmlDirBuilder::new(QmlUri::new(["com", "kdab"])) .class_name("C") + .depends(["QtQuick", "com.kdab.a"]) .plugin("P", true) .type_info("T") .write(&mut result) @@ -129,6 +147,8 @@ mod test { optional plugin P classname C typeinfo T +depends QtQuick +depends com.kdab.a prefer :/qt/qml/com/kdab/ " ); diff --git a/crates/qt-build-utils/src/qml/qmlls.rs b/crates/qt-build-utils/src/qml/qmlls.rs new file mode 100644 index 000000000..8108c6096 --- /dev/null +++ b/crates/qt-build-utils/src/qml/qmlls.rs @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: 2025 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Andrew Hayzen +// +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use std::{ + io, + path::{Path, PathBuf}, +}; + +/// A helper for building QML Language Server configuration files +#[derive(Default)] +pub struct QmlLsIniBuilder { + build_dir: Option, + no_cmake_calls: Option, +} + +impl QmlLsIniBuilder { + /// Construct a [QmlLsIniBuilder] + pub fn new() -> Self { + Self::default() + } + + /// Use the given build_dir + pub fn build_dir(mut self, build_dir: impl AsRef) -> Self { + self.build_dir = Some(build_dir.as_ref().to_path_buf()); + self + } + + /// Enable or disable cmake calls + pub fn no_cmake_calls(mut self, no_cmake_calls: bool) -> Self { + self.no_cmake_calls = Some(no_cmake_calls); + self + } + + /// Write the resultant qmlls ini file contents + pub fn write(self, writer: &mut impl io::Write) -> io::Result<()> { + if self.build_dir.is_none() && self.no_cmake_calls.is_none() { + return Ok(()); + } + + writeln!(writer, "[General]")?; + + if let Some(build_dir) = self.build_dir { + writeln!( + writer, + "buildDir=\"{}\"", + build_dir.to_string_lossy().escape_default() + )?; + } + + if let Some(no_cmake_calls) = self.no_cmake_calls { + writeln!( + writer, + "no-cmake-calls={}", + if no_cmake_calls { "true" } else { "false" } + )?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn qmlls() { + let mut result = Vec::new(); + QmlLsIniBuilder::new() + .build_dir("/a/b/c") + .no_cmake_calls(true) + .write(&mut result) + .unwrap(); + assert_eq!( + String::from_utf8(result).unwrap(), + "[General] +buildDir=\"/a/b/c\" +no-cmake-calls=true +" + ); + } +} diff --git a/examples/demo_threading/CMakeLists.txt b/examples/demo_threading/CMakeLists.txt index 4438b6c1f..eb2a4322b 100644 --- a/examples/demo_threading/CMakeLists.txt +++ b/examples/demo_threading/CMakeLists.txt @@ -56,8 +56,10 @@ if(NOT CxxQt_FOUND) include(FetchContent) FetchContent_Declare( CxxQt - GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git - GIT_TAG main + # GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git + # GIT_TAG main + GIT_REPOSITORY https://github.com/ahayzen-kdab/cxx-qt-cmake.git + GIT_TAG c353833f2e88655bf264ae16e10fad645ec6a721 ) FetchContent_MakeAvailable(CxxQt) diff --git a/examples/qml_features/CMakeLists.txt b/examples/qml_features/CMakeLists.txt index 544c2393e..5dc23fdba 100644 --- a/examples/qml_features/CMakeLists.txt +++ b/examples/qml_features/CMakeLists.txt @@ -56,8 +56,10 @@ if(NOT CxxQt_FOUND) include(FetchContent) FetchContent_Declare( CxxQt - GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git - GIT_TAG main + # GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git + # GIT_TAG main + GIT_REPOSITORY https://github.com/ahayzen-kdab/cxx-qt-cmake.git + GIT_TAG c353833f2e88655bf264ae16e10fad645ec6a721 ) FetchContent_MakeAvailable(CxxQt) @@ -147,4 +149,12 @@ if(BUILD_TESTING) else() MESSAGE(STATUS "add_valgrind_test is defined in the top level of CXX-Qt. It will not executed") endif() + + find_program(QMLLINT_COMMAND qmllint PATHS "${QT_INSTALL_BINS}") + if("${QMLLINT_COMMAND}" STREQUAL "QMLLINT_COMMAND-NOTFOUND") + MESSAGE(STATUS "qmllint not found. Please install it") + else() + file(GLOB QMLLINT_QML_FILES ${CMAKE_CURRENT_SOURCE_DIR}/qml/**/*.qml) + add_test(NAME example_qml_features_qmllint_check COMMAND ${QMLLINT_COMMAND} --max-warnings 0 -I ${CMAKE_CURRENT_BINARY_DIR}/cxxqt/qml_modules ${QMLLINT_QML_FILES}) + endif() endif() diff --git a/examples/qml_features/qml/pages/ContainersPage.qml b/examples/qml_features/qml/pages/ContainersPage.qml index 311046cdc..c3bacc38f 100644 --- a/examples/qml_features/qml/pages/ContainersPage.qml +++ b/examples/qml_features/qml/pages/ContainersPage.qml @@ -9,6 +9,7 @@ import QtQuick.Layouts 1.12 import com.kdab.cxx_qt.demo 1.0 Page { + id: root header: ToolBar { RowLayout { anchors.fill: parent @@ -16,7 +17,7 @@ Page { ToolButton { text: qsTr("Reset") - onClicked: rustContainers.reset() + onClicked: root.rustContainers.reset() } Item { @@ -25,8 +26,7 @@ Page { } } - RustContainers { - id: rustContainers + readonly property RustContainers rustContainers: RustContainers { } ColumnLayout { @@ -54,14 +54,14 @@ Page { Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("QHash values: %1").arg(rustContainers.stringHash || "Empty") + text: qsTr("QHash values: %1").arg(root.rustContainers.stringHash || "Empty") wrapMode: Text.Wrap } Button { text: qsTr("Insert") - onClicked: rustContainers.insertHash("Key" + spinBox.value, spinBox.value) + onClicked: root.rustContainers.insertHash("Key" + spinBox.value, spinBox.value) } } @@ -69,14 +69,14 @@ Page { Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("QList values: %1").arg(rustContainers.stringList || "Empty") + text: qsTr("QList values: %1").arg(root.rustContainers.stringList || "Empty") wrapMode: Text.Wrap } Button { text: qsTr("Append") - onClicked: rustContainers.appendList(spinBox.value) + onClicked: root.rustContainers.appendList(spinBox.value) } } @@ -84,14 +84,14 @@ Page { Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("QMap values: %1").arg(rustContainers.stringMap || "Empty") + text: qsTr("QMap values: %1").arg(root.rustContainers.stringMap || "Empty") wrapMode: Text.Wrap } Button { text: qsTr("Insert") - onClicked: rustContainers.insertMap("Key" + spinBox.value, spinBox.value) + onClicked: root.rustContainers.insertMap("Key" + spinBox.value, spinBox.value) } } @@ -99,14 +99,14 @@ Page { Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("QSet values: %1").arg(rustContainers.stringSet || "Empty") + text: qsTr("QSet values: %1").arg(root.rustContainers.stringSet || "Empty") wrapMode: Text.Wrap } Button { text: qsTr("Insert") - onClicked: rustContainers.insertSet(spinBox.value) + onClicked: root.rustContainers.insertSet(spinBox.value) } } @@ -114,14 +114,14 @@ Page { Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("QVector values: %1").arg(rustContainers.stringVector || "Empty") + text: qsTr("QVector values: %1").arg(root.rustContainers.stringVector || "Empty") wrapMode: Text.Wrap } Button { text: qsTr("Append") - onClicked: rustContainers.appendVector(spinBox.value) + onClicked: root.rustContainers.appendVector(spinBox.value) } } } diff --git a/examples/qml_features/qml/pages/CustomBaseClassPage.qml b/examples/qml_features/qml/pages/CustomBaseClassPage.qml index 9e432a593..0e5a4839e 100644 --- a/examples/qml_features/qml/pages/CustomBaseClassPage.qml +++ b/examples/qml_features/qml/pages/CustomBaseClassPage.qml @@ -98,8 +98,12 @@ Page { id: customBaseClass } delegate: ItemDelegate { + required property int id + required property int index + required property double value + highlighted: ListView.isCurrentItem - text: model.id + ": " + model.value + text: id + ": " + value width: ListView.view.width onClicked: ListView.view.currentIndex = index @@ -118,8 +122,12 @@ Page { id: transitiveInheritance } delegate: ItemDelegate { + required property int id + required property int index + required property double value + highlighted: ListView.isCurrentItem - text: model.id + ": " + model.value + text: id + ": " + value width: ListView.view.width onClicked: ListView.view.currentIndex = index diff --git a/examples/qml_features/qml/pages/CustomParentClassPage.qml b/examples/qml_features/qml/pages/CustomParentClassPage.qml index 20efb4617..1c4692a02 100644 --- a/examples/qml_features/qml/pages/CustomParentClassPage.qml +++ b/examples/qml_features/qml/pages/CustomParentClassPage.qml @@ -37,20 +37,32 @@ Page { } } - ColumnLayout { anchors.left: parent.left anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter + // TODO: qmllint fails here as in the qmltypes CustomParentClass + // is missing a prototype of QQuickPaintedItem it is not yet clear why + // this is missed. + // + // qmltyperegistrar claims the following + // QQuickPaintedItem is used as base type but cannot be found. + // + // The type QQuickPaintedItem is not embedded in the moc JSON, + // does it come from elsewhere? + // + // qmllint disable incompatible-type Quick.attached-property-type CustomParentClass { id: customPainter color: "red" Layout.alignment: Qt.AlignHCenter - height: 200 - width: 200 + Layout.preferredHeight: 200 + Layout.preferredWidth: 200 } + // qmllint enable incompatible-type Quick.attached-property-type + Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter diff --git a/examples/qml_features/qml/pages/ExternCxxQtPage.qml b/examples/qml_features/qml/pages/ExternCxxQtPage.qml index d2de7f435..4fb1b369a 100644 --- a/examples/qml_features/qml/pages/ExternCxxQtPage.qml +++ b/examples/qml_features/qml/pages/ExternCxxQtPage.qml @@ -8,9 +8,13 @@ import QtQuick.Layouts 1.12 import QtQuick.Window 2.12 import com.kdab.cxx_qt.demo 1.0 +// C++ code is not declarative as it still supports Qt 5 +// qmllint disable import import com.kdab.cxx_qt.demo_cpp 1.0 +// qmllint enable import Page { + id: root property int amount: 5 header: ToolBar { @@ -20,7 +24,10 @@ Page { ToolButton { text: qsTr("Trigger") - onClicked: rustExternCxxQt.triggerOnExternal(externalQObject, amountSpinBox.value) +// C++ code is not declarative as it still supports Qt 5 +// qmllint disable unresolved-type + onClicked: root.rustExternCxxQt.triggerOnExternal(root.externalQObject, amountSpinBox.value) + // qmllint enable unresolved-type } Item { @@ -29,12 +36,13 @@ Page { } } - ExternalQObject { - id: externalQObject +// C++ code is not declarative as it still supports Qt 5 +// qmllint disable import unresolved-type + readonly property ExternalQObject externalQObject: ExternalQObject { } + // qmllint enable import unresolved-type - ExternalCxxQtHelper { - id: rustExternCxxQt + readonly property ExternalCxxQtHelper rustExternCxxQt: ExternalCxxQtHelper { } ColumnLayout { @@ -63,10 +71,13 @@ Page { Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("Count: %1").arg(rustExternCxxQt.count) + text: qsTr("Count: %1").arg(root.rustExternCxxQt.count) wrapMode: Text.Wrap } } - Component.onCompleted: rustExternCxxQt.connectToExternal(externalQObject) +// C++ code is not declarative as it still supports Qt 5 +// qmllint disable unresolved-type + Component.onCompleted: root.rustExternCxxQt.connectToExternal(root.externalQObject) + // qmllint enable unresolved-type } diff --git a/examples/qml_features/qml/pages/InvokablesPage.qml b/examples/qml_features/qml/pages/InvokablesPage.qml index 34707fec6..2f4577bbf 100644 --- a/examples/qml_features/qml/pages/InvokablesPage.qml +++ b/examples/qml_features/qml/pages/InvokablesPage.qml @@ -9,6 +9,7 @@ import QtQuick.Layouts 1.12 import com.kdab.cxx_qt.demo 1.0 Page { + id: root background: Rectangle { color: privateState.color } @@ -36,7 +37,7 @@ Page { onClicked: { timerSync.running = false; - rustInvokables.reset(); + root.rustInvokables.reset(); privateState.load(); } } @@ -44,18 +45,18 @@ Page { // ANCHOR: book_namespaced_qenum ToolButton { text: qsTr("Red") - onClicked: rustInvokables.storeColorWithEnum(Colors.Red); + onClicked: root.rustInvokables.storeColorWithEnum(Colors.Red); } // ANCHOR_END: book_namespaced_qenum ToolButton { text: qsTr("Green") - onClicked: rustInvokables.storeColorWithEnum(Colors.Green); + onClicked: root.rustInvokables.storeColorWithEnum(Colors.Green); } ToolButton { text: qsTr("Blue") - onClicked: rustInvokables.storeColorWithEnum(Colors.Blue); + onClicked: root.rustInvokables.storeColorWithEnum(Colors.Blue); } Item { @@ -64,8 +65,7 @@ Page { } } - RustInvokables { - id: rustInvokables + readonly property RustInvokables rustInvokables: RustInvokables { } QtObject { @@ -75,7 +75,7 @@ Page { property bool loaded: false function load() { - color = rustInvokables.loadColor(); + color = root.rustInvokables.loadColor(); } Component.onCompleted: { @@ -99,7 +99,7 @@ Page { if (!privateState.loaded) { return; } - rustInvokables.storeColor(sliderRed.value, sliderGreen.value, sliderBlue.value); + root.rustInvokables.storeColor(sliderRed.value, sliderGreen.value, sliderBlue.value); } Slider { diff --git a/examples/qml_features/qml/pages/MultipleQObjectsPage.qml b/examples/qml_features/qml/pages/MultipleQObjectsPage.qml index d4cee389c..d0355b420 100644 --- a/examples/qml_features/qml/pages/MultipleQObjectsPage.qml +++ b/examples/qml_features/qml/pages/MultipleQObjectsPage.qml @@ -9,6 +9,7 @@ import QtQuick.Layouts 1.12 import com.kdab.cxx_qt.demo 1.0 Page { + id: root header: ToolBar { RowLayout { anchors.fill: parent @@ -16,13 +17,13 @@ Page { ToolButton { text: qsTr("Increment First") - onClicked: first.increment() + onClicked: root.first.increment() } ToolButton { text: qsTr("Increment Second") - onClicked: second.increment() + onClicked: root.second.increment() } Item { @@ -31,16 +32,12 @@ Page { } } - FirstObject { - id: first - + readonly property FirstObject first: FirstObject { onAccepted: console.warn("First Accepted") onRejected: console.warn("First Rejected") } - SecondObject { - id: second - + readonly property SecondObject second: SecondObject { onAccepted: console.warn("Second Accepted") onRejected: console.warn("Second Rejected") } @@ -58,17 +55,17 @@ Page { } Label { - color: first.color + color: root.first.color Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("First Object Count: %1, color: %2").arg(first.counter).arg(first.color) + text: qsTr("First Object Count: %1, color: %2").arg(root.first.counter).arg(root.first.color) wrapMode: Text.Wrap } Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("Second Object Count: %1, URL: %2").arg(second.counter).arg(second.url) + text: qsTr("Second Object Count: %1, URL: %2").arg(root.second.counter).arg(root.second.url) wrapMode: Text.Wrap } } diff --git a/examples/qml_features/qml/pages/NamingPage.qml b/examples/qml_features/qml/pages/NamingPage.qml index 5b3a2d27f..4556dc60c 100644 --- a/examples/qml_features/qml/pages/NamingPage.qml +++ b/examples/qml_features/qml/pages/NamingPage.qml @@ -9,8 +9,8 @@ import QtQuick.Layouts 1.12 import com.kdab.cxx_qt.demo 1.0 Page { - RenamedObject { - id: renamedObject + id: root + readonly property RenamedObject renamedObject: RenamedObject { numberProp: 1 } @@ -22,20 +22,20 @@ Page { Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("Counter: %1").arg(renamedObject.numberProp) + text: qsTr("Counter: %1").arg(root.renamedObject.numberProp) wrapMode: Text.Wrap } Button { text: qsTr("Increment Counter") - onClicked: renamedObject.increment() + onClicked: root.renamedObject.increment() } Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("Meaning of life: %1").arg(renamedObject.getNum()) + text: qsTr("Meaning of life: %1").arg(root.renamedObject.getNum()) wrapMode: Text.Wrap } } -} \ No newline at end of file +} diff --git a/examples/qml_features/qml/pages/NestedQObjectsPage.qml b/examples/qml_features/qml/pages/NestedQObjectsPage.qml index a67ec62c6..d590cf2c6 100644 --- a/examples/qml_features/qml/pages/NestedQObjectsPage.qml +++ b/examples/qml_features/qml/pages/NestedQObjectsPage.qml @@ -9,6 +9,7 @@ import QtQuick.Layouts 1.12 import com.kdab.cxx_qt.demo 1.0 Page { + id: root header: ToolBar { RowLayout { anchors.fill: parent @@ -16,20 +17,20 @@ Page { ToolButton { text: qsTr("Increment") - onClicked: outerObject.inner.counter += 1 + onClicked: root.outerObject.inner.counter += 1 } ToolButton { text: qsTr("Reset") - onClicked: outerObject.reset() + onClicked: root.outerObject.reset() } ToolButton { text: qsTr("Print") - onClicked: outerObject.printCount(innerObject) + onClicked: root.outerObject.printCount(root.innerObject) } Item { @@ -38,22 +39,20 @@ Page { } } - InnerObject { - id: innerObject + readonly property InnerObject innerObject: InnerObject { counter: 10 onCalled: () => console.warn("Inner signal called") } - OuterObject { - id: outerObject - inner: innerObject + readonly property OuterObject outerObject: OuterObject { + inner: root.innerObject onCalled: (inner) => console.warn("Signal called, inner value: ", inner.counter) } Label { anchors.centerIn: parent - text: innerObject.counter + text: root.innerObject.counter } } diff --git a/examples/qml_features/qml/pages/PropertiesPage.qml b/examples/qml_features/qml/pages/PropertiesPage.qml index d7592d3e1..544d46869 100644 --- a/examples/qml_features/qml/pages/PropertiesPage.qml +++ b/examples/qml_features/qml/pages/PropertiesPage.qml @@ -9,22 +9,23 @@ import QtQuick.Layouts 1.12 import com.kdab.cxx_qt.demo 1.0 Page { + id: root header: ToolBar { RowLayout { anchors.fill: parent ToolButton { - enabled: rustProperties.connected + enabled: root.rustProperties.connected text: qsTr("Disconnect") - onClicked: rustProperties.connectedUrl = undefined + onClicked: root.rustProperties.connectedUrl = undefined } ToolButton { - enabled: !rustProperties.connected + enabled: !root.rustProperties.connected text: qsTr("Connect") - onClicked: rustProperties.connectedUrl = urlTextField.text + onClicked: root.rustProperties.connectedUrl = urlTextField.text } Item { @@ -33,8 +34,7 @@ Page { } } - RustProperties { - id: rustProperties + readonly property RustProperties rustProperties: RustProperties { } ColumnLayout { @@ -67,7 +67,7 @@ Page { id: statusLabel Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: rustProperties.connected ? qsTr("%1: %2").arg(rustProperties.statusMessage).arg(rustProperties.connectedUrl) : rustProperties.statusMessage + text: root.rustProperties.connected ? qsTr("%1: %2").arg(root.rustProperties.statusMessage).arg(root.rustProperties.connectedUrl) : root.rustProperties.statusMessage wrapMode: Text.Wrap } } diff --git a/examples/qml_features/qml/pages/SerialisationPage.qml b/examples/qml_features/qml/pages/SerialisationPage.qml index 40482fd2b..f5b340b44 100644 --- a/examples/qml_features/qml/pages/SerialisationPage.qml +++ b/examples/qml_features/qml/pages/SerialisationPage.qml @@ -9,6 +9,7 @@ import QtQuick.Layouts 1.12 import com.kdab.cxx_qt.demo 1.0 Page { + id: root header: ToolBar { RowLayout { anchors.fill: parent @@ -18,9 +19,9 @@ Page { onClicked: { lastErrorLabel.errorMessage = ""; - serialisation.number = numberSpinBox.value; - serialisation.string = stringTextField.text; - jsonTextField.text = serialisation.asJsonStr(); + root.serialisation.number = numberSpinBox.value; + root.serialisation.string = stringTextField.text; + jsonTextField.text = root.serialisation.asJsonStr(); } } @@ -29,7 +30,7 @@ Page { onClicked: { lastErrorLabel.errorMessage = ""; - serialisation.fromJsonStr(jsonTextField.text); + root.serialisation.fromJsonStr(jsonTextField.text); } } @@ -39,8 +40,10 @@ Page { } } - Serialisation { - id: serialisation + readonly property Serialisation serialisation: Serialisation { + onError: (message) => { + lastErrorLabel.errorMessage = message; + } } GridLayout { @@ -60,7 +63,7 @@ Page { Binding { target: numberSpinBox property: "value" - value: serialisation.number + value: root.serialisation.number } } @@ -76,7 +79,7 @@ Page { Binding { target: stringTextField property: "text" - value: serialisation.string + value: root.serialisation.string } } @@ -101,14 +104,6 @@ Page { Layout.columnSpan: 2 text: errorMessage !== "" ? qsTr("Error: %1").arg(errorMessage) : "" wrapMode: Text.Wrap - - Connections { - target: serialisation - - function onError(message) { - lastErrorLabel.errorMessage = message; - } - } } } } diff --git a/examples/qml_features/qml/pages/SignalsPage.qml b/examples/qml_features/qml/pages/SignalsPage.qml index 9c82a9409..6e1a7c288 100644 --- a/examples/qml_features/qml/pages/SignalsPage.qml +++ b/examples/qml_features/qml/pages/SignalsPage.qml @@ -10,6 +10,7 @@ import QtQuick.Window 2.12 import com.kdab.cxx_qt.demo 1.0 Page { + id: root header: ToolBar { RowLayout { anchors.fill: parent @@ -17,21 +18,21 @@ Page { ToolButton { text: qsTr("Disconnect") - onClicked: rustSignals.disconnect() + onClicked: root.rustSignals.disconnect() } ToolButton { text: qsTr("Connect") - onClicked: rustSignals.connect(urlTextField.text) + onClicked: root.rustSignals.connect(urlTextField.text) } ToolButton { checkable: true - checked: rustSignals.logging_enabled + checked: root.rustSignals.logging_enabled text: qsTr("Toggle Logging") - onClicked: rustSignals.logging_enabled = !rustSignals.logging_enabled + onClicked: root.rustSignals.logging_enabled = !root.rustSignals.logging_enabled } Item { @@ -40,8 +41,18 @@ Page { } } - RustSignals { - id: rustSignals + readonly property RustSignals rustSignals: RustSignals { + onConnected: url => { + statusLabel.text = qsTr("Connected: %1").arg(url); + } + + onDisconnected: { + statusLabel.text = qsTr("Disconnected"); + } + + onError: message => { + statusLabel.text = qsTr("Error: %1").arg(message); + } } ColumnLayout { @@ -75,22 +86,6 @@ Page { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter wrapMode: Text.Wrap - - Connections { - target: rustSignals - - function onConnected(url) { - statusLabel.text = qsTr("Connected: %1").arg(url); - } - - function onDisconnected() { - statusLabel.text = qsTr("Disconnected"); - } - - function onError(message) { - statusLabel.text = qsTr("Error: %1").arg(message); - } - } } } } diff --git a/examples/qml_features/qml/pages/ThreadingPage.qml b/examples/qml_features/qml/pages/ThreadingPage.qml index 9be51fcb3..3b41a42c0 100644 --- a/examples/qml_features/qml/pages/ThreadingPage.qml +++ b/examples/qml_features/qml/pages/ThreadingPage.qml @@ -9,6 +9,7 @@ import QtQuick.Layouts 1.12 import com.kdab.cxx_qt.demo 1.0 Page { + id: root header: ToolBar { RowLayout { anchors.fill: parent @@ -16,13 +17,13 @@ Page { ToolButton { text: qsTr("Change Url") - onClicked: website.changeUrl() + onClicked: root.website.changeUrl() } ToolButton { text: qsTr("Fetch Title") - onClicked: website.fetchTitle() + onClicked: root.website.fetchTitle() } Item { @@ -31,8 +32,7 @@ Page { } } - ThreadingWebsite { - id: website + readonly property ThreadingWebsite website: ThreadingWebsite { } ColumnLayout { @@ -43,14 +43,14 @@ Page { Label { horizontalAlignment: Text.AlignHCenter Layout.fillWidth: true - text: qsTr("Url: %1").arg(website.url) + text: qsTr("Url: %1").arg(root.website.url) wrapMode: Text.Wrap } Label { horizontalAlignment: Text.AlignHCenter Layout.fillWidth: true - text: qsTr("Title: %1").arg(website.title) + text: qsTr("Title: %1").arg(root.website.title) wrapMode: Text.Wrap } } diff --git a/examples/qml_features/qml/pages/TypesPage.qml b/examples/qml_features/qml/pages/TypesPage.qml index 904fab719..d48979b28 100644 --- a/examples/qml_features/qml/pages/TypesPage.qml +++ b/examples/qml_features/qml/pages/TypesPage.qml @@ -7,9 +7,14 @@ import QtQuick.Controls 2.12 import QtQuick.Layouts 1.12 import com.kdab.cxx_qt.demo 1.0 +// C++ code is not declarative as it still supports Qt 5 +// qmllint disable import import com.kdab.cxx_qt.demo_cpp 1.0 +// qmllint enable import + Page { + id: root header: ToolBar { RowLayout { anchors.fill: parent @@ -17,40 +22,43 @@ Page { ToolButton { text: qsTr("Toggle Boolean") - onClicked: types.toggleBoolean() + onClicked: root.types.toggleBoolean() } ToolButton { text: qsTr("Load from Variant") property int counter: 0 - property var booleanVariant: types.boolean - property var pointVariant: types.point - property url url: types.url + property var booleanVariant: root.types.boolean + property var pointVariant: root.types.point + property url url: root.types.url + // C++ code is not declarative as it still supports Qt 5 + // qmllint disable import missing-property unresolved-type property CustomObject customObject: CustomObject { value: 0 } + // qmllint enable import missing-property unresolved-type readonly property var urlVariant: url onClicked: { - types.loadFromVariant((() => { - switch (counter) { + root.types.loadFromVariant((() => { + switch (counter) { case 0: - booleanVariant = !types.boolean; + booleanVariant = !root.types.boolean; return booleanVariant; case 1: - pointVariant = Qt.point(types.point.x + 1, types.point.y + 1); + pointVariant = Qt.point(root.types.point.x + 1, root.types.point.y + 1); return pointVariant; case 2: - url = types.url == "https://kdab.com" ? "https://github.com/kdab/cxx-qt" : "https://kdab.com" + url = root.types.url == "https://kdab.com" ? "https://github.com/kdab/cxx-qt" : "https://kdab.com"; return urlVariant; case 3: customObject.value += 1; return customObject.asStruct(); default: return null; - } - })()); + } + })()); counter = (counter + 1) % 4; } @@ -62,9 +70,7 @@ Page { } } - Types { - id: types - } + readonly property Types types: Types {} ColumnLayout { anchors.left: parent.left @@ -74,28 +80,28 @@ Page { Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("Boolean: %1").arg(types.boolean) + text: qsTr("Boolean: %1").arg(root.types.boolean) wrapMode: Text.Wrap } Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("QPoint x: %1, y: %2").arg(types.point.x).arg(types.point.y) + text: qsTr("QPoint x: %1, y: %2").arg(root.types.point.x).arg(root.types.point.y) wrapMode: Text.Wrap } Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("QUrl: %1").arg(types.url) + text: qsTr("QUrl: %1").arg(root.types.url) wrapMode: Text.Wrap } Label { Layout.fillWidth: true horizontalAlignment: Text.AlignHCenter - text: qsTr("CustomValue: %1").arg(types.customValue) + text: qsTr("CustomValue: %1").arg(root.types.customValue) wrapMode: Text.Wrap } } diff --git a/examples/qml_features/rust/build.rs b/examples/qml_features/rust/build.rs index 0853d6742..e224bb0fa 100644 --- a/examples/qml_features/rust/build.rs +++ b/examples/qml_features/rust/build.rs @@ -28,6 +28,8 @@ fn main() { "../qml/pages/ThreadingPage.qml", "../qml/pages/TypesPage.qml", ], + // Need to depend on QtQuick for QColor to work with qmllint/qmlls + depends: &["QtQuick"], ..Default::default() }) .files([ diff --git a/examples/qml_minimal/CMakeLists.txt b/examples/qml_minimal/CMakeLists.txt index c476100be..adf7da46d 100644 --- a/examples/qml_minimal/CMakeLists.txt +++ b/examples/qml_minimal/CMakeLists.txt @@ -65,10 +65,12 @@ if(NOT CxxQt_FOUND) include(FetchContent) FetchContent_Declare( CxxQt - GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git + # GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git # ANCHOR_END: book_cmake_find_cxx_qt_start - GIT_TAG main + # GIT_TAG main # ANCHOR: book_cmake_find_cxx_qt_end + GIT_REPOSITORY https://github.com/ahayzen-kdab/cxx-qt-cmake.git + GIT_TAG c353833f2e88655bf264ae16e10fad645ec6a721 ) FetchContent_MakeAvailable(CxxQt) @@ -162,6 +164,14 @@ if(BUILD_TESTING) else() MESSAGE(STATUS "add_valgrind_test is defined in the top level of CXX-Qt. It will not executed") endif() + + find_program(QMLLINT_COMMAND qmllint PATHS "${QT_INSTALL_BINS}") + if("${QMLLINT_COMMAND}" STREQUAL "QMLLINT_COMMAND-NOTFOUND") + MESSAGE(STATUS "qmllint not found. Please install it") + else() + file(GLOB QMLLINT_QML_FILES ${CMAKE_CURRENT_SOURCE_DIR}/qml/*.qml) + add_test(NAME example_qml_minimal_qmllint_check COMMAND ${QMLLINT_COMMAND} --max-warnings 0 -I ${CMAKE_CURRENT_BINARY_DIR}/cxxqt/qml_modules ${QMLLINT_QML_FILES}) + endif() endfunction() add_qml_test(myobject) diff --git a/examples/qml_minimal/qml/main.qml b/examples/qml_minimal/qml/main.qml index 0b32505e4..293c7db8e 100644 --- a/examples/qml_minimal/qml/main.qml +++ b/examples/qml_minimal/qml/main.qml @@ -16,16 +16,16 @@ import com.kdab.cxx_qt.demo 1.0 // ANCHOR_END: book_qml_import ApplicationWindow { + id: root height: 480 title: qsTr("Hello World") visible: true width: 640 color: palette.window - MyObject { - id: myObject + readonly property MyObject myObject: MyObject { number: 1 - string: qsTr("My String with my number: %1").arg(myObject.number) + string: qsTr("My String with my number: %1").arg(number) } Column { @@ -34,25 +34,25 @@ ApplicationWindow { spacing: 10 Label { - text: qsTr("Number: %1").arg(myObject.number) + text: qsTr("Number: %1").arg(root.myObject.number) color: palette.text } Label { - text: qsTr("String: %1").arg(myObject.string) + text: qsTr("String: %1").arg(root.myObject.string) color: palette.text } Button { text: qsTr("Increment Number") - onClicked: myObject.incrementNumber() + onClicked: root.myObject.incrementNumber() } Button { text: qsTr("Say Hi!") - onClicked: myObject.sayHi(myObject.string, myObject.number) + onClicked: root.myObject.sayHi(root.myObject.string, root.myObject.number) } Button { diff --git a/examples/qml_multi_crates/CMakeLists.txt b/examples/qml_multi_crates/CMakeLists.txt index af5980087..bbe31faf9 100644 --- a/examples/qml_multi_crates/CMakeLists.txt +++ b/examples/qml_multi_crates/CMakeLists.txt @@ -55,8 +55,10 @@ if(NOT CxxQt_FOUND) include(FetchContent) FetchContent_Declare( CxxQt - GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git - GIT_TAG main + # GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git + # GIT_TAG main + GIT_REPOSITORY https://github.com/ahayzen-kdab/cxx-qt-cmake.git + GIT_TAG c353833f2e88655bf264ae16e10fad645ec6a721 ) FetchContent_MakeAvailable(CxxQt) diff --git a/tests/basic_cxx_only/CMakeLists.txt b/tests/basic_cxx_only/CMakeLists.txt index b21447dde..d3be061df 100644 --- a/tests/basic_cxx_only/CMakeLists.txt +++ b/tests/basic_cxx_only/CMakeLists.txt @@ -46,8 +46,10 @@ if(NOT CxxQt_FOUND) include(FetchContent) FetchContent_Declare( CxxQt - GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git - GIT_TAG main + # GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git + # GIT_TAG main + GIT_REPOSITORY https://github.com/ahayzen-kdab/cxx-qt-cmake.git + GIT_TAG c353833f2e88655bf264ae16e10fad645ec6a721 ) FetchContent_MakeAvailable(CxxQt) diff --git a/tests/basic_cxx_qt/CMakeLists.txt b/tests/basic_cxx_qt/CMakeLists.txt index 667d652f6..80aedf50c 100644 --- a/tests/basic_cxx_qt/CMakeLists.txt +++ b/tests/basic_cxx_qt/CMakeLists.txt @@ -46,8 +46,10 @@ if(NOT CxxQt_FOUND) include(FetchContent) FetchContent_Declare( CxxQt - GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git - GIT_TAG main + # GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git + # GIT_TAG main + GIT_REPOSITORY https://github.com/ahayzen-kdab/cxx-qt-cmake.git + GIT_TAG c353833f2e88655bf264ae16e10fad645ec6a721 ) FetchContent_MakeAvailable(CxxQt) diff --git a/tests/qt_types_standalone/CMakeLists.txt b/tests/qt_types_standalone/CMakeLists.txt index 2ccb78a40..2ab9a32e4 100644 --- a/tests/qt_types_standalone/CMakeLists.txt +++ b/tests/qt_types_standalone/CMakeLists.txt @@ -46,8 +46,10 @@ if(NOT CxxQt_FOUND) include(FetchContent) FetchContent_Declare( CxxQt - GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git - GIT_TAG main + # GIT_REPOSITORY https://github.com/kdab/cxx-qt-cmake.git + # GIT_TAG main + GIT_REPOSITORY https://github.com/ahayzen-kdab/cxx-qt-cmake.git + GIT_TAG c353833f2e88655bf264ae16e10fad645ec6a721 ) FetchContent_MakeAvailable(CxxQt)