From bb49c51c1f3465811e1f950812e313ed23147127 Mon Sep 17 00:00:00 2001 From: Andrew Hayzen Date: Wed, 7 Jun 2023 18:30:58 +0100 Subject: [PATCH] cxx-qt-gen: use extern "RustQt" block for inherit and signals Closes #557 --- CHANGELOG.md | 1 + book/src/SUMMARY.md | 2 +- book/src/concepts/inheritance.md | 10 +- .../src/getting-started/1-qobjects-in-rust.md | 2 +- book/src/qobject/index.md | 2 +- book/src/qobject/qobject_struct.md | 2 +- book/src/qobject/signals.md | 6 +- crates/cxx-qt-gen/src/parser/cxxqtdata.rs | 148 +++++++++--------- crates/cxx-qt-gen/src/parser/inherit.rs | 109 +------------ crates/cxx-qt-gen/src/parser/mod.rs | 12 +- crates/cxx-qt-gen/src/parser/signals.rs | 68 +------- crates/cxx-qt-gen/src/syntax/attribute.rs | 6 +- crates/cxx-qt-gen/test_inputs/inheritance.rs | 8 +- .../test_inputs/passthrough_and_naming.rs | 8 +- crates/cxx-qt-gen/test_inputs/signals.rs | 6 +- crates/cxx-qt-macro/src/lib.rs | 70 --------- crates/cxx-qt/src/lib.rs | 2 - examples/demo_threading/rust/src/lib.rs | 6 +- .../rust/src/custom_base_class.rs | 18 ++- .../rust/src/multiple_qobjects.rs | 10 +- .../qml_features/rust/src/nested_qobjects.rs | 8 +- .../qml_features/rust/src/serialisation.rs | 4 +- examples/qml_features/rust/src/signals.rs | 6 +- 23 files changed, 141 insertions(+), 373 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb84f428d..8449dbc64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Always call `qt_build_utils::setup_linker()` in `CxxQtBuilder` and remove the proxy method - Moved to `syn` 2.0 internally and for any exported `syn` types - `impl cxx_qt::Threading for qobject::T` now needs to be specified for `qt_thread()` to be available +- `#[cxx_qt::qsignals]` and `#[cxx_qt::inherit]` are now used in an `extern "RustQt"` block as `#[qsignal]` and `#[inherit]` ### Removed diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md index 3ef59c04d..614ea9a60 100644 --- a/book/src/SUMMARY.md +++ b/book/src/SUMMARY.md @@ -20,7 +20,7 @@ SPDX-License-Identifier: MIT OR Apache-2.0 - [QObject](./qobject/index.md) - [`#[cxx_qt::bridge]` - Bridge Macro](./qobject/bridge-macro.md) - [`#[cxx_qt::qobject]` - Defining QObjects](./qobject/qobject_struct.md) - - [`#[cxx_qt::qsignals]` - Signals macro](./qobject/signals.md) + - [`#[qsignal]` - Signal macro](./qobject/signals.md) - [`qobject::T` - The generated QObject](./qobject/generated-qobject.md) - [CxxQtThread](./qobject/cxxqtthread.md) - [Concepts](./concepts/index.md) diff --git a/book/src/concepts/inheritance.md b/book/src/concepts/inheritance.md index 073043df1..0a1b9efd5 100644 --- a/book/src/concepts/inheritance.md +++ b/book/src/concepts/inheritance.md @@ -12,8 +12,8 @@ Some Qt APIs require you to override certain methods from an abstract base class To support creating such subclasses directly from within Rust, CXX-Qt provides you with multiple helpers. ## Accessing base class methods -To access the methods of a base class in Rust, use the `#[cxx_qt::inherit]` macro. -It can be placed in front of an `extern "C++"` block in a `#[cxx_qt::bridge]`. +To access the methods of a base class in Rust, use the `#[inherit]` macro. +It can be placed in front of a function in a `extern "RustQt"` block in a `#[cxx_qt::bridge]`. ```rust,ignore {{#include ../../../examples/qml_features/rust/src/custom_base_class.rs:book_inherit_qalm}} @@ -27,10 +27,10 @@ It can be placed in front of an `extern "C++"` block in a `#[cxx_qt::bridge]`. [Full example](https://github.com/KDAB/cxx-qt/blob/main/examples/qml_features/rust/src/custom_base_class.rs) This code implements a QAbstractListModel subclass. -For this, the `clear` method implemented in Rust needs to call `beginResetModel` and related methods from the base class, which are made accessible by using `#[cxx_qt::inherit]`. +For this, the `clear` method implemented in Rust needs to call `beginResetModel` and related methods from the base class, which are made accessible by using `#[inherit]`. See [the Qt docs](https://doc.qt.io/qt-6/qabstractlistmodel.html) for more details on the specific subclassing requirements. -Methods can be declared inside `#[cxx_qt::inherit]` in `extern "C++"` blocks similar to CXX, with the same restrictions regarding which types can be used. +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]` The declared methods will be case-converted as in other CXX-Qt APIs. @@ -58,7 +58,7 @@ The below example overrides the [`data`](https://doc.qt.io/qt-6/qabstractitemmod ``` [Full example](https://github.com/KDAB/cxx-qt/blob/main/examples/qml_features/rust/src/custom_base_class.rs) -When a method is overridden using `cxx_override`, the base class version of the method can be accessed by using `#[cxx_qt::inherit]` in combination with the `#[cxx_name]` attribute. +When a method is overridden using `cxx_override`, the base class version of the method can be accessed by using `#[inherit]` in combination with the `#[cxx_name]` attribute. In this case the base class version of the function must get a different name because Rust can't have two functions with the same name on one type. Example: diff --git a/book/src/getting-started/1-qobjects-in-rust.md b/book/src/getting-started/1-qobjects-in-rust.md index 368c7a484..41554a27a 100644 --- a/book/src/getting-started/1-qobjects-in-rust.md +++ b/book/src/getting-started/1-qobjects-in-rust.md @@ -47,7 +47,7 @@ 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. - `#[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). -- `#[cxx_qt::qsignals]` - Define the [Signals](https://doc.qt.io/qt-6/signalsandslots.html#signals) of a QObject T. + - `#[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. diff --git a/book/src/qobject/index.md b/book/src/qobject/index.md index ef5770158..345f5b4e5 100644 --- a/book/src/qobject/index.md +++ b/book/src/qobject/index.md @@ -15,7 +15,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) - * [`#[cxx_qt::qsignals]` - A macro for defining signals](./signals.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 ee3b3d300..44448ab86 100644 --- a/book/src/qobject/qobject_struct.md +++ b/book/src/qobject/qobject_struct.md @@ -27,7 +27,7 @@ The macro does multiple other things for you though: - 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 signals if paired with a [`#[cxx_qt::qsignals]` macro](./signals.md). +- 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. diff --git a/book/src/qobject/signals.md b/book/src/qobject/signals.md index 0850b23b2..76d911e53 100644 --- a/book/src/qobject/signals.md +++ b/book/src/qobject/signals.md @@ -5,9 +5,9 @@ SPDX-FileContributor: Andrew Hayzen SPDX-License-Identifier: MIT OR Apache-2.0 --> -# Signals enum +# Signals -The `cxx_qt::qsignals` attribute is used on an `extern "C++"` block to define [signals](https://doc.qt.io/qt-6/signalsandslots.html) for the a QObject. +The `qsignal` attribute is used in an `extern "RustQt"` block to define [signals](https://doc.qt.io/qt-6/signalsandslots.html) for the a QObject. ```rust,ignore,noplayground {{#include ../../../examples/qml_features/rust/src/signals.rs:book_signals_block}} @@ -57,7 +57,7 @@ In this case, it is no longer possible to disconnect later. ## Emitting a signal -Call the function signature defined in the `extern "C++` block to emit the signal. +Call the function signature defined in the `extern "RustQt"` block to emit the signal. Note that these are defined on the generated QObject [`qobject::T`](./generated-qobject.md), so can be called from any mutable `#[qinvokable]`. diff --git a/crates/cxx-qt-gen/src/parser/cxxqtdata.rs b/crates/cxx-qt-gen/src/parser/cxxqtdata.rs index 92f30ff3b..96ddd26ff 100644 --- a/crates/cxx-qt-gen/src/parser/cxxqtdata.rs +++ b/crates/cxx-qt-gen/src/parser/cxxqtdata.rs @@ -4,18 +4,14 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 use crate::syntax::foreignmod::foreign_mod_to_foreign_item_types; +use crate::syntax::safety::Safety; use crate::syntax::{attribute::attribute_find_path, path::path_to_single_ident}; use crate::{ - parser::{ - inherit::{InheritMethods, ParsedInheritedMethod}, - qobject::ParsedQObject, - signals::{ParsedSignal, SignalMethods}, - }, + parser::{inherit::ParsedInheritedMethod, qobject::ParsedQObject, signals::ParsedSignal}, syntax::expr::expr_to_string, }; -use proc_macro2::TokenStream; -use quote::ToTokens; use std::collections::BTreeMap; +use syn::ForeignItem; use syn::{ spanned::Spanned, Attribute, Error, Ident, Item, ItemForeignMod, ItemImpl, Result, Type, TypePath, @@ -184,75 +180,60 @@ impl ParsedCxxQtData { } } - fn parse_inherit_mod(&mut self, tokens: TokenStream) -> Result<()> { - let inherited: InheritMethods = syn::parse2(tokens)?; - - self.add_inherited_methods(inherited) - } - - fn add_inherited_methods(&mut self, inherited: InheritMethods) -> Result<()> { - for method in inherited.base_functions.into_iter() { - let parsed_inherited_method = ParsedInheritedMethod::parse(method, inherited.safety)?; - - if let Some(ref mut qobject) = self - .qobjects - .get_mut(&parsed_inherited_method.qobject_ident) - { - qobject.inherited_methods.push(parsed_inherited_method); - } else { - return Err(Error::new_spanned( - parsed_inherited_method.qobject_ident, - "No QObject with this name found.", - )); + fn parse_foreign_mod(&mut self, foreign_mod: ItemForeignMod) -> Result> { + if let Some(lit_str) = &foreign_mod.abi.name { + match lit_str.value().as_str() { + "RustQt" => { + self.parse_foreign_mod_rust_qt(foreign_mod)?; + return Ok(None); + } + // TODO: look for "C++Qt" later + _others => {} } } - Ok(()) - } - - fn parse_signals_mod(&mut self, tokens: TokenStream) -> Result<()> { - let signals: SignalMethods = syn::parse2(tokens)?; - self.add_signal_methods(signals) - } - - fn add_signal_methods(&mut self, signals: SignalMethods) -> Result<()> { - for method in signals.base_functions.into_iter() { - let parsed_signal_method = ParsedSignal::parse(method, signals.safety)?; - - if let Some(ref mut qobject) = - self.qobjects.get_mut(&parsed_signal_method.qobject_ident) - { - qobject.signals.push(parsed_signal_method); - } else { - return Err(Error::new_spanned( - parsed_signal_method.qobject_ident, - "No QObject with this name found.", - )); - } - } - Ok(()) + Ok(Some(Item::ForeignMod(foreign_mod))) } - fn parse_foreign_mod(&mut self, mut foreign_mod: ItemForeignMod) -> Result> { - // Check if the foreign mod has cxx_qt::inherit on it - if let Some(index) = attribute_find_path(&foreign_mod.attrs, &["cxx_qt", "inherit"]) { - // Remove the inherit attribute - foreign_mod.attrs.remove(index); - - self.parse_inherit_mod(foreign_mod.into_token_stream())?; - return Ok(None); - } + fn parse_foreign_mod_rust_qt(&mut self, mut foreign_mod: ItemForeignMod) -> Result<()> { + let safe_call = if foreign_mod.unsafety.is_some() { + Safety::Safe + } else { + Safety::Unsafe + }; - // Check if the foreign mod has cxx_qt::qsignals on it - if let Some(index) = attribute_find_path(&foreign_mod.attrs, &["cxx_qt", "qsignals"]) { - // Remove the signals attribute - foreign_mod.attrs.remove(index); + 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); + + let parsed_signal_method = ParsedSignal::parse(foreign_fn, safe_call)?; + + self.with_qobject(&parsed_signal_method.qobject_ident)? + .signals + .push(parsed_signal_method); + // 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); + + let parsed_inherited_method = + ParsedInheritedMethod::parse(foreign_fn, safe_call)?; + + self.with_qobject(&parsed_inherited_method.qobject_ident)? + .inherited_methods + .push(parsed_inherited_method); + } - self.parse_signals_mod(foreign_mod.into_token_stream())?; - return Ok(None); + // TODO: test for qinvokable later + } } - Ok(Some(Item::ForeignMod(foreign_mod))) + Ok(()) } /// Parse a [syn::ItemImpl] into the qobjects if it's a CXX-Qt implementation @@ -287,6 +268,17 @@ impl ParsedCxxQtData { Ok(Some(Item::Impl(imp))) } + + fn with_qobject(&mut self, qobject_ident: &Ident) -> Result<&mut ParsedQObject> { + if let Some(qobject) = self.qobjects.get_mut(qobject_ident) { + Ok(qobject) + } else { + Err(Error::new_spanned( + qobject_ident, + "No QObject with this name found.", + )) + } + } } #[cfg(test)] @@ -732,17 +724,18 @@ mod tests { let mut cxxqtdata = create_parsed_cxx_qt_data(); let unsafe_block: Item = parse_quote! { - #[cxx_qt::inherit] - unsafe extern "C++" { + unsafe extern "RustQt" { + #[inherit] fn test(self: &qobject::MyObject); + #[inherit] fn with_args(self: &qobject::MyObject, arg: i32); } }; let safe_block: Item = parse_quote! { - #[cxx_qt::inherit] - extern "C++" { + extern "RustQt" { #[cxx_name="withRename"] + #[inherit] unsafe fn with_rename(self: Pin<&mut qobject::MyObject>, arg: i32); } }; @@ -771,12 +764,13 @@ mod tests { fn test_parse_qsignals_safe() { let mut cxxqtdata = create_parsed_cxx_qt_data(); let block: Item = parse_quote! { - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { + #[qsignal] fn ready(self: Pin<&mut qobject::MyObject>); #[cxx_name="cppDataChanged"] #[inherit] + #[qsignal] fn data_changed(self: Pin<&mut qobject::MyObject>, data: i32); } }; @@ -815,8 +809,8 @@ mod tests { fn test_parse_qsignals_unknown_obj() { let mut cxxqtdata = create_parsed_cxx_qt_data(); let block: Item = parse_quote! { - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { + #[qsignal] fn ready(self: Pin<&mut qobject::UnknownObj>); } }; @@ -827,8 +821,8 @@ mod tests { fn test_parse_qsignals_unsafe() { let mut cxxqtdata = create_parsed_cxx_qt_data(); let block: Item = parse_quote! { - #[cxx_qt::qsignals] - extern "C++" { + extern "RustQt" { + #[qsignal] unsafe fn unsafe_signal(self: Pin<&mut qobject::MyObject>, arg: *mut T); } }; diff --git a/crates/cxx-qt-gen/src/parser/inherit.rs b/crates/cxx-qt-gen/src/parser/inherit.rs index 6e0ae85ec..b33218a45 100644 --- a/crates/cxx-qt-gen/src/parser/inherit.rs +++ b/crates/cxx-qt-gen/src/parser/inherit.rs @@ -11,74 +11,9 @@ use crate::{ }, }; use quote::format_ident; -use syn::{ - parse::{Parse, ParseStream}, - spanned::Spanned, - Attribute, Error, ForeignItem, ForeignItemFn, Ident, ItemForeignMod, LitStr, Result, Token, -}; - -/// This type is used when parsing the `#[cxx_qt::inherit]` macro contents into raw ForeignItemFn items -pub struct InheritMethods { - pub safety: Safety, - pub base_functions: Vec, -} - -impl Parse for InheritMethods { - fn parse(input: ParseStream) -> Result { - let mut base_functions = Vec::new(); - - // Ensure that any attributes on the block have been removed - // - // Otherwise parsing of unsafe can fail due to #[doc] - let attrs = input.call(Attribute::parse_outer)?; - if !attrs.is_empty() { - return Err(Error::new( - attrs.first().span(), - "Unexpected attribute on #[cxx_qt::qsignals] block.", - )); - } - - // This looks somewhat counter-intuitive, but if we add `unsafe` - // to the `extern "C++"` block, the contained functions will be safe to call. - let safety = if input.peek(Token![unsafe]) { - Safety::Safe - } else { - Safety::Unsafe - }; - if safety == Safety::Safe { - input.parse::()?; - } - - let extern_block = input.parse::()?; - if extern_block.abi.name != Some(LitStr::new("C++", extern_block.abi.span())) { - return Err(Error::new( - extern_block.abi.span(), - "Inherit blocks must be marked with `extern \"C++\"`", - )); - } - - for item in extern_block.items { - match item { - ForeignItem::Fn(function) => { - base_functions.push(function); - } - _ => { - return Err(Error::new( - item.span(), - "Only functions are allowed in #[cxx_qt::inherit] blocks", - )) - } - } - } +use syn::{spanned::Spanned, Error, ForeignItemFn, Ident, Result}; - Ok(InheritMethods { - safety, - base_functions, - }) - } -} - -/// Describes a method found in #[cxx_qt::inherit] +/// Describes a method found in an extern "RustQt" with #[inherit] pub struct ParsedInheritedMethod { /// The original [syn::ForeignItemFn] of the inherited method declaration pub method: ForeignItemFn, @@ -141,46 +76,8 @@ impl ParsedInheritedMethod { #[cfg(test)] mod tests { use super::*; - use quote::quote; - use syn::parse_quote; - - #[test] - fn test_parse_unsafe_mod() { - let module = quote! { - extern "C++" { - unsafe fn test(self: &qobject::T); - } - }; - let parsed: InheritMethods = syn::parse2(module).unwrap(); - assert_eq!(parsed.base_functions.len(), 1); - assert_eq!(parsed.safety, Safety::Unsafe); - } - #[test] - fn test_parse_safe_mod() { - let module = quote! { - unsafe extern "C++" { - fn test(self: &qobject::T); - unsafe fn test2(self: &qobject::T); - } - }; - let parsed: InheritMethods = syn::parse2(module).unwrap(); - assert_eq!(parsed.base_functions.len(), 2); - assert_eq!(parsed.safety, Safety::Safe); - } - - #[test] - fn test_parse_attributes() { - let module = quote! { - unsafe extern "C++" { - #[attribute] - fn test(self: &qobject::T); - } - }; - let parsed: InheritMethods = syn::parse2(module).unwrap(); - assert_eq!(parsed.base_functions.len(), 1); - assert_eq!(parsed.base_functions[0].attrs.len(), 1); - } + use syn::parse_quote; fn assert_parse_error(function: ForeignItemFn) { let result = ParsedInheritedMethod::parse(function, Safety::Safe); diff --git a/crates/cxx-qt-gen/src/parser/mod.rs b/crates/cxx-qt-gen/src/parser/mod.rs index 01240e5c9..fe3ee11bd 100644 --- a/crates/cxx-qt-gen/src/parser/mod.rs +++ b/crates/cxx-qt-gen/src/parser/mod.rs @@ -162,8 +162,8 @@ mod tests { #[cxx_qt::qobject] pub struct MyObject; - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { + #[qsignal] fn ready(self: Pin<&mut qobject::MyObject>); } } @@ -187,8 +187,8 @@ mod tests { #[cxx_qt::qobject] pub struct MyObject; - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { + #[qsignal] fn ready(self: Pin<&mut qobject::MyObject>); } @@ -216,8 +216,8 @@ mod tests { #[cxx_qt::qobject] pub struct MyObject; - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { + #[qsignal] fn ready(self: Pin<&mut qobject::UnknownObject>); } } diff --git a/crates/cxx-qt-gen/src/parser/signals.rs b/crates/cxx-qt-gen/src/parser/signals.rs index ae7830cb7..dc0720286 100644 --- a/crates/cxx-qt-gen/src/parser/signals.rs +++ b/crates/cxx-qt-gen/src/parser/signals.rs @@ -10,73 +10,7 @@ use crate::syntax::foreignmod; use crate::syntax::safety::Safety; use crate::{generator::naming::CombinedIdent, syntax::types}; use quote::format_ident; -use syn::Attribute; -use syn::{ - parse::{Parse, ParseStream}, - spanned::Spanned, - Error, ForeignItem, ForeignItemFn, Ident, ItemForeignMod, LitStr, Result, Token, -}; - -/// This type is used when parsing the `#[cxx_qt::qsignals]` macro contents into raw ForeignItemFn items -pub struct SignalMethods { - pub safety: Safety, - pub base_functions: Vec, -} - -impl Parse for SignalMethods { - fn parse(input: ParseStream) -> Result { - let mut base_functions = Vec::new(); - - // Ensure that any attributes on the block have been removed - // - // Otherwise parsing of unsafe can fail due to #[doc] - let attrs = input.call(Attribute::parse_outer)?; - if !attrs.is_empty() { - return Err(Error::new( - attrs.first().span(), - "Unexpected attribute on #[cxx_qt::inherit] block.", - )); - } - - // This looks somewhat counter-intuitive, but if we add `unsafe` - // to the `extern "C++"` block, the contained functions will be safe to call. - let safety = if input.peek(Token![unsafe]) { - Safety::Safe - } else { - Safety::Unsafe - }; - if safety == Safety::Safe { - input.parse::()?; - } - - let extern_block = input.parse::()?; - if extern_block.abi.name != Some(LitStr::new("C++", extern_block.abi.span())) { - return Err(Error::new( - extern_block.abi.span(), - "qsignals blocks must be marked with `extern \"C++\"`", - )); - } - - for item in extern_block.items { - match item { - ForeignItem::Fn(function) => { - base_functions.push(function); - } - _ => { - return Err(Error::new( - item.span(), - "Only functions are allowed in #[cxx_qt::qsignals] blocks", - )) - } - } - } - - Ok(SignalMethods { - safety, - base_functions, - }) - } -} +use syn::{spanned::Spanned, Error, ForeignItemFn, Ident, Result}; /// Describes an individual Signal pub struct ParsedSignal { diff --git a/crates/cxx-qt-gen/src/syntax/attribute.rs b/crates/cxx-qt-gen/src/syntax/attribute.rs index 4d17dfb89..a8bac4307 100644 --- a/crates/cxx-qt-gen/src/syntax/attribute.rs +++ b/crates/cxx-qt-gen/src/syntax/attribute.rs @@ -97,14 +97,14 @@ mod tests { let module: ItemMod = parse_quote! { #[qinvokable] #[cxx_qt::bridge] - #[cxx_qt::qsignals(MyObject)] + #[cxx_qt::object(MyObject)] #[cxx_qt::bridge(namespace = "my::namespace")] mod module; }; assert!(attribute_find_path(&module.attrs, &["qinvokable"]).is_some()); assert!(attribute_find_path(&module.attrs, &["cxx_qt", "bridge"]).is_some()); - assert!(attribute_find_path(&module.attrs, &["cxx_qt", "qsignals"]).is_some()); + assert!(attribute_find_path(&module.attrs, &["cxx_qt", "object"]).is_some()); assert!(attribute_find_path(&module.attrs, &["cxx_qt", "missing"]).is_none()); } @@ -113,7 +113,7 @@ mod tests { let module: ItemMod = parse_quote! { #[qinvokable] #[cxx_qt::bridge] - #[cxx_qt::qsignals(MyObject)] + #[cxx_qt::object(MyObject)] #[cxx_qt::bridge(namespace = "my::namespace")] #[cxx_qt::list(A, B, C)] #[cxx_qt::bridge(a = "b", namespace = "my::namespace")] diff --git a/crates/cxx-qt-gen/test_inputs/inheritance.rs b/crates/cxx-qt-gen/test_inputs/inheritance.rs index feed2de87..b076f261a 100644 --- a/crates/cxx-qt-gen/test_inputs/inheritance.rs +++ b/crates/cxx-qt-gen/test_inputs/inheritance.rs @@ -13,16 +13,16 @@ mod inheritance { data: Vec, } - #[cxx_qt::inherit] - unsafe extern "C++" { + unsafe extern "RustQt" { /// Inherited hasChildren from the base class #[cxx_name = "hasChildren"] + #[inherit] fn has_children_super(self: &qobject::MyObject, parent: &QModelIndex) -> bool; } - #[cxx_qt::inherit] - extern "C++" { + extern "RustQt" { /// Inherited fetchMore from the base class + #[inherit] unsafe fn fetch_more(self: Pin<&mut qobject::MyObject>, index: &QModelIndex); } 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 823dc1325..4929b9b87 100644 --- a/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs +++ b/crates/cxx-qt-gen/test_inputs/passthrough_and_naming.rs @@ -91,8 +91,8 @@ pub mod ffi { } } - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { + #[qsignal] fn ready(self: Pin<&mut qobject::MyObject>); } @@ -140,9 +140,9 @@ pub mod ffi { } } - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { #[my_attribute] + #[qsignal] fn ready(self: Pin<&mut qobject::SecondObject>); } diff --git a/crates/cxx-qt-gen/test_inputs/signals.rs b/crates/cxx-qt-gen/test_inputs/signals.rs index 21b92e184..94d86a084 100644 --- a/crates/cxx-qt-gen/test_inputs/signals.rs +++ b/crates/cxx-qt-gen/test_inputs/signals.rs @@ -6,10 +6,11 @@ mod ffi { type QPoint = cxx_qt_lib::QPoint; } - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { + #[qsignal] fn ready(self: Pin<&mut qobject::MyObject>); + #[qsignal] fn data_changed( self: Pin<&mut qobject::MyObject>, first: i32, @@ -20,6 +21,7 @@ mod ffi { #[cxx_name = "newData"] #[inherit] + #[qsignal] fn base_class_new_data( self: Pin<&mut qobject::MyObject>, first: i32, diff --git a/crates/cxx-qt-macro/src/lib.rs b/crates/cxx-qt-macro/src/lib.rs index 859d44085..6a110fb89 100644 --- a/crates/cxx-qt-macro/src/lib.rs +++ b/crates/cxx-qt-macro/src/lib.rs @@ -57,34 +57,6 @@ pub fn bridge(args: TokenStream, input: TokenStream) -> TokenStream { extract_and_generate(module) } -/// A macro which describes that an enum defines the signals for a QObject. -/// -/// It should not be used by itself and instead should be used inside a cxx_qt::bridge definition. -/// -/// # Example -/// -/// ```rust -/// #[cxx_qt::bridge] -/// mod my_object { -/// #[cxx_qt::qobject] -/// #[derive(Default)] -/// # // Note that we can't use properties as this confuses the linker on Windows -/// pub struct MyObject; -/// -/// #[cxx_qt::qsignals] -/// unsafe extern "C++" { -/// fn ready(self: Pin<&mut qobject::MyObject>); -/// } -/// } -/// -/// # // Note that we need a fake main for doc tests to build -/// # fn main() {} -/// ``` -#[proc_macro_attribute] -pub fn qsignals(_args: TokenStream, _input: TokenStream) -> TokenStream { - unreachable!("cxx_qt::qsignals should not be used as a macro by itself. Instead it should be used within a cxx_qt::bridge definition") -} - /// A macro which describes that a struct should be made into a QObject. /// /// It should not be used by itself and instead should be used inside a cxx_qt::bridge definition. @@ -129,48 +101,6 @@ 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") } -/// A macro which allows you to access base class methods from within Rust. -/// -/// It should not be used by itself and instead should be used inside a cxx_qt::bridge definition. -/// Furthermore, the macro must be placed within the `impl` block of a `qobject::T`. -/// See [the book page](https://kdab.github.io/cxx-qt/book/concepts/inheritance.html) for more -/// details. -/// -/// # Example -/// ``` rust -/// #[cxx_qt::bridge] -/// mod my_object { -/// extern "C++" { -/// include!("cxx-qt-lib/qmodelindex.h"); -/// type QModelIndex = cxx_qt_lib::QModelIndex; -/// } -/// -/// #[cxx_qt::qobject(base="QAbstractListModel")] -/// #[derive(Default)] -/// # // Note that we can't use properties as this confuses the linker on Windows -/// pub struct MyObject; -/// -/// #[cxx_qt::inherit] -/// extern "C++" { -/// // Unsafe to call -/// unsafe fn begin_insert_rows(self: Pin<&mut qobject::MyObject>, parent: &QModelIndex, first: i32, last: i32); -/// } -/// -/// #[cxx_qt::inherit] -/// unsafe extern "C++" { -/// // Safe to call - you are responsible to ensure this is true! -/// fn end_insert_rows(self: Pin<&mut qobject::MyObject>); -/// } -/// } -/// -/// # // Note that we need a fake main for doc tests to build -/// # fn main() {} -/// ``` -#[proc_macro_attribute] -pub fn inherit(_args: TokenStream, _input: TokenStream) -> TokenStream { - unreachable!("cxx_qt::inherit 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 fn extract_and_generate(module: ItemMod) -> TokenStream { Parser::from(module) diff --git a/crates/cxx-qt/src/lib.rs b/crates/cxx-qt/src/lib.rs index f1bc2bfe5..831d88b0d 100644 --- a/crates/cxx-qt/src/lib.rs +++ b/crates/cxx-qt/src/lib.rs @@ -10,9 +10,7 @@ //! See the [book](https://kdab.github.io/cxx-qt/book/) for more information. pub use cxx_qt_macro::bridge; -pub use cxx_qt_macro::inherit; pub use cxx_qt_macro::qobject; -pub use cxx_qt_macro::qsignals; /// This trait is automatically implemented for all types which are marked as `#[cxx_qt::qobject]`. /// It provides information about the type that is wrapped by the QObject, as well as the methods diff --git a/examples/demo_threading/rust/src/lib.rs b/examples/demo_threading/rust/src/lib.rs index 8c766c8cd..0e17bbfdc 100644 --- a/examples/demo_threading/rust/src/lib.rs +++ b/examples/demo_threading/rust/src/lib.rs @@ -70,13 +70,15 @@ mod ffi { // Enabling threading on the qobject impl cxx_qt::Threading for qobject::EnergyUsage {} - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { /// A new sensor has been detected + #[qsignal] fn sensor_added(self: Pin<&mut qobject::EnergyUsage>, uuid: QString); /// A value on an existing sensor has changed + #[qsignal] fn sensor_changed(self: Pin<&mut qobject::EnergyUsage>, uuid: QString); /// An existing sensor has been removed + #[qsignal] fn sensor_removed(self: Pin<&mut qobject::EnergyUsage>, uuid: QString); } diff --git a/examples/qml_features/rust/src/custom_base_class.rs b/examples/qml_features/rust/src/custom_base_class.rs index 3e8d5d314..17d70b25d 100644 --- a/examples/qml_features/rust/src/custom_base_class.rs +++ b/examples/qml_features/rust/src/custom_base_class.rs @@ -51,10 +51,10 @@ pub mod ffi { impl cxx_qt::Threading for qobject::CustomBaseClass {} // ANCHOR: book_qsignals_inherit - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { /// Inherit the DataChanged signal from the QAbstractListModel base class #[inherit] + #[qsignal] fn data_changed( self: Pin<&mut qobject::CustomBaseClass>, top_left: &QModelIndex, @@ -153,9 +153,9 @@ pub mod ffi { // ANCHOR: book_inherit_qalm_impl_unsafe // Create Rust bindings for C++ functions of the base class (QAbstractItemModel) - #[cxx_qt::inherit] - extern "C++" { + extern "RustQt" { /// Inherited beginInsertRows from the base class + #[inherit] unsafe fn begin_insert_rows( self: Pin<&mut qobject::CustomBaseClass>, parent: &QModelIndex, @@ -163,9 +163,11 @@ pub mod ffi { last: i32, ); /// Inherited endInsertRows from the base class + #[inherit] unsafe fn end_insert_rows(self: Pin<&mut qobject::CustomBaseClass>); /// Inherited beginRemoveRows from the base class + #[inherit] unsafe fn begin_remove_rows( self: Pin<&mut qobject::CustomBaseClass>, parent: &QModelIndex, @@ -173,23 +175,27 @@ pub mod ffi { last: i32, ); /// Inherited endRemoveRows from the base class + #[inherit] unsafe fn end_remove_rows(self: Pin<&mut qobject::CustomBaseClass>); /// Inherited beginResetModel from the base class + #[inherit] unsafe fn begin_reset_model(self: Pin<&mut qobject::CustomBaseClass>); /// Inherited endResetModel from the base class + #[inherit] unsafe fn end_reset_model(self: Pin<&mut qobject::CustomBaseClass>); } // ANCHOR_END: book_inherit_qalm_impl_unsafe // ANCHOR: book_inherit_qalm_impl_safe - #[cxx_qt::inherit] - unsafe extern "C++" { + unsafe extern "RustQt" { /// Inherited canFetchMore from the base class #[cxx_name = "canFetchMore"] + #[inherit] fn base_can_fetch_more(self: &qobject::CustomBaseClass, parent: &QModelIndex) -> bool; /// Inherited index from the base class + #[inherit] fn index( self: &qobject::CustomBaseClass, row: i32, diff --git a/examples/qml_features/rust/src/multiple_qobjects.rs b/examples/qml_features/rust/src/multiple_qobjects.rs index 899a023df..18bae1eff 100644 --- a/examples/qml_features/rust/src/multiple_qobjects.rs +++ b/examples/qml_features/rust/src/multiple_qobjects.rs @@ -38,12 +38,13 @@ pub mod ffi { // Enabling threading on the qobject impl cxx_qt::Threading for qobject::FirstObject {} - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { /// Accepted Q_SIGNAL + #[qsignal] fn accepted(self: Pin<&mut qobject::FirstObject>); /// Rejected Q_SIGNAL + #[qsignal] fn rejected(self: Pin<&mut qobject::FirstObject>); } @@ -85,12 +86,13 @@ pub mod ffi { // Enabling threading on the qobject impl cxx_qt::Threading for qobject::SecondObject {} - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { /// Accepted Q_SIGNAL + #[qsignal] fn accepted(self: Pin<&mut qobject::SecondObject>); /// Rejected Q_SIGNAL + #[qsignal] fn rejected(self: Pin<&mut qobject::SecondObject>); } diff --git a/examples/qml_features/rust/src/nested_qobjects.rs b/examples/qml_features/rust/src/nested_qobjects.rs index df9e014f8..fbecbea97 100644 --- a/examples/qml_features/rust/src/nested_qobjects.rs +++ b/examples/qml_features/rust/src/nested_qobjects.rs @@ -25,9 +25,9 @@ pub mod ffi { counter: i32, } - #[cxx_qt::qsignals] - extern "C++" { + extern "RustQt" { /// A signal showing how to refer to another QObject as an argument + #[qsignal] unsafe fn called(self: Pin<&mut qobject::InnerObject>, inner: *mut CxxInnerObject); } @@ -46,9 +46,9 @@ pub mod ffi { } } - #[cxx_qt::qsignals] - extern "C++" { + extern "RustQt" { /// A signal showing how to refer to another QObject as an argument + #[qsignal] unsafe fn called(self: Pin<&mut qobject::OuterObject>, inner: *mut CxxInnerObject); } diff --git a/examples/qml_features/rust/src/serialisation.rs b/examples/qml_features/rust/src/serialisation.rs index a1092f348..4d3bf2e61 100644 --- a/examples/qml_features/rust/src/serialisation.rs +++ b/examples/qml_features/rust/src/serialisation.rs @@ -48,9 +48,9 @@ pub mod ffi { pub string: QString, } - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { /// An error signal + #[qsignal] fn error(self: Pin<&mut qobject::Serialisation>, message: QString); } diff --git a/examples/qml_features/rust/src/signals.rs b/examples/qml_features/rust/src/signals.rs index 33132e90f..baa898479 100644 --- a/examples/qml_features/rust/src/signals.rs +++ b/examples/qml_features/rust/src/signals.rs @@ -21,15 +21,17 @@ pub mod ffi { } // ANCHOR: book_signals_block - #[cxx_qt::qsignals] - unsafe extern "C++" { + unsafe extern "RustQt" { /// A Q_SIGNAL emitted when a connection occurs + #[qsignal] fn connected(self: Pin<&mut qobject::RustSignals>, url: &QUrl); /// A Q_SIGNAL emitted when a disconnect occurs + #[qsignal] fn disconnected(self: Pin<&mut qobject::RustSignals>); /// A Q_SIGNAL emitted when an error occurs + #[qsignal] fn error(self: Pin<&mut qobject::RustSignals>, message: QString); } // ANCHOR_END: book_signals_block