Newer
Older
Gus Grubba
committed
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
Gus Grubba
committed
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
Gus Grubba
committed
import QtQuick.Controls 2.4
import QtQuick.Dialogs 1.3
Gus Grubba
committed
import QGroundControl 1.0
import QGroundControl.Palette 1.0
import QGroundControl.Controls 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.FlightDisplay 1.0
import QGroundControl.FlightMap 1.0
/// @brief Native QML top level window
/// All properties defined here are visible to all QML pages.
Gus Grubba
committed
ApplicationWindow {
minimumWidth: ScreenTools.isMobile ? Screen.width : Math.min(ScreenTools.defaultFontPixelWidth * 100, Screen.width)
minimumHeight: ScreenTools.isMobile ? Screen.height : Math.min(ScreenTools.defaultFontPixelWidth * 50, Screen.height)
//-- Full screen on mobile or tiny screens
if (ScreenTools.isMobile || Screen.height / ScreenTools.realPixelDensity < 120) {
} else {
width = ScreenTools.isMobile ? Screen.width : Math.min(250 * Screen.pixelDensity, Screen.width)
height = ScreenTools.isMobile ? Screen.height : Math.min(150 * Screen.pixelDensity, Screen.height)
// Start the sequence of first run prompt(s)
firstRunPromptManager.nextPrompt()
}
QtObject {
id: firstRunPromptManager
property var currentDialog: null
property var rgPromptIds: QGroundControl.corePlugin.firstRunPromptsToShow()
property int nextPromptIdIndex: 0
onRgPromptIdsChanged: console.log(QGroundControl.corePlugin, QGroundControl.corePlugin.firstRunPromptsToShow())
function clearNextPromptSignal() {
if (currentDialog) {
currentDialog.closed.disconnect(nextPrompt)
}
}
function nextPrompt() {
if (nextPromptIdIndex < rgPromptIds.length) {
currentDialog = showPopupDialogFromSource(QGroundControl.corePlugin.firstRunPromptResource(rgPromptIds[nextPromptIdIndex]))
currentDialog.closed.connect(nextPrompt)
nextPromptIdIndex++
} else {
currentDialog = null
showPreFlightChecklistIfNeeded()
}
readonly property real _topBottomMargins: ScreenTools.defaultFontPixelHeight * 0.5
Gus Grubba
committed
//-------------------------------------------------------------------------
//-- Global Scope Variables
property var activeVehicle: QGroundControl.multiVehicleManager.activeVehicle
property string formatedMessage: activeVehicle ? activeVehicle.formatedMessage : ""
/// Indicates usable height between toolbar and footer
property real availableHeight: mainWindow.height - mainWindow.header.height - mainWindow.footer.height
property var currentPlanMissionItem: planMasterControllerPlanView ? planMasterControllerPlanView.missionController.currentPlanViewItem : null
property var planMasterControllerPlanView: null
property var planMasterControllerFlyView: null
readonly property string navButtonWidth: ScreenTools.defaultFontPixelWidth * 24
readonly property real defaultTextHeight: ScreenTools.defaultFontPixelHeight
readonly property real defaultTextWidth: ScreenTools.defaultFontPixelWidth
Gus Grubba
committed
Gus Grubba
committed
QGCPalette { id: qgcPal; colorGroupEnabled: true }
//-------------------------------------------------------------------------
//-- Actions
signal armVehicleRequest
signal disarmVehicleRequest
signal vtolTransitionToFwdFlightRequest
signal vtolTransitionToMRFlightRequest
signal showPreFlightChecklistIfNeeded
Gus Grubba
committed
//-------------------------------------------------------------------------
//-- Global Scope Functions
/// Prevent view switching
function pushPreventViewSwitch() {
_rgPreventViewSwitch.push(true)
}
/// Allow view switching
function popPreventViewSwitch() {
if (_rgPreventViewSwitch.length == 1) {
return
}
_rgPreventViewSwitch.pop()
}
/// @return true: View switches are not currently allowed
function preventViewSwitch() {
return _rgPreventViewSwitch[_rgPreventViewSwitch.length - 1]
}
settingsWindow.visible = false
setupWindow.visible = false
analyzeWindow.visible = false
flightView.visible = false
planViewLoader.visible = false
Gus Grubba
committed
}
function showFlyView() {
Jacob Dahl
committed
if (!flightView.visible) {
Jacob Dahl
committed
}
flightView.visible = true
Gus Grubba
committed
}
function showPlanView() {
planViewLoader.visible = true
Gus Grubba
committed
}
function showAnalyzeView() {
analyzeWindow.visible = true
Gus Grubba
committed
}
function showSetupView() {
setupWindow.visible = true
Gus Grubba
committed
}
function showSettingsView() {
settingsWindow.visible = true
Gus Grubba
committed
}
//-------------------------------------------------------------------------
//-- Global simple message dialog
function showMessageDialog(title, text) {
var dialog = simpleMessageDialog.createObject(mainWindow, { title: title, text: text })
dialog.open()
}
Component {
id: simpleMessageDialog
MessageDialog {
standardButtons: StandardButton.Ok
modality: Qt.ApplicationModal
visible: false
Gus Grubba
committed
}
}
Gus Grubba
committed
//-------------------------------------------------------------------------
//-- Global complex dialog
/// Shows a QGCViewDialogContainer based dialog
/// @param component The dialog contents
Gus Grubba
committed
/// @param title Title for dialog
/// @param charWidth Width of dialog in characters
/// @param buttons Buttons to show in dialog using StandardButton enum
readonly property int showDialogFullWidth: -1 ///< Use for full width dialog
readonly property int showDialogDefaultWidth: 40 ///< Use for default dialog width
function showComponentDialog(component, title, charWidth, buttons) {
Gus Grubba
committed
var dialogWidth = charWidth === showDialogFullWidth ? mainWindow.width : ScreenTools.defaultFontPixelWidth * charWidth
mainWindowDialog.width = dialogWidth
mainWindowDialog.dialogComponent = component
mainWindowDialog.dialogTitle = title
mainWindowDialog.dialogButtons = buttons
Gus Grubba
committed
mainWindowDialog.open()
if (buttons & StandardButton.Cancel || buttons & StandardButton.Close || buttons & StandardButton.Discard || buttons & StandardButton.Abort || buttons & StandardButton.Ignore) {
mainWindowDialog.closePolicy = Popup.NoAutoClose;
mainWindowDialog.interactive = false;
} else {
Gus Grubba
committed
mainWindowDialog.closePolicy = Popup.CloseOnEscape | Popup.CloseOnPressOutside;
mainWindowDialog.interactive = true;
}
Gus Grubba
committed
}
Drawer {
id: mainWindowDialog
y: mainWindow.header.height
height: mainWindow.height - mainWindow.header.height
edge: Qt.RightEdge
interactive: false
background: Rectangle {
color: qgcPal.windowShadeDark
}
property var dialogComponent: null
property var dialogButtons: null
property string dialogTitle: ""
Loader {
id: dlgLoader
anchors.fill: parent
onLoaded: {
item.setupDialogButtons()
}
}
onOpened: {
dlgLoader.source = "QGCViewDialogContainer.qml"
}
onClosed: {
Gus Grubba
committed
dlgLoader.source = ""
}
}
// Dialogs based on QGCPopupDialog
function showPopupDialogFromComponent(component, properties) {
var dialog = popupDialogContainerComponent.createObject(mainWindow, { dialogComponent: component, dialogProperties: properties })
dialog.open()
return dialog
}
function showPopupDialogFromSource(source, properties) {
var dialog = popupDialogContainerComponent.createObject(mainWindow, { dialogSource: source, dialogProperties: properties })
dialog.open()
return dialog
Gus Grubba
committed
property bool _forceClose: false
function finishCloseProcess() {
_forceClose = true
// For some reason on the Qml side Qt doesn't automatically disconnect a signal when an object is destroyed.
// So we have to do it ourselves otherwise the signal flows through on app shutdown to an object which no longer exists.
firstRunPromptManager.clearNextPromptSignal()
Gus Grubba
committed
QGroundControl.linkManager.shutdown()
QGroundControl.videoManager.stopVideo();
Stefan Dunca
committed
mainWindow.close()
Gus Grubba
committed
}
// On attempting an application close we check for:
// Unsaved missions - then
// Pending parameter writes - then
// Active connections
onClosing: {
if (!_forceClose) {
unsavedMissionCloseDialog.check()
close.accepted = false
}
}
Gus Grubba
committed
MessageDialog {
Gus Grubba
committed
title: qsTr("%1 close").arg(QGroundControl.appName)
text: qsTr("You have a mission edit in progress which has not been saved/sent. If you close you will lose changes. Are you sure you want to close?")
standardButtons: StandardButton.Yes | StandardButton.No
Gus Grubba
committed
modality: Qt.ApplicationModal
visible: false
Gus Grubba
committed
function check() {
if (planMasterControllerPlanView && planMasterControllerPlanView.dirty) {
Gus Grubba
committed
} else {
Gus Grubba
committed
}
}
}
MessageDialog {
id: pendingParameterWritesCloseDialog
title: qsTr("%1 close").arg(QGroundControl.appName)
text: qsTr("You have pending parameter updates to a vehicle. If you close you will lose changes. Are you sure you want to close?")
standardButtons: StandardButton.Yes | StandardButton.No
modality: Qt.ApplicationModal
visible: false
onYes: activeConnectionsCloseDialog.check()
function check() {
for (var index=0; index<QGroundControl.multiVehicleManager.vehicles.count; index++) {
if (QGroundControl.multiVehicleManager.vehicles.get(index).parameterManager.pendingWrites) {
pendingParameterWritesCloseDialog.open()
return
}
}
activeConnectionsCloseDialog.check()
text: qsTr("There are still active connections to vehicles. Are you sure you want to exit?")
standardButtons: StandardButton.Yes | StandardButton.Cancel
if (QGroundControl.multiVehicleManager.activeVehicle) {
activeConnectionsCloseDialog.open()
}
}
}
//-------------------------------------------------------------------------
Gus Grubba
committed
background: Item {
id: rootBackground
anchors.fill: parent
}
//-------------------------------------------------------------------------
header: MainToolBar {
id: toolbar
height: ScreenTools.toolbarHeight
visible: !QGroundControl.videoManager.fullScreen
Gus Grubba
committed
}
footer: LogReplayStatusBar {
visible: QGroundControl.settingsManager.flyViewSettings.showLogReplayStatusBar.rawValue
}
//-------------------------------------------------------------------------
id: flightView
anchors.fill: parent
}
//-------------------------------------------------------------------------
id: planViewLoader
anchors.fill: parent
visible: false
source: "PlanView.qml"
}
//-------------------------------------------------------------------------
Loader {
id: settingsWindow
anchors.fill: parent
visible: false
source: "AppSettings.qml"
}
//-------------------------------------------------------------------------
Loader {
id: setupWindow
anchors.fill: parent
visible: false
source: "SetupView.qml"
Gus Grubba
committed
//-------------------------------------------------------------------------
Gus Grubba
committed
Loader {
Gus Grubba
committed
anchors.fill: parent
visible: false
source: "AnalyzeView.qml"
Gus Grubba
committed
}
//-------------------------------------------------------------------------
// @brief Loader helper for any child, no matter how deep, to display elements
Gus Grubba
committed
// This is DEPRECATED. Use Popup instead.
Gus Grubba
committed
Loader {
Gus Grubba
committed
anchors.centerIn: parent
}
//-------------------------------------------------------------------------
Gus Grubba
committed
function formatMessage(message) {
message = message.replace(new RegExp("<#E>", "g"), "color: " + qgcPal.warningText + "; font: " + (ScreenTools.defaultFontPointSize.toFixed(0) - 1) + "pt monospace;");
message = message.replace(new RegExp("<#I>", "g"), "color: " + qgcPal.warningText + "; font: " + (ScreenTools.defaultFontPointSize.toFixed(0) - 1) + "pt monospace;");
message = message.replace(new RegExp("<#N>", "g"), "color: " + qgcPal.text + "; font: " + (ScreenTools.defaultFontPointSize.toFixed(0) - 1) + "pt monospace;");
return message;
}
function showVehicleMessages() {
if(!vehicleMessageArea.visible) {
Gus Grubba
committed
if(QGroundControl.multiVehicleManager.activeVehicleAvailable) {
messageText.text = formatMessage(activeVehicle.formatedMessages)
//-- Hack to scroll to last message
for (var i = 0; i < activeVehicle.messageCount; i++)
messageFlick.flick(0,-5000)
activeVehicle.resetMessages()
} else {
messageText.text = qsTr("No Messages")
}
Gus Grubba
committed
}
}
onFormatedMessageChanged: {
if(vehicleMessageArea.visible) {
Gus Grubba
committed
messageText.append(formatMessage(formatedMessage))
//-- Hack to scroll down
messageFlick.flick(0,-500)
}
}
Popup {
Gus Grubba
committed
width: mainWindow.width * 0.666
height: mainWindow.height * 0.666
modal: true
focus: true
x: Math.round((mainWindow.width - width) * 0.5)
y: Math.round((mainWindow.height - height) * 0.5)
Gus Grubba
committed
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
background: Rectangle {
anchors.fill: parent
color: qgcPal.window
border.color: qgcPal.text
radius: ScreenTools.defaultFontPixelHeight * 0.5
}
QGCFlickable {
id: messageFlick
anchors.margins: ScreenTools.defaultFontPixelHeight
anchors.fill: parent
contentHeight: messageText.height
contentWidth: messageText.width
pixelAligned: true
clip: true
TextEdit {
id: messageText
readOnly: true
textFormat: TextEdit.RichText
color: qgcPal.text
}
}
Gus Grubba
committed
QGCColoredImage {
anchors.margins: ScreenTools.defaultFontPixelHeight * 0.5
anchors.top: parent.top
anchors.right: parent.right
width: ScreenTools.isMobile ? ScreenTools.defaultFontPixelHeight * 1.5 : ScreenTools.defaultFontPixelHeight
height: width
sourceSize.height: width
source: "/res/XDelete.svg"
fillMode: Image.PreserveAspectFit
mipmap: true
smooth: true
color: qgcPal.text
MouseArea {
anchors.fill: parent
anchors.margins: ScreenTools.isMobile ? -ScreenTools.defaultFontPixelHeight : 0
onClicked: {
Gus Grubba
committed
}
}
}
//-- Clear Messages
QGCColoredImage {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: ScreenTools.defaultFontPixelHeight * 0.5
height: ScreenTools.isMobile ? ScreenTools.defaultFontPixelHeight * 1.5 : ScreenTools.defaultFontPixelHeight
width: height
sourceSize.height: height
source: "/res/TrashDelete.svg"
fillMode: Image.PreserveAspectFit
mipmap: true
smooth: true
color: qgcPal.text
MouseArea {
anchors.fill: parent
onClicked: {
if(QGroundControl.multiVehicleManager.activeVehicleAvailable) {
activeVehicle.clearMessages();
Gus Grubba
committed
}
}
}
}
}
//-------------------------------------------------------------------------
Gus Grubba
committed
property var _messageQueue: []
Gus Grubba
committed
vehicleMessageArea.close()
if(systemMessageArea.visible || QGroundControl.videoManager.fullScreen) {
Gus Grubba
committed
_messageQueue.push(message)
} else {
_systemMessage = message
systemMessageArea.open()
Gus Grubba
committed
}
}
function showMissingParameterOverlay(missingParamName) {
showError(qsTr("Parameters missing: %1").arg(missingParamName))
}
function showFactError(errorMsg) {
showError(qsTr("Fact error: %1").arg(errorMsg))
}
Popup {
Gus Grubba
committed
y: ScreenTools.defaultFontPixelHeight
Gus Grubba
committed
width: mainWindow.width * 0.55
Gus Grubba
committed
modal: false
focus: true
closePolicy: Popup.CloseOnEscape
background: Rectangle {
anchors.fill: parent
color: qgcPal.alertBackground
radius: ScreenTools.defaultFontPixelHeight * 0.5
border.color: qgcPal.alertBorder
border.width: 2
}
onOpened: {
systemMessageText.text = mainWindow._systemMessage
Gus Grubba
committed
}
onClosed: {
//-- Are there messages in the waiting queue?
if(mainWindow._messageQueue.length) {
mainWindow._systemMessage = ""
Gus Grubba
committed
//-- Show all messages in queue
for (var i = 0; i < mainWindow._messageQueue.length; i++) {
var text = mainWindow._messageQueue[i]
if(i) mainWindow._systemMessage += "<br>"
mainWindow._systemMessage += text
Gus Grubba
committed
}
//-- Clear it
mainWindow._messageQueue = []
Gus Grubba
committed
} else {
mainWindow._systemMessage = ""
Gus Grubba
committed
}
}
Flickable {
anchors.margins: ScreenTools.defaultFontPixelHeight * 0.5
Gus Grubba
committed
anchors.fill: parent
contentHeight: systemMessageText.height
contentWidth: systemMessageText.width
Gus Grubba
committed
boundsBehavior: Flickable.StopAtBounds
pixelAligned: true
clip: true
TextEdit {
id: systemMessageText
width: systemMessageArea.width - systemMessageClose.width - (ScreenTools.defaultFontPixelHeight * 2)
anchors.centerIn: parent
Gus Grubba
committed
readOnly: true
textFormat: TextEdit.RichText
font.pointSize: ScreenTools.defaultFontPointSize
font.family: ScreenTools.demiboldFontFamily
wrapMode: TextEdit.WordWrap
color: qgcPal.alertText
}
}
//-- Dismiss Critical Message
QGCColoredImage {
Gus Grubba
committed
anchors.margins: ScreenTools.defaultFontPixelHeight * 0.5
anchors.top: parent.top
anchors.right: parent.right
width: ScreenTools.isMobile ? ScreenTools.defaultFontPixelHeight * 1.5 : ScreenTools.defaultFontPixelHeight
height: width
sourceSize.height: width
source: "/res/XDelete.svg"
fillMode: Image.PreserveAspectFit
color: qgcPal.alertText
MouseArea {
anchors.fill: parent
Gus Grubba
committed
onClicked: {
Gus Grubba
committed
}
}
}
//-- More text below indicator
QGCColoredImage {
anchors.margins: ScreenTools.defaultFontPixelHeight * 0.5
anchors.bottom: parent.bottom
anchors.right: parent.right
width: ScreenTools.isMobile ? ScreenTools.defaultFontPixelHeight * 1.5 : ScreenTools.defaultFontPixelHeight
height: width
sourceSize.height: width
source: "/res/ArrowDown.svg"
fillMode: Image.PreserveAspectFit
visible: systemMessageText.lineCount > 5
Gus Grubba
committed
color: qgcPal.alertText
MouseArea {
anchors.fill: parent
onClicked: {
systemMessageFlick.flick(0,-500)
Gus Grubba
committed
}
}
}
}
//-------------------------------------------------------------------------
//-- Indicator Popups
function showPopUp(item, dropItem) {
Gus Grubba
committed
indicatorDropdown.currentIndicator = dropItem
indicatorDropdown.currentItem = item
Gus Grubba
committed
indicatorDropdown.open()
}
function hidePopUp() {
indicatorDropdown.close()
indicatorDropdown.currentItem = null
indicatorDropdown.currentIndicator = null
}
Gus Grubba
committed
Popup {
id: indicatorDropdown
y: ScreenTools.defaultFontPixelHeight
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
property var currentItem: null
property var currentIndicator: null
Gus Grubba
committed
background: Rectangle {
width: loader.width
height: loader.height
color: Qt.rgba(0,0,0,0)
}
Loader {
id: loader
onLoaded: {
var centerX = mainWindow.contentItem.mapFromItem(indicatorDropdown.currentItem, 0, 0).x - (loader.width * 0.5)
if((centerX + indicatorDropdown.width) > (mainWindow.width - ScreenTools.defaultFontPixelWidth)) {
centerX = mainWindow.width - indicatorDropdown.width - ScreenTools.defaultFontPixelWidth
}
indicatorDropdown.x = centerX
Gus Grubba
committed
}
}
onOpened: {
loader.sourceComponent = indicatorDropdown.currentIndicator
}
onClosed: {
loader.sourceComponent = null
indicatorDropdown.currentIndicator = null
}
}
}