8import QGroundControl.Controls
9import QGroundControl.FactControls
10import QGroundControl.FlyView
11import QGroundControl.FlightMap
12import QGroundControl.Toolbar
14/// @brief Native QML top level window
15/// All properties defined here are visible to all QML pages.
19 // The special casing for android prevents white bars from showing up on the edges of the screen with newer android versions
20 flags: Qt.Window | (ScreenTools.isAndroid ? Qt.ExpandedClientAreaHint | Qt.NoTitleBarBackgroundHint : 0)
22 Component.onCompleted: {
23 // Start the sequence of first run prompt(s)
24 firstRunPromptManager.nextPrompt()
27 /// Saves main window position and size and re-opens it in the same position and size next time
28 MainWindowSavedState {
33 id: firstRunPromptManager
35 property var currentDialog: null
36 property var rgPromptIds: QGroundControl.corePlugin.firstRunPromptsToShow()
37 property int nextPromptIdIndex: 0
39 function clearNextPromptSignal() {
41 currentDialog.closed.disconnect(nextPrompt)
45 function nextPrompt() {
46 if (nextPromptIdIndex < rgPromptIds.length) {
47 var component = Qt.createComponent(QGroundControl.corePlugin.firstRunPromptResource(rgPromptIds[nextPromptIdIndex]));
48 currentDialog = component.createObject(mainWindow)
49 currentDialog.closed.connect(nextPrompt)
54 showPreFlightChecklistIfNeeded()
59 readonly property real _topBottomMargins: ScreenTools.defaultFontPixelHeight * 0.5
61 //-------------------------------------------------------------------------
62 //-- Global Scope Variables
67 readonly property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
68 readonly property real defaultTextHeight: ScreenTools.defaultFontPixelHeight
69 readonly property real defaultTextWidth: ScreenTools.defaultFontPixelWidth
70 readonly property var planMasterControllerFlyView: flyView.planController
71 readonly property var guidedControllerFlyView: flyView.guidedController
73 // Number of QGCTextField's with validation errors. Used to prevent closing panels with validation errors.
74 property int validationErrorCount: 0
76 // Property to manage RemoteID quick access to settings page
77 property bool commingFromRIDIndicator: false
80 /// Default color palette used throughout the UI
81 QGCPalette { id: qgcPal; colorGroupEnabled: true }
83 //-------------------------------------------------------------------------
86 signal armVehicleRequest
87 signal forceArmVehicleRequest
88 signal disarmVehicleRequest
89 signal vtolTransitionToFwdFlightRequest
90 signal vtolTransitionToMRFlightRequest
91 signal showPreFlightChecklistIfNeeded
93 //-------------------------------------------------------------------------
94 //-- Global Scope Functions
96 // This function is used to prevent view switching if there are validation errors
97 function allowViewSwitch(previousValidationErrorCount = 0) {
98 // Run validation on active focus control to ensure it is valid before switching views
99 if (mainWindow.activeFocusControl instanceof FactTextField) {
100 mainWindow.activeFocusControl._onEditingFinished()
102 return globals.validationErrorCount <= previousValidationErrorCount
105 function showPlanView() {
106 flyView.visible = false
107 planView.visible = true
108 toolDrawer.visible = false
111 function showFlyView() {
112 flyView.visible = true
113 planView.visible = false
114 toolDrawer.visible = false
117 function showTool(toolTitle, toolSource, toolIcon) {
118 toolDrawer.backIcon = flyView.visible ? "/qmlimages/PaperPlane.svg" : "/qmlimages/Plan.svg"
119 toolDrawer.toolTitle = toolTitle
120 toolDrawer.toolSource = toolSource
121 toolDrawer.toolIcon = toolIcon
122 toolDrawer.visible = true
125 function showAnalyzeTool() {
126 showTool(qsTr("Analyze Tools"), "qrc:/qml/QGroundControl/AnalyzeView/AnalyzeView.qml", "/qmlimages/Analyze.svg")
129 function showVehicleConfig() {
130 showTool(qsTr("Vehicle Configuration"), "qrc:/qml/QGroundControl/VehicleSetup/SetupView.qml", "/qmlimages/Gears.svg")
133 function showVehicleConfigParametersPage() {
135 toolDrawerLoader.item.showParametersPanel()
138 function showKnownVehicleComponentConfigPage(knownVehicleComponent) {
140 let vehicleComponent = globals.activeVehicle.autopilotPlugin.findKnownVehicleComponent(knownVehicleComponent)
141 if (vehicleComponent) {
142 toolDrawerLoader.item.showVehicleComponentPanel(vehicleComponent)
146 function showSettingsTool(settingsPage = "") {
147 showTool(qsTr("Application Settings"), "qrc:/qml/QGroundControl/Controls/AppSettings.qml", "/res/QGCLogoWhite")
148 if (settingsPage !== "") {
149 toolDrawerLoader.item.showSettingsPage(settingsPage)
153 //-------------------------------------------------------------------------
154 //-- Global simple message dialog
156 function _showMessageDialogWorker(owner, dialogTitle, dialogText, buttons = Dialog.Ok, acceptFunction = null, closeFunction = null) {
157 let dialog = simpleMessageDialogComponent.createObject(owner, { title: dialogTitle, text: dialogText, buttons: buttons, acceptFunction: acceptFunction, closeFunction: closeFunction })
161 // This variant is only meant to be called by QGCApplication
162 function _showMessageDialog(dialogTitle, dialogText) {
163 _showMessageDialogWorker(mainWindow, dialogTitle, dialogText)
167 target: QGroundControl
169 function onShowMessageDialogRequested(owner, title, text, buttons, acceptFunction, closeFunction) {
170 _showMessageDialogWorker(owner, title, text, buttons, acceptFunction, closeFunction)
175 id: simpleMessageDialogComponent
177 QGCSimpleMessageDialog {
181 property bool _forceClose: false
183 function finishCloseProcess() {
185 // For some reason on the Qml side Qt doesn't automatically disconnect a signal when an object is destroyed.
186 // So we have to do it ourselves otherwise the signal flows through on app shutdown to an object which no longer exists.
187 firstRunPromptManager.clearNextPromptSignal()
188 QGroundControl.linkManager.shutdown()
189 QGroundControl.videoManager.stopVideo();
193 // Check for things which should prevent the app from closing
194 // Returns true if it is OK to close
195 readonly property int _skipUnsavedMissionCheckMask: 0x01
196 readonly property int _skipPendingParameterWritesCheckMask: 0x02
197 readonly property int _skipActiveConnectionsCheckMask: 0x04
198 property int _closeChecksToSkip: 0
199 function performCloseChecks() {
200 if (!(_closeChecksToSkip & _skipUnsavedMissionCheckMask) && !checkForUnsavedMission()) {
203 if (!(_closeChecksToSkip & _skipPendingParameterWritesCheckMask) && !checkForPendingParameterWrites()) {
206 if (!(_closeChecksToSkip & _skipActiveConnectionsCheckMask) && !checkForActiveConnections()) {
213 property string closeDialogTitle: qsTr("Close %1").arg(QGroundControl.appName)
215 function checkForUnsavedMission() {
216 if (planView._planMasterController.dirtyForSave || planView._planMasterController.dirtyForUpload) {
217 QGroundControl.showMessageDialog(mainWindow, closeDialogTitle,
218 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?"),
219 Dialog.Yes | Dialog.No,
220 function() { _closeChecksToSkip |= _skipUnsavedMissionCheckMask; performCloseChecks() })
227 function checkForPendingParameterWrites() {
228 for (var index=0; index<QGroundControl.multiVehicleManager.vehicles.count; index++) {
229 if (QGroundControl.multiVehicleManager.vehicles.get(index).parameterManager.pendingWrites) {
230 QGroundControl.showMessageDialog(mainWindow, closeDialogTitle,
231 qsTr("You have pending parameter updates to a vehicle. If you close you will lose changes. Are you sure you want to close?"),
232 Dialog.Yes | Dialog.No,
233 function() { _closeChecksToSkip |= _skipPendingParameterWritesCheckMask; performCloseChecks() })
240 function checkForActiveConnections() {
241 if (QGroundControl.multiVehicleManager.activeVehicle) {
242 QGroundControl.showMessageDialog(mainWindow, closeDialogTitle,
243 qsTr("There are still active connections to vehicles. Are you sure you want to exit?"),
244 Dialog.Yes | Dialog.No,
245 function() { _closeChecksToSkip |= _skipActiveConnectionsCheckMask; performCloseChecks() })
252 onClosing: (close) => {
254 _closeChecksToSkip = 0
255 close.accepted = performCloseChecks()
259 background: Rectangle {
261 color: QGroundControl.globalPalette.window
275 footer: LogReplayStatusBar {
276 visible: QGroundControl.settingsManager.flyViewSettings.showLogReplayStatusBar.rawValue
280 id: showTouchAreasNotification
281 title: qsTr("Debug Touch Areas")
282 text: qsTr("Touch Area display toggled")
283 buttons: MessageDialog.Ok
287 id: advancedModeOnConfirmation
288 title: qsTr("Advanced Mode")
289 text: QGroundControl.corePlugin.showAdvancedUIMessage
290 buttons: MessageDialog.Yes | MessageDialog.No
291 onButtonClicked: function (button, role) {
292 if (button === MessageDialog.Yes) {
293 QGroundControl.corePlugin.showAdvancedUI = true
299 id: advancedModeOffConfirmation
300 title: qsTr("Advanced Mode")
301 text: qsTr("Turn off Advanced Mode?")
302 buttons: MessageDialog.Yes | MessageDialog.No
303 onButtonClicked: function (button, role) {
304 if (button === MessageDialog.Yes) {
305 QGroundControl.corePlugin.showAdvancedUI = false
310 function showToolSelectDialog() {
311 if (mainWindow.allowViewSwitch()) {
312 mainWindow.showIndicatorDrawer(toolSelectComponent, null)
317 id: toolSelectComponent
329 property var backIcon
330 property string toolTitle
331 property alias toolSource: toolDrawerLoader.source
332 property var toolIcon
335 if (!toolDrawer.visible) {
336 toolDrawerLoader.source = ""
340 // This need to block click event leakage to underlying map.
346 id: toolDrawerToolbar
347 anchors.left: parent.left
348 anchors.right: parent.right
349 anchors.top: parent.top
350 height: ScreenTools.toolbarHeight
351 color: qgcPal.toolbarBackground
354 id: toolDrawerToolbarLayout
355 anchors.leftMargin: ScreenTools.defaultFontPixelWidth
356 anchors.left: parent.left
357 anchors.top: parent.top
358 anchors.bottom: parent.bottom
359 spacing: ScreenTools.defaultFontPixelWidth
363 height: parent.height
364 icon.source: "/res/QGCLogoFull.svg"
366 onClicked: mainWindow.showToolSelectDialog()
370 id: toolbarDrawerText
371 text: toolDrawer.toolTitle
372 font.pointSize: ScreenTools.largeFontPointSize
379 anchors.left: parent.left
380 anchors.right: parent.right
381 anchors.top: toolDrawerToolbar.bottom
382 anchors.bottom: parent.bottom
385 target: toolDrawerLoader.item
386 ignoreUnknownSignals: true
387 function onPopout() { toolDrawer.visible = false }
392 //-------------------------------------------------------------------------
393 //-- Critical Vehicle Message Popup
395 function showCriticalVehicleMessage(message) {
396 closeIndicatorDrawer()
397 if (criticalVehicleMessagePopup.visible || QGroundControl.videoManager.fullScreen) {
398 // We received additional warning message while an older warning message was still displayed.
399 // When the user close the older one drop the message indicator tool so they can see the rest of them.
400 criticalVehicleMessagePopup.additionalCriticalMessagesReceived = true
402 criticalVehicleMessagePopup.criticalVehicleMessage = message
403 criticalVehicleMessagePopup.additionalCriticalMessagesReceived = false
404 criticalVehicleMessagePopup.open()
409 id: criticalVehicleMessagePopup
410 y: ScreenTools.toolbarHeight + ScreenTools.defaultFontPixelHeight
411 x: Math.round((mainWindow.width - width) * 0.5)
412 width: mainWindow.width * 0.55
413 height: criticalVehicleMessageText.contentHeight + ScreenTools.defaultFontPixelHeight * 2
417 property alias criticalVehicleMessage: criticalVehicleMessageText.text
418 property bool additionalCriticalMessagesReceived: false
420 background: Rectangle {
422 color: qgcPal.alertBackground
423 radius: ScreenTools.defaultFontPixelHeight * 0.5
424 border.color: qgcPal.alertBorder
428 anchors.horizontalCenter: parent.horizontalCenter
429 anchors.top: parent.top
430 anchors.topMargin: -(height / 2)
431 color: qgcPal.alertBackground
432 radius: ScreenTools.defaultFontPixelHeight * 0.25
433 border.color: qgcPal.alertBorder
435 width: vehicleWarningLabel.contentWidth + _margins
436 height: vehicleWarningLabel.contentHeight + _margins
438 property real _margins: ScreenTools.defaultFontPixelHeight * 0.25
441 id: vehicleWarningLabel
442 anchors.centerIn: parent
443 text: qsTr("Vehicle Error")
444 font.pointSize: ScreenTools.smallFontPointSize
445 color: qgcPal.alertText
450 id: additionalErrorsIndicator
451 anchors.horizontalCenter: parent.horizontalCenter
452 anchors.bottom: parent.bottom
453 anchors.bottomMargin: -(height / 2)
454 color: qgcPal.alertBackground
455 radius: ScreenTools.defaultFontPixelHeight * 0.25
456 border.color: qgcPal.alertBorder
458 width: additionalErrorsLabel.contentWidth + _margins
459 height: additionalErrorsLabel.contentHeight + _margins
460 visible: criticalVehicleMessagePopup.additionalCriticalMessagesReceived
462 property real _margins: ScreenTools.defaultFontPixelHeight * 0.25
465 id: additionalErrorsLabel
466 anchors.centerIn: parent
467 text: qsTr("Additional errors received")
468 font.pointSize: ScreenTools.smallFontPointSize
469 color: qgcPal.alertText
475 id: criticalVehicleMessageText
476 width: criticalVehicleMessagePopup.width - ScreenTools.defaultFontPixelHeight
477 anchors.centerIn: parent
478 wrapMode: Text.WordWrap
479 color: qgcPal.alertText
480 textFormat: TextEdit.RichText
486 criticalVehicleMessagePopup.close()
487 if (criticalVehicleMessagePopup.additionalCriticalMessagesReceived) {
488 criticalVehicleMessagePopup.additionalCriticalMessagesReceived = false;
489 flyView.dropMainStatusIndicatorTool();
491 QGroundControl.multiVehicleManager.activeVehicle.resetErrorLevelMessages();
497 //-------------------------------------------------------------------------
498 //-- Indicator Drawer
500 function showIndicatorDrawer(drawerComponent, indicatorItem) {
501 indicatorDrawer.sourceComponent = drawerComponent
502 indicatorDrawer.indicatorItem = indicatorItem
503 indicatorDrawer.open()
506 function closeIndicatorDrawer() {
507 indicatorDrawer.close()
513 y: ScreenTools.toolbarHeight + _margins
518 padding: _margins * 2
522 closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
524 property var sourceComponent
525 property var indicatorItem
527 property bool _expanded: false
528 property real _margins: ScreenTools.defaultFontPixelHeight / 4
530 function calcXPosition() {
532 var xCenter = indicatorItem.mapToItem(mainWindow.contentItem, indicatorItem.width / 2, 0).x
533 return Math.max(_margins, Math.min(xCenter - (contentItem.implicitWidth / 2), mainWindow.contentItem.width - contentItem.implicitWidth - _margins - (indicatorDrawer.padding * 2) - (ScreenTools.defaultFontPixelHeight / 2)))
541 indicatorDrawerLoader.sourceComponent = indicatorDrawer.sourceComponent
545 indicatorItem = undefined
546 indicatorDrawerLoader.sourceComponent = undefined
553 color: QGroundControl.globalPalette.window
554 radius: indicatorDrawer._margins
559 anchors.horizontalCenter: backgroundRect.right
560 anchors.verticalCenter: backgroundRect.top
561 width: ScreenTools.largeFontPixelHeight
564 color: QGroundControl.globalPalette.button
565 border.color: QGroundControl.globalPalette.buttonText
566 visible: indicatorDrawerLoader.item && indicatorDrawerLoader.item._showExpand && !indicatorDrawer._expanded
569 anchors.centerIn: parent
571 color: QGroundControl.globalPalette.buttonText
576 onClicked: indicatorDrawer._expanded = true
581 contentItem: QGCFlickable {
582 id: indicatorDrawerLoaderFlickable
583 implicitWidth: Math.min(mainWindow.contentItem.width - (2 * indicatorDrawer._margins) - (indicatorDrawer.padding * 2), indicatorDrawerLoader.width)
584 implicitHeight: Math.min(mainWindow.contentItem.height - ScreenTools.toolbarHeight - (2 * indicatorDrawer._margins) - (indicatorDrawer.padding * 2), indicatorDrawerLoader.height)
585 contentWidth: indicatorDrawerLoader.width
586 contentHeight: indicatorDrawerLoader.height
589 id: indicatorDrawerLoader
592 target: indicatorDrawerLoader.item
594 value: indicatorDrawer._expanded
598 target: indicatorDrawerLoader.item
600 value: indicatorDrawer
606 // We have to create the popup windows for the Analyze pages here so that the creation context is rooted
607 // to mainWindow. Otherwise if they are rooted to the AnalyzeView itself they will die when the analyze viewSwitch
610 function createrWindowedAnalyzePage(title, source) {
611 var windowedPage = windowedAnalyzePage.createObject(mainWindow)
612 windowedPage.title = title
613 windowedPage.source = source
617 id: windowedAnalyzePage
620 width: ScreenTools.defaultFontPixelWidth * 100
621 height: ScreenTools.defaultFontPixelHeight * 40
624 property alias source: loader.source
627 color: QGroundControl.globalPalette.window
633 onLoaded: item.popped = true