Add BatteryWidget
- Working battery icon - Read from `sys` files - Calculate values in Rust and pass it to Slint
This commit is contained in:
parent
48a116c547
commit
b0eaa42063
107
src/widgets/batterywidget.rs
Normal file
107
src/widgets/batterywidget.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
use super::common::run_cmd;
|
||||||
|
use crate::TopBar;
|
||||||
|
use slint::{ComponentHandle, SharedString, Timer, TimerMode};
|
||||||
|
use std::{fs, time::Duration};
|
||||||
|
|
||||||
|
// Read and trim a file
|
||||||
|
fn read_sys(path: &str) -> String {
|
||||||
|
fs::read_to_string(path)
|
||||||
|
.unwrap_or_default()
|
||||||
|
.trim()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert battery state strings into normalized tokens
|
||||||
|
fn normalize_status(s: &str) -> String {
|
||||||
|
s.to_lowercase()
|
||||||
|
.chars()
|
||||||
|
.filter(char::is_ascii_alphabetic)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map numeric capacity → level tags
|
||||||
|
fn capacity_level(cap: u8) -> String {
|
||||||
|
match cap {
|
||||||
|
0..=10 => "critical".into(),
|
||||||
|
11..=25 => "low".into(),
|
||||||
|
26..=75 => "normal".into(),
|
||||||
|
76..=95 => "high".into(),
|
||||||
|
_ => "full".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Choose icon from status + level
|
||||||
|
fn select_icon(status: &str, level: &str) -> SharedString {
|
||||||
|
let icon = match status {
|
||||||
|
"charging" => match level {
|
||||||
|
"critical" => "",
|
||||||
|
"low" => "",
|
||||||
|
"normal" => "",
|
||||||
|
"high" => "",
|
||||||
|
"full" => "",
|
||||||
|
_ => "",
|
||||||
|
},
|
||||||
|
"discharging" => match level {
|
||||||
|
"critical" => "",
|
||||||
|
"low" => "",
|
||||||
|
"normal" => "",
|
||||||
|
"high" => "",
|
||||||
|
"full" => "",
|
||||||
|
_ => "",
|
||||||
|
},
|
||||||
|
"notcharging" => "",
|
||||||
|
"full" => "",
|
||||||
|
_ => "", // unknown fallback
|
||||||
|
};
|
||||||
|
|
||||||
|
SharedString::from(icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main battery updater logic
|
||||||
|
fn start_battery_updater(ui: &TopBar) {
|
||||||
|
let weak = ui.as_weak();
|
||||||
|
|
||||||
|
// Timer fires every second (battery can change quickly)
|
||||||
|
let timer = Box::leak(Box::new(Timer::default()));
|
||||||
|
|
||||||
|
timer.start(TimerMode::Repeated, Duration::from_secs(1), move || {
|
||||||
|
if let Some(ui) = weak.upgrade() {
|
||||||
|
// Read sysfs values
|
||||||
|
let cap_str = read_sys("/sys/class/power_supply/BAT0/capacity");
|
||||||
|
let status_str = read_sys("/sys/class/power_supply/BAT0/status");
|
||||||
|
let level_str = read_sys("/sys/class/power_supply/BAT0/capacity_level");
|
||||||
|
|
||||||
|
// Normalize values
|
||||||
|
let status = normalize_status(&status_str);
|
||||||
|
let cap: u8 = cap_str.parse().unwrap_or(0);
|
||||||
|
let level = normalize_status(&level_str); // low, normal, full, etc.
|
||||||
|
let level_calc = capacity_level(cap); // fallback level
|
||||||
|
|
||||||
|
// Prefer kernel level if valid; otherwise use cap-based level
|
||||||
|
let final_level = if level.is_empty() { level_calc } else { level };
|
||||||
|
|
||||||
|
// Select icon
|
||||||
|
let icon = select_icon(&status, &final_level);
|
||||||
|
|
||||||
|
// Update Slint properties
|
||||||
|
ui.set_battery_status(status.into());
|
||||||
|
ui.set_battery_capacity(cap.into());
|
||||||
|
ui.set_battery_capacity_level(final_level.into());
|
||||||
|
ui.set_battery_icon(icon);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public entry: connect click callback + start updater
|
||||||
|
pub fn install(ui: &TopBar) {
|
||||||
|
let weak = ui.as_weak();
|
||||||
|
|
||||||
|
// Click → open battery monitor
|
||||||
|
ui.on_show_battery(move || {
|
||||||
|
if weak.upgrade().is_some() {
|
||||||
|
run_cmd("xclock"); // Fix later
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
start_battery_updater(ui);
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
mod batterywidget;
|
||||||
mod common;
|
mod common;
|
||||||
mod datewidget;
|
mod datewidget;
|
||||||
mod timewidget;
|
mod timewidget;
|
||||||
@ -7,8 +8,8 @@ use crate::TopBar;
|
|||||||
pub fn install_callbacks(ui: &TopBar) {
|
pub fn install_callbacks(ui: &TopBar) {
|
||||||
timewidget::install(ui);
|
timewidget::install(ui);
|
||||||
datewidget::install(ui);
|
datewidget::install(ui);
|
||||||
|
batterywidget::install(ui);
|
||||||
|
|
||||||
// In the future:
|
// In the future:
|
||||||
// networkwidget::install(ui);
|
// networkwidget::install(ui);
|
||||||
// batterywidget::install(ui);
|
|
||||||
}
|
}
|
||||||
|
|||||||
35
ui/battery-widget.slint
Normal file
35
ui/battery-widget.slint
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
export component BatteryWidget {
|
||||||
|
in property <int> bar_height;
|
||||||
|
in property <string> battery_status; // charging / discharging / notcharging / unknown
|
||||||
|
in property <int> battery_capacity; // numeric percentage
|
||||||
|
in property <string> battery_capacity_level; // critical / low / normal / high / full
|
||||||
|
in property <string> battery_icon;
|
||||||
|
|
||||||
|
callback show_battery();
|
||||||
|
|
||||||
|
height: bar_height * 1px;
|
||||||
|
width: bar_height * 1px;
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
background: touch_area.pressed ? #4a5f70
|
||||||
|
: touch_area.has-hover ? #6d8a4d
|
||||||
|
: #8e9162;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
HorizontalLayout {
|
||||||
|
padding-left: 3px;
|
||||||
|
padding-right: 3px;
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: battery_icon;
|
||||||
|
color: white;
|
||||||
|
vertical-alignment: center;
|
||||||
|
horizontal-alignment: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
touch_area := TouchArea {
|
||||||
|
clicked => { show_battery(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import { TimeWidget } from "time-widget.slint";
|
import { TimeWidget } from "time-widget.slint";
|
||||||
import { DateWidget } from "date-widget.slint";
|
import { DateWidget } from "date-widget.slint";
|
||||||
|
import { BatteryWidget } from "battery-widget.slint";
|
||||||
|
|
||||||
export component TopBar inherits Window {
|
export component TopBar inherits Window {
|
||||||
|
|
||||||
@ -17,6 +18,14 @@ export component TopBar inherits Window {
|
|||||||
in-out property <string> date_text;
|
in-out property <string> date_text;
|
||||||
callback show_calendar();
|
callback show_calendar();
|
||||||
|
|
||||||
|
// Battery widget
|
||||||
|
in property <string> battery_status;
|
||||||
|
in property <int> battery_capacity;
|
||||||
|
in property <string> battery_capacity_level;
|
||||||
|
in property <string> battery_icon;
|
||||||
|
callback show_battery();
|
||||||
|
|
||||||
|
|
||||||
title: "chocobar";
|
title: "chocobar";
|
||||||
width: bar_width *1px;
|
width: bar_width *1px;
|
||||||
height: bar_height *1px;
|
height: bar_height *1px;
|
||||||
@ -61,6 +70,15 @@ export component TopBar inherits Window {
|
|||||||
HorizontalLayout {
|
HorizontalLayout {
|
||||||
alignment: end; // Right-align
|
alignment: end; // Right-align
|
||||||
|
|
||||||
|
BatteryWidget {
|
||||||
|
bar_height: root.bar_height;
|
||||||
|
battery_status: root.battery_status;
|
||||||
|
battery_capacity: root.battery_capacity;
|
||||||
|
battery_capacity_level: root.battery_capacity_level;
|
||||||
|
battery_icon: root.battery_icon;
|
||||||
|
show_battery => root.show_battery();
|
||||||
|
}
|
||||||
|
|
||||||
DateWidget {
|
DateWidget {
|
||||||
// Get from root
|
// Get from root
|
||||||
date_text: root.date_text;
|
date_text: root.date_text;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user