cxx_qt/
lib.rs

1// SPDX-FileCopyrightText: 2023 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Leon Matthes <leon.matthes@kdab.com>
3//
4// SPDX-License-Identifier: MIT OR Apache-2.0
5
6#![deny(missing_docs)]
7
8//! This crate and its associated crates provide a framework for generating QObjects from Rust.
9//!
10//! See the [book](https://kdab.github.io/cxx-qt/book/) for more information.
11
12use std::{fs::File, io::Write, path::Path};
13
14mod connection;
15mod connectionguard;
16#[doc(hidden)]
17pub mod signalhandler;
18mod threading;
19
20/// A procedural macro which generates a QObject for a struct inside a module.
21///
22/// # Example
23///
24/// ```rust
25/// #[cxx_qt::bridge(namespace = "cxx_qt::my_object")]
26/// mod qobject {
27///     extern "RustQt" {
28///         #[qobject]
29///         # // Note that we can't use properties as this confuses the linker on Windows
30///         type MyObject = super::MyObjectRust;
31///
32///         #[qinvokable]
33///         fn invokable(self: &MyObject, a: i32, b: i32) -> i32;
34///     }
35/// }
36///
37/// #[derive(Default)]
38/// pub struct MyObjectRust;
39///
40/// impl qobject::MyObject {
41///     fn invokable(&self, a: i32, b: i32) -> i32 {
42///         a + b
43///     }
44/// }
45///
46/// # // Note that we need a fake main for doc tests to build
47/// # fn main() {
48/// # }
49/// ```
50pub use cxx_qt_macro::bridge;
51
52/// A macro which describes that a struct should be made into a QObject.
53///
54/// It should not be used by itself and instead should be used inside a cxx_qt::bridge definition.
55///
56/// # Example
57///
58/// ```rust
59/// #[cxx_qt::bridge]
60/// mod my_object {
61///     extern "RustQt" {
62///         #[qobject]
63///         # // Note that we can't use properties as this confuses the linker on Windows
64///         type MyObject = super::MyObjectRust;
65///     }
66/// }
67///
68/// #[derive(Default)]
69/// pub struct MyObjectRust;
70///
71/// # // Note that we need a fake main for doc tests to build
72/// # fn main() {
73/// # }
74/// ```
75///
76/// You can also specify a custom base class by using `#[base = QStringListModel]`, you must then use CXX to add any includes needed.
77///
78/// # Example
79///
80/// ```rust
81/// #[cxx_qt::bridge]
82/// mod my_object {
83///     extern "RustQt" {
84///         #[qobject]
85///         #[base = QStringListModel]
86///         # // Note that we can't use properties as this confuses the linker on Windows
87///         type MyModel = super::MyModelRust;
88///     }
89///
90///     unsafe extern "C++" {
91///         include!(<QtCore/QStringListModel>);
92///         type QStringListModel;
93///     }
94/// }
95///
96/// #[derive(Default)]
97/// pub struct MyModelRust;
98///
99/// # // Note that we need a fake main for doc tests to build
100/// # fn main() {
101/// # }
102/// ```
103pub use cxx_qt_macro::qobject;
104
105pub use connection::{ConnectionType, QMetaObjectConnection};
106pub use connectionguard::QMetaObjectConnectionGuard;
107pub use threading::{CxxQtThread, ThreadingQueueError};
108
109// Export static assertions that can then be used in cxx-qt-gen generation
110//
111// These are currently used to ensure that CxxQtSignalHandler has the right size
112#[doc(hidden)]
113pub use static_assertions;
114
115/// This trait is automatically implemented for all QObject types generated by CXX-Qt.
116/// It provides information about the inner Rust struct that is wrapped by the QObject, as well as the methods
117/// that Cxx-Qt will generate for the QObject.
118pub trait CxxQtType {
119    /// The Rust type that this QObject is wrapping.
120    type Rust;
121
122    /// Retrieve an immutable reference to the Rust struct backing this C++ object
123    fn rust(&self) -> &Self::Rust;
124
125    /// Retrieve a mutable reference to the Rust struct backing this C++ object
126    fn rust_mut(self: core::pin::Pin<&mut Self>) -> core::pin::Pin<&mut Self::Rust>;
127}
128
129/// This trait indicates that the object implements threading and has a method which returns a [CxxQtThread].
130///
131/// The QObjects generated by CXX-Qt are neither [`Send`](https://doc.rust-lang.org/std/marker/trait.Send.html) nor [`Sync`](https://doc.rust-lang.org/std/marker/trait.Sync.html).
132/// Therefore they may not be passed between threads nor accessed from multiple threads.
133///
134/// To achieve safe multi-threading on the Rust side we use an [CxxQtThread].
135/// A [CxxQtThread] represents a reference to the Qt thread that the QObject lives in.
136/// When a new Rust thread is started (e.g. in an invokable) the [CxxQtThread] can be moved into the thread to later update the QObject in a thread safe manner.
137///
138/// # Example
139///
140/// ```rust,ignore
141/// # // FIXME: test doesn't link correctly on Windows
142/// #[cxx_qt::bridge]
143/// mod qobject {
144///     extern "RustQt" {
145///         #[qobject]
146///         type MyStruct = super::MyStructRust;
147///
148///        #[qinvokable]
149///         fn say_hello(self: Pin<&mut MyStruct>);
150///     }
151///
152///     impl cxx_qt::Threading for MyStruct {}
153/// }
154///
155/// use cxx_qt::Threading;
156///
157/// #[derive(Default)]
158/// pub struct MyStructRust;
159///
160/// impl qobject::MyStruct {
161///     pub fn say_hello(self: core::pin::Pin<&mut Self>) {
162///         let qt_thread = self.qt_thread();
163///
164///         // Start a background thread that doesn't block the invokable
165///         std::thread::spawn(move || {
166///             std::thread::sleep(std::time::Duration::from_secs(1));
167///
168///             // Say hello on the Qt event loop
169///             qt_thread.queue(|_| {
170///                 println!("Hello");
171///             }).unwrap();
172///         });
173///     }
174/// }
175///
176/// # // Note that we need a fake main function for doc tests to build.
177/// # fn main() {}
178/// ```
179pub trait Threading: Sized {
180    #[doc(hidden)]
181    type BoxedQueuedFn;
182    #[doc(hidden)]
183    type ThreadingTypeId;
184
185    /// Create an instance of a [CxxQtThread]
186    ///
187    /// This allows for queueing closures onto the Qt event loop from a background thread.
188    fn qt_thread(&self) -> CxxQtThread<Self>;
189
190    #[doc(hidden)]
191    fn is_destroyed(cxx_qt_thread: &CxxQtThread<Self>) -> bool;
192
193    #[doc(hidden)]
194    fn queue<F>(cxx_qt_thread: &CxxQtThread<Self>, f: F) -> Result<(), ThreadingQueueError>
195    where
196        F: FnOnce(core::pin::Pin<&mut Self>),
197        F: Send + 'static;
198
199    #[doc(hidden)]
200    fn threading_clone(cxx_qt_thread: &CxxQtThread<Self>) -> CxxQtThread<Self>;
201
202    #[doc(hidden)]
203    fn threading_drop(cxx_qt_thread: core::pin::Pin<&mut CxxQtThread<Self>>);
204}
205
206/// Placeholder for upcasting objects, suppresses dead code warning
207#[allow(dead_code)]
208#[doc(hidden)]
209pub trait Upcast<T> {}
210
211/// This trait can be implemented on any [CxxQtType] to define a
212/// custom constructor in C++ for the QObject.
213///
214/// The `Arguments` must be a tuple of CXX types that will be the arguments to the constructor in C++.
215///
216/// If this trait is implemented for a given [CxxQtType], it must also be declared inside the
217/// [cxx_qt::bridge](bridge) macro.
218/// See the example below.
219///
220/// Note that declaring an implementation of this trait will stop CXX-Qt from generating a default constructor.
221/// Therefore an implementation of [Default] is no longer required for the Rust type.
222///
223/// # Minimal Example
224///
225/// ```rust
226/// #[cxx_qt::bridge]
227/// mod qobject {
228///     extern "RustQt" {
229///         #[qobject]
230///         type MyStruct = super::MyStructRust;
231///     }
232///
233///     // Declare that we want to use a custom constructor
234///     // Note that the arguments must be a tuple of CXX types.
235///     // Any associated types that aren't included here are assumed to be `()`.
236///     impl cxx_qt::Constructor<(i32, String), NewArguments=(i32, String)> for MyStruct {}
237/// }
238///
239/// // Struct without `Default` implementation
240/// pub struct MyStructRust {
241///     pub integer: i32,
242///     pub string: String
243/// }
244///
245/// impl cxx_qt::Constructor<(i32, String)> for qobject::MyStruct {
246///     type BaseArguments = (); // Will be passed to the base class constructor
247///     type InitializeArguments = (); // Will be passed to the "initialize" function
248///     type NewArguments = (i32, String); // Will be passed to the "new" function
249///
250///     fn route_arguments(args: (i32, String)) -> (
251///         Self::NewArguments,
252///         Self::BaseArguments,
253///         Self::InitializeArguments
254///     ) {
255///         (args, (), ())
256///     }
257///
258///     fn new((integer, string): (i32, String)) -> MyStructRust {
259///         MyStructRust {
260///             integer,
261///             string
262///         }
263///     }
264/// }
265///
266/// # // Note that we need a fake main function for doc tests to build.
267/// # fn main() {}
268/// ```
269///
270/// # Pseudo Code for generated C++ Constructor
271/// You can imagine this trait as creating a constructor roughly like this:
272/// ```cpp
273/// class MyCxxQtType : public QObject {
274///     public:
275///         MyCxxQtType(Arguments... args)
276///             : QObject(Constructor::route_arguments(args).BaseArguments)
277///             , m_rust(Constructor::new(Constructor::route_arguments(args).NewArguments))
278///         {
279///             Constructor::initialize(*this, Constructor::route_arguments(args).InitializeArguments);
280///         }
281/// }
282/// ```
283/// Note that in reality, `route_arguments` will only be called once and all arguments
284/// will be moved, never copied.
285///
286/// # Initializing the QObject
287///
288/// In addition to running code before constructing the inner Rust struct, it may be useful to run code from the context of the QObject itself (i.e. inside the Constructor implementation).
289///
290/// The `initialize` function can be used to run code inside a constructor.
291/// It is given a pinned mutable self reference to the QObject and the list of `InitializeArguments`.
292///
293/// ## Using the `Initialize` trait
294///
295/// The QML engine creates QML elements using their default constructor, so for most QML types only the `initialize` part of the constructor is of interest.
296/// To reduce the boilerplate of this use-case, CXX-Qt provides the [Initialize] trait.
297///
298/// If a QObject implements the `Initialize` trait, and the inner Rust struct is [Default]-constructible it will automatically implement `cxx_qt::Constructor<()>`.
299/// Additionally, implementing `impl cxx_qt::Initialize` will act as shorthand for `cxx_qt::Constructor<()>`.
300pub trait Constructor<Arguments>: CxxQtType {
301    /// The arguments that are passed to the [`new()`](Self::new) function to construct the inner Rust struct.
302    /// This must be a tuple of CXX compatible types.
303    ///
304    /// This way QObjects can be constructed that need additional arguments for constructing the
305    /// inner Rust type.
306    type NewArguments;
307    /// The arguments that should be passed to the constructor of the base class.
308    /// This must be a tuple of CXX compatible types.
309    type BaseArguments;
310    /// The arguments that should be used to initialize the QObject in the [`initialize()`](Self::initialize) function.
311    /// This must be a tuple of CXX compatible types.
312    type InitializeArguments;
313
314    /// This function is called by CXX-Qt to route the arguments to the correct places.
315    ///
316    /// Using this function, you can split up the arguments required by the QObject constructor
317    /// without additional copies.
318    ///
319    #[allow(unused_variables)]
320    fn route_arguments(
321        arguments: Arguments,
322    ) -> (
323        Self::NewArguments,
324        Self::BaseArguments,
325        Self::InitializeArguments,
326    );
327
328    /// This function is called to construct the inner Rust struct of the CXX-Qt QObject.
329    /// You can use this to construct Rust structs that do not provide a [Default] implementation.
330    fn new(arguments: Self::NewArguments) -> <Self as CxxQtType>::Rust;
331
332    /// This function is called to initialize the QObject.
333    /// After the members of the QObject is initialized, this function is called.
334    /// This is equivalent to the body of the constructor in C++.
335    ///
336    /// # Default
337    /// By default, this function does nothing
338    #[allow(unused_variables)]
339    fn initialize(self: core::pin::Pin<&mut Self>, arguments: Self::InitializeArguments) {
340        // By default, do nothing
341    }
342}
343
344/// This trait can be implemented on any [CxxQtType] to automatically define a default constructor
345/// that calls the `initialize` function after constructing a default Rust struct.
346///
347/// Ensure that the `impl cxx_qt::Constructor<()> for ... {}` is declared inside the CXX-Qt bridge.
348/// Alternatively, using `impl cxx_qt::Initialize for ... {}` acts as shorthand for the above line.
349///
350/// # Example
351///
352/// ```rust,ignore
353/// # // FIXME: test doesn't link correctly on Windows
354/// #[cxx_qt::bridge]
355/// mod qobject {
356///     extern "RustQt" {
357///         #[qobject]
358///         #[qproperty(i32, integer)]
359///         type MyStruct = super::MyStructRust;
360///     }
361///
362///     // Remember to tell the bridge about the default constructor
363///     impl cxx_qt::Constructor<()> for MyStruct {}
364///     // or
365///     impl cxx_qt::Initialize for MyStruct {}
366/// }
367///
368/// // Make sure the inner Rust struct implements `Default`
369/// #[derive(Default)]
370/// pub struct MyStructRust {
371///     integer: i32,
372/// }
373///
374/// impl cxx_qt::Initialize for qobject::MyStruct {
375///     fn initialize(self: core::pin::Pin<&mut Self>) {
376///         self.on_integer_changed(|qobject| {
377///             println!("New integer value: {}", qobject.integer);
378///         }).release();
379///     }
380/// }
381///
382/// # // Note that we need a fake main function for doc tests to build.
383/// # fn main() {}
384/// ```
385// TODO: Once the QObject type is available in the cxx-qt crate, also auto-generate a default
386// constructor that takes QObject and passes it to the parent.
387pub trait Initialize: CxxQtType {
388    /// This function is called to initialize the QObject after construction.
389    fn initialize(self: core::pin::Pin<&mut Self>);
390}
391
392impl<T> Constructor<()> for T
393where
394    T: Initialize,
395    T::Rust: Default,
396{
397    type NewArguments = ();
398    type BaseArguments = ();
399    type InitializeArguments = ();
400
401    fn new(_arguments: ()) -> <Self as CxxQtType>::Rust {
402        <Self as CxxQtType>::Rust::default()
403    }
404
405    fn route_arguments(
406        _arguments: (),
407    ) -> (
408        Self::NewArguments,
409        Self::BaseArguments,
410        Self::InitializeArguments,
411    ) {
412        ((), (), ())
413    }
414
415    fn initialize(self: core::pin::Pin<&mut Self>, _arguments: Self::InitializeArguments) {
416        Self::initialize(self);
417    }
418}
419
420#[doc(hidden)]
421// Write the cxx-qt headers to the specified directory.
422pub fn write_headers(directory: impl AsRef<Path>) {
423    let directory = directory.as_ref();
424    std::fs::create_dir_all(directory).expect("Could not create cxx-qt header directory");
425    // Note ensure that the build script is consistent with files that are copied
426    for (file_contents, file_name) in [
427        (include_str!("../include/connection.h"), "connection.h"),
428        (
429            include_str!("../include/signalhandler.h"),
430            "signalhandler.h",
431        ),
432        (include_str!("../include/thread.h"), "thread.h"),
433        (include_str!("../include/threading.h"), "threading.h"),
434        (include_str!("../include/type.h"), "type.h"),
435    ] {
436        // Note that we do not need rerun-if-changed for these files
437        // as include_str causes a rerun when the header changes
438        // and the files are always written to the target.
439        let h_path = format!("{}/{file_name}", directory.display());
440        let mut header = File::create(h_path).expect("Could not create cxx-qt header");
441        write!(header, "{file_contents}").expect("Could not write cxx-qt header");
442    }
443}