diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 1523185f6d0dceb1b4903f6ab1cdd5d102ddd6df..f789b9cfe2ba5ae7e403721bb741207e2d83eb7e 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -1387,3 +1387,6 @@ contains (CONFIG, QGC_DISABLE_INSTALLER_SETUP) { } else { include(QGCInstaller.pri) } + +DISTFILES += \ + src/QmlControls/QGroundControl/Specific/qmldir diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 7d51d491cd1faa1b18ee20bf83aa1c9da7c80348..60c9112a0a4761371abc01ebe6540228a95ce71b 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -246,6 +246,10 @@ src/FlightDisplay/VirtualJoystick.qml src/PlanView/VTOLLandingPatternMapVisual.qml src/PlanView/VTOLLandingPatternEditor.qml + src/QmlControls/QGroundControl/Specific/qmldir + src/QmlControls/QGroundControl/Specific/StartupWizard.qml + src/QmlControls/QGroundControl/Specific/BaseStartupWizardPage.qml + src/QmlControls/QGroundControl/Specific/UnitsWizardPage.qml src/Settings/ADSBVehicleManager.SettingsGroup.json diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 89d7522e30fb76b91df7aeee2cfca6ca6c8664d2..036e3bc960b408ffb5114de4e1ef5d035ef97bc9 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -366,7 +366,8 @@ void QGCApplication::_exitWithError(QString errorMessage) void QGCApplication::setLanguage() { _locale = QLocale::system(); - qDebug() << "System reported locale:" << _locale << _locale.name(); + qDebug() << "System reported locale:" << _locale << "; Name" << _locale.name() << "; Preffered (used in maps): " << (QLocale::system().uiLanguages().length() > 0 ? QLocale::system().uiLanguages()[0] : "None"); + int langID = toolbox()->settingsManager()->appSettings()->language()->rawValue().toInt(); //-- See App.SettinsGroup.json for index if(langID) { diff --git a/src/QmlControls/QGroundControl/Specific/BaseStartupWizardPage.qml b/src/QmlControls/QGroundControl/Specific/BaseStartupWizardPage.qml new file mode 100644 index 0000000000000000000000000000000000000000..2e902f6d306015f3b3df91b72f2c624ee4f73a44 --- /dev/null +++ b/src/QmlControls/QGroundControl/Specific/BaseStartupWizardPage.qml @@ -0,0 +1,10 @@ +import QtQuick 2.12 + +Item { + // `null` for default which makes the wizzard display one of the buttons: "Next" if more pages or "Done" if the last + property string doneText: null + // Blocks user from closing the wizard or going to the next page until this becomes true + property bool forceConfirmation: false + + signal closeView() +} diff --git a/src/QmlControls/QGroundControl/Specific/StartupWizard.qml b/src/QmlControls/QGroundControl/Specific/StartupWizard.qml new file mode 100644 index 0000000000000000000000000000000000000000..a4656d5e408ba347504a7fb8dcce44a99ce70eba --- /dev/null +++ b/src/QmlControls/QGroundControl/Specific/StartupWizard.qml @@ -0,0 +1,87 @@ +import QtQuick 2.11 +import QtQuick.Layouts 1.11 + +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl 1.0 +import QGroundControl.Specific 1.0 + + +Item { + id: _root + + implicitWidth: contentColumn.implicitWidth + contentColumn.anchors.margins * 2.5 + implicitHeight: contentColumn.implicitHeight + contentColumn.anchors.margins * 2 + contentColumn.spacing * 3 + + property bool forceKeepingOpen: _pageReady && pageLoader.item.forceConfirmation && !_armed + + signal closeView() + + property bool _pageReady: pageLoader.status === Loader.Ready + property int _currentIndex: 0 + property int _pagesCount: QGroundControl.corePlugin.startupPages.length + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool _armed: _activeVehicle && _activeVehicle.armed + + function doneOrJumpToNext() { + if(_currentIndex < _pagesCount - 1) + _currentIndex += 1 + else { + _root.closeView() + QGroundControl.settingsManager.appSettings.firstTimeStart.value = false + } + } + + Connections { + target: _pageReady ? pageLoader.item : null + onCloseView: doneOrJumpToNext() + } + + ColumnLayout { + id: contentColumn + anchors.fill: parent + anchors.margins: ScreenTools.defaultFontPixelHeight + spacing: ScreenTools.defaultFontPixelHeight * 0.5 + + QGCLabel { + id: titleLabel + text: qsTr("Welcome to " + QGroundControl.appName) + color: qgcPal.text + font.family: ScreenTools.demiboldFontFamily + font.pointSize: ScreenTools.mediumFontPointSize + } + + Rectangle { + id: separatorRect + height: 1 + color: qgcPal.windowShade + Layout.fillWidth: true + } + + Flickable { + id: flickablePage + clip: true + + contentWidth: pageLoader.item ? pageLoader.item.width : 0 + contentHeight: pageLoader.item ? pageLoader.item.height : 0 + + Layout.fillHeight: true + Layout.preferredWidth: contentWidth + Layout.preferredHeight: contentHeight + + Loader { + id: pageLoader + source: QGroundControl.corePlugin.startupPages[_currentIndex] + } + } + + QGCButton { + id: confirmButton + property string _acknowledgeText: _pagesCount <= 1 ? qsTr("Next") : qsTr("Done") + + text: (_pageReady && pageLoader.item && pageLoader.item.doneText) ? pageLoader.item.doneText : _acknowledgeText + onClicked: doneOrJumpToNext() + } + } +} diff --git a/src/QmlControls/QGroundControl/Specific/UnitsWizardPage.qml b/src/QmlControls/QGroundControl/Specific/UnitsWizardPage.qml new file mode 100644 index 0000000000000000000000000000000000000000..80df2bab5ecc263260ed271308e958552a6ab1b0 --- /dev/null +++ b/src/QmlControls/QGroundControl/Specific/UnitsWizardPage.qml @@ -0,0 +1,81 @@ +import QtQuick 2.12 +import QtQuick.Layouts 1.12 + +import QGroundControl 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.SettingsManager 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Specific 1.0 + +BaseStartupWizardPage { + width: settingsColumn.width + height: settingsColumn.height + + property real _margins: ScreenTools.defaultFontPixelWidth + property real _comboFieldWidth: ScreenTools.defaultFontPixelWidth * 20 + + doneText: qsTr("Confirm") + + ColumnLayout { + id: settingsColumn + anchors.horizontalCenter: parent.horizontalCenter + spacing: ScreenTools.defaultFontPixelHeight + + QGCLabel { + id: unitsSectionLabel + text: qsTr("Choose the measurement units you want to use in the application. You can change it later on in General Settings.") + + Layout.preferredWidth: unitsGrid.width + wrapMode: Text.WordWrap + } + + Rectangle { + Layout.preferredHeight: unitsGrid.height + (_margins * 2) + Layout.preferredWidth: unitsGrid.width + (_margins * 2) + color: qgcPal.windowShade + Layout.fillWidth: true + + GridLayout { + id: unitsGrid + anchors.topMargin: _margins + anchors.top: parent.top + Layout.fillWidth: false + anchors.horizontalCenter: parent.horizontalCenter + flow: GridLayout.TopToBottom + rows: 5 + + QGCLabel { text: qsTr("System of units") } + + Repeater { + model: [ qsTr("Distance"), qsTr("Area"), qsTr("Speed"), qsTr("Temperature") ] + QGCLabel { text: modelData } + } + + QGCComboBox { + model: [qsTr("Metric System"), qsTr("Imperial System")] + Layout.preferredWidth: _comboFieldWidth + + currentIndex: QGroundControl.settingsManager.unitsSettings.distanceUnits.value === UnitsSettings.DistanceUnitsMeters ? 0 : 1 + + onActivated: { + var metric = (currentIndex === 0); + QGroundControl.settingsManager.unitsSettings.distanceUnits.value = metric ? UnitsSettings.DistanceUnitsMeters : UnitsSettings.DistanceUnitsFeet + QGroundControl.settingsManager.unitsSettings.areaUnits.value = metric ? UnitsSettings.AreaUnitsSquareMeters : UnitsSettings.AreaUnitsSquareFeet + QGroundControl.settingsManager.unitsSettings.speedUnits.value = metric ? UnitsSettings.SpeedUnitsMetersPerSecond : UnitsSettings.SpeedUnitsFeetPerSecond + QGroundControl.settingsManager.unitsSettings.temperatureUnits.value = metric ? UnitsSettings.TemperatureUnitsCelsius : UnitsSettings.TemperatureUnitsFarenheit + } + } + Repeater { + model: [ QGroundControl.settingsManager.unitsSettings.distanceUnits, QGroundControl.settingsManager.unitsSettings.areaUnits, QGroundControl.settingsManager.unitsSettings.speedUnits, QGroundControl.settingsManager.unitsSettings.temperatureUnits ] + FactComboBox { + Layout.preferredWidth: _comboFieldWidth + fact: modelData + indexModel: false + } + } + } + } + } +} diff --git a/src/QmlControls/QGroundControl/Specific/qmldir b/src/QmlControls/QGroundControl/Specific/qmldir new file mode 100644 index 0000000000000000000000000000000000000000..ab493296867d194a019c16ec01ec161c055fa212 --- /dev/null +++ b/src/QmlControls/QGroundControl/Specific/qmldir @@ -0,0 +1,5 @@ +module QGroundControl.Specific + +BaseStartupWizardPage 1.0 BaseStartupWizardPage.qml +StartupWizard 1.0 StartupWizard.qml +UnitsWizardPage 1.0 UnitsWizardPage.qml diff --git a/src/Settings/App.SettingsGroup.json b/src/Settings/App.SettingsGroup.json index 945ec524553db6ed3c261c4a1d45db8e60399a47..f1312c92b069afbd76a1c9437c600937d1be3a84 100644 --- a/src/Settings/App.SettingsGroup.json +++ b/src/Settings/App.SettingsGroup.json @@ -276,5 +276,12 @@ "longDescription": "If this option is enabled, all Facts will be written to a CSV file with a 1 Hertz frequency.", "type": "bool", "defaultValue": false +}, +{ + "name": "firstTimeStart", + "shortDescription": "True if we should popup first time wizard", + "longDescription": "If this option is enabled, the first time startup wizard will prompt the user at first application start on a new system.", + "type": "bool", + "defaultValue": true } ] diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index 67e199dbb073bb81e6e02c910753c4c2e5d8a60a..5c83d5c70413274a8a9aec0735a15fa84c7ec673 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -99,6 +99,7 @@ DECLARE_SETTINGSFACT(AppSettings, language) DECLARE_SETTINGSFACT(AppSettings, disableAllPersistence) DECLARE_SETTINGSFACT(AppSettings, usePairing) DECLARE_SETTINGSFACT(AppSettings, saveCsvTelemetry) +DECLARE_SETTINGSFACT(AppSettings, firstTimeStart) DECLARE_SETTINGSFACT_NO_FUNC(AppSettings, indoorPalette) { diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index de4fa9049aa5e012f1ea6a8f8dc58e33b7fd7bb2..9c2a2b6a591e14589801c531bbca2f2b5d905074 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -59,6 +59,8 @@ public: DEFINE_SETTINGFACT(disableAllPersistence) DEFINE_SETTINGFACT(usePairing) DEFINE_SETTINGFACT(saveCsvTelemetry) + DEFINE_SETTINGFACT(firstTimeStart) + // Although this is a global setting it only affects ArduPilot vehicle since PX4 automatically starts the stream from the vehicle side DEFINE_SETTINGFACT(apmStartMavlinkStreams) diff --git a/src/Settings/UnitsSettings.cc b/src/Settings/UnitsSettings.cc index 4e3db17670a5286b1eef2cf90f5b3bd7e45439aa..86ba175285a4aa602896e460d22db8567e88a533 100644 --- a/src/Settings/UnitsSettings.cc +++ b/src/Settings/UnitsSettings.cc @@ -29,7 +29,18 @@ DECLARE_SETTINGSFACT_NO_FUNC(UnitsSettings, distanceUnits) metaData->setName(distanceUnitsName); metaData->setShortDescription("Distance units"); metaData->setEnumInfo(enumStrings, enumValues); - metaData->setRawDefaultValue(DistanceUnitsMeters); + + DistanceUnits defaultDistanceUnit = DistanceUnitsMeters; + switch(QLocale::system().measurementSystem()) { + case QLocale::MetricSystem: { + defaultDistanceUnit = DistanceUnitsMeters; + } break; + case QLocale::ImperialUSSystem: + case QLocale::ImperialUKSystem: + defaultDistanceUnit = DistanceUnitsFeet; + break; + } + metaData->setRawDefaultValue(defaultDistanceUnit); metaData->setQGCRebootRequired(true); _distanceUnitsFact = new SettingsFact(_settingsGroup, metaData, this); } @@ -54,7 +65,18 @@ DECLARE_SETTINGSFACT_NO_FUNC(UnitsSettings, areaUnits) metaData->setName(areaUnitsName); metaData->setShortDescription("Area units"); metaData->setEnumInfo(enumStrings, enumValues); - metaData->setRawDefaultValue(AreaUnitsSquareMeters); + + AreaUnits defaultAreaUnit = AreaUnitsSquareMeters; + switch(QLocale::system().measurementSystem()) { + case QLocale::MetricSystem: { + defaultAreaUnit = AreaUnitsSquareMeters; + } break; + case QLocale::ImperialUSSystem: + case QLocale::ImperialUKSystem: + defaultAreaUnit = AreaUnitsSquareMiles; + break; + } + metaData->setRawDefaultValue(defaultAreaUnit); metaData->setQGCRebootRequired(true); _areaUnitsFact = new SettingsFact(_settingsGroup, metaData, this); } @@ -78,7 +100,18 @@ DECLARE_SETTINGSFACT_NO_FUNC(UnitsSettings, speedUnits) metaData->setName(speedUnitsName); metaData->setShortDescription("Speed units"); metaData->setEnumInfo(enumStrings, enumValues); - metaData->setRawDefaultValue(SpeedUnitsMetersPerSecond); + + SpeedUnits defaultSpeedUnit = SpeedUnitsMetersPerSecond; + switch(QLocale::system().measurementSystem()) { + case QLocale::MetricSystem: { + defaultSpeedUnit = SpeedUnitsMetersPerSecond; + } break; + case QLocale::ImperialUSSystem: + case QLocale::ImperialUKSystem: + defaultSpeedUnit = SpeedUnitsMilesPerHour; + break; + } + metaData->setRawDefaultValue(defaultSpeedUnit); metaData->setQGCRebootRequired(true); _speedUnitsFact = new SettingsFact(_settingsGroup, metaData, this); } @@ -97,7 +130,18 @@ DECLARE_SETTINGSFACT_NO_FUNC(UnitsSettings, temperatureUnits) metaData->setName(temperatureUnitsName); metaData->setShortDescription("Temperature units"); metaData->setEnumInfo(enumStrings, enumValues); - metaData->setRawDefaultValue(TemperatureUnitsCelsius); + + TemperatureUnits defaultTemperatureUnit = TemperatureUnitsCelsius; + switch(QLocale::system().measurementSystem()) { + case QLocale::MetricSystem: { + defaultTemperatureUnit = TemperatureUnitsCelsius; + } break; + case QLocale::ImperialUSSystem: + case QLocale::ImperialUKSystem: + defaultTemperatureUnit = TemperatureUnitsFarenheit; + break; + } + metaData->setRawDefaultValue(defaultTemperatureUnit); metaData->setQGCRebootRequired(true); _temperatureUnitsFact = new SettingsFact(_settingsGroup, metaData, this); } diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index 5e2fb05b242abc3d5444ff0cf02ad83104cbdfcd..789ab3656454928c6358a1ce6713318306db7cd5 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -508,3 +508,9 @@ QString QGCCorePlugin::stableVersionCheckFileUrl() const return QString("https://s3-us-west-2.amazonaws.com/qgroundcontrol/latest/QGC.version.txt"); #endif } + +QStringList +QGCCorePlugin::startupPages() +{ + return { "/qml/QGroundControl/Specific/UnitsWizardPage.qml" }; +} diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h index 4ae206a427f16296162486a0aae80c275f9540d3..2eba39afc295995f711638a9c2bafae0006539c3 100644 --- a/src/api/QGCCorePlugin.h +++ b/src/api/QGCCorePlugin.h @@ -57,6 +57,8 @@ public: Q_PROPERTY(QString brandImageIndoor READ brandImageIndoor CONSTANT) Q_PROPERTY(QString brandImageOutdoor READ brandImageOutdoor CONSTANT) Q_PROPERTY(QmlObjectListModel* customMapItems READ customMapItems CONSTANT) + Q_PROPERTY(QStringList startupPages READ startupPages NOTIFY startupPagesChanged) + Q_INVOKABLE bool guidedActionsControllerLogging() const; @@ -159,6 +161,13 @@ public: /// @return Complex items to be made available to user virtual QStringList complexMissionItemNames(Vehicle* /*vehicle*/, const QStringList& complexMissionItemNames) { return complexMissionItemNames; } + /// Use it to customize the pages that are shown on startup. This will be queried + /// only if AppSettings::firstTimeStart Fact is true, that is reset to false when the user + /// goes for the fist time through all the pages. + /// Insert pages only if they are required to be displayed at start for a good user experience. + /// @return QML files paths that will be loaded using the StartupWizard control + virtual QStringList startupPages(); + bool showTouchAreas() const { return _showTouchAreas; } bool showAdvancedUI() const { return _showAdvancedUI; } void setShowTouchAreas(bool show); @@ -173,6 +182,7 @@ signals: void instrumentPagesChanged (); void showTouchAreasChanged (bool showTouchAreas); void showAdvancedUIChanged (bool showAdvancedUI); + void startupPagesChanged (); protected slots: void _activeVehicleChanged (Vehicle* activeVehicle); diff --git a/src/ui/MainRootWindow.qml b/src/ui/MainRootWindow.qml index b8edc6cb61840595efd95134a8b44713725d83fb..90382f08d903ddbaf137b857389c6ea51fc5e271 100644 --- a/src/ui/MainRootWindow.qml +++ b/src/ui/MainRootWindow.qml @@ -20,6 +20,7 @@ import QGroundControl.Controls 1.0 import QGroundControl.ScreenTools 1.0 import QGroundControl.FlightDisplay 1.0 import QGroundControl.FlightMap 1.0 +import QGroundControl.Specific 1.0 /// @brief Native QML top level window /// All properties defined here are visible to all QML pages. @@ -37,6 +38,11 @@ ApplicationWindow { width = ScreenTools.isMobile ? Screen.width : Math.min(250 * Screen.pixelDensity, Screen.width) height = ScreenTools.isMobile ? Screen.height : Math.min(150 * Screen.pixelDensity, Screen.height) } + + // Startup experience wizard and provide the source using QGCCorePlugin + if(QGroundControl.settingsManager.appSettings.firstTimeStart.value) { + startupPopup.open() + } } property var _rgPreventViewSwitch: [ false ] @@ -687,4 +693,34 @@ ApplicationWindow { } } + //-- Startup PopUp wizard + Popup { + id: startupPopup + anchors.centerIn: parent + + width: Math.min(startupWizard.implicitWidth, mainWindow.width - 2 * startupPopup._horizontalSpacing) + height: Math.min(startupWizard.implicitHeight, mainWindow.availableHeight - 2 * startupPopup._verticalSpacing) + + property real _horizontalSpacing: ScreenTools.defaultFontPixelWidth * 5 + property real _verticalSpacing: ScreenTools.defaultFontPixelHeight * 2 + + modal: true + focus: true + closePolicy: (startupWizard && startupWizard.forceKeepingOpen !== undefined && startupWizard.forceKeepingOpen) ? Popup.NoAutoClose : Popup.CloseOnEscape | Popup.CloseOnPressOutside + + Connections { + target: startupWizard + onCloseView: startupPopup.close() + } + + background: Rectangle { + radius: ScreenTools.defaultFontPixelHeight * 0.5 + color: qgcPal.window + } + + StartupWizard { + id: startupWizard + anchors.fill: parent + } + } }