mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-04-25 10:08:33 +02:00
feat(bar): add battery and network widgets
This commit is contained in:
81
komorebi-bar/src/battery.rs
Normal file
81
komorebi-bar/src/battery.rs
Normal file
@@ -0,0 +1,81 @@
|
||||
use crate::widget::BarWidget;
|
||||
use starship_battery::units::ratio::percent;
|
||||
use starship_battery::Manager;
|
||||
use starship_battery::State;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BatteryConfig {
|
||||
pub enable: bool,
|
||||
}
|
||||
|
||||
impl From<BatteryConfig> for Battery {
|
||||
fn from(value: BatteryConfig) -> Self {
|
||||
let manager = Manager::new().unwrap();
|
||||
let mut last_state = vec![];
|
||||
let mut state = None;
|
||||
|
||||
if let Ok(mut batteries) = manager.batteries() {
|
||||
if let Some(Ok(first)) = batteries.nth(0) {
|
||||
let percentage = first.state_of_charge().get::<percent>();
|
||||
match first.state() {
|
||||
State::Charging => state = Some(BatteryState::Charging),
|
||||
State::Discharging => state = Some(BatteryState::Discharging),
|
||||
_ => {}
|
||||
}
|
||||
last_state.push(format!("{percentage}%"));
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
enable: value.enable,
|
||||
manager,
|
||||
last_state,
|
||||
state: state.unwrap_or(BatteryState::Discharging),
|
||||
last_updated: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum BatteryState {
|
||||
Charging,
|
||||
Discharging,
|
||||
}
|
||||
|
||||
pub struct Battery {
|
||||
pub enable: bool,
|
||||
manager: Manager,
|
||||
pub state: BatteryState,
|
||||
last_state: Vec<String>,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl BarWidget for Battery {
|
||||
fn output(&mut self) -> Vec<String> {
|
||||
let mut outputs = self.last_state.clone();
|
||||
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated) > Duration::from_secs(10) {
|
||||
outputs.clear();
|
||||
|
||||
if let Ok(mut batteries) = self.manager.batteries() {
|
||||
if let Some(Ok(first)) = batteries.nth(0) {
|
||||
let percentage = first.state_of_charge().get::<percent>();
|
||||
match first.state() {
|
||||
State::Charging => self.state = BatteryState::Charging,
|
||||
State::Discharging => self.state = BatteryState::Discharging,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
outputs.push(format!("{percentage}%"));
|
||||
}
|
||||
}
|
||||
|
||||
self.last_state.clone_from(&outputs);
|
||||
self.last_updated = now;
|
||||
}
|
||||
|
||||
outputs
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,23 @@
|
||||
mod battery;
|
||||
mod date;
|
||||
mod media;
|
||||
mod memory;
|
||||
mod network;
|
||||
mod storage;
|
||||
mod time;
|
||||
mod widget;
|
||||
|
||||
use crate::battery::Battery;
|
||||
use crate::battery::BatteryConfig;
|
||||
use crate::battery::BatteryState;
|
||||
use crate::date::Date;
|
||||
use crate::date::DateFormat;
|
||||
use crate::media::Media;
|
||||
use crate::media::MediaConfig;
|
||||
use crate::memory::Memory;
|
||||
use crate::memory::MemoryConfig;
|
||||
use crate::network::Network;
|
||||
use crate::network::NetworkConfig;
|
||||
use crate::storage::Storage;
|
||||
use crate::storage::StorageConfig;
|
||||
use crate::time::TimeFormat;
|
||||
@@ -71,6 +78,8 @@ pub struct Config {
|
||||
storage: StorageConfig,
|
||||
memory: MemoryConfig,
|
||||
media: MediaConfig,
|
||||
battery: BatteryConfig,
|
||||
network: NetworkConfig,
|
||||
}
|
||||
|
||||
fn main() -> eframe::Result<()> {
|
||||
@@ -91,6 +100,11 @@ fn main() -> eframe::Result<()> {
|
||||
storage: StorageConfig { enable: true },
|
||||
memory: MemoryConfig { enable: true },
|
||||
media: MediaConfig { enable: true },
|
||||
battery: BatteryConfig { enable: true },
|
||||
network: NetworkConfig {
|
||||
enable: true,
|
||||
show_data: true,
|
||||
},
|
||||
};
|
||||
|
||||
// TODO: ensure that config.monitor_index represents a valid komorebi monitor index
|
||||
@@ -199,6 +213,8 @@ struct Komobar {
|
||||
memory: Memory,
|
||||
storage: Storage,
|
||||
media: Media,
|
||||
battery: Battery,
|
||||
network: Network,
|
||||
}
|
||||
|
||||
impl Komobar {
|
||||
@@ -224,6 +240,8 @@ impl Komobar {
|
||||
memory: Memory::from(config.memory),
|
||||
storage: Storage::from(config.storage),
|
||||
media: Media::from(config.media),
|
||||
battery: Battery::from(config.battery),
|
||||
network: Network::from(config.network),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,6 +370,26 @@ impl eframe::App for Komobar {
|
||||
|
||||
// TODO: make the order configurable
|
||||
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||
if self.battery.enable {
|
||||
let battery_output = self.battery.output();
|
||||
if !battery_output.is_empty() {
|
||||
for battery in battery_output {
|
||||
let emoji = match self.battery.state {
|
||||
BatteryState::Charging => "⚡️",
|
||||
BatteryState::Discharging => "🔋",
|
||||
};
|
||||
|
||||
ui.add(
|
||||
Label::new(format!("{emoji} {battery}"))
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
);
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
}
|
||||
}
|
||||
|
||||
if self.time.enable {
|
||||
for time in self.time.output() {
|
||||
if ui
|
||||
@@ -388,6 +426,53 @@ impl eframe::App for Komobar {
|
||||
ui.add_space(10.0);
|
||||
}
|
||||
|
||||
if self.network.enable {
|
||||
let network_output = self.network.output();
|
||||
|
||||
if !network_output.is_empty() {
|
||||
match network_output.len() {
|
||||
1 => {
|
||||
if ui
|
||||
.add(
|
||||
Label::new(format!("📶 {}", network_output[0]))
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
if let Err(error) =
|
||||
Command::new("cmd.exe").args(["/C", "ncpa"]).spawn()
|
||||
{
|
||||
eprintln!("{}", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
if ui
|
||||
.add(
|
||||
Label::new(format!(
|
||||
"📶 {} - {}",
|
||||
network_output[0], network_output[1]
|
||||
))
|
||||
.selectable(false)
|
||||
.sense(Sense::click()),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
if let Err(error) =
|
||||
Command::new("cmd.exe").args(["/C", "ncpa"]).spawn()
|
||||
{
|
||||
eprintln!("{}", error)
|
||||
}
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
ui.add_space(10.0);
|
||||
}
|
||||
}
|
||||
|
||||
if self.memory.enable {
|
||||
for ram in self.memory.output() {
|
||||
if ui
|
||||
|
||||
87
komorebi-bar/src/network.rs
Normal file
87
komorebi-bar/src/network.rs
Normal file
@@ -0,0 +1,87 @@
|
||||
use crate::widget::BarWidget;
|
||||
use std::time::Duration;
|
||||
use std::time::Instant;
|
||||
use sysinfo::Networks;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct NetworkConfig {
|
||||
pub enable: bool,
|
||||
pub show_data: bool,
|
||||
}
|
||||
|
||||
impl From<NetworkConfig> for Network {
|
||||
fn from(value: NetworkConfig) -> Self {
|
||||
let mut last_state = vec![];
|
||||
let mut networks = Networks::new_with_refreshed_list();
|
||||
|
||||
if let Ok(interface) = netdev::get_default_interface() {
|
||||
if let Some(friendly_name) = interface.friendly_name {
|
||||
last_state.push(friendly_name.clone());
|
||||
|
||||
if value.show_data {
|
||||
networks.refresh();
|
||||
for (interface_name, data) in &networks {
|
||||
if friendly_name.eq(interface_name) {
|
||||
last_state.push(format!(
|
||||
"{} MB (down) / {} MB (up)",
|
||||
data.total_received() / 1024 / 1024,
|
||||
data.total_transmitted() / 1024 / 1024,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
enable: value.enable,
|
||||
last_state,
|
||||
networks,
|
||||
show_data: value.show_data,
|
||||
last_updated: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Network {
|
||||
pub enable: bool,
|
||||
pub show_data: bool,
|
||||
networks: Networks,
|
||||
last_state: Vec<String>,
|
||||
last_updated: Instant,
|
||||
}
|
||||
|
||||
impl BarWidget for Network {
|
||||
fn output(&mut self) -> Vec<String> {
|
||||
let mut outputs = self.last_state.clone();
|
||||
|
||||
let now = Instant::now();
|
||||
if now.duration_since(self.last_updated) > Duration::from_secs(10) {
|
||||
outputs.clear();
|
||||
|
||||
if let Ok(interface) = netdev::get_default_interface() {
|
||||
if let Some(friendly_name) = &interface.friendly_name {
|
||||
outputs.push(friendly_name.clone());
|
||||
|
||||
if self.show_data {
|
||||
self.networks.refresh();
|
||||
for (interface_name, data) in &self.networks {
|
||||
if friendly_name.eq(interface_name) {
|
||||
outputs.push(format!(
|
||||
"{} MB (down) / {} MB (up)",
|
||||
data.total_received() / 1024 / 1024,
|
||||
data.total_transmitted() / 1024 / 1024,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.last_state.clone_from(&outputs);
|
||||
self.last_updated = now;
|
||||
}
|
||||
|
||||
outputs
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user