summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBernd Weimer <bernd.weimer@qt.io>2023-06-30 08:47:27 +0200
committerBernd Weimer <bernd.weimer@qt.io>2023-07-25 13:01:00 +0200
commit13c74e599394ed6c7305a5ca8f57456f763bb80f (patch)
tree6983331b622b45da06bd324573a47391fb1492de
parent8bdc93e39fe85e738c05b13b9af962a29a2bd06c (diff)
Add NotificationModel QML type
The type is a proxy for the NotificationManager model and provides the possibility to sort and filter it. Also improved the other proxy models. Fixes: QTBUG-114879 Pick-to: 6.6 Change-Id: Icf86e2a4a00fdbb296a97dd7ed4c1b9557a16ec8 Reviewed-by: Robert Griebl <robert.griebl@qt.io>
-rw-r--r--qmltypes/QtApplicationManager/SystemUI/plugins.qmltypes41
-rw-r--r--src/common-lib/utilities.cpp8
-rw-r--r--src/common-lib/utilities.h3
-rw-r--r--src/intent-server-lib/intentmodel.cpp27
-rw-r--r--src/intent-server-lib/intentmodel.h2
-rw-r--r--src/manager-lib/CMakeLists.txt1
-rw-r--r--src/manager-lib/applicationmodel.cpp27
-rw-r--r--src/manager-lib/applicationmodel.h2
-rw-r--r--src/manager-lib/notificationmanager.cpp2
-rw-r--r--src/manager-lib/notificationmodel.cpp264
-rw-r--r--src/manager-lib/notificationmodel.h61
-rw-r--r--src/tools/dumpqmltypes/dumpqmltypes.cpp2
-rw-r--r--tests/auto/qml/CMakeLists.txt1
-rw-r--r--tests/auto/qml/notifications/CMakeLists.txt1
-rw-r--r--tests/auto/qml/notifications/tst_notifications.qml56
15 files changed, 464 insertions, 34 deletions
diff --git a/qmltypes/QtApplicationManager/SystemUI/plugins.qmltypes b/qmltypes/QtApplicationManager/SystemUI/plugins.qmltypes
index 1b4b17b3..5c6e890c 100644
--- a/qmltypes/QtApplicationManager/SystemUI/plugins.qmltypes
+++ b/qmltypes/QtApplicationManager/SystemUI/plugins.qmltypes
@@ -592,6 +592,47 @@ Module {
}
}
Component {
+ name: "NotificationModel"
+ exports: [ "QtApplicationManager.SystemUI/NotificationModel 2.2", "QtApplicationManager.SystemUI/NotificationModel 2.1", "QtApplicationManager.SystemUI/NotificationModel 2.0" ]
+ exportMetaObjectRevisions: [ 514, 513, 512 ]
+ prototype: "QObject"
+ Property { name: "count"; type: "int"; isReadonly: true; isFinal: true }
+ Property { name: "filterFunction"; type: "QJSValue"; isFinal: true }
+ Property { name: "sortFunction"; type: "QJSValue"; isFinal: true }
+ Signal {
+ name: "countChanged"
+ }
+ Signal {
+ name: "filterFunctionChanged"
+ }
+ Signal {
+ name: "sortFunctionChanged"
+ }
+ Method {
+ name: "indexOfNotification"
+ type: "int"
+ Parameter { name: "notificationId"; type: "uint" }
+ }
+ Method {
+ name: "indexOfNotification"
+ type: "int"
+ Parameter { name: "notification"; type: "Notification"; isPointer: true }
+ }
+ Method {
+ name: "mapToSource"
+ type: "int"
+ Parameter { name: "ourIndex"; type: "int" }
+ }
+ Method {
+ name: "mapFromSource"
+ type: "int"
+ Parameter { name: "sourceIndex"; type: "int" }
+ }
+ Method {
+ name: "invalidate"
+ }
+ }
+ Component {
name: "NotificationManager"
exports: [ "QtApplicationManager.SystemUI/NotificationManager 2.0" ]
exportMetaObjectRevisions: [ 512 ]
diff --git a/src/common-lib/utilities.cpp b/src/common-lib/utilities.cpp
index 91645647..7c4e4282 100644
--- a/src/common-lib/utilities.cpp
+++ b/src/common-lib/utilities.cpp
@@ -10,6 +10,8 @@
#include <QCoreApplication>
#include <QNetworkInterface>
#include <QPluginLoader>
+#include <QQmlContext>
+#include <QQmlEngine>
#include <qplatformdefs.h>
#include "utilities.h"
@@ -346,4 +348,10 @@ void validateIdForFilesystemUsage(const QString &id) Q_DECL_NOEXCEPT_EXPR(false
throw Exception(Error::Parse, "must not consist of only white-space characters");
}
+QJSEngine *getJSEngine(const QObject *obj)
+{
+ QQmlContext *context = QQmlEngine::contextForObject(obj);
+ return context ? reinterpret_cast<QJSEngine*>(context->engine()) : nullptr;
+}
+
QT_END_NAMESPACE_AM
diff --git a/src/common-lib/utilities.h b/src/common-lib/utilities.h
index ed52ce4b..b803500e 100644
--- a/src/common-lib/utilities.h
+++ b/src/common-lib/utilities.h
@@ -20,6 +20,7 @@
#include <functional>
QT_FORWARD_DECLARE_CLASS(QDir)
+QT_FORWARD_DECLARE_CLASS(QJSEngine)
QT_BEGIN_NAMESPACE_AM
@@ -126,4 +127,6 @@ void closeAndClearFileDescriptors(QVector<int> &fdList);
// make sure that the given id can be used as a filename
void validateIdForFilesystemUsage(const QString &id) Q_DECL_NOEXCEPT_EXPR(false);
+QJSEngine *getJSEngine(const QObject *obj);
+
QT_END_NAMESPACE_AM
diff --git a/src/intent-server-lib/intentmodel.cpp b/src/intent-server-lib/intentmodel.cpp
index a3ef2bcd..c5c82503 100644
--- a/src/intent-server-lib/intentmodel.cpp
+++ b/src/intent-server-lib/intentmodel.cpp
@@ -2,7 +2,6 @@
// Copyright (C) 2019 Luxoft Sweden AB
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-#include <QQmlContext>
#include <QQmlEngine>
#include <QQmlInfo>
#include <QJSEngine>
@@ -13,6 +12,7 @@
#include "intentserver.h"
#include "intentmodel.h"
#include "intent.h"
+#include "utilities.h"
/*!
@@ -49,6 +49,7 @@
}
delegate: Image {
+ required property string icon
source: icon
}
}
@@ -127,10 +128,11 @@ bool IntentModel::filterAcceptsRow(int source_row, const QModelIndex &source_par
{
Q_UNUSED(source_parent)
- if (!d->m_engine)
- d->m_engine = getJSEngine();
- if (!d->m_engine)
- qCWarning(LogSystem) << "IntentModel can't filter without a JavaScript engine";
+ if (!d->m_engine) {
+ d->m_engine = getJSEngine(this);
+ if (!d->m_engine)
+ qCWarning(LogIntents) << "IntentModel can't filter without a JavaScript engine";
+ }
if (d->m_engine && d->m_filterFunction.isCallable()) {
const QObject *intent = IntentServer::instance()->intent(source_row);
@@ -143,10 +145,11 @@ bool IntentModel::filterAcceptsRow(int source_row, const QModelIndex &source_par
bool IntentModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
- if (!d->m_engine)
- d->m_engine = getJSEngine();
- if (!d->m_engine)
- qCWarning(LogSystem) << "IntentModel can't sort without a JavaScript engine";
+ if (!d->m_engine) {
+ d->m_engine = getJSEngine(this);
+ if (!d->m_engine)
+ qCWarning(LogIntents) << "IntentModel can't sort without a JavaScript engine";
+ }
if (d->m_engine && d->m_sortFunction.isCallable()) {
const QObject *intent1 = IntentServer::instance()->intent(source_left.row());
@@ -247,12 +250,6 @@ void IntentModel::invalidate()
QSortFilterProxyModel::invalidate();
}
-QJSEngine *IntentModel::getJSEngine() const
-{
- QQmlContext *context = QQmlEngine::contextForObject(this);
- return context ? reinterpret_cast<QJSEngine*>(context->engine()) : nullptr;
-}
-
QT_END_NAMESPACE_AM
#include "moc_intentmodel.cpp"
diff --git a/src/intent-server-lib/intentmodel.h b/src/intent-server-lib/intentmodel.h
index 65109131..3aef1548 100644
--- a/src/intent-server-lib/intentmodel.h
+++ b/src/intent-server-lib/intentmodel.h
@@ -56,8 +56,6 @@ signals:
void sortFunctionChanged();
private:
- QJSEngine *getJSEngine() const;
-
IntentModelPrivate *d;
};
diff --git a/src/manager-lib/CMakeLists.txt b/src/manager-lib/CMakeLists.txt
index 2e4f3481..35bc83d9 100644
--- a/src/manager-lib/CMakeLists.txt
+++ b/src/manager-lib/CMakeLists.txt
@@ -23,6 +23,7 @@ qt_internal_add_module(AppManManagerPrivate
inprocesssurfaceitem.cpp inprocesssurfaceitem.h
intentaminterface.cpp intentaminterface.h
notificationmanager.cpp notificationmanager.h
+ notificationmodel.cpp notificationmodel.h
package.cpp package.h
packagemanager.cpp packagemanager.h packagemanager_p.h
plugincontainer.cpp plugincontainer.h
diff --git a/src/manager-lib/applicationmodel.cpp b/src/manager-lib/applicationmodel.cpp
index 9915b39f..04d7b8f1 100644
--- a/src/manager-lib/applicationmodel.cpp
+++ b/src/manager-lib/applicationmodel.cpp
@@ -3,7 +3,6 @@
// Copyright (C) 2018 Pelagicore AG
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-#include <QQmlContext>
#include <QQmlEngine>
#include <QQmlInfo>
#include <QJSEngine>
@@ -14,6 +13,7 @@
#include "applicationmanager.h"
#include "applicationmodel.h"
#include "application.h"
+#include "utilities.h"
/*!
@@ -49,6 +49,7 @@
}
delegate: Image {
+ required property string icon
source: icon
}
}
@@ -135,10 +136,11 @@ bool ApplicationModel::filterAcceptsRow(int source_row, const QModelIndex &sourc
{
Q_UNUSED(source_parent)
- if (!d->m_engine)
- d->m_engine = getJSEngine();
- if (!d->m_engine)
- qCWarning(LogSystem) << "ApplicationModel can't filter without a JavaScript engine";
+ if (!d->m_engine) {
+ d->m_engine = getJSEngine(this);
+ if (!d->m_engine)
+ qCWarning(LogSystem) << "ApplicationModel can't filter without a JavaScript engine";
+ }
if (d->m_engine && d->m_filterFunction.isCallable()) {
const QObject *app = ApplicationManager::instance()->application(source_row);
@@ -151,10 +153,11 @@ bool ApplicationModel::filterAcceptsRow(int source_row, const QModelIndex &sourc
bool ApplicationModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
- if (!d->m_engine)
- d->m_engine = getJSEngine();
- if (!d->m_engine)
- qCWarning(LogSystem) << "ApplicationModel can't sort without a JavaScript engine";
+ if (!d->m_engine) {
+ d->m_engine = getJSEngine(this);
+ if (!d->m_engine)
+ qCWarning(LogSystem) << "ApplicationModel can't sort without a JavaScript engine";
+ }
if (d->m_engine && d->m_sortFunction.isCallable()) {
const QObject *app1 = ApplicationManager::instance()->application(source_left.row());
@@ -260,12 +263,6 @@ void ApplicationModel::invalidate()
QSortFilterProxyModel::invalidate();
}
-QJSEngine *ApplicationModel::getJSEngine() const
-{
- QQmlContext *context = QQmlEngine::contextForObject(this);
- return context ? reinterpret_cast<QJSEngine*>(context->engine()) : nullptr;
-}
-
QT_END_NAMESPACE_AM
#include "moc_applicationmodel.cpp"
diff --git a/src/manager-lib/applicationmodel.h b/src/manager-lib/applicationmodel.h
index 37c163d7..8d8fc093 100644
--- a/src/manager-lib/applicationmodel.h
+++ b/src/manager-lib/applicationmodel.h
@@ -57,8 +57,6 @@ signals:
void sortFunctionChanged();
private:
- QJSEngine *getJSEngine() const;
-
ApplicationModelPrivate *d;
};
diff --git a/src/manager-lib/notificationmanager.cpp b/src/manager-lib/notificationmanager.cpp
index 667b64bc..fc0ddc0d 100644
--- a/src/manager-lib/notificationmanager.cpp
+++ b/src/manager-lib/notificationmanager.cpp
@@ -16,6 +16,7 @@
#include "qml-utilities.h"
#include "dbus-utilities.h"
#include "package.h"
+#include "notificationmodel.h"
/*!
\qmltype NotificationManager
@@ -230,6 +231,7 @@ NotificationManager *NotificationManager::createInstance()
if (Q_UNLIKELY(s_instance))
qFatal("NotificationManager::createInstance() was called a second time.");
+ qmlRegisterType<NotificationModel>("QtApplicationManager.SystemUI", 2, 2, "NotificationModel");
qmlRegisterSingletonType<NotificationManager>("QtApplicationManager.SystemUI", 2, 0, "NotificationManager",
&NotificationManager::instanceForQml);
return s_instance = new NotificationManager();
diff --git a/src/manager-lib/notificationmodel.cpp b/src/manager-lib/notificationmodel.cpp
new file mode 100644
index 00000000..1bb563c5
--- /dev/null
+++ b/src/manager-lib/notificationmodel.cpp
@@ -0,0 +1,264 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QQmlEngine>
+#include <QQmlInfo>
+#include <QJSEngine>
+#include <QJSValueList>
+
+#include "global.h"
+#include "logging.h"
+#include "notificationmanager.h"
+#include "notificationmodel.h"
+#include "notification.h"
+#include "utilities.h"
+
+
+/*!
+ \qmltype NotificationModel
+ \inherits QSortFilterProxyModel
+ \ingroup system-ui-instantiable
+ \inqmlmodule QtApplicationManager.SystemUI
+ \brief A proxy model for the NotificationManager singleton.
+
+ The NotificationModel type provides a customizable model that can be used to tailor the
+ NotificationManager model to your needs. The NotificationManager singleton is a model itself,
+ that includes all available notifications. In contrast, the NotificationModel supports filtering
+ and sorting.
+
+ Since the NotificationModel is based on the NotificationManager model, the latter is referred
+ to as the \e source model. The NotificationModel includes the same \l {NotificationManager Roles}
+ {roles} as the NotificationManager model.
+
+ \note If you require a model with all notifications, with no filtering whatsoever, you should
+ use the NotificationManager directly, as it has better performance.
+
+ The following code snippet displays the summary of all notifications with a priority greater
+ than 5 in a list:
+
+ \qml
+ import QtQuick
+ import QtApplicationManager.SystemUI
+
+ ListView {
+ model: NotificationModel {
+ filterFunction: function(notification) {
+ return notification.priority > 5;
+ }
+ }
+
+ Text {
+ required property string summary
+ text: summary
+ }
+ }
+ \endqml
+*/
+
+/*!
+ \qmlproperty int NotificationModel::count
+ \readonly
+
+ Holds the number of notifications included in this model.
+*/
+
+/*!
+ \qmlproperty var NotificationModel::filterFunction
+
+ A JavaScript function callback that is invoked for each notification in the NotificationManager
+ source model. This function gets one Notification parameter and must return a Boolean.
+ If the notification passed should be included in this model, then the function must return
+ \c true; \c false otherwise.
+
+ If you need no filtering at all, you should set this property to an undefined (the default) or
+ null value.
+
+ \note Whenever this function is changed, the filter is reevaluated. Changes in the source model
+ are also reflected. To force a complete reevaluation, call \l {NotificationModel::invalidate()}
+ {invalidate()}.
+*/
+
+/*!
+ \qmlproperty var NotificationModel::sortFunction
+
+ A JavaScript function callback that is invoked to sort the notifications in this model. This
+ function gets two Notification parameters and must return a Boolean. If the first
+ notification should have a smaller index in this model than the second, the function must return
+ \c true; \c false otherwise.
+
+ If you need no sorting at all, you should set this property to an undefined (the default) or
+ null value.
+
+ \note Whenever this function is changed, the model is sorted. Changes in the source model are
+ also reflected. To force a complete reevaluation, call \l {NotificationModel::invalidate()}
+ {invalidate()}.
+*/
+
+
+QT_BEGIN_NAMESPACE_AM
+
+
+class NotificationModelPrivate
+{
+public:
+ QJSEngine *m_engine = nullptr;
+ QJSValue m_filterFunction;
+ QJSValue m_sortFunction;
+};
+
+
+NotificationModel::NotificationModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+ , d(new NotificationModelPrivate())
+{
+ setSourceModel(NotificationManager::instance());
+
+ connect(this, &QAbstractItemModel::rowsInserted, this, &NotificationModel::countChanged);
+ connect(this, &QAbstractItemModel::rowsRemoved, this, &NotificationModel::countChanged);
+ connect(this, &QAbstractItemModel::layoutChanged, this, &NotificationModel::countChanged);
+ connect(this, &QAbstractItemModel::modelReset, this, &NotificationModel::countChanged);
+}
+
+NotificationModel::~NotificationModel()
+{
+ delete d;
+}
+
+int NotificationModel::count() const
+{
+ return rowCount();
+}
+
+bool NotificationModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
+{
+ Q_UNUSED(source_parent)
+
+ if (!d->m_engine) {
+ d->m_engine = getJSEngine(this);
+ if (!d->m_engine)
+ qCWarning(LogNotifications) << "NotificationModel can't filter without a JavaScript engine";
+ }
+
+ if (d->m_engine && d->m_filterFunction.isCallable()) {
+ const QVariantMap notification = NotificationManager::instance()->get(source_row);
+ QJSValueList args = { d->m_engine->toScriptValue(notification) };
+ return d->m_filterFunction.call(args).toBool();
+ }
+
+ return true;
+}
+
+bool NotificationModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
+{
+ if (!d->m_engine) {
+ d->m_engine = getJSEngine(this);
+ if (!d->m_engine)
+ qCWarning(LogNotifications) << "NotificationModel can't sort without a JavaScript engine";
+ }
+
+ if (d->m_engine && d->m_sortFunction.isCallable()) {
+ const QVariantMap left = NotificationManager::instance()->get(source_left.row());
+ const QVariantMap right = NotificationManager::instance()->get(source_right.row());
+ QJSValueList args = { d->m_engine->toScriptValue(left), d->m_engine->toScriptValue(right) };
+ return d->m_sortFunction.call(args).toBool();
+ }
+
+ return QSortFilterProxyModel::lessThan(source_left, source_right);
+}
+
+QJSValue NotificationModel::filterFunction() const
+{
+ return d->m_filterFunction;
+}
+
+void NotificationModel::setFilterFunction(const QJSValue &callback)
+{
+ if (!callback.isCallable() && !callback.isNull() && !callback.isUndefined()) {
+ qmlWarning(this) << "The filterFunction property of NotificationModel needs to be either "
+ "callable, or undefined/null to clear it.";
+ }
+
+ if (!callback.equals(d->m_filterFunction)) {
+ d->m_filterFunction = callback;
+ emit filterFunctionChanged();
+ invalidateFilter();
+ }
+}
+
+QJSValue NotificationModel::sortFunction() const
+{
+ return d->m_sortFunction;
+}
+
+void NotificationModel::setSortFunction(const QJSValue &callback)
+{
+ if (!callback.isCallable() && !callback.isNull() && !callback.isUndefined()) {
+ qmlWarning(this) << "The sortFunction property of NotificationModel needs to be either "
+ "callable, or undefined/null to clear it.";
+ }
+ if (!callback.equals(d->m_sortFunction)) {
+ d->m_sortFunction = callback;
+ emit sortFunctionChanged();
+ invalidate();
+ sort(0);
+ }
+}
+
+/*!
+ \qmlmethod int NotificationModel::indexOfNotification(int notificationId)
+
+ Maps the notification corresponding to the given \a notificationId to its position within this
+ model. Returns \c -1 if the specified notification is invalid, or not part of this model.
+*/
+int NotificationModel::indexOfNotification(uint notificationId) const
+{
+ int idx = NotificationManager::instance()->indexOfNotification(notificationId);
+ return idx != -1 ? mapFromSource(idx) : idx;
+}
+
+/*!
+ \qmlmethod int NotificationModel::indexOfNotification(object notification)
+
+ Maps the \a notification to its position within this model. Returns \c -1 if the specified
+ notification is invalid, or not part of this model.
+*/
+int NotificationModel::indexOfNotification(Notification *notification) const
+{
+ return indexOfNotification(notification->notificationId());
+}
+
+/*!
+ \qmlmethod int NotificationModel::mapToSource(int index)
+
+ Maps a notification's \a index in this model to the corresponding index in the
+ NotificationManager model. Returns \c -1 if the specified \a index is invalid.
+*/
+int NotificationModel::mapToSource(int ourIndex) const
+{
+ return QSortFilterProxyModel::mapToSource(index(ourIndex, 0)).row();
+}
+
+/*!
+ \qmlmethod int NotificationModel::mapFromSource(int index)
+
+ Maps a notification's \a index from the NotificationManager model to the corresponding index in
+ this model. Returns \c -1 if the specified \a index is invalid.
+*/
+int NotificationModel::mapFromSource(int sourceIndex) const
+{
+ return QSortFilterProxyModel::mapFromSource(sourceModel()->index(sourceIndex, 0)).row();
+}
+
+/*!
+ \qmlmethod NotificationModel::invalidate()
+
+ Forces a reevaluation of the model.
+*/
+void NotificationModel::invalidate()
+{
+ QSortFilterProxyModel::invalidate();
+}
+
+QT_END_NAMESPACE_AM
+
+#include "moc_notificationmodel.cpp"
diff --git a/src/manager-lib/notificationmodel.h b/src/manager-lib/notificationmodel.h
new file mode 100644
index 00000000..307d58ac
--- /dev/null
+++ b/src/manager-lib/notificationmodel.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/QSortFilterProxyModel>
+#include <QtQml/QJSValue>
+#include <QtAppManCommon/global.h>
+
+QT_FORWARD_DECLARE_CLASS(QJSEngine);
+
+QT_BEGIN_NAMESPACE_AM
+
+class NotificationModelPrivate;
+class Notification;
+
+class NotificationModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+ Q_CLASSINFO("AM-QmlType", "QtApplicationManager.SystemUI/NotificationModel 2.2")
+ Q_CLASSINFO("AM-QmlPrototype", "QObject")
+
+ Q_PROPERTY(int count READ count NOTIFY countChanged FINAL)
+ Q_PROPERTY(QJSValue filterFunction READ filterFunction WRITE setFilterFunction NOTIFY filterFunctionChanged FINAL)
+ Q_PROPERTY(QJSValue sortFunction READ sortFunction WRITE setSortFunction NOTIFY sortFunctionChanged FINAL)
+
+public:
+ NotificationModel(QObject *parent = nullptr);
+ ~NotificationModel() override;
+
+ int count() const;
+
+ QJSValue filterFunction() const;
+ void setFilterFunction(const QJSValue &callback);
+
+ QJSValue sortFunction() const;
+ void setSortFunction(const QJSValue &callback);
+
+ Q_INVOKABLE int indexOfNotification(uint notificationId) const;
+ Q_INVOKABLE int indexOfNotification(QT_PREPEND_NAMESPACE_AM(Notification) *notification) const;
+ Q_INVOKABLE int mapToSource(int ourIndex) const;
+ Q_INVOKABLE int mapFromSource(int sourceIndex) const;
+ Q_INVOKABLE void invalidate();
+
+protected:
+ using QSortFilterProxyModel::mapToSource;
+ using QSortFilterProxyModel::mapFromSource;
+
+ bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
+
+signals:
+ void countChanged();
+ void filterFunctionChanged();
+ void sortFunctionChanged();
+
+private:
+ NotificationModelPrivate *d;
+};
+
+QT_END_NAMESPACE_AM
diff --git a/src/tools/dumpqmltypes/dumpqmltypes.cpp b/src/tools/dumpqmltypes/dumpqmltypes.cpp
index 84b83b7a..6355523c 100644
--- a/src/tools/dumpqmltypes/dumpqmltypes.cpp
+++ b/src/tools/dumpqmltypes/dumpqmltypes.cpp
@@ -14,6 +14,7 @@
#include <QtAppManManager/notificationmanager.h>
#include <QtAppManNotification/notification.h>
#include <QtAppManManager/notificationmanager.h>
+#include <QtAppManManager/notificationmodel.h>
#include <QtAppManManager/qmlinprocessapplicationinterface.h>
#include <QtAppManWindow/windowmanager.h>
#include <QtAppManWindow/window.h>
@@ -50,6 +51,7 @@ static const QVector<const QMetaObject *> all = {
&ApplicationInstaller::staticMetaObject,
&PackageManager::staticMetaObject,
&NotificationManager::staticMetaObject,
+ &NotificationModel::staticMetaObject,
&Application::staticMetaObject,
&AbstractRuntime::staticMetaObject,
&AbstractContainer::staticMetaObject,
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt
index efd57d78..3399e236 100644
--- a/tests/auto/qml/CMakeLists.txt
+++ b/tests/auto/qml/CMakeLists.txt
@@ -20,6 +20,7 @@ add_subdirectory(lifecycle)
add_subdirectory(resources)
add_subdirectory(keyinput)
add_subdirectory(monitoring)
+add_subdirectory(notifications)
if (QT_FEATURE_am_multi_process)
add_subdirectory(crash)
add_subdirectory(processtitle)
diff --git a/tests/auto/qml/notifications/CMakeLists.txt b/tests/auto/qml/notifications/CMakeLists.txt
new file mode 100644
index 00000000..312616ea
--- /dev/null
+++ b/tests/auto/qml/notifications/CMakeLists.txt
@@ -0,0 +1 @@
+qt_am_internal_add_qml_test(tst_notifications TEST_FILE tst_notifications.qml)
diff --git a/tests/auto/qml/notifications/tst_notifications.qml b/tests/auto/qml/notifications/tst_notifications.qml
new file mode 100644
index 00000000..a6e9ede4
--- /dev/null
+++ b/tests/auto/qml/notifications/tst_notifications.qml
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtTest
+import QtApplicationManager.SystemUI
+
+TestCase {
+ id: testCase
+ name: "Notifications"
+
+ Component {
+ id: notificationComponent
+ Notification {}
+ }
+
+ NotificationModel {
+ id: notificationModel
+ sortFunction: function(ln, rn) { return ln.priority < rn.priority; }
+ }
+
+ SignalSpy {
+ id: notificationModelCountSpy
+ target: notificationModel
+ signalName: "countChanged"
+ }
+
+ function test_notificationModel() {
+ compare(notificationModel.count, 0);
+
+ let ntfc = [];
+ for (let i = 0; i < 3; i++) {
+ ntfc[i] = notificationComponent.createObject(testCase, { summary: `N${i}`, sticky: true });
+ ntfc[i].priority = i === 1 ? 7 : i;
+ notificationModelCountSpy.clear();
+ ntfc[i].show();
+ notificationModelCountSpy.wait(1000 * AmTest.timeoutFactor);
+ compare(notificationModel.count, i + 1);
+ }
+
+ compare(notificationModel.indexOfNotification(ntfc[0].notificationId), 0);
+ compare(notificationModel.indexOfNotification(ntfc[1]), 2);
+ compare(notificationModel.indexOfNotification(ntfc[2].notificationId), 1);
+
+ notificationModelCountSpy.clear();
+ notificationModel.filterFunction = function(n) { return n.summary !== "N0"; };
+
+ notificationModelCountSpy.wait(1000 * AmTest.timeoutFactor);
+ compare(notificationModel.count, 2);
+ compare(notificationModel.indexOfNotification(ntfc[2]), 0);
+
+ ntfc[1].summary = "N0";
+ notificationModelCountSpy.wait(1000 * AmTest.timeoutFactor);
+ compare(notificationModel.count, 1);
+ }
+}