Add: Volume widget
- Use `amixer` command - Comment out bg color lines to use default vals
This commit is contained in:
parent
3dca171063
commit
fa206d7f42
@ -12,7 +12,6 @@ dirs = "6.0.0"
|
|||||||
i-slint-backend-winit = { version = "1.14.1", features = ["x11"] }
|
i-slint-backend-winit = { version = "1.14.1", features = ["x11"] }
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
slint = { version = "1.14.1", features = ["backend-winit"] }
|
slint = { version = "1.14.1", features = ["backend-winit"] }
|
||||||
tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread", "time"] }
|
|
||||||
toml = "0.9.8"
|
toml = "0.9.8"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|||||||
@ -2,6 +2,7 @@ mod batterywidget;
|
|||||||
mod common;
|
mod common;
|
||||||
mod datewidget;
|
mod datewidget;
|
||||||
mod timewidget;
|
mod timewidget;
|
||||||
|
mod volumewidget;
|
||||||
|
|
||||||
use crate::TopBar;
|
use crate::TopBar;
|
||||||
|
|
||||||
@ -9,6 +10,7 @@ pub fn install_callbacks(ui: &TopBar) {
|
|||||||
timewidget::install(ui);
|
timewidget::install(ui);
|
||||||
datewidget::install(ui);
|
datewidget::install(ui);
|
||||||
batterywidget::install(ui);
|
batterywidget::install(ui);
|
||||||
|
volumewidget::install(ui);
|
||||||
|
|
||||||
// In the future:
|
// In the future:
|
||||||
// networkwidget::install(ui);
|
// networkwidget::install(ui);
|
||||||
|
|||||||
97
src/widgets/volumewidget.rs
Normal file
97
src/widgets/volumewidget.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
use super::common::run_cmd;
|
||||||
|
use crate::TopBar;
|
||||||
|
use slint::{ComponentHandle, SharedString, Timer, TimerMode};
|
||||||
|
use std::process::Command;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
// Run `amixer get Master` and return stdout as String
|
||||||
|
fn read_amixer() -> String {
|
||||||
|
String::from_utf8(
|
||||||
|
Command::new("amixer")
|
||||||
|
.args(["get", "Master"])
|
||||||
|
.output()
|
||||||
|
.unwrap()
|
||||||
|
.stdout,
|
||||||
|
)
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse volume percentage (0–100)
|
||||||
|
fn parse_volume(amixer_out: &str) -> u8 {
|
||||||
|
// Looks for "[42%]" pattern
|
||||||
|
for token in amixer_out.split_whitespace() {
|
||||||
|
if let Some(stripped) = token.strip_prefix('[').and_then(|t| t.strip_suffix("%]"))
|
||||||
|
&& let Ok(val) = stripped.parse::<u8>()
|
||||||
|
{
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect mute: `[off]` means muted
|
||||||
|
fn is_muted(amixer_out: &str) -> bool {
|
||||||
|
amixer_out.contains("[off]")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select simple volume icon from level
|
||||||
|
fn select_icon(vol: u8, muted: bool) -> SharedString {
|
||||||
|
let icon = if muted {
|
||||||
|
"" // mute
|
||||||
|
} else if vol == 0 {
|
||||||
|
"" // empty
|
||||||
|
} else if vol <= 30 {
|
||||||
|
"" // low
|
||||||
|
} else if vol <= 70 {
|
||||||
|
"" // medium
|
||||||
|
} else {
|
||||||
|
"" // high
|
||||||
|
};
|
||||||
|
|
||||||
|
SharedString::from(icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build tooltip text
|
||||||
|
fn make_tooltip(vol: u8, muted: bool) -> SharedString {
|
||||||
|
if muted {
|
||||||
|
SharedString::from(format!("Volume: {vol}%"))
|
||||||
|
} else {
|
||||||
|
SharedString::from("Volume: {vol}%")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Timer loop: update volume every second
|
||||||
|
fn start_volume_updater(ui: &TopBar) {
|
||||||
|
let weak = ui.as_weak();
|
||||||
|
|
||||||
|
let timer = Box::leak(Box::new(Timer::default()));
|
||||||
|
timer.start(TimerMode::Repeated, Duration::from_secs(1), move || {
|
||||||
|
if let Some(ui) = weak.upgrade() {
|
||||||
|
let out = read_amixer();
|
||||||
|
//println!("{out}");
|
||||||
|
let vol = parse_volume(&out);
|
||||||
|
let muted = is_muted(&out);
|
||||||
|
|
||||||
|
// Compute values
|
||||||
|
let icon = select_icon(vol, muted);
|
||||||
|
let tooltip = make_tooltip(vol, muted);
|
||||||
|
|
||||||
|
// Push to UI
|
||||||
|
ui.set_volume_icon(icon);
|
||||||
|
ui.set_volume_tooltip(tooltip);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public entry: connect callback + start updater
|
||||||
|
pub fn install(ui: &TopBar) {
|
||||||
|
let weak = ui.as_weak();
|
||||||
|
|
||||||
|
ui.on_show_volume(move || {
|
||||||
|
if weak.upgrade().is_some() {
|
||||||
|
run_cmd("xclock"); // placeholder
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
start_volume_updater(ui);
|
||||||
|
}
|
||||||
@ -21,13 +21,22 @@ export component TopBar inherits Window {
|
|||||||
// Battery widget
|
// Battery widget
|
||||||
in property <string> battery_tooltip;
|
in property <string> battery_tooltip;
|
||||||
in property <string> battery_icon;
|
in property <string> battery_icon;
|
||||||
in property <color> battery_icon_color: #ffffff;
|
in property <color> battery_icon_color;
|
||||||
in property <color> battery_bg_normal: #8e9162;
|
/*in property <color> battery_bg_normal; // Enable later. Use defaults now.
|
||||||
in property <color> battery_bg_hover: #6d8a4d;
|
in property <color> battery_bg_hover;
|
||||||
in property <color> battery_bg_clicked: #4a5f70;
|
in property <color> battery_bg_clicked;*/
|
||||||
callback show_battery();
|
callback show_battery();
|
||||||
|
|
||||||
|
|
||||||
|
// Volume widget
|
||||||
|
in property <string> volume_tooltip;
|
||||||
|
in property <string> volume_icon;
|
||||||
|
/*in property <color> volume_icon_color;
|
||||||
|
in property <color> volume_bg_normal;
|
||||||
|
in property <color> volume_bg_hover;
|
||||||
|
in property <color> volume_bg_clicked;*/
|
||||||
|
callback show_volume();
|
||||||
|
|
||||||
title: "chocobar";
|
title: "chocobar";
|
||||||
width: bar_width *1px;
|
width: bar_width *1px;
|
||||||
height: bar_height *1px;
|
height: bar_height *1px;
|
||||||
@ -71,16 +80,28 @@ export component TopBar inherits Window {
|
|||||||
|
|
||||||
HorizontalLayout {
|
HorizontalLayout {
|
||||||
alignment: end; // Right-align
|
alignment: end; // Right-align
|
||||||
|
|
||||||
|
// SquareIconWidget - Volume
|
||||||
|
SquareIconWidget {
|
||||||
|
bar_height: root.bar_height;
|
||||||
|
icon_text: root.volume_icon;
|
||||||
|
tooltip_text: root.volume_tooltip;
|
||||||
|
/*icon_color: root.volume_icon_color;
|
||||||
|
bg_normal: root.volume_bg_normal;
|
||||||
|
bg_hover: root.volume_bg_hover;
|
||||||
|
bg_clicked: root.volume_bg_clicked;*/
|
||||||
|
square_btn_callback => root.show_volume();
|
||||||
|
}
|
||||||
|
|
||||||
// SquareIconWidget - Battery
|
// SquareIconWidget - Battery
|
||||||
SquareIconWidget {
|
SquareIconWidget {
|
||||||
bar_height: root.bar_height;
|
bar_height: root.bar_height;
|
||||||
icon_text: root.battery_icon;
|
icon_text: root.battery_icon;
|
||||||
tooltip_text: root.battery_tooltip;
|
tooltip_text: root.battery_tooltip;
|
||||||
icon_color: root.battery_icon_color;
|
icon_color: root.battery_icon_color;
|
||||||
bg_normal: root.battery_bg_normal;
|
/*bg_normal: root.battery_bg_normal;
|
||||||
bg_hover: root.battery_bg_hover;
|
bg_hover: root.battery_bg_hover;
|
||||||
bg_clicked: root.battery_bg_clicked;
|
bg_clicked: root.battery_bg_clicked;*/
|
||||||
square_btn_callback => root.show_battery();
|
square_btn_callback => root.show_battery();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user