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