diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc
index e0ef70e51d10921e87b330b523fd1e45c099ecff..7d51d491cd1faa1b18ee20bf83aa1c9da7c80348 100644
--- a/qgroundcontrol.qrc
+++ b/qgroundcontrol.qrc
@@ -142,6 +142,8 @@
src/QmlControls/QGCMenuSeparator.qml
src/QmlControls/QGCMouseArea.qml
src/QmlControls/QGCMovableItem.qml
+ src/QmlControls/QGCPopupDialog.qml
+ src/QmlControls/QGCPopupDialogContainer.qml
src/QmlControls/QGCPipable.qml
src/QmlControls/QGCRadioButton.qml
src/QmlControls/QGCSlider.qml
diff --git a/src/QmlControls/QGCPopupDialog.qml b/src/QmlControls/QGCPopupDialog.qml
new file mode 100644
index 0000000000000000000000000000000000000000..fa7f8cb01a74da890194b441ab4e02437b14b14e
--- /dev/null
+++ b/src/QmlControls/QGCPopupDialog.qml
@@ -0,0 +1,41 @@
+/****************************************************************************
+ *
+ * (c) 2009-2020 QGROUNDCONTROL PROJECT
+ *
+ * QGroundControl is licensed according to the terms in the file
+ * COPYING.md in the root of the source code directory.
+ *
+ ****************************************************************************/
+
+import QtQuick 2.12
+
+Item {
+ width: childrenRect.width
+ height: childrenRect.height
+
+ signal hideDialog
+
+ Keys.onReleased: {
+ if (event.key === Qt.Key_Escape) {
+ reject()
+ event.accepted = true
+ } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
+ accept()
+ event.accepted = true
+ }
+ }
+
+ function accept() {
+ if (acceptAllowed) {
+ Qt.inputMethod.hide()
+ hideDialog()
+ }
+ }
+
+ function reject() {
+ if (rejectAllowed) {
+ Qt.inputMethod.hide()
+ hideDialog()
+ }
+ }
+}
diff --git a/src/QmlControls/QGCPopupDialogContainer.qml b/src/QmlControls/QGCPopupDialogContainer.qml
new file mode 100644
index 0000000000000000000000000000000000000000..4a2b6d665b6a37fb6f438804e8844aff6742a58f
--- /dev/null
+++ b/src/QmlControls/QGCPopupDialogContainer.qml
@@ -0,0 +1,174 @@
+/****************************************************************************
+ *
+ * (c) 2009-2020 QGROUNDCONTROL PROJECT
+ *
+ * QGroundControl is licensed according to the terms in the file
+ * COPYING.md in the root of the source code directory.
+ *
+ ****************************************************************************/
+
+import QtQuick 2.12
+import QtQuick.Controls 2.4
+import QtQuick.Layouts 1.12
+import QtQuick.Dialogs 1.3
+
+import QGroundControl 1.0
+import QGroundControl.Controls 1.0
+import QGroundControl.Palette 1.0
+import QGroundControl.ScreenTools 1.0
+
+Popup {
+ anchors.centerIn: parent
+ width: mainFlickable.width
+ height: mainFlickable.height
+ padding: 0
+ modal: true
+ focus: true
+
+ background: Rectangle {
+ color: QGroundControl.globalPalette.window
+ }
+
+ property string title
+ property var buttons
+ property var dialogComponent
+
+ property real _contentMargin: ScreenTools.defaultFontPixelHeight / 2
+ property real _popupDoubleInset: ScreenTools.defaultFontPixelHeight * 2
+ property real _maxAvailableWidth: parent.width - _popupDoubleInset
+ property real _maxAvailableHeight: parent.height - _popupDoubleInset
+
+ Component.onCompleted: setupDialogButtons()
+
+ QGCPalette { id: qgcPal; colorGroupEnabled: parent.enabled }
+
+ function setupDialogButtons() {
+ acceptButton.visible = false
+ rejectButton.visible = false
+ // Accept role buttons
+ if (buttons & StandardButton.Ok) {
+ acceptButton.text = qsTr("Ok")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.Open) {
+ acceptButton.text = qsTr("Open")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.Save) {
+ acceptButton.text = qsTr("Save")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.Apply) {
+ acceptButton.text = qsTr("Apply")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.Open) {
+ acceptButton.text = qsTr("Open")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.SaveAll) {
+ acceptButton.text = qsTr("Save All")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.Yes) {
+ acceptButton.text = qsTr("Yes")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.YesToAll) {
+ acceptButton.text = qsTr("Yes to All")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.Retry) {
+ acceptButton.text = qsTr("Retry")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.Reset) {
+ acceptButton.text = qsTr("Reset")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.RestoreToDefaults) {
+ acceptButton.text = qsTr("Restore to Defaults")
+ acceptButton.visible = true
+ } else if (buttons & StandardButton.Ignore) {
+ acceptButton.text = qsTr("Ignore")
+ acceptButton.visible = true
+ }
+
+ // Reject role buttons
+ if (buttons & StandardButton.Cancel) {
+ rejectButton.text = qsTr("Cancel")
+ rejectButton.visible = true
+ } else if (buttons & StandardButton.Close) {
+ rejectButton.text = qsTr("Close")
+ rejectButton.visible = true
+ } else if (buttons & StandardButton.No) {
+ rejectButton.text = qsTr("No")
+ rejectButton.visible = true
+ } else if (buttons & StandardButton.NoToAll) {
+ rejectButton.text = qsTr("No to All")
+ rejectButton.visible = true
+ } else if (buttons & StandardButton.Abort) {
+ rejectButton.text = qsTr("Abort")
+ rejectButton.visible = true
+ }
+
+ if (rejectButton.visible) {
+ closePolicy = Popup.NoAutoClose | Popup.CloseOnEscape
+ } else {
+ closePolicy = Popup.NoAutoClose
+ }
+ }
+
+ Connections {
+ target: dialogComponentLoader.item
+ onHideDialog: close()
+ }
+
+ QGCFlickable {
+ id: mainFlickable
+ width: Math.min(mainColumnLayout.width, _maxAvailableWidth)
+ height: Math.min(mainColumnLayout.height, _maxAvailableHeight)
+ contentWidth: mainColumnLayout.width
+ contentHeight: mainColumnLayout.height
+
+ Rectangle {
+ width: titleRowLayout.width
+ height: titleRowLayout.height
+ color: qgcPal.windowShade
+ }
+
+ ColumnLayout {
+ id: mainColumnLayout
+ spacing: _contentMargin
+
+ RowLayout {
+ id: titleRowLayout
+ Layout.fillWidth: true
+
+ QGCLabel {
+ Layout.leftMargin: ScreenTools.defaultFontPixelWidth
+ Layout.fillWidth: true
+ text: title
+ height: parent.height
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ QGCButton {
+ id: rejectButton
+ onClicked: dialogComponentLoader.item.reject()
+ }
+
+ QGCButton {
+ id: acceptButton
+ primary: true
+ onClicked: dialogComponentLoader.item.accept()
+ }
+ }
+
+ Item {
+ id: item
+ width: dialogComponentLoader.width + (_contentMargin * 2)
+ height: dialogComponentLoader.height + _contentMargin
+
+ Loader {
+ id: dialogComponentLoader
+ x: _contentMargin
+ sourceComponent: dialogComponent
+ focus: true
+ property bool acceptAllowed: acceptButton.visible
+ property bool rejectAllowed: rejectButton.visible
+ }
+ }
+ }
+ }
+}
diff --git a/src/QmlControls/QGroundControl/Controls/qmldir b/src/QmlControls/QGroundControl/Controls/qmldir
index 33bd6924834dc0c8a9920b7ffd00c01c59ba6c2d..64250fb09f75cfea6a4135e1b510506d5af2719e 100644
--- a/src/QmlControls/QGroundControl/Controls/qmldir
+++ b/src/QmlControls/QGroundControl/Controls/qmldir
@@ -64,6 +64,8 @@ QGCMouseArea 1.0 QGCMouseArea.qml
QGCMovableItem 1.0 QGCMovableItem.qml
QGCOptionsComboBox 1.0 QGCOptionsComboBox.qml
QGCPipable 1.0 QGCPipable.qml
+QGCPopupDialog 1.0 QGCPopupDialog.qml
+QGCPopupDialogContainer 1.0 QGCPopupDialogContainer.qml
QGCRadioButton 1.0 QGCRadioButton.qml
QGCSlider 1.0 QGCSlider.qml
QGCSwitch 1.0 QGCSwitch.qml
diff --git a/src/ui/MainRootWindow.qml b/src/ui/MainRootWindow.qml
index c4e16a072765d5e81aef35090cce094f40ba929c..b8edc6cb61840595efd95134a8b44713725d83fb 100644
--- a/src/ui/MainRootWindow.qml
+++ b/src/ui/MainRootWindow.qml
@@ -223,6 +223,16 @@ ApplicationWindow {
}
}
+ function showPopupDialog(component, title, buttons) {
+ var popup = popupDialogContainterComponent.createObject(mainWindow, { "title": title, "buttons": buttons, "dialogComponent": component})
+ popup.open()
+ }
+
+ Component {
+ id: popupDialogContainterComponent
+ QGCPopupDialogContainer { }
+ }
+
property bool _forceClose: false
function finishCloseProcess() {