QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
PlanView.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Dialogs
4import QtLocation
5import QtPositioning
6import QtQuick.Layouts
7import QtQuick.Window
8
9import QGroundControl
10import QGroundControl.FlightMap
11import QGroundControl.Controls
12import QGroundControl.FactControls
13import QGroundControl.FlyView
14import QGroundControl.Geo
15import QGroundControl.Toolbar
16
17Item {
18 id: _root
19
20 readonly property int _decimalPlaces: 8
21 readonly property real _margin: ScreenTools.defaultFontPixelHeight * 0.5
22 readonly property real _toolsMargin: ScreenTools.defaultFontPixelWidth * 0.75
23 readonly property real _rightPanelWidth: Math.min(width / 3, ScreenTools.defaultFontPixelWidth * 30)
24
25 property var _planMasterController: planMasterController
26 property var _missionController: _planMasterController.missionController
27 property var _geoFenceController: _planMasterController.geoFenceController
28 property var _rallyPointController: _planMasterController.rallyPointController
29 property var _visualItems: _missionController.visualItems
30 property bool _singleComplexItem: _missionController.complexMissionItems.length === 1
31 property int _editingLayer: _layerMission
32 property var _appSettings: QGroundControl.settingsManager.appSettings
33 property var _planViewSettings: QGroundControl.settingsManager.planViewSettings
34 property bool _promptForPlanUsageShowing: false
35 property bool _addROIOnClick: false
36 property bool _addWaypointOnClick: false
37 property bool _homePositionSet: _missionController.homePositionSet
38
39 readonly property int _layerMission: 1
40 readonly property int _layerFence: 2
41 readonly property int _layerRally: 3
42
43 onVisibleChanged: {
44 if(visible) {
45 editorMap.zoomLevel = QGroundControl.flightMapZoom
46 editorMap.center = QGroundControl.flightMapPosition
47 }
48 }
49
50 Connections {
51 target: planToolBar
52 function onToolbarButtonClicked() {
53 _addWaypointOnClick = false
54 _addROIOnClick = false
55 }
56 }
57
58 function mapCenter() {
59 var coordinate = editorMap.center
60 coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
61 coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
62 coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
63 return coordinate
64 }
65
66 MapFitFunctions {
67 id: mapFitFunctions // The name for this id cannot be changed without breaking references outside of this code. Beware!
68 map: editorMap
69 usePlannedHomePosition: true
70 planMasterController: _planMasterController
71 }
72
73 PlanMasterController {
74 id: planMasterController
75 flyView: false
76
77 Component.onCompleted: {
78 _planMasterController.start()
79 _missionController.setCurrentPlanViewSeqNum(0, true)
80 }
81
82 onPromptForPlanUsageOnVehicleChange: {
83 if (!_promptForPlanUsageShowing) {
84 _promptForPlanUsageShowing = true
85 promptForPlanUsageOnVehicleChangePopupFactory.open()
86 }
87 }
88
89 function waitingOnIncompleteDataMessage(save) {
90 var saveOrUpload = save ? qsTr("Save") : qsTr("Upload")
91 QGroundControl.showMessageDialog(_root, qsTr("Unable to %1").arg(saveOrUpload), qsTr("Plan has incomplete items. Complete all items and %1 again.").arg(saveOrUpload))
92 }
93
94 function waitingOnTerrainDataMessage(save) {
95 var saveOrUpload = save ? qsTr("Save") : qsTr("Upload")
96 QGroundControl.showMessageDialog(_root, qsTr("Unable to %1").arg(saveOrUpload), qsTr("Plan is waiting on terrain data from server for correct altitude values."))
97 }
98
99 function checkReadyForSaveUpload(save) {
100 if (readyForSaveState() == VisualMissionItem.NotReadyForSaveData) {
101 waitingOnIncompleteDataMessage(save)
102 return false
103 } else if (readyForSaveState() == VisualMissionItem.NotReadyForSaveTerrain) {
104 waitingOnTerrainDataMessage(save)
105 return false
106 }
107 return true
108 }
109
110 function upload() {
111 if (!checkReadyForSaveUpload(false /* save */)) {
112 return
113 }
114 switch (_missionController.sendToVehiclePreCheck()) {
115 case MissionController.SendToVehiclePreCheckStateOk: sendToVehicle()
116 break
117 case MissionController.SendToVehiclePreCheckStateActiveMission: QGroundControl.showMessageDialog(_root, qsTr("Send To Vehicle"), qsTr("Current mission must be paused prior to uploading a new Plan"))
118 break
119 case MissionController.SendToVehiclePreCheckStateFirwmareVehicleMismatch: QGroundControl.showMessageDialog(_root, qsTr("Plan Upload"),
120 qsTr("This Plan was created for a different firmware or vehicle type than the firmware/vehicle type of vehicle you are uploading to. " +
121 "This can lead to errors or incorrect behavior. " +
122 "It is recommended to recreate the Plan for the correct firmware/vehicle type.\n\n" +
123 "Click 'Ok' to upload the Plan anyway."),
124 Dialog.Ok | Dialog.Cancel,
125 function() { _planMasterController.sendToVehicle() })
126 break
127 }
128 }
129
130 function loadFromSelectedFile() {
131 fileDialog.title = qsTr("Select Plan File")
132 fileDialog.planFiles = true
133 fileDialog.nameFilters = _planMasterController.loadNameFilters
134 fileDialog.openForLoad()
135 }
136
137 function saveToSelectedFile() {
138 if (!checkReadyForSaveUpload(true /* save */)) {
139 return
140 }
141 fileDialog.title = qsTr("Save Plan")
142 fileDialog.planFiles = true
143 fileDialog.nameFilters = _planMasterController.saveNameFilters
144 fileDialog.openForSave()
145 }
146
147 function fitViewportToItems() {
148 mapFitFunctions.fitMapViewportToMissionItems()
149 }
150
151 function saveKmlToSelectedFile() {
152 if (!checkReadyForSaveUpload(true /* save */)) {
153 return
154 }
155 fileDialog.title = qsTr("Save KML")
156 fileDialog.planFiles = false
157 fileDialog.nameFilters = ShapeFileHelper.fileDialogKMLFilters
158 fileDialog.openForSave()
159 }
160 }
161
162 Connections {
163 target: _missionController
164
165 function onNewItemsFromVehicle() {
166 if (_visualItems && _visualItems.count !== 1) {
167 mapFitFunctions.fitMapViewportToMissionItems()
168 }
169 _missionController.setCurrentPlanViewSeqNum(0, true)
170 }
171 }
172
173 function insertSimpleItemAfterCurrent(coordinate) {
174 var nextIndex = _missionController.currentPlanViewVIIndex + 1
175 _missionController.insertSimpleMissionItem(coordinate, nextIndex, true /* makeCurrentItem */)
176 }
177
178 function insertROIAfterCurrent(coordinate) {
179 var nextIndex = _missionController.currentPlanViewVIIndex + 1
180 _missionController.insertROIMissionItem(coordinate, nextIndex, true /* makeCurrentItem */)
181 }
182
183 function insertCancelROIAfterCurrent() {
184 var nextIndex = _missionController.currentPlanViewVIIndex + 1
185 _missionController.insertCancelROIMissionItem(nextIndex, true /* makeCurrentItem */)
186 }
187
188 function insertComplexItemAfterCurrent(complexItemName) {
189 var nextIndex = _missionController.currentPlanViewVIIndex + 1
190 _missionController.insertComplexMissionItem(complexItemName, mapCenter(), nextIndex, true /* makeCurrentItem */)
191 }
192
193 function insertTakeoffItemAfterCurrent() {
194 var nextIndex = _missionController.currentPlanViewVIIndex + 1
195 _missionController.insertTakeoffItem(mapCenter(), nextIndex, true /* makeCurrentItem */)
196 }
197
198 function insertLandItemAfterCurrent() {
199 var nextIndex = _missionController.currentPlanViewVIIndex + 1
200 _missionController.insertLandItem(mapCenter(), nextIndex, true /* makeCurrentItem */)
201 }
202
203 QGCFileDialog {
204 id: fileDialog
205 folder: _appSettings ? _appSettings.missionSavePath : ""
206
207 property bool planFiles: true ///< true: working with plan files, false: working with kml file
208
209 onAcceptedForSave: (file) => {
210 if (planFiles) {
211 if (_planMasterController.saveToFile(file)) {
212 close()
213 }
214 } else {
215 _planMasterController.saveToKml(file)
216 close()
217 }
218 }
219
220 onAcceptedForLoad: (file) => {
221 _planMasterController.loadFromFile(file)
222 _planMasterController.fitViewportToItems()
223 _missionController.setCurrentPlanViewSeqNum(0, true)
224 close()
225 }
226 }
227
228 PlanViewToolBar {
229 id: planToolBar
230 planMasterController: _planMasterController
231 showRallyPointsHelp: _editingLayer === _layerRally
232 }
233
234 Item {
235 id: mainPlanViewArea
236 anchors.left: parent.left
237 anchors.right: parent.right
238 anchors.top: planToolBar.bottom
239 anchors.bottom: parent.bottom
240
241 FlightMap {
242 id: editorMap
243 anchors.fill: parent
244 mapName: "MissionEditor"
245 allowGCSLocationCenter: true
246 allowVehicleLocationCenter: true
247 planView: true
248
249 zoomLevel: QGroundControl.flightMapZoom
250 center: QGroundControl.flightMapPosition
251
252 // This is the center rectangle of the map which is not obscured by tools
253 property rect centerViewport: Qt.rect(_leftToolWidth + _margin, _margin, editorMap.width - _leftToolWidth - _rightToolWidth - (_margin * 2), (missionStatus.visible ? missionStatus.y : height - _margin) - _margin)
254
255 property real _leftToolWidth: toolStrip.x + toolStrip.width
256 property real _rightToolWidth: rightPanel.width + rightPanel.anchors.rightMargin
257 property real _nonInteractiveOpacity: 0.5
258
259 // Initial map position duplicates Fly view position
260 Component.onCompleted: editorMap.center = QGroundControl.flightMapPosition
261
262 onZoomLevelChanged: {
263 QGroundControl.flightMapZoom = editorMap.zoomLevel
264 }
265 onCenterChanged: {
266 QGroundControl.flightMapPosition = editorMap.center
267 }
268
269 onMapClicked: (mouse) => {
270 // Take focus to close any previous editing
271 editorMap.focus = true
272
273 // Collapse layer switcher on any map click
274 layerSwitcher.expanded = false
275 collapseTimer.stop()
276
277 if (!mainWindow.allowViewSwitch()) {
278 return
279 }
280 var coordinate = editorMap.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */)
281 coordinate.latitude = coordinate.latitude.toFixed(_decimalPlaces)
282 coordinate.longitude = coordinate.longitude.toFixed(_decimalPlaces)
283 coordinate.altitude = coordinate.altitude.toFixed(_decimalPlaces)
284
285 switch (_editingLayer) {
286 case _layerMission:
287 if (_planMasterController.showCreateFromTemplate) {
288 _missionController.setHomePosition(coordinate)
289 } else if (_addROIOnClick) {
290 _addROIOnClick = false
291 if (_missionController.isROIActive) {
292 var pos = Qt.point(mouse.x, mouse.y)
293 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
294 pos = editorMap.mapToItem(globals.parent, pos)
295 var dropPanel = insertOrCancelROIDropPanelComponent.createObject(mainWindow, { mapClickCoord: coordinate, clickRect: Qt.rect(pos.x, pos.y, 0, 0) })
296 dropPanel.open()
297 } else {
298 insertROIAfterCurrent(coordinate)
299 }
300 } else if (_addWaypointOnClick) {
301 insertSimpleItemAfterCurrent(coordinate)
302 }
303 break
304 case _layerRally:
305 if (_rallyPointController.supported) {
306 _rallyPointController.addPoint(coordinate)
307 }
308 break
309 }
310 }
311
312 // Add the mission item visuals to the map
313 Repeater {
314 model: _missionController.visualItems
315 delegate: MissionItemMapVisual {
316 map: editorMap
317 opacity: _editingLayer == _layerMission ? 1 : editorMap._nonInteractiveOpacity
318 interactive: _editingLayer == _layerMission
319 vehicle: _planMasterController.controllerVehicle
320 onClicked: (sequenceNumber) => { _missionController.setCurrentPlanViewSeqNum(sequenceNumber, false) }
321 }
322 }
323
324 // Add lines between waypoints
325 MissionLineView {
326 showSpecialVisual: _missionController.isROIBeginCurrentItem
327 model: _missionController.simpleFlightPathSegments
328 opacity: _editingLayer == _layerMission ? 1 : editorMap._nonInteractiveOpacity
329 }
330
331 // Direction arrows in waypoint lines
332 MapItemView {
333 model: _editingLayer == _layerMission ? _missionController.directionArrows : undefined
334
335 delegate: MapLineArrow {
336 fromCoord: object ? object.coordinate1 : undefined
337 toCoord: object ? object.coordinate2 : undefined
338 arrowPosition: 3
339 z: QGroundControl.zOrderWaypointLines + 1
340 }
341 }
342
343 // UI for splitting the current segment
344 MapQuickItem {
345 id: splitSegmentItem
346 anchorPoint.x: sourceItem.width / 2
347 anchorPoint.y: sourceItem.height / 2
348 z: QGroundControl.zOrderWaypointLines + 1
349 visible: _editingLayer == _layerMission
350
351 sourceItem: SplitIndicator {
352 onClicked: _missionController.insertSimpleMissionItem(splitSegmentItem.coordinate,
353 _missionController.currentPlanViewVIIndex,
354 true /* makeCurrentItem */)
355 }
356
357 function _updateSplitCoord() {
358 if (_missionController.splitSegment) {
359 var distance = _missionController.splitSegment.coordinate1.distanceTo(_missionController.splitSegment.coordinate2)
360 var azimuth = _missionController.splitSegment.coordinate1.azimuthTo(_missionController.splitSegment.coordinate2)
361 splitSegmentItem.coordinate = _missionController.splitSegment.coordinate1.atDistanceAndAzimuth(distance / 2, azimuth)
362 } else {
363 coordinate = QtPositioning.coordinate()
364 }
365 }
366
367 Connections {
368 target: _missionController
369 function onSplitSegmentChanged() { splitSegmentItem._updateSplitCoord() }
370 }
371
372 Connections {
373 target: _missionController.splitSegment
374 function onCoordinate1Changed() { splitSegmentItem._updateSplitCoord() }
375 function onCoordinate2Changed() { splitSegmentItem._updateSplitCoord() }
376 }
377 }
378
379 // Add the vehicles to the map
380 MapItemView {
381 model: QGroundControl.multiVehicleManager.vehicles
382 delegate: VehicleMapItem {
383 vehicle: object
384 coordinate: object.coordinate
385 map: editorMap
386 size: ScreenTools.defaultFontPixelHeight * 3
387 z: QGroundControl.zOrderMapItems - 1
388 }
389 }
390
391 GeoFenceMapVisuals {
392 map: editorMap
393 myGeoFenceController: _geoFenceController
394 interactive: _editingLayer == _layerFence
395 homePosition: _missionController.plannedHomePosition
396 planView: true
397 opacity: _editingLayer != _layerFence ? editorMap._nonInteractiveOpacity : 1
398 }
399
400 RallyPointMapVisuals {
401 map: editorMap
402 myRallyPointController: _rallyPointController
403 interactive: _editingLayer == _layerRally
404 planView: true
405 opacity: _editingLayer != _layerRally ? editorMap._nonInteractiveOpacity : 1
406 }
407
408 }
409
410 //-----------------------------------------------------------
411 // Left tool strip
412 ToolStrip {
413 id: toolStrip
414 anchors.margins: _toolsMargin
415 anchors.left: parent.left
416 anchors.top: parent.top
417 z: QGroundControl.zOrderWidgets
418 maxHeight: parent.height - toolStrip.y
419 visible: _editingLayer == _layerMission
420
421 property bool _isMissionLayer: _editingLayer == _layerMission
422
423 Binding {
424 target: waypointButton
425 property: "checked"
426 value: _addWaypointOnClick
427 }
428
429 Binding {
430 target: roiButton
431 property: "checked"
432 value: _addROIOnClick
433 }
434
435 ToolStripActionList {
436 id: toolStripActionList
437 model: [
438 ToolStripAction {
439 text: qsTr("Takeoff")
440 iconSource: "/res/takeoff.svg"
441 enabled: _homePositionSet && _missionController.isInsertTakeoffValid
442 visible: toolStrip._isMissionLayer && !_planMasterController.controllerVehicle.rover
443 onTriggered: {
444 insertTakeoffItemAfterCurrent()
445 }
446 },
447 ToolStripAction {
448 text: _singleComplexItem ? _missionController.complexMissionItems[0].translatedName : qsTr("Pattern")
449 iconSource: "/qmlimages/MapDrawShape.svg"
450 enabled: _homePositionSet && _missionController.flyThroughCommandsAllowed
451 visible: toolStrip._isMissionLayer
452 dropPanelComponent: _singleComplexItem ? undefined : patternDropPanel
453 onTriggered: {
454 if (_singleComplexItem) {
455 insertComplexItemAfterCurrent(_missionController.complexMissionItems[0].canonicalName)
456 }
457 }
458 },
459 ToolStripAction {
460 id: waypointButton
461 text: qsTr("Waypoint")
462 iconSource: "/res/waypoint.svg"
463 enabled: _homePositionSet
464 visible: toolStrip._isMissionLayer
465 checkable: true
466 onTriggered: { _addWaypointOnClick = !_addWaypointOnClick; if (_addWaypointOnClick) _addROIOnClick = false }
467 },
468 ToolStripAction {
469 id: roiButton
470 text: qsTr("ROI")
471 iconSource: "/qmlimages/roi.svg"
472 enabled: _homePositionSet
473 visible: toolStrip._isMissionLayer && _planMasterController.controllerVehicle.supports.roiMode
474 checkable: true
475 onTriggered: { _addROIOnClick = !_addROIOnClick; if (_addROIOnClick) _addWaypointOnClick = false }
476 },
477 ToolStripAction {
478 text: _planMasterController.controllerVehicle.multiRotor
479 ? qsTr("Return")
480 : _missionController.isInsertLandValid && _missionController.hasLandItem
481 ? qsTr("Alt Land")
482 : qsTr("Land")
483 iconSource: "/res/rtl.svg"
484 enabled: _homePositionSet && _missionController.isInsertLandValid
485 visible: toolStrip._isMissionLayer
486 onTriggered: {
487 insertLandItemAfterCurrent()
488 }
489 },
490 ToolStripAction {
491 text: qsTr("Stats")
492 iconSource: "/res/chevron-double-right.svg"
493 visible: missionStatus.hidden && QGroundControl.corePlugin.options.showMissionStatus
494 onTriggered: missionStatus.showMissionStatus()
495 }
496 ]
497 }
498
499 model: toolStripActionList.model
500 }
501
502 MapScale {
503 anchors.margins: _toolsMargin
504 anchors.left: toolStrip.right
505 anchors.top: parent.top
506 mapControl: editorMap
507 autoHide: true
508 }
509
510 PlanViewRightPanel {
511 id: rightPanel
512 anchors.top: parent.top
513 anchors.bottom: parent.bottom
514 anchors.right: parent.right
515 width: _rightPanelWidth
516 planMasterController: _planMasterController
517 editorMap: editorMap
518 onEditingLayerChangeRequested: (layer) => _editingLayer = layer
519 }
520
521 // Layer switching icons — only active icon visible; click to expand choices leftward
522 Item {
523 id: layerSwitcher
524 anchors.right: rightPanel.left
525 anchors.rightMargin: _toolsMargin
526 anchors.top: parent.top
527 anchors.topMargin: _toolsMargin
528 width: layerRow.width
529 height: _layerButtonSize
530 z: QGroundControl.zOrderWidgets
531
532 property bool expanded: false
533 property real _layerButtonSize: ScreenTools.defaultFontPixelHeight * 2.0
534 property real _spacing: ScreenTools.defaultFontPixelHeight * 0.25
535
536 readonly property var _layers: [
537 { layer: _layerMission, icon: "/res/waypoint.svg", nodeType: "missionGroup" },
538 { layer: _layerFence, icon: "/res/GeoFence.svg", nodeType: "fenceGroup" },
539 { layer: _layerRally, icon: "/res/RallyPoint.svg", nodeType: "rallyGroup" }
540 ]
541
542 Timer {
543 id: collapseTimer
544 interval: 5000
545 onTriggered: layerSwitcher.expanded = false
546 }
547
548 function toggle() {
549 expanded = !expanded
550 if (expanded) {
551 collapseTimer.restart()
552 } else {
553 collapseTimer.stop()
554 }
555 }
556
557 function choose(nodeType) {
558 expanded = false
559 collapseTimer.stop()
560 rightPanel.selectLayer(nodeType)
561 }
562
563 // Row laid out right-to-left: active icon on the right, choices expand left
564 Row {
565 id: layerRow
566 anchors.right: parent.right
567 spacing: layerSwitcher._spacing
568 layoutDirection: Qt.RightToLeft
569
570 // Active layer button (always visible)
571 Rectangle {
572 width: layerSwitcher._layerButtonSize
573 height: width
574 radius: ScreenTools.defaultBorderRadius
575 color: QGroundControl.globalPalette.buttonHighlight
576
577 QGCColoredImage {
578 anchors.centerIn: parent
579 width: parent.width * 0.6
580 height: width
581 source: layerSwitcher._layers.find(l => l.layer === _editingLayer)?.icon ?? "/res/waypoint.svg"
582 color: QGroundControl.globalPalette.buttonHighlightText
583 }
584
585 QGCMouseArea {
586 anchors.fill: parent
587 onClicked: layerSwitcher.toggle()
588 }
589 }
590
591 // Choice buttons (only layers that are NOT the current one)
592 Repeater {
593 model: layerSwitcher._layers.filter(l => l.layer !== _editingLayer)
594
595 Rectangle {
596 required property var modelData
597 width: layerSwitcher._layerButtonSize
598 height: width
599 radius: ScreenTools.defaultBorderRadius
600 color: QGroundControl.globalPalette.button
601 visible: opacity > 0
602 opacity: layerSwitcher.expanded ? 1 : 0
603
604 Behavior on opacity { NumberAnimation { duration: 150 } }
605
606 QGCColoredImage {
607 anchors.centerIn: parent
608 width: parent.width * 0.6
609 height: width
610 source: modelData.icon
611 color: QGroundControl.globalPalette.buttonText
612 }
613
614 QGCMouseArea {
615 anchors.fill: parent
616 onClicked: layerSwitcher.choose(modelData.nodeType)
617 }
618 }
619 }
620 }
621 }
622
623 RowLayout {
624 id: missionStatus
625 anchors.margins: _toolsMargin
626 anchors.left: _calcLeftAnchor()
627 anchors.right: rightPanel.left
628 anchors.bottom: parent.bottom
629 spacing: 0
630 visible: !hidden && _editingLayer == _layerMission && QGroundControl.corePlugin.options.showMissionStatus
631
632 readonly property bool hidden: _planViewSettings.showMissionItemStatus.rawValue ? false : true
633
634 function showMissionStatus() {
635 _planViewSettings.showMissionItemStatus.rawValue = true
636 }
637
638 function _calcLeftAnchor() {
639 let bottomOfToolStrip = toolStrip.y + toolStrip.height
640 let largestStatsHeight = Math.max(terrainStatus.height, missionStats.height)
641 if (bottomOfToolStrip + largestStatsHeight > parent.height - missionStatus.anchors.margins) {
642 return toolStrip.right
643 }
644 return parent.left
645 }
646
647 function _toggleMissionStatusVisibility() {
648 _planViewSettings.showMissionItemStatus.rawValue = _planViewSettings.showMissionItemStatus.rawValue ? false : true
649 }
650
651 ColumnLayout {
652 id: missionStatsButtonLayout
653 Layout.alignment: Qt.AlignBottom
654 spacing: 0
655
656 property real _buttonImplicitWidth: ScreenTools.defaultFontPixelHeight * 1.5
657 property real _buttonImageMargins: _buttonImplicitWidth * 0.15
658
659 Rectangle {
660 id: terrainButton
661 implicitWidth: missionStatsButtonLayout._buttonImplicitWidth
662 implicitHeight: implicitWidth
663 color: checked ? QGroundControl.globalPalette.buttonHighlight : QGroundControl.globalPalette.button
664
665 property bool checked: true
666
667 QGCColoredImage {
668 anchors.margins: missionStatsButtonLayout._buttonImageMargins
669 anchors.fill: parent
670 source: "/res/terrain.svg"
671 color: parent.checked ? QGroundControl.globalPalette.buttonHighlightText : QGroundControl.globalPalette.buttonText
672 }
673
674 QGCMouseArea {
675 anchors.fill: parent
676 onClicked: {
677 terrainButton.checked = true
678 missionStatsButton.checked = false
679 }
680 }
681 }
682
683 Rectangle {
684 id: missionStatsButton
685 implicitWidth: missionStatsButtonLayout._buttonImplicitWidth
686 implicitHeight: implicitWidth
687 color: checked ? QGroundControl.globalPalette.buttonHighlight : QGroundControl.globalPalette.button
688
689 property bool checked: false
690
691 QGCColoredImage {
692 anchors.margins: missionStatsButtonLayout._buttonImageMargins
693 anchors.fill: parent
694 source: "/res/sliders.svg"
695 color: parent.checked ? QGroundControl.globalPalette.buttonHighlightText : QGroundControl.globalPalette.buttonText
696 }
697
698 QGCMouseArea {
699 anchors.fill: parent
700 onClicked: {
701 missionStatsButton.checked = true
702 terrainButton.checked = false
703 }
704 }
705 }
706
707 Rectangle {
708 id: bottomStatusOpenCloseButton
709 implicitWidth: missionStatsButtonLayout._buttonImplicitWidth
710 implicitHeight: implicitWidth
711 color: QGroundControl.globalPalette.button
712
713 QGCColoredImage {
714 anchors.margins: missionStatsButtonLayout._buttonImageMargins
715 anchors.fill: parent
716 source: "/res/chevron-double-left.svg"
717 color: QGroundControl.globalPalette.buttonText
718 }
719
720 QGCMouseArea {
721 anchors.fill: parent
722 onClicked: missionStatus._toggleMissionStatusVisibility()
723 }
724 }
725 }
726
727 TerrainStatus {
728 id: terrainStatus
729 Layout.alignment: Qt.AlignBottom
730 Layout.fillWidth: true
731 height: ScreenTools.defaultFontPixelHeight * 7
732 missionController: _missionController
733 visible: terrainButton.checked
734 onSetCurrentSeqNum: _missionController.setCurrentPlanViewSeqNum(seqNum, true)
735 }
736
737 MissionStats {
738 id: missionStats
739 Layout.alignment: Qt.AlignBottom
740 Layout.fillWidth: true
741 visible: missionStatsButton.checked
742 planMasterController: _root._planMasterController
743 }
744 }
745 }
746
747 //- ToolStrip ToolStripDropPanel Components
748
749 Component {
750 id: patternDropPanel
751
752 ColumnLayout {
753 spacing: ScreenTools.defaultFontPixelWidth * 0.5
754
755 QGCLabel { text: qsTr("Create complex pattern:") }
756
757 Repeater {
758 model: _missionController.complexMissionItems
759
760 QGCButton {
761 text: modelData.translatedName
762 Layout.fillWidth: true
763
764 onClicked: {
765 insertComplexItemAfterCurrent(modelData.canonicalName)
766 dropPanel.hide()
767 }
768 }
769 }
770 } // Column
771 }
772
773 QGCPopupDialogFactory {
774 id: promptForPlanUsageOnVehicleChangePopupFactory
775
776 dialogComponent: promptForPlanUsageOnVehicleChangePopupComponent
777 }
778
779 Component {
780 id: promptForPlanUsageOnVehicleChangePopupComponent
781 QGCPopupDialog {
782 title: _planMasterController.managerVehicle.isOfflineEditingVehicle ? qsTr("Plan View - Vehicle Disconnected") : qsTr("Plan View - Vehicle Changed")
783 buttons: Dialog.NoButton
784
785 ColumnLayout {
786 QGCLabel {
787 Layout.maximumWidth: parent.width
788 wrapMode: QGCLabel.WordWrap
789 text: _planMasterController.managerVehicle.isOfflineEditingVehicle ?
790 qsTr("The vehicle associated with the plan in the Plan View is no longer available. What would you like to do with that plan?") : qsTr("The plan being worked on in the Plan View is not from the current vehicle. What would you like to do with that plan?")
791 }
792
793 QGCButton {
794 Layout.fillWidth: true
795 text: (_planMasterController.dirtyForSave) ?
796 (_planMasterController.managerVehicle.isOfflineEditingVehicle ?
797 qsTr("Discard Unsaved Changes") : qsTr("Discard Unsaved Changes, Load New Plan From Vehicle")) : qsTr("Load New Plan From Vehicle")
798 onClicked: {
799 _planMasterController.showPlanFromManagerVehicle()
800 _promptForPlanUsageShowing = false
801 close();
802 }
803 }
804
805 QGCButton {
806 Layout.fillWidth: true
807 text: _planMasterController.managerVehicle.isOfflineEditingVehicle ?
808 qsTr("Keep Current Plan") : qsTr("Keep Current Plan, Don't Update From Vehicle")
809 onClicked: {
810 _promptForPlanUsageShowing = false
811 close()
812 }
813 }
814 }
815 }
816 }
817
818 Component {
819 id: insertOrCancelROIDropPanelComponent
820
821 DropPanel {
822 id: insertOrCancelROIDropPanel
823 onClosed: destroy()
824
825 property var mapClickCoord
826
827 sourceComponent: Component {
828 ColumnLayout {
829 spacing: ScreenTools.defaultFontPixelWidth / 2
830
831 QGCButton {
832 Layout.fillWidth: true
833 text: qsTr("Insert ROI")
834
835 onClicked: {
836 insertOrCancelROIDropPanel.close()
837 insertROIAfterCurrent(mapClickCoord)
838 }
839 }
840
841 QGCButton {
842 Layout.fillWidth: true
843 text: qsTr("Insert Cancel ROI")
844
845 onClicked: {
846 insertOrCancelROIDropPanel.close()
847 insertCancelROIAfterCurrent()
848 }
849 }
850 }
851 }
852 }
853 }
854}