Skip to main content

qtbridge_runtime/
qpropertymember.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::{QMetaType, QObjectMutPtr, QVariant};
8
9use crate::{QMetaTypeCompatible, QObjectHolder, QVariantConvertible, QmlElement};
10
11/// Enables a type to be used as a property.
12///
13/// Implemented for:
14/// - Primitive numeric types and `bool`
15/// - [`String`]
16/// - [`Vec<T>`] where `T` is one of the above
17/// - [`Rc<RefCell<T>>`] where `T` implements [`QObjectHolder`]
18/// - [`Vec<Rc<RefCell<T>>>`] where `T` implements [`QmlElement`]
19///
20/// You will not need to implement this trait yourself; adding support for custom types requires CXX/C++ bindings.
21pub trait QPropertyMember: Sized {
22    fn qmetatype() -> QMetaType;
23
24    /// Returns a `QVariant` representation of `self` for read operations.
25    /// `Owner` is the [`QObjectHolder`] that holds this property; passing it
26    /// allows returning views onto its members and borrowing correctly on
27    /// access. If the member is passed by value, `owner` can be ignored.
28    ///
29    /// # Safety
30    ///
31    /// `self` must be a field of `owner`.
32    ///
33    /// For `QObject`-backed members the returned `QVariant` carries a raw,
34    /// untracked `QObject*`: the caller must consume it and start tracking
35    /// its state immediately, before any garbage collector can run. The
36    /// metaobject dispatch upholds this by consuming the variant within
37    /// the same call stack. See [`from_qvariant`](QPropertyMember::from_qvariant).
38    unsafe fn to_qvariant<Owner: QObjectHolder>(&self, owner: &Owner) -> QVariant;
39
40    /// Returns a `QVariant` view of `self` for read operations, with access to
41    /// the property's notify signal. Unlike [`to_qvariant`](QPropertyMember::to_qvariant),
42    /// this variant can return a live view that emits `notify` when the
43    /// underlying data changes.
44    ///
45    /// The default implementation ignores `notify` and falls back to
46    /// [`to_qvariant`](QPropertyMember::to_qvariant).
47    ///
48    /// # Safety
49    ///
50    /// Same contract as [`to_qvariant`](QPropertyMember::to_qvariant).
51    unsafe fn to_qvariant_view<Owner, Notify>(&self, owner: &Owner, notify: Notify) -> QVariant
52    where
53        Owner: QObjectHolder,
54        Notify: Fn(&mut Owner) + 'static,
55    {
56        let _ = notify;
57        unsafe { self.to_qvariant(owner) }
58    }
59
60    /// Converts `value` into the concrete type, used for write operations.
61    ///
62    /// # Safety
63    ///
64    /// For `QObject`-backed members this reads a raw `QObject*` out of the
65    /// variant and dereferences it: the caller must guarantee the variant
66    /// genuinely holds a live `QObject` of a compatible type. The metaobject
67    /// dispatch upholds this (Qt type-checks the property write).
68    unsafe fn from_qvariant(value: &QVariant) -> Option<Self>;
69
70    /// Returns `true` if `self` and `other` are equal.
71    /// Used to decide whether the notify signal should be emitted and the
72    /// stored value replaced on a property write.
73    fn property_eq(&self, other: &Self) -> bool;
74}
75
76impl<T: PartialEq + QMetaTypeCompatible + QVariantConvertible> QPropertyMember for T {
77    fn qmetatype() -> QMetaType {
78        <Self as QMetaTypeCompatible>::compatible_qmetatype()
79    }
80
81    unsafe fn to_qvariant<Owner: QObjectHolder>(&self, _owner: &Owner) -> QVariant {
82        QVariantConvertible::to_qvariant(self)
83    }
84
85    unsafe fn from_qvariant(value: &QVariant) -> Option<Self> {
86        QVariantConvertible::try_from_qvariant(value)
87    }
88
89    fn property_eq(&self, other: &Self) -> bool {
90        self == other
91    }
92}
93
94impl<T: QObjectHolder> QPropertyMember for Rc<RefCell<T>> {
95    fn qmetatype() -> QMetaType {
96        <T as QObjectHolder>::get_qobject_ptr_qmetatype()
97    }
98
99    unsafe fn to_qvariant<Owner: QObjectHolder>(&self, _owner: &Owner) -> QVariant {
100        let ptr = T::rc_ref_cell_to_qobject(self).cast_mut();
101        let ptr_wrap = unsafe { QObjectMutPtr::from_raw(ptr.cast()) };
102        (&ptr_wrap).into()
103    }
104
105    unsafe fn from_qvariant(value: &QVariant) -> Option<Self> {
106        let ptr_wrap: QObjectMutPtr = value.value()?;
107        let ptr: *mut cxx_qt::QObject = ptr_wrap.into_raw();
108        Some(unsafe { T::qobject_to_rc_ref_cell(ptr.cast()) })
109    }
110
111    fn property_eq(&self, other: &Self) -> bool {
112        Rc::ptr_eq(self, other)
113    }
114}
115
116impl<T: QmlElement> QPropertyMember for Vec<Rc<RefCell<T>>> {
117    fn qmetatype() -> QMetaType {
118        T::get_list_qmetatype()
119    }
120
121    unsafe fn to_qvariant<Owner: QObjectHolder>(&self, owner: &Owner) -> QVariant {
122        // SAFETY: `self` is a field of `owner`, see `to_qvariant`.
123        unsafe { T::list_to_qvariant(owner, self, |_: &mut Owner| {}) }
124    }
125
126    unsafe fn to_qvariant_view<Owner, Notify>(&self, owner: &Owner, notify: Notify) -> QVariant
127    where
128        Owner: QObjectHolder,
129        Notify: Fn(&mut Owner) + 'static,
130    {
131        // SAFETY: `self` is a field of `owner`, see `to_qvariant`.
132        unsafe { T::list_to_qvariant(owner, self, notify) }
133    }
134
135    unsafe fn from_qvariant(_value: &QVariant) -> Option<Self> {
136        // Vec<Rc<RefCell<T>>> is exposed as writeable view and no write operation will ever happen
137        None
138    }
139
140    fn property_eq(&self, other: &Self) -> bool {
141        self.len() == other.len() && self.iter().zip(other.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
142    }
143}
144
145/// Returns the [`QMetaType`] of the return value of a `FnOnce`. Given a function
146/// |this: &Self| { &this.member } this allows to infer the metatype of a member
147/// field without requiring the type to be known at macro expansion time. The
148/// closure is never called.
149#[doc(hidden)]
150pub fn get_meta_type_of_fn_return_value<F, This, R>(_f: F) -> QMetaType
151where
152    F: FnOnce(&This) -> &R,
153    R: QPropertyMember,
154{
155    R::qmetatype()
156}