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. //! Battery module: Android-style battery icon fed by the battery service.
use iced::widget::{container, row, text}; use iced::widget::{container, row, stack, text};
use iced::{Border, Color, Element, Length, Task, Theme}; use iced::{alignment, Border, Color, Element, Length, Task, Theme};
use common::{BarModule, BatteryEvent, BatteryMsg, BatteryStatus, ModuleEffect, Service, Wire}; use common::{BarModule, BatteryEvent, BatteryMsg, BatteryStatus, ModuleEffect, Service, Wire};
use serde::Deserialize;
use toml::Table;
/// Icon shell size (logical px); fill scales inside it. /// Icon shell size (logical px); fill scales inside it.
const SHELL_W: f32 = 28.0; const SHELL_W: f32 = 28.0;
@@ -14,13 +16,27 @@ const PAD: f32 = 2.0;
pub struct Battery { pub struct Battery {
percent: Option<i32>, percent: Option<i32>,
status: BatteryStatus, 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 { impl Battery {
pub fn new() -> Self { pub fn new(config: Option<Table>) -> Self {
Self { Self {
percent: None, percent: None,
status: BatteryStatus::default(), 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> { fn view(&self, _window_id: Option<iced::window::Id>) -> Element<'_, Wire> {
let status = self.status; let status = self.status;
let icon = row![ let shell = container(
container(
container(text("")) container(text(""))
.width(Length::Fixed(self.fill_w())) .width(Length::Fixed(self.fill_w()))
.height(Length::Fill) .height(Length::Fill)
@@ -85,7 +100,27 @@ impl BarModule for Battery {
.width(Length::Fixed(SHELL_W)) .width(Length::Fixed(SHELL_W))
.height(Length::Fixed(SHELL_H)) .height(Length::Fixed(SHELL_H))
.padding(PAD) .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. // Nub cap on the right edge.
container(text("")) container(text(""))
.width(Length::Fixed(2.5)) .width(Length::Fixed(2.5))
@@ -94,11 +129,19 @@ impl BarModule for Battery {
] ]
.spacing(2) .spacing(2)
.align_y(iced::Alignment::Center); .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) .spacing(6)
.align_y(iced::Alignment::Center) .align_y(iced::Alignment::Center)
.into() .into()
} }
}
fn update(&mut self, msg: Wire) -> Task<ModuleEffect> { fn update(&mut self, msg: Wire) -> Task<ModuleEffect> {
match msg.downcast::<BatteryEvent>() { 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>> { pub fn all(module_config: Table) -> Vec<Box<dyn BarModule>> {
vec![ vec![
Box::new(audio::Audio::new()), 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(clock::Clock::new(Some(module_config.clone()))),
Box::new(weather::WeatherModule::default()), Box::new(weather::WeatherModule::default()),
Box::new(workspaces::Workspaces::new(Some(module_config))), Box::new(workspaces::Workspaces::new(Some(module_config))),