diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index dd64f5fdfbe..b07c1254636 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -127,6 +127,7 @@ src/QmlControls/MissionItemIndexLabel.qml src/PlanView/MissionItemMapVisual.qml src/PlanView/MissionItemStatus.qml + src/QmlControls/MvPanelPage.qml src/QmlControls/OfflineMapButton.qml src/QtLocationPlugin/QMLControl/OfflineMapEditor.qml src/UI/preferences/OfflineMapInfo.qml @@ -161,12 +162,14 @@ src/QmlControls/QGCMenuSeparator.qml src/QmlControls/QGCMouseArea.qml src/QmlControls/QGCMovableItem.qml + src/QmlControls/QGCPageIndicator.qml src/QmlControls/QGCPopupDialog.qml src/QmlControls/PipView.qml src/QmlControls/PipState.qml src/QmlControls/QGCRadioButton.qml src/QmlControls/QGCSimpleMessageDialog.qml src/QmlControls/QGCSlider.qml + src/QmlControls/QGCSwipeView.qml src/QmlControls/QGCSwitch.qml src/QmlControls/QGCTabBar.qml src/QmlControls/QGCTabButton.qml @@ -342,6 +345,7 @@ src/Viewer3D/Viewer3DQml/Models3D/Line3D.qml src/Viewer3D/Viewer3DQml/Models3D/Viewer3DVehicleItems.qml src/Viewer3D/Viewer3DQml/Viewer3DProgressBar.qml + src/FlightDisplay/FlyViewTopRightPanel.qml src/FirstRunPromptDialogs/UnitsFirstRunPrompt.qml diff --git a/src/FlightDisplay/FlyViewTopRightColumnLayout.qml b/src/FlightDisplay/FlyViewTopRightColumnLayout.qml index 9a7b8197215..245267b534b 100644 --- a/src/FlightDisplay/FlyViewTopRightColumnLayout.qml +++ b/src/FlightDisplay/FlyViewTopRightColumnLayout.qml @@ -20,29 +20,6 @@ import QGroundControl.ScreenTools ColumnLayout { width: _rightPanelWidth - RowLayout { - id: multiVehiclePanelSelector - Layout.alignment: Qt.AlignTop - spacing: ScreenTools.defaultFontPixelWidth - visible: QGroundControl.multiVehicleManager.vehicles.count > 1 && QGroundControl.corePlugin.options.flyView.showMultiVehicleList - - QGCMapPalette { id: mapPal; lightColors: true } - - QGCRadioButton { - id: singleVehicleRadio - text: qsTr("Single") - checked: _showSingleVehicleUI - onClicked: _showSingleVehicleUI = true - textColor: mapPal.text - } - - QGCRadioButton { - text: qsTr("Multi-Vehicle") - textColor: mapPal.text - onClicked: _showSingleVehicleUI = false - } - } - TerrainProgress { Layout.alignment: Qt.AlignTop Layout.preferredWidth: _rightPanelWidth @@ -54,7 +31,7 @@ ColumnLayout { Loader { id: photoVideoControlLoader Layout.alignment: Qt.AlignTop | Qt.AlignRight - sourceComponent: globals.activeVehicle && _showSingleVehicleUI ? photoVideoControlComponent : undefined + sourceComponent: globals.activeVehicle ? photoVideoControlComponent : undefined property real rightEdgeCenterInset: visible ? parent.width - x : 0 @@ -65,10 +42,4 @@ ColumnLayout { } } } - - MultiVehicleList { - Layout.preferredWidth: _rightPanelWidth - Layout.fillHeight: true - visible: !_showSingleVehicleUI - } } diff --git a/src/FlightDisplay/FlyViewTopRightPanel.qml b/src/FlightDisplay/FlyViewTopRightPanel.qml new file mode 100644 index 00000000000..2a8a10c010c --- /dev/null +++ b/src/FlightDisplay/FlyViewTopRightPanel.qml @@ -0,0 +1,252 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick +import QtQuick.Layouts + +import QGroundControl +import QGroundControl.FactSystem +import QGroundControl.FactControls +import QGroundControl.Controls +import QGroundControl.FlightDisplay +import QGroundControl.FlightMap +import QGroundControl.Palette +import QGroundControl.ScreenTools + + +Rectangle { + id: topRightPanel + width: contentWidth + height: Math.max(contentHeight, minimumHeight) + color: qgcPal.toolbarBackground + radius: ScreenTools.defaultFontPixelHeight / 2 + visible: !QGroundControl.videoManager.fullScreen && _multipleVehicles && _settingEnableMVPanel + clip: true + + property bool _settingEnableMVPanel: QGroundControl.settingsManager.appSettings.enableMultiVehiclePanel.value + property bool _multipleVehicles: QGroundControl.multiVehicleManager.vehicles.count > 1 + property var vehicles: QGroundControl.multiVehicleManager.vehicles + property var selectedVehicles: QGroundControl.multiVehicleManager.selectedVehicles + property real contentWidth: Math.max( + multiVehicleList.implicitWidth, + swipeViewContainer.implicitWidth + ) + ScreenTools.defaultFontPixelHeight + property real contentHeight: Math.min( + maximumHeight, + topRightPanelColumnLayout.implicitHeight + topRightPanelColumnLayout.spacing * ( topRightPanelColumnLayout.children.length - 1) + ) + property real minimumHeight: selectedVehiclesLabel.height + swipeViewContainer.height + property real maximumHeight + + QGCPalette { id: qgcPal } + + ColumnLayout { + id: topRightPanelColumnLayout + anchors.fill: parent + anchors.margins: ScreenTools.defaultFontPixelHeight / 2 + spacing: ScreenTools.defaultFontPixelHeight / 2 + + QGCLabel { + id: selectedVehiclesLabel + text: { + let ids = Array.from({length: selectedVehicles.count}, (_, i) => + selectedVehicles.get(i).id + ).sort((a, b) => a - b) + .join(", ") + return qsTr("Selected: ") + ids + } + } + + MultiVehicleList { + id: multiVehicleList + Layout.fillWidth: true + Layout.fillHeight: true + implicitHeight: multiVehicleList.innerColumnHeight * vehicles.count - _margins * 3 + + Rectangle { + anchors.fill: parent + visible: topRightPanel.height === maximumHeight + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.margins: 0 + height: 1 + color: QGroundControl.globalPalette.groupBorder + } + + gradient: Gradient { + orientation: Gradient.Vertical + GradientStop { position: 0.00; color: topRightPanel.color } + GradientStop { position: 0.05; color: "transparent" } + + GradientStop { position: 0.95; color: "transparent" } + GradientStop { position: 1.00; color: topRightPanel.color } + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.margins: 0 + height: 1 + color: QGroundControl.globalPalette.groupBorder + } + } + + } + + Rectangle { + id: swipeViewContainer + Layout.fillWidth: true + implicitHeight: swipePages.implicitHeight + implicitWidth: swipePages.implicitWidth + color: "transparent" + + QGCSwipeView { + id: swipePages + anchors.fill: parent + spacing: ScreenTools.defaultFontPixelHeight + implicitHeight: Math.max(buttonsPage.implicitHeight, photoVideoPage.implicitHeight) + implicitWidth: Math.max(buttonsPage.implicitWidth, photoVideoPage.implicitWidth) + + MvPanelPage { + id: buttonsPage + implicitHeight: buttonsColumnLayout.implicitHeight + ScreenTools.defaultFontPixelHeight * 2 + implicitWidth: buttonsColumnLayout.implicitWidth + ScreenTools.defaultFontPixelHeight * 2 + + ColumnLayout { + id: buttonsColumnLayout + anchors.right: parent.right + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + spacing: ScreenTools.defaultFontPixelHeight / 2 + implicitHeight: Math.max(selectionRowLayout.height, actionRowLayout.height) + ScreenTools.defaultFontPixelHeight * 2 + implicitWidth: Math.max(selectionRowLayout.width, actionRowLayout.width) + ScreenTools.defaultFontPixelHeight * 4 + + QGCLabel { + text: qsTr("Multi Vehicle Selection") + Layout.alignment: Qt.AlignHCenter + } + + RowLayout { + id: selectionRowLayout + Layout.alignment: Qt.AlignHCenter + + QGCButton { + text: qsTr("Select All") + enabled: multiVehicleList.selectedVehicles && multiVehicleList.selectedVehicles.count !== QGroundControl.multiVehicleManager.vehicles.count + onClicked: multiVehicleList.selectAll() + } + + QGCButton { + text: qsTr("Deselect All") + enabled: multiVehicleList.selectedVehicles && multiVehicleList.selectedVehicles.count > 0 + onClicked: multiVehicleList.deselectAll() + } + + } + + + QGCLabel { + text: qsTr("Multi Vehicle Actions") + Layout.alignment: Qt.AlignHCenter + } + + RowLayout { + id: actionRowLayout + Layout.alignment: Qt.AlignHCenter + + QGCButton { + text: qsTr("Arm") + enabled: multiVehicleList.armAvailable() + onClicked: _guidedController.confirmAction(_guidedController.actionMVArm) + Layout.preferredWidth: ScreenTools.defaultFontPixelHeight * 2.75 + leftPadding: 0 + rightPadding: 0 + } + + QGCButton { + text: qsTr("Disarm") + enabled: multiVehicleList.disarmAvailable() + onClicked: _guidedController.confirmAction(_guidedController.actionMVDisarm) + Layout.preferredWidth: ScreenTools.defaultFontPixelHeight * 2.75 + leftPadding: 0 + rightPadding: 0 + } + + QGCButton { + text: qsTr("Start") + enabled: multiVehicleList.startAvailable() + onClicked: _guidedController.confirmAction(_guidedController.actionMVStartMission) + Layout.preferredWidth: ScreenTools.defaultFontPixelHeight * 2.75 + leftPadding: 0 + rightPadding: 0 + } + + QGCButton { + text: qsTr("Pause") + enabled: multiVehicleList.pauseAvailable() + onClicked: _guidedController.confirmAction(_guidedController.actionMVPause) + Layout.preferredWidth: ScreenTools.defaultFontPixelHeight * 2.75 + leftPadding: 0 + rightPadding: 0 + } + } + } + } // Page 1 + + MvPanelPage { + + id: photoVideoPage + implicitHeight: photoVideoControlLoader.implicitHeight + ScreenTools.defaultFontPixelHeight * 2 + implicitWidth: photoVideoControlLoader.implicitWidth + ScreenTools.defaultFontPixelHeight * 2 + + // We use a Loader to load the photoVideoControlComponent only when the active vehicle is not null + // This make it easier to implement PhotoVideoControl without having to check for the mavlink camera + // to be null all over the place + + Loader { + id: photoVideoControlLoader + anchors.horizontalCenter: parent.horizontalCenter + sourceComponent: globals.activeVehicle ? photoVideoControlComponent : undefined + + property real rightEdgeCenterInset: visible ? parent.width - x : 0 + + Component { + id: photoVideoControlComponent + + PhotoVideoControl { + } + } + } + } // Page 2 + } // QGCSwipeView + + QGCPageIndicator { + id: pageIndicator + count: swipePages.count + currentIndex: swipePages.currentIndex + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.margins: ScreenTools.defaultFontPixelHeight / 4 + + delegate: Rectangle { + height: ScreenTools.defaultFontPixelHeight / 2 + width: height + radius: width / 2 + color: model.index === pageIndicator.currentIndex ? qgcPal.text : qgcPal.button + opacity: model.index === pageIndicator.currentIndex ? 0.9 : 0.3 + } + } + } + } +} + diff --git a/src/FlightDisplay/FlyViewWidgetLayer.qml b/src/FlightDisplay/FlyViewWidgetLayer.qml index 870e8d7dca7..851ec3a5414 100644 --- a/src/FlightDisplay/FlyViewWidgetLayer.qml +++ b/src/FlightDisplay/FlyViewWidgetLayer.qml @@ -59,17 +59,30 @@ Item { leftEdgeTopInset: toolStrip.leftEdgeTopInset leftEdgeCenterInset: toolStrip.leftEdgeCenterInset leftEdgeBottomInset: virtualJoystickMultiTouch.visible ? virtualJoystickMultiTouch.leftEdgeBottomInset : parentToolInsets.leftEdgeBottomInset - rightEdgeTopInset: topRightColumnLayout.rightEdgeTopInset - rightEdgeCenterInset: topRightColumnLayout.rightEdgeCenterInset + rightEdgeTopInset: topRightPanel.rightEdgeTopInset + rightEdgeCenterInset: topRightPanel.rightEdgeCenterInset rightEdgeBottomInset: bottomRightRowLayout.rightEdgeBottomInset topEdgeLeftInset: toolStrip.topEdgeLeftInset topEdgeCenterInset: mapScale.topEdgeCenterInset - topEdgeRightInset: topRightColumnLayout.topEdgeRightInset + topEdgeRightInset: topRightPanel.topEdgeRightInset bottomEdgeLeftInset: virtualJoystickMultiTouch.visible ? virtualJoystickMultiTouch.bottomEdgeLeftInset : parentToolInsets.bottomEdgeLeftInset bottomEdgeCenterInset: bottomRightRowLayout.bottomEdgeCenterInset bottomEdgeRightInset: virtualJoystickMultiTouch.visible ? virtualJoystickMultiTouch.bottomEdgeRightInset : bottomRightRowLayout.bottomEdgeRightInset } + FlyViewTopRightPanel { + id: topRightPanel + anchors.top: parent.top + anchors.right: parent.right + anchors.topMargin: _layoutMargin + anchors.rightMargin: _layoutMargin + maximumHeight: parent.height - (bottomRightRowLayout.height + _margins * 5) + + property real topEdgeRightInset: height + _layoutMargin + property real rightEdgeTopInset: width + _layoutMargin + property real rightEdgeCenterInset: rightEdgeTopInset + } + FlyViewTopRightColumnLayout { id: topRightColumnLayout anchors.margins: _layoutMargin @@ -77,6 +90,7 @@ Item { anchors.bottom: bottomRightRowLayout.top anchors.right: parent.right spacing: _layoutSpacing + visible: !topRightPanel.visible property real topEdgeRightInset: childrenRect.height + _layoutMargin property real rightEdgeTopInset: width + _layoutMargin diff --git a/src/FlightDisplay/GuidedActionsController.qml b/src/FlightDisplay/GuidedActionsController.qml index a8381956a6d..75df33d8b53 100644 --- a/src/FlightDisplay/GuidedActionsController.qml +++ b/src/FlightDisplay/GuidedActionsController.qml @@ -34,8 +34,10 @@ Item { readonly property string emergencyStopTitle: qsTr("EMERGENCY STOP") readonly property string armTitle: qsTr("Arm") + readonly property string mvArmTitle: qsTr("Arm (MV)") readonly property string forceArmTitle: qsTr("Force Arm") readonly property string disarmTitle: qsTr("Disarm") + readonly property string mvDisarmTitle: qsTr("Disarm (MV)") readonly property string rtlTitle: qsTr("Return") readonly property string takeoffTitle: qsTr("Takeoff") readonly property string gripperTitle: qsTr("Gripper Function") @@ -62,12 +64,15 @@ Item { readonly property string changeHeadingTitle: qsTr("Change Heading") readonly property string armMessage: qsTr("Arm the vehicle.") + readonly property string mvArmMessage: qsTr("Arm selected vehicles.") readonly property string forceArmMessage: qsTr("WARNING: This will force arming of the vehicle bypassing any safety checks.") readonly property string disarmMessage: qsTr("Disarm the vehicle") + readonly property string mvDisarmMessage: qsTr("Disarm selected vehicles.") readonly property string emergencyStopMessage: qsTr("WARNING: THIS WILL STOP ALL MOTORS. IF VEHICLE IS CURRENTLY IN THE AIR IT WILL CRASH.") readonly property string takeoffMessage: qsTr("Takeoff from ground and hold position.") - readonly property string gripperMessage: qsTr("Grab or Release the cargo") + readonly property string gripperMessage: qsTr("Grab or Release the cargo") readonly property string startMissionMessage: qsTr("Takeoff from ground and start the current mission.") + readonly property string mvStartMissionMessage: qsTr("Takeoff from ground and start the current mission for selected vehicles.") readonly property string continueMissionMessage: qsTr("Continue the mission from the current waypoint.") readonly property string resumeMissionUploadFailMessage: qsTr("Upload of resume mission failed. Confirm to retry upload") readonly property string landMessage: qsTr("Land the vehicle at the current position.") @@ -80,7 +85,7 @@ Item { readonly property string orbitMessage: qsTr("Orbit the vehicle around the specified location.") readonly property string landAbortMessage: qsTr("Abort the landing sequence.") readonly property string pauseMessage: qsTr("Pause the vehicle at it's current position, adjusting altitude up or down as needed.") - readonly property string mvPauseMessage: qsTr("Pause all vehicles at their current position.") + readonly property string mvPauseMessage: qsTr("Pause selected vehicles at their current position.") readonly property string vtolTransitionFwdMessage: qsTr("Transition VTOL to fixed wing flight.") readonly property string vtolTransitionMRMessage: qsTr("Transition VTOL to multi-rotor flight.") readonly property string roiMessage: qsTr("Make the specified location a Region Of Interest.") @@ -119,6 +124,10 @@ Item { readonly property int actionSetEstimatorOrigin: 28 readonly property int actionSetFlightMode: 29 readonly property int actionChangeHeading: 30 + readonly property int actionMVArm: 31 + readonly property int actionMVDisarm: 32 + + readonly property int customActionStart: 10000 // Custom actions ids should start here so that they don't collide with the built in actions @@ -417,6 +426,11 @@ Item { confirmDialog.message = armMessage confirmDialog.hideTrigger = Qt.binding(function() { return !showArm }) break; + case actionMVArm: + confirmDialog.title = mvArmTitle + confirmDialog.message = mvArmMessage + confirmDialog.hideTrigger = true + break; case actionForceArm: confirmDialog.title = forceArmTitle confirmDialog.message = forceArmMessage @@ -430,6 +444,11 @@ Item { confirmDialog.message = disarmMessage confirmDialog.hideTrigger = Qt.binding(function() { return !showDisarm }) break; + case actionMVDisarm: + confirmDialog.title = mvDisarmTitle + confirmDialog.message = mvDisarmMessage + confirmDialog.hideTrigger = true + break; case actionEmergencyStop: confirmDialog.title = emergencyStopTitle confirmDialog.message = emergencyStopMessage @@ -449,7 +468,7 @@ Item { break; case actionMVStartMission: confirmDialog.title = mvStartMissionTitle - confirmDialog.message = startMissionMessage + confirmDialog.message = mvStartMissionMessage confirmDialog.hideTrigger = true break; case actionContinueMission: @@ -576,7 +595,7 @@ Item { // Executes the specified action function executeAction(actionCode, actionData, sliderOutputValue, optionChecked) { var i; - var rgVehicle; + var selectedVehicles; switch (actionCode) { case actionRTL: _activeVehicle.guidedModeRTL(optionChecked) @@ -597,20 +616,35 @@ Item { _activeVehicle.startMission() break case actionMVStartMission: - rgVehicle = QGroundControl.multiVehicleManager.vehicles - for (i = 0; i < rgVehicle.count; i++) { - rgVehicle.get(i).startMission() + selectedVehicles = QGroundControl.multiVehicleManager.selectedVehicles + for (i = 0; i < selectedVehicles.count; i++) { + var vehicle = selectedVehicles.get(i) + if (vehicle.armed === true){ + vehicle.startMission() + } } break case actionArm: _activeVehicle.armed = true break + case actionMVArm: + selectedVehicles = QGroundControl.multiVehicleManager.selectedVehicles + for (i = 0; i < selectedVehicles.count; i++) { + selectedVehicles.get(i).armed = true + } + break case actionForceArm: _activeVehicle.forceArm() break case actionDisarm: _activeVehicle.armed = false break + case actionMVDisarm: + selectedVehicles = QGroundControl.multiVehicleManager.selectedVehicles + for (i = 0; i < selectedVehicles.count; i++) { + selectedVehicles.get(i).armed = false + } + break case actionEmergencyStop: _activeVehicle.emergencyStop() break @@ -638,9 +672,9 @@ Item { _activeVehicle.guidedModeChangeAltitude(altitudeChangeInMeters, true /* pauseVehicle */) break case actionMVPause: - rgVehicle = QGroundControl.multiVehicleManager.vehicles - for (i = 0; i < rgVehicle.count; i++) { - rgVehicle.get(i).pauseVehicle() + selectedVehicles = QGroundControl.multiVehicleManager.selectedVehicles + for (i = 0; i < selectedVehicles.count; i++) { + selectedVehicles.get(i).pauseVehicle() } break case actionVtolTransitionToFwdFlight: diff --git a/src/FlightDisplay/MultiVehicleList.qml b/src/FlightDisplay/MultiVehicleList.qml index 73b2e56f7ae..95376cd702e 100644 --- a/src/FlightDisplay/MultiVehicleList.qml +++ b/src/FlightDisplay/MultiVehicleList.qml @@ -19,66 +19,102 @@ import QGroundControl.Vehicle import QGroundControl.FlightMap Item { - property real _margin: ScreenTools.defaultFontPixelWidth / 2 - property real _widgetHeight: ScreenTools.defaultFontPixelHeight * 3 - property color _textColor: "black" - property real _rectOpacity: 0.8 - property var _guidedController: globals.guidedControllerFlyView - - QGCPalette { id: qgcPal } - - Rectangle { - id: mvCommands - anchors.left: parent.left - anchors.right: parent.right - height: mvCommandsColumn.height + (_margin *2) - color: qgcPal.missionItemEditor - opacity: _rectOpacity - radius: _margin - - DeadMouseArea { - anchors.fill: parent + property real _margin: ScreenTools.defaultFontPixelWidth / 2 + property real _widgetHeight: ScreenTools.defaultFontPixelHeight * 2 + property var _guidedController: globals.guidedControllerFlyView + property var _activeVehicleColor: "green" + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property var selectedVehicles: QGroundControl.multiVehicleManager.selectedVehicles + + property real innerColumnHeight + + function armAvailable() { + for (var i = 0; i < selectedVehicles.count; i++) { + var vehicle = selectedVehicles.get(i) + if (vehicle.armed === false) { + return true + } } + return false + } - Column { - id: mvCommandsColumn - anchors.margins: _margin - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - spacing: _margin - - QGCLabel { - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("The following commands will be applied to all vehicles") - color: _textColor - wrapMode: Text.WordWrap - font.pointSize: ScreenTools.smallFontPointSize + + function disarmAvailable() { + for (var i = 0; i < selectedVehicles.count; i++) { + var vehicle = selectedVehicles.get(i) + if (vehicle.armed === true) { + return true } + } + return false + } - Row { - spacing: _margin + function startAvailable() { + for (var i = 0; i < selectedVehicles.count; i++) { + var vehicle = selectedVehicles.get(i) + if (vehicle.armed === true && vehicle.flightMode !== vehicle.missionFlightMode){ + return true + } + } + return false + } - QGCButton { - text: qsTr("Pause") - onClicked: _guidedController.confirmAction(_guidedController.actionMVPause) - } + function pauseAvailable() { + for (var i = 0; i < selectedVehicles.count; i++) { + var vehicle = selectedVehicles.get(i) + if (vehicle.armed === true && vehicle.pauseVehicleSupported) { + return true + } + } + return false + } - QGCButton { - text: qsTr("Start Mission") - onClicked: _guidedController.confirmAction(_guidedController.actionMVStartMission) - } + function selectVehicle(vehicleId) { + QGroundControl.multiVehicleManager.selectVehicle(vehicleId) + } + + function deselectVehicle(vehicleId) { + QGroundControl.multiVehicleManager.deselectVehicle(vehicleId) + } + + function toggleSelect(vehicleId) { + if (!vehicleSelected(vehicleId)) { + selectVehicle(vehicleId) + } else { + deselectVehicle(vehicleId) + } + } + + function selectAll() { + var vehicles = QGroundControl.multiVehicleManager.vehicles + for (var i = 0; i < vehicles.count; i++) { + var vehicle = vehicles.get(i) + var vehicleId = vehicle.id + if (!vehicleSelected(vehicleId)) { + selectVehicle(vehicleId) + } + } + } + + function deselectAll() { + QGroundControl.multiVehicleManager.deselectAllVehicles() + } + + function vehicleSelected(vehicleId) { + for (var i = 0; i < selectedVehicles.count; i++ ) { + var currentId = selectedVehicles.get(i).id + if (vehicleId === currentId) { + return true } } + return false } QGCListView { - id: missionItemEditorListView + id: vehicleList anchors.left: parent.left anchors.right: parent.right - anchors.topMargin: _margin - anchors.top: mvCommands.bottom + anchors.top: parent.top anchors.bottom: parent.bottom spacing: ScreenTools.defaultFontPixelHeight / 2 orientation: ListView.Vertical @@ -89,95 +125,80 @@ Item { property real _cacheBuffer: height * 2 delegate: Rectangle { - width: missionItemEditorListView.width - height: innerColumn.y + innerColumn.height + _margin - color: qgcPal.missionItemEditor - opacity: _rectOpacity - radius: _margin + width: vehicleList.width + height: innerColumn.y + innerColumn.height + _margin + color: QGroundControl.multiVehicleManager.activeVehicle == _vehicle ? _activeVehicleColor : qgcPal.button + radius: _margin + border.width: _vehicle && vehicleSelected(_vehicle.id) ? 2 : 0 + border.color: qgcPal.text property var _vehicle: object - ColumnLayout { - id: innerColumn - anchors.margins: _margin - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.left - spacing: _margin + Rectangle { + height: parent.height + width: innerColumn.width + anchors.horizontalCenter: parent.horizontalCenter + color: "transparent" RowLayout { - Layout.fillWidth: true - - QGCLabel { - Layout.alignment: Qt.AlignTop - text: _vehicle ? _vehicle.id : "" - color: _textColor - } - - ColumnLayout { - Layout.alignment: Qt.AlignCenter - spacing: _margin - - FlightModeMenu { - Layout.alignment: Qt.AlignHCenter - font.pointSize: ScreenTools.largeFontPointSize - color: _textColor - currentVehicle: _vehicle - } - - QGCLabel { - Layout.alignment: Qt.AlignHCenter - text: _vehicle && _vehicle.armed ? qsTr("Armed") : qsTr("Disarmed") - color: _textColor - } - } + id: innerColumn + anchors.margins: _margin + spacing: _margin + onHeightChanged: { innerColumnHeight = height + _margin * 2 + spacing * 2 } QGCCompassWidget { - size: _widgetHeight - usedByMultipleVehicleList: true - vehicle: _vehicle + id: compassWidget + size: _widgetHeight + usedByMultipleVehicleList: true + vehicle: _vehicle } - QGCAttitudeWidget { - size: _widgetHeight - vehicle: _vehicle + QGCLabel { + text: " | " + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + Layout.alignment: Qt.AlignHCenter } - } // RowLayout - Row { - spacing: ScreenTools.defaultFontPixelWidth - - QGCButton { - text: qsTr("Arm") - visible: _vehicle && !_vehicle.armed - onClicked: _vehicle.armed = true + QGCLabel { + text: _vehicle ? _vehicle.id : "" + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + Layout.alignment: Qt.AlignHCenter } - QGCButton { - text: qsTr("Start Mission") - visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.missionFlightMode - onClicked: _vehicle.startMission() + QGCLabel { + text: " | " + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + Layout.alignment: Qt.AlignHCenter } - QGCButton { - text: qsTr("Pause") - visible: _vehicle && _vehicle.armed && _vehicle.pauseVehicleSupported - onClicked: _vehicle.pauseVehicle() - } + ColumnLayout { + spacing: _margin + Layout.rightMargin: compassWidget.width / 4 + Layout.alignment: Qt.AlignCenter - QGCButton { - text: qsTr("RTL") - visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.rtlFlightMode - onClicked: _vehicle.flightMode = _vehicle.rtlFlightMode - } + FlightModeMenu { + Layout.alignment: Qt.AlignHCenter + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + currentVehicle: _vehicle + } - QGCButton { - text: qsTr("Take control") - visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.takeControlFlightMode - onClicked: _vehicle.flightMode = _vehicle.takeControlFlightMode + QGCLabel { + Layout.alignment: Qt.AlignHCenter + text: _vehicle && _vehicle.armed ? qsTr("Armed") : qsTr("Disarmed") + color: qgcPal.text + } } - } // Row - } // ColumnLayout - } // delegate - Rectangle - } // QGCListView -} // Item + } + } + + QGCMouseArea { + anchors.fill: parent + onClicked: toggleSelect(_vehicle.id) + } + } + } +} diff --git a/src/FlightMap/Widgets/CompassHeadingIndicator.qml b/src/FlightMap/Widgets/CompassHeadingIndicator.qml index 4be55228c2e..110f4172eca 100644 --- a/src/FlightMap/Widgets/CompassHeadingIndicator.qml +++ b/src/FlightMap/Widgets/CompassHeadingIndicator.qml @@ -23,6 +23,7 @@ Canvas { property real compassSize property real heading + property bool simplified: false property var _qgcPal: QGroundControl.globalPalette @@ -33,7 +34,7 @@ Canvas { onPaint: { var ctx = getContext("2d") - ctx.strokeStyle = _qgcPal.text + ctx.strokeStyle = simplified ? "#EE3424" : _qgcPal.text ctx.fillStyle = "#EE3424" ctx.lineWidth = 1 ctx.beginPath() diff --git a/src/FlightMap/Widgets/QGCCompassWidget.qml b/src/FlightMap/Widgets/QGCCompassWidget.qml index 55ae9ad2b0a..1255edc82be 100644 --- a/src/FlightMap/Widgets/QGCCompassWidget.qml +++ b/src/FlightMap/Widgets/QGCCompassWidget.qml @@ -21,13 +21,16 @@ Rectangle { height: size radius: width / 2 color: qgcPal.window + border.color: qgcPal.text + border.width: usedByMultipleVehicleList ? 1 : 0 + opacity: vehicle && usedByMultipleVehicleList && !vehicle.armed ? 0.5 : 1 property real size: _defaultSize property var vehicle: null property bool usedByMultipleVehicleList: false - property real _defaultSize: ScreenTools.defaultFontPixelHeight * (10) - property real _sizeRatio: ScreenTools.isTinyScreen ? (size / _defaultSize) * 0.5 : size / _defaultSize + property real _defaultSize: usedByMultipleVehicleList ? ScreenTools.defaultFontPixelHeight * 3 : ScreenTools.defaultFontPixelHeight * 10 + property real _sizeRatio: (usedByMultipleVehicleList || ScreenTools.isTinyScreen) ? (size / _defaultSize) * 0.5 : size / _defaultSize property int _fontSize: ScreenTools.defaultFontPointSize * _sizeRatio < 8 ? 8 : ScreenTools.defaultFontPointSize * _sizeRatio property real _heading: vehicle ? vehicle.heading.rawValue : 0 property real _headingToHome: vehicle ? vehicle.headingToHome.rawValue : 0 @@ -36,7 +39,7 @@ Rectangle { property real _courseOverGround: vehicle ? vehicle.gps.courseOverGround.rawValue : 0 property var _flyViewSettings: QGroundControl.settingsManager.flyViewSettings property bool _showAdditionalIndicators: _flyViewSettings.showAdditionalIndicatorsCompass.value && !usedByMultipleVehicleList - property bool _lockNoseUpCompass: _flyViewSettings.lockNoseUpCompass.value + property bool _lockNoseUpCompass: _flyViewSettings.lockNoseUpCompass.value && !usedByMultipleVehicleList function showCOG(){ if (_groundSpeed < 0.5) { @@ -75,12 +78,14 @@ Rectangle { } CompassDial { - anchors.fill: parent + anchors.fill: parent + visible: !usedByMultipleVehicleList } CompassHeadingIndicator { compassSize: size heading: _heading + simplified: usedByMultipleVehicleList } Image { @@ -144,7 +149,7 @@ Rectangle { QGCLabel { anchors.horizontalCenter: parent.horizontalCenter y: size * 0.74 - text: vehicle ? _heading.toFixed(0) + "°" : "" + text: vehicle && !usedByMultipleVehicleList ? _heading.toFixed(0) + "°" : "" horizontalAlignment: Text.AlignHCenter } } diff --git a/src/QmlControls/MvPanelPage.qml b/src/QmlControls/MvPanelPage.qml new file mode 100644 index 00000000000..9c35438eb6b --- /dev/null +++ b/src/QmlControls/MvPanelPage.qml @@ -0,0 +1,28 @@ +import QtQuick +import QtQuick.Controls + +import QGroundControl +import QGroundControl.ScreenTools +import QGroundControl.Palette + +Item { + property bool showBorder: true + property real contentMargin: ScreenTools.defaultFontPixelHeight + default property alias contentChildren: contentContainer.data + + Rectangle { + color: "transparent" + anchors.fill: parent + border.color: QGroundControl.globalPalette.groupBorder + border.width: showBorder ? 1 : 0 + radius: ScreenTools.defaultFontPixelHeight / 3 + + Item { + id: contentContainer + anchors.fill: parent + anchors.margins: contentMargin / 2 + anchors.bottomMargin: contentMargin * 2 + } + } + +} diff --git a/src/QmlControls/QGCPageIndicator.qml b/src/QmlControls/QGCPageIndicator.qml new file mode 100644 index 00000000000..a4eef9bb535 --- /dev/null +++ b/src/QmlControls/QGCPageIndicator.qml @@ -0,0 +1,6 @@ +import QtQuick +import QtQuick.Controls + +PageIndicator { + +} diff --git a/src/QmlControls/QGCSwipeView.qml b/src/QmlControls/QGCSwipeView.qml new file mode 100644 index 00000000000..f6fae4e9da7 --- /dev/null +++ b/src/QmlControls/QGCSwipeView.qml @@ -0,0 +1,7 @@ +import QtQuick +import QtQuick.Controls + + +SwipeView { + +} diff --git a/src/QmlControls/QGroundControl/Controls/qmldir b/src/QmlControls/QGroundControl/Controls/qmldir index 142d5eb1061..3f15ed111b5 100644 --- a/src/QmlControls/QGroundControl/Controls/qmldir +++ b/src/QmlControls/QGroundControl/Controls/qmldir @@ -49,6 +49,7 @@ MissionItemEditor 1.0 MissionItemEditor.qml MissionItemIndexLabel 1.0 MissionItemIndexLabel.qml MissionItemMapVisual 1.0 MissionItemMapVisual.qml MissionItemStatus 1.0 MissionItemStatus.qml +MvPanelPage 1.0 MvPanelPage.qml OfflineMapButton 1.0 OfflineMapButton.qml OfflineMapEditor 1.0 OfflineMapEditor.qml OfflineMapInfo 1.0 OfflineMapInfo.qml @@ -84,11 +85,13 @@ QGCMenuSeparator 1.0 QGCMenuSeparator.qml QGCMouseArea 1.0 QGCMouseArea.qml QGCMovableItem 1.0 QGCMovableItem.qml QGCOptionsComboBox 1.0 QGCOptionsComboBox.qml +QGCPageIndicator 1.0 QGCPageIndicator.qml QGCPopupDialog 1.0 QGCPopupDialog.qml QGCRadioButton 1.0 QGCRadioButton.qml QGCRoundButton 1.0 QGCRoundButton.qml QGCSimpleMessageDialog 1.0 QGCSimpleMessageDialog.qml QGCSlider 1.0 QGCSlider.qml +QGCSwipeView 1.0 QGCSwipeView.qml QGCSwitch 1.0 QGCSwitch.qml QGCTabBar 1.0 QGCTabBar.qml QGCTabButton 1.0 QGCTabButton.qml diff --git a/src/QmlControls/QGroundControl/FlightDisplay/qmldir b/src/QmlControls/QGroundControl/FlightDisplay/qmldir index 888f82ab0f1..79d0a3c817f 100644 --- a/src/QmlControls/QGroundControl/FlightDisplay/qmldir +++ b/src/QmlControls/QGroundControl/FlightDisplay/qmldir @@ -16,6 +16,7 @@ FlyViewToolBarIndicators 1.0 FlyViewToolBarIndicators.qml FlyViewToolStrip 1.0 FlyViewToolStrip.qml FlyViewToolStripActionList 1.0 FlyViewToolStripActionList.qml FlyViewTopRightColumnLayout 1.0 FlyViewTopRightColumnLayout.qml +FlyViewTopRightPanel 1.0 FlyViewTopRightPanel.qml FlyViewVideo 1.0 FlyViewVideo.qml FlyViewWidgetLayer 1.0 FlyViewWidgetLayer.qml GripperMenu 1.0 GripperMenu.qml diff --git a/src/Settings/App.SettingsGroup.json b/src/Settings/App.SettingsGroup.json index 3f1b3412584..17a585f0f95 100644 --- a/src/Settings/App.SettingsGroup.json +++ b/src/Settings/App.SettingsGroup.json @@ -138,6 +138,13 @@ "type": "bool", "default": false }, +{ + "name": "enableMultiVehiclePanel", + "shortDesc": "Enable Multi-Vehicle Panel", + "longDesc": "Enable Multi-Vehicle Panel when multiple vehicles are connected.", + "type": "bool", + "default": true +}, { "name": "appFontPointSize", "shortDesc": "Application font size", diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index daae122795a..708c23e08a3 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -157,6 +157,7 @@ DECLARE_SETTINGSFACT(AppSettings, savePath) DECLARE_SETTINGSFACT(AppSettings, androidSaveToSDCard) DECLARE_SETTINGSFACT(AppSettings, useChecklist) DECLARE_SETTINGSFACT(AppSettings, enforceChecklist) +DECLARE_SETTINGSFACT(AppSettings, enableMultiVehiclePanel) DECLARE_SETTINGSFACT(AppSettings, mapboxToken) DECLARE_SETTINGSFACT(AppSettings, mapboxAccount) DECLARE_SETTINGSFACT(AppSettings, mapboxStyle) diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index c6a10f9693f..63e7216920f 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -43,6 +43,7 @@ class AppSettings : public SettingsGroup DEFINE_SETTINGFACT(androidSaveToSDCard) DEFINE_SETTINGFACT(useChecklist) DEFINE_SETTINGFACT(enforceChecklist) + DEFINE_SETTINGFACT(enableMultiVehiclePanel) DEFINE_SETTINGFACT(mapboxToken) DEFINE_SETTINGFACT(mapboxAccount) DEFINE_SETTINGFACT(mapboxStyle) diff --git a/src/UI/preferences/FlyViewSettings.qml b/src/UI/preferences/FlyViewSettings.qml index 560982c0188..3e07a88a050 100644 --- a/src/UI/preferences/FlyViewSettings.qml +++ b/src/UI/preferences/FlyViewSettings.qml @@ -28,6 +28,7 @@ SettingsPage { property var _customMavlinkActionsSettings: _settingsManager.customMavlinkActionsSettings property Fact _virtualJoystick: _settingsManager.appSettings.virtualJoystick property Fact _virtualJoystickAutoCenterThrottle: _settingsManager.appSettings.virtualJoystickAutoCenterThrottle + property Fact _enableMultiVehiclePanel: _settingsManager.appSettings.enableMultiVehiclePanel property Fact _showAdditionalIndicatorsCompass: _flyViewSettings.showAdditionalIndicatorsCompass property Fact _lockNoseUpCompass: _flyViewSettings.lockNoseUpCompass property Fact _guidedMinimumAltitude: _flyViewSettings.guidedMinimumAltitude @@ -69,6 +70,13 @@ SettingsPage { property Fact _enforceChecklist: _settingsManager.appSettings.enforceChecklist } + FactCheckBoxSlider { + Layout.fillWidth: true + text: qsTr("Enable Multi-Vehicle Panel") + fact: _enableMultiVehiclePanel + visible: _enableMultiVehiclePanel.visible + } + FactCheckBoxSlider { Layout.fillWidth: true text: qsTr("Keep Map Centered On Vehicle") diff --git a/src/UI/toolbar/MultiVehicleSelector.qml b/src/UI/toolbar/MultiVehicleSelector.qml index 215e07efb6a..5881dd1baa6 100644 --- a/src/UI/toolbar/MultiVehicleSelector.qml +++ b/src/UI/toolbar/MultiVehicleSelector.qml @@ -7,61 +7,112 @@ * ****************************************************************************/ - import QtQuick import QtQuick.Controls +import QtQuick.Layouts import QGroundControl +import QGroundControl.FactSystem +import QGroundControl.FactControls import QGroundControl.Controls import QGroundControl.MultiVehicleManager import QGroundControl.ScreenTools import QGroundControl.Palette -//------------------------------------------------------------------------- -//-- Multiple Vehicle Selector -QGCComboBox { - anchors.verticalCenter: parent.verticalCenter - font.pointSize: ScreenTools.mediumFontPointSize - currentIndex: -1 - sizeToContents: true - model: _vehicleModel - - property bool showIndicator: _multipleVehicles +RowLayout { + id: control + spacing: 0 - property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - property bool _multipleVehicles: QGroundControl.multiVehicleManager.vehicles.count > 1 - property var _vehicleModel: [ ] + property bool showIndicator: _multipleVehicles + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool _multipleVehicles: QGroundControl.multiVehicleManager.vehicles.count > 1 + property var _vehicleModel: [ ] Connections { target: QGroundControl.multiVehicleManager.vehicles - onCountChanged: _updateVehicleModel() + onCountChanged: _updateVehicleModel() } Component.onCompleted: _updateVehicleModel() on_ActiveVehicleChanged: _updateVehicleModel() + RowLayout { + Layout.fillWidth: true + + QGCColoredImage { + width: ScreenTools.defaultFontPixelWidth * 4 + height: ScreenTools.defaultFontPixelHeight * 1.33 + fillMode: Image.PreserveAspectFit + mipmap: true + color: qgcPal.text + source: "/InstrumentValueIcons/airplane.svg" + } + + QGCLabel { + text: _activeVehicle ? qsTr("Vehicle") + " " + _activeVehicle.id : qsTr("N/A") + font.pointSize: ScreenTools.mediumFontPointSize + Layout.alignment: Qt.AlignCenter + + MouseArea { + anchors.fill: parent + onClicked: mainWindow.showIndicatorDrawer(vehicleSelectorDrawer, control) + } + } + } + + Component { + id: vehicleSelectorDrawer + + ToolIndicatorPage { + showExpand: true + + contentComponent: Component { + ColumnLayout { + spacing: ScreenTools.defaultFontPixelWidth / 2 + + Repeater { + model: _vehicleModel + + QGCButton { + text: modelData + Layout.fillWidth: true + + onClicked: { + var vehicleId = modelData.split(" ")[1] + var vehicle = QGroundControl.multiVehicleManager.getVehicleById(vehicleId) + QGroundControl.multiVehicleManager.activeVehicle = vehicle + mainWindow.closeIndicatorDrawer() + } + } + } + } + } + + expandedComponent: Component { + SettingsGroupLayout { + Layout.fillWidth: true + + FactCheckBoxSlider { + Layout.fillWidth: true + text: qsTr("Enable Multi-Vehicle Panel") + fact: _enableMultiVehiclePanel + visible: _enableMultiVehiclePanel.visible + + property Fact _enableMultiVehiclePanel: QGroundControl.settingsManager.appSettings.enableMultiVehiclePanel + } + } + } + } + } + function _updateVehicleModel() { - var newCurrentIndex = -1 var newModel = [ ] if (_multipleVehicles) { for (var i = 0; i < QGroundControl.multiVehicleManager.vehicles.count; i++) { var vehicle = QGroundControl.multiVehicleManager.vehicles.get(i) newModel.push(qsTr("Vehicle") + " " + vehicle.id) - - if (vehicle.id === _activeVehicle.id) { - newCurrentIndex = i - } } } - currentIndex = -1 _vehicleModel = newModel - currentIndex = newCurrentIndex - } - - onActivated: (index) => { - var vehicleId = textAt(index).split(" ")[1] - var vehicle = QGroundControl.multiVehicleManager.getVehicleById(vehicleId) - QGroundControl.multiVehicleManager.activeVehicle = vehicle } } - diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 52cf5418f53..060cc8f71e8 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -42,6 +42,7 @@ MultiVehicleManager::MultiVehicleManager(QObject *parent) , _gcsHeartbeatTimer(new QTimer(this)) , _offlineEditingVehicle(new Vehicle(Vehicle::MAV_AUTOPILOT_TRACK, Vehicle::MAV_TYPE_TRACK, this)) , _vehicles(new QmlObjectListModel(this)) + , _selectedVehicles(new QmlObjectListModel(this)) { // qCDebug(MultiVehicleManagerLog) << Q_FUNC_INFO << this; } @@ -206,6 +207,8 @@ void MultiVehicleManager::_deleteVehiclePhase1(Vehicle *vehicle) qCWarning(MultiVehicleManagerLog) << "Vehicle not found in map!"; } + deselectVehicle(vehicle->id()); + _setActiveVehicleAvailable(false); _setParameterReadyVehicleAvailable(false); emit vehicleRemoved(vehicle); @@ -333,6 +336,42 @@ void MultiVehicleManager::_sendGCSHeartbeat() } } +void MultiVehicleManager::selectVehicle(int vehicleId) +{ + if(!_vehicleSelected(vehicleId)) { + Vehicle *const vehicle = getVehicleById(vehicleId); + _selectedVehicles->append(vehicle); + return; + } +} + +void MultiVehicleManager::deselectVehicle(int vehicleId) +{ + for (int i = 0; i < _selectedVehicles->count(); i++) { + Vehicle *const vehicle = qobject_cast(_selectedVehicles->get(i)); + if (vehicle->id() == vehicleId) { + _selectedVehicles->removeAt(i); + return; + } + } +} + +void MultiVehicleManager::deselectAllVehicles() +{ + _selectedVehicles->clear(); +} + +bool MultiVehicleManager::_vehicleSelected(int vehicleId) +{ + for (int i = 0; i < _selectedVehicles->count(); i++) { + Vehicle *const vehicle = qobject_cast(_selectedVehicles->get(i)); + if (vehicle->id() == vehicleId) { + return true; + } + } + return false; +} + Vehicle *MultiVehicleManager::getVehicleById(int vehicleId) const { for (int i = 0; i < _vehicles->count(); i++) { diff --git a/src/Vehicle/MultiVehicleManager.h b/src/Vehicle/MultiVehicleManager.h index fc4cf5057d4..389754d42a0 100644 --- a/src/Vehicle/MultiVehicleManager.h +++ b/src/Vehicle/MultiVehicleManager.h @@ -33,6 +33,7 @@ class MultiVehicleManager : public QObject Q_PROPERTY(bool parameterReadyVehicleAvailable READ _getParameterReadyVehicleAvailable NOTIFY parameterReadyVehicleAvailableChanged) Q_PROPERTY(Vehicle *activeVehicle READ activeVehicle WRITE setActiveVehicle NOTIFY activeVehicleChanged) Q_PROPERTY(QmlObjectListModel *vehicles READ vehicles CONSTANT) + Q_PROPERTY(QmlObjectListModel *selectedVehicles READ selectedVehicles CONSTANT) Q_PROPERTY(bool gcsHeartBeatEnabled READ _getGcsHeartbeatEnabled WRITE _setGcsHeartbeatEnabled NOTIFY gcsHeartBeatEnabledChanged) Q_PROPERTY(Vehicle *offlineEditingVehicle READ offlineEditingVehicle CONSTANT) @@ -45,7 +46,11 @@ class MultiVehicleManager : public QObject void init(); Q_INVOKABLE Vehicle *getVehicleById(int vehicleId) const; + Q_INVOKABLE void selectVehicle(int vehicleId); + Q_INVOKABLE void deselectVehicle(int vehicleId); + Q_INVOKABLE void deselectAllVehicles(); QmlObjectListModel *vehicles() const { return _vehicles; } + QmlObjectListModel *selectedVehicles() const { return _selectedVehicles; } Vehicle *offlineEditingVehicle() const { return _offlineEditingVehicle; } Vehicle *activeVehicle() const { return _activeVehicle; } void setActiveVehicle(Vehicle *vehicle); @@ -69,6 +74,7 @@ private slots: private: bool _vehicleExists(int vehicleId); + bool _vehicleSelected(int vehicleId); void _setActiveVehicle(Vehicle *vehicle); bool _getGcsHeartbeatEnabled() const { return _gcsHeartbeatEnabled; } void _setGcsHeartbeatEnabled(bool gcsHeartBeatEnabled); @@ -80,6 +86,7 @@ private slots: QTimer *_gcsHeartbeatTimer = nullptr; ///< Timer to emit heartbeats Vehicle *_offlineEditingVehicle = nullptr; ///< Disconnected vechicle used for offline editing QmlObjectListModel *_vehicles = nullptr; + QmlObjectListModel *_selectedVehicles = nullptr; bool _activeVehicleAvailable = false; ///< true: An active vehicle is available bool _gcsHeartbeatEnabled = false; ///< Enabled/disable heartbeat emission bool _parameterReadyVehicleAvailable = false; ///< true: An active vehicle with ready parameters is available