diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 61516930996..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 @@ -351,6 +354,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..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 new file mode 100644 index 00000000000..af4a00aaf90 --- /dev/null +++ b/src/FlightDisplay/FlyViewTopRightPanel.qml @@ -0,0 +1,213 @@ +/**************************************************************************** + * + * (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 { + + property bool panelVisible: togglePanelBtn.checked + property alias toggleBtn: togglePanelBtn + + Rectangle { + id: topRightPanel + anchors.fill: parent + color: qgcPal.toolbarBackground + visible: !QGroundControl.videoManager.fullScreen && togglePanelBtn.checked + clip: true + + QGCPalette { id: qgcPal } + + MultiVehicleList { + id: multiVehicleList + anchors.top: parent.top + anchors.bottom: parent.verticalCenter + anchors.right: parent.right + anchors.left: parent.left + anchors.margins: ScreenTools.defaultFontPixelHeight / 2 + + Rectangle { + anchors.fill: parent + + gradient: Gradient { + orientation: Gradient.Vertical + + 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 + } + } + + } + + QGCSwipeView { + id: swipePages + anchors.top: parent.verticalCenter + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.left: parent.left + + MvPanelPage { + id: buttonsPage + + ColumnLayout { + anchors.right: parent.right + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + anchors.bottomMargin: ScreenTools.defaultFontPixelHeight * 3 + spacing: ScreenTools.defaultFontPixelHeight / 2 + + 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() + 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 + + } + } + + + 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 { + + // 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 + anchors.bottomMargin: ScreenTools.defaultFontPixel + sourceComponent: globals.activeVehicle && togglePanelBtn.checked ? photoVideoControlComponent : undefined + + property real rightEdgeCenterInset: visible ? parent.width - x : 0 + + Component { + id: photoVideoControlComponent + + PhotoVideoControl { + } + } + } + } // Page 2 + } + + 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 55f8341dc3c..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 * 30 + property real _rightPanelWidth: ScreenTools.defaultFontPixelWidth * 40 property alias _gripperMenu: gripperOptions property real _layoutMargin: ScreenTools.defaultFontPixelWidth * 0.75 property bool _layoutSpacing: ScreenTools.defaultFontPixelWidth @@ -59,24 +59,39 @@ 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.rightMargin: _layoutMargin + width: _rightPanelWidth + 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: parent.top + 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 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 73b2e56f7ae..310294942ca 100644 --- a/src/FlightDisplay/MultiVehicleList.qml +++ b/src/FlightDisplay/MultiVehicleList.qml @@ -19,66 +19,103 @@ 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 + property var selectedVehicles: QGroundControl.multiVehicleManager.selectedVehicles 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 + 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 +126,91 @@ 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) ? 1 : 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 + id: innerColumn + anchors.margins: _margin + spacing: _margin - QGCLabel { - Layout.alignment: Qt.AlignTop - text: _vehicle ? _vehicle.id : "" - color: _textColor + QGCCompassWidget { + id: compassWidget + 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 + Layout.alignment: Qt.AlignHCenter } - QGCCompassWidget { - size: _widgetHeight - usedByMultipleVehicleList: true - vehicle: _vehicle + QGCLabel { + text: _vehicle ? _vehicle.id : "" + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + Layout.alignment: Qt.AlignHCenter } - 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 - } + ColumnLayout { + spacing: _margin + Layout.rightMargin: compassWidget.width / 4 + Layout.alignment: Qt.AlignCenter - QGCButton { - text: qsTr("Start Mission") - visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.missionFlightMode - onClicked: _vehicle.startMission() - } + FlightModeMenu { + Layout.alignment: Qt.AlignHCenter + font.pointSize: ScreenTools.largeFontPointSize + color: qgcPal.text + currentVehicle: _vehicle + } - QGCButton { - text: qsTr("Pause") - visible: _vehicle && _vehicle.armed && _vehicle.pauseVehicleSupported - onClicked: _vehicle.pauseVehicle() + QGCLabel { + Layout.alignment: Qt.AlignHCenter + text: _vehicle && _vehicle.armed ? qsTr("Armed") : qsTr("Disarmed") + color: qgcPal.text + } } + } + } - QGCButton { - text: qsTr("RTL") - visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.rtlFlightMode - onClicked: _vehicle.flightMode = _vehicle.rtlFlightMode - } + QGCMouseArea { + anchors.fill: parent + onClicked: singleClickTimer.start() + onDoubleClicked: { + singleClickTimer.stop() + QGroundControl.multiVehicleManager.activeVehicle = _vehicle + } - QGCButton { - text: qsTr("Take control") - visible: _vehicle && _vehicle.armed && _vehicle.flightMode !== _vehicle.takeControlFlightMode - onClicked: _vehicle.flightMode = _vehicle.takeControlFlightMode - } - } // Row - } // ColumnLayout - } // delegate - Rectangle - } // QGCListView -} // Item + Timer { + id: singleClickTimer + interval: 20 + running: false + repeat: false + onTriggered: 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..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/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 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 diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 48231e1ba75..5ef6c2b951f 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -327,6 +327,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 be09b4b6298..5b12ba3b4f5 100644 --- a/src/Vehicle/MultiVehicleManager.h +++ b/src/Vehicle/MultiVehicleManager.h @@ -43,6 +43,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 @@ -50,6 +51,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 @@ -61,6 +65,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); @@ -96,6 +101,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 @@ -108,6 +114,7 @@ private slots: QList _ignoreVehicleIds; ///< List of vehicle id for which we ignore further communication QmlObjectListModel _vehicles; + QmlObjectListModel _selectedVehicles; FirmwarePluginManager* _firmwarePluginManager; MAVLinkProtocol* _mavlinkProtocol;