QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MissionItemEditor.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Dialogs
4import QtQml
5import QtQuick.Layouts
6
7import QGroundControl
8import QGroundControl.Controls
9import QGroundControl.FactControls
10
11/// Mission item edit control
12Rectangle {
13 id: _root
14 height: _currentItem ? (editorLoader.y + editorLoader.height + _innerMargin) : (topRowLayout.y + topRowLayout.height + _margin)
15 color: _currentItem ? qgcPal.buttonHighlight : qgcPal.windowShade
16 radius: _radius
17 opacity: _currentItem ? 1.0 : 0.7
18 border.width: _readyForSave ? 0 : 2
19 border.color: qgcPal.warningText
20
21 property var map ///< Map control
22 property var masterController
23 property var missionItem ///< MissionItem associated with this editor
24 property bool readOnly ///< true: read only view, false: full editing view
25
26 signal clicked
27 signal remove
28 signal selectNextNotReadyItem
29
30 property var _masterController: masterController
31 property var _missionController: _masterController.missionController
32 property bool _currentItem: missionItem.isCurrentItem
33 property color _outerTextColor: _currentItem ? qgcPal.buttonHighlightText : qgcPal.text
34 property bool _noMissionItemsAdded: _missionController.visualItems ? _missionController.visualItems.count <= 1 : true
35 property real _sectionSpacer: ScreenTools.defaultFontPixelWidth / 2 // spacing between section headings
36 property bool _singleComplexItem: _missionController.complexMissionItemNames.length === 1
37 property bool _readyForSave: missionItem.readyForSaveState === VisualMissionItem.ReadyForSave
38
39 readonly property real _editFieldWidth: Math.min(width - _innerMargin * 2, ScreenTools.defaultFontPixelWidth * 12)
40 readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2
41 readonly property real _innerMargin: 2
42 readonly property real _radius: ScreenTools.defaultFontPixelWidth / 2
43 readonly property real _hamburgerSize: commandPicker.height * 0.75
44 readonly property real _trashSize: commandPicker.height * 0.75
45 readonly property bool _waypointsOnlyMode: QGroundControl.corePlugin.options.missionWaypointsOnly
46
47 QGCPalette {
48 id: qgcPal
49 colorGroupEnabled: enabled
50 }
51
52 FocusScope {
53 id: currentItemScope
54 anchors.fill: parent
55
56 MouseArea {
57 anchors.fill: parent
58 onClicked: {
59 if (mainWindow.allowViewSwitch()) {
60 currentItemScope.focus = true
61 _root.clicked()
62 }
63 }
64 }
65 }
66
67 QGCPopupDialogFactory {
68 id: editPositionDialogFactory
69
70 dialogComponent: editPositionDialog
71 }
72
73 Component {
74 id: editPositionDialog
75
76 EditPositionDialog {
77 coordinate: missionItem.isSurveyItem ? missionItem.centerCoordinate : missionItem.coordinate
78 onCoordinateChanged: missionItem.isSurveyItem ? missionItem.centerCoordinate = coordinate : missionItem.coordinate = coordinate
79 }
80 }
81
82 Row {
83 id: topRowLayout
84 anchors.margins: _margin
85 anchors.left: parent.left
86 anchors.top: parent.top
87 spacing: _margin
88
89 Rectangle {
90 id: notReadyForSaveIndicator
91 anchors.verticalCenter: parent.verticalCenter
92 width: _hamburgerSize
93 height: width
94 border.width: 1
95 border.color: qgcPal.warningText
96 color: "white"
97 radius: width / 2
98 visible: !_readyForSave
99
100 QGCLabel {
101 id: readyForSaveLabel
102 anchors.centerIn: parent
103 //: Indicator in Plan view to show mission item is not ready for save/send
104 text: qsTr("?")
105 color: qgcPal.warningText
106 font.pointSize: ScreenTools.smallFontPointSize
107 }
108 }
109
110 QGCColoredImage {
111 id: deleteButton
112 anchors.verticalCenter: parent.verticalCenter
113 height: _hamburgerSize
114 width: height
115 sourceSize.height: height
116 fillMode: Image.PreserveAspectFit
117 mipmap: true
118 smooth: true
119 color: qgcPal.buttonHighlightText
120 visible: _currentItem && missionItem.sequenceNumber !== 0
121 source: "/res/TrashDelete.svg"
122
123 QGCMouseArea {
124 fillItem: parent
125 onClicked: remove()
126 }
127 }
128
129 Item {
130 id: commandPicker
131 anchors.verticalCenter: parent.verticalCenter
132 height: ScreenTools.implicitComboBoxHeight
133 width: innerLayout.width
134 visible: !commandLabel.visible
135
136 RowLayout {
137 id: innerLayout
138 anchors.verticalCenter: parent.verticalCenter
139 spacing: _padding
140
141 property real _padding: ScreenTools.comboBoxPadding
142
143 QGCLabel { text: missionItem.commandName }
144
145 QGCColoredImage {
146 height: ScreenTools.defaultFontPixelWidth
147 width: height
148 fillMode: Image.PreserveAspectFit
149 smooth: true
150 antialiasing: true
151 color: qgcPal.text
152 source: "/qmlimages/arrow-down.png"
153 }
154 }
155
156 QGCMouseArea {
157 fillItem: parent
158 onClicked: commandDialogFactory.open()
159 }
160
161 QGCPopupDialogFactory {
162 id: commandDialogFactory
163
164 dialogComponent: commandDialog
165 }
166
167 Component {
168 id: commandDialog
169
170 MissionCommandDialog {
171 vehicle: masterController.controllerVehicle
172 missionItem: _root.missionItem
173 map: _root.map
174 // FIXME: Disabling fly through commands doesn't work since you may need to change from an RTL to something else
175 flyThroughCommandsAllowed: true //_missionController.flyThroughCommandsAllowed
176 }
177 }
178 }
179
180 QGCLabel {
181 id: commandLabel
182 anchors.verticalCenter: parent.verticalCenter
183 width: commandPicker.width
184 height: commandPicker.height
185 visible: !missionItem.isCurrentItem || !missionItem.isSimpleItem || _waypointsOnlyMode || missionItem.isTakeoffItem
186 verticalAlignment: Text.AlignVCenter
187 text: missionItem.commandName
188 color: _outerTextColor
189 }
190 }
191
192 Component {
193 id: hamburgerMenuDropPanelComponent
194
195 DropPanel {
196 id: hamburgerMenuDropPanel
197 onClosed: destroy()
198
199 sourceComponent: Component {
200 ColumnLayout {
201 spacing: ScreenTools.defaultFontPixelHeight / 2
202
203 QGCButton {
204 Layout.fillWidth: true
205 text: qsTr("Move to vehicle position")
206 enabled: _activeVehicle && missionItem.specifiesCoordinate
207
208 onClicked: {
209 missionItem.coordinate = _activeVehicle.coordinate
210 hamburgerMenuDropPanel.close()
211 }
212
213 property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
214 }
215
216 QGCButton {
217 Layout.fillWidth: true
218 text: qsTr("Move to previous item position")
219 enabled: _missionController.previousCoordinate.isValid
220 onClicked: {
221 missionItem.coordinate = _missionController.previousCoordinate
222 hamburgerMenuDropPanel.close()
223 }
224 }
225
226 QGCButton {
227 Layout.fillWidth: true
228 text: qsTr("Edit position...")
229 enabled: missionItem.specifiesCoordinate
230 onClicked: {
231 editPositionDialogFactory.open()
232 hamburgerMenuDropPanel.close()
233 }
234 }
235
236 Rectangle {
237 Layout.fillWidth: true
238 Layout.preferredHeight: 1
239 color: qgcPal.groupBorder
240 }
241
242 QGCCheckBoxSlider {
243 Layout.fillWidth: true
244 text: qsTr("Show all values")
245 visible: QGroundControl.corePlugin.showAdvancedUI
246 checked: missionItem.isSimpleItem ? missionItem.rawEdit : false
247 enabled: missionItem.isSimpleItem && !_waypointsOnlyMode
248
249 onClicked: {
250 missionItem.rawEdit = checked
251 if (missionItem.rawEdit && !missionItem.friendlyEditAllowed) {
252 missionItem.rawEdit = false
253 checked = false
254 QGroundControl.showMessageDialog(_root, qsTr("Mission Edit"), qsTr("You have made changes to the mission item which cannot be shown in Simple Mode"))
255 }
256 hamburgerMenuDropPanel.close()
257 }
258 }
259
260 Rectangle {
261 Layout.fillWidth: true
262 Layout.preferredHeight: 1
263 color: qgcPal.groupBorder
264 }
265
266 QGCLabel {
267 text: qsTr("Item #%1").arg(missionItem.sequenceNumber)
268 enabled: false
269 }
270 }
271 }
272 }
273 }
274
275 Component {
276 id: missionStartHamburgerMenuDropPanelComponent
277
278 DropPanel {
279 id: missionStartHamburgerMenuDropPanel
280 onClosed: destroy()
281
282 sourceComponent: Component {
283 ColumnLayout {
284 spacing: ScreenTools.defaultFontPixelHeight / 2
285
286 QGCButton {
287 Layout.fillWidth: true
288 text: qsTr("Offset mission...")
289 enabled: _missionController.plannedHomePosition.isValid
290 onClicked: {
291 offsetMissionDialogComponent.createObject(mainWindow).open()
292 missionStartHamburgerMenuDropPanel.close()
293 }
294 }
295
296 QGCButton {
297 Layout.fillWidth: true
298 text: qsTr("Rotate mission...")
299 enabled: _missionController.plannedHomePosition.isValid
300 onClicked: {
301 rotateMissionDialogComponent.createObject(mainWindow).open()
302 missionStartHamburgerMenuDropPanel.close()
303 }
304 }
305
306 QGCButton {
307 Layout.fillWidth: true
308 text: qsTr("Edit mission position...")
309 enabled: _missionController.plannedHomePosition.isValid
310 onClicked: {
311 editMissionPositionDialogComponent.createObject(mainWindow).open()
312 missionStartHamburgerMenuDropPanel.close()
313 }
314 }
315 }
316 }
317 }
318 }
319
320 Component {
321 id: offsetMissionDialogComponent
322
323 QGCPopupDialog {
324 id: root
325 title: qsTr("Offset Mission")
326 buttons: Dialog.Cancel | Dialog.Ok
327 modal: true
328
329 property real _margin: ScreenTools.defaultFontPixelWidth / 2
330 property real _textFieldWidth: ScreenTools.defaultFontPixelWidth * 20
331 property real _labelWidth: ScreenTools.defaultFontPixelWidth * 14
332
333 ColumnLayout {
334 spacing: _margin
335
336 RowLayout {
337 spacing: _margin
338 Layout.fillWidth: true
339
340 QGCLabel {
341 text: qsTr("East (m)")
342 Layout.preferredWidth: _labelWidth
343 Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
344 }
345
346 QGCTextField {
347 id: eastField
348 text: "0"
349 Layout.preferredWidth: _textFieldWidth
350 Layout.fillWidth: true
351 inputMethodHints: Qt.ImhPreferNumbers
352 validator: DoubleValidator {
353 notation: DoubleValidator.StandardNotation
354 locale: Qt.locale()
355 }
356 }
357 }
358
359 RowLayout {
360 spacing: _margin
361 Layout.fillWidth: true
362
363 QGCLabel {
364 text: qsTr("North (m)")
365 Layout.preferredWidth: _labelWidth
366 Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
367 }
368
369 QGCTextField {
370 id: northField
371 text: "0"
372 Layout.preferredWidth: _textFieldWidth
373 Layout.fillWidth: true
374 inputMethodHints: Qt.ImhPreferNumbers
375 validator: DoubleValidator {
376 notation: DoubleValidator.StandardNotation
377 locale: Qt.locale()
378 }
379 }
380 }
381
382 RowLayout {
383 spacing: _margin
384 Layout.fillWidth: true
385
386 QGCLabel {
387 text: qsTr("Up (m)")
388 Layout.preferredWidth: _labelWidth
389 Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
390 }
391
392 QGCTextField {
393 id: upField
394 text: "0"
395 Layout.preferredWidth: _textFieldWidth
396 Layout.fillWidth: true
397 inputMethodHints: Qt.ImhPreferNumbers
398 validator: DoubleValidator {
399 notation: DoubleValidator.StandardNotation
400 locale: Qt.locale()
401 }
402 }
403 }
404
405 ColumnLayout {
406 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
407
408 QGCCheckBox {
409 id: offsetTakeoffCheck
410 text: qsTr("Also move takeoff items")
411 Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
412 }
413
414 QGCCheckBox {
415 id: offsetLandingCheck
416 text: qsTr("Also move landing items")
417 Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
418 }
419 }
420
421 QGCLabel {
422 Layout.fillWidth: true
423 Layout.maximumWidth: _labelWidth + _textFieldWidth
424
425 wrapMode: Text.WordWrap
426 font.pointSize: ScreenTools.smallFontPointSize
427
428 text: qsTr("Note: Home altitude is not modified.")
429 }
430 }
431
432 onAccepted: {
433 var east = Number.fromLocaleString(Qt.locale(), eastField.text)
434 var north = Number.fromLocaleString(Qt.locale(), northField.text)
435 var up = Number.fromLocaleString(Qt.locale(), upField.text)
436
437 if (isNaN(east)) east = 0
438 if (isNaN(north)) north = 0
439 if (isNaN(up)) up = 0
440
441 _missionController.offsetMission(
442 east,
443 north,
444 up,
445 offsetTakeoffCheck.checked,
446 offsetLandingCheck.checked
447 )
448 }
449 }
450 }
451
452 Component {
453 id: rotateMissionDialogComponent
454
455 QGCPopupDialog {
456 id: root
457 title: qsTr("Rotate Mission")
458 buttons: Dialog.Cancel | Dialog.Ok
459 modal: true
460
461 property real _margin: ScreenTools.defaultFontPixelWidth / 2
462 property real _textFieldWidth: ScreenTools.defaultFontPixelWidth * 20
463 property real _labelWidth: ScreenTools.defaultFontPixelWidth * 14
464
465 ColumnLayout {
466 spacing: _margin
467
468 RowLayout {
469 spacing: _margin
470 Layout.fillWidth: true
471
472 QGCLabel {
473 text: qsTr("Clockwise (°)")
474 Layout.preferredWidth: _labelWidth
475 Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
476 }
477
478 QGCTextField {
479 id: degreesCWField
480 text: "0"
481 Layout.preferredWidth: _textFieldWidth
482 Layout.fillWidth: true
483 inputMethodHints: Qt.ImhPreferNumbers
484 validator: DoubleValidator {
485 notation: DoubleValidator.StandardNotation
486 locale: Qt.locale()
487 }
488 }
489 }
490
491 ColumnLayout {
492 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
493
494 QGCCheckBox {
495 id: rotateTakeoffCheck
496 text: qsTr("Also move takeoff items")
497 Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
498 }
499
500 QGCCheckBox {
501 id: rotateLandingCheck
502 text: qsTr("Also move landing items")
503 Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
504 }
505 }
506
507 QGCLabel {
508 Layout.fillWidth: true
509 Layout.maximumWidth: _labelWidth + _textFieldWidth
510
511 wrapMode: Text.WordWrap
512 font.pointSize: ScreenTools.smallFontPointSize
513
514 text: qsTr("Note: Complex items are rotated by moving their reference coordinate: their geometry and orientation are not changed.")
515 }
516 }
517
518 onAccepted: {
519 var degreesCW = Number.fromLocaleString(Qt.locale(), degreesCWField.text)
520
521 if (isNaN(degreesCW)) degreesCW = 0
522
523 _missionController.rotateMission(
524 degreesCW,
525 rotateTakeoffCheck.checked,
526 rotateLandingCheck.checked
527 )
528 }
529 }
530 }
531
532 Component {
533 id: editMissionPositionDialogComponent
534
535 EditPositionDialog {
536 id: missionEditPositionDialog
537
538 coordinate: _missionController.plannedHomePosition
539 onCoordinateChanged: _missionController.repositionMission(coordinate)
540 }
541 }
542
543
544 QGCColoredImage {
545 id: hamburger
546 anchors.margins: _margin
547 anchors.right: parent.right
548 anchors.verticalCenter: topRowLayout.verticalCenter
549 width: _hamburgerSize
550 height: _hamburgerSize
551 sourceSize.height: _hamburgerSize
552 source: "qrc:/qmlimages/Hamburger.svg"
553 visible: missionItem.isCurrentItem
554 color: qgcPal.buttonHighlightText
555
556 QGCMouseArea {
557 fillItem: hamburger
558 onClicked: (position) => {
559 currentItemScope.focus = true
560 position = Qt.point(position.x, position.y)
561 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
562 position = mapToItem(globals.parent, position)
563
564 var panelComponent = (missionItem.sequenceNumber === 0)
565 ? missionStartHamburgerMenuDropPanelComponent
566 : hamburgerMenuDropPanelComponent
567 var dropPanel = panelComponent.createObject(mainWindow, { clickRect: Qt.rect(position.x, position.y, 0, 0) })
568 dropPanel.open()
569 }
570 }
571 }
572
573 /*
574 QGCLabel {
575 id: notReadyForSaveLabel
576 anchors.margins: _margin
577 anchors.left: notReadyForSaveIndicator.right
578 anchors.right: parent.right
579 anchors.top: commandPicker.bottom
580 visible: _currentItem && !_readyForSave
581 text: missionItem.readyForSaveState === VisualMissionItem.NotReadyForSaveTerrain ?
582 qsTr("Incomplete: Waiting on terrain data.") :
583 qsTr("Incomplete: Item not fully specified.")
584 wrapMode: Text.WordWrap
585 horizontalAlignment: Text.AlignHCenter
586 color: qgcPal.warningText
587 }
588
589*/
590
591 Loader {
592 id: editorLoader
593 anchors.margins: _innerMargin
594 anchors.left: parent.left
595 anchors.top: topRowLayout.bottom
596 source: _currentItem ? missionItem.editorQml : ""
597 asynchronous: true
598
599 property var masterController: _masterController
600 property real availableWidth: _root.width - (anchors.margins * 2) ///< How wide the editor should be
601 property var editorRoot: _root
602 }
603}