diff options
author | Ivan Solovev <ivan.solovev@qt.io> | 2023-08-09 17:10:00 +0200 |
---|---|---|
committer | Ivan Solovev <ivan.solovev@qt.io> | 2023-08-22 16:15:05 +0200 |
commit | a33b935a26474be182c502d5dbcf613b1c7dec2c (patch) | |
tree | 901d546796cfbff8dbe1889f94df73ec15c34a6d | |
parent | c75bac86704748a3e5cd02335c4e9b9df5811729 (diff) |
SatelliteInfo: add Table View tab
This commit implements the previously missing Table View tab, which
provides a table of satellites.
A custom SortFilterModel is used to allow filtering and sorting.
The enum containing satellite model roles is moved into a separate
header and wrapped into its own namespace, so that it could be
re-used between the two models.
Task-number: QTBUG-114709
Change-Id: I8b6b5d2100e89c0791d832a69e73ac003de5e62f
Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
Reviewed-by: Juha Vuolle <juha.vuolle@qt.io>
(cherry picked from commit 72e8e301d936c94162434f51a93f89fc80ae6070)
20 files changed, 652 insertions, 29 deletions
diff --git a/examples/positioning/satelliteinfo/ApplicationScreen.qml b/examples/positioning/satelliteinfo/ApplicationScreen.qml index 259669ef..3d296356 100644 --- a/examples/positioning/satelliteinfo/ApplicationScreen.qml +++ b/examples/positioning/satelliteinfo/ApplicationScreen.qml @@ -12,6 +12,7 @@ Rectangle { id: root required property SatelliteModel satellitesModel + required property SortFilterModel sortFilterModel property color inUseColor: Theme.inUseColor property color inViewColor: Theme.inViewColor @@ -131,6 +132,9 @@ Rectangle { inUseColor: root.inUseColor } SatelliteView { + sortFilterModel: root.sortFilterModel + inViewColor: root.inViewColor + inUseColor: root.inUseColor } RssiView { satellitesModel: root.satellitesModel @@ -144,6 +148,7 @@ Rectangle { width: parent.width anchors.bottom: modeButton.top visible: viewsLayout.currentIndex !== navigationTab.tableViewIndex + height: visible ? implicitHeight : 0 } Button { diff --git a/examples/positioning/satelliteinfo/CMakeLists.txt b/examples/positioning/satelliteinfo/CMakeLists.txt index 51a7ed44..6797f7dd 100644 --- a/examples/positioning/satelliteinfo/CMakeLists.txt +++ b/examples/positioning/satelliteinfo/CMakeLists.txt @@ -69,7 +69,9 @@ qt_add_qml_module(satelliteinfo URI SatelliteInformation VERSION 1.0 SOURCES + roles.h satellitemodel.cpp satellitemodel.h + sortfiltermodel.cpp sortfiltermodel.h QML_FILES ApplicationScreen.qml Button.qml @@ -87,7 +89,10 @@ qt_add_qml_module(satelliteinfo Theme.qml ViewSwitch.qml RESOURCES + icons/checkbox.svg + icons/checkbox_blank.svg icons/darkmode.svg + icons/filter.svg icons/help.svg icons/lightmode.svg icons/place.svg @@ -95,8 +100,12 @@ qt_add_qml_module(satelliteinfo icons/qtlogo_white.png icons/rssiview.svg icons/satellite_small.png + icons/satellite1.png + icons/satellite2.png + icons/search.svg icons/settings.svg icons/skyview.svg + icons/sort.svg icons/tableview.svg ) diff --git a/examples/positioning/satelliteinfo/Main.qml b/examples/positioning/satelliteinfo/Main.qml index 7887d33e..c7903024 100644 --- a/examples/positioning/satelliteinfo/Main.qml +++ b/examples/positioning/satelliteinfo/Main.qml @@ -11,6 +11,7 @@ Window { id: root required property SatelliteModel satellitesModel + required property SortFilterModel sortFilterModel width: 360 height: 640 @@ -35,6 +36,7 @@ Window { id: applicationComponent ApplicationScreen { satellitesModel: root.satellitesModel + sortFilterModel: root.sortFilterModel } } diff --git a/examples/positioning/satelliteinfo/SatelliteView.qml b/examples/positioning/satelliteinfo/SatelliteView.qml index 7637bc87..9780fd03 100644 --- a/examples/positioning/satelliteinfo/SatelliteView.qml +++ b/examples/positioning/satelliteinfo/SatelliteView.qml @@ -1,10 +1,424 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +pragma ComponentBehavior: Bound +import QtPositioning import QtQuick +import QtQuick.Controls.Basic as QC +import QtQuick.Layouts +import SatelliteInformation Rectangle { - // Intentionally left empty. - // Will be filled in a follow-up patch. + id: root + + required property SortFilterModel sortFilterModel + required property color inViewColor + required property color inUseColor + + readonly property int iconItemWidth: 50 + color: Theme.darkBackgroundColor + + property var satelliteSystemModel: [ + {"name": "GPS", "id": GeoSatelliteInfo.GPS}, + {"name": "GLONASS", "id": GeoSatelliteInfo.GLONASS}, + {"name": "GALILEO", "id": GeoSatelliteInfo.GALILEO}, + {"name": "BEIDOU", "id": GeoSatelliteInfo.BEIDOU}, + {"name": "QZSS", "id": GeoSatelliteInfo.QZSS} + ] + + component RssiElement : Rectangle { + id: rssiRoot + + required property int rssi + + implicitHeight: rssiVal.implicitHeight + implicitWidth: rssiVal.implicitWidth + dbm.implicitWidth + rssiLayout.spacing + color: "transparent" + + RowLayout { + id: rssiLayout + anchors.fill: parent + spacing: 2 + Text { + id: rssiVal + text: rssiRoot.rssi >= 0 ? rssiRoot.rssi : qsTr("N/A") + color: Theme.textSecondaryColor + font.pixelSize: Theme.smallFontSize + font.weight: Theme.fontDefaultWeight + } + Text{ + id: dbm + visible: rssiRoot.rssi >= 0 + text: qsTr("dBm") + color: Theme.textGrayColor + font.pixelSize: Theme.smallFontSize + font.weight: Theme.fontDefaultWeight + } + } + } + + component StatusElement : Rectangle { + id: statusRoot + + required property bool inUse + + implicitHeight: statusText.implicitHeight + implicitWidth: statusText.implicitWidth + statusIcon.implicitWidth + statusLayout.spacing + color: "transparent" + + RowLayout { + id: statusLayout + anchors.fill: parent + spacing: Theme.defaultSpacing + Rectangle { + id: statusIcon + implicitHeight: statusText.font.pixelSize + implicitWidth: implicitHeight + radius: height / 2 + color: statusRoot.inUse ? root.inUseColor : root.inViewColor + } + Text { + id: statusText + text: statusRoot.inUse ? qsTr("In Use") : qsTr("In View") + color: Theme.textSecondaryColor + font.pixelSize: Theme.smallFontSize + font.weight: Theme.fontLightWeight + } + } + } + + RssiElement { + id: hiddenRssiElement + rssi: 999 + visible: false + } + StatusElement { + id: hiddenStatusElement + inUse: false + visible: false + } + + component ListHeader : Rectangle { + implicitHeight: identifierHdr.height + color: root.color + + RowLayout { + anchors { + fill: parent + leftMargin: Theme.defaultSpacing * 2 + rightMargin: Theme.defaultSpacing * 2 + } + spacing: Theme.defaultSpacing * 2 + Item { + id: blank + implicitWidth: root.iconItemWidth + } + Text { + id: identifierHdr + text: qsTr("Identifier") + color: Theme.textSecondaryColor + font.pixelSize: Theme.mediumFontSize + font.weight: Theme.fontDefaultWeight + Layout.fillWidth: true + } + Text { + id: rssiHdr + text: qsTr("RSSI") + color: Theme.textSecondaryColor + font.pixelSize: Theme.mediumFontSize + font.weight: Theme.fontDefaultWeight + Layout.preferredWidth: hiddenRssiElement.width + } + Text { + id: statusHdr + text: qsTr("Status") + color: Theme.textSecondaryColor + font.pixelSize: Theme.mediumFontSize + font.weight: Theme.fontDefaultWeight + Layout.preferredWidth: hiddenStatusElement.width + } + } + Rectangle { + anchors.bottom: parent.bottom + height: 1 + width: parent.width + color: Theme.tableSeparatorColor + } + } + + component ListItem : Rectangle { + id: listItem + + required property int index + required property var modelData + + color: "transparent" + + RowLayout { + anchors { + fill: parent + leftMargin: Theme.defaultSpacing * 2 + rightMargin: Theme.defaultSpacing * 2 + } + spacing: Theme.defaultSpacing * 2 + Rectangle { + implicitWidth: root.iconItemWidth + color: "transparent" + Image { + anchors.centerIn: parent + source: listItem.index % 2 === 0 ? "icons/satellite1.png" + : "icons/satellite2.png" + } + } + Text { + text: listItem.modelData.system + "-" + listItem.modelData.id + color: Theme.textMainColor + font.pixelSize: Theme.mediumFontSize + font.weight: Theme.fontDefaultWeight + Layout.fillWidth: true + } + RssiElement { + rssi: listItem.modelData.rssi + Layout.preferredWidth: hiddenRssiElement.width + } + StatusElement { + inUse: listItem.modelData.inUse + Layout.preferredWidth: hiddenStatusElement.width + } + } + Rectangle { + anchors.bottom: parent.bottom + height: 1 + width: parent.width + color: Theme.tableSeparatorColor + } + } + + component SearchInput : QC.TextField { + id: searchInput + palette { + placeholderText: Theme.grayColor + text: Theme.textMainColor + } + rightPadding: padding + searchIcon.width + searchIcon.anchors.rightMargin + font.pixelSize: Theme.smallFontSize + font.weight: Theme.fontLightWeight + background: Rectangle { + border { + width: 1 + color:Theme.searchBorderColor + } + color: "transparent" + radius: 3 + Image { + id: searchIcon + anchors { + right: parent.right + rightMargin: Theme.defaultSpacing + verticalCenter: parent.verticalCenter + } + source: "icons/search.svg" + sourceSize.height: searchInput.implicitHeight - searchInput.padding * 2 + fillMode: Image.PreserveAspectFit + } + } + } + + component IconButton : QC.ToolButton { + id: iconButton + property bool selected: false + icon.width: 0 + icon.height: 0 + icon.color: selected ? Theme.greenColor : Theme.grayColor + background: Rectangle { + border { + width: 1 + color: iconButton.selected ? Theme.greenColor : Theme.searchBorderColor + } + color: "transparent" + radius: 3 + } + } + + component MenuPopup : QC.Popup { + modal: true + background: Rectangle { + radius: 8 + color: Theme.backgroundColor + border { + width: 1 + color: Theme.separatorColor + } + } + } + + component CheckElement : QC.ItemDelegate { + id: checkElement + + readonly property int iconSize: font.pixelSize * 2 + + checked: true + padding: 0 + + display: QC.AbstractButton.TextBesideIcon + + font.pixelSize: Theme.smallFontSize + font.weight: Theme.fontLightWeight + palette.text: checked ? Theme.greenColor : Theme.textSecondaryColor + + icon.source: checked ? "icons/checkbox.svg" : "icons/checkbox_blank.svg" + icon.height: iconSize + icon.width: iconSize + icon.color: checked ? Theme.greenColor : Theme.textSecondaryColor + + LayoutMirroring.enabled: true + LayoutMirroring.childrenInherit: true + + background: Rectangle { + anchors.fill: parent + color: "transparent" + } + + onClicked: { + checked = !checked + } + } + + Rectangle { + id: sortAndFilter + width: root.width + height: searchField.implicitHeight + sortAndFilterLayout.anchors.margins * 2 + color: Theme.backgroundColor + RowLayout { + id: sortAndFilterLayout + anchors { + fill: parent + margins: Theme.defaultSpacing * 2 + } + spacing: Theme.defaultSpacing * 2 + SearchInput { + id: searchField + placeholderText: qsTr("Search") + Layout.fillWidth: true + onTextChanged: root.sortFilterModel.updateFilterString(text); + } + IconButton { + selected: sortMenu.visible + icon.source: "icons/sort.svg" + rotation: 90 + Layout.preferredHeight: searchField.implicitHeight + Layout.preferredWidth: Layout.preferredHeight + onClicked: sortMenu.open() + } + IconButton { + selected: filterMenu.visible + icon.source: "icons/filter.svg" + Layout.preferredHeight: searchField.implicitHeight + Layout.preferredWidth: Layout.preferredHeight + onClicked: filterMenu.open() + } + } + } + + Rectangle { + id: separator + anchors.top: sortAndFilter.bottom + color: Theme.separatorColor + height: 1 + width: root.width + } + + ListView { + id: satellitesView + anchors { + top: separator.bottom + bottom: root.bottom + } + width: root.width + clip: true + model: root.sortFilterModel + header: ListHeader { + height: 30 + width: root.width + z: 2 // to show on top of the items + } + headerPositioning: ListView.OverlayHeader + delegate: ListItem { + height: 50 + width: root.width + } + } + + MenuPopup { + id: sortMenu + x: root.width - sortMenu.width - Theme.defaultSpacing + y: satellitesView.y - Theme.defaultSpacing + ColumnLayout { + anchors.fill: parent + spacing: Theme.defaultSpacing + CheckElement { + checked: false + text: qsTr("By Identifier") + Layout.alignment: Qt.AlignRight + onCheckedChanged: { + root.sortFilterModel.updateSortRoles(Roles.SystemRole, checked) + } + } + CheckElement { + checked: false + text: qsTr("By Status") + Layout.alignment: Qt.AlignRight + onCheckedChanged: { + root.sortFilterModel.updateSortRoles(Roles.InUseRole, checked) + } + } + } + } + + MenuPopup { + id: filterMenu + x: root.width - filterMenu.width - Theme.defaultSpacing + y: satellitesView.y - Theme.defaultSpacing + ColumnLayout { + id: filterLayout + anchors.fill: parent + spacing: Theme.defaultSpacing + Text { + text: qsTr("Satellite Identifier") + color: Theme.textMainColor + font.pixelSize: Theme.mediumFontSize + font.weight: Theme.fontDefaultWeight + Layout.alignment: Qt.AlignRight + } + Repeater { + model: root.satelliteSystemModel + delegate: CheckElement { + required property var modelData + text: modelData.name + Layout.alignment: Qt.AlignRight + onCheckedChanged: { + root.sortFilterModel.updateSelectedSystems(modelData.id, checked) + } + } + } + Text { + text: qsTr("Satellite Status") + color: Theme.textMainColor + font.pixelSize: Theme.mediumFontSize + font.weight: Theme.fontDefaultWeight + Layout.alignment: Qt.AlignRight + } + CheckElement { + text: qsTr("In View") + Layout.alignment: Qt.AlignRight + onCheckedChanged: root.sortFilterModel.updateShowInView(checked) + } + CheckElement { + text: qsTr("In Use") + Layout.alignment: Qt.AlignRight + onCheckedChanged: root.sortFilterModel.updateShowInUse(checked) + } + } + } } diff --git a/examples/positioning/satelliteinfo/Theme.qml b/examples/positioning/satelliteinfo/Theme.qml index fbc053ae..f08abb84 100644 --- a/examples/positioning/satelliteinfo/Theme.qml +++ b/examples/positioning/satelliteinfo/Theme.qml @@ -65,6 +65,18 @@ QtObject { root.grayColor.g, root.grayColor.b, 0.3) + readonly property color tableSeparatorColor: root.darkMode + ? Qt.rgba(root.lightGrayColor.r, + root.lightGrayColor.g, + root.lightGrayColor.b, + 0.2) + : Qt.rgba(root.darkGrayColor.r, + root.darkGrayColor.g, + root.darkGrayColor.b, + 0.2) + + readonly property color searchBorderColor: root.darkMode ? root.lightGrayColor + : root.grayColor // font readonly property int largeFontSize: 18 diff --git a/examples/positioning/satelliteinfo/icons/checkbox.svg b/examples/positioning/satelliteinfo/icons/checkbox.svg new file mode 100644 index 00000000..a557664e --- /dev/null +++ b/examples/positioning/satelliteinfo/icons/checkbox.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="m419-407-98-98q-9-9-21.5-8.5T278-504q-9 9-9 21.5t9 21.5l120 119q9 9 21 9t21-9l247-247q9-9 9-21.5t-9-21.5q-9-9-21.5-9t-21.5 9L419-407ZM180-120q-24 0-42-18t-18-42v-600q0-24 18-42t42-18h600q24 0 42 18t18 42v600q0 24-18 42t-42 18H180Zm0-60h600v-600H180v600Zm0-600v600-600Z"/></svg> diff --git a/examples/positioning/satelliteinfo/icons/checkbox_blank.svg b/examples/positioning/satelliteinfo/icons/checkbox_blank.svg new file mode 100644 index 00000000..71826efc --- /dev/null +++ b/examples/positioning/satelliteinfo/icons/checkbox_blank.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M180-120q-24 0-42-18t-18-42v-600q0-24 18-42t42-18h600q24 0 42 18t18 42v600q0 24-18 42t-42 18H180Zm0-60h600v-600H180v600Z"/></svg>
\ No newline at end of file diff --git a/examples/positioning/satelliteinfo/icons/filter.svg b/examples/positioning/satelliteinfo/icons/filter.svg new file mode 100644 index 00000000..8a132d55 --- /dev/null +++ b/examples/positioning/satelliteinfo/icons/filter.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M440-160q-17 0-28.5-11.5T400-200v-240L161-745q-14-17-4-36t31-19h584q21 0 31 19t-4 36L560-440v240q0 17-11.5 28.5T520-160h-80Zm40-276 240-304H240l240 304Zm0 0Z"/></svg>
\ No newline at end of file diff --git a/examples/positioning/satelliteinfo/icons/qt_attribution.json b/examples/positioning/satelliteinfo/icons/qt_attribution.json index 4397e483..660ad237 100644 --- a/examples/positioning/satelliteinfo/icons/qt_attribution.json +++ b/examples/positioning/satelliteinfo/icons/qt_attribution.json @@ -10,6 +10,6 @@ "License": "Apache License Version 2.0", "LicenseId": "Apache-2.0", "Copyright": "© Google", - "Files": "darkmode.svg help.svg lightmode.svg rssiview.svg settings.svg skyview.svg tableview.svg" + "Files": "checkbox.svg checkbox_blank.svg darkmode.svg filter.svg help.svg lightmode.svg rssiview.svg search.svg settings.svg skyview.svg sort.svg tableview.svg" } ] diff --git a/examples/positioning/satelliteinfo/icons/satellite1.png b/examples/positioning/satelliteinfo/icons/satellite1.png Binary files differnew file mode 100644 index 00000000..e653e663 --- /dev/null +++ b/examples/positioning/satelliteinfo/icons/satellite1.png diff --git a/examples/positioning/satelliteinfo/icons/satellite2.png b/examples/positioning/satelliteinfo/icons/satellite2.png Binary files differnew file mode 100644 index 00000000..f09ab46e --- /dev/null +++ b/examples/positioning/satelliteinfo/icons/satellite2.png diff --git a/examples/positioning/satelliteinfo/icons/search.svg b/examples/positioning/satelliteinfo/icons/search.svg new file mode 100644 index 00000000..68447d73 --- /dev/null +++ b/examples/positioning/satelliteinfo/icons/search.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M378-329q-108.162 0-183.081-75Q120-479 120-585t75-181q75-75 181.5-75t181 75Q632-691 632-584.85 632-542 618-502q-14 40-42 75l242 240q9 8.556 9 21.778T818-143q-9 9-22.222 9-13.222 0-21.778-9L533-384q-30 26-69.959 40.5T378-329Zm-1-60q81.25 0 138.125-57.5T572-585q0-81-56.875-138.5T377-781q-82.083 0-139.542 57.5Q180-666 180-585t57.458 138.5Q294.917-389 377-389Z" fill="#969696"/></svg>
\ No newline at end of file diff --git a/examples/positioning/satelliteinfo/icons/sort.svg b/examples/positioning/satelliteinfo/icons/sort.svg new file mode 100644 index 00000000..2617b3ef --- /dev/null +++ b/examples/positioning/satelliteinfo/icons/sort.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="m194-281 98 98q9 9 9 21t-9 21q-9 9-21 9t-21-9L101-290q-5-5-7-10t-2-11q0-6 2-11t7-10l150-150q9-9 21-9t21 9q9 9 9 21t-9 21l-99 99h616q13 0 21.5 8.5T840-311q0 13-8.5 21.5T810-281H194Zm572-337H150q-13 0-21.5-8.5T120-648q0-13 8.5-21.5T150-678h616l-99-99q-9-9-9-21t9-21q9-9 21-9t21 9l150 150q5 5 7 10t2 11q0 6-2 11t-7 10L710-478q-9 9-21 9t-21-9q-9-9-9-21t9-21l98-98Z"/></svg>
\ No newline at end of file diff --git a/examples/positioning/satelliteinfo/main.cpp b/examples/positioning/satelliteinfo/main.cpp index 06ec425e..2fe7603c 100644 --- a/examples/positioning/satelliteinfo/main.cpp +++ b/examples/positioning/satelliteinfo/main.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "satellitemodel.h" +#include "sortfiltermodel.h" #include <QtGui/qfontdatabase.h> #include <QtGui/qguiapplication.h> @@ -34,10 +35,14 @@ int main(int argc, char *argv[]) } SatelliteModel satelliteModel; + SortFilterModel sortFilterModel; + sortFilterModel.setSourceModel(&satelliteModel); + sortFilterModel.sort(0); QQmlApplicationEngine engine; engine.setInitialProperties({ - {u"satellitesModel"_s, QVariant::fromValue(&satelliteModel)} + {u"satellitesModel"_s, QVariant::fromValue(&satelliteModel)}, + {u"sortFilterModel"_s, QVariant::fromValue(&sortFilterModel)} }); QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(1); }, Qt::QueuedConnection); diff --git a/examples/positioning/satelliteinfo/roles.h b/examples/positioning/satelliteinfo/roles.h new file mode 100644 index 00000000..639ed7cc --- /dev/null +++ b/examples/positioning/satelliteinfo/roles.h @@ -0,0 +1,26 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef ROLES_H +#define ROLES_H + +#include <QObject> +#include <QtQml/qqmlregistration.h> + +namespace Roles { + Q_NAMESPACE + enum SatelliteModelRoles { + IdRole = Qt::UserRole + 1, + RssiRole, + AzimuthRole, + ElevationRole, + SystemRole, + SystemIdRole, + InUseRole, + VisibleNameRole + }; + Q_ENUM_NS(SatelliteModelRoles) + QML_NAMED_ELEMENT(Roles) +} + +#endif // ROLES_H diff --git a/examples/positioning/satelliteinfo/satelliteinfo.pro b/examples/positioning/satelliteinfo/satelliteinfo.pro index 8d79084a..fae03277 100644 --- a/examples/positioning/satelliteinfo/satelliteinfo.pro +++ b/examples/positioning/satelliteinfo/satelliteinfo.pro @@ -11,10 +11,13 @@ QML_IMPORT_NAME = SatelliteInformation QML_IMPORT_MAJOR_VERSION = 1 SOURCES += main.cpp \ - satellitemodel.cpp + satellitemodel.cpp \ + sortfiltermodel.cpp HEADERS += \ - satellitemodel.h + roles.h \ + satellitemodel.h \ + sortfiltermodel.h qml_resources.files = \ qmldir \ @@ -39,7 +42,10 @@ qml_resources.prefix = /qt/qml/SatelliteInformation RESOURCES += qml_resources icon_resources.files = \ + icons/checkbox.svg \ + icons/checkbox_blank.svg \ icons/darkmode.svg \ + icons/filter.svg \ icons/help.svg \ icons/lightmode.svg \ icons/place.svg \ @@ -47,8 +53,12 @@ icon_resources.files = \ icons/qtlogo_white.png \ icons/rssiview.svg \ icons/satellite_small.png \ + icons/satellite1.png \ + icons/satellite2.png \ + icons/search.svg \ icons/settings.svg \ icons/skyview.svg \ + icons/sort.svg \ icons/tableview.svg icon_resources.prefix = /qt/qml/SatelliteInformation diff --git a/examples/positioning/satelliteinfo/satellitemodel.cpp b/examples/positioning/satelliteinfo/satellitemodel.cpp index d7052305..a9c2907d 100644 --- a/examples/positioning/satelliteinfo/satellitemodel.cpp +++ b/examples/positioning/satelliteinfo/satellitemodel.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause +#include "roles.h" #include "satellitemodel.h" using namespace Qt::StringLiterals; @@ -46,18 +47,23 @@ QVariant SatelliteModel::data(const QModelIndex &index, int role) const const QGeoSatelliteInfo &info = m_satellites.at(index.row()); switch (role) { - case IdRole: + case Roles::IdRole: return info.satelliteIdentifier(); - case RssiRole: + case Roles::RssiRole: return info.signalStrength(); - case AzimuthRole: + case Roles::AzimuthRole: return info.attribute(QGeoSatelliteInfo::Azimuth); - case ElevationRole: + case Roles::ElevationRole: return info.attribute(QGeoSatelliteInfo::Elevation); - case SystemRole: + case Roles::SystemRole: return systemString(info.satelliteSystem()); - case InUseRole: + case Roles::SystemIdRole: + return info.satelliteSystem(); + case Roles::InUseRole: return m_inUseIds.contains(info.satelliteIdentifier()); + case Roles::VisibleNameRole: + return u"%1-%2"_s.arg(systemString(info.satelliteSystem()), + QString::number(info.satelliteIdentifier())); } return QVariant(); @@ -66,12 +72,14 @@ QVariant SatelliteModel::data(const QModelIndex &index, int role) const QHash<int, QByteArray> SatelliteModel::roleNames() const { return { - {IdRole, "id"}, - {RssiRole, "rssi"}, - {AzimuthRole, "azimuth"}, - {ElevationRole, "elevation"}, - {SystemRole, "system"}, - {InUseRole, "inUse"} + {Roles::IdRole, "id"}, + {Roles::RssiRole, "rssi"}, + {Roles::AzimuthRole, "azimuth"}, + {Roles::ElevationRole, "elevation"}, + {Roles::SystemRole, "system"}, + {Roles::SystemIdRole, "systemId"}, + {Roles::InUseRole, "inUse"}, + {Roles::VisibleNameRole, "name"} }; } @@ -118,7 +126,7 @@ void SatelliteModel::updateSatellitesInView(const QList<QGeoSatelliteInfo> &inVi } else { m_satellites[idx] = inViewCopy.at(idx); emit dataChanged(index(idx), index(idx), - {RssiRole, AzimuthRole, ElevationRole}); + {Roles::RssiRole, Roles::AzimuthRole, Roles::ElevationRole}); } } m_allIds = idsInUpdate; @@ -131,5 +139,5 @@ void SatelliteModel::updateSatellitesInUse(const QList<QGeoSatelliteInfo> &inUse m_inUseIds.clear(); for (const QGeoSatelliteInfo &info : inUse) m_inUseIds.insert(info.satelliteIdentifier()); - emit dataChanged(index(0), index(m_satellites.size() - 1), {InUseRole}); + emit dataChanged(index(0), index(m_satellites.size() - 1), {Roles::InUseRole}); } diff --git a/examples/positioning/satelliteinfo/satellitemodel.h b/examples/positioning/satelliteinfo/satellitemodel.h index aacc8c9a..f61a0603 100644 --- a/examples/positioning/satelliteinfo/satellitemodel.h +++ b/examples/positioning/satelliteinfo/satellitemodel.h @@ -16,15 +16,6 @@ class SatelliteModel : public QAbstractListModel public: explicit SatelliteModel(QObject *parent = nullptr); - enum Roles { - IdRole = Qt::UserRole + 1, - RssiRole, - AzimuthRole, - ElevationRole, - SystemRole, - InUseRole - }; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; QHash<int, QByteArray> roleNames() const override; diff --git a/examples/positioning/satelliteinfo/sortfiltermodel.cpp b/examples/positioning/satelliteinfo/sortfiltermodel.cpp new file mode 100644 index 00000000..41b6e834 --- /dev/null +++ b/examples/positioning/satelliteinfo/sortfiltermodel.cpp @@ -0,0 +1,92 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "roles.h" +#include "sortfiltermodel.h" + +using namespace Qt::StringLiterals; + +SortFilterModel::SortFilterModel(QObject *parent) + : QSortFilterProxyModel{parent} +{ +} + +void SortFilterModel::updateFilterString(const QString &str) +{ + m_filterString = str; + invalidateFilter(); +} + +void SortFilterModel::updateShowInView(bool show) +{ + m_showInView = show; + invalidateFilter(); +} + +void SortFilterModel::updateShowInUse(bool show) +{ + m_showInUse = show; + invalidateFilter(); +} + +void SortFilterModel::updateSelectedSystems(int id, bool show) +{ + if (show) + m_selectedSystems.insert(id); + else + m_selectedSystems.remove(id); + invalidateFilter(); +} + +void SortFilterModel::updateSortRoles(int role, bool use) +{ + if (use) + m_sortRoles.insert(role); + else + m_sortRoles.remove(role); + invalidate(); +} + +bool SortFilterModel::filterAcceptsRow(int row, const QModelIndex &parent) const +{ + Q_UNUSED(parent); + const auto idx = sourceModel()->index(row, 0); + bool result = true; + if (!m_filterString.isEmpty()) { + const QString name = idx.data(Roles::VisibleNameRole).toString(); + result = name.contains(m_filterString, Qt::CaseInsensitive); + } + if (result) { + const int systemId = idx.data(Roles::SystemIdRole).toInt(); + result = m_selectedSystems.contains(systemId); + } + if (result) { + const bool used = idx.data(Roles::InUseRole).toBool(); + if (used) + result = m_showInUse; + else + result = m_showInView; + } + return result; +} + +bool SortFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + // Sort 'By Identifier' can actually be called a sort by satellite system + // name + sort by id. + if (m_sortRoles.contains(Roles::InUseRole)) { + const auto leftVal = left.data(Roles::InUseRole).toBool(); + const auto rightVal = right.data(Roles::InUseRole).toBool(); + if (leftVal != rightVal) + return leftVal < rightVal; + } + if (m_sortRoles.contains(Roles::SystemRole)) { + const auto leftVal = left.data(Roles::SystemRole).toString(); + const auto rightVal = right.data(Roles::SystemRole).toString(); + if (leftVal != rightVal) + return leftVal < rightVal; + } + const auto leftId = left.data(Roles::IdRole).toInt(); + const auto rightId = right.data(Roles::IdRole).toInt(); + return leftId < rightId; +} diff --git a/examples/positioning/satelliteinfo/sortfiltermodel.h b/examples/positioning/satelliteinfo/sortfiltermodel.h new file mode 100644 index 00000000..cbeed7dc --- /dev/null +++ b/examples/positioning/satelliteinfo/sortfiltermodel.h @@ -0,0 +1,44 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef SORTFILTERMODEL_H +#define SORTFILTERMODEL_H + +#include <QGeoSatelliteInfo> +#include <QSet> +#include <QSortFilterProxyModel> +#include <QQmlEngine> + +class SortFilterModel : public QSortFilterProxyModel +{ + Q_OBJECT + QML_ELEMENT +public: + explicit SortFilterModel(QObject *parent = nullptr); + +public slots: + void updateFilterString(const QString &str); + void updateShowInView(bool show); + void updateShowInUse(bool show); + void updateSelectedSystems(int id, bool show); + void updateSortRoles(int role, bool use); + +protected: + bool filterAcceptsRow(int row, const QModelIndex &parent) const override; + bool lessThan(const QModelIndex &left, const QModelIndex &right) const override; + +private: + QString m_filterString; + QSet<int> m_selectedSystems = { + QGeoSatelliteInfo::GPS, + QGeoSatelliteInfo::GLONASS, + QGeoSatelliteInfo::GALILEO, + QGeoSatelliteInfo::BEIDOU, + QGeoSatelliteInfo::QZSS + }; + bool m_showInView = true; + bool m_showInUse = true; + QSet<int> m_sortRoles; +}; + +#endif // SORTFILTERMODEL_H |