diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index db70301c428bd57552ad0c7607a7d49f5a16f4ff..bb3b44142a4777659984351327edaccf5ecbd571 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -1057,7 +1057,7 @@ HEADERS += \ src/FactSystem/FactGroup.h \ src/FactSystem/FactMetaData.h \ src/FactSystem/FactSystem.h \ - src/FactSystem/FactValidator.h \ + src/FactSystem/FactValueSliderListModel.h \ src/FactSystem/ParameterManager.h \ src/FactSystem/SettingsFact.h \ @@ -1067,7 +1067,7 @@ SOURCES += \ src/FactSystem/FactGroup.cc \ src/FactSystem/FactMetaData.cc \ src/FactSystem/FactSystem.cc \ - src/FactSystem/FactValidator.cc \ + src/FactSystem/FactValueSliderListModel.cc \ src/FactSystem/ParameterManager.cc \ src/FactSystem/SettingsFact.cc \ diff --git a/src/Camera/QGCCameraControl.cc b/src/Camera/QGCCameraControl.cc index d2cfe911cff25310d33e873a5c2c5f8a57eec9a7..ff11f7f6328f1377b94350fb078a91b29ebf216d 100644 --- a/src/Camera/QGCCameraControl.cc +++ b/src/Camera/QGCCameraControl.cc @@ -790,7 +790,7 @@ QGCCameraControl::_loadSettings(const QDomNodeList nodeList) QVariant typedValue; QString errorString; if (metaData->convertAndValidateRaw(attr, true /* convertOnly */, typedValue, errorString)) { - metaData->setIncrement(typedValue.toDouble()); + metaData->setRawIncrement(typedValue.toDouble()); } else { qWarning() << "Invalid step value for" << factName << " type:" << metaData->type() diff --git a/src/FactSystem/Fact.cc b/src/FactSystem/Fact.cc index 309b3fd0fbeffddbcb13d63ab5d15d85fd3e19a8..ec003ad3a3df90b2fc81c2f13d375a8242fae810 100644 --- a/src/FactSystem/Fact.cc +++ b/src/FactSystem/Fact.cc @@ -8,6 +8,7 @@ ****************************************************************************/ #include "Fact.h" +#include "FactValueSliderListModel.h" #include "QGCMAVLink.h" #include "QGCApplication.h" #include "QGCCorePlugin.h" @@ -18,13 +19,14 @@ static const char* kMissingMetadata = "Meta data pointer missing"; Fact::Fact(QObject* parent) - : QObject(parent) - , _componentId(-1) - , _rawValue(0) - , _type(FactMetaData::valueTypeInt32) - , _metaData(NULL) - , _sendValueChangedSignals(true) + : QObject (parent) + , _componentId (-1) + , _rawValue (0) + , _type (FactMetaData::valueTypeInt32) + , _metaData (NULL) + , _sendValueChangedSignals (true) , _deferredValueChangeSignal(false) + , _valueSliderModel (NULL) { FactMetaData* metaData = new FactMetaData(_type, this); setMetaData(metaData); @@ -34,14 +36,15 @@ Fact::Fact(QObject* parent) } Fact::Fact(int componentId, QString name, FactMetaData::ValueType_t type, QObject* parent) - : QObject(parent) - , _name(name) - , _componentId(componentId) - , _rawValue(0) - , _type(type) - , _metaData(NULL) - , _sendValueChangedSignals(true) + : QObject (parent) + , _name (name) + , _componentId (componentId) + , _rawValue (0) + , _type (type) + , _metaData (NULL) + , _sendValueChangedSignals (true) , _deferredValueChangeSignal(false) + , _valueSliderModel (NULL) { FactMetaData* metaData = new FactMetaData(_type, this); setMetaData(metaData); @@ -57,6 +60,7 @@ Fact::Fact(FactMetaData* metaData, QObject* parent) , _metaData (NULL) , _sendValueChangedSignals (true) , _deferredValueChangeSignal(false) + , _valueSliderModel (NULL) { // Allow core plugin a chance to override the default value qgcApp()->toolbox()->corePlugin()->adjustSettingMetaData(*metaData); @@ -78,7 +82,7 @@ const Fact& Fact::operator=(const Fact& other) _type = other._type; _sendValueChangedSignals = other._sendValueChangedSignals; _deferredValueChangeSignal = other._deferredValueChangeSignal; - + _valueSliderModel = NULL; if (_metaData && other._metaData) { *_metaData = *other._metaData; } else { @@ -633,10 +637,10 @@ QString Fact::enumOrValueString(void) return QString(); } -double Fact::increment(void) const +double Fact::rawIncrement(void) const { if (_metaData) { - return _metaData->increment(); + return _metaData->rawIncrement(); } else { qWarning() << kMissingMetadata << name(); } @@ -692,3 +696,12 @@ bool Fact::volatileValue(void) const return false; } } + +FactValueSliderListModel* Fact::valueSliderModel(void) +{ + if (!_valueSliderModel) { + _valueSliderModel = new FactValueSliderListModel(*this); + QQmlEngine::setObjectOwnership(_valueSliderModel, QQmlEngine::JavaScriptOwnership); + } + return _valueSliderModel; +} diff --git a/src/FactSystem/Fact.h b/src/FactSystem/Fact.h index 6257bb399143c22a667cc6088a4386b5fb0b403b..74fe6b518a7c2b1fdba0d1146e19231c11828c29 100644 --- a/src/FactSystem/Fact.h +++ b/src/FactSystem/Fact.h @@ -20,6 +20,9 @@ #include #include #include +#include + +class FactValueSliderListModel; /// @brief A Fact is used to hold a single value within the system. class Fact : public QObject @@ -115,7 +118,7 @@ public: bool valueEqualsDefault (void) const; bool rebootRequired (void) const; QString enumOrValueString (void); // This is not const, since an unknown value can modify the enum lists - double increment (void) const; + double rawIncrement (void) const; double cookedIncrement (void) const; bool typeIsString (void) const { return type() == FactMetaData::valueTypeString; } bool typeIsBool (void) const { return type() == FactMetaData::valueTypeBool; } @@ -124,6 +127,8 @@ public: bool writeOnly (void) const; bool volatileValue (void) const; + Q_INVOKABLE FactValueSliderListModel* valueSliderModel(void); + /// Returns the values as a string with full 18 digit precision if float/double. QString rawValueStringFullPrecision(void) const; @@ -193,6 +198,7 @@ protected: FactMetaData* _metaData; bool _sendValueChangedSignals; bool _deferredValueChangeSignal; + FactValueSliderListModel* _valueSliderModel; }; #endif diff --git a/src/FactSystem/FactControls/FactTextField.qml b/src/FactSystem/FactControls/FactTextField.qml index f17ea565cb063c8c2c8c3766ec7a697355c1e7d0..ab66d9f2d5deb57da887ebaca77fa1f1078558a8 100644 --- a/src/FactSystem/FactControls/FactTextField.qml +++ b/src/FactSystem/FactControls/FactTextField.qml @@ -20,16 +20,11 @@ QGCTextField { property string _validateString - // At this point all Facts are numeric inputMethodHints: ((fact && fact.typeIsString) || ScreenTools.isiOS) ? Qt.ImhNone : // iOS numeric keyboard has no done button, we can't use it Qt.ImhFormattedNumbersOnly // Forces use of virtual numeric keyboard onEditingFinished: { - if (ScreenTools.isMobile) { - // Toss focus on mobile after Done on virtual keyboard. Prevent strange interactions. - focus = false - } if (typeof qgcView !== 'undefined' && qgcView) { var errorString = fact.validate(text, false /* convertOnly */) if (errorString === "") { diff --git a/src/FactSystem/FactControls/FactValueSlider.qml b/src/FactSystem/FactControls/FactValueSlider.qml index f94db519768d33f21d0c6d9714f159b85d6d6373..405303deb60e72d3fafd40e208e9a104ed2f4515 100644 --- a/src/FactSystem/FactControls/FactValueSlider.qml +++ b/src/FactSystem/FactControls/FactValueSlider.qml @@ -1,5 +1,6 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 +import QtQuick.Dialogs 1.2 import QGroundControl.Palette 1.0 import QGroundControl.ScreenTools 1.0 @@ -11,9 +12,9 @@ Rectangle { width: _totalSlots * _itemWidth color: qgcPal.textField - property Fact fact: undefined - property int digitCount: 4 ///< The number of digits to show for each value - property int incrementSlots: 1 ///< The number of visible slots to left/right of center value + property Fact fact: undefined + property int digitCount: 4 ///< The number of digits to show for each value + property int incrementSlots: 1 ///< The number of visible slots to left/right of center value property int _totalDigitCount: digitCount + 1 + fact.units.length property real _margins: (ScreenTools.implicitTextFieldHeight - ScreenTools.defaultFontPixelHeight) / 2 @@ -32,6 +33,8 @@ Rectangle { property int _prevIncrementSlots: incrementSlots property int _nextIncrementSlots: incrementSlots property int _selectionWidth: 3 + property var _model: fact.valueSliderModel() + property var _fact: fact QGCPalette { id: qgcPal; colorGroupEnabled: parent.enabled } QGCPalette { id: qgcPalDisabled; colorGroupEnabled: false } @@ -46,61 +49,63 @@ Rectangle { _nextIncrementSlots = _totalSlots - _currentRelativeIndex - 1 } - Component.onCompleted: { - var currentValue = _value - _valueModel = [ _value.toFixed(_decimalPlaces) ] - - var addCount = 0 - var minValue = fact.min - currentValue -= _increment - while (currentValue >= minValue) { - _valueModel.unshift(currentValue.toFixed(_decimalPlaces)) - currentValue -= _increment - addCount++ - } - - var maxValue = fact.max - currentValue = _value + _increment - while (currentValue <= maxValue) { - _valueModel.push(currentValue.toFixed(_decimalPlaces)) - currentValue += _increment - } - - _currentIndex = addCount - valueListView.model = _valueModel + function reset() { + valueListView.positionViewAtIndex(0, ListView.Beginning) + _currentIndex = _model.resetInitialValue() valueListView.positionViewAtIndex(_currentIndex, ListView.Center) recalcRelativeIndex() } + Component.onCompleted: valueListView.maximumFlickVelocity = valueListView.maximumFlickVelocity / 2 + + Component { + id: editDialogComponent + + ParameterEditorDialog { + fact: _fact + onValueChanged: reset() + } + } + QGCListView { id: valueListView anchors.fill: parent orientation: ListView.Horizontal snapMode: ListView.SnapToItem clip: true + model: _model + + Component.onCompleted: reset() delegate: QGCLabel { width: _itemWidth height: _itemHeight verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter - text: modelData + " " + _units + text: value + " " + _units color: qgcPal.textFieldText MouseArea { anchors.fill: parent onClicked: { - _currentIndex = index - valueListView.positionViewAtIndex(_currentIndex, ListView.Center) - recalcRelativeIndex() - fact.value = valueListView.model[_currentIndex] + valueListView.focus = true + if (_currentIndex === index) { + qgcView.showDialog(editDialogComponent, qsTr("Value Details"), qgcView.showDialogDefaultWidth, StandardButton.Save | StandardButton.Cancel) + } else { + _currentIndex = index + valueListView.positionViewAtIndex(_currentIndex, ListView.Center) + recalcRelativeIndex() + fact.value = value + } } } } + onMovementStarted: valueListView.focus = true + onMovementEnded: { _currentIndex = firstVisibleIndex() + _currentRelativeIndex - fact.value = model[_currentIndex] + fact.value = _model.valueAtModelIndex(_currentIndex) } } diff --git a/src/FactSystem/FactMetaData.cc b/src/FactSystem/FactMetaData.cc index b726c6038dbcad382a69eb9854423194984fdf9a..c254cd2a1d6c268c69dd8cfb3bbdbf30b57a1e23 100644 --- a/src/FactSystem/FactMetaData.cc +++ b/src/FactSystem/FactMetaData.cc @@ -78,6 +78,7 @@ const char* FactMetaData::_defaultValueJsonKey = "defaultValue"; const char* FactMetaData::_mobileDefaultValueJsonKey = "mobileDefaultValue"; const char* FactMetaData::_minJsonKey = "min"; const char* FactMetaData::_maxJsonKey = "max"; +const char* FactMetaData::_incrementJsonKey = "increment"; const char* FactMetaData::_hasControlJsonKey = "control"; FactMetaData::FactMetaData(QObject* parent) @@ -93,7 +94,7 @@ FactMetaData::FactMetaData(QObject* parent) , _rawTranslator (_defaultTranslator) , _cookedTranslator (_defaultTranslator) , _rebootRequired (false) - , _increment (std::numeric_limits::quiet_NaN()) + , _rawIncrement (std::numeric_limits::quiet_NaN()) , _hasControl (true) , _readOnly (false) , _writeOnly (false) @@ -116,7 +117,7 @@ FactMetaData::FactMetaData(ValueType_t type, QObject* parent) , _rawTranslator (_defaultTranslator) , _cookedTranslator (_defaultTranslator) , _rebootRequired (false) - , _increment (std::numeric_limits::quiet_NaN()) + , _rawIncrement (std::numeric_limits::quiet_NaN()) , _hasControl (true) , _readOnly (false) , _writeOnly (false) @@ -146,7 +147,7 @@ FactMetaData::FactMetaData(ValueType_t type, const QString name, QObject* parent , _rawTranslator (_defaultTranslator) , _cookedTranslator (_defaultTranslator) , _rebootRequired (false) - , _increment (std::numeric_limits::quiet_NaN()) + , _rawIncrement (std::numeric_limits::quiet_NaN()) , _hasControl (true) , _readOnly (false) , _writeOnly (false) @@ -180,7 +181,7 @@ const FactMetaData& FactMetaData::operator=(const FactMetaData& other) _rawTranslator = other._rawTranslator; _cookedTranslator = other._cookedTranslator; _rebootRequired = other._rebootRequired; - _increment = other._increment; + _rawIncrement = other._rawIncrement; _hasControl = other._hasControl; _readOnly = other._readOnly; _writeOnly = other._writeOnly; @@ -975,7 +976,7 @@ QString FactMetaData::appSettingsAreaUnitsString(void) double FactMetaData::cookedIncrement(void) const { - return _rawTranslator(this->increment()).toDouble(); + return _rawTranslator(this->rawIncrement()).toDouble(); } int FactMetaData::decimalPlaces(void) const @@ -984,7 +985,7 @@ int FactMetaData::decimalPlaces(void) const int incrementDecimalPlaces = unknownDecimalPlaces; // First determine decimal places from increment - double increment = _rawTranslator(this->increment()).toDouble(); + double increment = _rawTranslator(this->rawIncrement()).toDouble(); if (!qIsNaN(increment)) { double integralPart; @@ -1029,12 +1030,18 @@ FactMetaData* FactMetaData::createFromJsonObject(const QJsonObject& json, QObjec return new FactMetaData(valueTypeUint32, metaDataParent); } - // Validate key types - QStringList keys; - QList types; - keys << _nameJsonKey << _decimalPlacesJsonKey << _typeJsonKey << _shortDescriptionJsonKey << _longDescriptionJsonKey << _unitsJsonKey << _minJsonKey << _maxJsonKey << _hasControlJsonKey; - types << QJsonValue::String << QJsonValue::Double << QJsonValue::String << QJsonValue::String << QJsonValue::String << QJsonValue::String << QJsonValue::Double << QJsonValue::Double << QJsonValue::Bool; - if (!JsonHelper::validateKeyTypes(json, keys, types, errorString)) { + QList keyInfoList = { + { _nameJsonKey, QJsonValue::String, true }, + { _typeJsonKey, QJsonValue::String, true }, + { _shortDescriptionJsonKey, QJsonValue::String, false }, + { _longDescriptionJsonKey, QJsonValue::String, false }, + { _unitsJsonKey, QJsonValue::String, false }, + { _decimalPlacesJsonKey, QJsonValue::Double, false }, + { _minJsonKey, QJsonValue::Double, false }, + { _maxJsonKey, QJsonValue::Double, false }, + { _hasControlJsonKey, QJsonValue::Bool, false }, + }; + if (!JsonHelper::validateKeys(json, keyInfoList, errorString)) { qWarning() << errorString; return new FactMetaData(valueTypeUint32, metaDataParent); } @@ -1100,6 +1107,20 @@ FactMetaData* FactMetaData::createFromJsonObject(const QJsonObject& json, QObjec } } + if (json.contains(_incrementJsonKey)) { + QVariant typedValue; + QString errorString; + QVariant initialValue = json[_incrementJsonKey].toVariant(); + if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) { + metaData->setRawIncrement(typedValue.toDouble()); + } else { + qWarning() << "Invalid increment value, name:" << metaData->name() + << " type:" << metaData->type() + << " value:" << initialValue + << " error:" << errorString; + } + } + if (json.contains(_minJsonKey)) { QVariant typedValue; QString errorString; diff --git a/src/FactSystem/FactMetaData.h b/src/FactSystem/FactMetaData.h index 8f99bb3f40b5957faad7a7fb8dfa2ed95792eef2..a82408476a95af2ca47529b08d30151ee7caaf7b 100644 --- a/src/FactSystem/FactMetaData.h +++ b/src/FactSystem/FactMetaData.h @@ -111,7 +111,7 @@ public: /// Amount to increment value when used in controls such as spin button or slider with detents. /// NaN for no increment available. - double increment (void) const { return _increment; } + double rawIncrement (void) const { return _rawIncrement; } double cookedIncrement (void) const; Translator rawTranslator (void) const { return _rawTranslator; } @@ -136,7 +136,7 @@ public: void setShortDescription(const QString& shortDescription) { _shortDescription = shortDescription; } void setRawUnits (const QString& rawUnits); void setRebootRequired (bool rebootRequired) { _rebootRequired = rebootRequired; } - void setIncrement (double increment) { _increment = increment; } + void setRawIncrement (double increment) { _rawIncrement = increment; } void setHasControl (bool bValue) { _hasControl = bValue; } void setReadOnly (bool bValue) { _readOnly = bValue; } void setWriteOnly (bool bValue) { _writeOnly = bValue; } @@ -249,7 +249,7 @@ private: Translator _rawTranslator; Translator _cookedTranslator; bool _rebootRequired; - double _increment; + double _rawIncrement; bool _hasControl; bool _readOnly; bool _writeOnly; @@ -286,6 +286,7 @@ private: static const char* _mobileDefaultValueJsonKey; static const char* _minJsonKey; static const char* _maxJsonKey; + static const char* _incrementJsonKey; static const char* _hasControlJsonKey; }; diff --git a/src/FactSystem/FactSystem.h b/src/FactSystem/FactSystem.h index e4321cddf09de7b8fe2779bf87e52a69b55e937e..29089ceab3e25f62be9531ac1b39af08d2719113 100644 --- a/src/FactSystem/FactSystem.h +++ b/src/FactSystem/FactSystem.h @@ -20,10 +20,6 @@ /// The components of the FactSystem are a Fact which holds an individual value. FactMetaData holds /// additional meta data associated with a Fact such as description, min/max ranges and so forth. -/// The FactValidator object is a QML validator which validates input according to the FactMetaData -/// settings. Client code can then use this system to expose sets of Facts to QML code. An example -/// of this is the PX4ParameterMetaData onbject which is part of the PX4 AutoPilot plugin. It exposes -/// the firmware parameters to QML such that you can bind QML ui elements directly to parameters. class FactSystem : public QGCTool { diff --git a/src/FactSystem/FactValidator.cc b/src/FactSystem/FactValidator.cc deleted file mode 100644 index c7f7e9696a99056b1cffca0ec3eb91406e0b0c63..0000000000000000000000000000000000000000 --- a/src/FactSystem/FactValidator.cc +++ /dev/null @@ -1,33 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2016 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - - -/// @file -/// @author Don Gagne - -#include "FactValidator.h" - -FactValidator::FactValidator(QObject* parent) : - QValidator(parent) -{ - -} - -void FactValidator::fixup(QString& input) const -{ - Q_UNUSED(input); -} - -FactValidator::State FactValidator::validate(QString& input, int& pos) const -{ - Q_UNUSED(input); - Q_UNUSED(pos); - - return Acceptable; -} diff --git a/src/FactSystem/FactValidator.h b/src/FactSystem/FactValidator.h deleted file mode 100644 index d81a117bc8731cfdbfe7f2bb0f3279b86b01d585..0000000000000000000000000000000000000000 --- a/src/FactSystem/FactValidator.h +++ /dev/null @@ -1,56 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2016 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - - -/// @file -/// @author Don Gagne - -#ifndef FactValidator_H -#define FactValidator_H - -#include - -class Fact; - -/// QML Validator for Facts (Work In Progress) -/// -/// The validator uses the FactMetaData to impose restrictions on the input. It is used as follows: -/// @code{.unparsed} -/// TextInput { -/// validator: FactValidator { fact: parameters["RC_MAP_THROTTLE"]; } -/// } -/// @endcode -class FactValidator : public QValidator -{ - Q_OBJECT - - Q_PROPERTY(Fact* fact READ fact WRITE setFact) - -public: - FactValidator(QObject* parent = NULL); - - // Property system methods - - /// Read accessor for fact property - Fact* fact(void) { return _fact; } - - /// Write accessor for fact property - void setFact(Fact* fact) { _fact = fact; } - - /// Override from QValidator - virtual void fixup(QString& input) const; - - /// Override from QValidator - virtual State validate(QString& input, int& pos) const; - -private: - Fact* _fact; ///< Fact that the validator is working on -}; - -#endif \ No newline at end of file diff --git a/src/FactSystem/FactValueSliderListModel.cc b/src/FactSystem/FactValueSliderListModel.cc new file mode 100644 index 0000000000000000000000000000000000000000..59adb5fd257de0cdf8222602eef36005d65479cc --- /dev/null +++ b/src/FactSystem/FactValueSliderListModel.cc @@ -0,0 +1,125 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "FactValueSliderListModel.h" + +#include +#include +#include + +#include + +const int FactValueSliderListModel::_valueRole = Qt::UserRole; +const int FactValueSliderListModel::_valueIndexRole = Qt::UserRole + 1; + +FactValueSliderListModel::FactValueSliderListModel(Fact& fact, QObject* parent) + : QAbstractListModel (parent) + , _fact (fact) + , _cValues (0) + , _firstValueIndexInWindow (0) + , _initialValueIndex (0) + , _cPrevValues (0) + , _cNextValues (0) + , _initialValue (0) + , _initialValueRounded (0) + , _increment (0) +{ +} + +FactValueSliderListModel::~FactValueSliderListModel() +{ +} + +int FactValueSliderListModel::resetInitialValue(void) +{ + // Remove any old rows + beginRemoveRows(QModelIndex(), 0, _cValues - 1); + _cValues = 0; + endRemoveRows(); + + _initialValue = _fact.cookedValue().toDouble(); + _initialValueRounded = qRound(_initialValue); + if (qRound(_fact.rawIncrement()) == _fact.rawIncrement()) { + _increment = qRound(_fact.cookedIncrement()); + } else { + _increment = _fact.cookedIncrement(); + } + _cPrevValues = qMin((_initialValue - _fact.cookedMin().toDouble()), 1000.0) / _increment; + _cNextValues = qMin((_fact.cookedMax().toDouble() - _initialValue), 1000.0) / _increment; + _initialValueIndex = _cPrevValues; + + int totalValueCount = _cPrevValues + 1 + _cNextValues; + beginInsertRows(QModelIndex(), 0, totalValueCount - 1); + _cValues = totalValueCount; + endInsertRows(); + + return _initialValueIndex; +} + +int FactValueSliderListModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + + return _cValues; +} + +QVariant FactValueSliderListModel::data(const QModelIndex &index, int role) const +{ + Q_UNUSED(role); + + if (!index.isValid()) { + return QVariant(); + } + + int valueIndex = index.row(); + if (valueIndex >= _cValues) { + return QVariant(); + } + + if (role == _valueRole) { + double value; + int cIncrementCount = valueIndex - _initialValueIndex; + if (cIncrementCount == 0) { + value = _initialValue; + } else { + value = _initialValueRounded + (cIncrementCount * _increment); + } + double precision = qPow(10, _fact.decimalPlaces()); + double atPrecision = qRound(value * precision) / precision; + //qDebug() << value << precision << atPrecision << _fact.decimalPlaces() << _fact.name(); + return QVariant(atPrecision); + } else if (role == _valueIndexRole) { + return QVariant::fromValue(valueIndex); + } else { + return QVariant(); + } + + +} + +QHash FactValueSliderListModel::roleNames(void) const +{ + QHash hash; + + hash[_valueRole] = "value"; + hash[_valueIndexRole] = "valueIndex"; + + return hash; +} + +double FactValueSliderListModel::valueAtModelIndex(int index) +{ + return data(createIndex(index, 0), _valueRole).toDouble(); + +} + +int FactValueSliderListModel::valueIndexAtModelIndex(int index) +{ + return data(createIndex(index, 0), _valueIndexRole).toInt(); +} diff --git a/src/FactSystem/FactValueSliderListModel.h b/src/FactSystem/FactValueSliderListModel.h new file mode 100644 index 0000000000000000000000000000000000000000..3f35b7156979beae1620ada3bd0d4fa1f72b4891 --- /dev/null +++ b/src/FactSystem/FactValueSliderListModel.h @@ -0,0 +1,48 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include + +#include "Fact.h" + +/// Provides a list model of values for incrementing/decrementing the value of a Fact +class FactValueSliderListModel : public QAbstractListModel +{ + Q_OBJECT + +public: + FactValueSliderListModel(Fact& fact, QObject* parent = NULL); + ~FactValueSliderListModel(); + + Q_INVOKABLE int resetInitialValue(void); + Q_INVOKABLE double valueAtModelIndex(int index); + Q_INVOKABLE int valueIndexAtModelIndex(int index); + +private: + // Overrides from QAbstractListModel + int rowCount(const QModelIndex & parent = QModelIndex()) const override; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; + QHash roleNames(void) const override; + + Fact& _fact; + int _cValues; + int _firstValueIndexInWindow; + int _initialValueIndex; + int _cPrevValues; + int _cNextValues; + int _windowSize; + double _initialValue; + double _initialValueRounded; + double _increment; + + static const int _valueRole; + static const int _valueIndexRole; +}; diff --git a/src/FirmwarePlugin/APM/APMParameterMetaData.cc b/src/FirmwarePlugin/APM/APMParameterMetaData.cc index 2760464324a7ea528ca20a9e2e31818b98dc8868..d297313e7c4cec9d3d23eba6ff337361597f4458 100644 --- a/src/FirmwarePlugin/APM/APMParameterMetaData.cc +++ b/src/FirmwarePlugin/APM/APMParameterMetaData.cc @@ -584,7 +584,7 @@ void APMParameterMetaData::addMetaDataToFact(Fact* fact, MAV_TYPE vehicleType) bool ok; increment = rawMetaData->incrementSize.toDouble(&ok); if (ok) { - metaData->setIncrement(increment); + metaData->setRawIncrement(increment); } else { qCDebug(APMParameterMetaDataLog) << "Invalid value for increment, name:" << metaData->name() << " increment:" << rawMetaData->incrementSize; } diff --git a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc index a4c3b6fc1889a75c1799f72518468986ad706611..5654a2a9c8cb81695e5b7800fe43fd7f2e7ac0cb 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc +++ b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc @@ -332,7 +332,7 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData QString text = xml.readElementText(); increment = text.toDouble(&ok); if (ok) { - metaData->setIncrement(increment); + metaData->setRawIncrement(increment); } else { qCWarning(PX4ParameterMetaDataLog) << "Invalid value for increment, name:" << metaData->name() << " increment:" << text; } diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 7e07d51f3b856933918cb6c87a10549fc3d70399..fc0c9d82b2a124a6157fdfa6ac6b8eeecda28487 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -73,15 +73,7 @@ Item { break; } } else { - // Note: We currently show alternate instruments all the time. This is a trial change for daily builds. - // Leaving non-alternate code in for now in case the trial fails. - var useAlternateInstruments = true//QGroundControl.settingsManager.appSettings.virtualJoystick.value || ScreenTools.isTinyScreen - if(useAlternateInstruments) { - instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidgetAlternate.qml" - } else { - instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidget.qml" - instrumentsLoader.state = QGroundControl.settingsManager.appSettings.showLargeCompass.value === 1 ? "centerRightMode" : "topRightMode" - } + instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidgetAlternate.qml" } } else { instrumentsLoader.source = "" diff --git a/src/MissionManager/CameraSection.FactMetaData.json b/src/MissionManager/CameraSection.FactMetaData.json index ab724a662d6cda5adfba7276d658fb7ff82f7591..818a18d42e9beb2e170fb13dd3853bbe4733ee02 100644 --- a/src/MissionManager/CameraSection.FactMetaData.json +++ b/src/MissionManager/CameraSection.FactMetaData.json @@ -32,6 +32,7 @@ "units": "gimbal-degrees", "min": -90, "max": 0, + "increment": 5, "decimalPlaces": 0, "defaultValue": 0 }, @@ -42,6 +43,7 @@ "units": "deg", "min": -180.0, "max": 180.0, + "increment": 5, "decimalPlaces": 0, "defaultValue": 0 }, diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 38e37aa6baa712c5a85b1b5bd52d5c4b61d9576e..bc774f7f43e7b478490ecd347b746d894f2702ea 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -462,7 +462,7 @@ void MissionController::removeMissionItem(int index) // Determine if the mission still has another survey style item in it bool foundSurvey = false; for (int i=1; i<_visualItems->count(); i++) { - if (_visualItems->value(i) || _visualItems->value(index)) { + if (_visualItems->value(i) || _visualItems->value(i)) { foundSurvey = true; break; } diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index 40b68604db83e8686e3fa627ad01f21dc3eca1ca..76ebceb84dd3fe946034e258bab9e2c00d408fd0 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -220,6 +220,7 @@ void SimpleMissionItem::_setupMetaData(void) if (!_altitudeMetaData) { _altitudeMetaData = new FactMetaData(FactMetaData::valueTypeDouble); _altitudeMetaData->setRawUnits("m"); + _altitudeMetaData->setRawIncrement(1); _altitudeMetaData->setDecimalPlaces(2); enumStrings.clear(); @@ -259,6 +260,7 @@ void SimpleMissionItem::_setupMetaData(void) _missionItem._commandFact.setMetaData(_commandMetaData); _missionItem._frameFact.setMetaData(_frameMetaData); _altitudeFact.setMetaData(_altitudeMetaData); + _amslAltAboveTerrainFact.setMetaData(_altitudeMetaData); } SimpleMissionItem::~SimpleMissionItem() @@ -659,6 +661,7 @@ void SimpleMissionItem::_altitudeChanged(void) } if (_altitudeMode == AltitudeAboveTerrain) { + _amslAltAboveTerrainFact.setRawValue(qQNaN()); _terrainAltChanged(); } else { _missionItem._param7Fact.setRawValue(_altitudeFact.rawValue()); diff --git a/src/PlanView/CameraSection.qml b/src/PlanView/CameraSection.qml index b91cd52b5e8d0413969f4bf1140185fb3b5cb3c8..0a01e62e09489081785e110d83f2f4ca54c582c2 100644 --- a/src/PlanView/CameraSection.qml +++ b/src/PlanView/CameraSection.qml @@ -74,6 +74,26 @@ Column { } } + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelWidth + visible: _camera.cameraModeSupported + + QGCCheckBox { + id: modeCheckBox + text: qsTr("Mode") + checked: _camera.specifyCameraMode + onClicked: _camera.specifyCameraMode = checked + } + FactComboBox { + fact: _camera.cameraMode + indexModel: false + enabled: modeCheckBox.checked + Layout.fillWidth: true + } + } + GridLayout { anchors.left: parent.left anchors.right: parent.right @@ -104,25 +124,5 @@ Column { enabled: gimbalCheckBox.checked } } - - RowLayout { - anchors.left: parent.left - anchors.right: parent.right - spacing: ScreenTools.defaultFontPixelWidth - visible: _camera.cameraModeSupported - - QGCCheckBox { - id: modeCheckBox - text: qsTr("Mode") - checked: _camera.specifyCameraMode - onClicked: _camera.specifyCameraMode = checked - } - FactComboBox { - fact: _camera.cameraMode - indexModel: false - enabled: modeCheckBox.checked - Layout.fillWidth: true - } - } } } diff --git a/src/PlanView/SimpleItemEditor.qml b/src/PlanView/SimpleItemEditor.qml index e63cc845356e4f0080f10512204a07c10a227431..d9bbe4f2dfe374b8f0a96bec0b33c21648db6f4e 100644 --- a/src/PlanView/SimpleItemEditor.qml +++ b/src/PlanView/SimpleItemEditor.qml @@ -19,6 +19,12 @@ Rectangle { property bool _specifiesAltitude: missionItem.specifiesAltitude property bool _altModeIsTerrain: missionItem.altitudeMode === 2 + property real _margin: ScreenTools.defaultFontPixelHeight / 2 + + ExclusiveGroup { + id: altRadios + onCurrentChanged: missionItem.altitudeMode = current.value + } Column { id: valuesColumn @@ -67,31 +73,63 @@ Rectangle { } } + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: altColumn.y + altColumn.height + _margin + color: qgcPal.windowShade + + Column { + id: altColumn + anchors.margins: _margin + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + + QGCLabel { + font.pointSize: ScreenTools.smallFontPointSize + text: qsTr("Altitude") + } + + RowLayout { + QGCRadioButton { text: qsTr("Rel"); exclusiveGroup: altRadios; checked: missionItem.altitudeMode === value; readonly property int value: 0 } + QGCRadioButton { text: qsTr("Abs"); exclusiveGroup: altRadios; checked: missionItem.altitudeMode === value; readonly property int value: 1 } + QGCRadioButton { text: qsTr("AGL"); exclusiveGroup: altRadios; checked: missionItem.altitudeMode === value; readonly property int value: 2 } + } + + FactValueSlider { + fact: missionItem.altitude + digitCount: 3 + incrementSlots: 1 + visible: _specifiesAltitude + } + + RowLayout { + spacing: _margin + + QGCLabel { + text: qsTr("Calculated Abs Alt") + font.pointSize: ScreenTools.smallFontPointSize + visible: _altModeIsTerrain + } + QGCLabel { + text: missionItem.amslAltAboveTerrain.valueString + " " + missionItem.amslAltAboveTerrain.units + visible: _altModeIsTerrain + } + } + } + } + GridLayout { anchors.left: parent.left anchors.right: parent.right flow: GridLayout.TopToBottom rows: missionItem.textFieldFacts.count + missionItem.nanFacts.count + - (missionItem.speedSection.available ? 1 : 0) + - (_specifiesAltitude ? 1 : 0) + - (_altModeIsTerrain ? 1 : 0) + (missionItem.speedSection.available ? 1 : 0) columns: 2 - QGCComboBox { - id: altCombo - model: [ qsTr("Alt (Rel)"), qsTr("AMSL"), qsTr("Above Terrain") ] - currentIndex: missionItem.altitudeMode - Layout.fillWidth: true - onActivated: missionItem.altitudeMode = index - visible: _specifiesAltitude - } - - QGCLabel { - text: qsTr("Actual AMSL Alt") - visible: _altModeIsTerrain - } - Repeater { model: missionItem.textFieldFacts @@ -117,18 +155,6 @@ Rectangle { } - FactTextField { - showUnits: true - fact: missionItem.altitude - Layout.fillWidth: true - visible: _specifiesAltitude - } - - FactLabel { - fact: missionItem.amslAltAboveTerrain - visible: _altModeIsTerrain - } - Repeater { model: missionItem.textFieldFacts diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 6d4d797b58f23fce3826493c78e92e4e0b14da7d..8bfd850b3d3e896e58d4101bbc6bbf7efedd432c 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -83,6 +83,7 @@ #include "CameraCalc.h" #include "VisualMissionItem.h" #include "EditPositionDialogController.h" +#include "FactValueSliderListModel.h" #ifndef NO_SERIAL_LINK #include "SerialLink.h" @@ -368,6 +369,7 @@ void QGCApplication::_initCommon(void) qmlRegisterUncreatableType ("QGroundControl.Controllers", 1, 0, "GeoFenceController", "Reference only"); qmlRegisterUncreatableType("QGroundControl.Controllers", 1, 0, "RallyPointController", "Reference only"); qmlRegisterUncreatableType ("QGroundControl.Controllers", 1, 0, "VisualMissionItem", "Reference only"); + qmlRegisterUncreatableType("QGroundControl.FactControls", 1, 0, "FactValueSliderListModel","Reference only"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "ParameterEditorController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "ESP8266ComponentController"); diff --git a/src/QmlControls/ParameterEditorDialog.qml b/src/QmlControls/ParameterEditorDialog.qml index 4a97a174dcc4dbef0a1254f67f63f10028ad9236..404a747d6e183d4be00dd92979f7cd4b1cf8a6c3 100644 --- a/src/QmlControls/ParameterEditorDialog.qml +++ b/src/QmlControls/ParameterEditorDialog.qml @@ -20,13 +20,16 @@ import QGroundControl.FactControls 1.0 import QGroundControl.ScreenTools 1.0 QGCViewDialog { - id: root + id: root + focus: true property Fact fact property bool showRCToParam: false property bool validate: false property string validateValue + signal valueChanged + property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 20 property bool _longDescriptionAvailable: fact.longDescription != "" property bool _editingParameter: fact.componentId != 0 @@ -41,15 +44,18 @@ QGCViewDialog { if (bitmaskColumn.visible && !manualEntry.checked) { fact.value = bitmaskValue(); fact.valueChanged(fact.value) + valueChanged() hideDialog(); } else if (factCombo.visible && !manualEntry.checked) { fact.enumIndex = factCombo.currentIndex + valueChanged() hideDialog() } else { var errorString = fact.validate(valueField.text, forceSave.checked) if (errorString === "") { fact.value = valueField.text fact.valueChanged(fact.value) + valueChanged() hideDialog() } else { validationError.text = errorString @@ -85,12 +91,8 @@ QGCViewDialog { } } - // set focus to the text field when becoming visible (in case of an Enum, - // the valueField is not visible, but it's not an issue because the combo - // box cannot have a focus) - onVisibleChanged: if (visible && !ScreenTools.isMobile) valueField.forceActiveFocus() - QGCFlickable { + id: flickable anchors.fill: parent contentHeight: _column.y + _column.height flickableDirection: Flickable.VerticalFlick @@ -120,9 +122,10 @@ QGCViewDialog { unitsLabel: fact.units showUnits: fact.units != "" Layout.fillWidth: true - inputMethodHints: ScreenTools.isiOS ? - Qt.ImhNone : // iOS numeric keyboard has not done button, we can't use it - Qt.ImhFormattedNumbersOnly // Forces use of virtual numeric keyboard + focus: true + inputMethodHints: (fact.typeIsString || ScreenTools.isiOS) ? + Qt.ImhNone : // iOS numeric keyboard has no done button, we can't use it + Qt.ImhFormattedNumbersOnly // Forces use of virtual numeric keyboard } QGCButton { diff --git a/src/QmlControls/QGCTextField.qml b/src/QmlControls/QGCTextField.qml index 459a775bc3305c3ac4922690b3f7db362b6c3b7c..f1a78e0b0355e17f00b6316d46cde021ea4f41e8 100644 --- a/src/QmlControls/QGCTextField.qml +++ b/src/QmlControls/QGCTextField.qml @@ -7,7 +7,10 @@ import QGroundControl.Palette 1.0 import QGroundControl.ScreenTools 1.0 TextField { - id: root + id: root + textColor: qgcPal.textFieldText + implicitHeight: ScreenTools.implicitTextFieldHeight + activeFocusOnPress: true property bool showUnits: false property bool showHelp: false @@ -17,18 +20,11 @@ TextField { property real _helpLayoutWidth: 0 - Component.onCompleted: { - if (typeof qgcTextFieldforwardKeysTo !== 'undefined') { - root.Keys.forwardTo = [qgcTextFieldforwardKeysTo] - } - } + Component.onCompleted: selectAllIfActiveFocus() + onActiveFocusChanged: selectAllIfActiveFocus() QGCPalette { id: qgcPal; colorGroupEnabled: enabled } - textColor: qgcPal.textFieldText - - implicitHeight: ScreenTools.implicitTextFieldHeight - onEditingFinished: { if (ScreenTools.isMobile) { // Toss focus on mobile after Done on virtual keyboard. Prevent strange interactions. @@ -36,6 +32,12 @@ TextField { } } + function selectAllIfActiveFocus() { + if (activeFocus) { + selectAll() + } + } + QGCLabel { id: unitsLabelWidthGenerator text: unitsLabel @@ -59,7 +61,7 @@ TextField { Rectangle { anchors.fill: parent - border.color: control.activeFocus ? "#47b" : "#999" + border.color: root.activeFocus ? "#47b" : "#999" color: qgcPal.textField } @@ -114,10 +116,4 @@ TextField { padding.right: control._helpLayoutWidth //control.showUnits ? unitsLabelWidthGenerator.width : control.__contentHeight * 0.333 } - - onActiveFocusChanged: { - if (activeFocus) { - selectAll() - } - } } diff --git a/src/QmlControls/QGCView.qml b/src/QmlControls/QGCView.qml index b50ac571e77367df7375af4404b21f47676c0939..4a4c2cef313167c767b2033012aef975f2cedc94 100644 --- a/src/QmlControls/QGCView.qml +++ b/src/QmlControls/QGCView.qml @@ -68,6 +68,7 @@ FactPanel { "viewPanel": viewPanel }) dialog.setupDialogButtons(buttons) + dialog.focus = true viewPanel.enabled = false } diff --git a/src/QmlControls/QGCViewDialog.qml b/src/QmlControls/QGCViewDialog.qml index 0c1e02b1c23ad98b26bc6d6daa8bc78a2b1b63e1..228432734d119843e2b6832008775ca1c5e0aae0 100644 --- a/src/QmlControls/QGCViewDialog.qml +++ b/src/QmlControls/QGCViewDialog.qml @@ -17,8 +17,6 @@ import QGroundControl.FactControls 1.0 import QGroundControl.ScreenTools 1.0 FactPanel { - property var qgcTextFieldforwardKeysTo: this ///< Causes all QGCTextFields to forward keys here if they have focus - property real defaultTextWidth: ScreenTools.defaultFontPixelWidth property real defaultTextHeight: ScreenTools.defaultFontPixelHeight diff --git a/src/QmlControls/QGCViewDialogContainer.qml b/src/QmlControls/QGCViewDialogContainer.qml index dda82095d2e11763365cad8d9cb7d3e6790e937b..f22dc7a55a275bee3dfb746191d2ca8f5d2a3996 100644 --- a/src/QmlControls/QGCViewDialogContainer.qml +++ b/src/QmlControls/QGCViewDialogContainer.qml @@ -15,9 +15,10 @@ import QGroundControl.Controls 1.0 import QGroundControl.Palette 1.0 import QGroundControl.ScreenTools 1.0 -Item { - id: _root - z: 5000 +FocusScope { + id: _root + z: 5000 + focus: true property alias dialogWidth: _dialogPanel.width property alias dialogTitle: titleLabel.text @@ -182,6 +183,7 @@ Item { anchors.top: _spacer.bottom anchors.bottom: parent.bottom sourceComponent: _dialogComponent + focus: true property bool acceptAllowed: _acceptButton.visible property bool rejectAllowed: _rejectButton.visible diff --git a/src/QmlControls/QmlObjectListModel.cc b/src/QmlControls/QmlObjectListModel.cc index f45a6e996c7b59c88a5d8f1c4d9ad4893d2e116e..1e481b902a118e87ea65a14eb09eefef8c120a3f 100644 --- a/src/QmlControls/QmlObjectListModel.cc +++ b/src/QmlControls/QmlObjectListModel.cc @@ -107,8 +107,6 @@ bool QmlObjectListModel::removeRows(int position, int rows, const QModelIndex& p beginRemoveRows(QModelIndex(), position, position + rows - 1); for (int row=0; rowdeleteLater(); _objectList.removeAt(position); } endRemoveRows(); diff --git a/src/comm/USBBoardInfo.json b/src/comm/USBBoardInfo.json index 535b81910f8f90210d4cbe31ec06f8110bd30cef..53013b6aacca59e6b26c9903bf8d9d522460d8f6 100644 --- a/src/comm/USBBoardInfo.json +++ b/src/comm/USBBoardInfo.json @@ -18,6 +18,7 @@ { "vendorID": 9900, "productID": 64, "boardClass": "Pixhawk", "name": "TAP V1" }, { "vendorID": 9900, "productID": 65, "boardClass": "Pixhawk", "name": "ASC V1" }, { "vendorID": 9900, "productID": 22, "boardClass": "Pixhawk", "name": "Crazyflie 2" }, + { "vendorID": 9900, "productID": 1, "boardClass": "Pixhawk", "name": "Omnibus F4 SD" }, { "vendorID": 9900, "productID": 21, "boardClass": "PX4 Flow", "name": "PX4 Flow" }, @@ -53,6 +54,7 @@ { "regExp": "^PX4 FMU", "boardClass": "Pixhawk" }, { "regExp": "^PX4 Crazyflie v2.0", "boardClass": "Pixhawk" }, { "regExp": "^Crazyflie BL", "boardClass": "Pixhawk" }, + { "regExp": "^PX4 OmnibusF4SD", "boardClass": "Pixhawk" }, { "regExp": "PX4.*Flow", "boardClass": "PX4 Flow" }, { "regExp": "^FT231X USB UART$", "boardClass": "SiK Radio" }, { "regExp": "USB UART$", "boardClass": "SiK Radio", "androidOnly": true, "comment": "Very broad fallback, too dangerous for non-android" }