Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhance battery indicator visuals with new SVGs and color coding #11881

Merged
merged 23 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
dc2148c
Enhance battery indicator visuals
Paschalis Sep 15, 2024
feb382c
Added colorYellow and colorYellowGreen to QGCPalette and updated Batt…
Paschalis Sep 17, 2024
ae4249a
Update color definitions for improved visibility for light theme
Paschalis Sep 17, 2024
c6ab475
Apply battery colors only in dark theme
Paschalis Sep 18, 2024
c7be67c
Update qgcimages.qrc to include new battery icons.
Paschalis Sep 20, 2024
e047024
Add battery configuration settings for dark and light themes.
Paschalis Sep 20, 2024
7bda84d
Implement logic for configurable battery thresholds.
Paschalis Sep 20, 2024
cf43592
Add header definitions for battery indicator settings.
Paschalis Sep 20, 2024
0b8cfbc
Improve battery indicator with dark theme support and configurable co…
Paschalis Sep 20, 2024
fb186df
Fix SVG file namespace issues in TelemRSSI.svg.
Paschalis Sep 20, 2024
5138b77
Add new battery configuration icons for light and dark themes (Batter…
Paschalis Sep 20, 2024
81ee7db
Modify palette, battery indicator, and update images.
Paschalis Sep 21, 2024
4f78b5a
Remove unnecessary battery images.
Paschalis Sep 21, 2024
012cbb4
Add BatteryEMERGENCY.svg image.
Paschalis Sep 21, 2024
7563664
Update ChangeLog with new battery display feature.
Paschalis Sep 21, 2024
0f5c193
Updated BatteryIndicatorSettings and UI for dynamic visibility and va…
Paschalis Sep 23, 2024
0461be3
Merge branch 'master' into master
Paschalis Sep 23, 2024
fc5d511
Merge branch 'mavlink:master' into master
Paschalis Sep 25, 2024
c2ce804
feat: Update battery indicator settings and visibility logic
Paschalis Sep 28, 2024
3a33e93
Merge branch 'master' into master
Paschalis Sep 28, 2024
80806a3
Adjusted spacing in BatteryIndicator.qml for better code readability
Paschalis Sep 28, 2024
fd10395
Merge branch 'master' into master
Paschalis Oct 2, 2024
27d3eb3
Merge branch 'mavlink:master' into master
Paschalis Oct 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions qgcimages.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@
<file alias="attitudePointer.svg">src/FlightMap/Images/attitudePointer.svg</file>
<file alias="AwarenessAircraft.svg">src/FlightMap/Images/AwarenessAircraft.svg</file>
<file alias="Battery.svg">src/UI/toolbar/Images/Battery.svg</file>
<file alias="BatteryGreen.svg">src/UI/toolbar/Images/BatteryGreen.svg</file>
<file alias="BatteryYellowGreen.svg">src/UI/toolbar/Images/BatteryYellowGreen.svg</file>
<file alias="BatteryYellow.svg">src/UI/toolbar/Images/BatteryYellow.svg</file>
<file alias="BatteryOrange.svg">src/UI/toolbar/Images/BatteryOrange.svg</file>
<file alias="BatteryCritical.svg">src/UI/toolbar/Images/BatteryCritical.svg</file>
<file alias="BatteryEMERGENCY.svg">src/UI/toolbar/Images/BatteryEMERGENCY.svg</file>
<file alias="camera.svg">resources/camera.svg</file>
<file alias="camera_photo.svg">src/Camera/images/camera_photo.svg</file>
<file alias="camera_video.svg">src/Camera/images/camera_video.svg</file>
Expand Down
8 changes: 5 additions & 3 deletions src/QmlControls/QGCPalette.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
2 changes: 2 additions & 0 deletions src/QmlControls/QGCPalette.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
39 changes: 26 additions & 13 deletions src/Settings/BatteryIndicator.SettingsGroup.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
{
"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": "%"
}
]
}
2 changes: 2 additions & 0 deletions src/Settings/BatteryIndicatorSettings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ DECLARE_SETTINGGROUP(BatteryIndicator, "BatteryIndicator")
}

DECLARE_SETTINGSFACT(BatteryIndicatorSettings, display)
DECLARE_SETTINGSFACT(BatteryIndicatorSettings, threshold1)
DECLARE_SETTINGSFACT(BatteryIndicatorSettings, threshold2)
2 changes: 2 additions & 0 deletions src/Settings/BatteryIndicatorSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ class BatteryIndicatorSettings : public SettingsGroup
DEFINE_SETTING_NAME_GROUP()

DEFINE_SETTINGFACT(display)
DEFINE_SETTINGFACT(threshold1)
DEFINE_SETTINGFACT(threshold2)
};
183 changes: 169 additions & 14 deletions src/UI/toolbar/BatteryIndicator.qml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ 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

Row {
id: batteryIndicatorRow
anchors.top: parent.top
Expand Down Expand Up @@ -81,17 +88,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
}
}

Expand Down Expand Up @@ -124,7 +167,7 @@ Item {
anchors.bottom: parent.bottom
width: height
sourceSize.width: width
source: "/qmlimages/Battery.svg"
source: getBatterySvgSource()
fillMode: Image.PreserveAspectFit
color: getBatteryColor()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this coloring still work given the fact that the svg's are already colored as opposed to white? All the other icons which are colored start out as white and then have the color applied. Did you change the palette to verify that the correct colors show up?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you verify this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I verified that the coloring works properly, even though the SVG icons are already colored. The icons display correctly in both Light and Dark themes. I also tested the color application from the palette, and the correct colors show up as expected in both themes without any issues.

Light.mp4
Dark.mp4

}
Expand All @@ -138,7 +181,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
Expand All @@ -147,7 +190,7 @@ Item {
QGCLabel {
Layout.alignment: Qt.AlignHCenter
font.pointSize: _showBoth ? ScreenTools.defaultFontPointSize : ScreenTools.mediumFontPointSize
color: getBatteryColor()
color: qgcPal.text
text: getBatteryVoltageText()
visible: _showBoth || _showVoltage
}
Expand Down Expand Up @@ -240,6 +283,7 @@ Item {
}
}


Component {
id: batteryExpandedComponent

Expand Down Expand Up @@ -278,6 +322,117 @@ Item {
}
}
}

}

SettingsGroupLayout {
heading: qsTr("Battery State Display")
Layout.fillWidth: true
spacing: ScreenTools.defaultFontPixelWidth * 0.5
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The visibility of this group should be based on batterySettings.visible. This allows custom builds to hide this section if they want to.

Also each individual settings has a visible property. If not visible then the editing field would normally be completely hidden. But in this caseI would show just a label with the percentage instead of a configurable text field.

Both of these combined give a good level of custom build override capability.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve implemented the visibility controls as you suggested, using visible: batterySettings.visible for the entire group and bool thresholdVisible: batterySettings.thresholdEditable for individual settings.

In the BatteryIndicatorSettings class, I’ve added QSettings to manage visibility and editability, defaulting to true if no value is set:

bool BatteryIndicatorSettings::visible() const {
    QSettings settings;
    return settings.value("ShowBatterySettings", true).toBool();
}

bool BatteryIndicatorSettings::thresholdEditable() const {
    QSettings settings;
    return settings.value("ThresholdEditable", true).toBool();
}

However, I’m unsure how to properly configure these two settings to effectively allow custom builds to hide this section if they choose to. Could you provide how to set this up correctly? Your expertise would be greatly appreciated!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the current setup, if the thresholdVisible property is false, the FactTextField for threshold1 and threshold2 will be hidden, and a QGCLabel will display the current percentages. This ensures that users can still see the battery thresholds without the editing fields when they're not meant to be configurable.

Screenshot from 2024-09-23 21-15-09

If the thresholdVisible property is true, the FactTextField for both threshold1 and threshold2 will be visible, allowing users to edit the thresholds directly.

Screenshot from 2024-09-23 21-23-33

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I’m unsure how to properly configure these two settings to effectively allow custom builds to hide this section

there is no extra work needed. SettingsGroup and SettingsFact base classes already support this automatically.


RowLayout {
//Layout.fillWidth: true

QGCColoredImage {
source: "/qmlimages/BatteryGreen.svg"
height: ScreenTools.defaultFontPixelHeight * 5
width: ScreenTools.defaultFontPixelWidth * 7
fillMode: Image.PreserveAspectFit
color: qgcPal.colorGreen
}

QGCLabel {
Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 3.5
text: qsTr("100%")
Layout.leftMargin: -13
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should never use fix pixels as margins. Everything needs to be based on the current font. Otherwise the ui won't scale to font size changes.

The way to lay this out is as follows:

  • Each battery icon plus either fixed percentage or text field should be in its own RowLayout. With the spacing on the layout set to something like font width times 1 or 2 depending on how it looks.
  • Then there is a top level RowLayout which has all these RowLayouts as children. And then there is a font based spacing on that to space out the battery+pct visuals.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate your guidance, and I've implemented the layout adjustments as you suggested. Each battery icon, along with its corresponding percentage or text field, is now placed in its own RowLayout. I've also set the spacing based on the current font width, ensuring that the UI scales properly with font size changes. The top-level RowLayout contains all these individual layouts with appropriate font-based spacing.

}

QGCColoredImage {
source: "/qmlimages/BatteryYellowGreen.svg"
height: ScreenTools.defaultFontPixelHeight * 5
width: ScreenTools.defaultFontPixelWidth * 7
fillMode: Image.PreserveAspectFit
color: qgcPal.colorYellowGreen
}

FactTextField {
id: threshold1Field
fact: batterySettings.threshold1
validator: IntValidator { bottom: 16 } // Value is at least 16
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be done by specifying min in the FactMetaData. Then you get automatic validation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’ve implemented the validation fully on the C++ side in BatteryIndicatorSettings.cc. Now, in the QML, we simply use:

onEditingFinished: {
    batterySettings.setThreshold2(parseInt(text));
}

implicitWidth: ScreenTools.defaultFontPixelWidth * 6 // Reduced width
Layout.leftMargin: -10
//Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 2 // Reduced width using Layout.preferredWidth
height: ScreenTools.defaultFontPixelHeight * 1.5
onEditingFinished: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you do final level of validation on a rawValueChanged signal to adjust if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've implemented the final level of validation in the BatteryIndicatorSettings.cc. For threshold1, I ensure that the value is always at least 16 and no more than 99. If the provided value is below 16, it gets adjusted to 17. If it exceeds 99, it's capped at 99. Additionally, if the new value is less than or equal to threshold2, it automatically gets set to one more than threshold2 to maintain the required order. For threshold2, I enforce a minimum value of 16. If the user attempts to set it to 15 or less, it gets adjusted up to 16. Moreover, if the new value is greater than or equal to threshold1, it is set to one less than threshold1 to ensure it remains less.

let newValue = parseInt(text);
if (newValue > batterySettings.threshold2.rawValue) {
batterySettings.threshold1.rawValue = newValue;
} else {
// Adjust value if invalid
batterySettings.threshold1.rawValue = batterySettings.threshold2.rawValue - 1;
text = batterySettings.threshold1.rawValue.toString(); // Update displayed text
}
}
}

QGCColoredImage {
source: "/qmlimages/BatteryYellow.svg"
height: ScreenTools.defaultFontPixelHeight * 5
width: ScreenTools.defaultFontPixelWidth * 7
fillMode: Image.PreserveAspectFit
color: qgcPal.colorYellow
}

FactTextField {
id: threshold2Field
fact: batterySettings.threshold2
validator: IntValidator { bottom: 16 }
implicitWidth: ScreenTools.defaultFontPixelWidth * 6
Layout.leftMargin: -10
height: ScreenTools.defaultFontPixelHeight * 1.5
onEditingFinished: {
let newValue = parseInt(text);
if (newValue < batterySettings.threshold1.rawValue) {
batterySettings.threshold2.rawValue = newValue;
} else {
batterySettings.threshold2.rawValue = batterySettings.threshold1.rawValue - 1;
text = batterySettings.threshold2.rawValue.toString();
}
}
}

QGCColoredImage {
source: "/qmlimages/BatteryOrange.svg"
height: ScreenTools.defaultFontPixelHeight * 5
width: ScreenTools.defaultFontPixelWidth * 7
fillMode: Image.PreserveAspectFit
color: qgcPal.colorOrange
}

QGCLabel {
Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 2.5
text: qsTr("Low")
Layout.leftMargin: -13
}

QGCColoredImage {
source: "/qmlimages/BatteryCritical.svg"
height: ScreenTools.defaultFontPixelHeight * 5
width: ScreenTools.defaultFontPixelWidth * 7
fillMode: Image.PreserveAspectFit
color: qgcPal.colorRed
}

QGCLabel {
Layout.preferredWidth: ScreenTools.defaultFontPixelWidth * 3.5
text: qsTr("Critical")
Layout.leftMargin: -13
}

}



}
}
}
Expand Down
Loading
Loading