This commit is contained in:
2026-09-16 12:12:57 +01:00
parent fe07022015
commit c1a67c9335
+27 -14
View File
@@ -4,8 +4,8 @@ use iced::widget::{column, container, mouse_area, row, stack, text};
use iced::{alignment, Border, Color, Element, Length, Task, Theme};
use common::{
BarModule, BatteryAction, BatteryEvent, BatteryMsg, BatteryStatus, Endpoint, ModuleEffect,
PopupSettings, Service, Wire,
pill, BarModule, BatteryAction, BatteryEvent, BatteryMsg, BatteryStatus, Endpoint,
ModuleEffect, PopupSettings, Service, Wire,
};
use serde::Deserialize;
use toml::Table;
@@ -15,6 +15,8 @@ const SHELL_W: f32 = 28.0;
const SHELL_H: f32 = 14.0;
const BORDER_W: f32 = 1.5;
const PAD: f32 = 2.0;
/// Charge at or below this shows the warning color while discharging.
const LOW_PCT: i32 = 20;
pub struct Battery {
percent: Option<i32>,
@@ -72,20 +74,22 @@ fn fmt_duration(secs: u64) -> String {
}
}
/// Icon ink: the theme's success green while charging, bar text otherwise.
fn ink(status: BatteryStatus, theme: &Theme) -> Color {
/// Icon ink: success green while charging, danger red when low and
/// discharging, bar text otherwise.
fn ink(status: BatteryStatus, percent: Option<i32>, theme: &Theme) -> Color {
let palette = theme.palette();
match status {
BatteryStatus::Charging => palette.success,
BatteryStatus::Discharging if percent.is_some_and(|p| p <= LOW_PCT) => palette.danger,
_ => palette.text,
}
}
/// Transparent shell with a rounded outline tinted by charge state.
fn shell_style(status: BatteryStatus) -> impl Fn(&Theme) -> container::Style {
fn shell_style(status: BatteryStatus, percent: Option<i32>) -> impl Fn(&Theme) -> container::Style {
move |theme: &Theme| container::Style {
border: Border {
color: ink(status, theme),
color: ink(status, percent, theme),
width: BORDER_W,
radius: 4.0.into(),
},
@@ -95,7 +99,11 @@ fn shell_style(status: BatteryStatus) -> impl Fn(&Theme) -> container::Style {
/// Solid fill (and nub cap) tinted by charge state. `dim` renders the
/// Android-style muted fill that sits behind the in-icon number.
fn solid_style(status: BatteryStatus, dim: bool) -> impl Fn(&Theme) -> container::Style {
fn solid_style(
status: BatteryStatus,
percent: Option<i32>,
dim: bool,
) -> impl Fn(&Theme) -> container::Style {
move |theme: &Theme| {
let palette = theme.palette();
let background = if dim {
@@ -104,7 +112,7 @@ fn solid_style(status: BatteryStatus, dim: bool) -> impl Fn(&Theme) -> container
..palette.text
}
} else {
ink(status, theme)
ink(status, percent, theme)
};
container::Style {
background: Some(background.into()),
@@ -134,12 +142,12 @@ impl Battery {
container(text(""))
.width(Length::Fixed(self.fill_w()))
.height(Length::Fill)
.style(solid_style(status, self.numbers_in_icon)),
.style(solid_style(status, self.percent, self.numbers_in_icon)),
)
.width(Length::Fixed(SHELL_W))
.height(Length::Fixed(SHELL_H))
.padding(PAD)
.style(shell_style(status));
.style(shell_style(status, self.percent));
// Optional percentage readout centered over the fill, in the frame's
// light color — legible on the dimmed fill, like Android.
let shell: Element<'_, Wire> = match (self.numbers_in_icon, self.percent) {
@@ -167,7 +175,7 @@ impl Battery {
container(text(""))
.width(Length::Fixed(2.5))
.height(Length::Fixed(6.0))
.style(solid_style(status, false)),
.style(solid_style(status, self.percent, false)),
]
.spacing(2)
.align_y(iced::Alignment::Center);
@@ -191,15 +199,20 @@ impl Battery {
BatteryAction::OpenPopup,
)),
)
.style(pill)
.id(iced::widget::Id::from(self.id()))
.into()
}
/// Popup surface: charge, status, and time to full/empty.
fn popup(&self) -> Element<'_, Wire> {
let charge = match self.percent {
Some(percent) => text(format!("{percent}%")).size(44),
None => text("--").size(44),
let (status, percent) = (self.status, self.percent);
let warn = move |theme: &Theme| text::Style {
color: Some(ink(status, percent, theme)),
};
let charge = match percent {
Some(percent) => text(format!("{percent}%")).size(44).style(warn),
None => text("--").size(44).style(warn),
};
let status = text(match self.status {
BatteryStatus::Charging => "Charging".to_string(),