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