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
54 property real maxContentAvailableWidth: mainWindow.width - _contentMargin * 6
55 property real maxContentAvailableHeight: mainWindow.height - titleRowLayout.height - _contentMargin * 7
56
57 readonly property real headerMinWidth: titleLabel.implicitWidth + rejectButton.width + acceptButton.width + titleRowLayout.spacing * 2
58
59 signal accepted
60 signal rejected
61
62 property var _qgcPal: QGroundControl.globalPalette
63 property real _frameSize: ScreenTools.defaultFontPixelWidth
64 property real _contentMargin: ScreenTools.defaultFontPixelHeight / 2
65 property bool _acceptAllowed: acceptButton.visible
66 property bool _rejectAllowed: rejectButton.visible
67 property int _previousValidationErrorCount: 0
68
69 background: QGCMouseArea {
70 width: mainWindow.width
71 height: mainWindow.height
72
73 onClicked: {
74 if (closePolicy & Popup.CloseOnPressOutside) {
75 _reject()
76 }
77 }
78 }
79
80 // 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
81 // orphaned dialogs which cause crashes.
82 Connections {
83 id: originalParentConnections
84 ignoreUnknownSignals: true // Prevents warning from initial connection when parent is null
85 onDestroyed: root.close()
86 }
87
88 Component.onCompleted: {
89 originalParentConnections.target = parent
90 parent = Overlay.overlay
91 }
92
93 onAboutToShow: {
94 _previousValidationErrorCount = globals.validationErrorCount
95 setupDialogButtons(buttons)
96 }
97
98 onClosed: {
99 globals.validationErrorCount = _previousValidationErrorCount
100 Qt.inputMethod.hide()
101 if (destroyOnClose) {
102 root.destroy()
103 }
104 }
105
106 function _accept() {
107 if (_acceptAllowed && mainWindow.allowViewSwitch(_previousValidationErrorCount)) {
108 accepted()
109 if (preventClose) {
110 preventClose = false
111 } else {
112 close()
113 }
114 }
115 }
116
117 function _reject() {
118 // Dialogs with cancel button are allowed to close with validation errors
119 if (_rejectAllowed && ((buttons & Dialog.Cancel) || mainWindow.allowViewSwitch(_previousValidationErrorCount))) {
120 rejected()
121 if (preventClose) {
122 preventClose = false
123 } else {
124 close()
125 }
126 }
127 }
128
129 QGCPalette { id: qgcPal; colorGroupEnabled: parent.enabled }
130
131 function setupDialogButtons(buttons) {
132 acceptButton.visible = false
133 rejectButton.visible = false
134 // Accept role buttons
135 if (buttons & Dialog.Ok) {
136 acceptButton.text = qsTr("Ok")
137 acceptButton.visible = true
138 } else if (buttons & Dialog.Open) {
139 acceptButton.text = qsTr("Open")
140 acceptButton.visible = true
141 } else if (buttons & Dialog.Save) {
142 acceptButton.text = qsTr("Save")
143 acceptButton.visible = true
144 } else if (buttons & Dialog.Apply) {
145 acceptButton.text = qsTr("Apply")
146 acceptButton.visible = true
147 } else if (buttons & Dialog.Open) {
148 acceptButton.text = qsTr("Open")
149 acceptButton.visible = true
150 } else if (buttons & Dialog.SaveAll) {
151 acceptButton.text = qsTr("Save All")
152 acceptButton.visible = true
153 } else if (buttons & Dialog.Yes) {
154 acceptButton.text = qsTr("Yes")
155 acceptButton.visible = true
156 } else if (buttons & Dialog.YesToAll) {
157 acceptButton.text = qsTr("Yes to All")
158 acceptButton.visible = true
159 } else if (buttons & Dialog.Retry) {
160 acceptButton.text = qsTr("Retry")
161 acceptButton.visible = true
162 } else if (buttons & Dialog.Reset) {
163 acceptButton.text = qsTr("Reset")
164 acceptButton.visible = true
165 } else if (buttons & Dialog.RestoreToDefaults) {
166 acceptButton.text = qsTr("Restore to Defaults")
167 acceptButton.visible = true
168 } else if (buttons & Dialog.Ignore) {
169 acceptButton.text = qsTr("Ignore")
170 acceptButton.visible = true
171 }
172
173 // Reject role buttons
174 if (buttons & Dialog.Cancel) {
175 rejectButton.text = qsTr("Cancel")
176 rejectButton.visible = true
177 } else if (buttons & Dialog.Close) {
178 rejectButton.text = qsTr("Close")
179 rejectButton.visible = true
180 } else if (buttons & Dialog.No) {
181 rejectButton.text = qsTr("No")
182 rejectButton.visible = true
183 } else if (buttons & Dialog.NoToAll) {
184 rejectButton.text = qsTr("No to All")
185 rejectButton.visible = true
186 } else if (buttons & Dialog.Abort) {
187 rejectButton.text = qsTr("Abort")
188 rejectButton.visible = true
189 }
190
191 closePolicy = Popup.NoAutoClose
192 if (buttons & Dialog.Cancel) {
193 closePolicy |= Popup.CloseOnEscape
194 }
195 }
196
197 function disableAcceptButton() {
198 acceptButton.enabled = false
199 }
200
201 Rectangle {
202 x: mainLayout.x - _contentMargin
203 y: mainLayout.y - _contentMargin
204 width: mainLayout.width + _contentMargin * 2
205 height: mainLayout.height + _contentMargin * 2
206 color: _qgcPal.windowShade
207 radius: root.padding / 2
208 border.width: 1
209 border.color: _qgcPal.windowShadeLight
210 }
211
212 ColumnLayout {
213 id: mainLayout
214 anchors.centerIn: parent
215 x: _contentMargin
216 y: _contentMargin
217 spacing: _contentMargin
218
219 RowLayout {
220 id: titleRowLayout
221 Layout.fillWidth: true
222 spacing: _contentMargin
223
224 QGCLabel {
225 id: titleLabel
226 Layout.fillWidth: true
227 text: root.title
228 font.pointSize: ScreenTools.mediumFontPointSize
229 verticalAlignment: Text.AlignVCenter
230 }
231
232 QGCButton {
233 id: rejectButton
234 onClicked: _reject()
235 Layout.minimumWidth: height * 1.5
236 }
237
238 QGCButton {
239 id: acceptButton
240 primary: true
241 onClicked: _accept()
242 Layout.minimumWidth: height * 1.5
243 }
244 }
245
246 Rectangle {
247 Layout.fillWidth: true
248 Layout.preferredWidth: Math.min(maxAvailableWidth, totalContentWidth)
249 Layout.preferredHeight: Math.min(maxAvailableHeight, totalContentHeight)
250 color: _qgcPal.window
251
252 property real totalContentWidth: dialogContentParent.childrenRect.width + _contentMargin * 2
253 property real totalContentHeight: dialogContentParent.childrenRect.height + _contentMargin * 2
254 property real maxAvailableWidth: mainWindow.width - _contentMargin * 4
255 property real maxAvailableHeight: mainWindow.height - titleRowLayout.height - _contentMargin * 5
256
257 QGCFlickable {
258 anchors.margins: _contentMargin
259 anchors.fill: parent
260 contentWidth: dialogContentParent.childrenRect.width
261 contentHeight: dialogContentParent.childrenRect.height
262
263 Item {
264 id: dialogContentParent
265 focus: true
266
267 Keys.onPressed: (event) => {
268 if (event.key === Qt.Key_Escape && _rejectAllowed) {
269 _reject()
270 event.accepted = true
271 }
272 }
273 }
274 }
275 }
276 }
277}