Skip to main content

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