summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2023-08-09 17:10:00 +0200
committerIvan Solovev <ivan.solovev@qt.io>2023-08-22 16:15:05 +0200
commita33b935a26474be182c502d5dbcf613b1c7dec2c (patch)
tree901d546796cfbff8dbe1889f94df73ec15c34a6d
parentc75bac86704748a3e5cd02335c4e9b9df5811729 (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)
-rw-r--r--examples/positioning/satelliteinfo/ApplicationScreen.qml5
-rw-r--r--examples/positioning/satelliteinfo/CMakeLists.txt9
-rw-r--r--examples/positioning/satelliteinfo/Main.qml2
-rw-r--r--examples/positioning/satelliteinfo/SatelliteView.qml418
-rw-r--r--examples/positioning/satelliteinfo/Theme.qml12
-rw-r--r--examples/positioning/satelliteinfo/icons/checkbox.svg1
-rw-r--r--examples/positioning/satelliteinfo/icons/checkbox_blank.svg1
-rw-r--r--examples/positioning/satelliteinfo/icons/filter.svg1
-rw-r--r--examples/positioning/satelliteinfo/icons/qt_attribution.json2
-rw-r--r--examples/positioning/satelliteinfo/icons/satellite1.pngbin0 -> 2006 bytes
-rw-r--r--examples/positioning/satelliteinfo/icons/satellite2.pngbin0 -> 1757 bytes
-rw-r--r--examples/positioning/satelliteinfo/icons/search.svg1
-rw-r--r--examples/positioning/satelliteinfo/icons/sort.svg1
-rw-r--r--examples/positioning/satelliteinfo/main.cpp7
-rw-r--r--examples/positioning/satelliteinfo/roles.h26
-rw-r--r--examples/positioning/satelliteinfo/satelliteinfo.pro14
-rw-r--r--examples/positioning/satelliteinfo/satellitemodel.cpp36
-rw-r--r--examples/positioning/satelliteinfo/satellitemodel.h9
-rw-r--r--examples/positioning/satelliteinfo/sortfiltermodel.cpp92
-rw-r--r--examples/positioning/satelliteinfo/sortfiltermodel.h44
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
new file mode 100644
index 00000000..e653e663
--- /dev/null
+++ b/examples/positioning/satelliteinfo/icons/satellite1.png
Binary files differ
diff --git a/examples/positioning/satelliteinfo/icons/satellite2.png b/examples/positioning/satelliteinfo/icons/satellite2.png
new file mode 100644
index 00000000..f09ab46e
--- /dev/null
+++ b/examples/positioning/satelliteinfo/icons/satellite2.png
Binary files differ
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