qtbridge_interfaces/qlist_model/
proxy_rust.rs

1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
3
4use super::proxy_cpp_bridge::{QListModelProxyCpp, ffi};
5use crate::{RustObjAccess, call_rust_trait_impl, call_cpp_impl};
6use qtbridge_runtime::qrustproxy::{QRustProxy, ConstructionMode};
7use qtbridge_runtime::{DispatchMetaCall, QObjectHolder};
8use qtbridge_runtime::QModelItem;
9use qtbridge_type_lib::{QByteArray, QHash, QMetaObject, QMetaType, QModelIndex, QVariant};
10use std::cell::RefCell;
11use std::rc::Rc;
12
13#[doc(hidden)]
14pub trait QListModelAdapter: DispatchMetaCall {
15    fn index(&self, row: i32, column: i32, parent: &QModelIndex) -> QModelIndex;
16    fn row_count(&self, parent: &QModelIndex) -> i32;
17    fn data(&self, index: &QModelIndex, role: i32) -> QVariant;
18    fn role_names(&self) -> QHash<i32, QByteArray>;
19    fn set_data(&mut self, index: &QModelIndex, value: &QVariant, role: i32) -> bool;
20    fn remove_rows(&mut self, first: i32, count: i32, parent: &QModelIndex) -> bool;
21    fn sibling(&self, row: i32, column: i32, idx: &QModelIndex) -> QModelIndex;
22}
23
24impl<T> QListModelAdapter for T
25where
26    T: QListModel + QObjectHolder<ProxyRust = QListModelProxyRust> {
27    fn index(&self, row: i32, column: i32, parent: &QModelIndex) -> QModelIndex {
28        let proxy = <Self as QObjectHolder>::get_rust_proxy(self);
29        proxy.base_index(row, column, parent)
30    }
31    fn row_count(&self, _parent: &QModelIndex) -> i32 {
32        return self.len() as i32;
33    }
34    fn data(&self, index: &QModelIndex, role: i32) -> QVariant {
35        let Some(item) = self.get(index.row() as usize)
36        else {
37            return QVariant::default();
38        };
39        item.get_role(role)
40    }
41    fn role_names(&self) -> QHash<i32, QByteArray> {
42        let names = T::Item::role_names();
43        let mut result = QHash::default();
44        names.iter()
45            .for_each(|(k, v)| result.insert(k, &QByteArray::from(v)));
46        result
47    }
48    fn set_data(&mut self, index: &QModelIndex, value: &QVariant, role: i32) -> bool {
49        if !index.is_valid() {
50            return false;
51        }
52        let Some(mut item) = self.get(index.row() as usize)
53            .cloned()
54        else {
55            return false;
56        };
57        let updated = item.set_role(role, value);
58        if updated {
59            self.set_unnotified(index.row() as usize, item);
60            self.get_rust_proxy_mut().base_data_changed(index, index);
61        }
62        updated
63    }
64    fn remove_rows(&mut self, first: i32, count: i32, parent: &QModelIndex) -> bool {
65        let first = first as usize;
66        let last = first + count as usize;
67        if last > self.len() {
68            return false;
69        }
70        self.get_rust_proxy_mut().base_begin_remove_rows(parent, first as i32, (last - 1) as i32);
71        for index in (first..last).rev() {
72            self.remove_unnotified(index);
73        }
74        self.get_rust_proxy_mut().base_end_remove_rows();
75        true
76    }
77    fn sibling(&self, row: i32, column: i32, idx: &QModelIndex) -> QModelIndex {
78        let proxy = self.get_rust_proxy();
79        proxy.base_sibling(row, column, idx)
80    }
81}
82
83/// A trait representing a list-based Qt model.
84///
85/// [`QListModel`] provides an interface for list-like data structures
86/// that are exposed to Qt through the Model-View concept.
87/// <https://doc.qt.io/qt-6/qtquick-modelviewsdata-modelview.html>.
88///
89/// This trait requires the `qobject` macro to set up the correct Qt proxy.
90/// The macro will further generate functionality in the form of the
91/// [`QListModelBase`] trait that supplements the [`QListModel`] functionality.
92///
93/// ## Design
94///
95/// - The model owns items of associated type `Item` that has to implement
96///   the [`QModelItem`] trait. Roles are derived from the [`QModelItem`]
97///   implementation.
98/// - Mutation methods are provided in an **unnotified** form, meaning
99///   they modify the underlying data without emitting Qt model signals.
100/// - These methods are used by the automatically implemented [`QListModelBase`]
101///   trait to create methods that collaborate the UI about changes in collections.
102///
103/// As a minimum you have to implement the methods [`QListModel::len`] and
104/// [`QListModel::get`] to create a readable list model. Further methods can be
105/// implemented to make the model fully mutable.
106///
107/// Methods that do not return an [`Option`] or a boolean value must succeed
108/// and perform exactly the operation described in the documentation to avoid
109/// invalidating the synchronization between any views and the underlying data.
110/// No additional structural changes may occur outside the provided functions.
111///
112/// **Note, that default implementations may `panic!`** if the corresponding method is
113/// not overridden. It is your responsibility to make sure that these functions are
114/// not called from QML.
115///
116/// ## Example
117///
118/// ``` ignore
119/// use qtbridge::qobject;
120/// #[qobject(Base = QListModel)]
121/// mod backend {
122///     use qtbridge::{QListModel, QListModelBase};
123///
124///     #[derive(Default)]
125///     pub struct Backend {
126///         string_list: Vec<String>,
127///     }
128///     impl QListModel for Backend {
129///         type Item = String;
130///
131///         fn len(&self) -> usize {
132///             self.string_list.len()
133///         }
134///         fn get(&self, index: usize) -> Option<&Self::Item> {
135///             self.string_list.get(index)
136///         }
137///     }
138/// }
139///
140/// ```
141///
142/// The list model can be used in QML views as follows
143/// ``` qml, ignore
144/// ListView {
145///     model: backend
146///     delegate: Text {
147///         required property string value
148///         text: value
149///     }
150/// }
151/// ```
152
153pub trait QListModel {
154    /// The item type stored in the model.
155    ///
156    /// Items must:
157    /// - Implement [`QModelItem`] to integrate with Qt
158    /// - Be [`Default`] for creating new items
159    /// - Be [`Clone`] for safe data access and copying
160    type Item: QModelItem + Default + Clone;
161
162    /// Returns the number of items in the list.
163    fn len(&self) -> usize;
164
165    /// Returns a reference to the item at `index`, or `None` if the index
166    /// is out of bounds.
167    fn get(&self, index: usize) -> Option<&Self::Item>;
168
169    /// Sets the item at `index`. Reimplement this function but call
170    /// [`QListModelBase::set`] to notify Qt about the modification.
171    ///
172    /// Returns `true` if the value was successfully set, or `false` if the
173    /// operation failed (e.g., index out of bounds or value fails
174    /// validation by the business logic).
175    ///
176    /// The default implementation does nothing and returns `false`.
177    fn set_unnotified(&mut self, _index: usize, _value: Self::Item) -> bool {
178        false
179    }
180
181    /// Appends an item to the end of the model. Reimplement this
182    /// function but call [`QListModelBase::push`] to notify Qt about the
183    /// modification.
184    ///
185    /// The function has to accept the value. Validation has to be
186    /// done before this function is called.
187    ///
188    /// The default implementation falls back to [`QListModel::insert_unnotified`],
189    /// which in turn panics by default.
190    fn push_unnotified(&mut self, value: Self::Item) {
191        self.insert_unnotified(self.len(), value);
192    }
193
194    /// Inserts `value` at `index`. Reimplement this function but
195    /// call [`QListModelBase::insert`] to notify Qt about the
196    /// modification.
197    ///
198    /// The function has to accept the value. Validation has to be
199    /// done before this function is called.
200    ///
201    /// Panics by default. Implementors must override this method to support
202    /// insertion.
203    fn insert_unnotified(&mut self, _index: usize, _value: Self::Item) {
204        panic!("In order to use insert, implement insert_unnotified")
205    }
206
207    /// Removes and returns the last item in the model. Reimplement this
208    /// function but call [`QListModelBase::pop`] to notify Qt
209    /// about the modification.
210    ///
211    /// Returns `None` if the model is empty. If the model is not empty,
212    /// the function has to guarantee the success of the operation.
213    ///
214    /// The default implementation falls back to [`QListModel::remove_unnotified`],
215    /// which in turn panics by default.
216    fn pop_unnotified(&mut self) -> Option<Self::Item> {
217        (self.len() > 0)
218            .then(|| self.remove_unnotified(self.len() - 1))
219    }
220
221    /// Removes and returns the item at `index`. Reimplement this
222    /// function but call [`QListModelBase::remove`] to notify Qt
223    /// about the modification.
224    ///
225    /// The index must be valid and the model has to guarantee the success of
226    /// the operation.
227    ///
228    /// Panics by default. Implementors must override this method to support
229    /// removal.
230    fn remove_unnotified(&mut self, _index: usize) -> Self::Item {
231        panic!("In order to use remove, implement remove_unnotified")
232    }
233
234    /// Resets the model’s internal storage. Reimplement this function but
235    /// call [`QListModelBase::reset`] to notify Qt about the modification.
236    ///
237    /// Panics by default. Implementors must override this method to support
238    /// a model reset.
239    ///
240    /// After [`QListModel::reset_unnotified`] returns, the internal storage
241    /// must reflect the new model state: [`QListModel::len`] and
242    /// [`QListModel::get`] must be consistent with the updated storage.
243    fn reset_unnotified(&mut self) {
244        panic!("In order to use reset, implement reset_unnotified")
245    }
246
247}
248
249/// A data-change signaling extension of [`QListModel`].
250///
251/// `QListModelBase` provides the signaling mutation API for list models.
252/// The methods defined in this trait wrap the corresponding
253/// `*_unnotified` methods from [`QListModel`] and automatically emit the
254/// required Qt model signals (such as `beginInsertRows`, `endInsertRows`,
255/// `dataChanged`, etc.). This allows the UI to react to changes in the
256/// underlying data.
257///
258/// This trait is automatically implemented by the [`qobject`] macro and
259/// should not be implemented manually.
260///
261/// ## Usage
262///
263/// When modifying data that you made accessible with [`QListModel`], you
264/// have to use the functions provided by this trait. Do **not** call the
265/// `*_unnotified` methods from [`QListModel`] directly unless you are
266/// manually handling Qt model notifications.
267///
268/// The correctness of this trait depends on implementors of [`QListModel`]
269/// ensuring that:
270///
271/// * The `*_unnotified` methods perform the exact mutation corresponding
272///   to the emitted Qt signals.
273/// * No additional structural changes occur.
274///
275/// Violating this contract may result in undefined behavior in Qt views.
276pub trait QListModelBase : QListModel + QObjectHolder<ProxyRust = QListModelProxyRust> {
277    /// Sets the item at `index` and notifies any attached views about
278    /// the change, if the operation is successful.
279    ///
280    /// This method calls [`QListModel::set_unnotified`].
281    ///
282    /// Returns `true` if the value was successfully updated,
283    /// or `false` if the operation failed (for example, if the index
284    /// was out of bounds or validation failed).
285    fn set(&mut self, index: usize, value: <Self as QListModel>::Item) -> bool {
286        if self.set_unnotified(index, value) {
287            let model_index = self.get_rust_proxy().base_index(index as i32, 0 , &QModelIndex::default());
288            self.get_rust_proxy_mut().base_data_changed(&model_index, &model_index);
289            true
290        } else {
291            false
292        }
293    }
294
295    /// Appends `value` to the end of the model and notifies any attached views about
296    /// the change.
297    ///
298    /// This method calls [`QListModel::push_unnotified`].
299    fn push(&mut self, value: Self::Item) {
300        self.get_rust_proxy_mut().base_begin_insert_rows(&QModelIndex::default(), self.len() as i32, self.len() as i32);
301        self.push_unnotified(value);
302        self.get_rust_proxy_mut().base_end_insert_rows();
303    }
304
305    /// Inserts `value` at `index` and notifies any attached views about
306    /// the change.
307    ///
308    /// This method calls [`QListModel::insert_unnotified`].
309    fn insert(&mut self, index: usize, value: Self::Item) {
310        self.get_rust_proxy_mut().base_begin_insert_rows(&QModelIndex::default(), index as i32, index as i32);
311        self.insert_unnotified(index, value);
312        self.get_rust_proxy_mut().base_end_insert_rows();
313    }
314
315    /// Removes and returns the last item in the model and notifies any attached views about
316    /// the change.
317    ///
318    /// This method calls [`QListModel::pop_unnotified`].
319    ///
320    /// Returns `None` if the model is empty. If the model is not empty,
321    /// the function has to guarantee the success of the operation.
322    fn pop(&mut self) -> Option<Self::Item> {
323        if self.len() == 0 {
324            return None;
325        }
326        self.get_rust_proxy_mut().base_begin_remove_rows(&QModelIndex::default(), self.len() as i32 - 1, self.len() as i32 - 1);
327        let value = self.pop_unnotified();
328        self.get_rust_proxy_mut().base_end_remove_rows();
329        value
330    }
331
332    /// Removes and returns the item at `index` and notifies any attached views about
333    /// the change.
334    ///
335    /// This method calls [`QListModel::remove_unnotified`].
336    fn remove(&mut self, index: usize) -> Self::Item {
337        self.get_rust_proxy_mut().base_begin_remove_rows(&QModelIndex::default(), index as i32, index as i32);
338        let value = self.remove_unnotified(index);
339        self.get_rust_proxy_mut().base_end_remove_rows();
340        value
341    }
342    /// Resets the entire model and notifies any attached views to resyncronize all data.
343    ///
344    /// This method calls [`QListModel::reset_unnotified`].
345    fn reset(&mut self) {
346        self.get_rust_proxy_mut().base_begin_reset_model();
347        self.reset_unnotified();
348        self.get_rust_proxy_mut().base_end_reset_model();
349    }
350}
351
352impl<T> QListModelBase for T
353where T: QListModel + QObjectHolder<ProxyRust = QListModelProxyRust> { }
354
355pub struct QListModelProxyRust {
356    cpp_proxy: *mut QListModelProxyCpp,
357    #[allow(dead_code)]
358    rust_obj: RustObjAccess<dyn QListModelAdapter>,
359    on_drop: Box<dyn FnOnce()>,
360}
361
362impl QRustProxy for QListModelProxyRust {
363
364    type ProxyCppType = QListModelProxyCpp;
365    type AdapterType = dyn QListModelAdapter;
366
367    fn new<OnDropFn: FnOnce() + 'static>(rust_obj: &Rc<RefCell<dyn QListModelAdapter>>, construct: ConstructionMode, on_drop: OnDropFn) -> *mut Self {
368        let boxed_self = Box::new(Self {
369            cpp_proxy: std::ptr::null_mut(),
370            rust_obj: match construct {
371                ConstructionMode::Strong | ConstructionMode::AtAddress(_) => RustObjAccess::new_strong(rust_obj.clone()),
372                ConstructionMode::Weak => RustObjAccess::new_weak(Rc::downgrade(rust_obj)),
373            },
374            on_drop: Box::new(on_drop),
375        });
376        let raw_self = Box::into_raw(boxed_self);
377
378        unsafe{ (*raw_self).cpp_proxy = match construct {
379            ConstructionMode::AtAddress(addr) => {
380                ffi::create_qlist_model_proxy_cpp_at( addr, raw_self)
381            }
382            ConstructionMode::Strong | ConstructionMode::Weak => {
383                ffi::create_qlist_model_proxy_cpp(raw_self)
384            }
385        }};
386        raw_self
387    }
388    fn get_static_meta_object() -> &'static QMetaObject {
389        ffi::static_qmeta_object_of_qlist_model_proxy_cpp()
390    }
391    fn get_size_of_cpp_proxy() -> usize {
392        ffi::size_of_qlist_model_proxy_cpp()
393    }
394    fn get_align_of_cpp_proxy() -> usize {
395        ffi::align_of_qlist_model_proxy_cpp()
396    }
397    fn get_qmetatype_list_of_cpp_proxy() -> QMetaType {
398        ffi::qmetatype_list_of_qlist_model_proxy_cpp()
399    }
400    fn get_cpp_proxy(&self) -> *const QListModelProxyCpp {
401        self.cpp_proxy as *const _
402    }
403    fn get_cpp_proxy_mut(&self) -> *mut QListModelProxyCpp {
404        self.cpp_proxy
405    }
406}
407
408impl QListModelProxyRust {
409    pub fn drop_self(self_ptr: *mut Self) {
410        let boxed_self = unsafe { Box::from_raw(self_ptr) };
411        (boxed_self.on_drop)();
412    }
413    pub fn invoke_slot(&self, slot_id: u32, inputs: &[*const u8], outputs: &[*mut u8]) {
414        call_rust_trait_impl!(self, invoke_slot(slot_id, inputs, outputs))
415    }
416    pub fn invoke_slot_mut(&mut self, slot_id: u32, inputs: &[*const u8], outputs: &[*mut u8]) {
417        call_rust_trait_impl!(mut self, invoke_slot_mut(slot_id, inputs, outputs))
418    }
419    pub fn read_property(&self, prop_id: u32) -> QVariant {
420        call_rust_trait_impl!(self, read_property(prop_id))
421    }
422    pub fn write_property(&mut self, prop_id: u32, value: &QVariant) {
423        call_rust_trait_impl!(mut self, write_property(prop_id, value))
424    }
425    pub fn index(&self, row: i32, column: i32, parent: &QModelIndex) -> QModelIndex {
426        call_rust_trait_impl!(self, index(row, column, parent))
427    }
428    pub fn row_count(&self, parent: &QModelIndex) -> i32 {
429        call_rust_trait_impl!(self, row_count(parent))
430    }
431    pub fn data(&self, index: &QModelIndex, role: i32) -> QVariant {
432        call_rust_trait_impl!(self, data(index, role))
433    }
434    pub fn role_names(&self) -> QHash<i32, QByteArray> {
435        call_rust_trait_impl!(self, role_names())
436    }
437    pub fn set_data(&mut self, index: &QModelIndex, value: &QVariant, role: i32) -> bool {
438        call_rust_trait_impl!(mut self, set_data(index, value, role))
439    }
440    pub fn remove_rows(&mut self, first: i32, count: i32, parent: &QModelIndex) -> bool {
441        call_rust_trait_impl!(mut self, remove_rows(first, count, parent))
442    }
443    pub fn sibling(&self, row: i32, column: i32, idx: &QModelIndex) -> QModelIndex {
444        call_rust_trait_impl!(self, sibling(row, column, idx))
445    }
446    pub fn base_index(&self, row: i32, column: i32, parent: &QModelIndex) -> QModelIndex {
447        call_cpp_impl!(self, base_index(row, column, parent))
448    }
449    pub fn base_role_names(&self) -> QHash<i32, QByteArray> {
450        call_cpp_impl!(self, base_role_names())
451    }
452    pub fn base_set_data(&mut self, index: &QModelIndex, value: &QVariant, role: i32) -> bool {
453        call_cpp_impl!(mut self, base_set_data(index, value, role))
454    }
455    pub fn base_remove_rows(&mut self, first: i32, count: i32, parent: &QModelIndex) -> bool {
456        call_cpp_impl!(mut self, base_remove_rows(first, count, parent))
457    }
458    pub fn base_sibling(&self, row: i32, column: i32, idx: &QModelIndex) -> QModelIndex {
459        call_cpp_impl!(self, base_sibling(row, column, idx))
460    }
461    pub fn base_data_changed(&mut self, top_left: &QModelIndex, bottom_right: &QModelIndex) {
462        call_cpp_impl!(mut self, base_data_changed(top_left, bottom_right))
463    }
464    pub fn base_begin_insert_rows(&mut self, parent: &QModelIndex, first: i32, last: i32) {
465        call_cpp_impl!(mut self, base_begin_insert_rows(parent, first, last))
466    }
467    pub fn base_end_insert_rows(&mut self) {
468        call_cpp_impl!(mut self, base_end_insert_rows())
469    }
470    pub fn base_begin_move_rows(&mut self, source_parent: &QModelIndex, source_first: i32, source_last: i32, destination_parent: &QModelIndex, destination_child: i32) {
471        call_cpp_impl!(mut self, base_begin_move_rows(source_parent, source_first, source_last, destination_parent, destination_child))
472    }
473    pub fn base_end_move_rows(&mut self) {
474        call_cpp_impl!(mut self, base_end_move_rows())
475    }
476    pub fn base_begin_remove_rows(&mut self, parent: &QModelIndex, first: i32, last: i32) {
477        call_cpp_impl!(mut self, base_begin_remove_rows(parent, first, last))
478    }
479    pub fn base_end_remove_rows(&mut self) {
480        call_cpp_impl!(mut self, base_end_remove_rows())
481    }
482    pub fn base_begin_reset_model(&mut self) {
483        call_cpp_impl!(mut self, base_begin_reset_model())
484    }
485    pub fn base_end_reset_model(&mut self) {
486        call_cpp_impl!(mut self, base_end_reset_model())
487    }
488}