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    /// 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}