diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro
index 9309e9ef4344..7c677265d6ac 100644
--- a/qgroundcontrol.pro
+++ b/qgroundcontrol.pro
@@ -1382,3 +1382,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 f363815097e8..d342c600f150 100644
--- a/qgroundcontrol.qrc
+++ b/qgroundcontrol.qrc
@@ -242,6 +242,10 @@
src/FlightMap/Widgets/VibrationPageWidget.qml
src/FlightMap/Widgets/VideoPageWidget.qml
src/FlightDisplay/VirtualJoystick.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/QmlControls/QGroundControl/Specific/BaseStartupWizardPage.qml b/src/QmlControls/QGroundControl/Specific/BaseStartupWizardPage.qml
new file mode 100644
index 000000000000..2e902f6d3060
--- /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 000000000000..af0123b2cd01
--- /dev/null
+++ b/src/QmlControls/QGroundControl/Specific/StartupWizard.qml
@@ -0,0 +1,71 @@
+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
+ width: contentColumn.width
+ height: contentColumn.height
+
+ 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.firstTimeStart = false
+ }
+ }
+
+ Column {
+ id: contentColumn
+ anchors.centerIn: parent
+ spacing: ScreenTools.defaultFontPixelHeight * 1
+ padding: spacing
+
+ QGCLabel {
+ text: qsTr("Welcome to " + QGroundControl.appName)
+ color: qgcPal.text
+ font.family: ScreenTools.demiboldFontFamily
+ font.pointSize: ScreenTools.mediumFontPointSize
+ }
+ Rectangle {
+ height: 1
+ color: qgcPal.windowShade
+ width: _pageReady ? pageLoader.item.width : 0
+ }
+
+ // Page content loader
+ Loader {
+ id: pageLoader
+ source: QGroundControl.corePlugin.startupPages[_currentIndex]
+ }
+
+ Connections {
+ target: _pageReady ? pageLoader.item : null
+ onCloseView: doneOrJumpToNext()
+ }
+
+ QGCButton {
+ 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 000000000000..554d744ea31a
--- /dev/null
+++ b/src/QmlControls/QGroundControl/Specific/UnitsWizardPage.qml
@@ -0,0 +1,81 @@
+import QtQuick 2.12
+import QtQuick.Controls 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
+
+ QGCLabel {
+ id: unitsSectionLabel
+ text: qsTr("Choose the measurement units you want to use in the application. You can change it later on in the Application Settings.")
+
+ Layout.preferredWidth: unitsGrid.width
+ wrapMode: Label.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 000000000000..ab493296867d
--- /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/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc
index 20ac868a18fa..6861fca0c8a4 100644
--- a/src/QmlControls/QGroundControlQmlGlobal.cc
+++ b/src/QmlControls/QGroundControlQmlGlobal.cc
@@ -24,6 +24,9 @@ const char* QGroundControlQmlGlobal::_flightMapPositionLatitudeSettingsKey =
const char* QGroundControlQmlGlobal::_flightMapPositionLongitudeSettingsKey = "Longitude";
const char* QGroundControlQmlGlobal::_flightMapZoomSettingsKey = "FlightMapZoom";
+static const QString _genericSettingsGroup("Generic");
+static const QString _firstTimeStartupSettingsKey("FirstTimeStartup");
+
QGeoCoordinate QGroundControlQmlGlobal::_coord = QGeoCoordinate(0.0,0.0);
double QGroundControlQmlGlobal::_zoom = 2;
@@ -217,6 +220,23 @@ bool QGroundControlQmlGlobal::apmFirmwareSupported()
return _firmwarePluginManager->supportedFirmwareTypes().contains(MAV_AUTOPILOT_ARDUPILOTMEGA);
}
+bool
+QGroundControlQmlGlobal::firstTimeStart()
+{
+ QSettings settings;
+ settings.beginGroup(_genericSettingsGroup);
+ return settings.value(_firstTimeStartupSettingsKey, true).toBool();
+}
+
+void
+QGroundControlQmlGlobal::setFirstTimeStart(bool firstTime)
+{
+ QSettings settings;
+ settings.beginGroup(_genericSettingsGroup);
+ settings.setValue(_firstTimeStartupSettingsKey, QVariant(firstTime));
+ emit firstTimeStartChanged();
+}
+
bool QGroundControlQmlGlobal::linesIntersect(QPointF line1A, QPointF line1B, QPointF line2A, QPointF line2B)
{
QPointF intersectPoint;
diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h
index a3ebcf4d6ffe..fb0e0e1f2f6f 100644
--- a/src/QmlControls/QGroundControlQmlGlobal.h
+++ b/src/QmlControls/QGroundControlQmlGlobal.h
@@ -121,6 +121,8 @@ class QGroundControlQmlGlobal : public QGCTool
Q_PROPERTY(QString qgcVersion READ qgcVersion CONSTANT)
Q_PROPERTY(bool skipSetupPage READ skipSetupPage WRITE setSkipSetupPage NOTIFY skipSetupPageChanged)
+ Q_PROPERTY(bool firstTimeStart READ firstTimeStart WRITE setFirstTimeStart NOTIFY firstTimeStartChanged)
+
Q_INVOKABLE void saveGlobalSetting (const QString& key, const QString& value);
Q_INVOKABLE QString loadGlobalSetting (const QString& key, const QString& defaultValue);
Q_INVOKABLE void saveBoolGlobalSetting (const QString& key, bool value);
@@ -236,6 +238,8 @@ class QGroundControlQmlGlobal : public QGCTool
bool apmFirmwareSupported ();
bool skipSetupPage () { return _skipSetupPage; }
void setSkipSetupPage (bool skip);
+ bool firstTimeStart();
+ void setFirstTimeStart(bool firstTime);
void setIsVersionCheckEnabled (bool enable);
void setMavlinkSystemID (int id);
@@ -264,6 +268,7 @@ class QGroundControlQmlGlobal : public QGCTool
void flightMapPositionChanged (QGeoCoordinate flightMapPosition);
void flightMapZoomChanged (double flightMapZoom);
void skipSetupPageChanged ();
+ void firstTimeStartChanged ();
private:
double _flightMapInitialZoom = 17.0;
diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc
index 48be2c132ab8..2021f503f817 100644
--- a/src/api/QGCCorePlugin.cc
+++ b/src/api/QGCCorePlugin.cc
@@ -476,3 +476,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 865bbf68b029..73c5e6868111 100644
--- a/src/api/QGCCorePlugin.h
+++ b/src/api/QGCCorePlugin.h
@@ -56,6 +56,8 @@ class QGCCorePlugin : public QGCTool
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;
@@ -157,6 +159,8 @@ class QGCCorePlugin : public QGCTool
/// @return Complex items to be made available to user
virtual QStringList complexMissionItemNames(Vehicle* /*vehicle*/, const QStringList& complexMissionItemNames) { return complexMissionItemNames; }
+ virtual QStringList startupPages();
+
bool showTouchAreas() const { return _showTouchAreas; }
bool showAdvancedUI() const { return _showAdvancedUI; }
void setShowTouchAreas(bool show);
@@ -171,6 +175,7 @@ class QGCCorePlugin : public QGCTool
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 c4e16a072765..42c190c435f9 100644
--- a/src/ui/MainRootWindow.qml
+++ b/src/ui/MainRootWindow.qml
@@ -37,6 +37,20 @@ 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.firstTimeStart) {
+ startupPopup.open()
+ }
+ }
+
+ // TODO: dev remove it
+ Shortcut {
+ sequence: "Ctrl+s"
+ onActivated: {
+ QGroundControl.firstTimeStart = true
+ startupPopup.open()
+ }
}
property var _rgPreventViewSwitch: [ false ]
@@ -677,4 +691,28 @@ ApplicationWindow {
}
}
+ //-- Startup PopUp wizard
+ Popup {
+ id: startupPopup
+ x: Math.max(Math.round((mainWindow.width - width) * 0.5), ScreenTools.defaultFontPixelWidth * 5)
+ y: Math.max(Math.round((mainWindow.height - height) * 0.5), ScreenTools.defaultFontPixelHeight * 2)
+
+ modal: true
+ focus: true
+ closePolicy: (startupLoader.item && startupLoader.item.forceKeepingOpen !== undefined && startupLoader.item.forceKeepingOpen) ? Popup.NoAutoClose : Popup.CloseOnEscape | Popup.CloseOnPressOutside
+
+ Connections {
+ target: startupLoader.item
+ onCloseView: startupPopup.close()
+ }
+
+ background: Rectangle {
+ radius: ScreenTools.defaultFontPixelHeight * 0.5
+ color: qgcPal.window
+ }
+ Loader {
+ id: startupLoader
+ source: "/qml/QGroundControl/Specific/StartupWizard.qml"
+ }
+ }
}