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::{
8    QMetaType, QMetaTypeGet, QObject, QString, QVariant,
9    QList_bool, QList_i8, QList_u8, QList_i16, QList_u16,
10    QList_i32, QList_u32, QList_i64, QList_u64, QList_isize, QList_usize,
11    QList_f32, QList_f64, QList_QString,
12};
13
14#[cfg(feature = "serde_json")]
15use qtbridge_type_lib::{QJsonArray, QJsonValue};
16
17use crate::{QObjectHolder, QmlRegister};
18
19/// Enables a type to be used as a property.
20///
21/// Implemented for:
22/// - Primitive numeric types and `bool`
23/// - [`String`]
24/// - [`Vec<T>`] where `T` is one of the above
25/// - [`Rc<RefCell<T>>`] where `T` implements [`QObjectHolder`]
26/// - [`Vec<Rc<RefCell<T>>>`] where `T` implements [`QmlRegister`]
27///
28/// You will not need to implement this trait yourself; adding support for custom types requires CXX/C++ bindings.
29pub trait QPropertyMember: Sized {
30    fn qmetatype() -> QMetaType;
31
32    /// Returns a `QVariant` representation of `self` for read operations.
33    /// `Owner` is the [`QObjectHolder`] that holds this property; passing it
34    /// allows returning views onto its members and borrowing correctly on
35    /// access. If the member is passed by value, `owner` can be ignored.
36    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    fn to_qvariant_view<Owner, Notify>(&self, owner: &Owner, notify: Notify) -> QVariant
46    where
47        Owner: QObjectHolder,
48        Notify: Fn(&mut Owner) + 'static,
49    {
50        let _ = notify;
51        self.to_qvariant(owner)
52    }
53
54    /// Converts `value` into the concrete type, used for write operations.
55    fn from_qvariant(value: &QVariant) -> Result<Self, ()>;
56
57    /// Returns `true` if `self` and `other` are equal.
58    /// Used to decide whether the notify signal should be emitted and the
59    /// stored value replaced on a property write.
60    fn property_eq(&self, other: &Self) -> bool;
61}
62
63macro_rules! impl_primitive {
64    ($($t:ty),*) => {
65        $(impl QPropertyMember for $t {
66            fn qmetatype() -> QMetaType { <$t as QMetaTypeGet>::get_qmetatype() }
67            fn to_qvariant<Owner: QObjectHolder>(&self, _owner: &Owner) -> QVariant { QVariant::from(self) }
68            fn from_qvariant(value: &QVariant) -> Result<Self, ()> { Self::try_from(value) }
69            fn property_eq(&self, other: &Self) -> bool { self == other }
70        })*
71    }
72}
73
74impl_primitive!(bool, i8, u8, i16, u16, i32, u32, i64, u64, isize, usize, f32, f64);
75
76macro_rules! impl_vec {
77    ($($t:ty => $qlist:ty),*) => {
78        $(impl QPropertyMember for Vec<$t> {
79            fn qmetatype() -> QMetaType { <$qlist as QMetaTypeGet>::get_qmetatype() }
80            fn to_qvariant<Owner: QObjectHolder>(&self, _owner: &Owner) -> QVariant { QVariant::from(self) }
81            fn from_qvariant(value: &QVariant) -> Result<Self, ()> { Self::try_from(value) }
82            fn property_eq(&self, other: &Self) -> bool { self == other }
83        })*
84    }
85}
86
87impl_vec!(
88    bool   => QList_bool,
89    i8     => QList_i8,
90    u8     => QList_u8,
91    i16    => QList_i16,
92    u16    => QList_u16,
93    i32    => QList_i32,
94    u32    => QList_u32,
95    i64    => QList_i64,
96    u64    => QList_u64,
97    isize  => QList_isize,
98    usize  => QList_usize,
99    f32    => QList_f32,
100    f64    => QList_f64,
101    String => QList_QString
102);
103
104impl QPropertyMember for String {
105    fn qmetatype() -> QMetaType { <QString as QMetaTypeGet>::get_qmetatype() }
106    fn to_qvariant<Owner: QObjectHolder>(&self, _owner: &Owner) -> QVariant { QVariant::from(self) }
107    fn from_qvariant(value: &QVariant) -> Result<Self, ()> { Self::try_from(value) }
108    fn property_eq(&self, other: &Self) -> bool { self == other }
109}
110
111impl<T: QObjectHolder> QPropertyMember for Rc<RefCell<T>> {
112    fn qmetatype() -> QMetaType {
113        <*mut QObject as QMetaTypeGet>::get_qmetatype()
114    }
115
116    fn to_qvariant<Owner: QObjectHolder>(&self, _owner: &Owner) -> QVariant {
117        T::rc_ref_cell_to_qobject(self).cast_mut().into()
118    }
119
120    fn from_qvariant(value: &QVariant) -> Result<Self, ()> {
121        let ptr = <*mut QObject>::try_from(value)?;
122        Ok(unsafe { T::qobject_to_rc_ref_cell(ptr) })
123    }
124
125    fn property_eq(&self, other: &Self) -> bool {
126        Rc::ptr_eq(self, other)
127    }
128}
129
130impl<T: QmlRegister> QPropertyMember for Vec<Rc<RefCell<T>>> {
131    fn qmetatype() -> QMetaType {
132        T::get_list_qmetatype()
133    }
134
135    fn to_qvariant<Owner: QObjectHolder>(&self, owner: &Owner) -> QVariant {
136        T::list_to_qvariant(owner, self, |_: &mut Owner| {})
137    }
138
139    fn to_qvariant_view<Owner, Notify>(&self, owner: &Owner, notify: Notify) -> QVariant
140    where
141        Owner: QObjectHolder,
142        Notify: Fn(&mut Owner) + 'static,
143    {
144        T::list_to_qvariant(owner, self, notify)
145    }
146
147    fn from_qvariant(_value: &QVariant) -> Result<Self, ()> {
148        // Vec<Rc<RefCell<T>>> is exposed as writeable view and no write operation will ever happen
149        Err(())
150    }
151
152    fn property_eq(&self, other: &Self) -> bool {
153        self.len() == other.len() && self.iter().zip(other.iter()).all(|(a, b)| Rc::ptr_eq(a, b))
154    }
155}
156
157#[cfg(feature = "serde_json")]
158impl QPropertyMember for serde_json::Value {
159    fn qmetatype() -> QMetaType { <QJsonValue as QMetaTypeGet>::get_qmetatype() }
160    fn to_qvariant<Owner: QObjectHolder>(&self, _owner: &Owner) -> QVariant {
161        QVariant::from(&crate::serde_tools::serde_to_qjsonvalue(self))
162    }
163    fn from_qvariant(value: &QVariant) -> Result<Self, ()> {
164        crate::serde_tools::qvariant_to_serde(value)
165    }
166    fn property_eq(&self, other: &Self) -> bool { self == other }
167}
168
169#[cfg(feature = "serde_json")]
170impl QPropertyMember for Vec<serde_json::Value> {
171    fn qmetatype() -> QMetaType { <QJsonArray as QMetaTypeGet>::get_qmetatype() }
172    fn to_qvariant<Owner: QObjectHolder>(&self, _owner: &Owner) -> QVariant {
173        QVariant::from(&crate::serde_tools::serde_to_qjsonarray(self))
174    }
175    fn from_qvariant(value: &QVariant) -> Result<Self, ()> {
176        match crate::serde_tools::qvariant_to_serde(value)? {
177            serde_json::Value::Array(arr) => Ok(arr),
178            _ => Err(()),
179        }
180    }
181    fn property_eq(&self, other: &Self) -> bool { self == other }
182}
183
184/// Returns the [`QMetaType`] of the return value of a `FnOnce`. Given a function
185/// |this: &Self| { &this.member } this allows to infer the metatype of a member
186/// field without requiring the type to be known at macro expansion time. The
187/// closure is never called.
188#[doc(hidden)]
189pub fn get_meta_type_of_fn_return_value<F, This, R>(_f: F) -> QMetaType
190where
191    F: FnOnce(&This) -> &R,
192    R: QPropertyMember,
193{
194    R::qmetatype()
195}