diff --git a/ChangeLog.md b/ChangeLog.md index 948495bb9ec..4fa9a949533 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -4,11 +4,13 @@ Note: This file only contains high level features or important fixes. ## 5.0 - Daily Build -* New combined compass and attitude instrument -* You can select between multiple instruments by clicking on the instrument conntrol on a desktop build or press and hold on mobile builds -* Support for Fly View and Joystick custom mavlink actions has changed. Both the name and formation of the command file is different now. Go to QGC docs to understand how it works now. -* Support for setting individual Mavlink message rates in the Mavlink Inspector. -* Support for Mavlink 2 signing +* Introduced a new combined compass and attitude instrument for enhanced navigation. +* Select between multiple instruments by clicking the instrument control on desktop or pressing and holding on mobile. +* Updated support for Fly View and Joystick custom MAVLink actions; command file names and formats have changed—refer to QGC docs for details. +* Added functionality for setting individual MAVLink message rates in the MAVLink Inspector. +* Enabled support for MAVLink 2 signing. +* Dynamic battery display that reduces the number of bars based on battery percentage, with configurable states (100%, Config 1, Config 2, Low, Critical) for clearer status indication. + ## 4.1 diff --git a/qgcimages.qrc b/qgcimages.qrc index 20f5b1ed135..89b8ab7e649 100644 --- a/qgcimages.qrc +++ b/qgcimages.qrc @@ -61,6 +61,12 @@ src/FlightMap/Images/attitudePointer.svg src/FlightMap/Images/AwarenessAircraft.svg src/UI/toolbar/Images/Battery.svg + src/UI/toolbar/Images/BatteryGreen.svg + src/UI/toolbar/Images/BatteryYellowGreen.svg + src/UI/toolbar/Images/BatteryYellow.svg + src/UI/toolbar/Images/BatteryOrange.svg + src/UI/toolbar/Images/BatteryCritical.svg + src/UI/toolbar/Images/BatteryEMERGENCY.svg resources/camera.svg src/Camera/images/camera_photo.svg src/Camera/images/camera_video.svg diff --git a/src/QmlControls/QGCPalette.cc b/src/QmlControls/QGCPalette.cc index ab13c7c27e9..c2ba0255a7b 100644 --- a/src/QmlControls/QGCPalette.cc +++ b/src/QmlControls/QGCPalette.cc @@ -68,9 +68,11 @@ void QGCPalette::_buildMap() DECLARE_QGC_COLOR(mapButtonHighlight, "#585858", "#be781c", "#585858", "#be781c") DECLARE_QGC_COLOR(mapIndicator, "#585858", "#be781c", "#585858", "#be781c") DECLARE_QGC_COLOR(mapIndicatorChild, "#585858", "#766043", "#585858", "#766043") - DECLARE_QGC_COLOR(colorGreen, "#009431", "#009431", "#00e04b", "#00e04b") - DECLARE_QGC_COLOR(colorOrange, "#b95604", "#b95604", "#de8500", "#de8500") - DECLARE_QGC_COLOR(colorRed, "#ed3939", "#ed3939", "#f32836", "#f32836") + DECLARE_QGC_COLOR(colorGreen, "#008f2d", "#008f2d", "#00e04b", "#00e04b") + DECLARE_QGC_COLOR(colorYellow, "#a2a200", "#a2a200", "#ffff00", "#ffff00") + DECLARE_QGC_COLOR(colorYellowGreen, "#799f26", "#799f26", "#9dbe2f", "#9dbe2f") + DECLARE_QGC_COLOR(colorOrange, "#bf7539", "#bf7539", "#de8500", "#de8500") + DECLARE_QGC_COLOR(colorRed, "#b52b2b", "#b52b2b", "#f32836", "#f32836") DECLARE_QGC_COLOR(colorGrey, "#808080", "#808080", "#bfbfbf", "#bfbfbf") DECLARE_QGC_COLOR(colorBlue, "#1a72ff", "#1a72ff", "#536dff", "#536dff") DECLARE_QGC_COLOR(alertBackground, "#eecc44", "#eecc44", "#eecc44", "#eecc44") diff --git a/src/QmlControls/QGCPalette.h b/src/QmlControls/QGCPalette.h index 477646fd9ec..5ba2c53f744 100644 --- a/src/QmlControls/QGCPalette.h +++ b/src/QmlControls/QGCPalette.h @@ -138,6 +138,8 @@ class QGCPalette : public QObject DEFINE_QGC_COLOR(brandingPurple, setBrandingPurple) DEFINE_QGC_COLOR(brandingBlue, setBrandingBlue) DEFINE_QGC_COLOR(colorGreen, setColorGreen) + DEFINE_QGC_COLOR(colorYellow, setColorYellow) + DEFINE_QGC_COLOR(colorYellowGreen, setColorYellowGreen) DEFINE_QGC_COLOR(colorOrange, setColorOrange) DEFINE_QGC_COLOR(colorRed, setColorRed) DEFINE_QGC_COLOR(colorGrey, setColorGrey) diff --git a/src/Settings/BatteryIndicator.SettingsGroup.json b/src/Settings/BatteryIndicator.SettingsGroup.json index 973ffa81438..7432d864b9d 100644 --- a/src/Settings/BatteryIndicator.SettingsGroup.json +++ b/src/Settings/BatteryIndicator.SettingsGroup.json @@ -1,15 +1,46 @@ { - "version": 1, - "fileType": "FactMetaData", - "QGC.MetaData.Facts": -[ -{ - "name": "display", - "shortDesc": "Select values to display in indicator", - "enumStrings": "Percentage,Voltage,Percentage and Voltage", - "enumValues": "0,1,2", - "type": "uint32", - "default": false -} -] + "version": 1, + "fileType": "FactMetaData", + "QGC.MetaData.Facts": [ + { + "name": "display", + "shortDesc": "Select values to display in indicator", + "enumStrings": "Percentage,Voltage,Percentage and Voltage", + "enumValues": "0,1,2", + "type": "uint32", + "default": false + }, + { + "name": "threshold1", + "shortDesc": "Battery level threshold 1", + "type": "uint32", + "default": 80, + "units": "%" + }, + { + "name": "threshold2", + "shortDesc": "Battery level threshold 2", + "type": "uint32", + "default": 60, + "units": "%" + }, + { + "name": "threshold1visible", + "shortDesc": "Controls the visibility of the 'threshold1visible'", + "type": "bool", + "default": true + }, + { + "name": "threshold2visible", + "shortDesc": "Controls the visibility of the 'threshold2visible'", + "type": "bool", + "default": true + }, + { + "name": "battery_state_display", + "shortDesc": "Controls the visibility of the 'Battery State Display'", + "type": "bool", + "default": true + } + ] } diff --git a/src/Settings/BatteryIndicatorSettings.cc b/src/Settings/BatteryIndicatorSettings.cc index d33483e3a65..217a7d2cca8 100644 --- a/src/Settings/BatteryIndicatorSettings.cc +++ b/src/Settings/BatteryIndicatorSettings.cc @@ -9,11 +9,94 @@ #include "BatteryIndicatorSettings.h" +#include #include +// Declare the settings group for Battery Indicator DECLARE_SETTINGGROUP(BatteryIndicator, "BatteryIndicator") { + // Register the BatteryIndicatorSettings type for QML use qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "BatteryIndicatorSettings", "Reference only"); } -DECLARE_SETTINGSFACT(BatteryIndicatorSettings, display) +// Declare standard setting facts for the BatteryIndicatorSettings +DECLARE_SETTINGSFACT(BatteryIndicatorSettings, display) // Visibility of battery indicator +DECLARE_SETTINGSFACT(BatteryIndicatorSettings, battery_state_display) // Battery state display mode + +// Declare visibility settings for threshold editability +DECLARE_SETTINGSFACT(BatteryIndicatorSettings, threshold1visible) // Determines if the FactTextField for threshold 1 is visible (editable) +DECLARE_SETTINGSFACT(BatteryIndicatorSettings, threshold2visible) // Determines if the FactTextField for threshold 2 is visible (editable) + +DECLARE_SETTINGSFACT_NO_FUNC(BatteryIndicatorSettings, threshold1) +{ + if (!_threshold1Fact) { + _threshold1Fact = _createSettingsFact(threshold1Name); + connect(_threshold1Fact, &SettingsFact::rawValueChanged, this, &BatteryIndicatorSettings::_threshold1Changed); + } + return _threshold1Fact; +} + +DECLARE_SETTINGSFACT_NO_FUNC(BatteryIndicatorSettings, threshold2) +{ + if (!_threshold2Fact) { + _threshold2Fact = _createSettingsFact(threshold2Name); + connect(_threshold2Fact, &SettingsFact::rawValueChanged, this, &BatteryIndicatorSettings::_threshold2Changed); + } + return _threshold2Fact; +} + +// Change handlers for thresholds +void BatteryIndicatorSettings::_threshold1Changed() { + validateThreshold1(); // Call validation when threshold1 value changes +} + +void BatteryIndicatorSettings::_threshold2Changed() { + validateThreshold2(); // Call validation when threshold2 value changes +} + +// Validate threshold1 value +void BatteryIndicatorSettings::validateThreshold1() { + int value = threshold1()->rawValue().toInt(); + setThreshold1(value); // Call the setter with the current value +} + +// Validate threshold2 value +void BatteryIndicatorSettings::validateThreshold2() { + int value = threshold2()->rawValue().toInt(); + setThreshold2(value); // Call the setter with the current value +} + +// Set threshold1 with validation +void BatteryIndicatorSettings::setThreshold1(int value) { + // Ensure value is at least 16 and less than 100 + if (value < 16) { + threshold1()->setRawValue(17); // Adjust to minimum valid value + } else if (value > 99) { + threshold1()->setRawValue(99); // Cap at maximum valid value + } else { + // Check if value is greater than threshold2 + if (value > threshold2()->rawValue().toInt()) { + threshold1()->setRawValue(value); + } else { + // Ensure threshold1 is greater than threshold2 + threshold1()->setRawValue(threshold2()->rawValue().toInt() + 1); + } + } +} + +// Set threshold2 with validation +void BatteryIndicatorSettings::setThreshold2(int value) { + // Ensure value is greater than 15 + if (value <= 15) { + threshold2()->setRawValue(16); // Adjust to the minimum valid value + return; + } + + // Check if value is less than threshold1 + if (value < threshold1()->rawValue().toInt()) { + threshold2()->setRawValue(value); + } else { + // Ensure threshold2 is less than threshold1 + threshold2()->setRawValue(threshold1()->rawValue().toInt() - 1); + } +} diff --git a/src/Settings/BatteryIndicatorSettings.h b/src/Settings/BatteryIndicatorSettings.h index 59c7f8eef5c..fd442e2f045 100644 --- a/src/Settings/BatteryIndicatorSettings.h +++ b/src/Settings/BatteryIndicatorSettings.h @@ -14,9 +14,31 @@ class BatteryIndicatorSettings : public SettingsGroup { Q_OBJECT + public: BatteryIndicatorSettings(QObject* parent = nullptr); + DEFINE_SETTING_NAME_GROUP() - DEFINE_SETTINGFACT(display) + // Standard setting facts + DEFINE_SETTINGFACT(display) // Visibility of battery indicator + DEFINE_SETTINGFACT(battery_state_display) // Battery state display mode + + // Define visibility settings for threshold editability + DEFINE_SETTINGFACT(threshold1visible) // Determines if the FactTextField for threshold 1 is visible (editable) + DEFINE_SETTINGFACT(threshold2visible) // Determines if the FactTextField for threshold 2 is visible (editable) + + // Define setting facts for thresholds + DEFINE_SETTINGFACT(threshold1) // First threshold for battery level + DEFINE_SETTINGFACT(threshold2) // Second threshold for battery level + Q_INVOKABLE void setThreshold1(int value); // Set threshold1 with validation + Q_INVOKABLE void setThreshold2(int value); // Set threshold2 with validation + +private: + void validateThreshold1(); // Validate threshold1 value + void validateThreshold2(); // Validate threshold2 value + + // Change handlers for thresholds + void _threshold1Changed(); + void _threshold2Changed(); }; diff --git a/src/UI/toolbar/BatteryIndicator.qml b/src/UI/toolbar/BatteryIndicator.qml index 8c28875562a..62df9061c17 100644 --- a/src/UI/toolbar/BatteryIndicator.qml +++ b/src/UI/toolbar/BatteryIndicator.qml @@ -37,6 +37,18 @@ Item { property bool _showVoltage: _indicatorDisplay.rawValue === 1 property bool _showBoth: _indicatorDisplay.rawValue === 2 + // Fetch battery settings + property var batterySettings: QGroundControl.settingsManager.batteryIndicatorSettings + + // Properties to hold the thresholds + property int threshold1: batterySettings.threshold1.rawValue + property int threshold2: batterySettings.threshold2.rawValue + + // Control visibility based on battery state display setting + property bool batteryState: batterySettings.battery_state_display.rawValue + property bool threshold1visible: batterySettings.threshold1visible.rawValue + property bool threshold2visible: batterySettings.threshold2visible.rawValue + Row { id: batteryIndicatorRow anchors.top: parent.top @@ -81,17 +93,53 @@ Item { function getBatteryColor() { switch (battery.chargeState.rawValue) { - case MAVLink.MAV_BATTERY_CHARGE_STATE_OK: - return qgcPal.text - case MAVLink.MAV_BATTERY_CHARGE_STATE_LOW: - return qgcPal.colorOrange - case MAVLink.MAV_BATTERY_CHARGE_STATE_CRITICAL: - case MAVLink.MAV_BATTERY_CHARGE_STATE_EMERGENCY: - case MAVLink.MAV_BATTERY_CHARGE_STATE_FAILED: - case MAVLink.MAV_BATTERY_CHARGE_STATE_UNHEALTHY: - return qgcPal.colorRed - default: - return qgcPal.text + case MAVLink.MAV_BATTERY_CHARGE_STATE_OK: + if (!isNaN(battery.percentRemaining.rawValue)) { + if (battery.percentRemaining.rawValue > threshold1) { + return qgcPal.colorGreen + } else if (battery.percentRemaining.rawValue > threshold2) { + return qgcPal.colorYellowGreen + } else { + return qgcPal.colorYellow + } + } else { + return qgcPal.text + } + case MAVLink.MAV_BATTERY_CHARGE_STATE_LOW: + return qgcPal.colorOrange + case MAVLink.MAV_BATTERY_CHARGE_STATE_CRITICAL: + case MAVLink.MAV_BATTERY_CHARGE_STATE_EMERGENCY: + case MAVLink.MAV_BATTERY_CHARGE_STATE_FAILED: + case MAVLink.MAV_BATTERY_CHARGE_STATE_UNHEALTHY: + return qgcPal.colorRed + default: + return qgcPal.text + } + } + + function getBatterySvgSource() { + + switch (battery.chargeState.rawValue) { + case MAVLink.MAV_BATTERY_CHARGE_STATE_OK: + if (!isNaN(battery.percentRemaining.rawValue)) { + if (battery.percentRemaining.rawValue > threshold1) { + return "/qmlimages/BatteryGreen.svg" + } else if (battery.percentRemaining.rawValue > threshold2) { + return "/qmlimages/BatteryYellowGreen.svg" + } else { + return "/qmlimages/BatteryYellow.svg" + } + } + case MAVLink.MAV_BATTERY_CHARGE_STATE_LOW: + return "/qmlimages/BatteryOrange.svg" // Low with orange svg + case MAVLink.MAV_BATTERY_CHARGE_STATE_CRITICAL: + return "/qmlimages/BatteryCritical.svg" // Critical with red svg + case MAVLink.MAV_BATTERY_CHARGE_STATE_EMERGENCY: + case MAVLink.MAV_BATTERY_CHARGE_STATE_FAILED: + case MAVLink.MAV_BATTERY_CHARGE_STATE_UNHEALTHY: + return "/qmlimages/BatteryEMERGENCY.svg" // Exclamation mark + default: + return "/qmlimages/Battery.svg" // Fallback if percentage is unavailable } } @@ -110,7 +158,7 @@ Item { return qsTr("n/a") } - function getBatteryVoltageText() { + function getBatteryVoltageText() { if (!isNaN(battery.voltage.rawValue)) { return battery.voltage.valueString + battery.voltage.units } else if (battery.chargeState.rawValue !== MAVLink.MAV_BATTERY_CHARGE_STATE_UNDEFINED) { @@ -124,7 +172,7 @@ Item { anchors.bottom: parent.bottom width: height sourceSize.width: width - source: "/qmlimages/Battery.svg" + source: getBatterySvgSource() fillMode: Image.PreserveAspectFit color: getBatteryColor() } @@ -138,7 +186,7 @@ Item { QGCLabel { Layout.alignment: Qt.AlignHCenter verticalAlignment: Text.AlignVCenter - color: getBatteryColor() + color: qgcPal.text text: getBatteryPercentageText() font.pointSize: _showBoth ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize visible: _showBoth || _showPercentage @@ -147,7 +195,7 @@ Item { QGCLabel { Layout.alignment: Qt.AlignHCenter font.pointSize: _showBoth ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize - color: getBatteryColor() + color: qgcPal.text text: getBatteryVoltageText() visible: _showBoth || _showVoltage } @@ -278,7 +326,113 @@ Item { } } } + } + + SettingsGroupLayout { + heading: qsTr("Battery State Display") + Layout.fillWidth: true + spacing: ScreenTools.defaultFontPixelHeight * 0.05 // Reduced outer spacing + visible: batteryState // Control visibility of the entire group + + RowLayout { + spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Reduced spacing between elements + + // Battery 100% + RowLayout { + spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label + QGCColoredImage { + source: "/qmlimages/BatteryGreen.svg" + height: ScreenTools.defaultFontPixelHeight * 5 + width: ScreenTools.defaultFontPixelWidth * 6 + fillMode: Image.PreserveAspectFit + color: qgcPal.colorGreen + } + QGCLabel { text: qsTr("100%") } + } + + // Threshold 1 + RowLayout { + spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and field + QGCColoredImage { + source: "/qmlimages/BatteryYellowGreen.svg" + height: ScreenTools.defaultFontPixelHeight * 5 + width: ScreenTools.defaultFontPixelWidth * 6 + fillMode: Image.PreserveAspectFit + color: qgcPal.colorYellowGreen + } + FactTextField { + id: threshold1Field + fact: batterySettings.threshold1 + implicitWidth: ScreenTools.defaultFontPixelWidth * 5.5 + height: ScreenTools.defaultFontPixelHeight * 1.5 + visible: threshold1visible + onEditingFinished: { + // Validate and set the new threshold value + batterySettings.setThreshold1(parseInt(text)); + } + } + } + QGCLabel { + visible: !threshold1visible + text: qsTr("") + batterySettings.threshold1.rawValue.toString() + qsTr("%") + } + + // Threshold 2 + RowLayout { + spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and field + QGCColoredImage { + source: "/qmlimages/BatteryYellow.svg" + height: ScreenTools.defaultFontPixelHeight * 5 + width: ScreenTools.defaultFontPixelWidth * 6 + fillMode: Image.PreserveAspectFit + color: qgcPal.colorYellow + } + FactTextField { + id: threshold2Field + fact: batterySettings.threshold2 + implicitWidth: ScreenTools.defaultFontPixelWidth * 5.5 + height: ScreenTools.defaultFontPixelHeight * 1.5 + visible: threshold2visible + onEditingFinished: { + // Validate and set the new threshold value + batterySettings.setThreshold2(parseInt(text)); + } + } + } + QGCLabel { + visible: !threshold2visible + text: qsTr("") + batterySettings.threshold2.rawValue.toString() + qsTr("%") + } + + // Low state + RowLayout { + spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label + QGCColoredImage { + source: "/qmlimages/BatteryOrange.svg" + height: ScreenTools.defaultFontPixelHeight * 5 + width: ScreenTools.defaultFontPixelWidth * 6 + fillMode: Image.PreserveAspectFit + color: qgcPal.colorOrange + } + QGCLabel { text: qsTr("Low") } + } + + // Critical state + RowLayout { + spacing: ScreenTools.defaultFontPixelWidth * 0.05 // Tighter spacing for icon and label + QGCColoredImage { + source: "/qmlimages/BatteryCritical.svg" + height: ScreenTools.defaultFontPixelHeight * 5 + width: ScreenTools.defaultFontPixelWidth * 6 + fillMode: Image.PreserveAspectFit + color: qgcPal.colorRed + } + QGCLabel { text: qsTr("Critical") } + } + } + } + } } } diff --git a/src/UI/toolbar/Images/BatteryCritical.svg b/src/UI/toolbar/Images/BatteryCritical.svg new file mode 100644 index 00000000000..28e83e3128a --- /dev/null +++ b/src/UI/toolbar/Images/BatteryCritical.svg @@ -0,0 +1,70 @@ + + + + + + + + + diff --git a/src/UI/toolbar/Images/BatteryEMERGENCY.svg b/src/UI/toolbar/Images/BatteryEMERGENCY.svg new file mode 100644 index 00000000000..bd2bf9c31d9 --- /dev/null +++ b/src/UI/toolbar/Images/BatteryEMERGENCY.svg @@ -0,0 +1,87 @@ + + + + + + + + + diff --git a/src/UI/toolbar/Images/BatteryGreen.svg b/src/UI/toolbar/Images/BatteryGreen.svg new file mode 100644 index 00000000000..c6095d2c461 --- /dev/null +++ b/src/UI/toolbar/Images/BatteryGreen.svg @@ -0,0 +1,102 @@ + + + + + + + + + diff --git a/src/UI/toolbar/Images/BatteryOrange.svg b/src/UI/toolbar/Images/BatteryOrange.svg new file mode 100644 index 00000000000..1bd9fe32fc0 --- /dev/null +++ b/src/UI/toolbar/Images/BatteryOrange.svg @@ -0,0 +1,78 @@ + + + + + + + + + diff --git a/src/UI/toolbar/Images/BatteryYellow.svg b/src/UI/toolbar/Images/BatteryYellow.svg new file mode 100644 index 00000000000..49a3c6e4daf --- /dev/null +++ b/src/UI/toolbar/Images/BatteryYellow.svg @@ -0,0 +1,86 @@ + + + + + + + + + diff --git a/src/UI/toolbar/Images/BatteryYellowGreen.svg b/src/UI/toolbar/Images/BatteryYellowGreen.svg new file mode 100644 index 00000000000..83ca709523d --- /dev/null +++ b/src/UI/toolbar/Images/BatteryYellowGreen.svg @@ -0,0 +1,94 @@ + + + + + + + + + diff --git a/src/UI/toolbar/Images/TelemRSSI.svg b/src/UI/toolbar/Images/TelemRSSI.svg index 077fa362211..c134a3cb72c 100644 --- a/src/UI/toolbar/Images/TelemRSSI.svg +++ b/src/UI/toolbar/Images/TelemRSSI.svg @@ -1,6 +1,6 @@ - +