mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-07-14 17:12:58 +02:00
feat(bar): add interactive layout and media widgets
This commit is contained in:
Generated
+1
@@ -2374,6 +2374,7 @@ dependencies = [
|
|||||||
"komorebi-client",
|
"komorebi-client",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
|
"windows 0.54.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
+3
-1
@@ -43,5 +43,7 @@ features = [
|
|||||||
"Win32_UI_Shell_Common",
|
"Win32_UI_Shell_Common",
|
||||||
"Win32_UI_WindowsAndMessaging",
|
"Win32_UI_WindowsAndMessaging",
|
||||||
"Win32_System_SystemServices",
|
"Win32_System_SystemServices",
|
||||||
"Win32_System_WindowsProgramming"
|
"Win32_System_WindowsProgramming",
|
||||||
|
"Media",
|
||||||
|
"Media_Control"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ chrono = "0.4"
|
|||||||
eframe = "0.28"
|
eframe = "0.28"
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
sysinfo = "0.30"
|
sysinfo = "0.30"
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
|
windows = { workspace = true }
|
||||||
+84
-19
@@ -1,4 +1,5 @@
|
|||||||
mod date;
|
mod date;
|
||||||
|
mod media;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod storage;
|
mod storage;
|
||||||
mod time;
|
mod time;
|
||||||
@@ -6,6 +7,8 @@ mod widget;
|
|||||||
|
|
||||||
use crate::date::Date;
|
use crate::date::Date;
|
||||||
use crate::date::DateFormat;
|
use crate::date::DateFormat;
|
||||||
|
use crate::media::Media;
|
||||||
|
use crate::media::MediaConfig;
|
||||||
use crate::memory::Memory;
|
use crate::memory::Memory;
|
||||||
use crate::memory::MemoryConfig;
|
use crate::memory::MemoryConfig;
|
||||||
use crate::storage::Storage;
|
use crate::storage::Storage;
|
||||||
@@ -15,12 +18,14 @@ use crate::widget::BarWidget;
|
|||||||
use crossbeam_channel::Receiver;
|
use crossbeam_channel::Receiver;
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::CursorIcon;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::Layout;
|
use eframe::egui::Layout;
|
||||||
|
use eframe::egui::Sense;
|
||||||
use eframe::egui::ViewportBuilder;
|
use eframe::egui::ViewportBuilder;
|
||||||
use eframe::egui::Visuals;
|
use eframe::egui::Visuals;
|
||||||
use eframe::emath::Pos2;
|
use eframe::emath::Pos2;
|
||||||
use eframe::emath::Vec2;
|
use eframe::emath::Vec2;
|
||||||
|
use komorebi_client::CycleDirection;
|
||||||
use komorebi_client::SocketMessage;
|
use komorebi_client::SocketMessage;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
@@ -65,6 +70,7 @@ pub struct Config {
|
|||||||
date: Date,
|
date: Date,
|
||||||
storage: StorageConfig,
|
storage: StorageConfig,
|
||||||
memory: MemoryConfig,
|
memory: MemoryConfig,
|
||||||
|
media: MediaConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> eframe::Result<()> {
|
fn main() -> eframe::Result<()> {
|
||||||
@@ -84,6 +90,7 @@ fn main() -> eframe::Result<()> {
|
|||||||
date: Date::new(true, DateFormat::DayDateMonthYear),
|
date: Date::new(true, DateFormat::DayDateMonthYear),
|
||||||
storage: StorageConfig { enable: true },
|
storage: StorageConfig { enable: true },
|
||||||
memory: MemoryConfig { enable: true },
|
memory: MemoryConfig { enable: true },
|
||||||
|
media: MediaConfig { enable: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: ensure that config.monitor_index represents a valid komorebi monitor index
|
// TODO: ensure that config.monitor_index represents a valid komorebi monitor index
|
||||||
@@ -178,12 +185,13 @@ struct Komobar {
|
|||||||
state_receiver: Receiver<komorebi_client::Notification>,
|
state_receiver: Receiver<komorebi_client::Notification>,
|
||||||
selected_workspace: String,
|
selected_workspace: String,
|
||||||
focused_window_title: String,
|
focused_window_title: String,
|
||||||
workspace_layout: String,
|
layout: String,
|
||||||
workspaces: Vec<String>,
|
workspaces: Vec<String>,
|
||||||
time: Time,
|
time: Time,
|
||||||
date: Date,
|
date: Date,
|
||||||
memory: Memory,
|
memory: Memory,
|
||||||
storage: Storage,
|
storage: Storage,
|
||||||
|
media: Media,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Komobar {
|
impl Komobar {
|
||||||
@@ -202,12 +210,13 @@ impl Komobar {
|
|||||||
state_receiver: rx,
|
state_receiver: rx,
|
||||||
selected_workspace: String::new(),
|
selected_workspace: String::new(),
|
||||||
focused_window_title: String::new(),
|
focused_window_title: String::new(),
|
||||||
workspace_layout: String::new(),
|
layout: String::new(),
|
||||||
workspaces: vec![],
|
workspaces: vec![],
|
||||||
time: config.time,
|
time: config.time,
|
||||||
date: config.date,
|
date: config.date,
|
||||||
memory: Memory::from(config.memory),
|
memory: Memory::from(config.memory),
|
||||||
storage: Storage::from(config.storage),
|
storage: Storage::from(config.storage),
|
||||||
|
media: Media::from(config.media),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -229,7 +238,7 @@ impl Komobar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.workspaces = workspaces;
|
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::Default(layout) => layout.to_string(),
|
||||||
komorebi_client::Layout::Custom(_) => String::from("Custom"),
|
komorebi_client::Layout::Custom(_) => String::from("Custom"),
|
||||||
};
|
};
|
||||||
@@ -241,6 +250,23 @@ impl Komobar {
|
|||||||
self.focused_window_title.clone_from(&title);
|
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.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);
|
ui.add_space(10.0);
|
||||||
});
|
});
|
||||||
@@ -311,8 +349,11 @@ impl eframe::App for Komobar {
|
|||||||
for time in self.time.output() {
|
for time in self.time.output() {
|
||||||
ctx.request_repaint();
|
ctx.request_repaint();
|
||||||
if ui
|
if ui
|
||||||
.label(format!("🕐 {}", time))
|
.add(
|
||||||
.on_hover_cursor(CursorIcon::default())
|
Label::new(format!("🕐 {}", time))
|
||||||
|
.selectable(false)
|
||||||
|
.sense(Sense::click()),
|
||||||
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
self.time.format.toggle()
|
self.time.format.toggle()
|
||||||
@@ -326,12 +367,15 @@ impl eframe::App for Komobar {
|
|||||||
if self.date.enable {
|
if self.date.enable {
|
||||||
for date in self.date.output() {
|
for date in self.date.output() {
|
||||||
if ui
|
if ui
|
||||||
.label(format!("📅 {}", date))
|
.add(
|
||||||
.on_hover_cursor(CursorIcon::default())
|
Label::new(format!("📅 {}", date))
|
||||||
|
.selectable(false)
|
||||||
|
.sense(Sense::click()),
|
||||||
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
self.date.format.next()
|
self.date.format.next()
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make spacing configurable
|
// TODO: make spacing configurable
|
||||||
@@ -341,9 +385,11 @@ impl eframe::App for Komobar {
|
|||||||
if self.memory.enable {
|
if self.memory.enable {
|
||||||
for ram in self.memory.output() {
|
for ram in self.memory.output() {
|
||||||
if ui
|
if ui
|
||||||
// TODO: make label configurable??
|
.add(
|
||||||
.label(format!("🐏 {}", ram))
|
Label::new(format!("🐏 {}", ram))
|
||||||
.on_hover_cursor(CursorIcon::default())
|
.selectable(false)
|
||||||
|
.sense(Sense::click()),
|
||||||
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
if let Err(error) =
|
if let Err(error) =
|
||||||
@@ -351,7 +397,7 @@ impl eframe::App for Komobar {
|
|||||||
{
|
{
|
||||||
eprintln!("{}", error)
|
eprintln!("{}", error)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.add_space(10.0);
|
ui.add_space(10.0);
|
||||||
@@ -360,9 +406,11 @@ impl eframe::App for Komobar {
|
|||||||
if self.storage.enable {
|
if self.storage.enable {
|
||||||
for disk in self.storage.output() {
|
for disk in self.storage.output() {
|
||||||
if ui
|
if ui
|
||||||
// TODO: Make emoji configurable??
|
.add(
|
||||||
.label(format!("🖴 {}", disk))
|
Label::new(format!("🖴 {}", disk))
|
||||||
.on_hover_cursor(CursorIcon::default())
|
.selectable(false)
|
||||||
|
.sense(Sense::click()),
|
||||||
|
)
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
if let Err(error) = Command::new("cmd.exe")
|
if let Err(error) = Command::new("cmd.exe")
|
||||||
@@ -375,7 +423,24 @@ impl eframe::App for Komobar {
|
|||||||
{
|
{
|
||||||
eprintln!("{}", error)
|
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);
|
ui.add_space(10.0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<MediaConfig> 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<String> {
|
||||||
|
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![]
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user