qtbridge_runtime/
qobjectholder.rs

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