diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 9309e9ef434..7c677265d6a 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 f363815097e..d342c600f15 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 00000000000..2e902f6d306 --- /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 00000000000..af0123b2cd0 --- /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 00000000000..a22031c61b3 --- /dev/null +++ b/src/QmlControls/QGroundControl/Specific/UnitsWizardPage.qml @@ -0,0 +1,80 @@ +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 + + 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 00000000000..ab493296867 --- /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 20ac868a18f..6861fca0c8a 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 a3ebcf4d6ff..fb0e0e1f2f6 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 48be2c132ab..2021f503f81 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 865bbf68b02..73c5e686811 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 c4e16a07276..42c190c435f 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" + } + } }