This commit is contained in:
2026-09-16 11:32:50 +01:00
parent 4d586d5137
commit 7bf6f989dc
2 changed files with 61 additions and 18 deletions
+50 -7
View File
@@ -1,9 +1,11 @@
//! Battery module: Android-style battery icon fed by the battery service.
use iced::widget::{container, row, text};
use iced::{Border, Color, Element, Length, Task, Theme};
use iced::widget::{container, row, stack, text};
use iced::{alignment, Border, Color, Element, Length, Task, Theme};
use common::{BarModule, BatteryEvent, BatteryMsg, BatteryStatus, ModuleEffect, Service, Wire};
use serde::Deserialize;
use toml::Table;
/// Icon shell size (logical px); fill scales inside it.
const SHELL_W: f32 = 28.0;
@@ -14,13 +16,27 @@ const PAD: f32 = 2.0;
pub struct Battery {
percent: Option<i32>,
status: BatteryStatus,
numbers_in_icon: bool,
}
/// `module.battery` table; everything optional.
#[derive(Deserialize, Default)]
struct BatteryConfig {
/// Render the percentage as a number centered inside the icon shell.
#[serde(default)]
numbers_in_icon: bool,
}
impl Battery {
pub fn new() -> Self {
pub fn new(config: Option<Table>) -> Self {
Self {
percent: None,
status: BatteryStatus::default(),
numbers_in_icon: config
.and_then(|table| table.get("battery").cloned())
.and_then(|battery| battery.try_into().ok())
.map(|cfg: BatteryConfig| cfg.numbers_in_icon)
.unwrap_or_default(),
}
}
@@ -75,8 +91,7 @@ impl BarModule for Battery {
fn view(&self, _window_id: Option<iced::window::Id>) -> Element<'_, Wire> {
let status = self.status;
let icon = row![
container(
let shell = container(
container(text(""))
.width(Length::Fixed(self.fill_w()))
.height(Length::Fill)
@@ -85,7 +100,27 @@ impl BarModule for Battery {
.width(Length::Fixed(SHELL_W))
.height(Length::Fixed(SHELL_H))
.padding(PAD)
.style(shell_style(status)),
.style(shell_style(status));
// Optional percentage readout centered over the fill, in the bar's
// background color — legible on the light fill, dim where unfilled.
let shell: Element<'_, Wire> = match (self.numbers_in_icon, self.percent) {
(true, Some(percent)) => stack![
shell,
container(
text(format!("{percent}")).size(9).style(|theme: &Theme| text::Style {
color: Some(theme.palette().background),
}),
)
.width(Length::Fill)
.height(Length::Fill)
.align_x(alignment::Horizontal::Center)
.align_y(alignment::Vertical::Center),
]
.into(),
_ => shell.into(),
};
let icon = row![
shell,
// Nub cap on the right edge.
container(text(""))
.width(Length::Fixed(2.5))
@@ -94,11 +129,19 @@ impl BarModule for Battery {
]
.spacing(2)
.align_y(iced::Alignment::Center);
row![icon]
if self.numbers_in_icon {
icon.into()
} else {
let label = match self.percent {
Some(percent) => text(format!("{percent}%")),
None => text("--"),
};
row![icon, label]
.spacing(6)
.align_y(iced::Alignment::Center)
.into()
}
}
fn update(&mut self, msg: Wire) -> Task<ModuleEffect> {
match msg.downcast::<BatteryEvent>() {
+1 -1
View File
@@ -16,7 +16,7 @@ pub use weather::*;
pub fn all(module_config: Table) -> Vec<Box<dyn BarModule>> {
vec![
Box::new(audio::Audio::new()),
Box::new(battery::Battery::new()),
Box::new(battery::Battery::new(Some(module_config.clone()))),
Box::new(clock::Clock::new(Some(module_config.clone()))),
Box::new(weather::WeatherModule::default()),
Box::new(workspaces::Workspaces::new(Some(module_config))),