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}