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}