diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3cc657130..183b603c0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -675,6 +675,8 @@ list(APPEND TRANSLATION_SOURCES ${VENUS_QML_MODULE_SOURCES})
list(APPEND VenusQMLModule_CPP_SOURCES
src/aggregatedevicemodel.h
src/aggregatedevicemodel.cpp
+ src/visibleitemmodel.h
+ src/visibleitemmodel.cpp
src/basedevicemodel.h
src/basedevicemodel.cpp
src/theme.h
diff --git a/components/PageGensetModel.qml b/components/PageGensetModel.qml
index 5cfe56ed1..1cb4ff599 100644
--- a/components/PageGensetModel.qml
+++ b/components/PageGensetModel.qml
@@ -6,7 +6,7 @@
import QtQuick
import Victron.VenusOS
-ObjectModel {
+VisibleItemModel {
id: root
property string bindPrefix
@@ -226,7 +226,7 @@ ObjectModel {
Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListQuantity {
//% "Speed"
text: qsTrId("ac-in-genset_speed")
@@ -345,7 +345,7 @@ ObjectModel {
Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSpinBox {
//% "Charge voltage"
text: qsTrId("genset_charge_voltage")
diff --git a/components/listitems/ListChargeSchedule.qml b/components/listitems/ListChargeSchedule.qml
index 25f669da9..c27137fb9 100644
--- a/components/listitems/ListChargeSchedule.qml
+++ b/components/listitems/ListChargeSchedule.qml
@@ -108,7 +108,7 @@ ListNavigation {
id: scheduledOptionsPage
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: itemEnabled
diff --git a/pages/ControlCardsPage.qml b/pages/ControlCardsPage.qml
index d588e9509..b6bafcd2f 100644
--- a/pages/ControlCardsPage.qml
+++ b/pages/ControlCardsPage.qml
@@ -36,7 +36,7 @@ Page {
maximumFlickVelocity: Theme.geometry_flickable_maximumFlickVelocity
flickDeceleration: Theme.geometry_flickable_flickDeceleration
- model: ObjectModel {
+ model: VisibleItemModel {
Loader {
active: systemType.value === "ESS" || systemType.value === "Hub-4"
width: active ? root.cardWidth : -cardsView.spacing
diff --git a/pages/SettingsPage.qml b/pages/SettingsPage.qml
index 65be38cd7..447da9634 100644
--- a/pages/SettingsPage.qml
+++ b/pages/SettingsPage.qml
@@ -24,7 +24,7 @@ SwipeViewPage {
id: settingsListView
clip: true
- model: ObjectModel {
+ model: VisibleItemModel {
SettingsListNavigation {
text: CommonWords.devices
//% "All connected devices"
diff --git a/pages/evcs/EvChargerPage.qml b/pages/evcs/EvChargerPage.qml
index fbdaeafd8..171b11e95 100644
--- a/pages/evcs/EvChargerPage.qml
+++ b/pages/evcs/EvChargerPage.qml
@@ -16,7 +16,7 @@ Page {
title: evCharger.name
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListItemBackground {
height: phaseTable.y + phaseTable.height
diff --git a/pages/evcs/EvChargerSetupPage.qml b/pages/evcs/EvChargerSetupPage.qml
index 0a5a4f777..a26d249e7 100644
--- a/pages/evcs/EvChargerSetupPage.qml
+++ b/pages/evcs/EvChargerSetupPage.qml
@@ -13,7 +13,7 @@ Page {
required property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListEvChargerPositionRadioButtonGroup {
dataItem.uid: root.bindPrefix + "/Position"
}
diff --git a/pages/invertercharger/OverviewInverterChargerPage.qml b/pages/invertercharger/OverviewInverterChargerPage.qml
index 81b558053..84cc1df63 100644
--- a/pages/invertercharger/OverviewInverterChargerPage.qml
+++ b/pages/invertercharger/OverviewInverterChargerPage.qml
@@ -50,7 +50,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListItem {
id: modeListButton
diff --git a/pages/settings/GeneratorCondition.qml b/pages/settings/GeneratorCondition.qml
index 7f88cc731..3afa2ebeb 100644
--- a/pages/settings/GeneratorCondition.qml
+++ b/pages/settings/GeneratorCondition.qml
@@ -56,7 +56,7 @@ ListNavigation {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
text: root.enableDescription
diff --git a/pages/settings/PageCanbusStatus.qml b/pages/settings/PageCanbusStatus.qml
index b3b9351db..6dc0eb28a 100644
--- a/pages/settings/PageCanbusStatus.qml
+++ b/pages/settings/PageCanbusStatus.qml
@@ -93,7 +93,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListTextGroup {
id: stateGroup
diff --git a/pages/settings/PageDeviceInfo.qml b/pages/settings/PageDeviceInfo.qml
index 98d4267f0..799bbc2be 100644
--- a/pages/settings/PageDeviceInfo.qml
+++ b/pages/settings/PageDeviceInfo.qml
@@ -16,7 +16,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
//% "Connection"
text: qsTrId("settings_deviceinfo_connection")
diff --git a/pages/settings/PageGenerator.qml b/pages/settings/PageGenerator.qml
index 147c7070e..210b61a3b 100644
--- a/pages/settings/PageGenerator.qml
+++ b/pages/settings/PageGenerator.qml
@@ -45,7 +45,7 @@ Page {
}
}
- ObjectModel {
+ VisibleItemModel {
id: startStopModel
ListSwitch {
diff --git a/pages/settings/PageGeneratorAcLoad.qml b/pages/settings/PageGeneratorAcLoad.qml
index b113e227e..091b75759 100644
--- a/pages/settings/PageGeneratorAcLoad.qml
+++ b/pages/settings/PageGeneratorAcLoad.qml
@@ -30,7 +30,7 @@ Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: enableSwitch
diff --git a/pages/settings/PageGeneratorConditions.qml b/pages/settings/PageGeneratorConditions.qml
index ad42f3069..462401bd7 100644
--- a/pages/settings/PageGeneratorConditions.qml
+++ b/pages/settings/PageGeneratorConditions.qml
@@ -33,7 +33,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: monitorService
diff --git a/pages/settings/PageGeneratorRuntimeService.qml b/pages/settings/PageGeneratorRuntimeService.qml
index fe230f6a8..d40bc596b 100644
--- a/pages/settings/PageGeneratorRuntimeService.qml
+++ b/pages/settings/PageGeneratorRuntimeService.qml
@@ -41,7 +41,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
//% "Total run time"
diff --git a/pages/settings/PageGeneratorTestRun.qml b/pages/settings/PageGeneratorTestRun.qml
index 7ca1f4e94..a6fd0f7ea 100644
--- a/pages/settings/PageGeneratorTestRun.qml
+++ b/pages/settings/PageGeneratorTestRun.qml
@@ -11,7 +11,7 @@ Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: enableSwitch
diff --git a/pages/settings/PageGps.qml b/pages/settings/PageGps.qml
index e3f35700c..edd348116 100644
--- a/pages/settings/PageGps.qml
+++ b/pages/settings/PageGps.qml
@@ -35,7 +35,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
text: CommonWords.status
secondaryText: {
diff --git a/pages/settings/PageHub4Debug.qml b/pages/settings/PageHub4Debug.qml
index ecef95e1b..7cfccfc36 100644
--- a/pages/settings/PageHub4Debug.qml
+++ b/pages/settings/PageHub4Debug.qml
@@ -20,7 +20,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSpinBox {
id: gridSetpoint
diff --git a/pages/settings/PageRelayGenerator.qml b/pages/settings/PageRelayGenerator.qml
index ad4b351c6..0f082f2c7 100644
--- a/pages/settings/PageRelayGenerator.qml
+++ b/pages/settings/PageRelayGenerator.qml
@@ -16,7 +16,7 @@ PageGenerator {
model: !relayFunction.isValid || relayFunction.value === 1 ? startStopModel : disabledModel
- ObjectModel {
+ VisibleItemModel {
id: disabledModel
ListItem {
diff --git a/pages/settings/PageSettingsAcSystem.qml b/pages/settings/PageSettingsAcSystem.qml
index cb2f87ce5..03d6f2b3c 100644
--- a/pages/settings/PageSettingsAcSystem.qml
+++ b/pages/settings/PageSettingsAcSystem.qml
@@ -34,7 +34,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: acInput1
diff --git a/pages/settings/PageSettingsAccessAndSecurity.qml b/pages/settings/PageSettingsAccessAndSecurity.qml
index 3bb12a3d5..2056305ab 100644
--- a/pages/settings/PageSettingsAccessAndSecurity.qml
+++ b/pages/settings/PageSettingsAccessAndSecurity.qml
@@ -67,7 +67,7 @@ Page {
id: settingsListView
boundsBehavior: Flickable.DragOverBounds
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
//% "Access level"
text: qsTrId("settings_access_level")
diff --git a/pages/settings/PageSettingsAlarmsAndFeedback.qml b/pages/settings/PageSettingsAlarmsAndFeedback.qml
index 5488d2107..372f8e920 100644
--- a/pages/settings/PageSettingsAlarmsAndFeedback.qml
+++ b/pages/settings/PageSettingsAlarmsAndFeedback.qml
@@ -12,7 +12,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
//% "Audible alarm"
text: qsTrId("settings_audible_alarm")
diff --git a/pages/settings/PageSettingsBatteries.qml b/pages/settings/PageSettingsBatteries.qml
index d39ab4443..10fead837 100644
--- a/pages/settings/PageSettingsBatteries.qml
+++ b/pages/settings/PageSettingsBatteries.qml
@@ -14,7 +14,7 @@ Page {
title: CommonWords.batteries
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: batteryMonitorRadioButtons
diff --git a/pages/settings/PageSettingsBatteryMeasurements.qml b/pages/settings/PageSettingsBatteryMeasurements.qml
index 481d8279f..6db33c4d5 100644
--- a/pages/settings/PageSettingsBatteryMeasurements.qml
+++ b/pages/settings/PageSettingsBatteryMeasurements.qml
@@ -81,7 +81,7 @@ Page {
Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
text: root._visibleText
//% "Active battery monitor"
diff --git a/pages/settings/PageSettingsBleSensors.qml b/pages/settings/PageSettingsBleSensors.qml
index 6d1df8282..85107b1f9 100644
--- a/pages/settings/PageSettingsBleSensors.qml
+++ b/pages/settings/PageSettingsBleSensors.qml
@@ -33,7 +33,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: enable
text: CommonWords.enable
diff --git a/pages/settings/PageSettingsBluetooth.qml b/pages/settings/PageSettingsBluetooth.qml
index 069f2fbac..38e07cefb 100644
--- a/pages/settings/PageSettingsBluetooth.qml
+++ b/pages/settings/PageSettingsBluetooth.qml
@@ -11,7 +11,7 @@ Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: bluetoothEnabled
diff --git a/pages/settings/PageSettingsCGwacs.qml b/pages/settings/PageSettingsCGwacs.qml
index 25bf2521d..c92d596e0 100644
--- a/pages/settings/PageSettingsCGwacs.qml
+++ b/pages/settings/PageSettingsCGwacs.qml
@@ -29,7 +29,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
text: CommonWords.ac_input_role
optionModel: Global.acInputs.roles.map(function(role) {
diff --git a/pages/settings/PageSettingsCanbus.qml b/pages/settings/PageSettingsCanbus.qml
index d6353c6ee..bbad89ea0 100644
--- a/pages/settings/PageSettingsCanbus.qml
+++ b/pages/settings/PageSettingsCanbus.qml
@@ -35,7 +35,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
//% "CAN-bus profile"
text: qsTrId("settings_canbus_profile")
diff --git a/pages/settings/PageSettingsConnectivity.qml b/pages/settings/PageSettingsConnectivity.qml
index bd1e062a3..e7eb90df7 100644
--- a/pages/settings/PageSettingsConnectivity.qml
+++ b/pages/settings/PageSettingsConnectivity.qml
@@ -12,7 +12,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListNavigation {
//% "Ethernet"
text: qsTrId("pagesettingsconnectivity_ethernet")
diff --git a/pages/settings/PageSettingsDisplayAndAppearance.qml b/pages/settings/PageSettingsDisplayAndAppearance.qml
index e1f4b3591..b92c05f9f 100644
--- a/pages/settings/PageSettingsDisplayAndAppearance.qml
+++ b/pages/settings/PageSettingsDisplayAndAppearance.qml
@@ -12,7 +12,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: autoBrightness
diff --git a/pages/settings/PageSettingsDisplayMinMax.qml b/pages/settings/PageSettingsDisplayMinMax.qml
index 4653d9719..753b15d30 100644
--- a/pages/settings/PageSettingsDisplayMinMax.qml
+++ b/pages/settings/PageSettingsDisplayMinMax.qml
@@ -10,7 +10,7 @@ Page {
id: root
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
//: Whether to adjust the min/max values in the range dynamically, based on the lowest and highest values observed on the system.
//% "Auto-ranging"
diff --git a/pages/settings/PageSettingsDisplayStartPage.qml b/pages/settings/PageSettingsDisplayStartPage.qml
index a54365442..4aedb9bb6 100644
--- a/pages/settings/PageSettingsDisplayStartPage.qml
+++ b/pages/settings/PageSettingsDisplayStartPage.qml
@@ -30,7 +30,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListNavigation {
id: startPageNavigation
//% "Start page"
@@ -99,7 +99,7 @@ Page {
Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: startPageMode
text: CommonWords.auto
diff --git a/pages/settings/PageSettingsDisplayUnits.qml b/pages/settings/PageSettingsDisplayUnits.qml
index d5e7fa17f..806ce4a9e 100644
--- a/pages/settings/PageSettingsDisplayUnits.qml
+++ b/pages/settings/PageSettingsDisplayUnits.qml
@@ -12,7 +12,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
text: CommonWords.temperature
optionModel: [
diff --git a/pages/settings/PageSettingsDvcc.qml b/pages/settings/PageSettingsDvcc.qml
index f0e2dc64e..e1d739685 100644
--- a/pages/settings/PageSettingsDvcc.qml
+++ b/pages/settings/PageSettingsDvcc.qml
@@ -13,7 +13,7 @@ Page {
GradientListView {
id: dvccSettings
- model: ObjectModel {
+ model: VisibleItemModel {
PrimaryListLabel {
//% "CAUTION: Read the manual before adjusting."
text: qsTrId("settings_dvcc_instructions")
diff --git a/pages/settings/PageSettingsDynamicEss.qml b/pages/settings/PageSettingsDynamicEss.qml
index 49e58e0ee..d64b0468e 100644
--- a/pages/settings/PageSettingsDynamicEss.qml
+++ b/pages/settings/PageSettingsDynamicEss.qml
@@ -10,7 +10,7 @@ Page {
id: root
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: dEssMode
text: CommonWords.mode
diff --git a/pages/settings/PageSettingsEthernet.qml b/pages/settings/PageSettingsEthernet.qml
index 97d31f100..03f382f81 100644
--- a/pages/settings/PageSettingsEthernet.qml
+++ b/pages/settings/PageSettingsEthernet.qml
@@ -14,7 +14,7 @@ Page {
model: networkServices.ready ? connectedModel : disconnectedModel
}
- ObjectModel {
+ VisibleItemModel {
id: disconnectedModel
ListText {
@@ -27,7 +27,7 @@ Page {
}
}
- ObjectModel {
+ VisibleItemModel {
id: connectedModel
ListText {
diff --git a/pages/settings/PageSettingsFirmware.qml b/pages/settings/PageSettingsFirmware.qml
index aa1c57068..867566f3c 100644
--- a/pages/settings/PageSettingsFirmware.qml
+++ b/pages/settings/PageSettingsFirmware.qml
@@ -12,7 +12,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
id: remotePort
diff --git a/pages/settings/PageSettingsFirmwareOffline.qml b/pages/settings/PageSettingsFirmwareOffline.qml
index c45c61430..75f8276d0 100644
--- a/pages/settings/PageSettingsFirmwareOffline.qml
+++ b/pages/settings/PageSettingsFirmwareOffline.qml
@@ -12,7 +12,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListFirmwareCheckButton {
//% "Check for updates on SD/USB"
diff --git a/pages/settings/PageSettingsFirmwareOnline.qml b/pages/settings/PageSettingsFirmwareOnline.qml
index 91585a64e..527020fda 100644
--- a/pages/settings/PageSettingsFirmwareOnline.qml
+++ b/pages/settings/PageSettingsFirmwareOnline.qml
@@ -12,7 +12,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
//% "Auto update"
diff --git a/pages/settings/PageSettingsFronius.qml b/pages/settings/PageSettingsFronius.qml
index d5968e6fc..909c62e64 100644
--- a/pages/settings/PageSettingsFronius.qml
+++ b/pages/settings/PageSettingsFronius.qml
@@ -24,7 +24,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListNavigation {
//% "Inverters"
text: qsTrId("page_settings_fronius_inverters")
diff --git a/pages/settings/PageSettingsFroniusInverter.qml b/pages/settings/PageSettingsFroniusInverter.qml
index 5d41ddd1d..7174056ac 100644
--- a/pages/settings/PageSettingsFroniusInverter.qml
+++ b/pages/settings/PageSettingsFroniusInverter.qml
@@ -25,7 +25,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListPvInverterPositionRadioButtonGroup {
dataItem.uid: bindPrefix + "/Position"
}
diff --git a/pages/settings/PageSettingsGeneral.qml b/pages/settings/PageSettingsGeneral.qml
index af18b73cb..8fafc52af 100644
--- a/pages/settings/PageSettingsGeneral.qml
+++ b/pages/settings/PageSettingsGeneral.qml
@@ -13,7 +13,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
SettingsListHeader {
//% "System"
text: qsTrId("pagesettingsgeneral_system")
diff --git a/pages/settings/PageSettingsGenerator.qml b/pages/settings/PageSettingsGenerator.qml
index 4bcdc7119..7dbd3b8c4 100644
--- a/pages/settings/PageSettingsGenerator.qml
+++ b/pages/settings/PageSettingsGenerator.qml
@@ -22,7 +22,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListNavigation {
//% "Conditions"
@@ -50,7 +50,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListSpinBox {
//% "Warm-up time"
text: qsTrId("page_settings_generator_warm_up_time")
diff --git a/pages/settings/PageSettingsGpsList.qml b/pages/settings/PageSettingsGpsList.qml
index 1b89a95de..2b3e5a6ad 100644
--- a/pages/settings/PageSettingsGpsList.qml
+++ b/pages/settings/PageSettingsGpsList.qml
@@ -74,7 +74,7 @@ Page {
Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
//: Format of reported GPS data
//% "Format"
diff --git a/pages/settings/PageSettingsGsm.qml b/pages/settings/PageSettingsGsm.qml
index 594f42dff..d1c2b2aa0 100644
--- a/pages/settings/PageSettingsGsm.qml
+++ b/pages/settings/PageSettingsGsm.qml
@@ -32,7 +32,7 @@ Page {
model: simStatus.isValid ? modemConnected : notConnected
- ObjectModel {
+ VisibleItemModel {
id: notConnected
ListItem {
@@ -41,7 +41,7 @@ Page {
}
}
- ObjectModel {
+ VisibleItemModel {
id: modemConnected
ListText {
@@ -160,7 +160,7 @@ Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: useDefaultApn
diff --git a/pages/settings/PageSettingsHub4.qml b/pages/settings/PageSettingsHub4.qml
index da61be4a0..45d14c358 100644
--- a/pages/settings/PageSettingsHub4.qml
+++ b/pages/settings/PageSettingsHub4.qml
@@ -32,7 +32,7 @@ Page {
}
}
- ObjectModel {
+ VisibleItemModel {
id: essSettings
ListRadioButtonGroup {
diff --git a/pages/settings/PageSettingsHub4Feedin.qml b/pages/settings/PageSettingsHub4Feedin.qml
index 1a3696006..ccb45cd60 100644
--- a/pages/settings/PageSettingsHub4Feedin.qml
+++ b/pages/settings/PageSettingsHub4Feedin.qml
@@ -12,7 +12,7 @@ Page {
property int hub4Mode
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: acFeedin
diff --git a/pages/settings/PageSettingsHub4Peakshaving.qml b/pages/settings/PageSettingsHub4Peakshaving.qml
index bd08b2844..7bb04df59 100644
--- a/pages/settings/PageSettingsHub4Peakshaving.qml
+++ b/pages/settings/PageSettingsHub4Peakshaving.qml
@@ -42,7 +42,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
//% "Peak shaving"
text: qsTrId("settings_ess_peak_shaving")
diff --git a/pages/settings/PageSettingsIntegrations.qml b/pages/settings/PageSettingsIntegrations.qml
index cf057c9e2..1a5af2c6b 100644
--- a/pages/settings/PageSettingsIntegrations.qml
+++ b/pages/settings/PageSettingsIntegrations.qml
@@ -12,7 +12,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListNavigation {
text: CommonWords.add_device
icon.source: "qrc:/images/icon_plus_32.svg"
diff --git a/pages/settings/PageSettingsIo.qml b/pages/settings/PageSettingsIo.qml
index 9f1d7b666..1d413ad2a 100644
--- a/pages/settings/PageSettingsIo.qml
+++ b/pages/settings/PageSettingsIo.qml
@@ -25,7 +25,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListNavigation {
//% "Analog inputs"
diff --git a/pages/settings/PageSettingsLarge.qml b/pages/settings/PageSettingsLarge.qml
index 8a9d839ac..f4a8db8ff 100644
--- a/pages/settings/PageSettingsLarge.qml
+++ b/pages/settings/PageSettingsLarge.qml
@@ -10,7 +10,7 @@ Page {
id: root
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
}
}
diff --git a/pages/settings/PageSettingsLogger.qml b/pages/settings/PageSettingsLogger.qml
index 853372365..83c4b5c25 100644
--- a/pages/settings/PageSettingsLogger.qml
+++ b/pages/settings/PageSettingsLogger.qml
@@ -31,7 +31,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: vrmPortalMode
diff --git a/pages/settings/PageSettingsModbus.qml b/pages/settings/PageSettingsModbus.qml
index 975608aaa..320a97e78 100644
--- a/pages/settings/PageSettingsModbus.qml
+++ b/pages/settings/PageSettingsModbus.qml
@@ -26,7 +26,7 @@ Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
text: CommonWords.automatic_scanning
dataItem.uid: root.settings + "/AutoScan"
diff --git a/pages/settings/PageSettingsModbusAddDevice.qml b/pages/settings/PageSettingsModbusAddDevice.qml
index ac9b0fba3..4cb4df066 100644
--- a/pages/settings/PageSettingsModbusAddDevice.qml
+++ b/pages/settings/PageSettingsModbusAddDevice.qml
@@ -21,7 +21,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: protocol
diff --git a/pages/settings/PageSettingsModbusDevices.qml b/pages/settings/PageSettingsModbusDevices.qml
index c4080d93e..97b89be92 100644
--- a/pages/settings/PageSettingsModbusDevices.qml
+++ b/pages/settings/PageSettingsModbusDevices.qml
@@ -39,7 +39,7 @@ Page {
//% "No Modbus devices saved"
text: qsTrId("settings_modbus_no_devices_saved")
}
- model: ObjectModel {
+ model: VisibleItemModel {
Column {
width: parent ? parent.width : 0
diff --git a/pages/settings/PageSettingsModbusTcp.qml b/pages/settings/PageSettingsModbusTcp.qml
index 7ff5dc913..f60304049 100644
--- a/pages/settings/PageSettingsModbusTcp.qml
+++ b/pages/settings/PageSettingsModbusTcp.qml
@@ -22,7 +22,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: enableModbusTcp
diff --git a/pages/settings/PageSettingsNodeRed.qml b/pages/settings/PageSettingsNodeRed.qml
index 804668342..1f4415d08 100644
--- a/pages/settings/PageSettingsNodeRed.qml
+++ b/pages/settings/PageSettingsNodeRed.qml
@@ -10,7 +10,7 @@ Page {
id: root
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: nodered
diff --git a/pages/settings/PageSettingsRelay.qml b/pages/settings/PageSettingsRelay.qml
index d0d463d85..0b6cbca9a 100644
--- a/pages/settings/PageSettingsRelay.qml
+++ b/pages/settings/PageSettingsRelay.qml
@@ -10,7 +10,7 @@ Page {
id: root
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: relayFunction
diff --git a/pages/settings/PageSettingsRelayTempSensors.qml b/pages/settings/PageSettingsRelayTempSensors.qml
index 65ca1127b..1faf0e06a 100644
--- a/pages/settings/PageSettingsRelayTempSensors.qml
+++ b/pages/settings/PageSettingsRelayTempSensors.qml
@@ -87,7 +87,7 @@ Page {
? tempRelayModel
: disabledModel
- ObjectModel {
+ VisibleItemModel {
id: tempRelayModel
ListSwitch {
@@ -113,7 +113,7 @@ Page {
}
}
- ObjectModel {
+ VisibleItemModel {
id: disabledModel
PrimaryListLabel {
diff --git a/pages/settings/PageSettingsRootfsSelect.qml b/pages/settings/PageSettingsRootfsSelect.qml
index 51d8fe8f3..9bef6725e 100644
--- a/pages/settings/PageSettingsRootfsSelect.qml
+++ b/pages/settings/PageSettingsRootfsSelect.qml
@@ -45,7 +45,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
PrimaryListLabel {
//% "This option allows you to switch between the current and the previous firmware version. No internet or sdcard needed."
text: qsTrId("settings_firmware_version_switch_option")
diff --git a/pages/settings/PageSettingsRvcDevice.qml b/pages/settings/PageSettingsRvcDevice.qml
index 26fa8fe39..efb21ccb8 100644
--- a/pages/settings/PageSettingsRvcDevice.qml
+++ b/pages/settings/PageSettingsRvcDevice.qml
@@ -19,7 +19,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
text: CommonWords.model_name
diff --git a/pages/settings/PageSettingsRvcDeviceConfiguration.qml b/pages/settings/PageSettingsRvcDeviceConfiguration.qml
index c22ac670f..4f83ea314 100644
--- a/pages/settings/PageSettingsRvcDeviceConfiguration.qml
+++ b/pages/settings/PageSettingsRvcDeviceConfiguration.qml
@@ -27,7 +27,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSpinBox {
//% "Charger instance"
text: qsTrId("settings_rvc_charger_instance")
diff --git a/pages/settings/PageSettingsSystem.qml b/pages/settings/PageSettingsSystem.qml
index cfe8e4b00..537cbe206 100644
--- a/pages/settings/PageSettingsSystem.qml
+++ b/pages/settings/PageSettingsSystem.qml
@@ -10,7 +10,7 @@ Page {
id: root
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: systemNameRadioButtons
diff --git a/pages/settings/PageSettingsSystemStatus.qml b/pages/settings/PageSettingsSystemStatus.qml
index 82ab5eca1..bc9d8f4c6 100644
--- a/pages/settings/PageSettingsSystemStatus.qml
+++ b/pages/settings/PageSettingsSystemStatus.qml
@@ -10,7 +10,7 @@ Page {
id: root
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
//% "Synchronize VE.Bus SOC with battery"
text: qsTrId("settings_system_status_sync_vebus_soc_with_battery")
diff --git a/pages/settings/PageSettingsTailscale.qml b/pages/settings/PageSettingsTailscale.qml
index 2e69fc326..a02867eb8 100644
--- a/pages/settings/PageSettingsTailscale.qml
+++ b/pages/settings/PageSettingsTailscale.qml
@@ -161,7 +161,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: switchTailscaleEnabled
@@ -250,7 +250,7 @@ Page {
Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
//% "Access local ethernet network"
text: qsTrId("settings_tailscale_local_network_access_ethernet")
@@ -307,7 +307,7 @@ Page {
Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListTextField {
//% "Custom \"tailscale up\" arguments"
text: qsTrId("settings_tailscale_advanced_custom_tailscale_up_arguments")
diff --git a/pages/settings/PageSettingsTankPump.qml b/pages/settings/PageSettingsTankPump.qml
index 802b81d67..3f390c559 100644
--- a/pages/settings/PageSettingsTankPump.qml
+++ b/pages/settings/PageSettingsTankPump.qml
@@ -30,7 +30,7 @@ Page {
}
}
- ObjectModel {
+ VisibleItemModel {
id: disabledModel
PrimaryListLabel {
@@ -39,7 +39,7 @@ Page {
}
}
- ObjectModel {
+ VisibleItemModel {
id: startStopModel
ListText {
diff --git a/pages/settings/PageSettingsTcpIp.qml b/pages/settings/PageSettingsTcpIp.qml
index 2f6941b00..ae7e56f71 100644
--- a/pages/settings/PageSettingsTcpIp.qml
+++ b/pages/settings/PageSettingsTcpIp.qml
@@ -115,7 +115,7 @@ Page {
model: root.ready ? connectedModel : disconnectedModel
}
- ObjectModel {
+ VisibleItemModel {
id: disconnectedModel
ListText {
@@ -128,7 +128,7 @@ Page {
}
}
- ObjectModel {
+ VisibleItemModel {
id: connectedModel
ListText {
diff --git a/pages/settings/PageSettingsVecanDevice.qml b/pages/settings/PageSettingsVecanDevice.qml
index 0c83b7b0f..13485ba96 100644
--- a/pages/settings/PageSettingsVecanDevice.qml
+++ b/pages/settings/PageSettingsVecanDevice.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
text: CommonWords.model_name
diff --git a/pages/settings/PageSettingsVrm.qml b/pages/settings/PageSettingsVrm.qml
index 62e44915d..d6986030e 100644
--- a/pages/settings/PageSettingsVrm.qml
+++ b/pages/settings/PageSettingsVrm.qml
@@ -10,7 +10,7 @@ Page {
id: root
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListNavigation {
//% "VRM Portal mode"
text: qsTrId("settings_vrm_portal_mode")
diff --git a/pages/settings/PageTzInfo.qml b/pages/settings/PageTzInfo.qml
index 5d3e0ff8a..1db8b8ffb 100644
--- a/pages/settings/PageTzInfo.qml
+++ b/pages/settings/PageTzInfo.qml
@@ -80,7 +80,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
//% "Date/Time UTC"
diff --git a/pages/settings/debug/PageDebug.qml b/pages/settings/debug/PageDebug.qml
index 9a3497159..308d23a63 100644
--- a/pages/settings/debug/PageDebug.qml
+++ b/pages/settings/debug/PageDebug.qml
@@ -10,7 +10,7 @@ Page {
id: root
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
component SwitchItem : ListItem {
id: switchItem
diff --git a/pages/settings/debug/PagePowerDebug.qml b/pages/settings/debug/PagePowerDebug.qml
index 387b27957..33fc9ee1f 100644
--- a/pages/settings/debug/PagePowerDebug.qml
+++ b/pages/settings/debug/PagePowerDebug.qml
@@ -73,7 +73,7 @@ Page {
// in a D-Bus format.
model: BackendConnection.type === BackendConnection.MqttSource ? invalidModel : validModel
- ObjectModel {
+ VisibleItemModel {
id: invalidModel
PrimaryListLabel {
@@ -81,7 +81,7 @@ Page {
}
}
- ObjectModel {
+ VisibleItemModel {
id: validModel
ListTextGroup {
diff --git a/pages/settings/debug/PageSettingsDemo.qml b/pages/settings/debug/PageSettingsDemo.qml
index 1f6446800..c54f115c8 100644
--- a/pages/settings/debug/PageSettingsDemo.qml
+++ b/pages/settings/debug/PageSettingsDemo.qml
@@ -13,7 +13,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
PrimaryListLabel {
text: "This page demonstrates the components that can be used to build settings pages."
}
@@ -24,6 +24,11 @@ Page {
onClicked: Global.pageManager.pushPage(newPageComponent, { title: "Page name" })
}
+ ListNavigation {
+ text: "VisibleItemModel demo"
+ onClicked: Global.pageManager.pushPage(visibleItemDemoComponent, { title: text })
+ }
+
ListSwitch {
text: "Switch"
property bool value
@@ -340,7 +345,7 @@ Page {
Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListItem {
text: "New page item"
}
@@ -348,4 +353,59 @@ Page {
}
}
}
+
+ Component {
+ id: visibleItemDemoComponent
+
+ Page {
+ component VisibleModelSwitch : ListSwitch {
+ preferredVisible: true
+ checked: preferredVisible
+ onClicked: preferredVisible = !preferredVisible
+ }
+
+ GradientListView {
+ header: PrimaryListLabel {
+ text: "VisibleItemModel filters out any non-visible items from the model.\nFor example, click a switch below to set preferredVisible=false and remove it from the model."
+ }
+ footer: Column {
+ width: parent.width
+ PrimaryListLabel {
+ horizontalAlignment: Text.AlignHCenter
+ text: "%1 items in source model, %2 items in visible model"
+ .arg(visibleItemModel.sourceModel.length)
+ .arg(visibleItemModel.count)
+ }
+ ListItemButton {
+ anchors.horizontalCenter: parent.horizontalCenter
+ text: "Reset 'preferredVisible' values"
+ onClicked: {
+ toggle1.preferredVisible = true
+ toggle2.preferredVisible = true
+ toggle3.preferredVisible = true
+ }
+ }
+ }
+
+ model: VisibleItemModel {
+ id: visibleItemModel
+
+ VisibleModelSwitch {
+ id: toggle1
+ text: "Toggle A"
+ }
+
+ VisibleModelSwitch {
+ id: toggle2
+ text: "Toggle B"
+ }
+
+ VisibleModelSwitch {
+ id: toggle3
+ text: "Toggle C"
+ }
+ }
+ }
+ }
+ }
}
diff --git a/pages/settings/debug/PageSystemData.qml b/pages/settings/debug/PageSystemData.qml
index c5768be69..4ebdd3507 100644
--- a/pages/settings/debug/PageSystemData.qml
+++ b/pages/settings/debug/PageSystemData.qml
@@ -22,7 +22,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListTextGroup {
text: "PV On ACIn1"
textModel: [
diff --git a/pages/settings/devicelist/PageAcCharger.qml b/pages/settings/devicelist/PageAcCharger.qml
index 239d8839a..80c5ea100 100644
--- a/pages/settings/devicelist/PageAcCharger.qml
+++ b/pages/settings/devicelist/PageAcCharger.qml
@@ -19,7 +19,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
text: CommonWords.switch_mode
dataItem.uid: root.bindPrefix + "/Mode"
diff --git a/pages/settings/devicelist/PageDigitalInput.qml b/pages/settings/devicelist/PageDigitalInput.qml
index 069f14804..07b8195c4 100644
--- a/pages/settings/devicelist/PageDigitalInput.qml
+++ b/pages/settings/devicelist/PageDigitalInput.qml
@@ -20,7 +20,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
text: CommonWords.state
dataItem.uid: root.bindPrefix + "/State"
@@ -40,7 +40,7 @@ Page {
readonly property string settingsBindPrefix: Global.systemSettings.serviceUid + "/Settings/DigitalInput/" + (deviceInstance.value || 0)
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
id: alarmSwitch
//% "Enable alarm"
diff --git a/pages/settings/devicelist/PageMeteo.qml b/pages/settings/devicelist/PageMeteo.qml
index 8dd7a5c8b..987effcdd 100644
--- a/pages/settings/devicelist/PageMeteo.qml
+++ b/pages/settings/devicelist/PageMeteo.qml
@@ -30,7 +30,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListQuantity {
property var displayText: Units.getDisplayText(VenusOS.Units_WattsPerSquareMeter, dataItem.value, 1)
diff --git a/pages/settings/devicelist/PageMeteoSettings.qml b/pages/settings/devicelist/PageMeteoSettings.qml
index d98a16111..51adc8db4 100644
--- a/pages/settings/devicelist/PageMeteoSettings.qml
+++ b/pages/settings/devicelist/PageMeteoSettings.qml
@@ -18,7 +18,7 @@ Page {
]
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
//% "Wind speed sensor"
text: qsTrId("page_meteo_settings_wind_speed_sensor")
diff --git a/pages/settings/devicelist/PageMotorDrive.qml b/pages/settings/devicelist/PageMotorDrive.qml
index d62a6e65c..58b508d17 100644
--- a/pages/settings/devicelist/PageMotorDrive.qml
+++ b/pages/settings/devicelist/PageMotorDrive.qml
@@ -19,7 +19,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListQuantity {
//% "Motor RPM"
text: qsTrId("devicelist_motordrive_motorrpm")
diff --git a/pages/settings/devicelist/PageUnsupportedDevice.qml b/pages/settings/devicelist/PageUnsupportedDevice.qml
index 19160a900..3d94953a1 100644
--- a/pages/settings/devicelist/PageUnsupportedDevice.qml
+++ b/pages/settings/devicelist/PageUnsupportedDevice.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
//% "Unsupported device found"
text: qsTrId("devicelist_unsupporteddevices_found")
diff --git a/pages/settings/devicelist/ac-in/PageAcInModel.qml b/pages/settings/devicelist/ac-in/PageAcInModel.qml
index 04cb1be65..d879c7955 100644
--- a/pages/settings/devicelist/ac-in/PageAcInModel.qml
+++ b/pages/settings/devicelist/ac-in/PageAcInModel.qml
@@ -6,7 +6,7 @@
import QtQuick
import Victron.VenusOS
-ObjectModel {
+VisibleItemModel {
id: root
property string bindPrefix
diff --git a/pages/settings/devicelist/ac-in/PageAcInSetup.qml b/pages/settings/devicelist/ac-in/PageAcInSetup.qml
index 86f1d6bda..9e910f9ad 100644
--- a/pages/settings/devicelist/ac-in/PageAcInSetup.qml
+++ b/pages/settings/devicelist/ac-in/PageAcInSetup.qml
@@ -73,7 +73,7 @@ Page {
GradientListView {
id: settingsListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: role
diff --git a/pages/settings/devicelist/ac-in/PageSmappeeCTSetup.qml b/pages/settings/devicelist/ac-in/PageSmappeeCTSetup.qml
index 2afdf20fe..122a60341 100644
--- a/pages/settings/devicelist/ac-in/PageSmappeeCTSetup.qml
+++ b/pages/settings/devicelist/ac-in/PageSmappeeCTSetup.qml
@@ -29,7 +29,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: type
diff --git a/pages/settings/devicelist/battery/BatterySettingsAlarmModel.qml b/pages/settings/devicelist/battery/BatterySettingsAlarmModel.qml
index feb56c49b..21562a415 100644
--- a/pages/settings/devicelist/battery/BatterySettingsAlarmModel.qml
+++ b/pages/settings/devicelist/battery/BatterySettingsAlarmModel.qml
@@ -6,7 +6,7 @@
import QtQuick
import Victron.VenusOS
-ObjectModel {
+VisibleItemModel {
id: root
property string bindPrefix
diff --git a/pages/settings/devicelist/battery/BatterySettingsRelayModel.qml b/pages/settings/devicelist/battery/BatterySettingsRelayModel.qml
index 380adc9fb..70d490dfb 100644
--- a/pages/settings/devicelist/battery/BatterySettingsRelayModel.qml
+++ b/pages/settings/devicelist/battery/BatterySettingsRelayModel.qml
@@ -6,7 +6,7 @@
import QtQuick
import Victron.VenusOS
-ObjectModel {
+VisibleItemModel {
id: root
property string bindPrefix
diff --git a/pages/settings/devicelist/battery/Page48TlDiagnostics.qml b/pages/settings/devicelist/battery/Page48TlDiagnostics.qml
index ac2ebbe05..148eecf73 100644
--- a/pages/settings/devicelist/battery/Page48TlDiagnostics.qml
+++ b/pages/settings/devicelist/battery/Page48TlDiagnostics.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListItem {
//% "Status LEDs"
text: qsTrId("batterydiagnostics_status_leds")
diff --git a/pages/settings/devicelist/battery/PageBattery.qml b/pages/settings/devicelist/battery/PageBattery.qml
index fbd860e1f..eac1cd38b 100644
--- a/pages/settings/devicelist/battery/PageBattery.qml
+++ b/pages/settings/devicelist/battery/PageBattery.qml
@@ -21,7 +21,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
text: CommonWords.switch_mode
dataItem.uid: root.bindPrefix + "/Mode"
diff --git a/pages/settings/devicelist/battery/PageBatteryAlarms.qml b/pages/settings/devicelist/battery/PageBatteryAlarms.qml
index 23ec476fe..9d3759d8d 100644
--- a/pages/settings/devicelist/battery/PageBatteryAlarms.qml
+++ b/pages/settings/devicelist/battery/PageBatteryAlarms.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListAlarm {
text: CommonWords.low_battery_voltage
dataItem.uid: root.bindPrefix + "/Alarms/LowVoltage"
diff --git a/pages/settings/devicelist/battery/PageBatteryDetails.qml b/pages/settings/devicelist/battery/PageBatteryDetails.qml
index c5480ba97..9c7593263 100644
--- a/pages/settings/devicelist/battery/PageBatteryDetails.qml
+++ b/pages/settings/devicelist/battery/PageBatteryDetails.qml
@@ -13,7 +13,7 @@ Page {
property BatteryDetails details
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListQuantityGroup {
//% "Lowest cell voltage"
text: qsTrId("batterydetails_lowest_cell_voltage")
diff --git a/pages/settings/devicelist/battery/PageBatteryHistory.qml b/pages/settings/devicelist/battery/PageBatteryHistory.qml
index 1ecf94dde..b46d7dde2 100644
--- a/pages/settings/devicelist/battery/PageBatteryHistory.qml
+++ b/pages/settings/devicelist/battery/PageBatteryHistory.qml
@@ -13,7 +13,7 @@ Page {
required property BatteryHistory history
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListQuantity {
//% "Deepest discharge"
text: qsTrId("batteryalarms_deepest_discharge")
diff --git a/pages/settings/devicelist/battery/PageBatteryParameters.qml b/pages/settings/devicelist/battery/PageBatteryParameters.qml
index 355c442de..0ca08fded 100644
--- a/pages/settings/devicelist/battery/PageBatteryParameters.qml
+++ b/pages/settings/devicelist/battery/PageBatteryParameters.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListQuantity {
//% "Charge Voltage Limit (CVL)"
text: qsTrId("batteryparameters_charge_voltage_limit_cvl")
diff --git a/pages/settings/devicelist/battery/PageBatterySettings.qml b/pages/settings/devicelist/battery/PageBatterySettings.qml
index 169139783..99b2c14cf 100644
--- a/pages/settings/devicelist/battery/PageBatterySettings.qml
+++ b/pages/settings/devicelist/battery/PageBatterySettings.qml
@@ -32,7 +32,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListNavigation {
//% "Battery bank"
text: qsTrId("batterysettings_battery_bank")
diff --git a/pages/settings/devicelist/battery/PageBatterySettingsBattery.qml b/pages/settings/devicelist/battery/PageBatterySettingsBattery.qml
index 6afdba2ef..e83becc1c 100644
--- a/pages/settings/devicelist/battery/PageBatterySettingsBattery.qml
+++ b/pages/settings/devicelist/battery/PageBatterySettingsBattery.qml
@@ -18,7 +18,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
//% "Nominal Voltage"
text: qsTrId("batterysettingsbattery_nominal_voltage")
diff --git a/pages/settings/devicelist/battery/PageLynxIonDiagnostics.qml b/pages/settings/devicelist/battery/PageLynxIonDiagnostics.qml
index fa02a57cb..b83e52f2f 100644
--- a/pages/settings/devicelist/battery/PageLynxIonDiagnostics.qml
+++ b/pages/settings/devicelist/battery/PageLynxIonDiagnostics.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
//% "Shutdowns due error"
text: qsTrId("lynxiondiagnostics_shutdowns_due_error")
diff --git a/pages/settings/devicelist/battery/PageLynxIonIo.qml b/pages/settings/devicelist/battery/PageLynxIonIo.qml
index 5b2d89d45..443958b03 100644
--- a/pages/settings/devicelist/battery/PageLynxIonIo.qml
+++ b/pages/settings/devicelist/battery/PageLynxIonIo.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
//% "System Switch"
text: qsTrId("lynxionio_system_switch")
diff --git a/pages/settings/devicelist/battery/PageLynxIonSystem.qml b/pages/settings/devicelist/battery/PageLynxIonSystem.qml
index a9469c356..466c1ab0e 100644
--- a/pages/settings/devicelist/battery/PageLynxIonSystem.qml
+++ b/pages/settings/devicelist/battery/PageLynxIonSystem.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
//% "Capacity"
text: qsTrId("lynxionsystem_capacity")
diff --git a/pages/settings/devicelist/dc-in/PageAlternatorModel.qml b/pages/settings/devicelist/dc-in/PageAlternatorModel.qml
index 4c1fb1984..fd16bdb5c 100644
--- a/pages/settings/devicelist/dc-in/PageAlternatorModel.qml
+++ b/pages/settings/devicelist/dc-in/PageAlternatorModel.qml
@@ -6,7 +6,7 @@
import QtQuick
import Victron.VenusOS
-ObjectModel {
+VisibleItemModel {
id: root
property string bindPrefix
@@ -113,7 +113,7 @@ ObjectModel {
model: overallHistoryModel
}
- ObjectModel {
+ VisibleItemModel {
id: overallHistoryModel
ListText {
diff --git a/pages/settings/devicelist/dc-in/PageDcDcConverter.qml b/pages/settings/devicelist/dc-in/PageDcDcConverter.qml
index e0b3d95c1..777317c64 100644
--- a/pages/settings/devicelist/dc-in/PageDcDcConverter.qml
+++ b/pages/settings/devicelist/dc-in/PageDcDcConverter.qml
@@ -19,7 +19,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
text: CommonWords.switch_mode
dataItem.uid: root.bindPrefix + "/Mode"
diff --git a/pages/settings/devicelist/dc-in/PageDcMeterAlarms.qml b/pages/settings/devicelist/dc-in/PageDcMeterAlarms.qml
index 03ff39a42..d4b90f892 100644
--- a/pages/settings/devicelist/dc-in/PageDcMeterAlarms.qml
+++ b/pages/settings/devicelist/dc-in/PageDcMeterAlarms.qml
@@ -23,7 +23,7 @@ Page {
&& !highTemp.visible
}
- model: ObjectModel {
+ model: VisibleItemModel {
ListAlarm {
id: lowVoltage
diff --git a/pages/settings/devicelist/dc-in/PageDcMeterHistory.qml b/pages/settings/devicelist/dc-in/PageDcMeterHistory.qml
index 57b4a3ab6..0d3520366 100644
--- a/pages/settings/devicelist/dc-in/PageDcMeterHistory.qml
+++ b/pages/settings/devicelist/dc-in/PageDcMeterHistory.qml
@@ -25,7 +25,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListQuantity {
text: CommonWords.minimum_voltage
dataItem.uid: root.bindPrefix + "/History/MinimumVoltage"
diff --git a/pages/settings/devicelist/dc-in/PageDcMeterModel.qml b/pages/settings/devicelist/dc-in/PageDcMeterModel.qml
index 0576dc734..11766b270 100644
--- a/pages/settings/devicelist/dc-in/PageDcMeterModel.qml
+++ b/pages/settings/devicelist/dc-in/PageDcMeterModel.qml
@@ -6,7 +6,7 @@
import QtQuick
import Victron.VenusOS
-ObjectModel {
+VisibleItemModel {
id: root
property string bindPrefix
diff --git a/pages/settings/devicelist/inverter/PageInverter.qml b/pages/settings/devicelist/inverter/PageInverter.qml
index 2b1a71246..25489dc10 100644
--- a/pages/settings/devicelist/inverter/PageInverter.qml
+++ b/pages/settings/devicelist/inverter/PageInverter.qml
@@ -19,7 +19,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListItem {
id: modeListButton
diff --git a/pages/settings/devicelist/inverter/PageSolarStats.qml b/pages/settings/devicelist/inverter/PageSolarStats.qml
index 67f6b8225..1729337e6 100644
--- a/pages/settings/devicelist/inverter/PageSolarStats.qml
+++ b/pages/settings/devicelist/inverter/PageSolarStats.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListQuantity {
//% "Maximum PV voltage"
text: qsTrId("inverter_maximum_pv_voltage")
diff --git a/pages/settings/devicelist/pulsemeter/PagePulseCounter.qml b/pages/settings/devicelist/pulsemeter/PagePulseCounter.qml
index 1d0c86aa1..41bf7ecca 100644
--- a/pages/settings/devicelist/pulsemeter/PagePulseCounter.qml
+++ b/pages/settings/devicelist/pulsemeter/PagePulseCounter.qml
@@ -19,7 +19,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListQuantity {
//% "Aggregate"
text: qsTrId("pulsecounter_aggregate")
diff --git a/pages/settings/devicelist/pulsemeter/PagePulseCounterSetup.qml b/pages/settings/devicelist/pulsemeter/PagePulseCounterSetup.qml
index 5d02a7d24..17b07b6fc 100644
--- a/pages/settings/devicelist/pulsemeter/PagePulseCounterSetup.qml
+++ b/pages/settings/devicelist/pulsemeter/PagePulseCounterSetup.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListVolumeUnitRadioButtonGroup {}
ListSwitch {
diff --git a/pages/settings/devicelist/rs/PageMultiRs.qml b/pages/settings/devicelist/rs/PageMultiRs.qml
index 3a602fde3..d9b3c1559 100644
--- a/pages/settings/devicelist/rs/PageMultiRs.qml
+++ b/pages/settings/devicelist/rs/PageMultiRs.qml
@@ -46,7 +46,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
text: CommonWords.state
secondaryText: Global.system.systemStateToText(dataItem.value)
diff --git a/pages/settings/devicelist/rs/PageRsSystem.qml b/pages/settings/devicelist/rs/PageRsSystem.qml
index e1a3a21ec..f8b10020c 100644
--- a/pages/settings/devicelist/rs/PageRsSystem.qml
+++ b/pages/settings/devicelist/rs/PageRsSystem.qml
@@ -25,7 +25,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListItem {
id: modeListButton
diff --git a/pages/settings/devicelist/rs/PageRsSystemAlarms.qml b/pages/settings/devicelist/rs/PageRsSystemAlarms.qml
index e279d72a0..6359e16c6 100644
--- a/pages/settings/devicelist/rs/PageRsSystemAlarms.qml
+++ b/pages/settings/devicelist/rs/PageRsSystemAlarms.qml
@@ -17,7 +17,7 @@ Page {
uid: root.bindPrefix + "/Ac/NumberOfPhases"
}
- ObjectModel {
+ VisibleItemModel {
id: validAlarmsModel
VeBusAlarm {
@@ -52,7 +52,7 @@ Page {
}
}
- ObjectModel {
+ VisibleItemModel {
id: noAlarmsModel
PrimaryListLabel {
diff --git a/pages/settings/devicelist/rs/PageRsSystemEss.qml b/pages/settings/devicelist/rs/PageRsSystemEss.qml
index 3c14b2abc..965159628 100644
--- a/pages/settings/devicelist/rs/PageRsSystemEss.qml
+++ b/pages/settings/devicelist/rs/PageRsSystemEss.qml
@@ -27,7 +27,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
id: essMode
text: CommonWords.mode
diff --git a/pages/settings/devicelist/tank/PageTankAlarm.qml b/pages/settings/devicelist/tank/PageTankAlarm.qml
index 3e8477491..a9d0ab48e 100644
--- a/pages/settings/devicelist/tank/PageTankAlarm.qml
+++ b/pages/settings/devicelist/tank/PageTankAlarm.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSwitch {
//% "Enable alarm"
text: qsTrId("devicelist_tankalarm_enable_alarm")
diff --git a/pages/settings/devicelist/tank/PageTankSensor.qml b/pages/settings/devicelist/tank/PageTankSensor.qml
index e2ebe1e28..1664510e2 100644
--- a/pages/settings/devicelist/tank/PageTankSensor.qml
+++ b/pages/settings/devicelist/tank/PageTankSensor.qml
@@ -19,7 +19,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
text: CommonWords.status
dataItem.uid: root.bindPrefix + "/Status"
diff --git a/pages/settings/devicelist/tank/PageTankSetup.qml b/pages/settings/devicelist/tank/PageTankSetup.qml
index a025cab70..c55bf347c 100644
--- a/pages/settings/devicelist/tank/PageTankSetup.qml
+++ b/pages/settings/devicelist/tank/PageTankSetup.qml
@@ -13,7 +13,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListSpinBox {
//% "Capacity"
text: qsTrId("devicelist_tanksetup_capacity")
diff --git a/pages/settings/devicelist/temperature/PageTemperatureSensor.qml b/pages/settings/devicelist/temperature/PageTemperatureSensor.qml
index a8c6e48f3..01b273917 100644
--- a/pages/settings/devicelist/temperature/PageTemperatureSensor.qml
+++ b/pages/settings/devicelist/temperature/PageTemperatureSensor.qml
@@ -29,7 +29,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
text: CommonWords.status
dataItem.uid: root.bindPrefix + "/Status"
diff --git a/pages/settings/devicelist/temperature/PageTemperatureSensorSetup.qml b/pages/settings/devicelist/temperature/PageTemperatureSensorSetup.qml
index 2cd04647d..388f5fc2b 100644
--- a/pages/settings/devicelist/temperature/PageTemperatureSensorSetup.qml
+++ b/pages/settings/devicelist/temperature/PageTemperatureSensorSetup.qml
@@ -22,7 +22,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListRadioButtonGroup {
text: CommonWords.type
dataItem.uid: bindPrefix + "/TemperatureType"
diff --git a/pages/solar/PvInverterPage.qml b/pages/solar/PvInverterPage.qml
index 42bbab0f5..ba6527337 100644
--- a/pages/solar/PvInverterPage.qml
+++ b/pages/solar/PvInverterPage.qml
@@ -14,7 +14,7 @@ Page {
title: pvInverter.name
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListItemBackground {
height: phaseTable.y + phaseTable.height
diff --git a/pages/solar/SolarChargerAlarmsAndErrorsPage.qml b/pages/solar/SolarChargerAlarmsAndErrorsPage.qml
index a2c267dc2..e8fbe9840 100644
--- a/pages/solar/SolarChargerAlarmsAndErrorsPage.qml
+++ b/pages/solar/SolarChargerAlarmsAndErrorsPage.qml
@@ -15,7 +15,7 @@ Page {
GradientListView {
id: chargerListView
- model: ObjectModel {
+ model: VisibleItemModel {
PrimaryListLabel {
preferredVisible: lowBatteryAlarm.visible || highBatteryAlarm.visible || highTemperatureAlarm.visible || shortCircuitAlarm.visible
leftPadding: 0
diff --git a/pages/solar/SolarChargerNetworkedOperationPage.qml b/pages/solar/SolarChargerNetworkedOperationPage.qml
index 1a6277a80..b964bb1f0 100644
--- a/pages/solar/SolarChargerNetworkedOperationPage.qml
+++ b/pages/solar/SolarChargerNetworkedOperationPage.qml
@@ -15,7 +15,7 @@ Page {
GradientListView {
id: chargerListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
id: networkModeEnabled
//% "Networked"
diff --git a/pages/solar/SolarChargerPage.qml b/pages/solar/SolarChargerPage.qml
index 2054d8901..89b9c3700 100644
--- a/pages/solar/SolarChargerPage.qml
+++ b/pages/solar/SolarChargerPage.qml
@@ -16,7 +16,7 @@ Page {
title: solarCharger.name
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListItemBackground {
height: trackerTable.y + trackerTable.height
diff --git a/pages/vebusdevice/PageAcSensor.qml b/pages/vebusdevice/PageAcSensor.qml
index 70b0f8263..e4c07216e 100644
--- a/pages/vebusdevice/PageAcSensor.qml
+++ b/pages/vebusdevice/PageAcSensor.qml
@@ -13,7 +13,7 @@ Page {
property int index
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
Column {
width: parent ? parent.width : 0
diff --git a/pages/vebusdevice/PageAcSensors.qml b/pages/vebusdevice/PageAcSensors.qml
index 6e5b0eebb..3c9e4c101 100644
--- a/pages/vebusdevice/PageAcSensors.qml
+++ b/pages/vebusdevice/PageAcSensors.qml
@@ -12,7 +12,7 @@ Page {
property string bindPrefix
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
Column {
width: parent ? parent.width : 0
diff --git a/pages/vebusdevice/PageVeBus.qml b/pages/vebusdevice/PageVeBus.qml
index b28badeb0..000fd9afd 100644
--- a/pages/vebusdevice/PageVeBus.qml
+++ b/pages/vebusdevice/PageVeBus.qml
@@ -67,7 +67,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListItem {
id: modeListButton
@@ -174,7 +174,7 @@ Page {
onTriggered: Global.pageManager.popPage()
}
- model: ObjectModel {
+ model: VisibleItemModel {
ListItem {
text: {
var message = ""
diff --git a/pages/vebusdevice/PageVeBusAdvanced.qml b/pages/vebusdevice/PageVeBusAdvanced.qml
index 83e853995..9e677a21f 100644
--- a/pages/vebusdevice/PageVeBusAdvanced.qml
+++ b/pages/vebusdevice/PageVeBusAdvanced.qml
@@ -78,7 +78,7 @@ Page {
GradientListView {
id: gradientListView
- model: ObjectModel {
+ model: VisibleItemModel {
ListButton {
property bool interrupt: vebusSubState.value === VenusOS.VeBusDevice_ChargeState_Equalize
@@ -260,7 +260,7 @@ Page {
Page {
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
Column {
width: parent ? parent.width : 0
diff --git a/pages/vebusdevice/PageVeBusAlarms.qml b/pages/vebusdevice/PageVeBusAlarms.qml
index 8b5970a21..05a9bbcf4 100644
--- a/pages/vebusdevice/PageVeBusAlarms.qml
+++ b/pages/vebusdevice/PageVeBusAlarms.qml
@@ -23,7 +23,7 @@ Page {
title: CommonWords.alarm_status
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
text: CommonWords.vebus_error
diff --git a/pages/vebusdevice/PageVeBusBms.qml b/pages/vebusdevice/PageVeBusBms.qml
index ba2cd757c..d38125259 100644
--- a/pages/vebusdevice/PageVeBusBms.qml
+++ b/pages/vebusdevice/PageVeBusBms.qml
@@ -13,7 +13,7 @@ Page {
property int bmsType
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
text: CommonWords.allow_to_charge
secondaryText: CommonWords.yesOrNo(dataItem.value)
diff --git a/pages/vebusdevice/PageVeBusDebug.qml b/pages/vebusdevice/PageVeBusDebug.qml
index 5933a7196..234f78fea 100644
--- a/pages/vebusdevice/PageVeBusDebug.qml
+++ b/pages/vebusdevice/PageVeBusDebug.qml
@@ -36,7 +36,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListNavigation {
text: CommonWords.ac_sensors
diff --git a/pages/vebusdevice/PageVeBusError11Device.qml b/pages/vebusdevice/PageVeBusError11Device.qml
index cb9b7aa7c..4b19ed800 100644
--- a/pages/vebusdevice/PageVeBusError11Device.qml
+++ b/pages/vebusdevice/PageVeBusError11Device.qml
@@ -26,7 +26,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
//% "Last VE.Bus Error 11 report #%1"
diff --git a/pages/vebusdevice/PageVeBusError11View.qml b/pages/vebusdevice/PageVeBusError11View.qml
index 0996f057b..c67bc50d0 100644
--- a/pages/vebusdevice/PageVeBusError11View.qml
+++ b/pages/vebusdevice/PageVeBusError11View.qml
@@ -18,7 +18,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
PrimaryListLabel {
//% "VE.Bus Error 11 reporting requires minimum VE.Bus firmware version 454."
diff --git a/pages/vebusdevice/PageVeBusKwhCounters.qml b/pages/vebusdevice/PageVeBusKwhCounters.qml
index c1ad50915..972b66cd0 100644
--- a/pages/vebusdevice/PageVeBusKwhCounters.qml
+++ b/pages/vebusdevice/PageVeBusKwhCounters.qml
@@ -13,7 +13,7 @@ Page {
property var service
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
ListText {
//% "VE.Bus Quirks"
diff --git a/pages/vebusdevice/PageVeBusSerialNumbers.qml b/pages/vebusdevice/PageVeBusSerialNumbers.qml
index 66a5f2253..d9946fef0 100644
--- a/pages/vebusdevice/PageVeBusSerialNumbers.qml
+++ b/pages/vebusdevice/PageVeBusSerialNumbers.qml
@@ -30,7 +30,7 @@ Page {
}
GradientListView {
- model: ObjectModel {
+ model: VisibleItemModel {
Column {
width: parent ? parent.width : 0
diff --git a/src/visibleitemmodel.cpp b/src/visibleitemmodel.cpp
new file mode 100644
index 000000000..7f96b3fc9
--- /dev/null
+++ b/src/visibleitemmodel.cpp
@@ -0,0 +1,242 @@
+/*
+** Copyright (C) 2025 Victron Energy B.V.
+** See LICENSE.txt for license information.
+*/
+
+#include "visibleitemmodel.h"
+
+#include
+#include
+#include
+
+using namespace Victron::VenusOS;
+
+
+class VisibleItemModelPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(VisibleItemModel)
+public:
+ VisibleItemModelPrivate()
+ : QObjectPrivate() {}
+
+ static void sourceModel_append(QQmlListProperty *prop, QObject *object) {
+ static_cast(prop->data)->append(object);
+ }
+
+ static qsizetype sourceModel_count(QQmlListProperty *prop) {
+ auto d = static_cast(prop->data);
+ return d->allObjects.count();
+ }
+
+ static QObject *sourceModel_at(QQmlListProperty *prop, qsizetype index) {
+ auto d = static_cast(prop->data);
+ return d->allObjects.value(index);
+ }
+
+ static void sourceModel_clear(QQmlListProperty *prop) {
+ static_cast(prop->data)->clear();
+ }
+
+ static void sourceModel_removeLast(QQmlListProperty *prop) {
+ static_cast(prop->data)->removeLast();
+ }
+
+ void append(QObject *object) {
+ Q_Q(VisibleItemModel);
+ allObjects.append(object);
+
+ if (QQuickItem *item = qobject_cast(object)) {
+ const QVariant preferredVisible = item->property("preferredVisible");
+ if (preferredVisible.isValid()) {
+ QObject::connect(item, SIGNAL(preferredVisibleChanged()), q, SLOT(preferredVisibleChanged()));
+ }
+ // Add the item to the visible item list, if preferredVisible=true, or if it has no
+ // preferredVisible property and thus should not be filtered out.
+ preferredVisibleChanged(item, !preferredVisible.isValid() || preferredVisible.toBool());
+ }
+
+ emit q->sourceModelChanged();
+ }
+
+ void removeLast() {
+ Q_Q(VisibleItemModel);
+ if (allObjects.count() > 0) {
+ QObject *last = allObjects.takeLast();
+ last->disconnect(q);
+ const int visibleItemIndex = visibleItems.indexOf(last);
+ if (visibleItemIndex >= 0) {
+ QQmlChangeSet changeSet;
+ changeSet.remove(visibleItemIndex, 1);
+ visibleItems.removeAt(visibleItemIndex);
+ emit q->modelUpdated(changeSet, false);
+ emit q->countChanged();
+ }
+ emit q->sourceModelChanged();
+ }
+ }
+
+ void clear() {
+ Q_Q(VisibleItemModel);
+ if (!allObjects.isEmpty()) {
+ for (const QObject *object : allObjects) {
+ object->disconnect(q);
+ }
+ allObjects.clear();
+ if (!visibleItems.isEmpty()) {
+ QQmlChangeSet changeSet;
+ changeSet.remove(0, visibleItems.count());
+ visibleItems.clear();
+ emit q->modelUpdated(changeSet, false);
+ emit q->countChanged();
+ }
+ emit q->sourceModelChanged();
+ }
+ }
+
+ void preferredVisibleChanged(QQuickItem *item, bool preferredVisible) {
+ Q_Q(VisibleItemModel);
+
+ if (preferredVisible) {
+ const int insertionIndex = visibleInsertionIndex(item);
+ if (insertionIndex >= 0) {
+ QQmlChangeSet changeSet;
+ changeSet.insert(insertionIndex, 1);
+ visibleItems.insert(insertionIndex, item);
+ emit q->modelUpdated(changeSet, false);
+ emit q->countChanged();
+ }
+ } else {
+ const int removalIndex = visibleItems.indexOf(item);
+ if (removalIndex >= 0) {
+ QQmlChangeSet changeSet;
+ changeSet.remove(removalIndex, 1);
+ visibleItems.removeAt(removalIndex);
+ emit q->modelUpdated(changeSet, false);
+ emit q->countChanged();
+ }
+ }
+ }
+
+ /*
+ Returns the index at which an item should be inserted into visibleItems.
+ Returns -1 if the index should not be inserted.
+ */
+ int visibleInsertionIndex(QQuickItem *item) {
+ if (!item || visibleItems.indexOf(item) >= 0) {
+ return -1;
+ }
+
+ // In the overall list: find the last item that is visible before this one.
+ const int overallIndex = allObjects.indexOf(item);
+ if (overallIndex < 0) {
+ return -1;
+ }
+ QObject *prevVisibleItem = nullptr;
+ for (int i = overallIndex - 1; i >= 0 && i < allObjects.count(); --i) {
+ if (QQuickItem *previousItem = qobject_cast(allObjects.at(i))) {
+ const QVariant preferredVisible = previousItem->property("preferredVisible");
+ if (!preferredVisible.isValid() || preferredVisible.toBool()) {
+ prevVisibleItem = previousItem;
+ break;
+ }
+ }
+ }
+
+ // In the visible items list: find the index of that previous visible item.
+ if (prevVisibleItem) {
+ const int prevVisibleIndex = visibleItems.indexOf(prevVisibleItem);
+ if (prevVisibleIndex >= 0) {
+ // Insert the new item after that previous visible item.
+ return prevVisibleIndex + 1;
+ }
+ }
+ // There is no previous visible item, so this must be the first visible item.
+ return 0;
+ }
+
+ QVector allObjects;
+ QVector visibleItems;
+};
+
+VisibleItemModel::VisibleItemModel(QObject *parent)
+ : QQmlInstanceModel(*(new VisibleItemModelPrivate), parent)
+{
+}
+
+QQmlListProperty VisibleItemModel::sourceModel()
+{
+ Q_D(VisibleItemModel);
+ return QQmlListProperty(this, d,
+ VisibleItemModelPrivate::sourceModel_append,
+ VisibleItemModelPrivate::sourceModel_count,
+ VisibleItemModelPrivate::sourceModel_at,
+ VisibleItemModelPrivate::sourceModel_clear,
+ nullptr,
+ VisibleItemModelPrivate::sourceModel_removeLast);
+}
+
+QObject* VisibleItemModel::get(int index)
+{
+ return object(index, QQmlIncubator::Synchronous);
+}
+
+int VisibleItemModel::count() const
+{
+ Q_D(const VisibleItemModel);
+ return d->visibleItems.count();
+}
+
+bool VisibleItemModel::isValid() const
+{
+ return true;
+}
+
+QObject *VisibleItemModel::object(int index, QQmlIncubator::IncubationMode)
+{
+ Q_D(VisibleItemModel);
+ if (index < 0 || index >= d->visibleItems.count()) {
+ return nullptr;
+ }
+ return d->visibleItems.at(index);
+}
+
+QQmlInstanceModel::ReleaseFlags VisibleItemModel::release(QObject *, ReusableFlag)
+{
+ // Always return Referenced flag. Otherwise, when a view sees the item is no longer referenced,
+ // it will unparent the item.
+ return QQmlInstanceModel::Referenced;
+}
+
+QVariant VisibleItemModel::variantValue(int index, const QString &role)
+{
+ Q_D(VisibleItemModel);
+ if (index < 0 || index >= d->visibleItems.count()) {
+ return QVariant();
+ }
+ return d->visibleItems.at(index)->property(role.toUtf8().constData());
+}
+
+QQmlIncubator::Status VisibleItemModel::incubationStatus(int)
+{
+ // The model does not internally create objects, so any referenced objects are always available.
+ return QQmlIncubator::Ready;
+}
+
+int VisibleItemModel::indexOf(QObject *item, QObject *context) const
+{
+ Q_D(const VisibleItemModel);
+ for (int i = 0; i < d->visibleItems.count(); ++i) {
+ if (d->visibleItems.at(i) == item) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void VisibleItemModel::preferredVisibleChanged()
+{
+ Q_D(VisibleItemModel);
+ if (QQuickItem *item = qobject_cast(sender())) {
+ d->preferredVisibleChanged(item, item->property("preferredVisible").toBool());
+ }
+}
diff --git a/src/visibleitemmodel.h b/src/visibleitemmodel.h
new file mode 100644
index 000000000..8226a8287
--- /dev/null
+++ b/src/visibleitemmodel.h
@@ -0,0 +1,73 @@
+/*
+** Copyright (C) 2025 Victron Energy B.V.
+** See LICENSE.txt for license information.
+*/
+
+#ifndef VISIBLEITEMMODEL_H
+#define VISIBLEITEMMODEL_H
+
+#include
+#include
+
+class VisibleItemModelPrivate;
+
+namespace Victron {
+namespace VenusOS {
+
+/*
+ Provides an instance model that filters out non-visible items from the specified source model.
+
+ If any item in the source model has preferredVisible=false, then it is filtered out of the
+ instance model, and thus will not be loaded in any view that uses the model. Non-items are also
+ filtered out of the model.
+
+ The sourceModel is the default property, so items can be declared directly as children,
+ similarly to how ObjectModel is used:
+
+ ListView {
+ // Make a VisibleItemModel with two source items
+ model: VisibleItemModel {
+ ListItem { preferredVisible: false } // this item will not be loaded by the view
+ ListItem {} // this item will be loaded by the view
+ }
+ }
+*/
+class VisibleItemModel : public QQmlInstanceModel
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(VisibleItemModel)
+
+ Q_PROPERTY(QQmlListProperty sourceModel READ sourceModel NOTIFY sourceModelChanged DESIGNABLE false FINAL)
+ Q_CLASSINFO("DefaultProperty", "sourceModel")
+ QML_ELEMENT
+
+public:
+ explicit VisibleItemModel(QObject *parent = nullptr);
+
+ Q_INVOKABLE QObject* get(int index);
+
+ int count() const override;
+ bool isValid() const override;
+ QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override;
+ ReleaseFlags release(QObject *object, ReusableFlag reusable = NotReusable) override;
+ QVariant variantValue(int index, const QString &role) override;
+ void setWatchedRoles(const QList &) override {}
+ QQmlIncubator::Status incubationStatus(int index) override;
+
+ int indexOf(QObject *object, QObject *context) const override;
+
+ QQmlListProperty sourceModel();
+
+Q_SIGNALS:
+ void sourceModelChanged();
+
+private:
+ Q_DISABLE_COPY(VisibleItemModel)
+
+ Q_INVOKABLE void preferredVisibleChanged();
+};
+
+} /* VenusOS */
+} /* Victron */
+
+#endif // VISIBLEITEMMODEL_H
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 0fd803a30..c00ebb768 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -20,3 +20,4 @@ add_subdirectory(firmwareversion)
add_subdirectory(units)
add_subdirectory(screenblanker)
add_subdirectory(backendconnection)
+add_subdirectory(visibleitemmodel)
diff --git a/tests/visibleitemmodel/CMakeLists.txt b/tests/visibleitemmodel/CMakeLists.txt
new file mode 100644
index 000000000..daa7414a9
--- /dev/null
+++ b/tests/visibleitemmodel/CMakeLists.txt
@@ -0,0 +1,49 @@
+#
+# Copyright (C) 2024 Victron Energy B.V.
+# See LICENSE.txt for license information.
+#
+
+cmake_minimum_required(VERSION 3.16)
+project(tst_visibleitemmodel LANGUAGES CXX)
+
+set(CMAKE_AUTOMOC ON)
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml QuickTest Quick)
+
+qt_add_executable(tst_visibleitemmodel
+ tst_visibleitemmodel.cpp
+ ../../src/visibleitemmodel.h
+ ../../src/visibleitemmodel.cpp
+)
+
+include_directories(../../src)
+
+qt_add_qml_module( ${PROJECT_NAME}
+ URI ${PROJECT_NAME}
+ VERSION 1.0
+ RESOURCE_PREFIX /
+ QML_FILES
+ tst_visibleitemmodel.qml
+ OUTPUT_DIRECTORY Victron/VenusOS
+)
+
+set_target_properties(tst_visibleitemmodel PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+option(VENUS_INSTALL_TESTS "enable test installation via cmake -DVENUS_INSTALL_TESTS=ON" OFF) # Disabled by default
+if (VENUS_INSTALL_TESTS)
+ install(FILES tst_visibleitemmodel.qml DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../install/tests/visibleitemmodel)
+ install(TARGETS tst_visibleitemmodel DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/../../install/tests/visibleitemmodel)
+endif()
+
+target_link_libraries(tst_visibleitemmodel PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::QuickPrivate
+ Qt6::QuickTest
+ Qt6::Quick
+)
+
+add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
diff --git a/tests/visibleitemmodel/tst_visibleitemmodel.cpp b/tests/visibleitemmodel/tst_visibleitemmodel.cpp
new file mode 100644
index 000000000..6bcdd3a24
--- /dev/null
+++ b/tests/visibleitemmodel/tst_visibleitemmodel.cpp
@@ -0,0 +1,16 @@
+/*
+** Copyright (C) 2024 Victron Energy B.V.
+** See LICENSE.txt for license information.
+*/
+
+#include
+#include
+#include "visibleitemmodel.h"
+
+int main(int argc, char **argv) \
+{
+ qmlRegisterType("Victron.VenusOS", 2, 0, "VisibleItemModel");
+
+ QTEST_SET_MAIN_SOURCE_PATH
+ return quick_test_main(argc, argv, "tst_visibleitemmodel", nullptr);
+}
diff --git a/tests/visibleitemmodel/tst_visibleitemmodel.qml b/tests/visibleitemmodel/tst_visibleitemmodel.qml
new file mode 100644
index 000000000..254b04a64
--- /dev/null
+++ b/tests/visibleitemmodel/tst_visibleitemmodel.qml
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2024 Victron Energy B.V.
+ * See LICENSE.txt for license information.
+*/
+
+import QtTest
+import QtQuick
+import Victron.VenusOS
+import QtQuick.Window
+
+TestCase {
+ id: root
+ name: "visibleItemModelTest"
+ when: windowShown
+
+ component ListDelegate : Rectangle {
+ property bool preferredVisible: true
+
+ width: 800
+ height: 20
+ border.width: 1
+
+ Text { text: parent.objectName }
+ }
+
+ Window {
+ width: 800
+ height: 600
+ visible: true
+
+ Rectangle {
+ anchors.fill: parent
+ color: "yellow"
+
+ ListView {
+ id: viewA
+ anchors.fill: parent
+ model: VisibleItemModel {
+ ListDelegate {
+ id: itemA1
+ objectName: "itemA1"
+ }
+ ListDelegate {
+ id: itemA2
+ objectName: "itemA2"
+ }
+ ListDelegate {
+ id: itemA3
+ objectName: "itemA3"
+ }
+ }
+ }
+
+ ListView {
+ id: viewB
+ anchors.fill: parent
+ model: VisibleItemModel {
+ ListDelegate {
+ id: itemB1
+ property bool shouldBeVisible
+ objectName: "itemB1"
+ preferredVisible: shouldBeVisible
+ }
+ ListDelegate {
+ id: itemB2
+ objectName: "itemB2"
+ }
+ ListDelegate {
+ id: itemB3
+ objectName: "itemB3"
+ }
+ ListDelegate {
+ id: itemB4
+ objectName: "itemB4"
+ preferredVisible: false
+ }
+ ListDelegate {
+ id: itemB5
+ objectName: "itemB5"
+ }
+ }
+ }
+
+ ListView {
+ id: viewWithQObjects
+ anchors.fill: parent
+ model: VisibleItemModel {
+ QtObject { id: viewWithQObjects_object1 }
+ ListDelegate { id: viewWithQObjects_object2 }
+ }
+ }
+
+ ListView {
+ id: viewWithNestedItems
+ anchors.fill: parent
+ model: VisibleItemModel {
+ Column {
+ id: viewWithNestedItems_item1
+ width: parent ? parent.width : 0
+ ListDelegate {}
+ ListDelegate {}
+ }
+ ListDelegate { id: viewWithNestedItems_item2 }
+ }
+ }
+ }
+ }
+
+ function test_allItemsVisible() {
+ const visibleItemModel = viewA.model
+ const allItemsList = viewA.model.sourceModel
+
+ compare(allItemsList.length, 3)
+ compare(allItemsList[0], itemA1)
+ compare(allItemsList[1], itemA2)
+ compare(allItemsList[2], itemA3)
+
+ compare(visibleItemModel.count, 3)
+ compare(viewA.count, 3)
+ compare(visibleItemModel.get(0), itemA1)
+ compare(visibleItemModel.get(1), itemA2)
+ compare(visibleItemModel.get(2), itemA3)
+ }
+
+ function test_someItemsVisible() {
+ const visibleItemModel = viewB.model
+ const allItemsList = viewB.model.sourceModel
+
+ compare(allItemsList.length, 5)
+ compare(allItemsList[0], itemB1)
+ compare(allItemsList[1], itemB2)
+ compare(allItemsList[2], itemB3)
+ compare(allItemsList[3], itemB4)
+ compare(allItemsList[4], itemB5)
+
+ // The two non-visible items should be filtered out
+ compare(visibleItemModel.count, 3)
+ compare(viewB.count, 3)
+ compare(visibleItemModel.get(0), itemB2)
+ compare(visibleItemModel.get(1), itemB3)
+ compare(visibleItemModel.get(2), itemB5)
+ verify(!visibleItemModel.get(3))
+ verify(!visibleItemModel.get(4))
+
+ // Test that the model is updated if an item becomes visible by forcing preferredVisible=true.
+ itemB4.preferredVisible = true
+ compare(visibleItemModel.count, 4)
+ compare(viewB.count, 4)
+ compare(visibleItemModel.get(0), itemB2)
+ compare(visibleItemModel.get(1), itemB3)
+ compare(visibleItemModel.get(2), itemB4)
+ compare(visibleItemModel.get(3), itemB5)
+
+ // Test that the model is updated if an item becomes visible by updating a binding.
+ itemB1.shouldBeVisible = true
+ compare(visibleItemModel.count, 5)
+ compare(viewB.count, 5)
+ compare(visibleItemModel.get(0), itemB1)
+ compare(visibleItemModel.get(1), itemB2)
+ compare(visibleItemModel.get(2), itemB3)
+ compare(visibleItemModel.get(3), itemB4)
+ compare(visibleItemModel.get(4), itemB5)
+ }
+
+ function test_itemsVisibilityChanges() {
+ const visibleItemModel = viewA.model
+ const allItemsList = viewA.model.sourceModel
+
+ // Hide all items
+ compare(allItemsList.length, 3)
+ compare(visibleItemModel.count, 3)
+ for (let i = 0; i < 3; ++i) {
+ allItemsList[i].preferredVisible = false
+ }
+ compare(allItemsList.length, 3)
+ compare(visibleItemModel.count, 0)
+ compare(viewA.count, 0)
+
+ // Make the 2nd source item visible, so it becomes the 1st item in the visibleItemModel.
+ allItemsList[1].preferredVisible = true
+ compare(visibleItemModel.count, 1)
+ compare(viewA.count, 1)
+ compare(visibleItemModel.get(0), itemA2)
+
+ // Make the 1st source item visible, so it becomes the 1st item in the visibleItemModel.
+ allItemsList[0].preferredVisible = true
+ compare(visibleItemModel.count, 2)
+ compare(viewA.count, 2)
+ compare(visibleItemModel.get(0), itemA1)
+ compare(visibleItemModel.get(1), itemA2)
+
+ // Make the 1st source item visible again
+ allItemsList[0].preferredVisible = false
+ compare(visibleItemModel.count, 1)
+ compare(viewA.count, 1)
+ compare(visibleItemModel.get(0), itemA2)
+
+ // Make the 3rd item visible, so it becomes the 2nd item in the visibleItemModel.
+ allItemsList[2].preferredVisible = true
+ compare(visibleItemModel.count, 2)
+ compare(viewA.count, 2)
+ compare(visibleItemModel.get(0), itemA2)
+ compare(visibleItemModel.get(1), itemA3)
+
+ // Make the 1st item visible again
+ allItemsList[0].preferredVisible = true
+ compare(visibleItemModel.count, 3)
+ compare(viewA.count, 3)
+ compare(visibleItemModel.get(0), itemA1)
+ compare(visibleItemModel.get(1), itemA2)
+ compare(visibleItemModel.get(2), itemA3)
+ }
+
+ function test_viewWithQObjects() {
+ // The sourceModel has both a QtObject and an Item
+ compare(viewWithQObjects.model.sourceModel.length, 2)
+ compare(viewWithQObjects.model.sourceModel[0], viewWithQObjects_object1)
+ compare(viewWithQObjects.model.sourceModel[1], viewWithQObjects_object2)
+
+ // The QtObject is filtered out of the view and the model.
+ compare(viewWithQObjects.model.count, 1)
+ compare(viewWithQObjects.model.get(0), viewWithQObjects_object2)
+ compare(viewWithQObjects.count, 1)
+ }
+
+ function test_viewWithNestedItems() {
+ compare(viewWithNestedItems.model.sourceModel.length, 2)
+ compare(viewWithNestedItems.model.sourceModel[0], viewWithNestedItems_item1)
+ compare(viewWithNestedItems.model.sourceModel[1], viewWithNestedItems_item2)
+
+ // The Column should not be filtered out, as it does not have a preferredVisible property.
+ compare(viewWithNestedItems.model.count, 2)
+ compare(viewWithNestedItems.model.get(0), viewWithNestedItems_item1)
+ compare(viewWithNestedItems.model.get(1), viewWithNestedItems_item2)
+ compare(viewWithNestedItems.count, 2)
+ }
+}