diff --git a/src/FactSystem/FactControls/FactTextField.qml b/src/FactSystem/FactControls/FactTextField.qml index 961e7c08995..a4cb05aefed 100644 --- a/src/FactSystem/FactControls/FactTextField.qml +++ b/src/FactSystem/FactControls/FactTextField.qml @@ -8,8 +8,7 @@ import QGroundControl.Controls import QGroundControl.ScreenTools QGCTextField { - id: control - + id: control text: fact ? fact.valueString : "" unitsLabel: fact ? fact.units : "" showUnits: true @@ -18,50 +17,21 @@ QGCTextField { signal updated() - property Fact fact: null - - property string _validateString + property Fact fact: null onEditingFinished: { var errorString = fact.validate(text, false /* convertOnly */) if (errorString === "") { - globals.validationError = false - validationToolTip.visible = false + clearValidationError() fact.value = text control.updated() } else { - globals.validationError = true - validationToolTip.text = errorString - validationToolTip.visible = true + showValidationError(errorString, fact.valueString) } } onHelpClicked: helpDialogComponent.createObject(mainWindow).open() - ToolTip { - id: validationToolTip - - QGCMouseArea { - anchors.fill: parent - onClicked: { - control.text = fact.valueString - validationToolTip.visible = false - globals.validationError = false - } - } - } - - Component { - id: validationErrorDialogComponent - - ParameterEditorDialog { - title: qsTr("Invalid Value") - validate: true - validateValue: _validateString - fact: control.fact - } - } - Component { id: helpDialogComponent diff --git a/src/QmlControls/QGCPopupDialog.qml b/src/QmlControls/QGCPopupDialog.qml index 2397102b75a..bdfc4fd04f1 100644 --- a/src/QmlControls/QGCPopupDialog.qml +++ b/src/QmlControls/QGCPopupDialog.qml @@ -50,8 +50,6 @@ Popup { property string title property var buttons: Dialog.Ok - property bool acceptAllowed: acceptButton.visible - property bool rejectAllowed: rejectButton.visible property alias acceptButtonEnabled: acceptButton.enabled property alias rejectButtonEnabled: rejectButton.enabled property var dialogProperties @@ -66,6 +64,8 @@ Popup { property var _qgcPal: QGroundControl.globalPalette property real _frameSize: ScreenTools.defaultFontPixelWidth property real _contentMargin: ScreenTools.defaultFontPixelHeight / 2 + property bool _acceptAllowed: acceptButton.visible + property bool _rejectAllowed: rejectButton.visible background: QGCMouseArea { width: mainWindow.width @@ -73,13 +73,7 @@ Popup { onClicked: { if (closePolicy & Popup.CloseOnPressOutside) { - if (rejectAllowed) { - focus = true // Take focus to force FactTextFields to validate - _reject() - } else if (acceptAllowed) { - focus = true // Take focus to force FactTextFields to validate - _accept() - } + _reject() } } } @@ -99,7 +93,7 @@ Popup { } function _accept() { - if (acceptAllowed && !globals.validationError) { + if (_acceptAllowed && mainWindow.allowViewSwitch()) { accepted() if (preventClose) { preventClose = false @@ -110,7 +104,8 @@ Popup { } function _reject() { - if (rejectAllowed && !globals.validationError) { + // Dialogs with cancel button are allowed to close with validation errors + if (_rejectAllowed && ((buttons & Dialog.Cancel) || mainWindow.allowViewSwitch())) { rejected() if (preventClose) { preventClose = false @@ -183,7 +178,7 @@ Popup { } closePolicy = Popup.NoAutoClose - if (rejectAllowed) { + if (buttons & Dialog.Cancel) { closePolicy |= Popup.CloseOnEscape } } @@ -259,7 +254,7 @@ Popup { focus: true Keys.onPressed: (event) => { - if (event.key === Qt.Key_Escape && rejectAllowed) { + if (event.key === Qt.Key_Escape && _rejectAllowed) { _reject() event.accepted = true } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { diff --git a/src/QmlControls/QGCTextField.qml b/src/QmlControls/QGCTextField.qml index 1db2a2413f9..7de56bb71f8 100644 --- a/src/QmlControls/QGCTextField.qml +++ b/src/QmlControls/QGCTextField.qml @@ -27,15 +27,16 @@ TextField { property string unitsLabel: "" property string extraUnitsLabel: "" property bool numericValuesOnly: false // true: Used as hint for mobile devices to show numeric only keyboard - property alias textColor: control.color + property alias textColor: control.color + property bool validationError: false property real _helpLayoutWidth: 0 property real _marginPadding: ScreenTools.defaultFontPixelHeight / 3 signal helpClicked - Component.onCompleted: selectAllIfActiveFocus() - onActiveFocusChanged: selectAllIfActiveFocus() + Component.onCompleted: checkActiveFocus() + onActiveFocusChanged: checkActiveFocus() QGCPalette { id: qgcPal; colorGroupEnabled: enabled } @@ -46,15 +47,37 @@ TextField { } } - function selectAllIfActiveFocus() { + function checkActiveFocus() { if (activeFocus) { selectAll() + if (validationError) { + validationToolTip.visible = true + } + } + } + + function showValidationError(errorString, originalValidValue = undefined) { + validationToolTip.text = errorString + validationToolTip.originalValidValue = originalValidValue + validationToolTip.visible = true + if (!validationError) { + validationError = true + globals.validationErrorCount++ + } + } + + function clearValidationError() { + validationToolTip.visible = false + validationToolTip.originalValidValue = undefined + if (validationError) { + validationError = false + globals.validationErrorCount-- } } background: Rectangle { - border.width: qgcPal.globalTheme === QGCPalette.Light ? 1 : 0 - border.color: qgcPal.buttonBorder + border.width: control.validationError ? 2 : (qgcPal.globalTheme === QGCPalette.Light ? 1 : 0) + border.color: control.validationError ? qgcPal.colorRed : qgcPal.buttonBorder radius: ScreenTools.buttonBorderRadius color: qgcPal.textField implicitWidth: ScreenTools.implicitTextFieldWidth @@ -117,6 +140,22 @@ TextField { } } + ToolTip { + id: validationToolTip + + property var originalValidValue: undefined + + QGCMouseArea { + anchors.fill: parent + onClicked: { + if (validationToolTip.originalValidValue !== undefined) { + control.text = validationToolTip.originalValidValue + control.clearValidationError() + } + } + } + } + MouseArea { anchors.top: parent.top anchors.bottom: parent.bottom diff --git a/src/UI/AppSettings.qml b/src/UI/AppSettings.qml index 11a1698efa6..f0480b522d6 100644 --- a/src/UI/AppSettings.qml +++ b/src/UI/AppSettings.qml @@ -114,14 +114,12 @@ Rectangle { } onClicked: { - focus = true - if (mainWindow.preventViewSwitch()) { - return - } - if (rightPanel.source !== url) { - rightPanel.source = url + if (mainWindow.allowViewSwitch()) { + if (rightPanel.source !== url) { + rightPanel.source = url + } + checked = true } - checked = true } Component.onCompleted: { diff --git a/src/UI/MainRootWindow.qml b/src/UI/MainRootWindow.qml index 4e6586d9c14..c571d98da98 100644 --- a/src/UI/MainRootWindow.qml +++ b/src/UI/MainRootWindow.qml @@ -87,9 +87,10 @@ ApplicationWindow { readonly property var planMasterControllerFlyView: flyView.planController readonly property var guidedControllerFlyView: flyView.guidedController - property bool validationError: false // There is a FactTextField somewhere with a validation error + // Number of QGCTextField's with validation errors. Used to prevent closing panels with validation errors. + property int validationErrorCount: 0 - // Property to manage RemoteID quick acces to settings page + // Property to manage RemoteID quick access to settings page property bool commingFromRIDIndicator: false } @@ -109,9 +110,13 @@ ApplicationWindow { //------------------------------------------------------------------------- //-- Global Scope Functions - /// @return true: View switches are not currently allowed - function preventViewSwitch() { - return globals.validationError + // This function is used to prevent view switching if there are validation errors + function allowViewSwitch() { + // Run validation on active focus control to ensure it is valid before switching views + if (mainWindow.activeFocusControl instanceof QGCTextField) { + mainWindow.activeFocusControl.onEditingFinished() + } + return globals.validationErrorCount === 0 } function showPlanView() { @@ -259,7 +264,7 @@ ApplicationWindow { } function showToolSelectDialog() { - if (!mainWindow.preventViewSwitch()) { + if (mainWindow.allowViewSwitch()) { mainWindow.showIndicatorDrawer(toolSelectComponent, null) } } @@ -291,7 +296,7 @@ ApplicationWindow { text: qsTr("Vehicle Setup") imageResource: "/qmlimages/Gears.svg" onClicked: { - if (!mainWindow.preventViewSwitch()) { + if (mainWindow.allowViewSwitch()) { mainWindow.closeIndicatorDrawer() mainWindow.showVehicleSetupTool() } @@ -306,7 +311,7 @@ ApplicationWindow { imageResource: "/qmlimages/Analyze.svg" visible: QGroundControl.corePlugin.showAdvancedUI onClicked: { - if (!mainWindow.preventViewSwitch()) { + if (mainWindow.allowViewSwitch()) { mainWindow.closeIndicatorDrawer() mainWindow.showAnalyzeTool() } @@ -322,7 +327,7 @@ ApplicationWindow { imageColor: "transparent" visible: !QGroundControl.corePlugin.options.combineSettingsAndSetup onClicked: { - if (!mainWindow.preventViewSwitch()) { + if (mainWindow.allowViewSwitch()) { drawer.close() mainWindow.showSettingsTool() } @@ -498,7 +503,9 @@ ApplicationWindow { x: parent.mapFromItem(backIcon, backIcon.x, backIcon.y).x width: (backTextLabel.x + backTextLabel.width) - backIcon.x onClicked: { - toolDrawer.visible = false + if (mainWindow.allowViewSwitch()) { + toolDrawer.visible = false + } } } } diff --git a/src/UI/preferences/LinkSettings.qml b/src/UI/preferences/LinkSettings.qml index d1a0f82f17b..3b4ed5fe02a 100644 --- a/src/UI/preferences/LinkSettings.qml +++ b/src/UI/preferences/LinkSettings.qml @@ -142,9 +142,9 @@ SettingsPage { id: linkDialogComponent QGCPopupDialog { - title: originalConfig ? qsTr("Edit Link") : qsTr("Add New Link") - buttons: Dialog.Save | Dialog.Cancel - acceptAllowed: nameField.text !== "" + title: originalConfig ? qsTr("Edit Link") : qsTr("Add New Link") + buttons: Dialog.Save | Dialog.Cancel + acceptButtonEnabled: nameField.text !== "" property var originalConfig property var editingConfig diff --git a/src/UI/toolbar/RemoteIDIndicator.qml b/src/UI/toolbar/RemoteIDIndicator.qml index 221c4da56a6..52c713825f6 100644 --- a/src/UI/toolbar/RemoteIDIndicator.qml +++ b/src/UI/toolbar/RemoteIDIndicator.qml @@ -90,7 +90,7 @@ Item { } function goToSettings() { - if (!mainWindow.preventViewSwitch()) { + if (mainWindow.allowViewSwitch()) { globals.commingFromRIDIndicator = true mainWindow.showSettingsTool() } diff --git a/src/UI/toolbar/RemoteIDIndicatorPage.qml b/src/UI/toolbar/RemoteIDIndicatorPage.qml index 2b3f93fbc85..4f8b9f52b23 100644 --- a/src/UI/toolbar/RemoteIDIndicatorPage.qml +++ b/src/UI/toolbar/RemoteIDIndicatorPage.qml @@ -46,7 +46,7 @@ ToolIndicatorPage { } function goToSettings() { - if (!mainWindow.preventViewSwitch()) { + if (mainWindow.allowViewSwitch()) { globals.commingFromRIDIndicator = true mainWindow.showSettingsTool() } diff --git a/src/VehicleSetup/SetupView.qml b/src/VehicleSetup/SetupView.qml index cc5f5d853cc..b5841168bb8 100644 --- a/src/VehicleSetup/SetupView.qml +++ b/src/VehicleSetup/SetupView.qml @@ -40,10 +40,9 @@ Rectangle { property var _corePlugin: QGroundControl.corePlugin function showSummaryPanel() { - if (mainWindow.preventViewSwitch()) { - return + if (mainWindow.allowViewSwitch()) { + _showSummaryPanel() } - _showSummaryPanel() } function _showSummaryPanel() { @@ -62,48 +61,45 @@ Rectangle { } function showPanel(button, qmlSource) { - if (mainWindow.preventViewSwitch()) { - return + if (mainWindow.allowViewSwitch()) { + button.checked = true + panelLoader.setSource(qmlSource) } - button.checked = true - panelLoader.setSource(qmlSource) } function showVehicleComponentPanel(vehicleComponent) { - if (mainWindow.preventViewSwitch()) { - return - } - var autopilotPlugin = QGroundControl.multiVehicleManager.activeVehicle.autopilot - var prereq = autopilotPlugin.prerequisiteSetup(vehicleComponent) - if (prereq !== "") { - _messagePanelText = qsTr("%1 setup must be completed prior to %2 setup.").arg(prereq).arg(vehicleComponent.name) - panelLoader.setSourceComponent(messagePanelComponent) - } else { - panelLoader.setSource(vehicleComponent.setupSource, vehicleComponent) - for(var i = 0; i < componentRepeater.count; i++) { - var obj = componentRepeater.itemAt(i); - if (obj.text === vehicleComponent.name) { - obj.checked = true - break; + if (mainWindow.allowViewSwitch()) { + var autopilotPlugin = QGroundControl.multiVehicleManager.activeVehicle.autopilot + var prereq = autopilotPlugin.prerequisiteSetup(vehicleComponent) + if (prereq !== "") { + _messagePanelText = qsTr("%1 setup must be completed prior to %2 setup.").arg(prereq).arg(vehicleComponent.name) + panelLoader.setSourceComponent(messagePanelComponent) + } else { + panelLoader.setSource(vehicleComponent.setupSource, vehicleComponent) + for(var i = 0; i < componentRepeater.count; i++) { + var obj = componentRepeater.itemAt(i); + if (obj.text === vehicleComponent.name) { + obj.checked = true + break; + } } } } } function showNamedComponentPanel(panelButtonName) { - if (mainWindow.preventViewSwitch()) { - return - } - for (var i=0; i