battery module test

This commit is contained in:
2026-09-16 11:26:05 +01:00
parent 63e1c0ee4c
commit 2bb7b31b76
2 changed files with 88 additions and 12 deletions
+85 -12
View File
@@ -1,17 +1,69 @@
//! Battery module: prints the charge percentage from the battery service.
//! Battery module: Android-style battery icon fed by the battery service.
use iced::widget::text;
use iced::{Element, Task};
use iced::widget::{container, row, text};
use iced::{Border, Color, Element, Length, Task, Theme};
use common::{BarModule, BatteryEvent, BatteryMsg, ModuleEffect, Service, Wire};
use common::{BarModule, BatteryEvent, BatteryMsg, BatteryStatus, ModuleEffect, Service, Wire};
/// Icon shell size (logical px); fill scales inside it.
const SHELL_W: f32 = 28.0;
const SHELL_H: f32 = 14.0;
const BORDER_W: f32 = 1.5;
const PAD: f32 = 2.0;
pub struct Battery {
percent: Option<i32>,
status: BatteryStatus,
}
impl Battery {
pub fn new() -> Self {
Self { percent: None }
Self {
percent: None,
status: BatteryStatus::default(),
}
}
/// Fill width for the level, leaving a sliver visible below 100%.
fn fill_w(&self) -> f32 {
match self.percent {
Some(p) => ((SHELL_W - 2.0 * (BORDER_W + PAD)) * p as f32 / 100.0)
.clamp(0.0, SHELL_W)
.max(if p > 0 { 2.0 } else { 0.0 }),
None => 0.0,
}
}
}
/// Icon ink: green while charging, white otherwise.
fn ink(status: BatteryStatus) -> Color {
match status {
BatteryStatus::Charging => Color::from_rgb8(0x76, 0xFF, 0x03),
_ => Color::WHITE,
}
}
/// Transparent shell with a rounded outline tinted by charge state.
fn shell_style(status: BatteryStatus) -> impl Fn(&Theme) -> container::Style {
move |_theme: &Theme| container::Style {
border: Border {
color: ink(status),
width: BORDER_W,
radius: 4.0.into(),
},
..container::Style::default()
}
}
/// Solid fill (and nub cap) tinted by charge state.
fn solid_style(status: BatteryStatus) -> impl Fn(&Theme) -> container::Style {
move |_theme: &Theme| container::Style {
background: Some(ink(status).into()),
border: Border {
radius: 2.0.into(),
..Default::default()
},
..container::Style::default()
}
}
@@ -21,16 +73,37 @@ impl BarModule for Battery {
}
fn view(&self, _window_id: Option<iced::window::Id>) -> Element<'_, Wire> {
match self.percent {
Some(percent) => text(format!("{percent}%")).into(),
None => text("--").into(),
}
let status = self.status;
let icon = row![
container(
container(text(""))
.width(Length::Fixed(self.fill_w()))
.height(Length::Fill)
.style(solid_style(status)),
)
.width(Length::Fixed(SHELL_W))
.height(Length::Fixed(SHELL_H))
.padding(PAD)
.style(shell_style(status)),
// Nub cap on the right edge.
container(text(""))
.width(Length::Fixed(2.5))
.height(Length::Fixed(6.0))
.style(solid_style(status)),
]
.spacing(2)
.align_y(iced::Alignment::Center);
row![icon]
.spacing(6)
.align_y(iced::Alignment::Center)
.into()
}
fn update(&mut self, msg: Wire) -> Task<ModuleEffect> {
if let Some(BatteryEvent::PercentageChanged(percent)) = msg.downcast::<BatteryEvent>()
{
self.percent = Some(*percent);
match msg.downcast::<BatteryEvent>() {
Some(BatteryEvent::PercentageChanged(percent)) => self.percent = Some(*percent),
Some(BatteryEvent::StatusChanged(status)) => self.status = *status,
_ => {}
}
Task::none()
}