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}