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}