QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCPopupDialog.qml
Go to the documentation of this file.
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Layouts
4import QtQuick.Dialogs
5
6import QGroundControl
7import QGroundControl.Controls
8
9// Provides the standard dialog mechanism for QGC. Works 99% like Qml Dialog.
10//
11// Example usage:
12// QGCPopupDialogFactory {
13// id: myDialogFactory
14// dialogComponent: myDialogComponent
15// }
16//
17// Component {
18// id: myDialogComponent
19//
20// QGCPopupDialog {
21// ...
22// }
23// }
24//
25// onFoo: myDialogFactory.open()
26// onBar: myDialogFactory.open({ title: "My Title", myProp: someValue })
27//
28// Notes:
29// * Use QGCPopupDialogFactory to create and open dialogs. The factory handles correct parenting and cleanup of the dialog instances.
30// * The dialog automatically reparents itself to Overlay.overlay on creation, while tracking the original parent's lifetime to prevent orphaned dialogs.
31// Differences from standard Qml Dialog:
32// * The QGCPopupDialog object will automatically be destroyed when it closed. You can override this
33// behaviour by setting destroyOnClose to false if it was not created dynamically.
34// * Dialog will automatically close after accepted/rejected signal processing. You can prevent this by setting
35// preventClose = true prior to returning from your signal handlers.
36Popup {
37 id: root
38 width: mainWindow.width
39 height: mainWindow.height
40 modal: true
41 focus: true
42 margins: 0
43
44 default property alias dialogContent: dialogContentParent.data
45
46 property string title
47 property var buttons: Dialog.Ok
48 property alias acceptButtonEnabled: acceptButton.enabled
49 property alias rejectButtonEnabled: rejectButton.enabled
50 property var dialogProperties
51 property bool destroyOnClose: true
52 property bool preventClose: false
53 property bool bypassNavigationCheck: false
54
55 property real maxContentAvailableWidth: mainWindow.width - _contentMargin * 6
56 property real maxContentAvailableHeight: mainWindow.height - titleRowLayout.height - _contentMargin * 7
57
58 readonly property real headerMinWidth: titleLabel.implicitWidth + rejectButton.width + acceptButton.width + titleRowLayout.spacing * 2
59
60 signal accepted
61 signal rejected
62
63 property var _qgcPal: QGroundControl.globalPalette
64 property real _frameSize: ScreenTools.defaultFontPixelWidth
65 property real _contentMargin: ScreenTools.defaultFontPixelHeight / 2
66 property bool _acceptAllowed: acceptButton.visible
67 property bool _rejectAllowed: rejectButton.visible
68 property int _previousValidationErrorCount: 0
69
70 background: QGCMouseArea {
71 width: mainWindow.width
72 height: mainWindow.height
73
74 onClicked: {
75 if (closePolicy & Popup.CloseOnPressOutside) {
76 _reject()
77 }
78 }
79 }
80
81 // We use this to track when the original parent of the dialog is destroyed. This allows us to automatically close the dialog when that happens which prevents
82 // orphaned dialogs which cause crashes.
83 Connections {
84 id: originalParentConnections
85 ignoreUnknownSignals: true // Prevents warning from initial connection when parent is null
86 onDestroyed: root.close()
87 }
88
89 Component.onCompleted: {
90 originalParentConnections.target = parent
91 parent = Overlay.overlay
92 }
93
94 onAboutToShow: {
95 _previousValidationErrorCount = globals.validationErrorCount
96 setupDialogButtons(buttons)
97 }
98
99 onClosed: {
100 globals.validationErrorCount = _previousValidationErrorCount
101 Qt.inputMethod.hide()
102 if (destroyOnClose) {
103 root.destroy()
104 }
105 }
106
107 function _accept() {
108 if (_acceptAllowed && (bypassNavigationCheck || mainWindow.allowViewSwitch(_previousValidationErrorCount))) {
109 accepted()
110 if (preventClose) {
111 preventClose = false
112 } else {
113 close()
114 }
115 }
116 }
117
118 function _reject() {
119 // Dialogs with cancel button are allowed to close with validation errors
120 if (_rejectAllowed && ((buttons & Dialog.Cancel) || bypassNavigationCheck || mainWindow.allowViewSwitch(_previousValidationErrorCount))) {
121 rejected()
122 if (preventClose) {
123 preventClose = false
124 } else {
125 close()
126 }
127 }
128 }
129
130 QGCPalette { id: qgcPal; colorGroupEnabled: parent.enabled }
131
132 function setupDialogButtons(buttons) {
133 acceptButton.visible = false
134 rejectButton.visible = false
135 // Accept role buttons
136 if (buttons & Dialog.Ok) {
137 acceptButton.text = qsTr("Ok")
138 acceptButton.visible = true
139 } else if (buttons & Dialog.Open) {
140 acceptButton.text = qsTr("Open")
141 acceptButton.visible = true
142 } else if (buttons & Dialog.Save) {
143 acceptButton.text = qsTr("Save")
144 acceptButton.visible = true
145 } else if (buttons & Dialog.Apply) {
146 acceptButton.text = qsTr("Apply")
147 acceptButton.visible = true
148 } else if (buttons & Dialog.Open) {
149 acceptButton.text = qsTr("Open")
150 acceptButton.visible = true
151 } else if (buttons & Dialog.SaveAll) {
152 acceptButton.text = qsTr("Save All")
153 acceptButton.visible = true
154 } else if (buttons & Dialog.Yes) {
155 acceptButton.text = qsTr("Yes")
156 acceptButton.visible = true
157 } else if (buttons & Dialog.YesToAll) {
158 acceptButton.text = qsTr("Yes to All")
159 acceptButton.visible = true
160 } else if (buttons & Dialog.Retry) {
161 acceptButton.text = qsTr("Retry")
162 acceptButton.visible = true
163 } else if (buttons & Dialog.Reset) {
164 acceptButton.text = qsTr("Reset")
165 acceptButton.visible = true
166 } else if (buttons & Dialog.RestoreToDefaults) {
167 acceptButton.text = qsTr("Restore to Defaults")
168 acceptButton.visible = true
169 } else if (buttons & Dialog.Ignore) {
170 acceptButton.text = qsTr("Ignore")
171 acceptButton.visible = true
172 }
173
174 // Reject role buttons
175 if (buttons & Dialog.Cancel) {
176 rejectButton.text = qsTr("Cancel")
177 rejectButton.visible = true
178 } else if (buttons & Dialog.Close) {
179 rejectButton.text = qsTr("Close")
180 rejectButton.visible = true
181 } else if (buttons & Dialog.No) {
182 rejectButton.text = qsTr("No")
183 rejectButton.visible = true
184 } else if (buttons & Dialog.NoToAll) {
185 rejectButton.text = qsTr("No to All")
186 rejectButton.visible = true
187 } else if (buttons & Dialog.Abort) {
188 rejectButton.text = qsTr("Abort")
189 rejectButton.visible = true
190 }
191
192 closePolicy = Popup.NoAutoClose
193 if (buttons & Dialog.Cancel) {
194 closePolicy |= Popup.CloseOnEscape
195 }
196 }
197
198 function disableAcceptButton() {
199 acceptButton.enabled = false
200 }
201
202 Rectangle {
203 x: mainLayout.x - _contentMargin
204 y: mainLayout.y - _contentMargin
205 width: mainLayout.width + _contentMargin * 2
206 height: mainLayout.height + _contentMargin * 2
207 color: _qgcPal.windowShade
208 radius: root.padding / 2
209 border.width: 1
210 border.color: _qgcPal.windowShadeLight
211 }
212
213 ColumnLayout {
214 id: mainLayout
215 anchors.centerIn: parent
216 x: _contentMargin
217 y: _contentMargin
218 spacing: _contentMargin
219
220 RowLayout {
221 id: titleRowLayout
222 Layout.fillWidth: true
223 spacing: _contentMargin
224
225 QGCLabel {
226 id: titleLabel
227 objectName: "popupDialog_title"
228 Layout.fillWidth: true
229 text: root.title
230 font.pointSize: ScreenTools.mediumFontPointSize
231 verticalAlignment: Text.AlignVCenter
232 }
233
234 QGCButton {
235 id: rejectButton
236 objectName: "popupDialog_rejectButton"
237 onClicked: _reject()
238 Layout.minimumWidth: height * 1.5
239 }
240
241 QGCButton {
242 id: acceptButton
243 objectName: "popupDialog_acceptButton"
244 primary: true
245 onClicked: _accept()
246 Layout.minimumWidth: height * 1.5
247 }
248 }
249
250 Rectangle {
251 Layout.fillWidth: true
252 Layout.preferredWidth: Math.min(maxAvailableWidth, totalContentWidth)
253 Layout.preferredHeight: Math.min(maxAvailableHeight, totalContentHeight)
254 color: _qgcPal.window
255
256 property real totalContentWidth: dialogContentParent.childrenRect.width + _contentMargin * 2
257 property real totalContentHeight: dialogContentParent.childrenRect.height + _contentMargin * 2
258 property real maxAvailableWidth: mainWindow.width - _contentMargin * 4
259 property real maxAvailableHeight: mainWindow.height - titleRowLayout.height - _contentMargin * 5
260
261 QGCFlickable {
262 anchors.margins: _contentMargin
263 anchors.fill: parent
264 contentWidth: dialogContentParent.childrenRect.width
265 contentHeight: dialogContentParent.childrenRect.height
266
267 Item {
268 id: dialogContentParent
269 focus: true
270
271 Keys.onPressed: (event) => {
272 if (event.key === Qt.Key_Escape && _rejectAllowed) {
273 _reject()
274 event.accepted = true
275 }
276 }
277 }
278 }
279 }
280 }
281}