Skip to main content

cxx_qt_lib/core/qlist/
mod.rs

1// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3//
4// SPDX-License-Identifier: MIT OR Apache-2.0
5
6#[cfg(feature = "qt_gui")]
7use crate::QColor;
8#[cfg(not(target_os = "emscripten"))]
9use crate::QDateTime;
10use crate::{
11    QByteArray, QDate, QLine, QLineF, QMargins, QMarginsF, QPersistentModelIndex, QPoint, QPointF,
12    QRect, QRectF, QSize, QSizeF, QString, QTime, QUrl, QUuid, QVariant,
13};
14use core::{marker::PhantomData, mem::MaybeUninit};
15use cxx::{type_id, ExternType};
16use std::fmt;
17
18/// The `QList` class is a template class that provides a dynamic array.
19///
20/// To use `QList` with a custom type, implement the [`QListElement`] trait for `T`.
21///
22/// Qt Documentation: [QList]("https://doc.qt.io/qt/qlist.html#details")
23#[repr(C)]
24pub struct QList<T>
25where
26    T: QListElement,
27{
28    /// The layout has changed between Qt 5 and Qt 6
29    ///
30    /// Qt5 `QList` has one pointer as a member
31    /// Qt6 `QVector`/`QList` has one member, which contains two pointers and a size_t
32    #[cfg(cxxqt_qt_version_major = "5")]
33    _space: MaybeUninit<usize>,
34    #[cfg(cxxqt_qt_version_major = "6")]
35    _space: MaybeUninit<[usize; 3]>,
36    _value: PhantomData<T>,
37}
38
39impl<T> Clone for QList<T>
40where
41    T: QListElement,
42{
43    /// Constructs a copy of the `QList`.
44    fn clone(&self) -> Self {
45        T::clone(self)
46    }
47}
48
49impl<T> Default for QList<T>
50where
51    T: QListElement,
52{
53    /// Constructs an empty list.
54    fn default() -> Self {
55        T::default()
56    }
57}
58
59impl<T> Drop for QList<T>
60where
61    T: QListElement,
62{
63    /// Destroys the `QList`.
64    fn drop(&mut self) {
65        T::drop(self);
66    }
67}
68
69impl<T> PartialEq for QList<T>
70where
71    T: QListElement + PartialEq,
72{
73    /// Returns `true` if both lists contain the same elements in the same order, otherwise `false`.
74    fn eq(&self, other: &Self) -> bool {
75        self.len() == other.len() && self.iter().zip(other.iter()).all(|(a, b)| a == b)
76    }
77}
78
79impl<T> Eq for QList<T> where T: QListElement + Eq {}
80
81impl<T> fmt::Debug for QList<T>
82where
83    T: QListElement + fmt::Debug,
84{
85    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86        f.debug_list().entries(self.iter()).finish()
87    }
88}
89
90impl<T> QList<T>
91where
92    T: QListElement,
93{
94    /// Inserts `value` at the end of the list.
95    ///
96    /// The value is a reference here so it can be opaque or trivial but
97    /// note that the value is copied when being appended into the list.
98    pub fn append_clone(&mut self, value: &T) {
99        T::append_clone(self, value);
100    }
101
102    /// Removes all elements from the list.
103    ///
104    /// In Qt 6, the capacity is preserved. In Qt 5, this function releases the memory used by the list.
105    pub fn clear(&mut self) {
106        T::clear(self);
107    }
108
109    /// Returns `true` if the list contains an occurrence of `value`; otherwise returns `false`.
110    pub fn contains(&self, value: &T) -> bool {
111        T::contains(self, value)
112    }
113
114    /// Returns the item at index position `index` in the list, or `None` if `index` is out of bounds (i.e. `index < 0 || index >= self.len()`).
115    pub fn get(&self, index: isize) -> Option<&T> {
116        if index >= 0 && index < self.len() {
117            Some(unsafe { T::get_unchecked(self, index) })
118        } else {
119            None
120        }
121    }
122
123    /// Returns the index position of the first occurrence of `value` in the list. Returns -1 if no item matched.
124    pub fn index_of(&self, value: &T) -> isize {
125        T::index_of(self, value)
126    }
127
128    /// Inserts item `value` into the list at index position `pos`.
129    ///
130    /// The value is a reference here so it can be opaque or trivial but
131    /// note that the value is copied when being inserted into the list.
132    pub fn insert_clone(&mut self, pos: isize, value: &T) {
133        T::insert_clone(self, pos, value);
134    }
135
136    /// Returns `true` if the list has size 0; otherwise returns `false`.
137    pub fn is_empty(&self) -> bool {
138        T::len(self) == 0
139    }
140
141    /// An iterator visiting all elements in arbitrary order.
142    /// The iterator element type is `&'a T`.
143    pub fn iter(&self) -> Iter<'_, T> {
144        Iter {
145            list: self,
146            index: 0,
147        }
148    }
149
150    /// Returns the number of items in the list.
151    pub fn len(&self) -> isize {
152        T::len(self)
153    }
154
155    /// Removes the element at index position `pos`.
156    ///
157    /// Element removal will preserve the list's capacity and not reduce the amount of allocated memory.
158    pub fn remove(&mut self, pos: isize) {
159        T::remove(self, pos);
160    }
161
162    /// Attempts to allocate memory for at least `size` elements.
163    ///
164    /// If you know in advance how large the list will be, you should call this function to prevent reallocations and memory fragmentation. If you resize the list often, you are also likely to get better performance.
165    ///
166    /// If in doubt about how much space shall be needed, it is usually better to use an upper bound as `size`, or a high estimate of the most likely size, if a strict upper bound would be much bigger than this. If `size` is an underestimate, the list will grow as needed once the reserved size is exceeded, which may lead to a larger allocation than your best overestimate would have and will slow the operation that triggers it.
167    pub fn reserve(&mut self, size: isize) {
168        T::reserve(self, size);
169    }
170
171    /// Helper function for handling Rust values.
172    pub(crate) fn reserve_usize(&mut self, size: usize) {
173        if size != 0 {
174            T::reserve(self, isize::try_from(size).unwrap_or(isize::MAX));
175        }
176    }
177}
178
179impl<T> QList<T>
180where
181    T: QListElement + ExternType<Kind = cxx::kind::Trivial>,
182{
183    /// Inserts `value` at the end of the list.
184    pub fn append(&mut self, value: T) {
185        T::append(self, value);
186    }
187
188    /// Inserts item `value` into the list at index position `pos`.
189    pub fn insert(&mut self, pos: isize, value: T) {
190        T::insert(self, pos, value);
191    }
192}
193
194impl<T> From<&QList<T>> for Vec<T>
195where
196    T: QListElement + Clone,
197{
198    /// Convert a reference to a [`QList`] into a [`Vec`] by making a deep copy of the data.
199    /// The original `QList` can still be used after constructing the `Vec`.
200    fn from(qlist: &QList<T>) -> Self {
201        let mut vec = Vec::with_capacity(qlist.len().try_into().unwrap());
202        for element in qlist.iter() {
203            vec.push(element.clone());
204        }
205        vec
206    }
207}
208
209impl<T, S> From<S> for QList<T>
210where
211    T: QListElement + Clone,
212    S: AsRef<[T]>,
213{
214    /// Convert anything that can be cheaply converted to a slice, such as an [array] or [`Vec`], into a [`QList`]
215    /// by making a deep copy of the data.
216    /// The original slice can still be used after constructing the `QList`.
217    fn from(vec: S) -> Self {
218        let mut qlist = Self::default();
219        qlist.reserve_usize(vec.as_ref().len());
220        for element in vec.as_ref() {
221            qlist.append_clone(element);
222        }
223        qlist
224    }
225}
226
227impl<'a, T> Extend<&'a T> for QList<T>
228where
229    T: QListElement,
230{
231    fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
232        let iter = iter.into_iter();
233        self.reserve_usize(iter.size_hint().0);
234        for element in iter {
235            self.append_clone(element);
236        }
237    }
238}
239
240impl<T> Extend<T> for QList<T>
241where
242    T: QListElement + ExternType<Kind = cxx::kind::Trivial>,
243{
244    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
245        let iter = iter.into_iter();
246        self.reserve_usize(iter.size_hint().0);
247        for element in iter {
248            self.append(element);
249        }
250    }
251}
252
253impl<'a, T> FromIterator<&'a T> for QList<T>
254where
255    T: QListElement,
256{
257    fn from_iter<I: IntoIterator<Item = &'a T>>(iter: I) -> Self {
258        let mut qlist = Self::default();
259        qlist.extend(iter);
260        qlist
261    }
262}
263
264impl<T> FromIterator<T> for QList<T>
265where
266    T: QListElement + ExternType<Kind = cxx::kind::Trivial>,
267{
268    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
269        let mut qlist = Self::default();
270        qlist.extend(iter);
271        qlist
272    }
273}
274
275unsafe impl<T> ExternType for QList<T>
276where
277    T: ExternType + QListElement,
278{
279    type Id = T::TypeId;
280    type Kind = cxx::kind::Trivial;
281}
282
283pub struct Iter<'a, T>
284where
285    T: QListElement,
286{
287    list: &'a QList<T>,
288    index: isize,
289}
290
291impl<'a, T> Iterator for Iter<'a, T>
292where
293    T: QListElement,
294{
295    type Item = &'a T;
296
297    fn next(&mut self) -> Option<Self::Item> {
298        if self.index < self.list.len() {
299            let next = unsafe { T::get_unchecked(self.list, self.index) };
300            self.index += 1;
301            Some(next)
302        } else {
303            None
304        }
305    }
306
307    fn size_hint(&self) -> (usize, Option<usize>) {
308        let len = self.len();
309        (len, Some(len))
310    }
311}
312
313impl<T> ExactSizeIterator for Iter<'_, T>
314where
315    T: QListElement,
316{
317    fn len(&self) -> usize {
318        (self.list.len() - self.index) as usize
319    }
320}
321
322impl<'a, T> IntoIterator for &'a QList<T>
323where
324    T: QListElement,
325{
326    type Item = &'a T;
327
328    type IntoIter = Iter<'a, T>;
329
330    fn into_iter(self) -> Self::IntoIter {
331        self.iter()
332    }
333}
334
335/// Trait implementation for an element in a [`QList`].
336pub trait QListElement: Sized {
337    type TypeId;
338
339    fn append(list: &mut QList<Self>, value: Self)
340    where
341        Self: ExternType<Kind = cxx::kind::Trivial>;
342    fn append_clone(list: &mut QList<Self>, value: &Self);
343    fn clear(list: &mut QList<Self>);
344    fn clone(list: &QList<Self>) -> QList<Self>;
345    fn contains(list: &QList<Self>, value: &Self) -> bool;
346    fn default() -> QList<Self>;
347    fn drop(list: &mut QList<Self>);
348    /// # Safety
349    ///
350    /// Calling this method with an out-of-bounds index is undefined behavior
351    /// even if the resulting reference is not used.
352    unsafe fn get_unchecked(list: &QList<Self>, pos: isize) -> &Self;
353    fn index_of(list: &QList<Self>, value: &Self) -> isize;
354    fn insert(list: &mut QList<Self>, pos: isize, value: Self)
355    where
356        Self: ExternType<Kind = cxx::kind::Trivial>;
357    fn insert_clone(list: &mut QList<Self>, pos: isize, value: &Self);
358    fn len(list: &QList<Self>) -> isize;
359    fn remove(list: &mut QList<Self>, pos: isize);
360    fn reserve(vector: &mut QList<Self>, size: isize);
361}
362
363macro_rules! impl_qlist_element {
364    ( $typeName:ty, $module:ident, $typeId:literal ) => {
365        mod $module;
366
367        impl QListElement for $typeName {
368            type TypeId = type_id!($typeId);
369
370            fn append(list: &mut QList<Self>, value: Self) {
371                $module::append(list, &value);
372            }
373
374            fn append_clone(list: &mut QList<Self>, value: &Self) {
375                $module::append(list, value);
376            }
377
378            fn clear(list: &mut QList<Self>) {
379                list.cxx_clear()
380            }
381
382            fn clone(list: &QList<Self>) -> QList<Self> {
383                $module::clone(list)
384            }
385
386            fn contains(list: &QList<Self>, value: &Self) -> bool {
387                list.cxx_contains(value)
388            }
389
390            fn default() -> QList<Self> {
391                $module::default()
392            }
393
394            fn drop(list: &mut QList<Self>) {
395                $module::drop(list);
396            }
397
398            unsafe fn get_unchecked(list: &QList<Self>, pos: isize) -> &Self {
399                $module::get_unchecked(list, pos)
400            }
401
402            fn index_of(list: &QList<Self>, value: &Self) -> isize {
403                $module::index_of(list, value)
404            }
405
406            fn insert(list: &mut QList<Self>, pos: isize, value: Self) {
407                $module::insert(list, pos, &value);
408            }
409
410            fn insert_clone(list: &mut QList<Self>, pos: isize, value: &Self) {
411                $module::insert(list, pos, value);
412            }
413
414            fn len(list: &QList<Self>) -> isize {
415                $module::len(list)
416            }
417
418            fn remove(list: &mut QList<Self>, pos: isize) {
419                $module::remove(list, pos);
420            }
421
422            fn reserve(list: &mut QList<Self>, size: isize) {
423                $module::reserve(list, size);
424            }
425        }
426    };
427}
428
429impl_qlist_element!(bool, qlist_bool, "QList_bool");
430impl_qlist_element!(f32, qlist_f32, "QList_f32");
431impl_qlist_element!(f64, qlist_f64, "QList_f64");
432impl_qlist_element!(i8, qlist_i8, "QList_i8");
433impl_qlist_element!(i16, qlist_i16, "QList_i16");
434impl_qlist_element!(i32, qlist_i32, "QList_i32");
435impl_qlist_element!(i64, qlist_i64, "QList_i64");
436impl_qlist_element!(QByteArray, qlist_qbytearray, "QList_QByteArray");
437#[cfg(feature = "qt_gui")]
438impl_qlist_element!(QColor, qlist_qcolor, "QList_QColor");
439impl_qlist_element!(QDate, qlist_qdate, "QList_QDate");
440#[cfg(not(target_os = "emscripten"))]
441impl_qlist_element!(QDateTime, qlist_qdatetime, "QList_QDateTime");
442impl_qlist_element!(QLine, qlist_qline, "QList_QLine");
443impl_qlist_element!(QLineF, qlist_qlinef, "QList_QLineF");
444impl_qlist_element!(QMargins, qlist_qmargins, "QList_QMargins");
445impl_qlist_element!(QMarginsF, qlist_qmarginsf, "QList_QMarginsF");
446impl_qlist_element!(
447    QPersistentModelIndex,
448    qlist_qpersistentmodelindex,
449    "QList_QPersistentModelIndex"
450);
451impl_qlist_element!(QPoint, qlist_qpoint, "QList_QPoint");
452impl_qlist_element!(QPointF, qlist_qpointf, "QList_QPointF");
453impl_qlist_element!(QRect, qlist_qrect, "QList_QRect");
454impl_qlist_element!(QRectF, qlist_qrectf, "QList_QRectF");
455impl_qlist_element!(QSize, qlist_qsize, "QList_QSize");
456impl_qlist_element!(QSizeF, qlist_qsizef, "QList_QSizeF");
457impl_qlist_element!(QString, qlist_qstring, "QList_QString");
458impl_qlist_element!(QTime, qlist_qtime, "QList_QTime");
459impl_qlist_element!(QUrl, qlist_qurl, "QList_QUrl");
460impl_qlist_element!(QUuid, qlist_quuid, "QList_QUuid");
461impl_qlist_element!(QVariant, qlist_qvariant, "QList_QVariant");
462impl_qlist_element!(u8, qlist_u8, "QList_u8");
463impl_qlist_element!(u16, qlist_u16, "QList_u16");
464impl_qlist_element!(u32, qlist_u32, "QList_u32");
465impl_qlist_element!(u64, qlist_u64, "QList_u64");
466
467#[cfg(test)]
468mod test {
469    use super::*;
470
471    #[test]
472    fn qlist_from_array_to_vec() {
473        let array = [0, 1, 2];
474        let qlist = QList::<u8>::from(array);
475        assert_eq!(Vec::from(&qlist), array);
476    }
477
478    #[cfg(feature = "serde")]
479    #[test]
480    fn qlist_serde() {
481        let qlist = QList::<u8>::from([0, 1, 2]);
482        assert_eq!(crate::serde_impl::roundtrip(&qlist), qlist);
483    }
484}