From 6315decc2b04654ebac2a24867194750f3f9da2b Mon Sep 17 00:00:00 2001 From: Ahmet Kaan Demirci Date: Sun, 6 Oct 2024 13:40:55 +0300 Subject: [PATCH 1/3] Multi Vehicle Panel Overhaul --- qgroundcontrol.qrc | 1 + src/FlightDisplay/FlyViewTopRightPanel.qml | 218 +++++++++++ src/FlightDisplay/FlyViewWidgetLayer.qml | 24 +- src/FlightDisplay/MultiVehicleList.qml | 359 ++++++++++++------ .../Widgets/CompassHeadingIndicator.qml | 3 +- src/FlightMap/Widgets/QGCCompassWidget.qml | 11 +- .../QGroundControl/FlightDisplay/qmldir | 1 + 7 files changed, 486 insertions(+), 131 deletions(-) create mode 100644 src/FlightDisplay/FlyViewTopRightPanel.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 61516930996..e26529bf5c6 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -351,6 +351,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/FlyViewTopRightPanel.qml b/src/FlightDisplay/FlyViewTopRightPanel.qml new file mode 100644 index 00000000000..a7eaff47231 --- /dev/null +++ b/src/FlightDisplay/FlyViewTopRightPanel.qml @@ -0,0 +1,218 @@ +/**************************************************************************** + * + * (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.Controls +import QGroundControl.FlightDisplay +import QGroundControl.FlightMap +import QGroundControl.Palette +import QGroundControl.ScreenTools + + +Item { + + Rectangle { + id: topRightPanel + anchors.fill: parent + width: parent.width + height: parent.height + color: qgcPal.toolbarBackground + visible: !QGroundControl.videoManager.fullScreen && togglePanelBtn.checked + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + + QGCPalette { id: qgcPal } + + MultiVehicleList { + id: multiVehicleList + anchors.top: parent.top + anchors.bottom: parent.verticalCenter + anchors.right: parent.right + anchors.left: parent.left + anchors.margins: _toolsMargin + } + + TerrainProgress { + id: terrainProgress + anchors.top: multiVehicleList.bottom + } + + ColumnLayout { + id: selectionGroup + anchors.top: terrainProgress.bottom + anchors.right: parent.right + anchors.left: parent.left + anchors.margins: _margins + Layout.alignment: Qt.AlignHCenter + + QGCLabel { + text: qsTr("Multi Vehicle Selection") + } + + QGCFlickable { + Layout.fillWidth: true + height: ScreenTools.defaultFontPixelWidth * 6 + contentWidth: selectionRowLayout.width + flickableDirection: Flickable.HorizontalFlick + + RowLayout { + id: selectionRowLayout + spacing: _margins + + QGCButton { + text: qsTr("Select All") + enabled: multiVehicleList.getSelectedVehicles().length !== QGroundControl.multiVehicleManager.vehicles.count + onClicked: multiVehicleList.selectAll() + } + + QGCButton { + text: qsTr("Deselect All") + enabled: multiVehicleList.getSelectedVehicles().length > 0 + onClicked: multiVehicleList.deselectAll() + } + + QGCButton { + text: qsTr("Activate") + enabled: multiVehicleList.getSelectedVehicles().length === 1 && _activeVehicle !== multiVehicleList.getSelectedVehicles()[0] + onClicked: multiVehicleList.activateVehicle() + } + + } + } + + } + + ColumnLayout { + id: actionGroup + anchors.top: selectionGroup.bottom + anchors.right: parent.right + anchors.left: parent.left + anchors.margins: _margins + + QGCLabel { + text: qsTr("Multi Vehicle Actions") + } + + QGCFlickable { + Layout.fillWidth: true + height: ScreenTools.defaultFontPixelWidth * 6 + contentWidth: actionRowLayout.width + flickableDirection: Flickable.HorizontalFlick + + RowLayout { + id: actionRowLayout + spacing: _margins + + QGCButton { + text: qsTr("Arm") + enabled: multiVehicleList.armAvailable() + onClicked: multiVehicleList.armSelectedVehicles() + } + + QGCButton { + text: qsTr("Disarm") + enabled: multiVehicleList.disarmAvailable() + onClicked: multiVehicleList.disarmSelectedVehicles() + } + + QGCButton { + text: qsTr("Start") + enabled: multiVehicleList.startAvailable() + onClicked: multiVehicleList.startSelectedVehicles() + } + + QGCButton { + text: qsTr("Pause") + enabled: multiVehicleList.pauseAvailable() + onClicked: multiVehicleList.pauseSelectedVehicles() + } + } + } + } + + ColumnLayout { + id: vehicleModeGroup + anchors.top: actionGroup.bottom + anchors.right: parent.right + anchors.left: parent.left + anchors.margins: _margins + Layout.alignment: Qt.AlignHCenter + + QGCLabel { + text: qsTr("Multi Vehicle Modes") + } + + QGCFlickable { + Layout.fillWidth: true + height: ScreenTools.defaultFontPixelWidth * 6 + contentWidth: vehicleModeRowLayout.width + flickableDirection: Flickable.HorizontalFlick + + RowLayout { + id: vehicleModeRowLayout + spacing: _margins + + QGCButton { + text: qsTr("RTL") + enabled: multiVehicleList.rtlAvailable() + onClicked: multiVehicleList.rtlSelectedVehicles() + } + + QGCButton { + text: qsTr("Take control") + enabled: multiVehicleList.takeControlAvailable() + onClicked: multiVehicleList.takeControlSelectedVehicles() + } + + } + } + + } + + } + + QGCButton { + id: togglePanelBtn + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: topRightPanel.visible ? parent.height : 0 + width: _rightPanelWidth / 5 + height: _rightPanelWidth / 18 + checkable: true + + background: Rectangle { + radius: parent.height / 2 + color: qgcPal.toolbarBackground + border.color: qgcPal.text + border.width: 1 + } + } + + // 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.top: togglePanelBtn.bottom + sourceComponent: globals.activeVehicle && !togglePanelBtn.checked ? photoVideoControlComponent : undefined + + property real rightEdgeCenterInset: visible ? parent.width - x : 0 + + Component { + id: photoVideoControlComponent + + PhotoVideoControl { + } + } + } + +} diff --git a/src/FlightDisplay/FlyViewWidgetLayer.qml b/src/FlightDisplay/FlyViewWidgetLayer.qml index 55f8341dc3c..b11d99896d9 100644 --- a/src/FlightDisplay/FlyViewWidgetLayer.qml +++ b/src/FlightDisplay/FlyViewWidgetLayer.qml @@ -46,7 +46,7 @@ Item { property real _margins: ScreenTools.defaultFontPixelWidth / 2 property real _toolsMargin: ScreenTools.defaultFontPixelWidth * 0.75 property rect _centerViewport: Qt.rect(0, 0, width, height) - property real _rightPanelWidth: ScreenTools.defaultFontPixelWidth * 30 + property real _rightPanelWidth: ScreenTools.defaultFontPixelWidth * 35 property alias _gripperMenu: gripperOptions property real _layoutMargin: ScreenTools.defaultFontPixelWidth * 0.75 property bool _layoutSpacing: ScreenTools.defaultFontPixelWidth @@ -59,26 +59,26 @@ 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 } - FlyViewTopRightColumnLayout { - id: topRightColumnLayout - anchors.margins: _layoutMargin - anchors.top: parent.top - anchors.bottom: bottomRightRowLayout.top - anchors.right: parent.right - spacing: _layoutSpacing + FlyViewTopRightPanel { + id: topRightPanel + anchors.top: parent.top + anchors.right: parent.right + anchors.rightMargin: _layoutMargin + width: _rightPanelWidth + height: _rightPanelWidth * 2 - property real topEdgeRightInset: childrenRect.height + _layoutMargin + property real topEdgeRightInset: height + _layoutMargin property real rightEdgeTopInset: width + _layoutMargin property real rightEdgeCenterInset: rightEdgeTopInset } diff --git a/src/FlightDisplay/MultiVehicleList.qml b/src/FlightDisplay/MultiVehicleList.qml index 73b2e56f7ae..e4f04a9650b 100644 --- a/src/FlightDisplay/MultiVehicleList.qml +++ b/src/FlightDisplay/MultiVehicleList.qml @@ -19,66 +19,218 @@ 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 + 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 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 + ListModel { + id: selectedVehiclesModel + } + + function getSelectedVehicles() { + var selectedVehicles = [ ] + forEachSelectedVehicle(function(vehicle) { + selectedVehicles.push(vehicle) + }) + return selectedVehicles + } + + function getVehicleIndex(vehicleId) { + for (var i = 0; i < selectedVehiclesModel.count; i++) { + if (selectedVehiclesModel.get(i).id === vehicleId) { + return i + } } + return -1 + } + + function forEachSelectedVehicle(action) { + for (var i = 0; i < selectedVehiclesModel.count; i++) { + var vehicleId = selectedVehiclesModel.get(i).id + var vehicle = QGroundControl.multiVehicleManager.getVehicleById(vehicleId) + action(vehicle) + } + } + + function armSelectedVehicles() { + forEachSelectedVehicle(function(vehicle) { + vehicle.armed = true + console.log("vehicle " + vehicle.id + " has been armed.") + }) + } - 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 armAvailable() { + var available = false + forEachSelectedVehicle(function(vehicle) { + if(vehicle.armed === false){ + available = true } + }) + return available + } - Row { - spacing: _margin + function disarmSelectedVehicles() { + forEachSelectedVehicle(function(vehicle) { + vehicle.armed = false + console.log("vehicle " + vehicle.id + " has been disarmed.") + }) + } - QGCButton { - text: qsTr("Pause") - onClicked: _guidedController.confirmAction(_guidedController.actionMVPause) - } + function disarmAvailable() { + var available = false + forEachSelectedVehicle(function(vehicle) { + if(vehicle.armed === true){ + available = true + } + }) + return available + } - QGCButton { - text: qsTr("Start Mission") - onClicked: _guidedController.confirmAction(_guidedController.actionMVStartMission) - } + function startSelectedVehicles() { + forEachSelectedVehicle(function(vehicle) { + if (vehicle.armed === true){ + vehicle.startMission() + console.log("mission started for " + vehicle.id) + } + + }) + } + + function startAvailable() { + var available = false + forEachSelectedVehicle(function(vehicle) { + if(vehicle.armed === true && vehicle.flightMode !== vehicle.missionFlightMode){ + available = true + } + }) + return available + } + + function pauseSelectedVehicles() { + forEachSelectedVehicle(function(vehicle) { + if(vehicle.pauseVehicleSupported){ + vehicle.pauseVehicle() + console.log("mission paused for " + vehicle.id) + } + }) + } + function pauseAvailable() { + var available = false + forEachSelectedVehicle(function(vehicle) { + if(vehicle.armed === true && vehicle.pauseVehicleSupported){ + available = true + } + }) + return available + } + + function rtlSelectedVehicles() { + forEachSelectedVehicle(function(vehicle) { + if (vehicle.armed === true){ + vehicle.flightMode = vehicle.rtlFlightMode + console.log("vehicle " + vehicle.id + " returning to launch") + } + }) + } + + function rtlAvailable(){ + var available = false + forEachSelectedVehicle(function(vehicle) { + if(vehicle.armed === true && vehicle.flightMode !== vehicle.rtlFlightMode){ + available = true + } + }) + return available + } + + function takeControlSelectedVehicles() { + forEachSelectedVehicle(function(vehicle) { + vehicle.flightMode = vehicle.takeControlFlightMode + console.log("taking control for " + vehicle.id) + }) + } + + function takeControlAvailable(){ + var available = false + forEachSelectedVehicle(function(vehicle) { + if(vehicle.armed === true && vehicle.flightMode !== vehicle.takeControlFlightMode){ + available = true + } + }) + return available + } + + function activateVehicle() { + if (selectedVehiclesModel.count === 1){ + var vehicle = getSelectedVehicles()[0] + QGroundControl.multiVehicleManager.activeVehicle = vehicle + } + } + + 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 (getVehicleIndex(vehicleId) === -1){ + selectVehicle(vehicleId) + } + } + printSelectedVehicles() + } + + function deselectAll() { + selectedVehiclesModel.clear() + } + + function isSelected(vehicleId) { + for (var i = 0; i < selectedVehiclesModel.count; i++) { + if (selectedVehiclesModel.get(i).id === vehicleId) { + return true } } + return false + } + + + function selectVehicle(vehicleId) { + selectedVehiclesModel.append({ id: vehicleId }) + } + + function deselectVehicle(vehicleId) { + var index = getVehicleIndex(vehicleId) + if (index !== -1) { + selectedVehiclesModel.remove(index) + } + } + + function toggleSelect(vehicleId) { + if (getVehicleIndex(vehicleId) !== -1) { + deselectVehicle(vehicleId) + } else { + selectVehicle(vehicleId) + } + printSelectedVehicles() + } + + function printSelectedVehicles() { + var vehicles = [ ] + forEachSelectedVehicle(function(vehicle) { + vehicles.push(vehicle.id) + }) + console.log(vehicles) } 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 +241,72 @@ 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: isSelected(_vehicle.id) ? 1 : 0 + border.color: qgcPal.text property var _vehicle: object - ColumnLayout { + RowLayout { id: innerColumn anchors.margins: _margin anchors.top: parent.top anchors.left: parent.left - anchors.right: parent.left spacing: _margin + Layout.fillWidth: true - RowLayout { - Layout.fillWidth: true - - QGCLabel { - Layout.alignment: Qt.AlignTop - text: _vehicle ? _vehicle.id : "" - color: _textColor - } + QGCCompassWidget { + size: _widgetHeight + usedByMultipleVehicleList: true + vehicle: _vehicle + } - 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 - } - } + QGCLabel { + text: " | " + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + } - QGCCompassWidget { - size: _widgetHeight - usedByMultipleVehicleList: true - vehicle: _vehicle - } + QGCLabel { + text: _vehicle ? _vehicle.id : "" + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + } - QGCAttitudeWidget { - size: _widgetHeight - vehicle: _vehicle - } - } // RowLayout + QGCLabel { + text: " | " + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + } - Row { - spacing: ScreenTools.defaultFontPixelWidth + ColumnLayout { + Layout.alignment: Qt.AlignCenter + spacing: _margin - QGCButton { - text: qsTr("Arm") - visible: _vehicle && !_vehicle.armed - onClicked: _vehicle.armed = true + FlightModeMenu { + Layout.alignment: Qt.AlignHCenter + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + currentVehicle: _vehicle } - QGCButton { - text: qsTr("Start Mission") - visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.missionFlightMode - onClicked: _vehicle.startMission() + QGCLabel { + Layout.alignment: Qt.AlignHCenter + text: _vehicle && _vehicle.armed ? qsTr("Armed") : qsTr("Disarmed") + color: qgcPal.text } + } - QGCButton { - text: qsTr("Pause") - visible: _vehicle && _vehicle.armed && _vehicle.pauseVehicleSupported - onClicked: _vehicle.pauseVehicle() - } + } - QGCButton { - text: qsTr("RTL") - visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.rtlFlightMode - onClicked: _vehicle.flightMode = _vehicle.rtlFlightMode - } + QGCMouseArea { + anchors.fill: parent + onClicked: multiVehicleList.toggleSelect(_vehicle.id) + } - QGCButton { - text: qsTr("Take control") - visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.takeControlFlightMode - onClicked: _vehicle.flightMode = _vehicle.takeControlFlightMode - } - } // Row - } // ColumnLayout - } // delegate - Rectangle - } // QGCListView -} // Item + } + } +} 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..18c81c2456e 100644 --- a/src/FlightMap/Widgets/QGCCompassWidget.qml +++ b/src/FlightMap/Widgets/QGCCompassWidget.qml @@ -21,12 +21,15 @@ 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 _defaultSize: usedByMultipleVehicleList ? ScreenTools.defaultFontPixelHeight * 3 : ScreenTools.defaultFontPixelHeight * 10 property real _sizeRatio: 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 @@ -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/QGroundControl/FlightDisplay/qmldir b/src/QmlControls/QGroundControl/FlightDisplay/qmldir index 4340d2b3e96..ffff9bc2914 100644 --- a/src/QmlControls/QGroundControl/FlightDisplay/qmldir +++ b/src/QmlControls/QGroundControl/FlightDisplay/qmldir @@ -12,6 +12,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 FlyViewInsetViewer 1.0 FlyViewInsetViewer.qml From 85eff5f3b7583356932fce54ed12a34302c0cbfe Mon Sep 17 00:00:00 2001 From: Ahmet Kaan Demirci Date: Sun, 27 Oct 2024 13:38:16 +0300 Subject: [PATCH 2/3] Add guided actions capability to the multi vehicle panel && rework on the implementation logic of the mv panel selection --- src/FlightDisplay/FlyViewTopRightPanel.qml | 57 +---- src/FlightDisplay/GuidedActionsController.qml | 54 +++- src/FlightDisplay/MultiVehicleList.qml | 230 +++++------------- src/Vehicle/MultiVehicleManager.cc | 35 +++ src/Vehicle/MultiVehicleManager.h | 7 + 5 files changed, 160 insertions(+), 223 deletions(-) diff --git a/src/FlightDisplay/FlyViewTopRightPanel.qml b/src/FlightDisplay/FlyViewTopRightPanel.qml index a7eaff47231..64b19c6329a 100644 --- a/src/FlightDisplay/FlyViewTopRightPanel.qml +++ b/src/FlightDisplay/FlyViewTopRightPanel.qml @@ -70,22 +70,16 @@ Item { QGCButton { text: qsTr("Select All") - enabled: multiVehicleList.getSelectedVehicles().length !== QGroundControl.multiVehicleManager.vehicles.count + enabled: multiVehicleList.selectedVehicles && multiVehicleList.selectedVehicles.count !== QGroundControl.multiVehicleManager.vehicles.count onClicked: multiVehicleList.selectAll() } QGCButton { text: qsTr("Deselect All") - enabled: multiVehicleList.getSelectedVehicles().length > 0 + enabled: multiVehicleList.selectedVehicles && multiVehicleList.selectedVehicles.count > 0 onClicked: multiVehicleList.deselectAll() } - QGCButton { - text: qsTr("Activate") - enabled: multiVehicleList.getSelectedVehicles().length === 1 && _activeVehicle !== multiVehicleList.getSelectedVehicles()[0] - onClicked: multiVehicleList.activateVehicle() - } - } } @@ -115,68 +109,31 @@ Item { QGCButton { text: qsTr("Arm") enabled: multiVehicleList.armAvailable() - onClicked: multiVehicleList.armSelectedVehicles() + onClicked: _guidedController.confirmAction(_guidedController.actionMVArm) } QGCButton { text: qsTr("Disarm") enabled: multiVehicleList.disarmAvailable() - onClicked: multiVehicleList.disarmSelectedVehicles() + onClicked: _guidedController.confirmAction(_guidedController.actionMVDisarm) } QGCButton { text: qsTr("Start") enabled: multiVehicleList.startAvailable() - onClicked: multiVehicleList.startSelectedVehicles() + onClicked: _guidedController.confirmAction(_guidedController.actionMVStartMission) } QGCButton { text: qsTr("Pause") enabled: multiVehicleList.pauseAvailable() - onClicked: multiVehicleList.pauseSelectedVehicles() + onClicked: _guidedController.confirmAction(_guidedController.actionMVPause) } } } } - ColumnLayout { - id: vehicleModeGroup - anchors.top: actionGroup.bottom - anchors.right: parent.right - anchors.left: parent.left - anchors.margins: _margins - Layout.alignment: Qt.AlignHCenter - - QGCLabel { - text: qsTr("Multi Vehicle Modes") - } - - QGCFlickable { - Layout.fillWidth: true - height: ScreenTools.defaultFontPixelWidth * 6 - contentWidth: vehicleModeRowLayout.width - flickableDirection: Flickable.HorizontalFlick - - RowLayout { - id: vehicleModeRowLayout - spacing: _margins - - QGCButton { - text: qsTr("RTL") - enabled: multiVehicleList.rtlAvailable() - onClicked: multiVehicleList.rtlSelectedVehicles() - } - - QGCButton { - text: qsTr("Take control") - enabled: multiVehicleList.takeControlAvailable() - onClicked: multiVehicleList.takeControlSelectedVehicles() - } - - } - } - - } + // TODO: mv vehicle modes } diff --git a/src/FlightDisplay/GuidedActionsController.qml b/src/FlightDisplay/GuidedActionsController.qml index 7328690aef3..e374cc54005 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 + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle property var _flyViewSettings: QGroundControl.settingsManager.flyViewSettings @@ -405,6 +414,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 @@ -418,6 +432,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 @@ -437,7 +456,7 @@ Item { break; case actionMVStartMission: confirmDialog.title = mvStartMissionTitle - confirmDialog.message = startMissionMessage + confirmDialog.message = mvStartMissionMessage confirmDialog.hideTrigger = true break; case actionContinueMission: @@ -562,7 +581,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) @@ -583,20 +602,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 @@ -624,9 +658,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 e4f04a9650b..62a8fba3c20 100644 --- a/src/FlightDisplay/MultiVehicleList.qml +++ b/src/FlightDisplay/MultiVehicleList.qml @@ -24,151 +24,66 @@ Item { property var _guidedController: globals.guidedControllerFlyView property var _activeVehicleColor: "green" property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property var selectedVehicles: QGroundControl.multiVehicleManager.selectedVehicles QGCPalette { id: qgcPal } - ListModel { - id: selectedVehiclesModel - } - - function getSelectedVehicles() { - var selectedVehicles = [ ] - forEachSelectedVehicle(function(vehicle) { - selectedVehicles.push(vehicle) - }) - return selectedVehicles - } - - function getVehicleIndex(vehicleId) { - for (var i = 0; i < selectedVehiclesModel.count; i++) { - if (selectedVehiclesModel.get(i).id === vehicleId) { - return i - } - } - return -1 - } - - function forEachSelectedVehicle(action) { - for (var i = 0; i < selectedVehiclesModel.count; i++) { - var vehicleId = selectedVehiclesModel.get(i).id - var vehicle = QGroundControl.multiVehicleManager.getVehicleById(vehicleId) - action(vehicle) - } - } - - function armSelectedVehicles() { - forEachSelectedVehicle(function(vehicle) { - vehicle.armed = true - console.log("vehicle " + vehicle.id + " has been armed.") - }) - } - function armAvailable() { - var available = false - forEachSelectedVehicle(function(vehicle) { - if(vehicle.armed === false){ - available = true + for (var i = 0; i < selectedVehicles.count; i++) { + var vehicle = selectedVehicles.get(i) + if (vehicle.armed === false) { + return true } - }) - return available + } + return false } - function disarmSelectedVehicles() { - forEachSelectedVehicle(function(vehicle) { - vehicle.armed = false - console.log("vehicle " + vehicle.id + " has been disarmed.") - }) - } function disarmAvailable() { - var available = false - forEachSelectedVehicle(function(vehicle) { - if(vehicle.armed === true){ - available = true - } - }) - return available - } - - function startSelectedVehicles() { - forEachSelectedVehicle(function(vehicle) { - if (vehicle.armed === true){ - vehicle.startMission() - console.log("mission started for " + vehicle.id) + for (var i = 0; i < selectedVehicles.count; i++) { + var vehicle = selectedVehicles.get(i) + if (vehicle.armed === true) { + return true } - - }) + } + return false } function startAvailable() { - var available = false - forEachSelectedVehicle(function(vehicle) { - if(vehicle.armed === true && vehicle.flightMode !== vehicle.missionFlightMode){ - available = true + for (var i = 0; i < selectedVehicles.count; i++) { + var vehicle = selectedVehicles.get(i) + if (vehicle.armed === true && vehicle.flightMode !== vehicle.missionFlightMode){ + return true } - }) - return available + } + return false } - function pauseSelectedVehicles() { - forEachSelectedVehicle(function(vehicle) { - if(vehicle.pauseVehicleSupported){ - vehicle.pauseVehicle() - console.log("mission paused for " + vehicle.id) - } - }) - } function pauseAvailable() { - var available = false - forEachSelectedVehicle(function(vehicle) { - if(vehicle.armed === true && vehicle.pauseVehicleSupported){ - available = true - } - }) - return available - } - - function rtlSelectedVehicles() { - forEachSelectedVehicle(function(vehicle) { - if (vehicle.armed === true){ - vehicle.flightMode = vehicle.rtlFlightMode - console.log("vehicle " + vehicle.id + " returning to launch") - } - }) - } - - function rtlAvailable(){ - var available = false - forEachSelectedVehicle(function(vehicle) { - if(vehicle.armed === true && vehicle.flightMode !== vehicle.rtlFlightMode){ - available = true + for (var i = 0; i < selectedVehicles.count; i++) { + var vehicle = selectedVehicles.get(i) + if (vehicle.armed === true && vehicle.pauseVehicleSupported) { + return true } - }) - return available + } + return false } - function takeControlSelectedVehicles() { - forEachSelectedVehicle(function(vehicle) { - vehicle.flightMode = vehicle.takeControlFlightMode - console.log("taking control for " + vehicle.id) - }) + function selectVehicle(vehicleId) { + QGroundControl.multiVehicleManager.selectVehicle(vehicleId) } - function takeControlAvailable(){ - var available = false - forEachSelectedVehicle(function(vehicle) { - if(vehicle.armed === true && vehicle.flightMode !== vehicle.takeControlFlightMode){ - available = true - } - }) - return available + function deselectVehicle(vehicleId) { + QGroundControl.multiVehicleManager.deselectVehicle(vehicleId) } - function activateVehicle() { - if (selectedVehiclesModel.count === 1){ - var vehicle = getSelectedVehicles()[0] - QGroundControl.multiVehicleManager.activeVehicle = vehicle + function toggleSelect(vehicleId) { + if (!vehicleSelected(vehicleId)) { + selectVehicle(vehicleId) + } else { + deselectVehicle(vehicleId) } + printSelectedVehicles() } function selectAll() { @@ -176,7 +91,7 @@ Item { for (var i = 0; i < vehicles.count; i++) { var vehicle = vehicles.get(i) var vehicleId = vehicle.id - if (getVehicleIndex(vehicleId) === -1){ + if (!vehicleSelected(vehicleId)) { selectVehicle(vehicleId) } } @@ -184,45 +99,24 @@ Item { } function deselectAll() { - selectedVehiclesModel.clear() + QGroundControl.multiVehicleManager.deselectAllVehicles() } - function isSelected(vehicleId) { - for (var i = 0; i < selectedVehiclesModel.count; i++) { - if (selectedVehiclesModel.get(i).id === vehicleId) { + function vehicleSelected(vehicleId) { + for (var i = 0; i < selectedVehicles.count; i++ ) { + var currentId = selectedVehicles.get(i).id + if (vehicleId === currentId) { return true } } return false } - - function selectVehicle(vehicleId) { - selectedVehiclesModel.append({ id: vehicleId }) - } - - function deselectVehicle(vehicleId) { - var index = getVehicleIndex(vehicleId) - if (index !== -1) { - selectedVehiclesModel.remove(index) - } - } - - function toggleSelect(vehicleId) { - if (getVehicleIndex(vehicleId) !== -1) { - deselectVehicle(vehicleId) - } else { - selectVehicle(vehicleId) - } - printSelectedVehicles() - } - function printSelectedVehicles() { - var vehicles = [ ] - forEachSelectedVehicle(function(vehicle) { - vehicles.push(vehicle.id) - }) - console.log(vehicles) + console.log(selectedVehicles) + for (var i = 0; i < selectedVehicles.count; i++) { + console.log(selectedVehicles.get(i).id) + } } QGCListView { @@ -245,7 +139,7 @@ Item { height: innerColumn.y + innerColumn.height + _margin color: QGroundControl.multiVehicleManager.activeVehicle == _vehicle ? _activeVehicleColor : qgcPal.button radius: _margin - border.width: isSelected(_vehicle.id) ? 1 : 0 + border.width: _vehicle && vehicleSelected(_vehicle.id) ? 1 : 0 border.color: qgcPal.text property var _vehicle: object @@ -287,26 +181,36 @@ Item { spacing: _margin FlightModeMenu { - Layout.alignment: Qt.AlignHCenter - font.pointSize: ScreenTools.largeFontPointSize - color: qgcPal.text - currentVehicle: _vehicle + Layout.alignment: Qt.AlignHCenter + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + currentVehicle: _vehicle } QGCLabel { - Layout.alignment: Qt.AlignHCenter - text: _vehicle && _vehicle.armed ? qsTr("Armed") : qsTr("Disarmed") - color: qgcPal.text + Layout.alignment: Qt.AlignHCenter + text: _vehicle && _vehicle.armed ? qsTr("Armed") : qsTr("Disarmed") + color: qgcPal.text } } - } QGCMouseArea { - anchors.fill: parent - onClicked: multiVehicleList.toggleSelect(_vehicle.id) - } + anchors.fill: parent + onClicked: singleClickTimer.start() + onDoubleClicked: { + singleClickTimer.stop() + QGroundControl.multiVehicleManager.activeVehicle = _vehicle + } + Timer { + id: singleClickTimer + interval: 20 + running: false + repeat: false + onTriggered: multiVehicleList.toggleSelect(_vehicle.id) + } + } } } } diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index d5bbb22046d..1b9f4d615bd 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -329,6 +329,41 @@ QString MultiVehicleManager::loadSetting(const QString &name, const QString& def return settings.value(name, defaultValue).toString(); } +void MultiVehicleManager::selectVehicle(int vehicleId) +{ + if(!_vehicleSelected(vehicleId)) { + Vehicle* vehicle = getVehicleById(vehicleId); + _selectedVehicles.append(vehicle); + } +} + +void MultiVehicleManager::deselectVehicle(int vehicleId) +{ + for (int i=0; i<_selectedVehicles.count(); i++) { + Vehicle* vehicle = qobject_cast(_selectedVehicles[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* vehicle = qobject_cast(_selectedVehicles[i]); + if (vehicle->id() == vehicleId) { + return true; + } + } + return false; +} + Vehicle* MultiVehicleManager::getVehicleById(int vehicleId) { for (int i=0; i< _vehicles.count(); i++) { diff --git a/src/Vehicle/MultiVehicleManager.h b/src/Vehicle/MultiVehicleManager.h index 70d66c6061b..591c4e5ce8b 100644 --- a/src/Vehicle/MultiVehicleManager.h +++ b/src/Vehicle/MultiVehicleManager.h @@ -44,6 +44,7 @@ class MultiVehicleManager : public QGCTool Q_PROPERTY(bool parameterReadyVehicleAvailable READ parameterReadyVehicleAvailable 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 gcsHeartbeatEnabled WRITE setGcsHeartbeatEnabled NOTIFY gcsHeartBeatEnabledChanged) Q_PROPERTY(Vehicle* offlineEditingVehicle READ offlineEditingVehicle CONSTANT) Q_PROPERTY(QGeoCoordinate lastKnownLocation READ lastKnownLocation NOTIFY lastKnownLocationChanged) //< Current vehicles last know location @@ -51,6 +52,9 @@ class MultiVehicleManager : public QGCTool // Methods Q_INVOKABLE Vehicle* getVehicleById(int vehicleId); + Q_INVOKABLE void selectVehicle(int vehicleId); + Q_INVOKABLE void deselectVehicle(int vehicleId); + Q_INVOKABLE void deselectAllVehicles(); // Property accessors @@ -62,6 +66,7 @@ class MultiVehicleManager : public QGCTool void setActiveVehicle(Vehicle* vehicle); QmlObjectListModel* vehicles(void) { return &_vehicles; } + QmlObjectListModel* selectedVehicles(void) { return &_selectedVehicles; } bool gcsHeartbeatEnabled(void) const { return _gcsHeartbeatEnabled; } void setGcsHeartbeatEnabled(bool gcsHeartBeatEnabled); @@ -97,6 +102,7 @@ private slots: private: bool _vehicleExists(int vehicleId); + bool _vehicleSelected(int vehicleId); bool _activeVehicleAvailable; ///< true: An active vehicle is available bool _parameterReadyVehicleAvailable; ///< true: An active vehicle with ready parameters is available @@ -109,6 +115,7 @@ private slots: QList _ignoreVehicleIds; ///< List of vehicle id for which we ignore further communication QmlObjectListModel _vehicles; + QmlObjectListModel _selectedVehicles; FirmwarePluginManager* _firmwarePluginManager; JoystickManager* _joystickManager; From dcbe4d7a136e2157cd950436b710fc04578e88e5 Mon Sep 17 00:00:00 2001 From: Ahmet Kaan Demirci Date: Tue, 29 Oct 2024 20:29:46 +0300 Subject: [PATCH 3/3] Add swipe pages to top right panel && UI improvements --- qgroundcontrol.qrc | 3 + .../FlyViewTopRightColumnLayout.qml | 31 +-- src/FlightDisplay/FlyViewTopRightPanel.qml | 252 ++++++++++-------- src/FlightDisplay/FlyViewWidgetLayer.qml | 19 +- src/FlightDisplay/MultiVehicleList.qml | 104 ++++---- src/QmlControls/MvPanelPage.qml | 33 +++ src/QmlControls/QGCPageIndicator.qml | 6 + src/QmlControls/QGCSwipeView.qml | 7 + .../QGroundControl/Controls/qmldir | 3 + 9 files changed, 268 insertions(+), 190 deletions(-) create mode 100644 src/QmlControls/MvPanelPage.qml create mode 100644 src/QmlControls/QGCPageIndicator.qml create mode 100644 src/QmlControls/QGCSwipeView.qml diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index e26529bf5c6..492659b9a6a 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -133,6 +133,7 @@ src/PlanView/MissionItemStatus.qml src/QmlControls/ModeSwitchDisplay.qml src/QmlControls/MultiRotorMotorDisplay.qml + src/QmlControls/MvPanelPage.qml src/QmlControls/OfflineMapButton.qml src/QtLocationPlugin/QMLControl/OfflineMapEditor.qml src/UI/preferences/OfflineMapInfo.qml @@ -168,12 +169,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 diff --git a/src/FlightDisplay/FlyViewTopRightColumnLayout.qml b/src/FlightDisplay/FlyViewTopRightColumnLayout.qml index 9a7b8197215..332ee4af10b 100644 --- a/src/FlightDisplay/FlyViewTopRightColumnLayout.qml +++ b/src/FlightDisplay/FlyViewTopRightColumnLayout.qml @@ -20,28 +20,7 @@ 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 - } - } + property bool mvPanelVisible: false TerrainProgress { Layout.alignment: Qt.AlignTop @@ -54,7 +33,7 @@ ColumnLayout { Loader { id: photoVideoControlLoader Layout.alignment: Qt.AlignTop | Qt.AlignRight - sourceComponent: globals.activeVehicle && _showSingleVehicleUI ? photoVideoControlComponent : undefined + sourceComponent: globals.activeVehicle && !mvPanelVisible ? photoVideoControlComponent : undefined property real rightEdgeCenterInset: visible ? parent.width - x : 0 @@ -65,10 +44,4 @@ ColumnLayout { } } } - - MultiVehicleList { - Layout.preferredWidth: _rightPanelWidth - Layout.fillHeight: true - visible: !_showSingleVehicleUI - } } diff --git a/src/FlightDisplay/FlyViewTopRightPanel.qml b/src/FlightDisplay/FlyViewTopRightPanel.qml index 64b19c6329a..af4a00aaf90 100644 --- a/src/FlightDisplay/FlyViewTopRightPanel.qml +++ b/src/FlightDisplay/FlyViewTopRightPanel.qml @@ -20,15 +20,15 @@ import QGroundControl.ScreenTools Item { + property bool panelVisible: togglePanelBtn.checked + property alias toggleBtn: togglePanelBtn + Rectangle { id: topRightPanel anchors.fill: parent - width: parent.width - height: parent.height color: qgcPal.toolbarBackground visible: !QGroundControl.videoManager.fullScreen && togglePanelBtn.checked - - property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + clip: true QGCPalette { id: qgcPal } @@ -38,138 +38,176 @@ Item { anchors.bottom: parent.verticalCenter anchors.right: parent.right anchors.left: parent.left - anchors.margins: _toolsMargin - } - - TerrainProgress { - id: terrainProgress - anchors.top: multiVehicleList.bottom - } - - ColumnLayout { - id: selectionGroup - anchors.top: terrainProgress.bottom - anchors.right: parent.right - anchors.left: parent.left - anchors.margins: _margins - Layout.alignment: Qt.AlignHCenter - - QGCLabel { - text: qsTr("Multi Vehicle Selection") - } - - QGCFlickable { - Layout.fillWidth: true - height: ScreenTools.defaultFontPixelWidth * 6 - contentWidth: selectionRowLayout.width - flickableDirection: Flickable.HorizontalFlick + anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - RowLayout { - id: selectionRowLayout - spacing: _margins + Rectangle { + anchors.fill: parent - QGCButton { - text: qsTr("Select All") - enabled: multiVehicleList.selectedVehicles && multiVehicleList.selectedVehicles.count !== QGroundControl.multiVehicleManager.vehicles.count - onClicked: multiVehicleList.selectAll() - } + gradient: Gradient { + orientation: Gradient.Vertical - QGCButton { - text: qsTr("Deselect All") - enabled: multiVehicleList.selectedVehicles && multiVehicleList.selectedVehicles.count > 0 - onClicked: multiVehicleList.deselectAll() - } + GradientStop { position: 0.95; color: "transparent" } + GradientStop { position: 1.0; color: topRightPanel.color } + } + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: 1 + color: QGroundControl.globalPalette.groupBorder } } } - ColumnLayout { - id: actionGroup - anchors.top: selectionGroup.bottom - anchors.right: parent.right - anchors.left: parent.left - anchors.margins: _margins - - QGCLabel { - text: qsTr("Multi Vehicle Actions") - } + QGCSwipeView { + id: swipePages + anchors.top: parent.verticalCenter + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.left: parent.left - QGCFlickable { - Layout.fillWidth: true - height: ScreenTools.defaultFontPixelWidth * 6 - contentWidth: actionRowLayout.width - flickableDirection: Flickable.HorizontalFlick + MvPanelPage { + id: buttonsPage - RowLayout { - id: actionRowLayout - spacing: _margins + ColumnLayout { + anchors.right: parent.right + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.bottomMargin: ScreenTools.defaultFontPixelHeight * 3 + spacing: ScreenTools.defaultFontPixelHeight / 2 - QGCButton { - text: qsTr("Arm") - enabled: multiVehicleList.armAvailable() - onClicked: _guidedController.confirmAction(_guidedController.actionMVArm) + QGCLabel { + text: qsTr("Multi Vehicle Selection") + Layout.alignment: Qt.AlignHCenter } - QGCButton { - text: qsTr("Disarm") - enabled: multiVehicleList.disarmAvailable() - onClicked: _guidedController.confirmAction(_guidedController.actionMVDisarm) + 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() + Layout.preferredWidth: ScreenTools.defaultFontPixelHeight * 5 + } + + QGCButton { + text: qsTr("Deselect All") + enabled: multiVehicleList.selectedVehicles && multiVehicleList.selectedVehicles.count > 0 + onClicked: multiVehicleList.deselectAll() + Layout.preferredWidth: ScreenTools.defaultFontPixelHeight * 5 + + } } - QGCButton { - text: qsTr("Start") - enabled: multiVehicleList.startAvailable() - onClicked: _guidedController.confirmAction(_guidedController.actionMVStartMission) + + QGCLabel { + text: qsTr("Multi Vehicle Actions") + Layout.alignment: Qt.AlignHCenter } - QGCButton { - text: qsTr("Pause") - enabled: multiVehicleList.pauseAvailable() - onClicked: _guidedController.confirmAction(_guidedController.actionMVPause) + 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 - // TODO: mv vehicle modes + MvPanelPage { - } + // 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 - QGCButton { - id: togglePanelBtn - anchors.top: parent.top - anchors.horizontalCenter: parent.horizontalCenter - anchors.topMargin: topRightPanel.visible ? parent.height : 0 - width: _rightPanelWidth / 5 - height: _rightPanelWidth / 18 - checkable: true - - background: Rectangle { - radius: parent.height / 2 - color: qgcPal.toolbarBackground - border.color: qgcPal.text - border.width: 1 - } - } + Loader { + id: photoVideoControlLoader + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottomMargin: ScreenTools.defaultFontPixel + sourceComponent: globals.activeVehicle && togglePanelBtn.checked ? photoVideoControlComponent : undefined - // 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.top: togglePanelBtn.bottom - sourceComponent: globals.activeVehicle && !togglePanelBtn.checked ? photoVideoControlComponent : undefined + property real rightEdgeCenterInset: visible ? parent.width - x : 0 - property real rightEdgeCenterInset: visible ? parent.width - x : 0 + Component { + id: photoVideoControlComponent - Component { - id: photoVideoControlComponent + PhotoVideoControl { + } + } + } + } // Page 2 + } - PhotoVideoControl { + QGCPageIndicator { + id: pageIndicator + count: swipePages.count + currentIndex: swipePages.currentIndex + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: togglePanelBtn.height + + 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 } } } + QGCButton { + id: togglePanelBtn + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter + anchors.topMargin: topRightPanel.visible ? parent.height : 0 + width: _rightPanelWidth / 5 + height: _rightPanelWidth / 18 + checkable: true + + background: Rectangle { + radius: parent.height / 2 + color: qgcPal.toolbarBackground + border.color: parent.checked ? QGroundControl.globalPalette.groupBorder : qgcPal.text + border.width: 1 + } + } } diff --git a/src/FlightDisplay/FlyViewWidgetLayer.qml b/src/FlightDisplay/FlyViewWidgetLayer.qml index b11d99896d9..8638557e198 100644 --- a/src/FlightDisplay/FlyViewWidgetLayer.qml +++ b/src/FlightDisplay/FlyViewWidgetLayer.qml @@ -46,7 +46,7 @@ Item { property real _margins: ScreenTools.defaultFontPixelWidth / 2 property real _toolsMargin: ScreenTools.defaultFontPixelWidth * 0.75 property rect _centerViewport: Qt.rect(0, 0, width, height) - property real _rightPanelWidth: ScreenTools.defaultFontPixelWidth * 35 + property real _rightPanelWidth: ScreenTools.defaultFontPixelWidth * 40 property alias _gripperMenu: gripperOptions property real _layoutMargin: ScreenTools.defaultFontPixelWidth * 0.75 property bool _layoutSpacing: ScreenTools.defaultFontPixelWidth @@ -76,13 +76,28 @@ Item { anchors.right: parent.right anchors.rightMargin: _layoutMargin width: _rightPanelWidth - height: _rightPanelWidth * 2 + height: _rightPanelWidth * 1.75 property real topEdgeRightInset: height + _layoutMargin property real rightEdgeTopInset: width + _layoutMargin property real rightEdgeCenterInset: rightEdgeTopInset } + FlyViewTopRightColumnLayout { + id: topRightColumnLayout + anchors.margins: _layoutMargin + anchors.top: topRightPanel.top + anchors.bottom: bottomRightRowLayout.top + anchors.right: parent.right + anchors.topMargin: topRightPanel.toggleBtn.height + _toolsMargin + spacing: _layoutSpacing + mvPanelVisible: topRightPanel.panelVisible + + property real topEdgeRightInset: childrenRect.height + _layoutMargin + property real rightEdgeTopInset: width + _layoutMargin + property real rightEdgeCenterInset: rightEdgeTopInset + } + FlyViewBottomRightRowLayout { id: bottomRightRowLayout anchors.margins: _layoutMargin diff --git a/src/FlightDisplay/MultiVehicleList.qml b/src/FlightDisplay/MultiVehicleList.qml index 62a8fba3c20..310294942ca 100644 --- a/src/FlightDisplay/MultiVehicleList.qml +++ b/src/FlightDisplay/MultiVehicleList.qml @@ -83,7 +83,6 @@ Item { } else { deselectVehicle(vehicleId) } - printSelectedVehicles() } function selectAll() { @@ -95,7 +94,6 @@ Item { selectVehicle(vehicleId) } } - printSelectedVehicles() } function deselectAll() { @@ -112,13 +110,6 @@ Item { return false } - function printSelectedVehicles() { - console.log(selectedVehicles) - for (var i = 0; i < selectedVehicles.count; i++) { - console.log(selectedVehicles.get(i).id) - } - } - QGCListView { id: vehicleList anchors.left: parent.left @@ -144,53 +135,62 @@ Item { property var _vehicle: object - RowLayout { - id: innerColumn - anchors.margins: _margin - anchors.top: parent.top - anchors.left: parent.left - spacing: _margin - Layout.fillWidth: true - - QGCCompassWidget { - size: _widgetHeight - usedByMultipleVehicleList: true - vehicle: _vehicle - } + Rectangle { + height: parent.height + width: innerColumn.width + anchors.horizontalCenter: parent.horizontalCenter + color: "transparent" - QGCLabel { - text: " | " - font.pointSize: ScreenTools.largeFontPointSize - color: qgcPal.text - } - - QGCLabel { - text: _vehicle ? _vehicle.id : "" - font.pointSize: ScreenTools.largeFontPointSize - color: qgcPal.text - } + RowLayout { + id: innerColumn + anchors.margins: _margin + spacing: _margin - QGCLabel { - text: " | " - font.pointSize: ScreenTools.largeFontPointSize - color: qgcPal.text - } + QGCCompassWidget { + id: compassWidget + size: _widgetHeight + usedByMultipleVehicleList: true + vehicle: _vehicle + } - ColumnLayout { - Layout.alignment: Qt.AlignCenter - spacing: _margin + QGCLabel { + text: " | " + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + Layout.alignment: Qt.AlignHCenter + } - FlightModeMenu { - Layout.alignment: Qt.AlignHCenter - font.pointSize: ScreenTools.largeFontPointSize - color: qgcPal.text - currentVehicle: _vehicle + QGCLabel { + text: _vehicle ? _vehicle.id : "" + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + Layout.alignment: Qt.AlignHCenter } QGCLabel { - Layout.alignment: Qt.AlignHCenter - text: _vehicle && _vehicle.armed ? qsTr("Armed") : qsTr("Disarmed") - color: qgcPal.text + text: " | " + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + Layout.alignment: Qt.AlignHCenter + } + + ColumnLayout { + spacing: _margin + Layout.rightMargin: compassWidget.width / 4 + Layout.alignment: Qt.AlignCenter + + FlightModeMenu { + Layout.alignment: Qt.AlignHCenter + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + currentVehicle: _vehicle + } + + QGCLabel { + Layout.alignment: Qt.AlignHCenter + text: _vehicle && _vehicle.armed ? qsTr("Armed") : qsTr("Disarmed") + color: qgcPal.text + } } } } @@ -199,8 +199,8 @@ Item { anchors.fill: parent onClicked: singleClickTimer.start() onDoubleClicked: { - singleClickTimer.stop() - QGroundControl.multiVehicleManager.activeVehicle = _vehicle + singleClickTimer.stop() + QGroundControl.multiVehicleManager.activeVehicle = _vehicle } Timer { @@ -208,7 +208,7 @@ Item { interval: 20 running: false repeat: false - onTriggered: multiVehicleList.toggleSelect(_vehicle.id) + onTriggered: toggleSelect(_vehicle.id) } } } diff --git a/src/QmlControls/MvPanelPage.qml b/src/QmlControls/MvPanelPage.qml new file mode 100644 index 00000000000..dd51a30b985 --- /dev/null +++ b/src/QmlControls/MvPanelPage.qml @@ -0,0 +1,33 @@ +import QtQuick +import QtQuick.Controls + +import QGroundControl +import QGroundControl.ScreenTools +import QGroundControl.Palette + +Item { + property bool showBorder: true + property real contentMargin: _margins * 2 + default property alias contentChildren: contentContainer.data + + property real _margins: ScreenTools.defaultFontPixelHeight / 2 + + Rectangle { + color: "transparent" + x: _margins + y: _margins + height: parent.height - _margins * 2 + width: parent.width - _margins * 2 + border.color: QGroundControl.globalPalette.groupBorder + border.width: showBorder ? 1 : 0 + radius: ScreenTools.defaultFontPixelHeight / 2 + + + Item { + id: contentContainer + anchors.fill: parent + anchors.margins: _margins + } + } + +} 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 816f521baca..258a668db60 100644 --- a/src/QmlControls/QGroundControl/Controls/qmldir +++ b/src/QmlControls/QGroundControl/Controls/qmldir @@ -55,6 +55,7 @@ MissionItemMapVisual 1.0 MissionItemMapVisual.qml MissionItemStatus 1.0 MissionItemStatus.qml ModeSwitchDisplay 1.0 ModeSwitchDisplay.qml MultiRotorMotorDisplay 1.0 MultiRotorMotorDisplay.qml +MvPanelPage 1.0 MvPanelPage.qml OfflineMapButton 1.0 OfflineMapButton.qml OfflineMapEditor 1.0 OfflineMapEditor.qml OfflineMapInfo 1.0 OfflineMapInfo.qml @@ -89,10 +90,12 @@ QGCMovableItem 1.0 QGCMovableItem.qml QGCOptionsComboBox 1.0 QGCOptionsComboBox.qml PipView 1.0 PipView.qml PipState 1.0 PipState.qml +QGCPageIndicator 1.0 QGCPageIndicator.qml QGCPopupDialog 1.0 QGCPopupDialog.qml QGCRadioButton 1.0 QGCRadioButton.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