From 60d758083944e2cc7931c761c9d35e40319b3cb4 Mon Sep 17 00:00:00 2001 From: DoinLakeFlyer Date: Tue, 7 Apr 2020 10:44:39 -0700 Subject: [PATCH] New grid based values page editor support --- qgcresources.qrc | 2 + resources/LockClosed.svg | 136 ++++++ resources/LockOpen.svg | 136 ++++++ src/FlightMap/Widgets/ValuePageWidget.qml | 373 +++++++++------- .../Widgets/ValuesWidgetController.cc | 422 +++++++++++++++++- .../Widgets/ValuesWidgetController.h | 122 ++++- src/QGCApplication.cc | 2 + src/QmlControls/PageView.qml | 14 +- src/QmlControls/QGCLabel.qml | 9 +- src/api/QGCCorePlugin.cc | 37 +- src/api/QGCCorePlugin.h | 5 +- 11 files changed, 1038 insertions(+), 220 deletions(-) create mode 100644 resources/LockClosed.svg create mode 100644 resources/LockOpen.svg diff --git a/qgcresources.qrc b/qgcresources.qrc index 3b76854b2..977a849a0 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -25,6 +25,8 @@ resources/JoystickBezel.png resources/JoystickBezelLight.png resources/land.svg + resources/LockClosed.svg + resources/LockOpen.svg resources/notile.png resources/Pause.svg resources/pause-mission.svg diff --git a/resources/LockClosed.svg b/resources/LockClosed.svg new file mode 100644 index 000000000..600eb240e --- /dev/null +++ b/resources/LockClosed.svg @@ -0,0 +1,136 @@ + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + padlock silhouette a.j. 01 + 2011-01-20T20:53:10 + Originally uploaded by AJ Ashton for OCAL 0.18 + https://openclipart.org/detail/105751/padlock-silhouette-a.j.--01-by-anonymous + + + Anonymous + + + + + fix + keyword + librarians + tag + + + + + + + + + + + diff --git a/resources/LockOpen.svg b/resources/LockOpen.svg new file mode 100644 index 000000000..1bed7f6ce --- /dev/null +++ b/resources/LockOpen.svg @@ -0,0 +1,136 @@ + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + padlock unlocked silhou 01 + 2011-01-20T20:53:10 + Originally uploaded by AJ Ashton for OCAL 0.18 + https://openclipart.org/detail/105745/padlock-unlocked-silhou-01-by-anonymous + + + Anonymous + + + + + fix + keyword + librarians + tag + + + + + + + + + + + diff --git a/src/FlightMap/Widgets/ValuePageWidget.qml b/src/FlightMap/Widgets/ValuePageWidget.qml index 944f59442..e0db7f752 100644 --- a/src/FlightMap/Widgets/ValuePageWidget.qml +++ b/src/FlightMap/Widgets/ValuePageWidget.qml @@ -10,6 +10,8 @@ import QtQuick 2.3 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.5 +import QtQml 2.12 import QGroundControl.Controls 1.0 import QGroundControl.ScreenTools 1.0 @@ -21,23 +23,32 @@ import QGroundControl 1.0 /// Value page for InstrumentPanel PageView Column { - id: _largeColumn + id: _root width: pageWidth - spacing: _margins + spacing: ScreenTools.defaultFontPixelHeight / 2 property bool showSettingsIcon: true - - property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle - property real _margins: ScreenTools.defaultFontPixelWidth / 2 + property bool showLockIcon: true + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle + property real _margins: ScreenTools.defaultFontPixelWidth / 2 + property int _colMax: 4 + property bool _settingsUnlocked: false + property var _valuePickerInstrumentValue: null + property int _valuePickerRowIndex: 0 + property var _rgFontSizes: [ ScreenTools.defaultFontPointSize, ScreenTools.smallFontPointSize, ScreenTools.mediumFontPointSize, ScreenTools.largeFontPointSize ] + property real _blankEntryHeight: ScreenTools.defaultFontPixelHeight * 2 + property real _columnButtonWidth: ScreenTools.minTouchPixels / 2 + property real _columnButtonHeight: ScreenTools.minTouchPixels + property real _columnButtonSpacing: 2 + property real _columnButtonsTotalHeight: (_columnButtonHeight * 2) + _columnButtonSpacing QGCPalette { id:qgcPal; colorGroupEnabled: true } - ValuesWidgetController { - id: controller - } + ValuesWidgetController { id: controller } - function showSettings() { - mainWindow.showComponentDialog(propertyPicker, qsTr("Value Widget Setup"), mainWindow.showDialogDefaultWidth, StandardButton.Ok) + function showSettings(settingsUnlocked) { + _settingsUnlocked = settingsUnlocked } function listContains(list, value) { @@ -49,88 +60,166 @@ Column { return false } - Repeater { - model: _activeVehicle ? controller.largeValues : 0 - Loader { - sourceComponent: fact ? largeValue : undefined - property Fact fact: _activeVehicle.getFact(modelData.replace("Vehicle.", "")) - } - } // Repeater - Large - - Flow { - id: _smallFlow - width: parent.width - layoutDirection: Qt.LeftToRight - spacing: _margins - - Repeater { - model: _activeVehicle ? controller.smallValues : 0 - Loader { - sourceComponent: fact ? smallValue : undefined - property Fact fact: _activeVehicle.getFact(modelData.replace("Vehicle.", "")) - } - } // Repeater - Small - } // Flow + ButtonGroup { id: factRadioGroup } Component { - id: largeValue + id: valueItemMouseAreaComponent - Column { - width: _largeColumn.width - property bool largeValue: listContains(controller.altitudeProperties, fact.name) - - QGCLabel { - width: parent.width - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - text: fact.shortDescription + (fact.units ? " (" + fact.units + ")" : "") - } - QGCLabel { - width: parent.width - horizontalAlignment: Text.AlignHCenter - font.pointSize: ScreenTools.mediumFontPointSize * (largeValue ? 1.3 : 1.0) - font.family: largeValue ? ScreenTools.demiboldFontFamily : ScreenTools.normalFontFamily - fontSizeMode: Text.HorizontalFit - text: fact.enumOrValueString + MouseArea { + anchors.centerIn: parent + width: parent.width + height: _columnButtonsTotalHeight + visible: _settingsUnlocked + + property var instrumentValue + property int rowIndex + + onClicked: { + _valuePickerInstrumentValue = instrumentValue + _valuePickerRowIndex = rowIndex + mainWindow.showComponentDialog(valuePickerDialog, qsTr("Select Value"), mainWindow.showDialogDefaultWidth, StandardButton.Ok) } } } - Component { - id: smallValue + Repeater { + id: rowRepeater + model: controller.valuesModel Column { - width: (pageWidth / 2) - (_margins / 2) - 0.1 - clip: true - - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - horizontalAlignment: Text.AlignHCenter - font.pointSize: ScreenTools.isTinyScreen ? ScreenTools.smallFontPointSize * 0.75 : ScreenTools.smallFontPointSize - text: fact.shortDescription - } - QGCLabel { - width: parent.width - horizontalAlignment: Text.AlignHCenter - fontSizeMode: Text.HorizontalFit - text: fact.enumOrValueString - } - QGCLabel { - width: parent.width - horizontalAlignment: Text.AlignHCenter - font.pointSize: ScreenTools.isTinyScreen ? ScreenTools.smallFontPointSize * 0.75 : ScreenTools.smallFontPointSize - fontSizeMode: Text.HorizontalFit - text: fact.units + id: rowRepeaterLayout + spacing: 1 + + property int rowIndex: index + + Row { + id: columnRow + spacing: 1 + + Repeater { + id: columnRepeater + model: object + + property real _interColumnSpacing: (columnRepeater.count - (_settingsUnlocked ? 0 : 1)) * columnRow.spacing + property real columnWidth: (pageWidth - (_settingsUnlocked ? _columnButtonWidth : 0) - _interColumnSpacing) / columnRepeater.count + + onItemAdded: valueItemMouseAreaComponent.createObject(item, { "instrumentValue": object.get(index), "rowIndex": index }) + + Item { + width: columnRepeater.columnWidth + height: value.y + value.height + anchors.verticalCenter: _settingsUnlocked ? parent.verticalCenter : undefined + anchors.bottom: _settingsUnlocked ? undefined : parent.bottom + + QGCLabel { + width: columnRepeater.columnWidth + height: _columnButtonsTotalHeight + font.pointSize: ScreenTools.smallFontPointSize + text: _settingsUnlocked ? qsTr("BLANK") : "" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + visible: !object.fact + } + + QGCLabel { + id: label + width: columnRepeater.columnWidth + font.pointSize: ScreenTools.smallFontPointSize + text: object.label.toUpperCase() + horizontalAlignment: Text.AlignHCenter + visible: object.fact && object.label + } + + QGCLabel { + id: value + anchors.topMargin: label.visible ? 2 : 0 + anchors.top: label.visible ? label.bottom : parent.top + width: columnRepeater.columnWidth + font.pointSize: _rgFontSizes[object.fontSize] + text: visible ? (object.fact.enumOrValueString + (object.showUnits ? object.fact.units : "")) : "" + horizontalAlignment: Text.AlignHCenter + visible: object.fact + } + } + } // Repeater - columns + + ColumnLayout { + id: columnsButtonsLayout + width: _columnButtonWidth + spacing: _columnButtonSpacing + visible: _settingsUnlocked + + QGCButton { + Layout.fillHeight: true + Layout.minimumHeight: ScreenTools.minTouchPixels + Layout.preferredWidth: parent.width + text: qsTr("+") + onClicked: controller.appendColumn(rowRepeaterLayout.rowIndex) + } + + QGCButton { + Layout.fillHeight: true + Layout.minimumHeight: ScreenTools.minTouchPixels + Layout.preferredWidth: parent.width + text: qsTr("-") + enabled: index !== 0 || columnRepeater.count !== 1 + onClicked: controller.deleteLastColumn(rowRepeaterLayout.rowIndex) + } + } + } // RowLayout + + RowLayout { + width: parent.width + height: ScreenTools.defaultFontPixelWidth * 2 + spacing: 1 + visible: _settingsUnlocked + + QGCButton { + Layout.fillWidth: true + Layout.preferredHeight: ScreenTools.defaultFontPixelWidth * 2 + text: qsTr("+") + onClicked: controller.insertRow(index) + } + + QGCButton { + Layout.fillWidth: true + Layout.preferredHeight: ScreenTools.defaultFontPixelWidth * 2 + text: qsTr("-") + enabled: index !== 0 + onClicked: controller.deleteRow(index) + } } } + } // Repeater - rows + + QGCButton { + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Reset To Defaults") + visible: _settingsUnlocked + onClicked: controller.resetToDefaults() } Component { - id: propertyPicker + id: valuePickerDialog QGCViewDialog { - id: _propertyPickerDialog + function accept() { + if (factRadioGroup.checkedButton) { + _valuePickerInstrumentValue.setFact(factRadioGroup.checkedButton.radioFactGroupName, factRadioGroup.checkedButton.radioFact.name, labelTextField.text, fontSizeCombo.currentIndex) + } else { + _valuePickerInstrumentValue.clearFact() + } + + hideDialog() + } + + Connections { + target: factRadioGroup + onCheckedButtonChanged: labelTextField.text = factRadioGroup.checkedButton.radioFact.shortDescription + } + + ButtonGroup { id: fontRadioGroup } QGCFlickable { anchors.fill: parent @@ -138,39 +227,50 @@ Column { flickableDirection: Flickable.VerticalFlick clip: true - Column { + ColumnLayout { id: column anchors.left: parent.left anchors.right: parent.right spacing: _margins - /* - Leaving this here for now just in case - FactCheckBox { - text: qsTr("Show large compass") - fact: _showLargeCompass - visible: _showLargeCompass.visible + RowLayout { + Layout.fillWidth: true + spacing: ScreenTools.defaultFontPixelWidth + + QGCLabel { text: qsTr("Label") } + QGCTextField { + id: labelTextField + Layout.fillWidth: true + text: _valuePickerInstrumentValue.label + } + } - property Fact _showLargeCompass: QGroundControl.settingsManager.appSettings.showLargeCompass + RowLayout { + spacing: ScreenTools.defaultFontPixelWidth + + QGCLabel { text: qsTr("Font Size (for whole row)") } + QGCComboBox { + id: fontSizeCombo + model: [ qsTr("Default"), qsTr("Small"), qsTr("Medium"), qsTr("Large") ] + currentIndex: _valuePickerInstrumentValue.fontSize + sizeToContents: true + onActivated: _valuePickerInstrumentValue.fontSize = index + } } - */ - Item { - width: 1 - height: _margins + QGCCheckBox { + text: qsTr("Show Units") + checked: _valuePickerInstrumentValue.showUnits + onClicked: _valuePickerInstrumentValue.showUnits = checked } - QGCLabel { - id: _label - anchors.left: parent.left - anchors.right: parent.right - wrapMode: Text.WordWrap - text: qsTr("Select the values you want to display:") + QGCButton { + text: qsTr("Blank Entry") + onClicked: { _valuePickerInstrumentValue.clearFact(); hideDialog() } } Loader { - anchors.left: parent.left - anchors.right: parent.right + Layout.fillWidth: true sourceComponent: factGroupList property var factGroup: _activeVehicle @@ -181,14 +281,12 @@ Column { model: _activeVehicle.factGroupNames Loader { - anchors.left: parent.left - anchors.right: parent.right + Layout.fillWidth: true sourceComponent: factGroupList property var factGroup: _activeVehicle.getFactGroup(modelData) property string factGroupName: modelData } - } } } @@ -203,8 +301,6 @@ Column { // property string factGroupName Column { - spacing: _margins - SectionHeader { id: header anchors.left: parent.left @@ -214,78 +310,23 @@ Column { } Column { - spacing: _margins - visible: header.checked + visible: header.checked Repeater { model: factGroup ? factGroup.factNames : 0 - RowLayout { - spacing: _margins - visible: factGroup.getFact(modelData).shortDescription !== "" - - property string propertyName: factGroupName + "." + modelData + QGCRadioButton { + text: radioFact.shortDescription + ButtonGroup.group: factRadioGroup + checked: radioFactGroupName == _valuePickerInstrumentValue.factGroupName && radioFact == _valuePickerInstrumentValue.fact - function removeFromList(list, value) { - var newList = [] - for (var i=0; i -const char* ValuesWidgetController::_groupKey = "ValuesWidget"; -const char* ValuesWidgetController::_largeValuesKey = "large"; -const char* ValuesWidgetController::_smallValuesKey = "small"; +const char* ValuesWidgetController::_groupKey = "ValuesWidget2"; +const char* ValuesWidgetController::_rowsKey = "rows"; +const char* ValuesWidgetController::_columnsKey = "columns"; -ValuesWidgetController::ValuesWidgetController(void) +const char* ValuesWidgetController::_deprecatedGroupKey = "ValuesWidget"; +const char* ValuesWidgetController::_deprecatedLargeValuesKey = "large"; +const char* ValuesWidgetController::_deprecatedSmallValuesKey = "small"; + +const char* InstrumentValue::_factGroupNameKey = "groupName"; +const char* InstrumentValue::_factNameKey = "factName"; +const char* InstrumentValue::_labelKey = "label"; +const char* InstrumentValue::_fontSizeKey = "fontSize"; +const char* InstrumentValue::_showUnitsKey = "showUnits"; + +ValuesWidgetController::ValuesWidgetController(bool forDefaultSettingsCreation) + : _valuesModel(new QmlObjectListModel(this)) { QSettings settings; settings.beginGroup(_groupKey); - QStringList largeDefaults, smallDefaults; - qgcApp()->toolbox()->corePlugin()->valuesWidgetDefaultSettings(largeDefaults, smallDefaults); + _multiVehicleMgr = qgcApp()->toolbox()->multiVehicleManager(); + connect(_multiVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &ValuesWidgetController::_activeVehicleChanged); + + if (!forDefaultSettingsCreation) { + _loadSettings(); + } +} + +InstrumentValue* ValuesWidgetController::_createNewInstrumentValueWorker(Vehicle* activeVehicle, int fontSize, QmlObjectListModel* rowModel) +{ + InstrumentValue* newValue = new InstrumentValue(activeVehicle, fontSize, rowModel); + + connect(newValue, &InstrumentValue::factChanged, this, &ValuesWidgetController::_saveSettings); + connect(newValue, &InstrumentValue::factGroupNameChanged, this, &ValuesWidgetController::_saveSettings); + connect(newValue, &InstrumentValue::labelChanged, this, &ValuesWidgetController::_saveSettings); + connect(newValue, &InstrumentValue::fontSizeChanged, this, &ValuesWidgetController::_saveSettings); + connect(newValue, &InstrumentValue::showUnitsChanged, this, &ValuesWidgetController::_saveSettings); + + return newValue; +} - _largeValues = settings.value(_largeValuesKey, largeDefaults).toStringList(); - _smallValues = settings.value(_smallValuesKey, smallDefaults).toStringList(); - _altitudeProperties << "altitudeRelative" << "altitudeAMSL"; +InstrumentValue* ValuesWidgetController::appendColumn(int rowIndex) +{ + InstrumentValue* newValue = nullptr; - // Keep back compat for removed WGS84 value - if (_largeValues.contains ("Vehicle.altitudeWGS84")) { - setLargeValues(_largeValues.replaceInStrings("Vehicle.altitudeWGS84", "Vehicle.altitudeRelative")); + if (rowIndex >= 0 && rowIndex < _valuesModel->count()) { + QmlObjectListModel* row = _valuesModel->value(rowIndex); + int fontSize = InstrumentValue::DefaultFontSize; + if (row->count()) { + fontSize = row->value(0)->fontSize(); + } + newValue = _createNewInstrumentValueWorker(_currentActiveVehicle(), fontSize, row); + row->append(newValue); } - if (_smallValues.contains ("Vehicle.altitudeWGS84")) { - setSmallValues(_largeValues.replaceInStrings("Vehicle.altitudeWGS84", "Vehicle.altitudeRelative")); + _saveSettings(); + + return newValue; +} + +void ValuesWidgetController::deleteLastColumn(int rowIndex) +{ + if (rowIndex >= 0 && rowIndex < _valuesModel->count()) { + QmlObjectListModel* row = _valuesModel->value(rowIndex); + + if (rowIndex != 0 || row->count() > 1) { + row->removeAt(row->count() - 1); + } + if (row->count() == 0) { + // Last column was deleted, delete the row as well + _valuesModel->removeAt(rowIndex); + } } + _saveSettings(); } -void ValuesWidgetController::setLargeValues(const QStringList& values) +QmlObjectListModel* ValuesWidgetController::appendRow(bool addBlanksColumn) { + QmlObjectListModel* newRow = new QmlObjectListModel(_valuesModel); + _valuesModel->append(newRow); + int rowIndex = _valuesModel->count() - 1; + if (addBlanksColumn) { + appendColumn(rowIndex); + } + _saveSettings(); + return newRow; +} + +QmlObjectListModel* ValuesWidgetController::insertRow(int atIndex, bool addBlanksColumn) +{ + QmlObjectListModel* newRow = nullptr; + + if (atIndex >= 0 && atIndex < _valuesModel->count() + 1) { + QmlObjectListModel* newRow = new QmlObjectListModel(_valuesModel); + _valuesModel->insert(atIndex, newRow); + if (addBlanksColumn) { + appendColumn(atIndex); + } + _saveSettings(); + } + return newRow; +} + +void ValuesWidgetController::deleteRow(int rowIndex) +{ + if (rowIndex >= 0 && rowIndex < _valuesModel->count()) { + if (_valuesModel->count() > 1) { + _valuesModel->removeAt(rowIndex); + } + _saveSettings(); + } +} + +Vehicle* ValuesWidgetController::_currentActiveVehicle(void) +{ + Vehicle* activeVehicle = _multiVehicleMgr->activeVehicle(); + if (!activeVehicle) { + activeVehicle = _multiVehicleMgr->offlineEditingVehicle(); + } + return activeVehicle; +} + +void ValuesWidgetController::_activeVehicleChanged(Vehicle* activeVehicle) +{ + if (!activeVehicle) { + activeVehicle = _currentActiveVehicle(); + } + + for (int rowIndex=0; rowIndex<_valuesModel->count(); rowIndex++) { + QmlObjectListModel* rowModel = _valuesModel->value(rowIndex); + for (int colIndex=0; colIndexcount(); colIndex++) { + rowModel->value(colIndex)->activeVehicleChanged(activeVehicle); + } + } +} + +bool ValuesWidgetController::_validRowIndex(int rowIndex) +{ + return rowIndex >= 0 && rowIndex < _valuesModel->count(); +} + + +int ValuesWidgetController::fontSizeForRow(int rowIndex) +{ + return _validRowIndex(rowIndex) ? _rgFontSizeByRow[rowIndex].toInt() : _rgFontSizeByRow[0].toInt(); +} + +void ValuesWidgetController::setFontSizeForRow(int rowIndex, int fontSize) +{ + if (_validRowIndex(rowIndex)) { + _rgFontSizeByRow[rowIndex] = fontSize; + } +} + +void ValuesWidgetController::_saveSettings(void) +{ + if (_preventSaveSettings) { + return; + } + QSettings settings; settings.beginGroup(_groupKey); - settings.setValue(_largeValuesKey, values); + settings.remove(QStringLiteral("")); + settings.beginWriteArray(_rowsKey); + + for (int rowIndex=0; rowIndex<_valuesModel->count(); rowIndex++) { + QmlObjectListModel* colValuesModel = _valuesModel->value(rowIndex); + + settings.setArrayIndex(rowIndex); + settings.beginWriteArray(_columnsKey); + + for (int colIndex=0; colIndexcount(); colIndex++) { + settings.setArrayIndex(colIndex); + colValuesModel->value(colIndex)->saveToSettings(settings); + } + + settings.endArray(); + } + + settings.endArray(); +} + + +void ValuesWidgetController::_loadSettings(void) +{ + QSettings settings; + + _valuesModel->deleteLater(); + _valuesModel = new QmlObjectListModel(this); + emit valuesModelChanged(_valuesModel); + + if (settings.childGroups().contains(_deprecatedGroupKey)) { + settings.beginGroup(_deprecatedGroupKey); + + QStringList largeValues = settings.value(_deprecatedLargeValuesKey).toStringList(); + QStringList smallValues = settings.value(_deprecatedSmallValuesKey).toStringList(); + QStringList altitudeProperties = { "altitudeRelative" , "altitudeAMSL" }; + + int rowIndex = -1; + int valueCount = 0; + QmlObjectListModel* rowModel = nullptr; + for (const QString& largeValue: largeValues) { + QStringList parts = largeValue.split("."); + + rowModel = appendRow(false /* addBlankColumn */); + rowIndex++; + + InstrumentValue* colValue = appendColumn(rowIndex); + colValue->setFact(parts[0], parts[1], QString()); + colValue->setLabel(colValue->fact()->shortDescription()); + colValue->setShowUnits(true); + colValue->setFontSize(altitudeProperties.contains(parts[1]) ? InstrumentValue::LargeFontSize : InstrumentValue::DefaultFontSize); + } - _largeValues = values; - emit largeValuesChanged(values); + valueCount = 0; + rowModel = nullptr; + for (const QString& smallValue: smallValues) { + QStringList parts = smallValue.split("."); + + if (!(valueCount++ & 1)) { + rowModel = appendRow(false /* addBlankColumn */); + rowIndex++; + } + + InstrumentValue* colValue = appendColumn(rowIndex); + colValue->setFact(parts[0], parts[1], QString()); + colValue->setLabel(colValue->fact()->shortDescription()); + colValue->setShowUnits(true); + colValue->setFontSize(InstrumentValue::SmallFontSize); + } + + settings.endGroup(); + settings.remove(_deprecatedGroupKey); + } else { + _preventSaveSettings = true; + + settings.beginGroup(_groupKey); + int cRows = settings.beginReadArray(_rowsKey); + + for (int rowIndex=0; rowIndexreadFromSettings(settings); + } + + settings.endArray(); + } + + settings.endArray(); + + _preventSaveSettings = false; + } + + // Use defaults if nothing there + if (_valuesModel->count() == 0) { + _valuesModel->deleteLater(); + _valuesModel = qgcApp()->toolbox()->corePlugin()->valuesWidgetDefaultSettings(this); + emit valuesModelChanged(_valuesModel); + } } -void ValuesWidgetController::setSmallValues(const QStringList& values) +void ValuesWidgetController::resetToDefaults(void) { QSettings settings; settings.beginGroup(_groupKey); - settings.setValue(_smallValuesKey, values); + settings.remove(""); + + _loadSettings(); +} + +void ValuesWidgetController::setPreventSaveSettings(bool preventSaveSettings) +{ + _preventSaveSettings = preventSaveSettings; +} + +InstrumentValue::InstrumentValue(Vehicle* activeVehicle, int fontSize, QmlObjectListModel* rowModel) + : QObject (rowModel) + , _activeVehicle(activeVehicle) + , _rowModel (rowModel) + , _fontSize (fontSize) +{ + +} + +void InstrumentValue::activeVehicleChanged(Vehicle* activeVehicle) +{ + _activeVehicle = activeVehicle; + + if (_fact) { + _fact = nullptr; + + FactGroup* factGroup = nullptr; + QString factName; + if (_factGroupName == QStringLiteral("Vehicle")) { + factGroup = _activeVehicle; + } else { + factGroup = _activeVehicle->getFactGroup(_factGroupName); + } + + if (factGroup) { + _fact = factGroup->getFact(factName); + } + emit factChanged(_fact); + } +} + +void InstrumentValue::setFact(QString factGroupName, QString factName, QString label) +{ + if (_fact) { + _fact = nullptr; + } + + FactGroup* factGroup = nullptr; + if (factGroupName == QStringLiteral("Vehicle")) { + factGroup = _activeVehicle; + } else { + factGroup = _activeVehicle->getFactGroup(factGroupName); + } + + if (factGroup) { + _fact = factGroup->getFact(factName); + } + + if (_fact) { + _factGroupName = factGroupName; + _label = label; + } else { + _factGroupName.clear(); + _label.clear(); + } + + emit labelChanged(_label); + emit factChanged(_fact); + emit factGroupNameChanged(_factGroupName); +} + +void InstrumentValue::_setFontSize(int fontSize) +{ + if (fontSize != _fontSize) { + _fontSize = fontSize; + emit fontSizeChanged(fontSize); + } +} + +void InstrumentValue::setFontSize(int fontSize) +{ + _setFontSize(fontSize); + + // All other items in row must change to match + for (int i=0; i<_rowModel->count(); i++) { + InstrumentValue* instrumentValue = _rowModel->value(i); + if (instrumentValue != this) { + instrumentValue->_setFontSize(fontSize); + } + } +} + +void InstrumentValue::saveToSettings(QSettings& settings) const +{ + if (_fact) { + settings.setValue(_factGroupNameKey, _factGroupName); + settings.setValue(_factNameKey, _fact->name()); + } else { + settings.setValue(_factGroupNameKey, ""); + settings.setValue(_factNameKey, ""); + } + settings.setValue(_labelKey, _label); + settings.setValue(_fontSizeKey, _fontSize); + settings.setValue(_showUnitsKey, _showUnits); +} + +void InstrumentValue::readFromSettings(const QSettings& settings) +{ + _factGroupName = settings.value(_factGroupNameKey).toString(); + _label = settings.value(_labelKey).toString(); + _fontSize = settings.value(_fontSizeKey).toInt(); + _showUnits = settings.value(_showUnitsKey).toBool(); + + QString factName = settings.value(_factNameKey).toString(); + if (!factName.isEmpty()) { + setFact(_factGroupName, factName, _label); + } + + emit factChanged (_fact); + emit factGroupNameChanged (_factGroupName); + emit labelChanged (_label); + emit fontSizeChanged (_fontSize); + emit showUnitsChanged (_showUnits); +} + +void InstrumentValue::setLabel(const QString& label) +{ + if (label != _label) { + _label = label; + emit labelChanged(label); + } +} + +void InstrumentValue::setShowUnits(bool showUnits) +{ + if (showUnits != _showUnits) { + _showUnits = showUnits; + emit showUnitsChanged(showUnits); + } +} + +void InstrumentValue::clearFact(void) +{ + _fact = nullptr; + _factGroupName.clear(); + _label.clear(); + _showUnits = true; - _smallValues = values; - emit smallValuesChanged(values); + emit factChanged (_fact); + emit factGroupNameChanged (_factGroupName); + emit labelChanged (_label); + emit showUnitsChanged (_showUnits); } diff --git a/src/FlightMap/Widgets/ValuesWidgetController.h b/src/FlightMap/Widgets/ValuesWidgetController.h index d66b9bd7c..92398ae56 100644 --- a/src/FlightMap/Widgets/ValuesWidgetController.h +++ b/src/FlightMap/Widgets/ValuesWidgetController.h @@ -7,42 +7,124 @@ * ****************************************************************************/ +#pragma once -#ifndef ValuesWidgetController_H -#define ValuesWidgetController_H +#include "FactSystem.h" +#include "QmlObjectListModel.h" +#include "QGCApplication.h" #include +class ValuesWidgetController; + +class InstrumentValue : public QObject +{ + Q_OBJECT + +public: + enum FontSize { + DefaultFontSize=0, + SmallFontSize, + MediumFontSize, + LargeFontSize + }; + + InstrumentValue(Vehicle* activeVehicle, int fontSize, QmlObjectListModel* rowModel); + + Q_PROPERTY(QString factGroupName MEMBER _factGroupName NOTIFY factGroupNameChanged) + Q_PROPERTY(Fact* fact READ fact NOTIFY factChanged) + Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) + Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged) + Q_PROPERTY(bool showUnits READ showUnits WRITE setShowUnits NOTIFY showUnitsChanged) + + Q_INVOKABLE void setFact(QString factGroupName, QString factName, QString label); + Q_INVOKABLE void clearFact(void); + + Fact* fact (void) { return _fact; } + int fontSize (void) const { return _fontSize; } + QString label (void) const { return _label; } + bool showUnits (void) const { return _showUnits; } + void setFontSize (int fontSize); + void setLabel (const QString& label); + void setShowUnits (bool showUnits); + void activeVehicleChanged (Vehicle* activeVehicle); + void saveToSettings (QSettings& settings) const; + void readFromSettings (const QSettings& settings); + +signals: + void factChanged (Fact* fact); + void factGroupNameChanged (QString factGroup); + void labelChanged (QString label); + void fontSizeChanged (int fontSize); + void showUnitsChanged (bool showUnits); + +private slots: + +private: + void _setFontSize(int fontSize); + + Vehicle* _activeVehicle = nullptr; + QmlObjectListModel* _rowModel = nullptr; + Fact* _fact = nullptr; + QString _factGroupName; + QString _label; + bool _showUnits = true; + int _fontSize = DefaultFontSize; + + static const char* _factGroupNameKey; + static const char* _factNameKey; + static const char* _labelKey; + static const char* _fontSizeKey; + static const char* _showUnitsKey; +}; + class ValuesWidgetController : public QObject { Q_OBJECT public: - ValuesWidgetController(void); + ValuesWidgetController(bool forDefaultSettingsCreation = false); - Q_PROPERTY(QStringList largeValues READ largeValues WRITE setLargeValues NOTIFY largeValuesChanged) - Q_PROPERTY(QStringList smallValues READ smallValues WRITE setSmallValues NOTIFY smallValuesChanged) + Q_PROPERTY(QmlObjectListModel* valuesModel READ valuesModel NOTIFY valuesModelChanged) - Q_PROPERTY(QStringList altitudeProperties READ altitudeProperties CONSTANT) + Q_INVOKABLE InstrumentValue* appendColumn (int rowIndex); + Q_INVOKABLE void deleteLastColumn (int rowIndex); + Q_INVOKABLE QmlObjectListModel* appendRow (bool addBlanksColumn = true); + Q_INVOKABLE QmlObjectListModel* insertRow (int atIndex, bool addBlanksColumn = true); + Q_INVOKABLE void deleteRow (int rowIndex); + Q_INVOKABLE int fontSizeForRow (int rowIndex); + Q_INVOKABLE void setFontSizeForRow (int rowIndex, int fontSize); + Q_INVOKABLE void resetToDefaults (void); - QStringList largeValues(void) const { return _largeValues; } - QStringList smallValues(void) const { return _smallValues; } - void setLargeValues(const QStringList& values); - void setSmallValues(const QStringList& values); - QStringList altitudeProperties(void) const { return _altitudeProperties; } + QmlObjectListModel* valuesModel(void) { return _valuesModel; } + + /// Turn on/off saving changes to QSettings + void setPreventSaveSettings(bool preventSaveSettings); signals: - void largeValuesChanged(QStringList values); - void smallValuesChanged(QStringList values); + void valuesModelChanged(QmlObjectListModel* valuesModel); + +private slots: + void _activeVehicleChanged(Vehicle* activeVehicle); + Vehicle* _currentActiveVehicle(void); + void _saveSettings (void); private: - QStringList _largeValues; - QStringList _smallValues; - QStringList _altitudeProperties; + bool _validRowIndex (int rowIndex); + InstrumentValue* _createNewInstrumentValueWorker (Vehicle* activeVehicle, int fontSize, QmlObjectListModel* rowModel); + void _loadSettings (void); + + MultiVehicleManager* _multiVehicleMgr = nullptr; + QmlObjectListModel* _valuesModel = nullptr; + QVariantList _rgFontSizeByRow; + bool _preventSaveSettings = false; static const char* _groupKey; - static const char* _largeValuesKey; - static const char* _smallValuesKey; -}; + static const char* _rowsKey; + static const char* _columnsKey; + + static const char* _deprecatedGroupKey; + static const char* _deprecatedLargeValuesKey; + static const char* _deprecatedSmallValuesKey; -#endif +}; diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 4d0614164..89d7522e3 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -100,6 +100,7 @@ #include "LogReplayLink.h" #include "VehicleObjectAvoidance.h" #include "TrajectoryPoints.h" +#include "ValuesWidgetController.h" #if defined(QGC_ENABLE_PAIRING) #include "PairingManager.h" @@ -523,6 +524,7 @@ void QGCApplication::_initCommon() qmlRegisterUncreatableType ("QGroundControl.FlightMap", 1, 0, "QGCMapPolygon", kRefOnly); qmlRegisterUncreatableType ("QGroundControl.FlightMap", 1, 0, "QGCGeoBoundingCube", kRefOnly); qmlRegisterUncreatableType ("QGroundControl.FlightMap", 1, 0, "TrajectoryPoints", kRefOnly); + qmlRegisterUncreatableType (kQGCControllers, 1, 0, "InstrumentValue", kRefOnly); qmlRegisterType ("QGroundControl.FlightMap", 1, 0, "QGCMapCircle"); diff --git a/src/QmlControls/PageView.qml b/src/QmlControls/PageView.qml index bece28681..3cff0b39c 100644 --- a/src/QmlControls/PageView.qml +++ b/src/QmlControls/PageView.qml @@ -17,6 +17,7 @@ Rectangle { property real _margins: ScreenTools.defaultFontPixelWidth / 2 property real _pageWidth: _root.width property var _instrumentPages: QGroundControl.corePlugin.instrumentPages + property bool _settingsUnlocked: false QGCPalette { id:qgcPal; colorGroupEnabled: parent.enabled } @@ -29,11 +30,13 @@ Rectangle { centeredLabel: true font.pointSize: ScreenTools.smallFontPointSize + onCurrentIndexChanged: _settingsUnlocked = false + QGCColoredImage { anchors.leftMargin: _margins anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter - source: "/res/gear-black.svg" + source: pageWidgetLoader.item.showLockIcon ? (_settingsUnlocked ? "/res/LockOpen.svg" : "/res/LockClosed.svg") : "/res/gear-black.svg" mipmap: true height: parent.height * 0.7 width: height @@ -44,7 +47,14 @@ Rectangle { QGCMouseArea { fillItem: parent - onClicked: pageWidgetLoader.item.showSettings() + onClicked: { + if (pageWidgetLoader.item.showLockIcon) { + _settingsUnlocked = !_settingsUnlocked + pageWidgetLoader.item.showSettings(_settingsUnlocked) + } else { + pageWidgetLoader.item.showSettings() + } + } } } } diff --git a/src/QmlControls/QGCLabel.qml b/src/QmlControls/QGCLabel.qml index ef0bdede8..26afb1b8e 100644 --- a/src/QmlControls/QGCLabel.qml +++ b/src/QmlControls/QGCLabel.qml @@ -1,9 +1,8 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Controls.Styles 1.4 +import QtQuick 2.12 +import QtQuick.Controls 2.12 -import QGroundControl.Palette 1.0 -import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 Text { font.pointSize: ScreenTools.defaultFontPointSize diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index 48be2c132..5c841cf3b 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -22,6 +22,7 @@ #endif #include "QGCLoggingCategory.h" #include "QGCCameraManager.h" +#include "ValuesWidgetController.h" #include #include @@ -406,10 +407,40 @@ QString QGCCorePlugin::showAdvancedUIMessage() const "Are you sure you want to enable Advanced Mode?"); } -void QGCCorePlugin::valuesWidgetDefaultSettings(QStringList& largeValues, QStringList& smallValues) +QmlObjectListModel* QGCCorePlugin::valuesWidgetDefaultSettings(QObject* valuesModelParent) { - Q_UNUSED(smallValues); - largeValues << "Vehicle.altitudeRelative" << "Vehicle.groundSpeed" << "Vehicle.flightTime"; + ValuesWidgetController controller(true /* forDefaultSettingsCreation */); + + // We don't want these to get written out to settings. This way if the user doesn't modify them + // they will get new changes to default settings from newer builds automatically on next run. + controller.setPreventSaveSettings(true); + + QmlObjectListModel* columnModel = controller.appendRow(); + InstrumentValue* colValue = columnModel->value(0); + colValue->setFact("Vehicle", "altitudeRelative", QString()); + colValue->setLabel(colValue->fact()->shortDescription()); + colValue->setShowUnits(true); + colValue->setFontSize(InstrumentValue::LargeFontSize); + + columnModel = controller.appendRow(); + colValue = columnModel->value(0); + colValue->setFact("Vehicle", "groundSpeed", QString()); + colValue->setLabel(colValue->fact()->shortDescription()); + colValue->setShowUnits(true); + colValue->setFontSize(InstrumentValue::DefaultFontSize); + + columnModel = controller.appendRow(); + colValue = columnModel->value(0); + colValue->setFact("Vehicle", "flightTime", QString()); + colValue->setLabel(colValue->fact()->shortDescription()); + colValue->setShowUnits(false); + colValue->setFontSize(InstrumentValue::DefaultFontSize); + + controller.setPreventSaveSettings(false); + + controller.valuesModel()->setParent(valuesModelParent); + + return controller.valuesModel(); } QQmlApplicationEngine* QGCCorePlugin::createRootWindow(QObject *parent) diff --git a/src/api/QGCCorePlugin.h b/src/api/QGCCorePlugin.h index 865bbf68b..e8aa5eb70 100644 --- a/src/api/QGCCorePlugin.h +++ b/src/api/QGCCorePlugin.h @@ -105,8 +105,9 @@ public: /// Allows a plugin to override the specified color name from the palette virtual void paletteOverride(QString colorName, QGCPalette::PaletteColorInfo_t& colorInfo); - /// Allows the plugin to override the default settings for the Values Widget large and small values - virtual void valuesWidgetDefaultSettings(QStringList& largeValues, QStringList& smallValues); + /// Return the default Intrument Value model for the Values Widget. The returned model will be + /// re-parented to valuesModelParent for ownership. + virtual QmlObjectListModel* valuesWidgetDefaultSettings(QObject* valuesModelParent); /// Allows the plugin to override the creation of the root (native) window. virtual QQmlApplicationEngine* createRootWindow(QObject* parent); -- 2.22.0