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