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