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, 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        return 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::default();
46        names.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 Qt 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/// ```
159pub trait QListModel {
160    /// The item type stored in the model.
161    ///
162    /// Items must:
163    /// - Implement [`QModelItem`] to integrate with Qt
164    /// - Be [`Default`] for creating new items
165    /// - Be [`Clone`] for safe data access and copying
166    type Item: QModelItem + Default + Clone;
167
168    /// Returns the number of items in the list.
169    fn len(&self) -> usize;
170
171    /// Returns a reference to the item at `index`, or `None` if the index
172    /// is out of bounds.
173    fn get(&self, index: usize) -> Option<&Self::Item>;
174
175    /// Sets the item at `index`. Reimplement this function but call
176    /// [`QListModelBase::set`] to notify Qt about the modification.
177    ///
178    /// Returns `true` if the value was successfully set, or `false` if the
179    /// operation failed (e.g., index out of bounds or value fails
180    /// validation by the business logic).
181    ///
182    /// The default implementation does nothing and returns `false`.
183    fn set_unnotified(&mut self, _index: usize, _value: Self::Item) -> bool {
184        false
185    }
186
187    /// Appends an item to the end of the model. Reimplement this
188    /// function but call [`QListModelBase::push`] to notify Qt about the
189    /// modification.
190    ///
191    /// The function has to accept the value. Validation has to be
192    /// done before this function is called.
193    ///
194    /// The default implementation falls back to [`QListModel::insert_unnotified`],
195    /// which in turn panics by default.
196    fn push_unnotified(&mut self, value: Self::Item) {
197        self.insert_unnotified(self.len(), value);
198    }
199
200    /// Inserts `value` at `index`. Reimplement this function but
201    /// call [`QListModelBase::insert`] to notify Qt about the
202    /// modification.
203    ///
204    /// The function has to accept the value. Validation has to be
205    /// done before this function is called.
206    ///
207    /// Panics by default. Implementors must override this method to support
208    /// insertion.
209    fn insert_unnotified(&mut self, _index: usize, _value: Self::Item) {
210        panic!("In order to use insert, implement insert_unnotified")
211    }
212
213    /// Removes and returns the last item in the model. Reimplement this
214    /// function but call [`QListModelBase::pop`] to notify Qt
215    /// about the modification.
216    ///
217    /// Returns `None` if the model is empty. If the model is not empty,
218    /// the function has to guarantee the success of the operation.
219    ///
220    /// The default implementation falls back to [`QListModel::remove_unnotified`],
221    /// which in turn panics by default.
222    fn pop_unnotified(&mut self) -> Option<Self::Item> {
223        (self.len() > 0)
224            .then(|| self.remove_unnotified(self.len() - 1))
225    }
226
227    /// Removes and returns the item at `index`. Reimplement this
228    /// function but call [`QListModelBase::remove`] to notify Qt
229    /// about the modification.
230    ///
231    /// The index must be valid and the model has to guarantee the success of
232    /// the operation.
233    ///
234    /// Panics by default. Implementors must override this method to support
235    /// removal.
236    fn remove_unnotified(&mut self, _index: usize) -> Self::Item {
237        panic!("In order to use remove, implement remove_unnotified")
238    }
239
240    /// Resets the model's internal storage. Reimplement this function but
241    /// call [`QListModelBase::reset`] to notify Qt about the modification.
242    ///
243    /// Panics by default. Implementors must override this method to support
244    /// a model reset.
245    ///
246    /// After [`QListModel::reset_unnotified`] returns, the internal storage
247    /// must reflect the new model state: [`QListModel::len`] and
248    /// [`QListModel::get`] must be consistent with the updated storage.
249    fn reset_unnotified(&mut self) {
250        panic!("In order to use reset, implement reset_unnotified")
251    }
252}
253
254/// A data-change signaling extension of [`QListModel`].
255///
256/// `QListModelBase` provides the signaling mutation API for list models.
257/// The methods defined in this trait wrap the corresponding
258/// `*_unnotified` methods from [`QListModel`] and automatically emit the
259/// required Qt model signals (such as `beginInsertRows`, `endInsertRows`,
260/// `dataChanged`, etc.). This allows the UI to react to changes in the
261/// underlying data.
262///
263/// This trait is automatically implemented by the `qobject` macro and
264/// should not be implemented manually.
265///
266/// ## Usage
267///
268/// When modifying data that you made accessible with [`QListModel`], you
269/// have to use the functions provided by this trait. Do **not** call the
270/// `*_unnotified` methods from [`QListModel`] directly unless you are
271/// manually handling Qt model notifications.
272///
273/// The correctness of this trait depends on implementors of [`QListModel`]
274/// ensuring that:
275///
276/// * The `*_unnotified` methods perform the exact mutation corresponding
277///   to the emitted Qt signals.
278/// * No additional structural changes occur.
279///
280/// Violating this contract may result in undefined behavior in Qt views.
281pub trait QListModelBase : QListModel + QObjectHolder<ProxyRust = QListModelProxyRust> {
282    /// Sets the item at `index` and notifies any attached views about
283    /// the change, if the operation is successful.
284    ///
285    /// This method calls [`QListModel::set_unnotified`].
286    ///
287    /// Returns `true` if the value was successfully updated,
288    /// or `false` if the operation failed (for example, if the index
289    /// was out of bounds or validation failed).
290    fn set(&mut self, index: usize, value: <Self as QListModel>::Item) -> bool {
291        if self.set_unnotified(index, value) {
292            let proxy = self.try_get_rust_proxy_ptr().expect("No proxy");
293            let model_index = unsafe { &*proxy }.base_index(&*self, index as i32, 0, &QModelIndex::default());
294            unsafe { &mut *proxy }.base_data_changed(&mut *self, &model_index, &model_index);
295            true
296        } else {
297            false
298        }
299    }
300
301    /// Appends `value` to the end of the model and notifies any attached views about
302    /// the change.
303    ///
304    /// This method calls [`QListModel::push_unnotified`].
305    fn push(&mut self, value: Self::Item) {
306        let proxy = self.try_get_rust_proxy_ptr().expect("No proxy");
307        let len = self.len() as i32;
308        unsafe { &mut *proxy }.base_begin_insert_rows(&mut *self, &QModelIndex::default(), len, len);
309        self.push_unnotified(value);
310        unsafe { &mut *proxy }.base_end_insert_rows(&mut *self);
311    }
312
313    /// Inserts `value` at `index` and notifies any attached views about
314    /// the change.
315    ///
316    /// This method calls [`QListModel::insert_unnotified`].
317    fn insert(&mut self, index: usize, value: Self::Item) {
318        let proxy = self.try_get_rust_proxy_ptr().expect("No proxy");
319        unsafe { &mut *proxy }.base_begin_insert_rows(&mut *self, &QModelIndex::default(), index as i32, index as i32);
320        self.insert_unnotified(index, value);
321        unsafe { &mut *proxy }.base_end_insert_rows(&mut *self);
322    }
323
324    /// Removes and returns the last item in the model and notifies any attached views about
325    /// the change.
326    ///
327    /// This method calls [`QListModel::pop_unnotified`].
328    ///
329    /// Returns `None` if the model is empty. If the model is not empty,
330    /// the function has to guarantee the success of the operation.
331    fn pop(&mut self) -> Option<Self::Item> {
332        if self.len() == 0 {
333            return None;
334        }
335        let proxy = self.try_get_rust_proxy_ptr().expect("No proxy");
336        let len = self.len() as i32;
337        unsafe { &mut *proxy }.base_begin_remove_rows(&mut *self, &QModelIndex::default(), len - 1, len - 1);
338        let value = self.pop_unnotified();
339        unsafe { &mut *proxy }.base_end_remove_rows(&mut *self);
340        value
341    }
342
343    /// Removes and returns the item at `index` and notifies any attached views about
344    /// the change.
345    ///
346    /// This method calls [`QListModel::remove_unnotified`].
347    fn remove(&mut self, index: usize) -> Self::Item {
348        let proxy = self.try_get_rust_proxy_ptr().expect("No proxy");
349        unsafe { &mut *proxy }.base_begin_remove_rows(&mut *self, &QModelIndex::default(), index as i32, index as i32);
350        let value = self.remove_unnotified(index);
351        unsafe { &mut *proxy }.base_end_remove_rows(&mut *self);
352        value
353    }
354
355    /// Resets the entire model and notifies any attached views to resynchronize all data.
356    ///
357    /// This method calls [`QListModel::reset_unnotified`].
358    fn reset(&mut self) {
359        let proxy = self.try_get_rust_proxy_ptr().expect("No proxy");
360        unsafe { &mut *proxy }.base_begin_reset_model(&mut *self);
361        self.reset_unnotified();
362        unsafe { &mut *proxy }.base_end_reset_model(&mut *self);
363    }
364}
365
366impl<T> QListModelBase for T
367where T: QListModel + QObjectHolder<ProxyRust = QListModelProxyRust> { }
368
369pub type QListModelProxyRust = GenericRustProxy<QListModelProxyCpp, dyn QListModelAdapter>;
370
371impl QListModelProxyRust {
372    pub fn index(&self, row: i32, column: i32, parent: &QModelIndex) -> QModelIndex {
373        call_rust_trait_impl!(self, index(row, column, parent))
374    }
375    pub fn row_count(&self, parent: &QModelIndex) -> i32 {
376        call_rust_trait_impl!(self, row_count(parent))
377    }
378    pub fn data(&self, index: &QModelIndex, role: i32) -> QVariant {
379        call_rust_trait_impl!(self, data(index, role))
380    }
381    pub fn role_names(&self) -> QHash<i32, QByteArray> {
382        call_rust_trait_impl!(self, role_names())
383    }
384    pub fn set_data(&mut self, index: &QModelIndex, value: &QVariant, role: i32) -> bool {
385        call_rust_trait_impl!(mut self, set_data(index, value, role))
386    }
387    pub fn remove_rows(&mut self, first: i32, count: i32, parent: &QModelIndex) -> bool {
388        call_rust_trait_impl!(mut self, remove_rows(first, count, parent))
389    }
390    pub fn sibling(&self, row: i32, column: i32, idx: &QModelIndex) -> QModelIndex {
391        call_rust_trait_impl!(self, sibling(row, column, idx))
392    }
393
394    pub fn base_index(&self, reference: &dyn QListModelAdapter, row: i32, column: i32, parent: &QModelIndex) -> QModelIndex {
395        call_cpp_impl!(self, reference, base_index(row, column, parent))
396    }
397    pub fn base_role_names(&self, reference: &dyn QListModelAdapter) -> QHash<i32, QByteArray> {
398        call_cpp_impl!(self, reference, base_role_names())
399    }
400    pub fn base_set_data(&mut self, mut_ref: &mut dyn QListModelAdapter, index: &QModelIndex, value: &QVariant, role: i32) -> bool {
401        call_cpp_impl!(mut self, mut_ref, base_set_data(index, value, role))
402    }
403    pub fn base_remove_rows(&mut self, mut_ref: &mut dyn QListModelAdapter, first: i32, count: i32, parent: &QModelIndex) -> bool {
404        call_cpp_impl!(mut self, mut_ref, base_remove_rows(first, count, parent))
405    }
406    pub fn base_sibling(&self, reference: &dyn QListModelAdapter, row: i32, column: i32, idx: &QModelIndex) -> QModelIndex {
407        call_cpp_impl!(self, reference, base_sibling(row, column, idx))
408    }
409    pub fn base_data_changed(&mut self, mut_ref: &mut dyn QListModelAdapter, top_left: &QModelIndex, bottom_right: &QModelIndex) {
410        call_cpp_impl!(mut self, mut_ref, base_data_changed(top_left, bottom_right))
411    }
412    pub fn base_begin_insert_rows(&mut self, mut_ref: &mut dyn QListModelAdapter, parent: &QModelIndex, first: i32, last: i32) {
413        call_cpp_impl!(mut self, mut_ref, base_begin_insert_rows(parent, first, last))
414    }
415    pub fn base_end_insert_rows(&mut self, mut_ref: &mut dyn QListModelAdapter) {
416        call_cpp_impl!(mut self, mut_ref, base_end_insert_rows())
417    }
418    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) {
419        call_cpp_impl!(mut self, mut_ref, base_begin_move_rows(source_parent, source_first, source_last, destination_parent, destination_child))
420    }
421    pub fn base_end_move_rows(&mut self, mut_ref: &mut dyn QListModelAdapter) {
422        call_cpp_impl!(mut self, mut_ref, base_end_move_rows())
423    }
424    pub fn base_begin_remove_rows(&mut self, mut_ref: &mut dyn QListModelAdapter, parent: &QModelIndex, first: i32, last: i32) {
425        call_cpp_impl!(mut self, mut_ref, base_begin_remove_rows(parent, first, last))
426    }
427    pub fn base_end_remove_rows(&mut self, mut_ref: &mut dyn QListModelAdapter) {
428        call_cpp_impl!(mut self, mut_ref, base_end_remove_rows())
429    }
430    pub fn base_begin_reset_model(&mut self, mut_ref: &mut dyn QListModelAdapter) {
431        call_cpp_impl!(mut self, mut_ref, base_begin_reset_model())
432    }
433    pub fn base_end_reset_model(&mut self, mut_ref: &mut dyn QListModelAdapter) {
434        call_cpp_impl!(mut self, mut_ref, base_end_reset_model())
435    }
436}