diff options
Diffstat (limited to 'src/qml/jsapi/qjslist.h')
-rw-r--r-- | src/qml/jsapi/qjslist.h | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/src/qml/jsapi/qjslist.h b/src/qml/jsapi/qjslist.h new file mode 100644 index 0000000000..d604e266f2 --- /dev/null +++ b/src/qml/jsapi/qjslist.h @@ -0,0 +1,368 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QJSLIST_H +#define QJSLIST_H + +#include <QtQml/qtqmlglobal.h> +#include <QtQml/qqmllist.h> +#include <QtQml/qjsengine.h> +#include <QtCore/qobject.h> +#include <QtCore/qstring.h> + +#include <algorithm> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version. It will be kept compatible with the intended usage by +// code generated using qmlcachegen. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +struct QJSListIndexClamp +{ + static qsizetype clamp(qsizetype start, qsizetype max, qsizetype min = 0) + { + Q_ASSERT(min >= 0); + Q_ASSERT(min <= max); + return std::clamp(start < 0 ? max + qsizetype(start) : qsizetype(start), min, max); + } +}; + +template<typename List, typename Value = typename List::value_type> +struct QJSList : private QJSListIndexClamp +{ + Q_DISABLE_COPY_MOVE(QJSList) + + QJSList(List *list, QJSEngine *engine) : m_list(list), m_engine(engine) {} + + Value at(qsizetype index) const + { + Q_ASSERT(index >= 0 && index < size()); + return *(m_list->cbegin() + index); + } + + qsizetype size() const { return m_list->size(); } + + void resize(qsizetype size) + { + m_list->resize(size); + } + + bool includes(const Value &value) const + { + return std::find(m_list->cbegin(), m_list->cend(), value) != m_list->cend(); + } + + bool includes(const Value &value, qsizetype start) const + { + return std::find(m_list->cbegin() + clamp(start, m_list->size()), m_list->cend(), value) + != m_list->cend(); + } + + QString join(const QString &separator = QStringLiteral(",")) const + { + QString result; + bool atBegin = true; + std::for_each(m_list->cbegin(), m_list->cend(), [&](const Value &value) { + if (atBegin) + atBegin = false; + else + result += separator; + result += m_engine->coerceValue<Value, QString>(value); + }); + return result; + } + + List slice() const + { + return *m_list; + } + List slice(qsizetype start) const + { + List result; + std::copy(m_list->cbegin() + clamp(start, m_list->size()), m_list->cend(), + std::back_inserter(result)); + return result; + } + List slice(qsizetype start, qsizetype end) const + { + const qsizetype size = m_list->size(); + const qsizetype clampedStart = clamp(start, size); + const qsizetype clampedEnd = clamp(end, size, clampedStart); + + List result; + std::copy(m_list->cbegin() + clampedStart, m_list->cbegin() + clampedEnd, + std::back_inserter(result)); + return result; + } + + qsizetype indexOf(const Value &value) const + { + const auto begin = m_list->cbegin(); + const auto end = m_list->cend(); + const auto it = std::find(begin, end, value); + if (it == end) + return -1; + const qsizetype result = it - begin; + Q_ASSERT(result >= 0); + return result; + } + qsizetype indexOf(const Value &value, qsizetype start) const + { + const auto begin = m_list->cbegin(); + const auto end = m_list->cend(); + const auto it = std::find(begin + clamp(start, m_list->size()), end, value); + if (it == end) + return -1; + const qsizetype result = it - begin; + Q_ASSERT(result >= 0); + return result; + } + + qsizetype lastIndexOf(const Value &value) const + { + const auto begin = std::make_reverse_iterator(m_list->cend()); + const auto end = std::make_reverse_iterator(m_list->cbegin()); + const auto it = std::find(begin, end, value); + return (end - it) - 1; + } + qsizetype lastIndexOf(const Value &value, qsizetype start) const + { + const qsizetype size = m_list->size(); + if (size == 0) + return -1; + + // Construct a one-past-end iterator as input. + const qsizetype clampedStart = std::min(clamp(start, size), size - 1); + const auto begin = std::make_reverse_iterator(m_list->cbegin() + clampedStart + 1); + + const auto end = std::make_reverse_iterator(m_list->cbegin()); + const auto it = std::find(begin, end, value); + return (end - it) - 1; + } + + QString toString() const { return join(); } + +private: + List *m_list = nullptr; + QJSEngine *m_engine = nullptr; +}; + +template<> +struct QJSList<QQmlListProperty<QObject>, QObject *> : private QJSListIndexClamp +{ + Q_DISABLE_COPY_MOVE(QJSList) + + QJSList(QQmlListProperty<QObject> *list, QJSEngine *engine) : m_list(list), m_engine(engine) {} + + QObject *at(qsizetype index) const + { + Q_ASSERT(index >= 0 && index < size()); + return m_list->at(m_list, index); + } + + qsizetype size() const + { + return m_list->count(m_list); + } + + void resize(qsizetype size) + { + qsizetype current = m_list->count(m_list); + if (current < size && m_list->append) { + do { + m_list->append(m_list, nullptr); + } while (++current < size); + } else if (current > size && m_list->removeLast) { + do { + m_list->removeLast(m_list); + } while (--current > size); + } + } + + bool includes(const QObject *value) const + { + if (!m_list->count || !m_list->at) + return false; + + const qsizetype size = m_list->count(m_list); + for (qsizetype i = 0; i < size; ++i) { + if (m_list->at(m_list, i) == value) + return true; + } + + return false; + } + bool includes(const QObject *value, qsizetype start) const + { + if (!m_list->count || !m_list->at) + return false; + + const qsizetype size = m_list->count(m_list); + for (qsizetype i = clamp(start, size); i < size; ++i) { + if (m_list->at(m_list, i) == value) + return true; + } + + return false; + } + + QString join(const QString &separator = QStringLiteral(",")) const + { + if (!m_list->count || !m_list->at) + return QString(); + + QString result; + for (qsizetype i = 0, end = m_list->count(m_list); i < end; ++i) { + if (i != 0) + result += separator; + result += m_engine->coerceValue<QObject *, QString>(m_list->at(m_list, i)); + } + + return result; + } + + QObjectList slice() const + { + return m_list->toList<QObjectList>(); + } + QObjectList slice(qsizetype start) const + { + if (!m_list->count || !m_list->at) + return QObjectList(); + + const qsizetype size = m_list->count(m_list); + const qsizetype clampedStart = clamp(start, size); + QObjectList result; + result.reserve(size - clampedStart); + for (qsizetype i = clampedStart; i < size; ++i) + result.append(m_list->at(m_list, i)); + return result; + } + QObjectList slice(qsizetype start, qsizetype end) const + { + if (!m_list->count || !m_list->at) + return QObjectList(); + + const qsizetype size = m_list->count(m_list); + const qsizetype clampedStart = clamp(start, size); + const qsizetype clampedEnd = clamp(end, size, clampedStart); + QObjectList result; + result.reserve(clampedEnd - clampedStart); + for (qsizetype i = clampedStart; i < clampedEnd; ++i) + result.append(m_list->at(m_list, i)); + return result; + } + + qsizetype indexOf(const QObject *value) const + { + if (!m_list->count || !m_list->at) + return -1; + + const qsizetype end = m_list->count(m_list); + for (qsizetype i = 0; i < end; ++i) { + if (m_list->at(m_list, i) == value) + return i; + } + return -1; + } + qsizetype indexOf(const QObject *value, qsizetype start) const + { + if (!m_list->count || !m_list->at) + return -1; + + const qsizetype size = m_list->count(m_list); + for (qsizetype i = clamp(start, size); i < size; ++i) { + if (m_list->at(m_list, i) == value) + return i; + } + return -1; + } + + qsizetype lastIndexOf(const QObject *value) const + { + if (!m_list->count || !m_list->at) + return -1; + + for (qsizetype i = m_list->count(m_list) - 1; i >= 0; --i) { + if (m_list->at(m_list, i) == value) + return i; + } + return -1; + } + qsizetype lastIndexOf(const QObject *value, qsizetype start) const + { + if (!m_list->count || !m_list->at) + return -1; + + const qsizetype size = m_list->count(m_list); + if (size == 0) + return -1; + + qsizetype clampedStart = std::min(clamp(start, size), size - 1); + for (qsizetype i = clampedStart; i >= 0; --i) { + if (m_list->at(m_list, i) == value) + return i; + } + return -1; + } + + QString toString() const { return join(); } + +private: + QQmlListProperty<QObject> *m_list = nullptr; + QJSEngine *m_engine = nullptr; +}; + +struct QJSListForInIterator +{ +public: + using Ptr = QJSListForInIterator *; + template<typename List, typename Value> + void init(const QJSList<List, Value> &list) + { + m_index = 0; + m_size = list.size(); + } + + bool hasNext() const { return m_index < m_size; } + qsizetype next() { return m_index++; } + +private: + qsizetype m_index; + qsizetype m_size; +}; + +// QJSListForInIterator must not require initialization so that we can jump over it with goto. +static_assert(std::is_trivial_v<QJSListForInIterator>); + +struct QJSListForOfIterator +{ +public: + using Ptr = QJSListForOfIterator *; + void init() { m_index = 0; } + + template<typename List, typename Value> + bool hasNext(const QJSList<List, Value> &list) const { return m_index < list.size(); } + + template<typename List, typename Value> + Value next(const QJSList<List, Value> &list) { return list.at(m_index++); } + +private: + qsizetype m_index; +}; + +// QJSListForOfIterator must not require initialization so that we can jump over it with goto. +static_assert(std::is_trivial_v<QJSListForOfIterator>); + +QT_END_NAMESPACE + +#endif // QJSLIST_H |