From ef83177125eb393324965baa6f144de15a0bc382 Mon Sep 17 00:00:00 2001 From: Andrew Hayzen Date: Mon, 12 Jun 2023 15:38:30 +0100 Subject: [PATCH] cxx-qt-gen: use #[qproperty] on the struct instead of field This is a stepping stone to moving qproperty to be defined on the type in the extern "RustQt" block. Related to #559 --- CHANGELOG.md | 1 + .../src/getting-started/1-qobjects-in-rust.md | 2 +- .../2-our-first-cxx-qt-module.md | 4 +- book/src/qobject/qobject_struct.md | 6 +- .../src/generator/cpp/property/mod.rs | 3 - .../src/generator/naming/property.rs | 1 - .../src/generator/rust/property/mod.rs | 3 - crates/cxx-qt-gen/src/parser/property.rs | 78 +++++++++++- crates/cxx-qt-gen/src/parser/qobject.rs | 47 +++---- crates/cxx-qt-gen/src/syntax/fields.rs | 117 ------------------ crates/cxx-qt-gen/src/syntax/mod.rs | 1 - .../test_inputs/passthrough_and_naming.rs | 4 +- crates/cxx-qt-gen/test_inputs/properties.rs | 4 +- examples/demo_threading/rust/src/lib.rs | 6 +- .../plugin/rust/src/lib.rs | 4 +- examples/qml_features/rust/src/containers.rs | 14 +-- .../rust/src/multiple_qobjects.rs | 8 +- .../qml_features/rust/src/nested_qobjects.rs | 4 +- examples/qml_features/rust/src/properties.rs | 8 +- .../qml_features/rust/src/serialisation.rs | 4 +- examples/qml_features/rust/src/signals.rs | 2 +- examples/qml_features/rust/src/singleton.rs | 2 +- examples/qml_features/rust/src/threading.rs | 4 +- examples/qml_features/rust/src/types.rs | 8 +- examples/qml_features/rust/src/uncreatable.rs | 2 +- examples/qml_minimal/rust/src/cxxqt_object.rs | 4 +- tests/basic_cxx_qt/rust/src/data.rs | 4 +- tests/basic_cxx_qt/rust/src/lib.rs | 4 +- tests/basic_cxx_qt/rust/src/types.rs | 18 +-- 29 files changed, 149 insertions(+), 218 deletions(-) delete mode 100644 crates/cxx-qt-gen/src/syntax/fields.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index a9be4b509..5edebf7a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `#[cxx_qt::qsignals]` and `#[cxx_qt::inherit]` are now used in an `extern "RustQt"` block as `#[qsignal]` and `#[inherit]` - `#[qinvokable]` is now defined as a signature in `extern "RustQt"` - `rust_mut` is now safe to call +- `#[qproperty]` is now defined as an attribute on the qobject rather than the field ### Removed diff --git a/book/src/getting-started/1-qobjects-in-rust.md b/book/src/getting-started/1-qobjects-in-rust.md index 41554a27a..3fddf4d99 100644 --- a/book/src/getting-started/1-qobjects-in-rust.md +++ b/book/src/getting-started/1-qobjects-in-rust.md @@ -71,7 +71,7 @@ Typically this will be instantiated by QML and the lifetime will be directly ass The generated QObject subclass will then defer to the Rust struct for any behavior, which is then defined in Rust. For example, using the `#[qinvokable]` attribute, we can define functions that will be exposed to C++, but will execute Rust code. -Also, any fields in the Rust struct marked with `#[qproperty]` will be exposed to Qt as `Q_PROPERTY` fields. +Also, any fields in the Rust struct can be exposed to Qt as `Q_PROPERTY` fields by using the `#[qproperty(T, NAME)]` attribute on the struct. Therefore allowing you to assign them from QML as well. But enough theory for now, lets jump in and write [our first CXX-Qt module](./2-our-first-cxx-qt-module.md). diff --git a/book/src/getting-started/2-our-first-cxx-qt-module.md b/book/src/getting-started/2-our-first-cxx-qt-module.md index a36fb3d0f..cd970ab87 100644 --- a/book/src/getting-started/2-our-first-cxx-qt-module.md +++ b/book/src/getting-started/2-our-first-cxx-qt-module.md @@ -69,13 +69,13 @@ Additionally, we need to either `impl Default` or `#[derive(Default)]` for our s ``` The Rust struct can be defined just like a normal Rust struct and can contain any kind of field, even Rust-only types. -If a field is marked as `#[qproperty]` it will be exposed to the C++ side as a `Q_PROPERTY`. +If a field is tagged as `#[qproperty]` it will be exposed to the C++ side as a `Q_PROPERTY`. That means the newly created QObject subclass will have two properties as members: `number` and `string`. For names that contain multiple words, like `my_number`, CXX-Qt will automatically rename the field from snake_case to camelCase to fit with C++/QML naming conventions (e.g. `myNumber`). ### Types -Do note though that any fields marked as `#[qproperty]` must be types that CXX can translate to C++ types. +Do note though that any fields tagged as `#[qproperty]` must be types that CXX can translate to C++ types. In our case that means: - `number: i32` -> `::std::int32_t number` - `string: QString` -> `QString string` diff --git a/book/src/qobject/qobject_struct.md b/book/src/qobject/qobject_struct.md index 643d469b3..6eba2ca75 100644 --- a/book/src/qobject/qobject_struct.md +++ b/book/src/qobject/qobject_struct.md @@ -26,7 +26,7 @@ The macro does multiple other things for you though: - Generate a C++ QObject subclass that wraps the `MyObject` Rust struct. - Expose the generated QObject subclass to Rust as [`qobject::MyObject`](./generated-qobject.md) - Generate getters/setters for all fields. -- Generate `Q_PROPERTY`s for all fields that are marked as `#[qproperty]`. +- Generate `Q_PROPERTY`s for all fields that are tagged as `#[qproperty]`. - Generate signals if paired with a [`#[qsignal]` macro](./signals.md). ## Exposing to QML @@ -71,7 +71,7 @@ Fields within the `#[cxx_qt::qobject]` marked struct can be tagged with `#[qprop {{#include ../../../examples/qml_features/rust/src/properties.rs:book_properties_struct}} ``` -Any type that CXX supports may be marked as a `#[qproperty]`. +Any type that CXX supports may be tagged as a `#[qproperty]`. See the [Types page](../concepts/types.md) for a list of supported types. For every `#[qproperty]`, CXX-Qt will generate setters and getters, as well as a "changed" signal. @@ -90,7 +90,7 @@ where `` is the name of the property. These setters and getters assure that the changed signal is emitted every time the property is edited. -Any field that's not marked as `#[qproperty]` won't be accessible from C++, but it will be accessible from Rust. +Any field that's not tagged as `#[qproperty]` won't be accessible from C++, but it will be accessible from Rust. See the [Private fields section](#private-methods-and-fields) ## Default diff --git a/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs b/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs index 2ef2f7995..ca68e03fd 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/property/mod.rs @@ -73,12 +73,10 @@ mod tests { ParsedQProperty { ident: format_ident!("trivial_property"), ty: parse_quote! { i32 }, - vis: syn::Visibility::Inherited, }, ParsedQProperty { ident: format_ident!("opaque_property"), ty: parse_quote! { UniquePtr }, - vis: syn::Visibility::Inherited, }, ]; let qobject_idents = create_qobjectname(); @@ -251,7 +249,6 @@ mod tests { let properties = vec![ParsedQProperty { ident: format_ident!("mapped_property"), ty: parse_quote! { A1 }, - vis: syn::Visibility::Inherited, }]; let qobject_idents = create_qobjectname(); diff --git a/crates/cxx-qt-gen/src/generator/naming/property.rs b/crates/cxx-qt-gen/src/generator/naming/property.rs index 9f88882f7..96dc847cd 100644 --- a/crates/cxx-qt-gen/src/generator/naming/property.rs +++ b/crates/cxx-qt-gen/src/generator/naming/property.rs @@ -80,7 +80,6 @@ pub mod tests { let property = ParsedQProperty { ident: format_ident!("my_property"), ty, - vis: syn::Visibility::Inherited, }; QPropertyName::from(&property) } diff --git a/crates/cxx-qt-gen/src/generator/rust/property/mod.rs b/crates/cxx-qt-gen/src/generator/rust/property/mod.rs index 360a5da3f..155c0f21f 100644 --- a/crates/cxx-qt-gen/src/generator/rust/property/mod.rs +++ b/crates/cxx-qt-gen/src/generator/rust/property/mod.rs @@ -69,17 +69,14 @@ mod tests { ParsedQProperty { ident: format_ident!("trivial_property"), ty: parse_quote! { i32 }, - vis: syn::Visibility::Inherited, }, ParsedQProperty { ident: format_ident!("opaque_property"), ty: parse_quote! { UniquePtr }, - vis: parse_quote! { pub }, }, ParsedQProperty { ident: format_ident!("unsafe_property"), ty: parse_quote! { *mut T }, - vis: syn::Visibility::Inherited, }, ]; let qobject_idents = create_qobjectname(); diff --git a/crates/cxx-qt-gen/src/parser/property.rs b/crates/cxx-qt-gen/src/parser/property.rs index c66d1b97a..93d4daf67 100644 --- a/crates/cxx-qt-gen/src/parser/property.rs +++ b/crates/cxx-qt-gen/src/parser/property.rs @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 -use syn::{Ident, Type, Visibility}; +use syn::{parse::ParseStream, Attribute, Ident, Result, Token, Type}; /// Describes a single Q_PROPERTY for a struct pub struct ParsedQProperty { @@ -11,6 +11,78 @@ pub struct ParsedQProperty { pub ident: Ident, /// The [syn::Type] of the property pub ty: Type, - /// The [syn::Visibility] of the property - pub vis: Visibility, +} + +impl ParsedQProperty { + pub fn parse(attr: Attribute) -> Result { + attr.parse_args_with(|input: ParseStream| -> Result { + let ty = input.parse()?; + let _comma = input.parse::()?; + let ident = input.parse()?; + + // TODO: later we'll need to parse setters and getters here + // which are key-value, hence this not being parsed as a list + + Ok(Self { ident, ty }) + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use quote::format_ident; + use syn::{parse_quote, ItemStruct}; + + #[test] + fn test_parse_property() { + let mut input: ItemStruct = parse_quote! { + #[qproperty(T, name)] + struct MyStruct; + }; + let property = ParsedQProperty::parse(input.attrs.remove(0)).unwrap(); + assert_eq!(property.ident, format_ident!("name")); + assert_eq!(property.ty, parse_quote! { T }); + } + + #[test] + fn test_parse_property_arg_extra() { + let mut input: ItemStruct = parse_quote! { + #[qproperty(T, name, A = B)] + struct MyStruct; + }; + let property = ParsedQProperty::parse(input.attrs.remove(0)); + assert!(property.is_err()); + } + + #[test] + fn test_parse_property_arg_wrong() { + let mut input: ItemStruct = parse_quote! { + #[qproperty(A = B, name)] + struct MyStruct; + }; + let property = ParsedQProperty::parse(input.attrs.remove(0)); + assert!(property.is_err()); + } + + #[test] + fn test_parse_property_no_name() { + let mut input: ItemStruct = parse_quote! { + #[qproperty(T)] + struct MyStruct; + }; + let property = ParsedQProperty::parse(input.attrs.remove(0)); + assert!(property.is_err()); + } + + #[test] + fn test_parse_property_no_type() { + let mut input: ItemStruct = parse_quote! { + #[qproperty(T)] + struct MyStruct; + }; + let property = ParsedQProperty::parse(input.attrs.remove(0)); + assert!(property.is_err()); + } } diff --git a/crates/cxx-qt-gen/src/parser/qobject.rs b/crates/cxx-qt-gen/src/parser/qobject.rs index 0d8fee6ef..e2cd8eb27 100644 --- a/crates/cxx-qt-gen/src/parser/qobject.rs +++ b/crates/cxx-qt-gen/src/parser/qobject.rs @@ -3,10 +3,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::syntax::{ - attribute::{attribute_find_path, attribute_tokens_to_map, AttributeDefault}, - fields::fields_to_named_fields_mut, -}; +use crate::syntax::attribute::{attribute_find_path, attribute_tokens_to_map, AttributeDefault}; use crate::{ parser::{ inherit::ParsedInheritedMethod, invokable::ParsedQInvokable, property::ParsedQProperty, @@ -15,8 +12,8 @@ use crate::{ syntax::path::path_compare_str, }; use syn::{ - spanned::Spanned, Error, Fields, Ident, ImplItem, Item, ItemImpl, ItemStruct, LitStr, Result, - Visibility, + spanned::Spanned, Attribute, Error, Ident, ImplItem, Item, ItemImpl, ItemStruct, LitStr, + Result, Visibility, }; /// Metadata for registering QML element @@ -95,7 +92,7 @@ impl ParsedQObject { // Parse any properties in the struct // and remove the #[qproperty] attribute - let properties = Self::parse_struct_fields(&mut qobject_struct.fields)?; + let properties = Self::parse_struct_attributes(&mut qobject_struct.attrs)?; // Ensure that the QObject is marked as pub otherwise the error is non obvious // https://github.com/KDAB/cxx-qt/issues/457 @@ -255,21 +252,11 @@ impl ParsedQObject { } } - /// Extract all the properties from [syn::Fields] from a [syn::ItemStruct] - fn parse_struct_fields(fields: &mut Fields) -> Result> { + fn parse_struct_attributes(attrs: &mut Vec) -> Result> { let mut properties = vec![]; - for field in fields_to_named_fields_mut(fields)? { - // Try to find any properties defined within the struct - if let Some(index) = attribute_find_path(&field.attrs, &["qproperty"]) { - // Remove the #[qproperty] attribute - field.attrs.remove(index); - - properties.push(ParsedQProperty { - ident: field.ident.clone().unwrap(), - ty: field.ty.clone(), - vis: field.vis.clone(), - }); - } + + while let Some(index) = attribute_find_path(attrs, &["qproperty"]) { + properties.push(ParsedQProperty::parse(attrs.remove(index))?); } Ok(properties) @@ -281,7 +268,7 @@ pub mod tests { use super::*; use crate::parser::tests::f64_type; - use syn::{parse_quote, ItemImpl, Visibility}; + use syn::{parse_quote, ItemImpl}; pub fn create_parsed_qobject() -> ParsedQObject { let qobject_struct: ItemStruct = parse_quote! { @@ -318,11 +305,10 @@ pub mod tests { fn test_from_struct_properties_and_fields() { let qobject_struct: ItemStruct = parse_quote! { #[cxx_qt::qobject] + #[qproperty(i32, int_property)] + #[qproperty(i32, public_property)] pub struct MyObject { - #[qproperty] int_property: i32, - - #[qproperty] pub public_property: i32, field: i32, @@ -392,11 +378,10 @@ pub mod tests { fn test_parse_struct_fields_valid() { let item: ItemStruct = parse_quote! { #[cxx_qt::qobject] + #[qproperty(f64, f64_property)] + #[qproperty(f64, public_property)] pub struct T { - #[qproperty] f64_property: f64, - - #[qproperty] pub public_property: f64, field: f64, @@ -407,20 +392,18 @@ pub mod tests { assert_eq!(properties[0].ident, "f64_property"); assert_eq!(properties[0].ty, f64_type()); - assert!(matches!(properties[0].vis, Visibility::Inherited)); assert_eq!(properties[1].ident, "public_property"); assert_eq!(properties[1].ty, f64_type()); - assert!(matches!(properties[1].vis, Visibility::Public(_))); } #[test] - fn test_parse_struct_fields_invalid() { + fn test_parse_struct_fields() { let item: ItemStruct = parse_quote! { #[cxx_qt::qobject] pub struct T(f64); }; - assert!(ParsedQObject::from_struct(&item, 0).is_err()); + assert!(ParsedQObject::from_struct(&item, 0).is_ok()); } #[test] diff --git a/crates/cxx-qt-gen/src/syntax/fields.rs b/crates/cxx-qt-gen/src/syntax/fields.rs deleted file mode 100644 index ff3dd3f7c..000000000 --- a/crates/cxx-qt-gen/src/syntax/fields.rs +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company -// SPDX-FileContributor: Andrew Hayzen -// -// SPDX-License-Identifier: MIT OR Apache-2.0 - -use syn::{spanned::Spanned, Error, Field, Fields, FieldsNamed, Result}; - -/// In a group of [syn::Fields] extract any [syn::FieldNamed] fields and allow for mutation -/// -/// If there are [syn::FieldsUnnamed] then an error occurs -pub fn fields_to_named_fields_mut(fields: &mut Fields) -> Result> { - match fields { - Fields::Named(FieldsNamed { named, .. }) => Ok(named.iter_mut().collect()), - Fields::Unnamed(_) => Err(Error::new(fields.span(), "Fields cannot be unnamed")), - // Unit is an empty struct or enum etc - Fields::Unit => Ok(vec![]), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - use syn::{parse_quote, ItemStruct, Type, Variant}; - - /// Helper which returns a f64 as a [syn::Type] - fn f64_type() -> Type { - parse_quote! { f64 } - } - - #[test] - fn test_fields_to_named_fields_enum_variant_named() { - let mut v: Variant = parse_quote! { - PointChanged { x: f64, y: f64 } - }; - let result = fields_to_named_fields_mut(&mut v.fields).unwrap(); - assert_eq!(result.len(), 2); - assert_eq!(result[0].ident.as_ref().unwrap(), "x"); - assert_eq!(result[0].ty, f64_type()); - assert_eq!(result[1].ident.as_ref().unwrap(), "y"); - assert_eq!(result[1].ty, f64_type()); - } - - #[test] - fn test_fields_to_named_fields_enum_variant_unamed() { - let mut v: Variant = parse_quote! { - PointChanged(f64, f64) - }; - let result = fields_to_named_fields_mut(&mut v.fields); - assert!(result.is_err()); - } - - #[test] - fn test_fields_to_named_fields_enum_variant_empty() { - let mut v: Variant = parse_quote! { - PointChanged - }; - let result = fields_to_named_fields_mut(&mut v.fields).unwrap(); - assert_eq!(result.len(), 0); - } - - #[test] - fn test_fields_to_named_fields_struct_named() { - let mut s: ItemStruct = parse_quote! { - struct Point { - x: f64, - y: f64 - } - }; - let result = fields_to_named_fields_mut(&mut s.fields).unwrap(); - assert_eq!(result.len(), 2); - assert_eq!(result[0].ident.as_ref().unwrap(), "x"); - assert_eq!(result[0].ty, f64_type()); - assert_eq!(result[1].ident.as_ref().unwrap(), "y"); - assert_eq!(result[1].ty, f64_type()); - } - - #[test] - fn test_fields_to_named_fields_struct_unamed() { - let mut s: ItemStruct = parse_quote! { - struct Point(f64, f64); - }; - let result = fields_to_named_fields_mut(&mut s.fields); - assert!(result.is_err()); - } - - #[test] - fn test_fields_to_named_fields_struct_empty() { - let mut s: ItemStruct = parse_quote! { - struct Point; - }; - let result = fields_to_named_fields_mut(&mut s.fields).unwrap(); - assert_eq!(result.len(), 0); - } - - #[test] - fn test_fields_to_named_fields_mutatable() { - let mut s: ItemStruct = parse_quote! { - struct Point { - #[attribute] - x: f64, - y: f64 - } - }; - let mut result = fields_to_named_fields_mut(&mut s.fields).unwrap(); - assert_eq!(result.len(), 2); - result[0].attrs.clear(); - - let expected: ItemStruct = parse_quote! { - struct Point { - x: f64, - y: f64 - } - }; - assert_eq!(s, expected); - } -} diff --git a/crates/cxx-qt-gen/src/syntax/mod.rs b/crates/cxx-qt-gen/src/syntax/mod.rs index 8f1a5c72f..d90bb538e 100644 --- a/crates/cxx-qt-gen/src/syntax/mod.rs +++ b/crates/cxx-qt-gen/src/syntax/mod.rs @@ -5,7 +5,6 @@ pub mod attribute; pub mod expr; -pub mod fields; pub mod foreignmod; pub mod path; mod qtfile; diff --git a/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs b/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs index 4929b9b87..701c19170 100644 --- a/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs +++ b/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs @@ -80,8 +80,8 @@ pub mod ffi { } #[cxx_qt::qobject(base = "QStringListModel")] + #[qproperty(i32, property_name)] pub struct MyObject { - #[qproperty] property_name: i32, } @@ -127,8 +127,8 @@ pub mod ffi { } #[cxx_qt::qobject] + #[qproperty(i32, property_name)] pub struct SecondObject { - #[qproperty] property_name: i32, } diff --git a/crates/cxx-qt-gen/test_inputs/properties.rs b/crates/cxx-qt-gen/test_inputs/properties.rs index 6d72503e6..be22f0346 100644 --- a/crates/cxx-qt-gen/test_inputs/properties.rs +++ b/crates/cxx-qt-gen/test_inputs/properties.rs @@ -8,10 +8,10 @@ mod ffi { #[cxx_qt::qobject] #[derive(Default)] + #[qproperty(i32, primitive)] + #[qproperty(QPoint, trivial)] pub struct MyObject { - #[qproperty] primitive: i32, - #[qproperty] trivial: QPoint, opaque: UniquePtr, diff --git a/examples/demo_threading/rust/src/lib.rs b/examples/demo_threading/rust/src/lib.rs index 690b4ed1b..ec4b6ab41 100644 --- a/examples/demo_threading/rust/src/lib.rs +++ b/examples/demo_threading/rust/src/lib.rs @@ -24,15 +24,15 @@ mod ffi { } #[cxx_qt::qobject(qml_uri = "com.kdab.energy", qml_version = "1.0")] + #[qproperty(f64, average_use)] + #[qproperty(u32, sensors)] + #[qproperty(f64, total_use)] pub struct EnergyUsage { /// The average power usage of the connected sensors - #[qproperty] average_use: f64, /// The count of connected sensors - #[qproperty] sensors: u32, /// The total power usage of the connected sensors - #[qproperty] total_use: f64, /// The join handles of the running threads diff --git a/examples/qml_extension_plugin/plugin/rust/src/lib.rs b/examples/qml_extension_plugin/plugin/rust/src/lib.rs index 8e5c5cb55..d2a381e3c 100644 --- a/examples/qml_extension_plugin/plugin/rust/src/lib.rs +++ b/examples/qml_extension_plugin/plugin/rust/src/lib.rs @@ -37,10 +37,10 @@ mod ffi { } #[cxx_qt::qobject] + #[qproperty(i32, number)] + #[qproperty(QString, string)] pub struct MyObject { - #[qproperty] pub number: i32, - #[qproperty] pub string: QString, } diff --git a/examples/qml_features/rust/src/containers.rs b/examples/qml_features/rust/src/containers.rs index 0dc469255..3ae96ed23 100644 --- a/examples/qml_features/rust/src/containers.rs +++ b/examples/qml_features/rust/src/containers.rs @@ -37,22 +37,22 @@ pub mod ffi { /// It has Q_PROPERTYs which expose a string with the container's contents to show in QML #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] #[derive(Default)] + #[qproperty(QString, string_hash)] + #[qproperty(QString, string_list)] + #[qproperty(QString, string_map)] + #[qproperty(QString, string_set)] + #[qproperty(QString, string_vector)] + // Expose as a Q_PROPERTY so that QML tests can ensure that QVariantMap works + #[qproperty(QMap_QString_QVariant, map)] pub struct RustContainers { - #[qproperty] string_hash: QString, - #[qproperty] string_list: QString, - #[qproperty] string_map: QString, - #[qproperty] string_set: QString, - #[qproperty] string_vector: QString, pub(crate) hash: QHash_QString_QVariant, pub(crate) list: QList_i32, - // Expose as a Q_PROPERTY so that QML tests can ensure that QVariantMap works - #[qproperty] pub(crate) map: QMap_QString_QVariant, pub(crate) set: QSet_i32, pub(crate) vector: QVector_i32, diff --git a/examples/qml_features/rust/src/multiple_qobjects.rs b/examples/qml_features/rust/src/multiple_qobjects.rs index 574883ae9..b0e3b7a67 100644 --- a/examples/qml_features/rust/src/multiple_qobjects.rs +++ b/examples/qml_features/rust/src/multiple_qobjects.rs @@ -19,10 +19,10 @@ pub mod ffi { /// The first QObject #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[qproperty(i32, counter)] + #[qproperty(QColor, color)] pub struct FirstObject { - #[qproperty] counter: i32, - #[qproperty] color: QColor, } @@ -56,10 +56,10 @@ pub mod ffi { /// The second QObject #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[qproperty(i32, counter)] + #[qproperty(QUrl, url)] pub struct SecondObject { - #[qproperty] counter: i32, - #[qproperty] url: QUrl, } diff --git a/examples/qml_features/rust/src/nested_qobjects.rs b/examples/qml_features/rust/src/nested_qobjects.rs index 15bcae873..bd98a0fcb 100644 --- a/examples/qml_features/rust/src/nested_qobjects.rs +++ b/examples/qml_features/rust/src/nested_qobjects.rs @@ -20,8 +20,8 @@ pub mod ffi { /// The inner QObject #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] #[derive(Default)] + #[qproperty(i32, counter)] pub struct InnerObject { - #[qproperty] counter: i32, } @@ -33,8 +33,8 @@ pub mod ffi { /// The outer QObject which has a Q_PROPERTY pointing to the inner QObject #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[qproperty(*mut CxxInnerObject, inner)] pub struct OuterObject { - #[qproperty] inner: *mut CxxInnerObject, } diff --git a/examples/qml_features/rust/src/properties.rs b/examples/qml_features/rust/src/properties.rs index eb9623f92..972f6bd29 100644 --- a/examples/qml_features/rust/src/properties.rs +++ b/examples/qml_features/rust/src/properties.rs @@ -20,21 +20,21 @@ pub mod ffi { /// A QObject which has Q_PROPERTYs // ANCHOR: book_properties_struct #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[qproperty(bool, connected)] + #[qproperty(QUrl, connected_url)] + #[qproperty(QUrl, previous_connected_url)] + #[qproperty(QString, status_message)] pub struct RustProperties { /// A connected Q_PROPERTY - #[qproperty] connected: bool, /// A connected_url Q_PROPERTY - #[qproperty] pub(crate) connected_url: QUrl, /// A previous_connected_url Q_PROPERTY - #[qproperty] previous_connected_url: QUrl, /// A status_message Q_PROPERTY - #[qproperty] status_message: QString, } // ANCHOR_END: book_properties_struct diff --git a/examples/qml_features/rust/src/serialisation.rs b/examples/qml_features/rust/src/serialisation.rs index b6b018008..b618edd45 100644 --- a/examples/qml_features/rust/src/serialisation.rs +++ b/examples/qml_features/rust/src/serialisation.rs @@ -39,12 +39,12 @@ pub mod ffi { /// A QObject which can be serialised #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[qproperty(i32, number)] + #[qproperty(QString, string)] pub struct Serialisation { /// The number Q_PROPERTY - #[qproperty] pub number: i32, /// The string Q_PROPERTY - #[qproperty] pub string: QString, } diff --git a/examples/qml_features/rust/src/signals.rs b/examples/qml_features/rust/src/signals.rs index fb52cade0..1abb32e09 100644 --- a/examples/qml_features/rust/src/signals.rs +++ b/examples/qml_features/rust/src/signals.rs @@ -38,10 +38,10 @@ pub mod ffi { // ANCHOR: book_signals_struct #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] #[derive(Default)] + #[qproperty(bool, logging_enabled)] pub struct RustSignals { pub(crate) connections: Option<[cxx_qt_lib::QMetaObjectConnection; 3]>, - #[qproperty] logging_enabled: bool, } diff --git a/examples/qml_features/rust/src/singleton.rs b/examples/qml_features/rust/src/singleton.rs index 12fe50d2b..7777b1a72 100644 --- a/examples/qml_features/rust/src/singleton.rs +++ b/examples/qml_features/rust/src/singleton.rs @@ -12,9 +12,9 @@ pub mod ffi { /// A QObject which is a QML_SINGLETON #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0", qml_singleton)] #[derive(Default)] + #[qproperty(i32, persistent_value)] pub struct RustSingleton { /// A Q_PROPERTY with a persistent value - #[qproperty] persistent_value: i32, } diff --git a/examples/qml_features/rust/src/threading.rs b/examples/qml_features/rust/src/threading.rs index fc5d798c9..97f577e0f 100644 --- a/examples/qml_features/rust/src/threading.rs +++ b/examples/qml_features/rust/src/threading.rs @@ -23,12 +23,12 @@ pub mod ffi { /// A QObject which has threading #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[qproperty(QString, title)] + #[qproperty(QUrl, url)] pub struct ThreadingWebsite { /// The title Q_PROPERTY - #[qproperty] title: QString, /// The url Q_PROPERTY - #[qproperty] url: QUrl, pub(crate) loading: std::sync::atomic::AtomicBool, diff --git a/examples/qml_features/rust/src/types.rs b/examples/qml_features/rust/src/types.rs index b4d0c2e4c..b632d5d98 100644 --- a/examples/qml_features/rust/src/types.rs +++ b/examples/qml_features/rust/src/types.rs @@ -75,14 +75,14 @@ pub mod ffi { /// A QObject which shows custom types #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[qproperty(bool, boolean)] + #[qproperty(QPointF, point)] + #[qproperty(QUrl, url)] + #[qproperty(i32, custom_value)] pub struct Types { - #[qproperty] boolean: bool, - #[qproperty] point: QPointF, - #[qproperty] url: QUrl, - #[qproperty] custom_value: i32, } diff --git a/examples/qml_features/rust/src/uncreatable.rs b/examples/qml_features/rust/src/uncreatable.rs index 4908e3eee..04990fdd6 100644 --- a/examples/qml_features/rust/src/uncreatable.rs +++ b/examples/qml_features/rust/src/uncreatable.rs @@ -12,9 +12,9 @@ pub mod ffi { /// A QObject which is a QML_UNCREATABLE #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0", qml_uncreatable)] #[derive(Default)] + #[qproperty(i32, value)] pub struct RustUncreatable { /// A value Q_PROPERTY - #[qproperty] value: i32, } } diff --git a/examples/qml_minimal/rust/src/cxxqt_object.rs b/examples/qml_minimal/rust/src/cxxqt_object.rs index 23476ae73..fc4f7487f 100644 --- a/examples/qml_minimal/rust/src/cxxqt_object.rs +++ b/examples/qml_minimal/rust/src/cxxqt_object.rs @@ -24,10 +24,10 @@ pub mod ffi { /// The Rust struct for the QObject // ANCHOR: book_rustobj_struct #[cxx_qt::qobject(qml_uri = "com.kdab.cxx_qt.demo", qml_version = "1.0")] + #[qproperty(i32, number)] + #[qproperty(QString, string)] pub struct MyObject { - #[qproperty] number: i32, - #[qproperty] string: QString, } // ANCHOR_END: book_rustobj_struct diff --git a/tests/basic_cxx_qt/rust/src/data.rs b/tests/basic_cxx_qt/rust/src/data.rs index 455107431..e99a36363 100644 --- a/tests/basic_cxx_qt/rust/src/data.rs +++ b/tests/basic_cxx_qt/rust/src/data.rs @@ -33,10 +33,10 @@ mod ffi { } #[cxx_qt::qobject] + #[qproperty(i32, number)] + #[qproperty(QString, string)] pub struct MyData { - #[qproperty] pub number: i32, - #[qproperty] pub string: QString, } diff --git a/tests/basic_cxx_qt/rust/src/lib.rs b/tests/basic_cxx_qt/rust/src/lib.rs index 48df43c8b..5c8a0d40a 100644 --- a/tests/basic_cxx_qt/rust/src/lib.rs +++ b/tests/basic_cxx_qt/rust/src/lib.rs @@ -18,10 +18,10 @@ mod ffi { } #[cxx_qt::qobject] + #[qproperty(i32, number)] + #[qproperty(QString, string)] pub struct MyObject { - #[qproperty] number: i32, - #[qproperty] string: QString, pub(crate) update_call_count: i32, diff --git a/tests/basic_cxx_qt/rust/src/types.rs b/tests/basic_cxx_qt/rust/src/types.rs index 87fc99439..da09b7233 100644 --- a/tests/basic_cxx_qt/rust/src/types.rs +++ b/tests/basic_cxx_qt/rust/src/types.rs @@ -8,24 +8,24 @@ mod ffi { #[cxx_qt::qobject] #[derive(Default)] + #[qproperty(bool, boolean)] + #[qproperty(f32, float_32)] + #[qproperty(f64, float_64)] + #[qproperty(i8, int_8)] + #[qproperty(i16, int_16)] + #[qproperty(i32, int_32)] + #[qproperty(u8, uint_8)] + #[qproperty(u16, uint_16)] + #[qproperty(u32, uint_32)] pub struct MyTypes { - #[qproperty] boolean: bool, - #[qproperty] float_32: f32, - #[qproperty] float_64: f64, - #[qproperty] int_8: i8, - #[qproperty] int_16: i16, - #[qproperty] int_32: i32, - #[qproperty] uint_8: u8, - #[qproperty] uint_16: u16, - #[qproperty] uint_32: u32, } }