From 18358efed80b0e754d9e9109500fa9a9aea0be87 Mon Sep 17 00:00:00 2001 From: LGUG2Z Date: Mon, 12 Aug 2024 19:02:48 -0700 Subject: [PATCH] feat(bar): add interactive layout and media widgets --- Cargo.lock | 1 + Cargo.toml | 4 +- komorebi-bar/Cargo.toml | 3 +- komorebi-bar/src/main.rs | 103 +++++++++++++++++++++++++++++++------- komorebi-bar/src/media.rs | 63 +++++++++++++++++++++++ 5 files changed, 153 insertions(+), 21 deletions(-) create mode 100644 komorebi-bar/src/media.rs diff --git a/Cargo.lock b/Cargo.lock index 5f81bcaa2..96679c8f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2374,6 +2374,7 @@ dependencies = [ "komorebi-client", "serde_json", "sysinfo", + "windows 0.54.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 2537946f7..8f6f51330 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,5 +43,7 @@ features = [ "Win32_UI_Shell_Common", "Win32_UI_WindowsAndMessaging", "Win32_System_SystemServices", - "Win32_System_WindowsProgramming" + "Win32_System_WindowsProgramming", + "Media", + "Media_Control" ] diff --git a/komorebi-bar/Cargo.toml b/komorebi-bar/Cargo.toml index f32f35dc4..d2a83ce92 100644 --- a/komorebi-bar/Cargo.toml +++ b/komorebi-bar/Cargo.toml @@ -13,4 +13,5 @@ chrono = "0.4" eframe = "0.28" serde_json = "1" sysinfo = "0.30" -crossbeam-channel = "0.5" \ No newline at end of file +crossbeam-channel = "0.5" +windows = { workspace = true } \ No newline at end of file diff --git a/komorebi-bar/src/main.rs b/komorebi-bar/src/main.rs index 45f01f08d..efd47fe19 100644 --- a/komorebi-bar/src/main.rs +++ b/komorebi-bar/src/main.rs @@ -1,4 +1,5 @@ mod date; +mod media; mod memory; mod storage; mod time; @@ -6,6 +7,8 @@ mod widget; 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::storage::Storage; @@ -15,12 +18,14 @@ use crate::widget::BarWidget; use crossbeam_channel::Receiver; use eframe::egui; use eframe::egui::Align; -use eframe::egui::CursorIcon; +use eframe::egui::Label; use eframe::egui::Layout; +use eframe::egui::Sense; use eframe::egui::ViewportBuilder; use eframe::egui::Visuals; use eframe::emath::Pos2; use eframe::emath::Vec2; +use komorebi_client::CycleDirection; use komorebi_client::SocketMessage; use std::io::BufReader; use std::io::Read; @@ -65,6 +70,7 @@ pub struct Config { date: Date, storage: StorageConfig, memory: MemoryConfig, + media: MediaConfig, } fn main() -> eframe::Result<()> { @@ -84,6 +90,7 @@ fn main() -> eframe::Result<()> { date: Date::new(true, DateFormat::DayDateMonthYear), storage: StorageConfig { enable: true }, memory: MemoryConfig { enable: true }, + media: MediaConfig { enable: true }, }; // TODO: ensure that config.monitor_index represents a valid komorebi monitor index @@ -178,12 +185,13 @@ struct Komobar { state_receiver: Receiver, selected_workspace: String, focused_window_title: String, - workspace_layout: String, + layout: String, workspaces: Vec, time: Time, date: Date, memory: Memory, storage: Storage, + media: Media, } impl Komobar { @@ -202,12 +210,13 @@ impl Komobar { state_receiver: rx, selected_workspace: String::new(), focused_window_title: String::new(), - workspace_layout: String::new(), + layout: String::new(), workspaces: vec![], time: config.time, date: config.date, memory: Memory::from(config.memory), storage: Storage::from(config.storage), + media: Media::from(config.media), } } } @@ -229,7 +238,7 @@ impl Komobar { } self.workspaces = workspaces; - self.workspace_layout = match monitor.workspaces()[focused_workspace_idx].layout() { + self.layout = match monitor.workspaces()[focused_workspace_idx].layout() { komorebi_client::Layout::Default(layout) => layout.to_string(), komorebi_client::Layout::Custom(_) => String::from("Custom"), }; @@ -241,6 +250,23 @@ impl Komobar { self.focused_window_title.clone_from(&title); } } + } else { + self.focused_window_title.clear(); + } + + if let Some(container) = monitor.workspaces()[focused_workspace_idx].monocle_container() + { + if let Some(window) = container.focused_window() { + if let Ok(title) = window.title() { + self.focused_window_title.clone_from(&title); + } + } + } + + if let Some(window) = monitor.workspaces()[focused_workspace_idx].maximized_window() { + if let Ok(title) = window.title() { + self.focused_window_title.clone_from(&title); + } } } } @@ -296,11 +322,23 @@ impl eframe::App for Komobar { } } - ui.label(&self.workspace_layout); + if ui + .add( + Label::new(&self.layout) + .selectable(false) + .sense(Sense::click()), + ) + .clicked() + { + komorebi_client::send_message(&SocketMessage::CycleLayout( + CycleDirection::Next, + )) + .unwrap(); + } ui.add_space(10.0); - ui.label(&self.focused_window_title); + ui.add(Label::new(&self.focused_window_title).selectable(false)); ui.add_space(10.0); }); @@ -311,8 +349,11 @@ impl eframe::App for Komobar { for time in self.time.output() { ctx.request_repaint(); if ui - .label(format!("🕐 {}", time)) - .on_hover_cursor(CursorIcon::default()) + .add( + Label::new(format!("🕐 {}", time)) + .selectable(false) + .sense(Sense::click()), + ) .clicked() { self.time.format.toggle() @@ -326,12 +367,15 @@ impl eframe::App for Komobar { if self.date.enable { for date in self.date.output() { if ui - .label(format!("📅 {}", date)) - .on_hover_cursor(CursorIcon::default()) + .add( + Label::new(format!("📅 {}", date)) + .selectable(false) + .sense(Sense::click()), + ) .clicked() { self.date.format.next() - }; + } } // TODO: make spacing configurable @@ -341,9 +385,11 @@ impl eframe::App for Komobar { if self.memory.enable { for ram in self.memory.output() { if ui - // TODO: make label configurable?? - .label(format!("🐏 {}", ram)) - .on_hover_cursor(CursorIcon::default()) + .add( + Label::new(format!("🐏 {}", ram)) + .selectable(false) + .sense(Sense::click()), + ) .clicked() { if let Err(error) = @@ -351,7 +397,7 @@ impl eframe::App for Komobar { { eprintln!("{}", error) } - }; + } } ui.add_space(10.0); @@ -360,9 +406,11 @@ impl eframe::App for Komobar { if self.storage.enable { for disk in self.storage.output() { if ui - // TODO: Make emoji configurable?? - .label(format!("🖴 {}", disk)) - .on_hover_cursor(CursorIcon::default()) + .add( + Label::new(format!("🖴 {}", disk)) + .selectable(false) + .sense(Sense::click()), + ) .clicked() { if let Err(error) = Command::new("cmd.exe") @@ -375,7 +423,24 @@ impl eframe::App for Komobar { { eprintln!("{}", error) } - }; + } + + ui.add_space(10.0); + } + } + + if self.media.enable { + for media in self.media.output() { + if ui + .add( + Label::new(format!("🎧 {media}")) + .selectable(false) + .sense(Sense::click()), + ) + .clicked() + { + self.media.toggle(); + } ui.add_space(10.0); } diff --git a/komorebi-bar/src/media.rs b/komorebi-bar/src/media.rs new file mode 100644 index 000000000..f94224358 --- /dev/null +++ b/komorebi-bar/src/media.rs @@ -0,0 +1,63 @@ +use crate::widget::BarWidget; +use windows::Media::Control::GlobalSystemMediaTransportControlsSessionManager; + +#[derive(Copy, Clone, Debug)] +pub struct MediaConfig { + pub enable: bool, +} + +impl From for Media { + fn from(value: MediaConfig) -> Self { + Self::new(value.enable) + } +} + +#[derive(Clone, Debug)] +pub struct Media { + pub enable: bool, + pub session_manager: GlobalSystemMediaTransportControlsSessionManager, +} + +impl Media { + pub fn new(enable: bool) -> Self { + Self { + enable, + session_manager: GlobalSystemMediaTransportControlsSessionManager::RequestAsync() + .unwrap() + .get() + .unwrap(), + } + } + + pub fn toggle(&self) { + if let Ok(session) = self.session_manager.GetCurrentSession() { + if let Ok(op) = session.TryTogglePlayPauseAsync() { + op.get().unwrap_or_default(); + } + } + } +} + +impl BarWidget for Media { + fn output(&mut self) -> Vec { + if let Ok(session) = self.session_manager.GetCurrentSession() { + if let Ok(operation) = session.TryGetMediaPropertiesAsync() { + if let Ok(properties) = operation.get() { + if let (Ok(artist), Ok(title)) = (properties.Artist(), properties.Title()) { + if artist.is_empty() { + return vec![format!("{title}")]; + } + + if title.is_empty() { + return vec![format!("{artist}")]; + } + + return vec![format!("{artist} - {title}")]; + } + } + } + } + + vec![] + } +}