Skip to main content

qtbridge_runtime/
qmlelement.rs

1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
3
4use std::rc::Rc;
5use std::cell::RefCell;
6
7use crate::QObjectHolder;
8use crate::qobjectholder::CppProxyOf;
9use crate::qqmllistproperty::{list_append, list_count, list_at, list_clear, list_property_interface_for, list_property_to_qvariant};
10use crate::qproxies::QCppProxy;
11use crate::registry::Owner;
12use qtbridge_type_lib::QObject;
13use qtbridge_type_lib::QMetaType;
14use qtbridge_type_lib::QMetaTypeInterface;
15use qtbridge_type_lib::QVariant;
16
17pub trait QmlElement : QObjectHolder + Default
18{
19    const URI: &str;
20    const ELEMENT_NAME: &str;
21    const MINOR_VERSION: u8;
22    const MAJOR_VERSION: u8;
23
24    /// Returns the [`QMetaType`] identifying this type, unique per concrete
25    /// type.
26    ///
27    /// The `#[qobject]` macro overrides this with a per-type `OnceLock` body; the
28    /// default serves generic types and hand-written impls.
29    fn get_qmetatype() -> QMetaType {
30        let iface = crate::qmetatypeforqobject::interface_for_generic::<Self>();
31        QMetaType::new_with_interface(iface)
32    }
33    const IS_SINGLETON: bool;
34
35    fn get_list_qmetatype() -> QMetaType {
36        // TODO: The HashMap can be replaced with OnceCell in a per-type generated implementation
37        // of this function in qtbridge-gen. Might improve performance.
38        use std::collections::HashMap;
39        thread_local!(static LIST_IFACE_MAP: RefCell<HashMap<i32, &'static QMetaTypeInterface>>
40            = RefCell::new(HashMap::new()));
41
42        let element = Self::get_qmetatype();
43        let key = element.id();
44
45        let existing = LIST_IFACE_MAP.with_borrow(|m| m.get(&key).copied());
46        let iface = existing.unwrap_or_else(|| {
47            let leaked: &'static QMetaTypeInterface = Box::leak(Box::new(
48                list_property_interface_for(&element)
49            ));
50            LIST_IFACE_MAP.with_borrow_mut(|m| m.insert(key, leaked));
51            leaked
52        });
53        QMetaType::new_with_interface(iface)
54    }
55
56    /// Returns a `QVariant` holding a `QQmlListProperty` view onto `store`.
57    ///
58    /// # Safety
59    ///
60    /// `store` must be a field of `owner`: the list callbacks locate it
61    /// through its byte offset within `owner`.
62    unsafe fn list_to_qvariant<Owner, Notify>(owner: &Owner, store: &Vec<Rc<RefCell<Self>>>, _notify: Notify) -> QVariant
63    where
64        Owner: QObjectHolder,
65        Notify: Fn(&mut Owner) + 'static,
66    {
67        debug_assert_eq!(std::mem::size_of::<Notify>(), 0, "Notify must be a zero-sized type");
68        let qobject = owner.get_qobject_ptr();
69        let base = owner as *const Owner as *const u8;
70        let field = store as *const _ as *const u8;
71        // SAFETY: `store` is a field within `owner`, so both pointers are in the same allocation.
72        // The `owner` can be reconstructed safely, even when objects are moved since we store a
73        // shared reference to it. With the known offset we can also reconstruct `store`. The callbacks
74        // in crate::qqmllistproperty expect this exact format.
75        let store_offset = unsafe { field.offset_from(base) } as usize;
76        unsafe { list_property_to_qvariant(
77            &Self::get_list_qmetatype(),
78            qobject,
79            store_offset as *mut u8,
80            (list_append::<Owner, Self, Notify> as *const ()).addr(),
81            (list_count::<Owner, Self> as *const ()).addr(),
82            (list_at::<Owner, Self> as *const ()).addr(),
83            (list_clear::<Owner, Self, Notify> as *const ()).addr(),
84        ) }
85    }
86
87    fn register() {
88        let meta_obj_data = <Self as QObjectHolder>::get_shared_dynamic_meta_object_data();
89        let meta_obj = unsafe {
90            meta_obj_data
91                .get_meta_object()
92                .as_ref()
93                .expect("Failed to get QMetaObject")
94        };
95
96        if Self::IS_SINGLETON {
97            // SAFETY: The constructor is monomorphized for `Self`.
98            unsafe { crate::qmlprivate::qml_register_singleton(
99                <Self as QObjectHolder>::get_qobject_ptr_qmetatype(),
100                monomorphize_singleton_ctor::<Self>(),
101                Self::URI.as_bytes(),
102                Self::MAJOR_VERSION,
103                Self::MINOR_VERSION,
104                Self::ELEMENT_NAME.as_bytes(),
105                meta_obj,
106            ) }
107        } else {
108            let list_metatype = Self::get_list_qmetatype();
109            list_metatype.register_type();
110
111            // SAFETY: The constructor is monomorphized for `Self` and
112            // placement-constructs `Self`'s C++ proxy, whose size is passed.
113            unsafe { crate::qmlprivate::qml_register_element(
114                <Self as QObjectHolder>::get_qobject_ptr_qmetatype(),
115                list_metatype,
116                <CppProxyOf<Self> as QCppProxy>::get_size() as u32,
117                <CppProxyOf<Self> as QCppProxy>::parser_status_cast(),
118                monomorphize_element_ctor::<Self>(),
119                Self::URI.as_bytes(),
120                Self::MAJOR_VERSION,
121                Self::MINOR_VERSION,
122                Self::ELEMENT_NAME.as_bytes(),
123                meta_obj,
124            ) };
125        }
126    }
127}
128
129unsafe fn element_ctor<T: QmlElement>(addr: *mut u8, _userdata: *mut u8) {
130    let instance = std::rc::Rc::new(std::cell::RefCell::new(T::default()));
131    unsafe { T::register_instance_at(instance.clone(), Owner::Engine, Some(addr)) };
132}
133
134fn singleton_ctor<T: QmlElement>() -> *mut QObject {
135    let instance = std::rc::Rc::new(std::cell::RefCell::new(T::default()));
136    T::register_instance(instance.clone(), Owner::Engine);
137    instance.borrow().get_qobject_ptr()
138}
139
140fn monomorphize_element_ctor<T: QmlElement>() -> usize {
141    extern "C" fn default_ctor<T: QmlElement>(addr: *mut u8, userdata: *mut u8) {
142        unsafe { element_ctor::<T>(addr, userdata) }
143    }
144    default_ctor::<T> as *const () as usize
145}
146
147fn monomorphize_singleton_ctor<T: QmlElement>() -> usize {
148    extern "C" fn default_ctor<T: QmlElement>() -> *mut QObject {
149        singleton_ctor::<T>()
150    }
151    default_ctor::<T> as *const () as usize
152}