QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MainWindow.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Dialogs
4import QtQuick.Layouts
5import QtQuick.Window
6
7import QGroundControl
8import QGroundControl.Controls
9import QGroundControl.FactControls
10import QGroundControl.FlyView
11import QGroundControl.FlightMap
12import QGroundControl.PlanView
13import QGroundControl.Toolbar
14
15/// @brief Native QML top level window
16/// All properties defined here are visible to all QML pages.
17ApplicationWindow {
18 id: mainWindow
19 visible: true
20 // The special casing for android prevents white bars from showing up on the edges of the screen with newer android versions
21 flags: Qt.Window | (ScreenTools.isAndroid ? Qt.ExpandedClientAreaHint | Qt.NoTitleBarBackgroundHint : 0)
22
23 Component.onCompleted: {
24 // Start the sequence of first run prompt(s)
25 firstRunPromptManager.nextPrompt()
26 }
27
28 /// Saves main window position and size and re-opens it in the same position and size next time
29 MainWindowSavedState {
30 window: mainWindow
31 }
32
33 QtObject {
34 id: firstRunPromptManager
35
36 property var currentDialog: null
37 property var rgPromptIds: QGroundControl.corePlugin.firstRunPromptsToShow()
38 property int nextPromptIdIndex: 0
39
40 function clearNextPromptSignal() {
41 if (currentDialog) {
42 currentDialog.closed.disconnect(nextPrompt)
43 }
44 }
45
46 function nextPrompt() {
47 if (nextPromptIdIndex < rgPromptIds.length) {
48 var component = Qt.createComponent(QGroundControl.corePlugin.firstRunPromptResource(rgPromptIds[nextPromptIdIndex]));
49 currentDialog = component.createObject(mainWindow)
50 currentDialog.closed.connect(nextPrompt)
51 currentDialog.open()
52 nextPromptIdIndex++
53 } else {
54 currentDialog = null
55 showPreFlightChecklistIfNeeded()
56 }
57 }
58 }
59
60 readonly property real _topBottomMargins: ScreenTools.defaultFontPixelHeight * 0.5
61
62 //-------------------------------------------------------------------------
63 //-- Global Scope Variables
64
65 QtObject {
66 id: globals
67
68 readonly property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
69 readonly property real defaultTextHeight: ScreenTools.defaultFontPixelHeight
70 readonly property real defaultTextWidth: ScreenTools.defaultFontPixelWidth
71 readonly property var planMasterControllerFlyView: flyView.planController
72 readonly property var guidedControllerFlyView: flyView.guidedController
73
74 // Number of QGCTextField's with validation errors. Used to prevent closing panels with validation errors.
75 property int validationErrorCount: 0
76
77 // Set to a non-empty string to block navigation with a custom reason (e.g. during calibration)
78 property string navigationBlockedReason: ""
79
80 // Property to manage RemoteID quick access to settings page
81 property bool commingFromRIDIndicator: false
82 }
83
84 /// Default color palette used throughout the UI
85 QGCPalette { id: qgcPal; colorGroupEnabled: true }
86
87 //-------------------------------------------------------------------------
88 //-- Actions
89
90 signal armVehicleRequest
91 signal forceArmVehicleRequest
92 signal disarmVehicleRequest
93 signal vtolTransitionToFwdFlightRequest
94 signal vtolTransitionToMRFlightRequest
95 signal showPreFlightChecklistIfNeeded
96
97 //-------------------------------------------------------------------------
98 //-- Global Scope Functions
99
100 // This function is used to prevent view switching if there are validation errors
101 function allowViewSwitch(previousValidationErrorCount = 0, showErrorOnDisallow = true) {
102 // Check for explicit navigation block (e.g. calibration in progress)
103 if (globals.navigationBlockedReason !== "") {
104 if (showErrorOnDisallow) {
105 validationErrorToast.text = globals.navigationBlockedReason
106 if (validationErrorToast.visible) {
107 validationErrorToast.close()
108 }
109 validationErrorToast.open()
110 }
111 return false
112 }
113 // Run validation on active focus control to ensure it is valid before switching views
114 if (mainWindow.activeFocusControl instanceof FactTextField) {
115 mainWindow.activeFocusControl._onEditingFinished()
116 }
117 var allowed = globals.validationErrorCount <= previousValidationErrorCount
118 if (!allowed && showErrorOnDisallow) {
119 validationErrorToast.text = qsTr("Please correct the invalid value before continuing")
120 if (validationErrorToast.visible) {
121 validationErrorToast.close()
122 }
123 validationErrorToast.open()
124 }
125 return allowed
126 }
127
128 function showPlanView() {
129 flyView.visible = false
130 planView.visible = true
131 toolDrawer.visible = false
132 }
133
134 function showFlyView() {
135 flyView.visible = true
136 planView.visible = false
137 toolDrawer.visible = false
138 }
139
140 function showTool(toolTitle, toolSource, toolIcon) {
141 toolDrawer.backIcon = flyView.visible ? "/qmlimages/PaperPlane.svg" : "/qmlimages/Plan.svg"
142 toolDrawer.toolTitle = toolTitle
143 toolDrawer.toolSource = toolSource
144 toolDrawer.toolIcon = toolIcon
145 toolDrawer.visible = true
146 }
147
148 function showAnalyzeTool() {
149 showTool(qsTr("Analyze Tools"), "qrc:/qml/QGroundControl/AnalyzeView/AnalyzeView.qml", "/qmlimages/Analyze.svg")
150 }
151
152 function showVehicleConfig() {
153 showTool(qsTr("Vehicle Configuration"), "qrc:/qml/QGroundControl/VehicleSetup/VehicleConfigView.qml", "/qmlimages/Gears.svg")
154 }
155
156 function showVehicleConfigParametersPage() {
157 showVehicleConfig()
158 toolDrawerLoader.item.showParametersPanel()
159 }
160
161 function showKnownVehicleComponentConfigPage(knownVehicleComponent) {
162 showVehicleConfig()
163 let vehicleComponent = globals.activeVehicle.autopilotPlugin.findKnownVehicleComponent(knownVehicleComponent)
164 if (vehicleComponent) {
165 toolDrawerLoader.item.showVehicleComponentPanel(vehicleComponent)
166 }
167 }
168
169 function showSettingsTool(settingsPage = "") {
170 showTool(qsTr("Application Settings"), "qrc:/qml/QGroundControl/Controls/AppSettings.qml", "/res/QGCLogoWhite")
171 if (settingsPage !== "") {
172 toolDrawerLoader.item.showSettingsPage(settingsPage)
173 }
174 }
175
176 //-------------------------------------------------------------------------
177 //-- Global simple message dialog
178
179 function _showMessageDialogWorker(owner, dialogTitle, dialogText, buttons = Dialog.Ok, acceptFunction = null, closeFunction = null, bypassNavigationCheck = false) {
180 let dialog = simpleMessageDialogComponent.createObject(owner, { title: dialogTitle, text: dialogText, buttons: buttons, acceptFunction: acceptFunction, closeFunction: closeFunction, bypassNavigationCheck: bypassNavigationCheck })
181 dialog.open()
182 }
183
184 // This variant is only meant to be called by QGCApplication
185 function _showMessageDialog(dialogTitle, dialogText) {
186 _showMessageDialogWorker(mainWindow, dialogTitle, dialogText)
187 }
188
189 Connections {
190 target: QGroundControl
191
192 function onShowMessageDialogRequested(owner, title, text, buttons, acceptFunction, closeFunction) {
193 _showMessageDialogWorker(owner, title, text, buttons, acceptFunction, closeFunction)
194 }
195 }
196
197 Component {
198 id: simpleMessageDialogComponent
199
200 QGCSimpleMessageDialog {
201 }
202 }
203
204 property bool _forceClose: false
205 property bool suppressCriticalVehicleMessages: false
206
207 function finishCloseProcess() {
208 _forceClose = true
209 // For some reason on the Qml side Qt doesn't automatically disconnect a signal when an object is destroyed.
210 // So we have to do it ourselves otherwise the signal flows through on app shutdown to an object which no longer exists.
211 firstRunPromptManager.clearNextPromptSignal()
212 QGroundControl.linkManager.shutdown()
213 QGroundControl.videoManager.stopVideo();
214 mainWindow.close()
215 }
216
217 // Check for things which should prevent the app from closing
218 // Returns true if it is OK to close
219 readonly property int _skipUnsavedMissionCheckMask: 0x01
220 readonly property int _skipPendingParameterWritesCheckMask: 0x02
221 readonly property int _skipActiveConnectionsCheckMask: 0x04
222 property int _closeChecksToSkip: 0
223 property bool _reentrantCloseGuard: false
224 function performCloseChecks() {
225 if (!(_closeChecksToSkip & _skipUnsavedMissionCheckMask) && !checkForUnsavedMission()) {
226 return false
227 }
228 if (!(_closeChecksToSkip & _skipPendingParameterWritesCheckMask) && !checkForPendingParameterWrites()) {
229 return false
230 }
231 if (!(_closeChecksToSkip & _skipActiveConnectionsCheckMask) && !checkForActiveConnections()) {
232 return false
233 }
234 finishCloseProcess()
235 return true
236 }
237
238 function checkForUnsavedMission() {
239 if (planView._planMasterController.dirtyForSave || planView._planMasterController.dirtyForUpload) {
240 let accepted = false
241 _reentrantCloseGuard = true
242 _showMessageDialogWorker(mainWindow, qsTr("Unsaved Mission"),
243 qsTr("You have a mission edit in progress which has not been saved/uploaded. If you close you will lose changes. Are you sure you want to close?"),
244 Dialog.Yes | Dialog.No,
245 function() { accepted = true; _closeChecksToSkip |= _skipUnsavedMissionCheckMask; performCloseChecks() },
246 function() { if (!accepted) _reentrantCloseGuard = false },
247 true /* bypassNavigationCheck */)
248 return false
249 } else {
250 return true
251 }
252 }
253
254 function checkForPendingParameterWrites() {
255 for (var index=0; index<QGroundControl.multiVehicleManager.vehicles.count; index++) {
256 if (QGroundControl.multiVehicleManager.vehicles.get(index).parameterManager.pendingWrites) {
257 let accepted = false
258 _reentrantCloseGuard = true
259 _showMessageDialogWorker(mainWindow, qsTr("Pending Parameter Updates"),
260 qsTr("You have pending parameter updates to a vehicle. If you close you will lose changes. Are you sure you want to close?"),
261 Dialog.Yes | Dialog.No,
262 function() { accepted = true; _closeChecksToSkip |= _skipPendingParameterWritesCheckMask; performCloseChecks() },
263 function() { if (!accepted) _reentrantCloseGuard = false },
264 true /* bypassNavigationCheck */)
265 return false
266 }
267 }
268 return true
269 }
270
271 function checkForActiveConnections() {
272 if (QGroundControl.multiVehicleManager.activeVehicle) {
273 let accepted = false
274 _reentrantCloseGuard = true
275 _showMessageDialogWorker(mainWindow, qsTr("Active Vehicle Connections"),
276 qsTr("There are still active connections to vehicles. Are you sure you want to exit?"),
277 Dialog.Yes | Dialog.No,
278 function() { accepted = true; _closeChecksToSkip |= _skipActiveConnectionsCheckMask; performCloseChecks() },
279 function() { if (!accepted) _reentrantCloseGuard = false },
280 true /* bypassNavigationCheck */)
281 return false
282 } else {
283 return true
284 }
285 }
286
287 onClosing: (close) => {
288 if (!_forceClose) {
289 if (_reentrantCloseGuard) {
290 close.accepted = false
291 return
292 }
293 _closeChecksToSkip = 0
294 close.accepted = performCloseChecks()
295 }
296 }
297
298 background: Rectangle {
299 anchors.fill: parent
300 color: QGroundControl.globalPalette.window
301 }
302
303 FlyView {
304 id: flyView
305 objectName: "mainView_fly"
306 anchors.fill: parent
307 }
308
309 PlanView {
310 id: planView
311 objectName: "mainView_plan"
312 anchors.fill: parent
313 visible: false
314 }
315
316 footer: LogReplayStatusBar {
317 visible: QGroundControl.settingsManager.flyViewSettings.showLogReplayStatusBar.rawValue
318 }
319
320 MessageDialog {
321 id: showTouchAreasNotification
322 title: qsTr("Debug Touch Areas")
323 text: qsTr("Touch Area display toggled")
324 buttons: MessageDialog.Ok
325 }
326
327 MessageDialog {
328 id: advancedModeOnConfirmation
329 title: qsTr("Advanced Mode")
330 text: QGroundControl.corePlugin.showAdvancedUIMessage
331 buttons: MessageDialog.Yes | MessageDialog.No
332 onButtonClicked: function (button, role) {
333 if (button === MessageDialog.Yes) {
334 QGroundControl.corePlugin.showAdvancedUI = true
335 }
336 }
337 }
338
339 MessageDialog {
340 id: advancedModeOffConfirmation
341 title: qsTr("Advanced Mode")
342 text: qsTr("Turn off Advanced Mode?")
343 buttons: MessageDialog.Yes | MessageDialog.No
344 onButtonClicked: function (button, role) {
345 if (button === MessageDialog.Yes) {
346 QGroundControl.corePlugin.showAdvancedUI = false
347 }
348 }
349 }
350
351 function showToolSelectDialog() {
352 if (mainWindow.allowViewSwitch()) {
353 mainWindow.showIndicatorDrawer(toolSelectComponent, null)
354 }
355 }
356
357 // Toast notification shown when a view switch is blocked by a validation error
358 ToolTip {
359 id: validationErrorToast
360 x: (mainWindow.width - width) / 2
361 y: mainWindow.height - height - ScreenTools.defaultFontPixelHeight * 3
362 timeout: 3000
363 closePolicy: Popup.NoAutoClose
364 text: qsTr("Please correct the invalid value before continuing")
365
366 background: Rectangle {
367 color: qgcPal.alertBackground
368 radius: ScreenTools.defaultFontPixelWidth / 2
369 }
370
371 contentItem: QGCLabel {
372 text: validationErrorToast.text
373 color: qgcPal.alertText
374 }
375 }
376
377 Component {
378 id: toolSelectComponent
379
380 SelectViewDropdown {
381 }
382 }
383
384 Rectangle {
385 id: toolDrawer
386 objectName: "mainView_toolDrawer"
387 anchors.fill: parent
388 visible: false
389 color: qgcPal.window
390
391 property var backIcon
392 property string toolTitle
393 property alias toolSource: toolDrawerLoader.source
394 property var toolIcon
395
396 onVisibleChanged: {
397 if (!toolDrawer.visible) {
398 toolDrawerLoader.source = ""
399 }
400 }
401
402 // This need to block click event leakage to underlying map.
403 DeadMouseArea {
404 anchors.fill: parent
405 }
406
407 Rectangle {
408 id: toolDrawerToolbar
409 anchors.left: parent.left
410 anchors.right: parent.right
411 anchors.top: parent.top
412 height: ScreenTools.toolbarHeight
413 color: qgcPal.toolbarBackground
414
415 RowLayout {
416 id: toolDrawerToolbarLayout
417 anchors.leftMargin: ScreenTools.defaultFontPixelWidth
418 anchors.left: parent.left
419 anchors.top: parent.top
420 anchors.bottom: parent.bottom
421 spacing: ScreenTools.defaultFontPixelWidth
422
423 QGCToolBarButton {
424 id: qgcButton
425 objectName: "toolbar_qgcLogo"
426 height: parent.height
427 icon.source: "/res/QGCLogoFull.svg"
428 logo: true
429 onClicked: mainWindow.showToolSelectDialog()
430 }
431
432 QGCLabel {
433 id: toolbarDrawerText
434 text: toolDrawer.toolTitle
435 font.pointSize: ScreenTools.largeFontPointSize
436 }
437 }
438 }
439
440 Loader {
441 id: toolDrawerLoader
442 anchors.left: parent.left
443 anchors.right: parent.right
444 anchors.top: toolDrawerToolbar.bottom
445 anchors.bottom: parent.bottom
446 }
447 }
448
449 //-------------------------------------------------------------------------
450 //-- Critical Vehicle Message Popup
451
452 function showCriticalVehicleMessage(message) {
453 if (suppressCriticalVehicleMessages) {
454 return
455 }
456 if (criticalVehicleMessagePopup.visible || QGroundControl.videoManager.fullScreen) {
457 // We received additional warning message while an older warning message was still displayed.
458 // When the user close the older one drop the message indicator tool so they can see the rest of them.
459 criticalVehicleMessagePopup.additionalCriticalMessagesReceived = true
460 } else {
461 criticalVehicleMessagePopup.criticalVehicleMessage = message
462 criticalVehicleMessagePopup.additionalCriticalMessagesReceived = false
463 criticalVehicleMessagePopup.open()
464 }
465 }
466
467 Popup {
468 id: criticalVehicleMessagePopup
469 y: ScreenTools.toolbarHeight + ScreenTools.defaultFontPixelHeight
470 x: Math.round((mainWindow.width - width) * 0.5)
471 width: mainWindow.width * 0.55
472 height: criticalVehicleMessageText.contentHeight + ScreenTools.defaultFontPixelHeight * 2
473 modal: false
474 focus: true
475
476 property alias criticalVehicleMessage: criticalVehicleMessageText.text
477 property bool additionalCriticalMessagesReceived: false
478
479 background: Rectangle {
480 anchors.fill: parent
481 color: qgcPal.alertBackground
482 radius: ScreenTools.defaultFontPixelHeight * 0.5
483 border.color: qgcPal.alertBorder
484 border.width: 2
485
486 Rectangle {
487 anchors.horizontalCenter: parent.horizontalCenter
488 anchors.top: parent.top
489 anchors.topMargin: -(height / 2)
490 color: qgcPal.alertBackground
491 radius: ScreenTools.defaultFontPixelHeight * 0.25
492 border.color: qgcPal.alertBorder
493 border.width: 1
494 width: vehicleWarningLabel.contentWidth + _margins
495 height: vehicleWarningLabel.contentHeight + _margins
496
497 property real _margins: ScreenTools.defaultFontPixelHeight * 0.25
498
499 QGCLabel {
500 id: vehicleWarningLabel
501 anchors.centerIn: parent
502 text: qsTr("Vehicle Error")
503 font.pointSize: ScreenTools.smallFontPointSize
504 color: qgcPal.alertText
505 }
506 }
507
508 Rectangle {
509 id: additionalErrorsIndicator
510 anchors.horizontalCenter: parent.horizontalCenter
511 anchors.bottom: parent.bottom
512 anchors.bottomMargin: -(height / 2)
513 color: qgcPal.alertBackground
514 radius: ScreenTools.defaultFontPixelHeight * 0.25
515 border.color: qgcPal.alertBorder
516 border.width: 1
517 width: additionalErrorsLabel.contentWidth + _margins
518 height: additionalErrorsLabel.contentHeight + _margins
519 visible: criticalVehicleMessagePopup.additionalCriticalMessagesReceived
520
521 property real _margins: ScreenTools.defaultFontPixelHeight * 0.25
522
523 QGCLabel {
524 id: additionalErrorsLabel
525 anchors.centerIn: parent
526 text: qsTr("Additional errors received")
527 font.pointSize: ScreenTools.smallFontPointSize
528 color: qgcPal.alertText
529 }
530 }
531 }
532
533 QGCLabel {
534 id: criticalVehicleMessageText
535 width: criticalVehicleMessagePopup.width - ScreenTools.defaultFontPixelHeight
536 anchors.centerIn: parent
537 wrapMode: Text.WordWrap
538 color: qgcPal.alertText
539 textFormat: TextEdit.RichText
540 }
541
542 MouseArea {
543 anchors.fill: parent
544 onClicked: {
545 criticalVehicleMessagePopup.close()
546 if (criticalVehicleMessagePopup.additionalCriticalMessagesReceived) {
547 criticalVehicleMessagePopup.additionalCriticalMessagesReceived = false;
548 flyView.dropMainStatusIndicatorTool();
549 } else if (QGroundControl.multiVehicleManager.activeVehicle) {
550 QGroundControl.multiVehicleManager.activeVehicle.resetErrorLevelMessages();
551 }
552 }
553 }
554 }
555
556 //-------------------------------------------------------------------------
557 //-- Indicator Drawer
558
559 function showIndicatorDrawer(drawerComponent, indicatorItem) {
560 indicatorDrawer.sourceComponent = drawerComponent
561 indicatorDrawer.indicatorItem = indicatorItem
562 indicatorDrawer.open()
563 }
564
565 function closeIndicatorDrawer() {
566 indicatorDrawer.close()
567 }
568
569 Popup {
570 id: indicatorDrawer
571 x: calcXPosition()
572 y: ScreenTools.toolbarHeight + _margins
573 leftInset: 0
574 rightInset: 0
575 topInset: 0
576 bottomInset: 0
577 padding: _margins * 2
578 visible: false
579 modal: true
580 focus: true
581 closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
582
583 property var sourceComponent
584 property var indicatorItem
585
586 property bool _expanded: false
587 property real _margins: ScreenTools.defaultFontPixelHeight / 4
588
589 function calcXPosition() {
590 if (indicatorItem) {
591 var xCenter = indicatorItem.mapToItem(mainWindow.contentItem, indicatorItem.width / 2, 0).x
592 return Math.max(_margins, Math.min(xCenter - (contentItem.implicitWidth / 2), mainWindow.contentItem.width - contentItem.implicitWidth - _margins - (indicatorDrawer.padding * 2) - (ScreenTools.defaultFontPixelHeight / 2)))
593 } else {
594 return _margins
595 }
596 }
597
598 onOpened: {
599 _expanded = false;
600 indicatorDrawerLoader.sourceComponent = indicatorDrawer.sourceComponent
601 }
602 onClosed: {
603 _expanded = false
604 indicatorItem = undefined
605 indicatorDrawerLoader.sourceComponent = undefined
606 }
607
608 background: Item {
609 Rectangle {
610 id: backgroundRect
611 anchors.fill: parent
612 color: QGroundControl.globalPalette.window
613 radius: indicatorDrawer._margins
614 opacity: 0.85
615 }
616
617 Rectangle {
618 objectName: "indicatorDrawerExpandButton"
619 anchors.horizontalCenter: backgroundRect.right
620 anchors.verticalCenter: backgroundRect.top
621 width: ScreenTools.largeFontPixelHeight
622 height: width
623 radius: width / 2
624 color: QGroundControl.globalPalette.button
625 border.color: QGroundControl.globalPalette.buttonText
626 visible: indicatorDrawerLoader.item && indicatorDrawerLoader.item._showExpand && !indicatorDrawer._expanded
627
628 QGCLabel {
629 anchors.centerIn: parent
630 text: ">"
631 color: QGroundControl.globalPalette.buttonText
632 }
633
634 QGCMouseArea {
635 fillItem: parent
636 onClicked: indicatorDrawer._expanded = true
637 }
638 }
639 }
640
641 contentItem: QGCFlickable {
642 id: indicatorDrawerLoaderFlickable
643 implicitWidth: Math.min(mainWindow.contentItem.width - (2 * indicatorDrawer._margins) - (indicatorDrawer.padding * 2), indicatorDrawerLoader.width)
644 implicitHeight: Math.min(mainWindow.contentItem.height - ScreenTools.toolbarHeight - (2 * indicatorDrawer._margins) - (indicatorDrawer.padding * 2), indicatorDrawerLoader.height)
645 contentWidth: indicatorDrawerLoader.width
646 contentHeight: indicatorDrawerLoader.height
647
648 Loader {
649 id: indicatorDrawerLoader
650 objectName: "indicatorDrawerLoader"
651
652 Binding {
653 target: indicatorDrawerLoader.item
654 property: "expanded"
655 value: indicatorDrawer._expanded
656 }
657
658 Binding {
659 target: indicatorDrawerLoader.item
660 property: "drawer"
661 value: indicatorDrawer
662 }
663 }
664 }
665 }
666
667 // Analyze page items (both in-panel and popped-out windows) are created with mainWindow as their
668 // QObject parent so their lifetime is not tied to AnalyzeView. This lets a popped-out window
669 // survive AnalyzeView being unloaded from the tool drawer.
670
671 // Tracks the analyze page item currently shown inside AnalyzeView's panel (not popped out).
672 // null when no page is loaded or the item has been handed off to a popup window.
673 property var _inPanelAnalyzePage: null
674
675 // Called by AnalyzeView.Component.onDestruction to destroy the in-panel item while
676 // panelContainer is still alive.
677 function destroyInPanelAnalyzePage() {
678 if (_inPanelAnalyzePage) {
679 _inPanelAnalyzePage.destroy()
680 _inPanelAnalyzePage = null
681 }
682 }
683
684 // Called by AnalyzeView to create an analyze page item owned by mainWindow.
685 // The caller sets the visual parent to panelContainer after creation.
686 function createAnalyzePage(source) {
687 if (_inPanelAnalyzePage) {
688 _inPanelAnalyzePage.destroy()
689 _inPanelAnalyzePage = null
690 }
691 var component = Qt.createComponent(source)
692 if (component.status !== Component.Ready) {
693 console.warn("createAnalyzePage failed source:", source, "errorString:", component.errorString())
694 return null
695 }
696 _inPanelAnalyzePage = component.createObject(mainWindow)
697 return _inPanelAnalyzePage
698 }
699
700 // Called by AnalyzeView when the in-panel item is handed off to a popup window.
701 // Clears _inPanelAnalyzePage so destroyInPanelAnalyzePage() does not destroy it
702 // when AnalyzeView is torn down.
703 function analyzePageMovedToPopup() {
704 _inPanelAnalyzePage = null
705 }
706
707 function createWindowedAnalyzePage(title, source, requiresVehicle, existingItem) {
708 var windowedPage = windowedAnalyzePage.createObject(mainWindow)
709 windowedPage.title = title
710 windowedPage.requiresVehicle = requiresVehicle
711 if (existingItem) {
712 windowedPage.adoptItem(existingItem)
713 } else {
714 windowedPage.source = source
715 }
716 windowedPage.visible = true
717 }
718
719 Component {
720 id: windowedAnalyzePage
721
722 Window {
723 width: ScreenTools.defaultFontPixelWidth * 100
724 height: ScreenTools.defaultFontPixelHeight * 40
725 visible: false
726
727 property alias source: loader.source
728 property bool requiresVehicle: false
729
730 function adoptItem(item) {
731 loader.visible = false
732 loader.source = ""
733 item.parent = contentRect
734 item.anchors.fill = contentRect
735 item.popped = true
736 item.visible = true
737 }
738
739 Connections {
740 target: QGroundControl.multiVehicleManager
741 function onActiveVehicleChanged() {
742 if (requiresVehicle) {
743 close()
744 }
745 }
746 }
747
748 Rectangle {
749 id: contentRect
750 color: QGroundControl.globalPalette.window
751 anchors.fill: parent
752
753 Loader {
754 id: loader
755 anchors.fill: parent
756 onLoaded: item.popped = true
757 }
758 }
759
760 onClosing: {
761 visible = false
762 // Destroy any reparented children (not owned by loader)
763 for (var i = contentRect.children.length - 1; i >= 0; i--) {
764 var child = contentRect.children[i]
765 if (child !== loader) {
766 child.destroy()
767 }
768 }
769 source = ""
770 Qt.callLater(destroy)
771 }
772 }
773 }
774}