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
+ }
+ }
}