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