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 required property var missionItem ///< MissionItem associated with this editor
14 required property var map ///< Map control
15
16 signal clicked
17 signal remove
18 signal selectNextNotReadyItem
19 signal editorExpandedAndLoaded
20
21 id: _root
22 height: _currentItem ? (editorLoader.y + editorLoader.height + _innerMargin) : (topRowLayout.y + topRowLayout.height + _margin)
23 color: _currentItem ? qgcPal.buttonHighlight : qgcPal.windowShade
24 radius: _radius
25 opacity: _currentItem ? 1.0 : 0.7
26 border.width: _readyForSave ? 0 : 2
27 border.color: qgcPal.warningText
28
29 property var _masterController: missionItem.masterController
30 property var _missionController: _masterController.missionController
31 property bool _currentItem: missionItem.isCurrentItem
32 property color _outerTextColor: _currentItem ? qgcPal.buttonHighlightText : qgcPal.text
33 property bool _noMissionItemsAdded: _missionController.visualItems ? _missionController.visualItems.count <= 1 : true
34 property real _sectionSpacer: ScreenTools.defaultFontPixelWidth / 2 // spacing between section headings
35 property bool _singleComplexItem: _missionController.complexMissionItems.length === 1
36 property bool _readyForSave: missionItem.readyForSaveState === VisualMissionItem.ReadyForSave
37
38 readonly property real _editFieldWidth: Math.min(width - _innerMargin * 2, ScreenTools.defaultFontPixelWidth * 12)
39 readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2
40 readonly property real _innerMargin: 2
41 readonly property real _radius: ScreenTools.defaultFontPixelWidth / 2
42 readonly property real _hamburgerSize: commandPicker.height * 0.75
43 readonly property real _trashSize: commandPicker.height * 0.75
44 readonly property bool _waypointsOnlyMode: QGroundControl.corePlugin.options.missionWaypointsOnly
45
46 // setSource() injects missionItem before internal bindings activate
47 function _loadEditor() {
48 if (missionItem.isCurrentItem) {
49 editorLoader.setSource(missionItem.editorQml, {
50 missionItem: _root.missionItem,
51 availableWidth: _root.width - (editorLoader.anchors.margins * 2)
52 })
53 } else {
54 editorLoader.setSource("")
55 }
56 }
57
58 Connections {
59 target: missionItem
60 function onIsCurrentItemChanged() { _root._loadEditor() }
61 }
62
63 QGCPalette {
64 id: qgcPal
65 colorGroupEnabled: enabled
66 }
67
68 FocusScope {
69 id: currentItemScope
70 anchors.fill: parent
71
72 MouseArea {
73 anchors.fill: parent
74 onClicked: {
75 if (mainWindow.allowViewSwitch()) {
76 currentItemScope.focus = true
77 _root.clicked()
78 }
79 }
80 }
81 }
82
83 QGCPopupDialogFactory {
84 id: editPositionDialogFactory
85
86 dialogComponent: editPositionDialog
87 }
88
89 Component {
90 id: editPositionDialog
91
92 EditPositionDialog {
93 property bool _editCenterCoordinate: false
94
95 onCoordinateChanged: {
96 if (_editCenterCoordinate)
97 missionItem.centerCoordinate = coordinate
98 else
99 missionItem.coordinate = coordinate
100 }
101 }
102 }
103
104 Row {
105 id: topRowLayout
106 anchors.margins: _margin
107 anchors.left: parent.left
108 anchors.top: parent.top
109 spacing: _margin
110
111 Rectangle {
112 id: notReadyForSaveIndicator
113 anchors.verticalCenter: parent.verticalCenter
114 width: _hamburgerSize
115 height: width
116 border.width: 1
117 border.color: qgcPal.warningText
118 color: "white"
119 radius: width / 2
120 visible: !_readyForSave
121
122 QGCLabel {
123 id: readyForSaveLabel
124 anchors.centerIn: parent
125 //: Indicator in Plan view to show mission item is not ready for save/send
126 text: qsTr("?")
127 color: qgcPal.warningText
128 font.pointSize: ScreenTools.smallFontPointSize
129 }
130 }
131
132 QGCColoredImage {
133 id: deleteButton
134 anchors.verticalCenter: parent.verticalCenter
135 height: _hamburgerSize
136 width: height
137 sourceSize.height: height
138 fillMode: Image.PreserveAspectFit
139 mipmap: true
140 smooth: true
141 color: qgcPal.buttonHighlightText
142 visible: _currentItem && missionItem.sequenceNumber !== 0
143 source: "/res/TrashDelete.svg"
144
145 QGCMouseArea {
146 fillItem: parent
147 onClicked: remove()
148 }
149 }
150
151 Item {
152 id: commandPicker
153 anchors.verticalCenter: parent.verticalCenter
154 height: ScreenTools.implicitComboBoxHeight
155 width: innerLayout.width
156 visible: !commandLabel.visible
157
158 RowLayout {
159 id: innerLayout
160 anchors.verticalCenter: parent.verticalCenter
161 spacing: _padding
162
163 property real _padding: ScreenTools.comboBoxPadding
164
165 QGCLabel { text: missionItem.commandName }
166
167 QGCColoredImage {
168 height: ScreenTools.defaultFontPixelWidth
169 width: height
170 fillMode: Image.PreserveAspectFit
171 smooth: true
172 antialiasing: true
173 color: qgcPal.text
174 source: "/qmlimages/arrow-down.png"
175 }
176 }
177
178 QGCMouseArea {
179 fillItem: parent
180 onClicked: commandDialogFactory.open()
181 }
182
183 QGCPopupDialogFactory {
184 id: commandDialogFactory
185
186 dialogComponent: commandDialog
187 }
188
189 Component {
190 id: commandDialog
191
192 MissionCommandDialog {
193 vehicle: _masterController.controllerVehicle
194 missionItem: _root.missionItem
195 map: _root.map
196 // FIXME: Disabling fly through commands doesn't work since you may need to change from an RTL to something else
197 flyThroughCommandsAllowed: true //_missionController.flyThroughCommandsAllowed
198 }
199 }
200 }
201
202 QGCLabel {
203 id: commandLabel
204 anchors.verticalCenter: parent.verticalCenter
205 width: commandPicker.width
206 height: commandPicker.height
207 visible: !missionItem.isCurrentItem || !missionItem.isSimpleItem || _waypointsOnlyMode || missionItem.isTakeoffItem
208 verticalAlignment: Text.AlignVCenter
209 text: missionItem.commandName
210 color: _outerTextColor
211 }
212 }
213
214 Component {
215 id: hamburgerMenuDropPanelComponent
216
217 DropPanel {
218 id: hamburgerMenuDropPanel
219 onClosed: destroy()
220
221 sourceComponent: Component {
222 ColumnLayout {
223 spacing: ScreenTools.defaultFontPixelHeight / 2
224
225 QGCButton {
226 Layout.fillWidth: true
227 text: qsTr("Move to vehicle position")
228 enabled: _activeVehicle && missionItem.specifiesCoordinate
229
230 onClicked: {
231 missionItem.coordinate = _activeVehicle.coordinate
232 hamburgerMenuDropPanel.close()
233 }
234
235 property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
236 }
237
238 QGCButton {
239 Layout.fillWidth: true
240 text: qsTr("Move to previous item position")
241 enabled: _missionController.previousCoordinate.isValid
242 onClicked: {
243 missionItem.coordinate = _missionController.previousCoordinate
244 hamburgerMenuDropPanel.close()
245 }
246 }
247
248 QGCButton {
249 Layout.fillWidth: true
250 text: qsTr("Edit position...")
251 enabled: missionItem.specifiesCoordinate
252 onClicked: {
253 const editCenterCoordinate = missionItem.isSurveyItem
254 editPositionDialogFactory.open({
255 _editCenterCoordinate: editCenterCoordinate,
256 coordinate: editCenterCoordinate ? missionItem.centerCoordinate : missionItem.coordinate,
257 altitudeFact: !editCenterCoordinate && missionItem.specifiesAltitude ? missionItem.altitude : null,
258 altitudeFrame: !editCenterCoordinate && missionItem.specifiesAltitude ? missionItem.altitudeFrame : QGroundControl.AltitudeFrameNone,
259 })
260 hamburgerMenuDropPanel.close()
261 }
262 }
263
264 Rectangle {
265 Layout.fillWidth: true
266 Layout.preferredHeight: 1
267 color: qgcPal.groupBorder
268 }
269
270 QGCCheckBoxSlider {
271 Layout.fillWidth: true
272 text: qsTr("Show all values")
273 visible: QGroundControl.corePlugin.showAdvancedUI
274 checked: missionItem.isSimpleItem ? missionItem.rawEdit : false
275 enabled: missionItem.isSimpleItem && !_waypointsOnlyMode
276
277 onClicked: {
278 missionItem.rawEdit = checked
279 if (missionItem.rawEdit && !missionItem.friendlyEditAllowed) {
280 missionItem.rawEdit = false
281 checked = false
282 QGroundControl.showMessageDialog(_root, qsTr("Mission Edit"), qsTr("You have made changes to the mission item which cannot be shown in Simple Mode"))
283 }
284 hamburgerMenuDropPanel.close()
285 }
286 }
287
288 Rectangle {
289 Layout.fillWidth: true
290 Layout.preferredHeight: 1
291 color: qgcPal.groupBorder
292 }
293
294 QGCLabel {
295 text: qsTr("Item #%1").arg(missionItem.sequenceNumber)
296 enabled: false
297 }
298 }
299 }
300 }
301 }
302
303 QGCColoredImage {
304 id: hamburger
305 anchors.margins: _margin
306 anchors.right: parent.right
307 anchors.verticalCenter: topRowLayout.verticalCenter
308 width: _hamburgerSize
309 height: _hamburgerSize
310 sourceSize.height: _hamburgerSize
311 source: "qrc:/qmlimages/Hamburger.svg"
312 visible: missionItem.isCurrentItem && missionItem.sequenceNumber !== 0
313 color: qgcPal.buttonHighlightText
314
315 QGCMouseArea {
316 fillItem: hamburger
317 onClicked: (position) => {
318 currentItemScope.focus = true
319 position = Qt.point(position.x, position.y)
320 // For some strange reason using mainWindow in mapToItem doesn't work, so we use globals.parent instead which also gets us mainWindow
321 position = mapToItem(globals.parent, position)
322
323 var dropPanel = hamburgerMenuDropPanelComponent.createObject(mainWindow, { clickRect: Qt.rect(position.x, position.y, 0, 0) })
324 dropPanel.open()
325 }
326 }
327 }
328
329 /*
330 QGCLabel {
331 id: notReadyForSaveLabel
332 anchors.margins: _margin
333 anchors.left: notReadyForSaveIndicator.right
334 anchors.right: parent.right
335 anchors.top: commandPicker.bottom
336 visible: _currentItem && !_readyForSave
337 text: missionItem.readyForSaveState === VisualMissionItem.NotReadyForSaveTerrain ?
338 qsTr("Incomplete: Waiting on terrain data.") :
339 qsTr("Incomplete: Item not fully specified.")
340 wrapMode: Text.WordWrap
341 horizontalAlignment: Text.AlignHCenter
342 color: qgcPal.warningText
343 }
344
345*/
346
347 Loader {
348 id: editorLoader
349 anchors.margins: _innerMargin
350 anchors.left: parent.left
351 anchors.top: topRowLayout.bottom
352 asynchronous: true
353
354 Component.onCompleted: _root._loadEditor()
355 }
356
357 onHeightChanged: {
358 if (_currentItem && editorLoader.status === Loader.Ready) {
359 _editorHeightSettleTimer.restart()
360 }
361 }
362
363 Timer {
364 id: _editorHeightSettleTimer
365 interval: 100
366 onTriggered: _root.editorExpandedAndLoaded()
367 }
368}