diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 614ea9a60..1323a0123 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -19,7 +19,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 - [Building with Cargo](./getting-started/5-cargo-executable.md) - [QObject](./qobject/index.md) - [`#[cxx_qt::bridge]` - Bridge Macro](./qobject/bridge-macro.md) - - [`#[cxx_qt::qobject]` - Defining QObjects](./qobject/qobject_struct.md) + - [`#[qobject]` - Defining QObjects](./qobject/qobject_struct.md) - [`#[qsignal]` - Signal macro](./qobject/signals.md) - [`qobject::T` - The generated QObject](./qobject/generated-qobject.md) - [CxxQtThread](./qobject/cxxqtthread.md) diff --git a/book/src/concepts/inheritance.md b/book/src/concepts/inheritance.md index 90d071b5a..568714650 100644 --- a/book/src/concepts/inheritance.md +++ b/book/src/concepts/inheritance.md @@ -33,7 +33,7 @@ For this, the `clear` method implemented in Rust needs to call `beginResetModel` See [the Qt docs](https://doc.qt.io/qt-6/qabstractlistmodel.html) for more details on the specific subclassing requirements. Methods in a `extern "RustQt"` block similar to CXX can be tagged with an `#[inherit]` attribute, with the same restrictions regarding which types can be used. -Additionally, the `self` type must be either `self: Pin<&mut qobject::T>` or `self: &qobject::T`, where `qobject::T` must refer to a QObject marked with `#[cxx_qt::qobject]` in the `#[cxx_qt::bridge]` +Additionally, the `self` type must be either `self: Pin<&mut qobject::T>` or `self: &qobject::T`, where `qobject::T` must refer to a QObject marked with `#[qobject]` in the `#[cxx_qt::bridge]` The declared methods will be case-converted as in other CXX-Qt APIs. To explicitly declare the C++ method name, use the `#[cxx_name="myFunctionName"]` attribute. diff --git a/book/src/getting-started/1-qobjects-in-rust.md b/book/src/getting-started/1-qobjects-in-rust.md index 3fddf4d99..0f29faa62 100644 --- a/book/src/getting-started/1-qobjects-in-rust.md +++ b/book/src/getting-started/1-qobjects-in-rust.md @@ -44,14 +44,14 @@ These concepts include: As with CXX, to use these features you mark a Rust module with an attribute macro (`#[cxx_qt::bridge]`). Then you can use the afformentioned features with the help of more macros. -- `#[cxx_qt::qobject]` - Expose a Rust struct to Qt as a QObject subclass. +- `#[qobject]` - Expose a Rust struct to Qt as a QObject subclass. - `#[qproperty]` - Expose a field of the Rust struct to QML/C++ as a [`Q_PROPERTY`](https://doc.qt.io/qt-6/qtqml-cppintegration-exposecppattributes.html#exposing-properties). - `#[qinvokable]` - Expose a function on the QObject to QML and C++ as a [`Q_INVOKABLE`](https://doc.qt.io/qt-6/qtqml-cppintegration-exposecppattributes.html#exposing-methods-including-qt-slots). - `#[qsignal]` - Define the [Signals](https://doc.qt.io/qt-6/signalsandslots.html#signals) of a QObject T. CXX-Qt will then expand this Rust module into two separate parts: -- C++ files that define a QObject subclass for each `#[cxx_qt::qobject]` marked struct. -- The Rust code for the `#[cxx_qt::qobject]` marked Rust struct +- C++ files that define a QObject subclass for each `#[qobject]` marked struct. +- The Rust code for the `#[qobject]` marked Rust struct
@@ -59,11 +59,11 @@ CXX-Qt will then expand this Rust module into two separate parts:
-CXX-Qt also generates the code needed for interaction of the C++ QObject subclass and the `#[cxx_qt::qobject]` marked struct using the [CXX library](https://cxx.rs/). +CXX-Qt also generates the code needed for interaction of the C++ QObject subclass and the `#[qobject]` marked struct using the [CXX library](https://cxx.rs/). For more details, see the [Concepts: Bridge](../concepts/bridge.md) page. The important take away here is the duality of any subclass generated by CXX-Qt. -These classes are made up of the actual QObject subclass instance that C++ interacts with, as well as an instance of the `#[cxx_qt::qobject]` marked struct on the Rust side. +These classes are made up of the actual QObject subclass instance that C++ interacts with, as well as an instance of the `#[qobject]` marked struct on the Rust side. When such a QObject is instantiated, it will always also construct an instance of the Rust struct as well. The lifetime of the Rust struct will be bound to that of the QObject. If the QObject is deleted, the Rust struct will be deleted as well. 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 cd970ab87..341195fb8 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 @@ -52,13 +52,13 @@ Additionally, a `#[cxx_qt::bridge]` gives you a few more features that allow you ## QObject struct -To create a new QObject subclass, we can define a struct within our module and mark it with `#[cxx_qt::qobject]`. +To create a new QObject subclass, we can define a struct within our module and mark it with `#[qobject]`. ```rust,ignore {{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_struct}} ``` -Optionally, add `qml_uri` and `qml_version` inside `#[cxx_qt::qobject]` to tell the Rust build script to generate a QML plugin +Optionally, add `qml_uri` and `qml_version` inside `#[qobject]` to tell the Rust build script to generate a QML plugin that will register the QObject with QML engine at startup. If you want the name of the QML type and the Rust type to be different, you can also add `qml_name = "OtherName"`. This takes the place of the [qt_add_qml_module CMake function](https://doc.qt.io/qt-6/qt-add-qml-module.html) (because that doesn't work with CXX-Qt's build system). @@ -92,7 +92,7 @@ For more details on the available types, see the [Qt types page](../concepts/typ ## qobject::T CXX-Qt will then automatically generate a new QObject subclass for our `MyObject` struct and expose it as an [`extern "C++"` opaque type](https://cxx.rs/extern-c++.html#opaque-c-types) to Rust. -For any Rust struct `T` that is marked with `#[cxx_qt::qobject]`, CXX-Qt will expose the corresponding C++ QObject under `qobject::T`. +For any Rust struct `T` that is marked with `#[qobject]`, CXX-Qt will expose the corresponding C++ QObject under `qobject::T`. In our case, this means we can refer to the C++ QObject for our `MyObject` struct, as `qobject::MyObject`. This type can be used like any other CXX opaque type. diff --git a/book/src/qobject/generated-qobject.md b/book/src/qobject/generated-qobject.md index de45584bb..cdef3fd03 100644 --- a/book/src/qobject/generated-qobject.md +++ b/book/src/qobject/generated-qobject.md @@ -8,10 +8,10 @@ SPDX-License-Identifier: MIT OR Apache-2.0 # `qobject::T` - The generated QObject One of the key features of CXX-Qt is the ability to create your own QObjects from Rust. -This is what the [`#[cxx_qt::qobject]` macro](./qobject_struct.md) is for. +This is what the [`#[qobject]` macro](./qobject_struct.md) is for. This page serves to document the details of what is generated and how to interact with the generated QObject from Rust. -The `#[cxx_qt::qobject]` macro generates a QObject for a given Rust struct. +The `#[qobject]` macro generates a QObject for a given Rust struct. Whilst this QObject is a C++ type, CXX-Qt will automatically wrap it as a [CXX Opaque Type](https://cxx.rs/extern-c++.html#opaque-c-types). These generated QObjects are accessible to Rust in a generated module with the name `qobject`. Each struct `T`'s generated QObject is accessible as `qobject::T`. @@ -37,7 +37,7 @@ Example: // In file qt_types.rs #[cxx_qt::bridge] mod ffi { - #[cxx_qt::qobject] + #[qobject] #[derive(Default)] pub struct MyObject {} } @@ -89,7 +89,7 @@ There is also an advanced way to access the data in the internal Rust struct: fn rust(&self) -> &T fn rust_mut(self: Pin<&mut Self>) -> &mut T ``` -Where `T` is the struct with the `#[cxx_qt::qobject]` macro. +Where `T` is the struct with the `#[qobject]` macro. This allows you to directly manipulate the internal Rust struct without having to use the generated accessor methods. diff --git a/book/src/qobject/index.md b/book/src/qobject/index.md index 345f5b4e5..623b603da 100644 --- a/book/src/qobject/index.md +++ b/book/src/qobject/index.md @@ -14,7 +14,7 @@ For a simpler introduction, take a look at our [Getting Started guide](../gettin QObject Features and Parts: * [`#[cxx_qt::bridge]` - The macro around the module](./bridge-macro.md) - * [`#[cxx_qt::qobject]` - Marking a Rust struct as a QObject](./qobject_struct.md) + * [`#[qobject]` - Marking a Rust struct as a QObject](./qobject_struct.md) * [`#[qsignal]` - A macro for defining signals](./signals.md) * [`qobject:T` - The generated QObject](./generated-qobject.md) * [`CxxQtThread` - Queueing closures onto the Qt event loop](./cxxqtthread.md) diff --git a/book/src/qobject/qobject_struct.md b/book/src/qobject/qobject_struct.md index 6eba2ca75..f89a73a3a 100644 --- a/book/src/qobject/qobject_struct.md +++ b/book/src/qobject/qobject_struct.md @@ -5,14 +5,14 @@ SPDX-FileContributor: Andrew Hayzen SPDX-License-Identifier: MIT OR Apache-2.0 --> -# `#[cxx_qt::qobject]` Macro - Defining QObjects in Rust +# `#[qobject]` Macro - Defining QObjects in Rust Defining QObjects is at the heart of CXX-Qt. -Therefore `#[cxx_qt::qobject]` can be considered the most important macro in CXX-Qt. +Therefore `#[qobject]` can be considered the most important macro in CXX-Qt. ## Requirements - Like most other CXX-Qt macros, it can only be used from within a [`#[cxx_qt::bridge]`](./bridge-macro.md). -- The `#[cxx_qt::qobject]` macro must be placed on a Rust struct. +- The `#[qobject]` macro must be placed on a Rust struct. - The struct must [`impl Default`](#default), so that it can be constructed as part of a QObject. ## Effects @@ -30,10 +30,10 @@ The macro does multiple other things for you though: - Generate signals if paired with a [`#[qsignal]` macro](./signals.md). ## Exposing to QML -`#[cxx_qt::qobject]` supports registering the Qt object as a QML type directly at build time. +`#[qobject]` supports registering the Qt object as a QML type directly at build time. This is comparable to [adding `QML_ELEMENT` in C++](https://doc.qt.io/qt-6/qtqml-cppintegration-definetypes.html). -For this, add the `qml_uri` and `qml_version` attributes to the `#[cxx_qt::qobject]` macro. +For this, add the `qml_uri` and `qml_version` attributes to the `#[qobject]` macro. ``` rust,ignore,noplayground {{#include ../../../examples/qml_minimal/rust/src/cxxqt_object.rs:book_rustobj_struct}} ``` @@ -65,7 +65,7 @@ For more information on inheritance and how to override methods see the [Inherit ## Properties -Fields within the `#[cxx_qt::qobject]` marked struct can be tagged with `#[qproperty]` to be exposed as [`Q_PROPERTY`s](https://doc.qt.io/qt-6/properties.html) on the generated QObject: +Fields within the `#[qobject]` marked struct can be tagged with `#[qproperty]` to be exposed as [`Q_PROPERTY`s](https://doc.qt.io/qt-6/properties.html) on the generated QObject: ```rust,ignore,noplayground {{#include ../../../examples/qml_features/rust/src/properties.rs:book_properties_struct}} @@ -95,7 +95,7 @@ See the [Private fields section](#private-methods-and-fields) ## Default -The [`Default` trait](https://doc.rust-lang.org/std/default/trait.Default.html) needs to be implemented for the `#[cxx_qt::qobject]` marked struct either by hand or by using the derive macro `#[derive(Default)]`. +The [`Default` trait](https://doc.rust-lang.org/std/default/trait.Default.html) needs to be implemented for the `#[qobject]` marked struct either by hand or by using the derive macro `#[derive(Default)]`. This needs to provide default values for every [`#[qproperty]`](#properties) and [private field](#private-methods-and-fields) @@ -114,7 +114,7 @@ CXX-Qt allows you to define invokables using Rust code. This way you can easily add a Rust-powered backend to your QML frontend. Invokables, by definition, must be defined on a C++ class however. -This is where the QObject subclass generated by `#[cxx_qt::qobject]` comes into play. +This is where the QObject subclass generated by `#[qobject]` comes into play. For details on this, see the [`qobject::T` page](./generated-qobject.md). The important part for invokables is that they need to be implemented on the `qobject::T`, not `T`. @@ -144,7 +144,7 @@ These are normal Rust methods, so they aren't restricted to CXX-compatible types ## Private Methods and Fields -Fields within your `#[cxx_qt::qobject]` struct that aren't tagged as `#[qproperty]` are not exposed as properties to Qt. These can be considered as "private to Rust" fields, and are useful for storing channels for threading or internal information for the QObject. +Fields within your `#[qobject]` struct that aren't tagged as `#[qproperty]` are not exposed as properties to Qt. These can be considered as "private to Rust" fields, and are useful for storing channels for threading or internal information for the QObject. Because they aren't available from C++, they also don't have any special type requirements and can be any Rust type. Use the `rust` and `rust_mut` methods to access the struct and therefore the fields. diff --git a/crates/cxx-qt-gen/src/generator/cpp/mod.rs b/crates/cxx-qt-gen/src/generator/cpp/mod.rs index 246eaddfc..dcd8341d3 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/mod.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/mod.rs @@ -54,7 +54,7 @@ mod tests { #[cxx_qt::bridge] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } } @@ -73,7 +73,7 @@ mod tests { #[cxx_qt::bridge(cxx_file_stem = "my_object")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } } @@ -92,7 +92,7 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } } diff --git a/crates/cxx-qt-gen/src/generator/cpp/qobject.rs b/crates/cxx-qt-gen/src/generator/cpp/qobject.rs index 83833c254..a34542d57 100644 --- a/crates/cxx-qt-gen/src/generator/cpp/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/cpp/qobject.rs @@ -166,7 +166,7 @@ mod tests { #[cxx_qt::bridge] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } } @@ -191,7 +191,8 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject(base = "QStringListModel")] + #[qobject] + #[base = "QStringListModel"] type MyObject = super::MyObjectRust; } } @@ -214,7 +215,8 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject(qml_element = "MyQmlElement")] + #[qobject] + #[qml_element = "MyQmlElement"] type MyNamedObject = super::MyNamedObjectRust; } } @@ -240,7 +242,9 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject(qml_element, qml_singleton)] + #[qobject] + #[qml_element] + #[qml_singleton] type MyObject = super::MyObjectRust; } } @@ -267,7 +271,9 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject(qml_element, qml_uncreatable)] + #[qobject] + #[qml_element] + #[qml_uncreatable] type MyObject = super::MyObjectRust; } } diff --git a/crates/cxx-qt-gen/src/generator/rust/mod.rs b/crates/cxx-qt-gen/src/generator/rust/mod.rs index 798059965..eefb5d93e 100644 --- a/crates/cxx-qt-gen/src/generator/rust/mod.rs +++ b/crates/cxx-qt-gen/src/generator/rust/mod.rs @@ -80,7 +80,7 @@ mod tests { #[cxx_qt::bridge] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } } @@ -109,7 +109,7 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } } @@ -130,7 +130,7 @@ mod tests { #[cxx_qt::bridge(cxx_file_stem = "my_object")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } } diff --git a/crates/cxx-qt-gen/src/generator/rust/qobject.rs b/crates/cxx-qt-gen/src/generator/rust/qobject.rs index 06600225d..8fe702962 100644 --- a/crates/cxx-qt-gen/src/generator/rust/qobject.rs +++ b/crates/cxx-qt-gen/src/generator/rust/qobject.rs @@ -206,7 +206,9 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject(qml_element, qml_singleton)] + #[qobject] + #[qml_element] + #[qml_singleton] type MyObject = super::MyObjectRust; } } diff --git a/crates/cxx-qt-gen/src/parser/cxxqtdata.rs b/crates/cxx-qt-gen/src/parser/cxxqtdata.rs index df379dfe2..db6abd9b2 100644 --- a/crates/cxx-qt-gen/src/parser/cxxqtdata.rs +++ b/crates/cxx-qt-gen/src/parser/cxxqtdata.rs @@ -3,7 +3,7 @@ // // SPDX-License-Identifier: MIT OR Apache-2.0 -use crate::syntax::attribute::attribute_find_path; +use crate::syntax::attribute::{attribute_find_path, attribute_take_path}; use crate::syntax::foreignmod::{foreign_mod_to_foreign_item_types, ForeignTypeIdentAlias}; use crate::syntax::path::path_from_idents; use crate::syntax::safety::Safety; @@ -86,32 +86,48 @@ impl ParsedCxxQtData { == Some("RustQt".to_string()) { for foreign_item in &foreign_mod.items { - if let ForeignItem::Verbatim(tokens) = foreign_item { - let foreign_alias: ForeignTypeIdentAlias = syn::parse2(tokens.clone())?; - - // TODO: in the future qobject macro will be removed and all types in RustQt will be QObjects - if let Some(index) = - attribute_find_path(&foreign_alias.attrs, &["cxx_qt", "qobject"]) - { - // Load the QObject - let mut qobject = - ParsedQObject::from_foreign_item_type(&foreign_alias, index)?; - - // Inject the bridge namespace if the qobject one is empty - if qobject.namespace.is_empty() && !self.namespace.is_empty() { - qobject.namespace = self.namespace.clone(); + match foreign_item { + // Fn are parsed later in parse_foreign_mod_rust_qt + ForeignItem::Fn(_) => {} + ForeignItem::Verbatim(tokens) => { + let mut foreign_alias: ForeignTypeIdentAlias = + syn::parse2(tokens.clone())?; + + // Check this type is tagged with a #[qobject] + if attribute_take_path(&mut foreign_alias.attrs, &["qobject"]) + .is_some() + { + // Load the QObject + let mut qobject = ParsedQObject::try_from(&foreign_alias)?; + + // Inject the bridge namespace if the qobject one is empty + if qobject.namespace.is_empty() && !self.namespace.is_empty() { + qobject.namespace = self.namespace.clone(); + } + + // Add the QObject type to the qualified mappings + self.qualified_mappings.insert( + foreign_alias.ident_left.clone(), + path_from_idents( + &self.module_ident, + &foreign_alias.ident_left, + ), + ); + + // Note that we assume a compiler error will occur later + // if you had two structs with the same name + self.qobjects + .insert(foreign_alias.ident_left.clone(), qobject); + } else { + return Err(Error::new( + foreign_item.span(), + "type A = super::B must be tagged with #[qobject]", + )); } - - // Add the QObject type to the qualified mappings - self.qualified_mappings.insert( - foreign_alias.ident_left.clone(), - path_from_idents(&self.module_ident, &foreign_alias.ident_left), - ); - - // Note that we assume a compiler error will occur later - // if you had two structs with the same name - self.qobjects - .insert(foreign_alias.ident_left.clone(), qobject); + } + // Const Macro, Type are unsupported in extern "RustQt" for now + _others => { + return Err(Error::new(foreign_item.span(), "Unsupported item")) } } } @@ -242,10 +258,7 @@ impl ParsedCxxQtData { for item in foreign_mod.items.drain(..) { if let ForeignItem::Fn(mut foreign_fn) = item { // Test if the function is a signal - if let Some(index) = attribute_find_path(&foreign_fn.attrs, &["qsignal"]) { - // Remove the signals attribute - foreign_fn.attrs.remove(index); - + if attribute_take_path(&mut foreign_fn.attrs, &["qsignal"]).is_some() { let parsed_signal_method = ParsedSignal::parse(foreign_fn, safe_call)?; self.with_qobject(&parsed_signal_method.qobject_ident)? @@ -254,10 +267,7 @@ impl ParsedCxxQtData { // Test if the function is an inheritance method // // Note that we need to test for qsignal first as qsignals have their own inherit meaning - } else if let Some(index) = attribute_find_path(&foreign_fn.attrs, &["inherit"]) { - // Remove the inherit attribute - foreign_fn.attrs.remove(index); - + } else if attribute_take_path(&mut foreign_fn.attrs, &["inherit"]).is_some() { let parsed_inherited_method = ParsedInheritedMethod::parse(foreign_fn, safe_call)?; @@ -344,9 +354,7 @@ mod tests { let module: ItemMod = parse_quote! { mod module { extern "RustQt" { - type Other = super::OtherRust; - - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } } @@ -364,11 +372,9 @@ mod tests { let module: ItemMod = parse_quote! { mod module { extern "RustQt" { - type Other = super::OtherRust; - - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; - #[cxx_qt::qobject] + #[qobject] type SecondObject = super::SecondObjectRust; } } @@ -390,10 +396,10 @@ mod tests { let module: ItemMod = parse_quote! { mod module { extern "RustQt" { - type Other = super::OtherRust; - #[cxx_qt::qobject(namespace = "qobject_namespace")] + #[qobject] + #[namespace = "qobject_namespace"] type MyObject = super::MyObjectRust; - #[cxx_qt::qobject] + #[qobject] type SecondObject = super::SecondObjectRust; } } @@ -421,7 +427,7 @@ mod tests { } #[test] - fn test_find_qobjects_no_macro() { + fn test_find_qobjects_no_qobject() { let mut cxx_qt_data = ParsedCxxQtData::new(format_ident!("ffi"), "".to_string()); let module: ItemMod = parse_quote! { @@ -433,8 +439,7 @@ mod tests { } }; let result = cxx_qt_data.find_qobject_types(&module.content.unwrap().1); - assert!(result.is_ok()); - assert_eq!(cxx_qt_data.qobjects.len(), 0); + assert!(result.is_err()); } #[test] diff --git a/crates/cxx-qt-gen/src/parser/inherit.rs b/crates/cxx-qt-gen/src/parser/inherit.rs index 50fd0e0e7..9d4fa530d 100644 --- a/crates/cxx-qt-gen/src/parser/inherit.rs +++ b/crates/cxx-qt-gen/src/parser/inherit.rs @@ -7,7 +7,7 @@ use crate::{ generator::naming::CombinedIdent, parser::parameter::ParsedFunctionParameter, syntax::{ - attribute::attribute_find_path, expr::expr_to_string, foreignmod, safety::Safety, types, + attribute::attribute_take_path, expr::expr_to_string, foreignmod, safety::Safety, types, }, }; use quote::format_ident; @@ -46,13 +46,11 @@ impl ParsedInheritedMethod { let mut ident = CombinedIdent::from_rust_function(method.sig.ident.clone()); - if let Some(index) = attribute_find_path(&method.attrs, &["cxx_name"]) { + if let Some(attr) = attribute_take_path(&mut method.attrs, &["cxx_name"]) { ident.cpp = format_ident!( "{}", - expr_to_string(&method.attrs[index].meta.require_name_value()?.value)? + expr_to_string(&attr.meta.require_name_value()?.value)? ); - - method.attrs.remove(index); } let safe = method.sig.unsafety.is_none(); diff --git a/crates/cxx-qt-gen/src/parser/method.rs b/crates/cxx-qt-gen/src/parser/method.rs index ded43f6f0..f49ec874b 100644 --- a/crates/cxx-qt-gen/src/parser/method.rs +++ b/crates/cxx-qt-gen/src/parser/method.rs @@ -5,7 +5,7 @@ use crate::{ parser::parameter::ParsedFunctionParameter, - syntax::{attribute::*, foreignmod, safety::Safety, types}, + syntax::{attribute::attribute_take_path, foreignmod, safety::Safety, types}, }; use std::collections::HashSet; use syn::{spanned::Spanned, Error, ForeignItemFn, Ident, Result}; @@ -56,13 +56,7 @@ impl ParsedMethod { } // Determine if the method is invokable - let is_qinvokable = if let Some(index) = attribute_find_path(&method.attrs, &["qinvokable"]) - { - method.attrs.remove(index); - true - } else { - false - }; + let is_qinvokable = attribute_take_path(&mut method.attrs, &["qinvokable"]).is_some(); // Parse any C++ specifiers let mut specifiers = HashSet::new(); @@ -71,8 +65,7 @@ impl ParsedMethod { ParsedQInvokableSpecifiers::Override, ParsedQInvokableSpecifiers::Virtual, ] { - if let Some(index) = attribute_find_path(&method.attrs, specifier.as_str_slice()) { - method.attrs.remove(index); + if attribute_take_path(&mut method.attrs, specifier.as_str_slice()).is_some() { specifiers.insert(specifier); } } diff --git a/crates/cxx-qt-gen/src/parser/mod.rs b/crates/cxx-qt-gen/src/parser/mod.rs index 4275f0dc8..4ce9a0d90 100644 --- a/crates/cxx-qt-gen/src/parser/mod.rs +++ b/crates/cxx-qt-gen/src/parser/mod.rs @@ -12,9 +12,11 @@ pub mod property; pub mod qobject; pub mod signals; -use crate::syntax::attribute::{attribute_find_path, attribute_tokens_to_map, AttributeDefault}; +use crate::syntax::{attribute::attribute_take_path, expr::expr_to_string}; use cxxqtdata::ParsedCxxQtData; -use syn::{spanned::Spanned, token::Brace, Error, Ident, ItemMod, LitStr, Result}; +use syn::{ + punctuated::Punctuated, spanned::Spanned, token::Brace, Error, ItemMod, Meta, Result, Token, +}; /// A struct representing a module block with CXX-Qt relevant [syn::Item]'s /// parsed into ParsedCxxQtData, to be used later to generate Rust & C++ code. @@ -37,23 +39,26 @@ impl Parser { let mut cxx_file_stem = module.ident.to_string(); // Remove the cxx_qt::bridge attribute - if let Some(index) = attribute_find_path(&module.attrs, &["cxx_qt", "bridge"]) { - let attr_map = attribute_tokens_to_map::( - &module.attrs[index], - AttributeDefault::None, - )?; - - // Parse any namespace in the cxx_qt::bridge macro - if let Some(lit_str) = attr_map.get("e::format_ident!("namespace")) { - namespace = lit_str.value(); - } - - // Parse any custom file stem - if let Some(stem) = attr_map.get("e::format_ident!("cxx_file_stem")) { - cxx_file_stem = stem.value(); + if let Some(attr) = attribute_take_path(&mut module.attrs, &["cxx_qt", "bridge"]) { + // If we are no #[cxx_qt::bridge] but #[cxx_qt::bridge(A = B)] then process + if !matches!(attr.meta, Meta::Path(_)) { + let nested = + attr.parse_args_with(Punctuated::::parse_terminated)?; + for meta in nested { + match meta { + Meta::NameValue(name_value) => { + // Parse any namespace in the cxx_qt::bridge macro + if name_value.path.is_ident("namespace") { + namespace = expr_to_string(&name_value.value)?; + // Parse any custom file stem + } else if name_value.path.is_ident("cxx_file_stem") { + cxx_file_stem = expr_to_string(&name_value.value)?; + } + } + _others => {} + } + } } - - module.attrs.remove(index); } else { return Err(Error::new( module.span(), @@ -150,7 +155,7 @@ mod tests { #[cxx_qt::bridge(namespace = "cxx_qt")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } @@ -186,7 +191,7 @@ mod tests { #[cxx_qt::bridge] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } @@ -217,7 +222,7 @@ mod tests { #[cxx_qt::bridge] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; } diff --git a/crates/cxx-qt-gen/src/parser/qobject.rs b/crates/cxx-qt-gen/src/parser/qobject.rs index b71fbf46d..f436fa2bf 100644 --- a/crates/cxx-qt-gen/src/parser/qobject.rs +++ b/crates/cxx-qt-gen/src/parser/qobject.rs @@ -9,12 +9,11 @@ use crate::{ property::ParsedQProperty, signals::ParsedSignal, }, syntax::{ - attribute::{attribute_find_path, attribute_tokens_to_map, AttributeDefault}, - foreignmod::ForeignTypeIdentAlias, + attribute::attribute_take_path, expr::expr_to_string, foreignmod::ForeignTypeIdentAlias, path::path_compare_str, }, }; -use syn::{Attribute, Error, Ident, ImplItem, Item, ItemImpl, LitStr, Result}; +use syn::{Attribute, Error, Ident, ImplItem, Item, ItemImpl, Meta, Result}; /// Metadata for registering QML element #[derive(Clone, Debug, Default, Eq, PartialEq)] @@ -65,32 +64,26 @@ pub struct ParsedQObject { pub others: Vec, } -impl ParsedQObject { - /// Parse a ForeignTypeIdentAlias into a [ParsedQObject] with the index of the cxx_qt::qobject specified - pub fn from_foreign_item_type( - qobject_ty: &ForeignTypeIdentAlias, - attr_index: usize, - ) -> Result { - let qml_metadata = Self::parse_qml_metadata(qobject_ty, attr_index)?; - - let attrs_map = attribute_tokens_to_map::( - &qobject_ty.attrs[attr_index], - AttributeDefault::Some(|span| LitStr::new("", span)), - )?; +impl TryFrom<&ForeignTypeIdentAlias> for ParsedQObject { + type Error = syn::Error; + + /// Parse a ForeignTypeIdentAlias into a [ParsedQObject] with the index of the #[qobject] specified + fn try_from(qobject_ty: &ForeignTypeIdentAlias) -> Result { + let mut qobject_ty = qobject_ty.clone(); + + // Find any QML metadata + let qml_metadata = Self::parse_qml_metadata(&qobject_ty.ident_left, &mut qobject_ty.attrs)?; // Find if there is any base class - let base_class = attrs_map - .get("e::format_ident!("base")) - .map(|base| base.value()); + let base_class = attribute_take_path(&mut qobject_ty.attrs, &["base"]) + .map(|attr| expr_to_string(&attr.meta.require_name_value()?.value)) + .transpose()?; // Load the namespace, if it is empty then the ParsedCxxQtData will inject any global namespace - let namespace = attrs_map - .get("e::format_ident!("namespace")) - .map_or_else(|| "".to_owned(), |base| base.value()); - - // Remove the macro from the type - let mut qobject_ty = qobject_ty.clone(); - qobject_ty.attrs.remove(attr_index); + let namespace = attribute_take_path(&mut qobject_ty.attrs, &["namespace"]) + .map(|attr| expr_to_string(&attr.meta.require_name_value()?.value)) + .transpose()? + .unwrap_or_else(|| "".to_owned()); // Parse any properties in the type // and remove the #[qproperty] attribute @@ -112,32 +105,35 @@ impl ParsedQObject { others: vec![], }) } +} +impl ParsedQObject { fn parse_qml_metadata( - qobject_ty: &ForeignTypeIdentAlias, - attr_index: usize, + qobject_ident: &Ident, + attrs: &mut Vec, ) -> Result> { - let attrs_map = attribute_tokens_to_map::( - &qobject_ty.attrs[attr_index], - AttributeDefault::Some(|span| LitStr::new("", span)), - )?; - match attrs_map.get("e::format_ident!("qml_element")) { - Some(qml_element) => { - let name = if qml_element.value().is_empty() { - qobject_ty.ident_left.to_string() - } else { - qml_element.value() - }; - let qml_uncreatable = attrs_map.get("e::format_ident!("qml_uncreatable")); - let qml_singleton = attrs_map.get("e::format_ident!("qml_singleton")); - Ok(Some(QmlElementMetadata { - name, - uncreatable: qml_uncreatable.is_some(), - singleton: qml_singleton.is_some(), - })) - } - None => Ok(None), + // Find if there is a qml_element attribute + if let Some(attr) = attribute_take_path(attrs, &["qml_element"]) { + // Extract the name of the qml_element + let name = match attr.meta { + Meta::NameValue(name_value) => expr_to_string(&name_value.value)?, + _ => qobject_ident.to_string(), + }; + + // Determine if this element is uncreatable + let uncreatable = attribute_take_path(attrs, &["qml_uncreatable"]).is_some(); + + // Determine if this element is a singleton + let singleton = attribute_take_path(attrs, &["qml_singleton"]).is_some(); + + return Ok(Some(QmlElementMetadata { + name, + uncreatable, + singleton, + })); } + + Ok(None) } pub fn parse_trait_impl(&mut self, imp: ItemImpl) -> Result<()> { @@ -215,8 +211,8 @@ impl ParsedQObject { // elements once using path_compare_str and then building ParsedQProperty // from the extracted elements. // https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html#method.extract_if - while let Some(index) = attribute_find_path(attrs, &["qproperty"]) { - properties.push(ParsedQProperty::parse(attrs.remove(index))?); + while let Some(attr) = attribute_take_path(attrs, &["qproperty"]) { + properties.push(ParsedQProperty::parse(attr)?); } Ok(properties) @@ -232,20 +228,20 @@ pub mod tests { pub fn create_parsed_qobject() -> ParsedQObject { let qobject_struct: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; }; - ParsedQObject::from_foreign_item_type(&qobject_struct, 0).unwrap() + ParsedQObject::try_from(&qobject_struct).unwrap() } #[test] fn test_from_struct_no_base_class() { let qobject_struct: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; }; - let qobject = ParsedQObject::from_foreign_item_type(&qobject_struct, 0).unwrap(); + let qobject = ParsedQObject::try_from(&qobject_struct).unwrap(); assert!(qobject.base_class.is_none()); assert!(qobject.qml_metadata.is_none()); } @@ -253,35 +249,36 @@ pub mod tests { #[test] fn test_from_struct_base_class() { let qobject_struct: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(base = "QStringListModel")] + #[qobject] + #[base = "QStringListModel"] type MyObject = super::MyObjectRust; }; - let qobject = ParsedQObject::from_foreign_item_type(&qobject_struct, 0).unwrap(); + let qobject = ParsedQObject::try_from(&qobject_struct).unwrap(); assert_eq!(qobject.base_class.as_ref().unwrap(), "QStringListModel"); } #[test] fn test_from_struct_properties_and_fields() { let qobject_struct: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject] + #[qobject] #[qproperty(i32, int_property)] #[qproperty(i32, public_property)] type MyObject = super::MyObjectRust; }; - let qobject = ParsedQObject::from_foreign_item_type(&qobject_struct, 0).unwrap(); + let qobject = ParsedQObject::try_from(&qobject_struct).unwrap(); assert_eq!(qobject.properties.len(), 2); } #[test] fn test_from_struct_fields() { let qobject_struct: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; }; - let qobject = ParsedQObject::from_foreign_item_type(&qobject_struct, 0).unwrap(); + let qobject = ParsedQObject::try_from(&qobject_struct).unwrap(); assert_eq!(qobject.properties.len(), 0); } @@ -330,14 +327,12 @@ pub mod tests { #[test] fn test_parse_struct_fields_valid() { let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject] + #[qobject] #[qproperty(f64, f64_property)] #[qproperty(f64, public_property)] type T = super::TRust; }; - let properties = ParsedQObject::from_foreign_item_type(&item, 0) - .unwrap() - .properties; + let properties = ParsedQObject::try_from(&item).unwrap().properties; assert_eq!(properties.len(), 2); assert_eq!(properties[0].ident, "f64_property"); @@ -350,10 +345,11 @@ pub mod tests { #[test] fn test_qml_metadata() { let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] type MyObject = super::MyObjectRust; }; - let qobject = ParsedQObject::from_foreign_item_type(&item, 0).unwrap(); + let qobject = ParsedQObject::try_from(&item).unwrap(); assert_eq!( qobject.qml_metadata, Some(QmlElementMetadata { @@ -367,10 +363,11 @@ pub mod tests { #[test] fn test_qml_metadata_named() { let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_element = "OtherName")] + #[qobject] + #[qml_element = "OtherName"] type MyObject = super::MyObjectRust; }; - let qobject = ParsedQObject::from_foreign_item_type(&item, 0).unwrap(); + let qobject = ParsedQObject::try_from(&item).unwrap(); assert_eq!( qobject.qml_metadata, Some(QmlElementMetadata { @@ -384,10 +381,12 @@ pub mod tests { #[test] fn test_qml_metadata_singleton() { let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_element, qml_singleton)] + #[qobject] + #[qml_element] + #[qml_singleton] type MyObject = super::MyObjectRust; }; - let qobject = ParsedQObject::from_foreign_item_type(&item, 0).unwrap(); + let qobject = ParsedQObject::try_from(&item).unwrap(); assert_eq!( qobject.qml_metadata, Some(QmlElementMetadata { @@ -401,10 +400,12 @@ pub mod tests { #[test] fn test_qml_metadata_uncreatable() { let item: ForeignTypeIdentAlias = parse_quote! { - #[cxx_qt::qobject(qml_element, qml_uncreatable)] + #[qobject] + #[qml_element] + #[qml_uncreatable] type MyObject = super::MyObjectRust; }; - let qobject = ParsedQObject::from_foreign_item_type(&item, 0).unwrap(); + let qobject = ParsedQObject::try_from(&item).unwrap(); assert_eq!( qobject.qml_metadata, Some(QmlElementMetadata { diff --git a/crates/cxx-qt-gen/src/parser/signals.rs b/crates/cxx-qt-gen/src/parser/signals.rs index a0c980f5c..411c0ff30 100644 --- a/crates/cxx-qt-gen/src/parser/signals.rs +++ b/crates/cxx-qt-gen/src/parser/signals.rs @@ -4,7 +4,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::parser::parameter::ParsedFunctionParameter; -use crate::syntax::attribute::attribute_find_path; +use crate::syntax::attribute::attribute_take_path; use crate::syntax::expr::expr_to_string; use crate::syntax::foreignmod; use crate::syntax::safety::Safety; @@ -56,8 +56,6 @@ impl ParsedSignal { )); } - let mut inherit = false; - let self_receiver = foreignmod::self_type_from_foreign_fn(&method.sig)?; let (qobject_ident, mutability) = types::extract_qobject_ident(&self_receiver.ty)?; let mutable = mutability.is_some(); @@ -72,21 +70,14 @@ impl ParsedSignal { let mut ident = CombinedIdent::from_rust_function(method.sig.ident.clone()); - if let Some(index) = attribute_find_path(&method.attrs, &["cxx_name"]) { + if let Some(attr) = attribute_take_path(&mut method.attrs, &["cxx_name"]) { ident.cpp = format_ident!( "{}", - expr_to_string(&method.attrs[index].meta.require_name_value()?.value)? + expr_to_string(&attr.meta.require_name_value()?.value)? ); - - method.attrs.remove(index); - } - - if let Some(index) = attribute_find_path(&method.attrs, &["inherit"]) { - inherit = true; - - method.attrs.remove(index); } + let inherit = attribute_take_path(&mut method.attrs, &["inherit"]).is_some(); let safe = method.sig.unsafety.is_none(); Ok(Self { diff --git a/crates/cxx-qt-gen/src/syntax/attribute.rs b/crates/cxx-qt-gen/src/syntax/attribute.rs index 789d435a7..fb9154687 100644 --- a/crates/cxx-qt-gen/src/syntax/attribute.rs +++ b/crates/cxx-qt-gen/src/syntax/attribute.rs @@ -4,37 +4,7 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::syntax::path::path_compare_str; -use proc_macro2::Span; -use std::collections::HashMap; -use syn::{ - // ext::IdentExt, - parse::{Parse, ParseStream}, - spanned::Spanned, - Attribute, - Error, - Meta, - Result, - Token, -}; - -/// Representation of a key and an optional value in an attribute, eg `attribute(key = value)` or `attribute(key)` -struct AttributeMapValue { - pub key: K, - pub value: Option, -} - -impl Parse for AttributeMapValue { - fn parse(input: ParseStream) -> Result { - let key = input.parse::()?; - let value = if input.peek(Token![=]) { - input.parse::()?; - Some(input.parse::()?) - } else { - None - }; - Ok(AttributeMapValue { key, value }) - } -} +use syn::Attribute; /// Returns the index of the first [syn::Attribute] that matches a given path pub fn attribute_find_path(attrs: &[Attribute], path: &[&str]) -> Option { @@ -47,50 +17,16 @@ pub fn attribute_find_path(attrs: &[Attribute], path: &[&str]) -> Option None } -/// Whether the attribute has a default value if there is one missing -/// -/// This is useful in attribute maps where only a key may be specified. -pub enum AttributeDefault { - Some(fn(Span) -> V), - None, -} - -/// Returns a map of keys and values from an attribute, eg attribute(a = b, c = d) -/// -/// A default value can be specified by using [AttributeDefault]. -pub fn attribute_tokens_to_map( - attr: &Attribute, - default_value: AttributeDefault, -) -> Result> { - if let Meta::List(meta_list) = &attr.meta { - meta_list.parse_args_with(|input: ParseStream| -> Result> { - let mut map = HashMap::new(); - for item in input.parse_terminated(AttributeMapValue::parse, Token![,])? { - if let std::collections::hash_map::Entry::Vacant(e) = map.entry(item.key) { - if let Some(value) = item.value { - e.insert(value); - } else if let AttributeDefault::Some(default_value) = default_value { - e.insert(default_value(attr.span())); - } else { - return Err(Error::new(attr.span(), "Attribute key is missing a value")); - } - } else { - return Err(Error::new(attr.span(), "Duplicate keys in the attributes")); - } - } - Ok(map) - }) - } else { - Ok(HashMap::default()) - } +/// Takes and returns the first [syn::Attribute] that matches a given path +pub fn attribute_take_path(attrs: &mut Vec, path: &[&str]) -> Option { + attribute_find_path(attrs, path).map(|index| attrs.remove(index)) } #[cfg(test)] mod tests { use super::*; - use quote::format_ident; - use syn::{parse_quote, Ident, ItemMod, LitStr}; + use syn::{parse_quote, ItemMod}; #[test] fn test_attribute_find_path() { @@ -109,78 +45,17 @@ mod tests { } #[test] - fn test_attribute_tokens_to_map() { - let module: ItemMod = parse_quote! { + fn test_attribute_take_path() { + let mut module: ItemMod = parse_quote! { #[qinvokable] #[cxx_qt::bridge] #[cxx_qt::object(MyObject)] #[cxx_qt::bridge(namespace = "my::namespace")] - #[cxx_qt::list(A, B, C)] - #[cxx_qt::bridge(a = "b", namespace = "my::namespace")] - #[cxx_qt::bridge(a = "b", namespace = "my::namespace", namespace = "my::namespace")] - #[cxx_qt::bridge()] - #[qinvokable(inner)] mod module; }; - assert_eq!( - attribute_tokens_to_map::(&module.attrs[0], AttributeDefault::None) - .unwrap() - .len(), - 0 - ); - assert_eq!( - attribute_tokens_to_map::(&module.attrs[1], AttributeDefault::None) - .unwrap() - .len(), - 0 - ); - assert!( - attribute_tokens_to_map::(&module.attrs[2], AttributeDefault::None) - .is_err() - ); - - let result = - attribute_tokens_to_map::(&module.attrs[3], AttributeDefault::None) - .unwrap(); - let ident = format_ident!("namespace"); - assert_eq!(result.len(), 1); - assert!(result.contains_key(&ident)); - assert_eq!(result[&ident].value(), "my::namespace"); - - assert!( - attribute_tokens_to_map::(&module.attrs[4], AttributeDefault::None) - .is_err() - ); - - let result = - attribute_tokens_to_map::(&module.attrs[5], AttributeDefault::None) - .unwrap(); - let ident = format_ident!("namespace"); - assert_eq!(result.len(), 2); - assert!(result.contains_key(&ident)); - assert_eq!(result[&ident].value(), "my::namespace"); - - assert!( - attribute_tokens_to_map::(&module.attrs[6], AttributeDefault::None) - .is_err() - ); - - let result = - attribute_tokens_to_map::(&module.attrs[7], AttributeDefault::None) - .unwrap(); - assert_eq!(result.len(), 0); - - assert!( - attribute_tokens_to_map::(&module.attrs[8], AttributeDefault::None) - .is_err() - ); - let result = attribute_tokens_to_map::( - &module.attrs[8], - AttributeDefault::Some(|span| LitStr::new("", span)), - ) - .unwrap(); - assert_eq!(result.len(), 1); - assert!(result.contains_key(&format_ident!("inner"))); + assert_eq!(module.attrs.len(), 4); + assert!(attribute_take_path(&mut module.attrs, &["qinvokable"]).is_some()); + assert_eq!(module.attrs.len(), 3); } } diff --git a/crates/cxx-qt-gen/test_inputs/inheritance.rs b/crates/cxx-qt-gen/test_inputs/inheritance.rs index c28bfd0b9..f3a9cf6e7 100644 --- a/crates/cxx-qt-gen/test_inputs/inheritance.rs +++ b/crates/cxx-qt-gen/test_inputs/inheritance.rs @@ -8,7 +8,8 @@ mod inheritance { } extern "RustQt" { - #[cxx_qt::qobject(base = "QAbstractItemModel")] + #[qobject] + #[base = "QAbstractItemModel"] type MyObject = super::MyObjectRust; } diff --git a/crates/cxx-qt-gen/test_inputs/invokables.rs b/crates/cxx-qt-gen/test_inputs/invokables.rs index 167d9e898..de6aee48a 100644 --- a/crates/cxx-qt-gen/test_inputs/invokables.rs +++ b/crates/cxx-qt-gen/test_inputs/invokables.rs @@ -11,7 +11,7 @@ mod ffi { } unsafe extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; fn cpp_method(self: &MyObject); 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 d5f2134b4..bc2a5bf01 100644 --- a/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs +++ b/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs @@ -80,7 +80,8 @@ pub mod ffi { } extern "RustQt" { - #[cxx_qt::qobject(base = "QStringListModel")] + #[qobject] + #[base = "QStringListModel"] #[qproperty(i32, property_name)] type MyObject = super::MyObjectRust; } @@ -94,7 +95,7 @@ pub mod ffi { } extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] #[qproperty(i32, property_name)] type SecondObject = super::SecondObjectRust; } diff --git a/crates/cxx-qt-gen/test_inputs/properties.rs b/crates/cxx-qt-gen/test_inputs/properties.rs index 9943a1e81..d7ec0204a 100644 --- a/crates/cxx-qt-gen/test_inputs/properties.rs +++ b/crates/cxx-qt-gen/test_inputs/properties.rs @@ -7,7 +7,7 @@ mod ffi { } extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] #[derive(Default)] #[qproperty(i32, primitive)] #[qproperty(QPoint, trivial)] diff --git a/crates/cxx-qt-gen/test_inputs/signals.rs b/crates/cxx-qt-gen/test_inputs/signals.rs index e6d9323a9..ca53aecc4 100644 --- a/crates/cxx-qt-gen/test_inputs/signals.rs +++ b/crates/cxx-qt-gen/test_inputs/signals.rs @@ -7,7 +7,7 @@ mod ffi { } unsafe extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type MyObject = super::MyObjectRust; #[qsignal] diff --git a/crates/cxx-qt-macro/src/lib.rs b/crates/cxx-qt-macro/src/lib.rs index 9911f7d29..8f2378177 100644 --- a/crates/cxx-qt-macro/src/lib.rs +++ b/crates/cxx-qt-macro/src/lib.rs @@ -20,7 +20,7 @@ use cxx_qt_gen::{write_rust, GeneratedRustBlocks, Parser}; /// #[cxx_qt::bridge(namespace = "cxx_qt::my_object")] /// mod qobject { /// unsafe extern "RustQt" { -/// #[cxx_qt::qobject] +/// #[qobject] /// # // Note that we can't use properties as this confuses the linker on Windows /// type MyObject = super::MyObjectRust; /// @@ -73,7 +73,7 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { /// #[cxx_qt::bridge] /// mod my_object { /// extern "RustQt" { -/// #[cxx_qt::qobject] +/// #[qobject] /// # // Note that we can't use properties as this confuses the linker on Windows /// type MyObject = super::MyObjectRust; /// } @@ -86,7 +86,7 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { /// # fn main() {} /// ``` /// -/// You can also specify a custom base class by using `#[cxx_qt::qobject(base = "QStringListModel")]`, you must then use CXX to add any includes needed. +/// You can also specify a custom base class by using `#[base = "QStringListModel"]`, you must then use CXX to add any includes needed. /// /// # Example /// @@ -94,7 +94,8 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { /// #[cxx_qt::bridge] /// mod my_object { /// extern "RustQt" { -/// #[cxx_qt::qobject(base = "QStringListModel")] +/// #[qobject] +/// #[base = "QStringListModel"] /// # // Note that we can't use properties as this confuses the linker on Windows /// type MyModel = super::MyModelRust; /// } @@ -112,7 +113,7 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn qobject(_args: TokenStream, _input: TokenStream) -> TokenStream { - unreachable!("cxx_qt::qobject should not be used as a macro by itself. Instead it should be used within a cxx_qt::bridge definition") + unreachable!("qobject should not be used as a macro by itself. Instead it should be used within a cxx_qt::bridge definition") } // Take the module and C++ namespace and generate the rust code diff --git a/crates/cxx-qt/src/lib.rs b/crates/cxx-qt/src/lib.rs index 560bac010..85d70c1e7 100644 --- a/crates/cxx-qt/src/lib.rs +++ b/crates/cxx-qt/src/lib.rs @@ -16,7 +16,7 @@ pub use cxx_qt_macro::qobject; pub use cxxqtthread::CxxQtThread; -/// This trait is automatically implemented for all types which are marked as `#[cxx_qt::qobject]`. +/// This trait is automatically implemented for all types which are marked as `#[qobject]`. /// It provides information about the type that is wrapped by the QObject, as well as the methods /// that Cxx-Qt will generate for the QObject. pub trait CxxQtType { @@ -94,7 +94,7 @@ pub trait Threading: Locking + Sized { /// #[cxx_qt::bridge] /// mod qobject { /// extern "RustQt" { -/// #[cxx_qt::qobject] +/// #[qobject] /// type MyStruct = super::MyStructRust; /// } /// diff --git a/examples/demo_threading/rust/src/lib.rs b/examples/demo_threading/rust/src/lib.rs index c5823b0d8..a726da8ee 100644 --- a/examples/demo_threading/rust/src/lib.rs +++ b/examples/demo_threading/rust/src/lib.rs @@ -18,7 +18,8 @@ pub mod qobject { } extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(f64, average_use)] #[qproperty(u32, sensors)] #[qproperty(f64, total_use)] diff --git a/examples/qml_extension_plugin/plugin/rust/src/lib.rs b/examples/qml_extension_plugin/plugin/rust/src/lib.rs index 287681d5e..51cbcc2ed 100644 --- a/examples/qml_extension_plugin/plugin/rust/src/lib.rs +++ b/examples/qml_extension_plugin/plugin/rust/src/lib.rs @@ -35,7 +35,7 @@ pub mod qobject { } unsafe extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] #[qproperty(i32, number)] #[qproperty(QString, string)] type MyObject = super::MyObjectRust; diff --git a/examples/qml_features/rust/src/containers.rs b/examples/qml_features/rust/src/containers.rs index c5533b5e5..2079b48ea 100644 --- a/examples/qml_features/rust/src/containers.rs +++ b/examples/qml_features/rust/src/containers.rs @@ -33,7 +33,8 @@ pub mod qobject { } unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(QString, string_hash)] #[qproperty(QString, string_list)] #[qproperty(QString, string_map)] diff --git a/examples/qml_features/rust/src/custom_base_class.rs b/examples/qml_features/rust/src/custom_base_class.rs index 8a8780832..194e3c4e7 100644 --- a/examples/qml_features/rust/src/custom_base_class.rs +++ b/examples/qml_features/rust/src/custom_base_class.rs @@ -34,7 +34,9 @@ pub mod qobject { // ANCHOR: book_inherit_qalm // ANCHOR: book_qobject_base extern "RustQt" { - #[cxx_qt::qobject(base = "QAbstractListModel", qml_element)] + #[qobject] + #[base = "QAbstractListModel"] + #[qml_element] type CustomBaseClass = super::CustomBaseClassRust; } // ANCHOR_END: book_qobject_base diff --git a/examples/qml_features/rust/src/custom_parent_class.rs b/examples/qml_features/rust/src/custom_parent_class.rs index dfedb1833..0508a53e1 100644 --- a/examples/qml_features/rust/src/custom_parent_class.rs +++ b/examples/qml_features/rust/src/custom_parent_class.rs @@ -43,11 +43,8 @@ pub mod qobject { } unsafe extern "RustQt" { - #[cxx_qt::qobject( - base = "QQuickPaintedItem", - qml_uri = "com.kdab.cxx_qt.demo", - qml_version = "1.0" - )] + #[qobject] + #[base = "QQuickPaintedItem"] #[qproperty(QColor, color)] type CustomParentClass = super::CustomParentClassRust; diff --git a/examples/qml_features/rust/src/invokables.rs b/examples/qml_features/rust/src/invokables.rs index 40584f3da..4863450e4 100644 --- a/examples/qml_features/rust/src/invokables.rs +++ b/examples/qml_features/rust/src/invokables.rs @@ -16,7 +16,8 @@ pub mod qobject { } unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] type RustInvokables = super::RustInvokablesRust; } diff --git a/examples/qml_features/rust/src/multiple_qobjects.rs b/examples/qml_features/rust/src/multiple_qobjects.rs index bb31bdc0e..df4473251 100644 --- a/examples/qml_features/rust/src/multiple_qobjects.rs +++ b/examples/qml_features/rust/src/multiple_qobjects.rs @@ -18,7 +18,8 @@ pub mod qobject { } extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(i32, counter)] #[qproperty(QColor, color)] type FirstObject = super::FirstObjectRust; @@ -44,7 +45,8 @@ pub mod qobject { } extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(i32, counter)] #[qproperty(QUrl, url)] type SecondObject = super::SecondObjectRust; diff --git a/examples/qml_features/rust/src/nested_qobjects.rs b/examples/qml_features/rust/src/nested_qobjects.rs index 7d9af1fd6..665edb498 100644 --- a/examples/qml_features/rust/src/nested_qobjects.rs +++ b/examples/qml_features/rust/src/nested_qobjects.rs @@ -11,7 +11,8 @@ pub mod qobject { // ANCHOR: book_extern_block extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(i32, counter)] type InnerObject = super::InnerObjectRust; } @@ -24,7 +25,8 @@ pub mod qobject { } extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(*mut InnerObject, inner)] type OuterObject = super::OuterObjectRust; diff --git a/examples/qml_features/rust/src/properties.rs b/examples/qml_features/rust/src/properties.rs index 2d5eb292d..ef8812d3f 100644 --- a/examples/qml_features/rust/src/properties.rs +++ b/examples/qml_features/rust/src/properties.rs @@ -19,7 +19,8 @@ pub mod qobject { unsafe extern "RustQt" { // ANCHOR: book_properties_struct - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(bool, connected)] #[qproperty(QUrl, connected_url)] #[qproperty(QUrl, previous_connected_url)] diff --git a/examples/qml_features/rust/src/serialisation.rs b/examples/qml_features/rust/src/serialisation.rs index 1222e1c28..3504b59fe 100644 --- a/examples/qml_features/rust/src/serialisation.rs +++ b/examples/qml_features/rust/src/serialisation.rs @@ -36,7 +36,8 @@ pub mod qobject { } unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(i32, number)] #[qproperty(QString, string)] type Serialisation = super::SerialisationRust; diff --git a/examples/qml_features/rust/src/signals.rs b/examples/qml_features/rust/src/signals.rs index 4d9daa028..7a9a989e5 100644 --- a/examples/qml_features/rust/src/signals.rs +++ b/examples/qml_features/rust/src/signals.rs @@ -36,7 +36,8 @@ pub mod qobject { unsafe extern "RustQt" { // ANCHOR: book_signals_struct - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(bool, logging_enabled)] type RustSignals = super::RustSignalsRust; } diff --git a/examples/qml_features/rust/src/singleton.rs b/examples/qml_features/rust/src/singleton.rs index edca3d2b8..9eefb0f28 100644 --- a/examples/qml_features/rust/src/singleton.rs +++ b/examples/qml_features/rust/src/singleton.rs @@ -10,7 +10,9 @@ #[cxx_qt::bridge(cxx_file_stem = "rust_singleton")] pub mod qobject { unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_element, qml_singleton)] + #[qobject] + #[qml_element] + #[qml_singleton] #[qproperty(i32, persistent_value)] type RustSingleton = super::RustSingletonRust; diff --git a/examples/qml_features/rust/src/threading.rs b/examples/qml_features/rust/src/threading.rs index f2e8c92f9..90e319bcb 100644 --- a/examples/qml_features/rust/src/threading.rs +++ b/examples/qml_features/rust/src/threading.rs @@ -22,7 +22,8 @@ pub mod qobject { } extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(QString, title)] #[qproperty(QUrl, url)] type ThreadingWebsite = super::ThreadingWebsiteRust; diff --git a/examples/qml_features/rust/src/types.rs b/examples/qml_features/rust/src/types.rs index c0c8e81b7..220dd4bec 100644 --- a/examples/qml_features/rust/src/types.rs +++ b/examples/qml_features/rust/src/types.rs @@ -74,7 +74,8 @@ pub mod ffi { } unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(bool, boolean)] #[qproperty(QPointF, point)] #[qproperty(QUrl, url)] diff --git a/examples/qml_features/rust/src/uncreatable.rs b/examples/qml_features/rust/src/uncreatable.rs index 33ab655a1..68f8da92e 100644 --- a/examples/qml_features/rust/src/uncreatable.rs +++ b/examples/qml_features/rust/src/uncreatable.rs @@ -10,7 +10,9 @@ #[cxx_qt::bridge(cxx_file_stem = "rust_uncreatable")] pub mod ffi { extern "RustQt" { - #[cxx_qt::qobject(qml_element, qml_uncreatable)] + #[qobject] + #[qml_element] + #[qml_uncreatable] #[qproperty(i32, value)] type RustUncreatable = super::RustUncreatableRust; } diff --git a/examples/qml_minimal/qml/main.qml b/examples/qml_minimal/qml/main.qml index e4ab8035f..4151a7fe4 100644 --- a/examples/qml_minimal/qml/main.qml +++ b/examples/qml_minimal/qml/main.qml @@ -11,7 +11,7 @@ import QtQuick.Window 2.12 // ANCHOR: book_qml_import // This must match the qml_uri and qml_version -// specified with the #[cxx_qt::qobject] macro in Rust. +// specified with the #[qobject] macro in Rust. import com.kdab.cxx_qt.demo 1.0 // ANCHOR_END: book_qml_import diff --git a/examples/qml_minimal/rust/src/cxxqt_object.rs b/examples/qml_minimal/rust/src/cxxqt_object.rs index 38f4649de..9fb7a6a48 100644 --- a/examples/qml_minimal/rust/src/cxxqt_object.rs +++ b/examples/qml_minimal/rust/src/cxxqt_object.rs @@ -23,7 +23,8 @@ pub mod qobject { // ANCHOR: book_rustobj_struct_signature unsafe extern "RustQt" { - #[cxx_qt::qobject(qml_element)] + #[qobject] + #[qml_element] #[qproperty(i32, number)] #[qproperty(QString, string)] type MyObject = super::MyObjectRust; diff --git a/tests/basic_cxx_qt/rust/src/data.rs b/tests/basic_cxx_qt/rust/src/data.rs index 1c1b468bd..1b4112299 100644 --- a/tests/basic_cxx_qt/rust/src/data.rs +++ b/tests/basic_cxx_qt/rust/src/data.rs @@ -31,7 +31,7 @@ mod qobject { } unsafe extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] #[qproperty(i32, number)] #[qproperty(QString, string)] type MyData = super::MyDataRust; diff --git a/tests/basic_cxx_qt/rust/src/empty.rs b/tests/basic_cxx_qt/rust/src/empty.rs index 598b5d998..a54e74d1d 100644 --- a/tests/basic_cxx_qt/rust/src/empty.rs +++ b/tests/basic_cxx_qt/rust/src/empty.rs @@ -6,7 +6,7 @@ #[cxx_qt::bridge(cxx_file_stem = "empty")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] type Empty = super::EmptyRust; } } diff --git a/tests/basic_cxx_qt/rust/src/lib.rs b/tests/basic_cxx_qt/rust/src/lib.rs index 5a7937aa5..b4c81e491 100644 --- a/tests/basic_cxx_qt/rust/src/lib.rs +++ b/tests/basic_cxx_qt/rust/src/lib.rs @@ -18,7 +18,7 @@ mod qobject { } unsafe extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] #[qproperty(i32, number)] #[qproperty(QString, string)] type MyObject = super::MyObjectRust; diff --git a/tests/basic_cxx_qt/rust/src/locking.rs b/tests/basic_cxx_qt/rust/src/locking.rs index 700b4dd51..e3b8db181 100644 --- a/tests/basic_cxx_qt/rust/src/locking.rs +++ b/tests/basic_cxx_qt/rust/src/locking.rs @@ -8,7 +8,7 @@ pub mod qobject { unsafe extern "RustQt" { /// A QObject which has cxx_qt::Locking - #[cxx_qt::qobject] + #[qobject] type RustLockingEnabled = super::RustLockingEnabledRust; fn get_counter(self: &RustLockingEnabled) -> u32; @@ -18,7 +18,7 @@ pub mod qobject { unsafe extern "RustQt" { /// A QObject which has !cxx_qt::Locking - #[cxx_qt::qobject] + #[qobject] type RustLockingDisabled = super::RustLockingDisabledRust; fn get_counter(self: &RustLockingDisabled) -> u32; diff --git a/tests/basic_cxx_qt/rust/src/types.rs b/tests/basic_cxx_qt/rust/src/types.rs index 4b48b91d4..bddd2e070 100644 --- a/tests/basic_cxx_qt/rust/src/types.rs +++ b/tests/basic_cxx_qt/rust/src/types.rs @@ -7,7 +7,7 @@ #[cxx_qt::bridge(cxx_file_stem = "my_types", namespace = "cxx_qt::my_types")] mod ffi { extern "RustQt" { - #[cxx_qt::qobject] + #[qobject] #[qproperty(bool, boolean)] #[qproperty(f32, float_32)] #[qproperty(f64, float_64)]