1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
use std::cell::RefCell;
use std::rc::Rc;
use qtbridge_type_lib::{QObject, QVariant, QMetaType, QMetaObject};
use crate::qrustproxy::{QRustProxy, ConstructionMode};
use crate::QMetaInfo;
use std::collections::HashMap;
pub trait QObjectHolder : QMetaInfo + Default {
/// Alias for the Rust proxy type corresponding to the user-defined type.
/// The Rust proxy is an intermediate layer between the Rust object and the C++ proxy,
/// forwarding calls in both directions and managing borrowing of the Rust object
/// during QAIM calls (and TBD for meta calls as well).
#[doc(hidden)]
type ProxyRust : QRustProxy;
#[doc(hidden)]
fn try_borrow_mut_proxies_map<F, R>(f: F) -> R
where
F: FnOnce( &mut HashMap<*const u8, *const u8>) -> R
{
thread_local! { static INSTANCES: RefCell<HashMap<*const u8, *const u8>> =
RefCell::new(HashMap::new());
}
INSTANCES.with_borrow_mut(f)
}
/// Return an immutable reference to the Rust proxy linked to the Rust object specified in the argument.
#[doc(hidden)]
fn get_rust_proxy(&self) -> &Self::ProxyRust
{
Self::get_rust_proxy_mut(self)
}
/// Return a mutable reference to the Rust proxy linked to the Rust object specified in the argument.
#[doc(hidden)]
fn get_rust_proxy_mut(&self) -> &mut Self::ProxyRust
{
Self::try_get_rust_proxy_mut(self)
.expect("No proxy registered for given rust object")
}
/// Return a Result wrapping mutable reference to the Rust proxy associated with the specified object.
#[doc(hidden)]
fn try_get_rust_proxy_mut(&self) -> Option<&mut Self::ProxyRust>
{
let rust_obj_ptr = std::ptr::from_ref(self).cast::<u8>();
let proxy_ptr = Self::try_borrow_mut_proxies_map(|map| {
map.get(&rust_obj_ptr).copied().unwrap_or_default()
});
unsafe {
(proxy_ptr as *mut Self::ProxyRust).as_mut()
}
}
/// Try to get the [`QObject`] linked to this Rust `struct`.
#[doc(hidden)]
fn try_get_qobject(&self) -> Option<&mut QObject> {
let rust_proxy = Self::try_get_rust_proxy_mut(&self)?;
let cpp_proxy = rust_proxy.get_cpp_proxy();
let qobject_ptr: *const QObject = cpp_proxy.cast();
unsafe { qobject_ptr.cast_mut().as_mut() }
}
/// Get the [`QObject`] linked to this Rust `struct`. Panics if no
/// [`QObject`] is attached.
#[doc(hidden)]
fn get_qobject(&self) -> &mut QObject
{
self.try_get_qobject()
.expect("QObject is not attached")
}
/// This function has to be implemented on the specific type and
/// provides the conversion from the specific type to the dynamic
/// trait type.
///
/// This function ensures that the type indeed implements the trait
/// specified by the [`QRustProxy`].
#[doc(hidden)]
fn as_adaptor_trait(rust_obj_rc: Rc<RefCell<Self>>) -> Rc<RefCell<<Self::ProxyRust as QRustProxy>::AdapterType>>;
/// Register the given Rust object instance in the multiton.
/// Create Rust and C++ proxies and links them to the Rust object.
/// If `construction` is `AtAddress`, the C++ proxy is created using
/// placement new operator at respective address
#[doc(hidden)]
fn register_instance_in_map(rust_obj_rc: Rc<RefCell<Self>>, construction: ConstructionMode) {
let key = (*rust_obj_rc).as_ptr() as *const u8;
let dyn_rc = Self::as_adaptor_trait(rust_obj_rc);
let proxy = Self::ProxyRust::new(&dyn_rc, construction, Self::unregister_instance_in_map);
Self::try_borrow_mut_proxies_map(|proxies| {
proxies.insert(key, proxy as *const u8);
})
}
/// Removes the entry associated with the specified Rust object from the multiton map.
#[doc(hidden)]
fn unregister_instance_in_map(rust_obj_ptr: *const u8) {
Self::try_borrow_mut_proxies_map(|proxies| proxies.remove(&rust_obj_ptr))
.expect("Proxy object for rust object is not registered")
.cast_mut();
}
/// Configure the [`QObject`] associated with the given Rust object to use
/// the dynamic metaobject specific to this Rust type.
#[doc(hidden)]
fn set_dynamic_meta(instance: &Rc<RefCell<Self>>)
{
let dynamic_meta = Self::get_shared_dynamic_meta_object();
let instance_ref = &instance.borrow();
let qobject_ref = instance_ref.get_qobject();
dynamic_meta.set_to_qobject(qobject_ref);
}
/// Create a new default-initialized instance of this type and attach
/// the required [`QObject`]. This enables use of this instance in QML.
/// Instances created with this function must remain at its original heap
/// location and must not be moved out of `Rc<RefCell<T>>`.
fn default_with_attached_qobject() -> std::rc::Rc<std::cell::RefCell<Self>> {
let instance = std::rc::Rc::new(std::cell::RefCell::new(Self::default()));
Self::attach_qobject(&instance);
instance
}
/// Create and attach a dedicated [`QObject`] to the `instance`.
/// The instance must remain at its original heap location and must
/// not be moved out of `Rc<RefCell<T>>`.
fn attach_qobject(instance: &std::rc::Rc<std::cell::RefCell<Self>>) {
Self::register_instance_in_map(
instance.clone(),
ConstructionMode::Weak
);
Self::set_dynamic_meta(instance);
}
/// Detach and remove the dedicated [`QObject`] from the specified object.
/// This function is intended to be called during the [`Drop`] implementation
/// of this type.
fn detach_qobject(&self) {
if let Some(qobj) = Self::try_get_qobject(self) {
QObject::delete(std::ptr::from_mut(qobj));
}
}
/// Return a [`QVariant`] containing a pointer to this object.
fn as_qvariant(&self) -> QVariant {
let qobj_ref = self.get_qobject();
let qobj_ptr = std::ptr::from_mut(qobj_ref);
qobj_ptr.into()
}
#[doc(hidden)]
fn get_static_meta_object() -> &'static QMetaObject {
<Self::ProxyRust as QRustProxy>::get_static_meta_object()
}
#[doc(hidden)]
fn get_size_of_cpp_proxy() -> usize {
<Self::ProxyRust as QRustProxy>::get_size_of_cpp_proxy()
}
#[doc(hidden)]
fn get_align_of_cpp_proxy() -> usize {
<Self::ProxyRust as QRustProxy>::get_align_of_cpp_proxy()
}
#[doc(hidden)]
fn get_qmetatype_list_of_cpp_proxy() -> QMetaType {
<Self::ProxyRust as QRustProxy>::get_qmetatype_list_of_cpp_proxy()
}
}