mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-04-25 10:08:33 +02:00
feat(bar): add komorebi widget (+config) and themes
This commit is contained in:
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -816,6 +816,15 @@ dependencies = [
|
|||||||
"wayland-client",
|
"wayland-client",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "catppuccin-egui"
|
||||||
|
version = "5.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b812fd8e72d65e1afefd7c96ca4c919fe4dc34d470aed2cf459acbcb1cd8f64e"
|
||||||
|
dependencies = [
|
||||||
|
"egui",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.1.5"
|
version = "1.1.5"
|
||||||
@@ -2644,6 +2653,7 @@ name = "komorebi-bar"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"catppuccin-egui",
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
"eframe",
|
"eframe",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ edition = "2021"
|
|||||||
komorebi-client = { path = "../komorebi-client" }
|
komorebi-client = { path = "../komorebi-client" }
|
||||||
|
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
catppuccin-egui = { version = "5.1", default-features = false, features = ["egui28"] }
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
crossbeam-channel = "0.5"
|
crossbeam-channel = "0.5"
|
||||||
eframe = "0.28"
|
eframe = "0.28"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::widget::BarWidget;
|
use crate::widget::BarWidget;
|
||||||
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::Sense;
|
use eframe::egui::Sense;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
@@ -54,7 +55,7 @@ pub struct Battery {
|
|||||||
last_updated: Instant,
|
last_updated: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BarWidget for Battery {
|
impl Battery {
|
||||||
fn output(&mut self) -> Vec<String> {
|
fn output(&mut self) -> Vec<String> {
|
||||||
let mut outputs = self.last_state.clone();
|
let mut outputs = self.last_state.clone();
|
||||||
|
|
||||||
@@ -81,8 +82,10 @@ impl BarWidget for Battery {
|
|||||||
|
|
||||||
outputs
|
outputs
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, ui: &mut Ui) {
|
impl BarWidget for Battery {
|
||||||
|
fn render(&mut self, _ctx: &Context, ui: &mut Ui) {
|
||||||
if self.enable {
|
if self.enable {
|
||||||
let output = self.output();
|
let output = self.output();
|
||||||
if !output.is_empty() {
|
if !output.is_empty() {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::widget::BarWidget;
|
use crate::widget::BarWidget;
|
||||||
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::Sense;
|
use eframe::egui::Sense;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
@@ -52,14 +53,16 @@ pub struct Date {
|
|||||||
pub format: DateFormat,
|
pub format: DateFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BarWidget for Date {
|
impl Date {
|
||||||
fn output(&mut self) -> Vec<String> {
|
fn output(&mut self) -> Vec<String> {
|
||||||
vec![chrono::Local::now()
|
vec![chrono::Local::now()
|
||||||
.format(&self.format.fmt_string())
|
.format(&self.format.fmt_string())
|
||||||
.to_string()]
|
.to_string()]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, ui: &mut Ui) {
|
impl BarWidget for Date {
|
||||||
|
fn render(&mut self, _ctx: &Context, ui: &mut Ui) {
|
||||||
if self.enable {
|
if self.enable {
|
||||||
for output in self.output() {
|
for output in self.output() {
|
||||||
if ui
|
if ui
|
||||||
|
|||||||
142
komorebi-bar/src/komorebi.rs
Normal file
142
komorebi-bar/src/komorebi.rs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
use crate::img_to_texture;
|
||||||
|
use crate::widget::BarWidget;
|
||||||
|
use crate::KomorebiNotificationState;
|
||||||
|
use eframe::egui::Context;
|
||||||
|
use eframe::egui::Image;
|
||||||
|
use eframe::egui::Label;
|
||||||
|
use eframe::egui::SelectableLabel;
|
||||||
|
use eframe::egui::Sense;
|
||||||
|
use eframe::egui::Ui;
|
||||||
|
use komorebi_client::CycleDirection;
|
||||||
|
use komorebi_client::SocketMessage;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct KomorebiConfig {
|
||||||
|
pub enable: bool,
|
||||||
|
pub monitor_index: usize,
|
||||||
|
pub workspaces: KomorebiWorkspacesConfig,
|
||||||
|
pub layout: KomorebiLayoutConfig,
|
||||||
|
pub focused_window: KomorebiFocusedWindowConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct KomorebiWorkspacesConfig {
|
||||||
|
pub enable: bool,
|
||||||
|
pub hide_empty_workspaces: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct KomorebiLayoutConfig {
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct KomorebiFocusedWindowConfig {
|
||||||
|
pub enable: bool,
|
||||||
|
pub show_icon: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<KomorebiConfig> for Komorebi {
|
||||||
|
fn from(value: KomorebiConfig) -> Self {
|
||||||
|
Self {
|
||||||
|
enable: value.enable,
|
||||||
|
komorebi_notification_state: Rc::new(RefCell::new(KomorebiNotificationState {
|
||||||
|
selected_workspace: String::new(),
|
||||||
|
focused_window_title: String::new(),
|
||||||
|
focused_window_pid: None,
|
||||||
|
focused_window_icon: None,
|
||||||
|
layout: String::new(),
|
||||||
|
workspaces: vec![],
|
||||||
|
monitor_index: value.monitor_index,
|
||||||
|
hide_empty_workspaces: value.workspaces.hide_empty_workspaces,
|
||||||
|
})),
|
||||||
|
workspaces: value.workspaces,
|
||||||
|
layout: value.layout,
|
||||||
|
focused_window: value.focused_window,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Komorebi {
|
||||||
|
pub enable: bool,
|
||||||
|
pub komorebi_notification_state: Rc<RefCell<KomorebiNotificationState>>,
|
||||||
|
pub workspaces: KomorebiWorkspacesConfig,
|
||||||
|
pub layout: KomorebiLayoutConfig,
|
||||||
|
pub focused_window: KomorebiFocusedWindowConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BarWidget for Komorebi {
|
||||||
|
fn render(&mut self, ctx: &Context, ui: &mut Ui) {
|
||||||
|
if self.enable {
|
||||||
|
let mut komorebi_notification_state = self.komorebi_notification_state.borrow_mut();
|
||||||
|
let mut update = None;
|
||||||
|
|
||||||
|
if self.workspaces.enable {
|
||||||
|
for (i, ws) in komorebi_notification_state.workspaces.iter().enumerate() {
|
||||||
|
if ui
|
||||||
|
.add(SelectableLabel::new(
|
||||||
|
komorebi_notification_state.selected_workspace.eq(ws),
|
||||||
|
ws.to_string(),
|
||||||
|
))
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
update = Some(ws.to_string());
|
||||||
|
komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(false))
|
||||||
|
.unwrap();
|
||||||
|
komorebi_client::send_message(&SocketMessage::FocusWorkspaceNumber(i))
|
||||||
|
.unwrap();
|
||||||
|
// TODO: store MFF value from state and restore that here instead of "true"
|
||||||
|
komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(true))
|
||||||
|
.unwrap();
|
||||||
|
komorebi_client::send_message(&SocketMessage::Retile).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(update) = update {
|
||||||
|
komorebi_notification_state.selected_workspace = update;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.add_space(10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.layout.enable {
|
||||||
|
if ui
|
||||||
|
.add(
|
||||||
|
Label::new(&komorebi_notification_state.layout)
|
||||||
|
.selectable(false)
|
||||||
|
.sense(Sense::click()),
|
||||||
|
)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
komorebi_client::send_message(&SocketMessage::CycleLayout(
|
||||||
|
CycleDirection::Next,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.add_space(10.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.focused_window.enable {
|
||||||
|
if self.focused_window.show_icon {
|
||||||
|
if let Some(img) = &komorebi_notification_state.focused_window_icon {
|
||||||
|
ui.add(
|
||||||
|
Image::from(&img_to_texture(ctx, img))
|
||||||
|
.maintain_aspect_ratio(true)
|
||||||
|
.max_height(15.0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.add(
|
||||||
|
Label::new(&komorebi_notification_state.focused_window_title).selectable(false),
|
||||||
|
);
|
||||||
|
|
||||||
|
ui.add_space(10.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
mod battery;
|
mod battery;
|
||||||
mod date;
|
mod date;
|
||||||
|
mod komorebi;
|
||||||
mod media;
|
mod media;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod network;
|
mod network;
|
||||||
@@ -12,6 +13,11 @@ use crate::battery::BatteryConfig;
|
|||||||
use crate::date::Date;
|
use crate::date::Date;
|
||||||
use crate::date::DateConfig;
|
use crate::date::DateConfig;
|
||||||
use crate::date::DateFormat;
|
use crate::date::DateFormat;
|
||||||
|
use crate::komorebi::Komorebi;
|
||||||
|
use crate::komorebi::KomorebiConfig;
|
||||||
|
use crate::komorebi::KomorebiFocusedWindowConfig;
|
||||||
|
use crate::komorebi::KomorebiLayoutConfig;
|
||||||
|
use crate::komorebi::KomorebiWorkspacesConfig;
|
||||||
use crate::media::Media;
|
use crate::media::Media;
|
||||||
use crate::media::MediaConfig;
|
use crate::media::MediaConfig;
|
||||||
use crate::memory::Memory;
|
use crate::memory::Memory;
|
||||||
@@ -28,22 +34,20 @@ use eframe::egui;
|
|||||||
use eframe::egui::Align;
|
use eframe::egui::Align;
|
||||||
use eframe::egui::ColorImage;
|
use eframe::egui::ColorImage;
|
||||||
use eframe::egui::Context;
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
|
||||||
use eframe::egui::Layout;
|
use eframe::egui::Layout;
|
||||||
use eframe::egui::Sense;
|
|
||||||
use eframe::egui::TextureHandle;
|
use eframe::egui::TextureHandle;
|
||||||
use eframe::egui::ViewportBuilder;
|
use eframe::egui::ViewportBuilder;
|
||||||
use eframe::egui::Visuals;
|
|
||||||
use eframe::emath::Pos2;
|
use eframe::emath::Pos2;
|
||||||
use eframe::emath::Vec2;
|
use eframe::emath::Vec2;
|
||||||
use font_loader::system_fonts;
|
use font_loader::system_fonts;
|
||||||
use font_loader::system_fonts::FontPropertyBuilder;
|
use font_loader::system_fonts::FontPropertyBuilder;
|
||||||
use image::RgbaImage;
|
use image::RgbaImage;
|
||||||
use komorebi_client::CycleDirection;
|
|
||||||
use komorebi_client::SocketMessage;
|
use komorebi_client::SocketMessage;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use time::Time;
|
use time::Time;
|
||||||
@@ -77,7 +81,6 @@ pub struct Config {
|
|||||||
inner_size: Position,
|
inner_size: Position,
|
||||||
position: Position,
|
position: Position,
|
||||||
outer_margin: Position,
|
outer_margin: Position,
|
||||||
transparent: bool,
|
|
||||||
monitor_index: usize,
|
monitor_index: usize,
|
||||||
monitor_work_area_offset: Option<komorebi_client::Rect>,
|
monitor_work_area_offset: Option<komorebi_client::Rect>,
|
||||||
font_family: Option<String>,
|
font_family: Option<String>,
|
||||||
@@ -88,6 +91,16 @@ pub struct Config {
|
|||||||
media: MediaConfig,
|
media: MediaConfig,
|
||||||
battery: BatteryConfig,
|
battery: BatteryConfig,
|
||||||
network: NetworkConfig,
|
network: NetworkConfig,
|
||||||
|
komorebi: KomorebiConfig,
|
||||||
|
theme: Theme,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum Theme {
|
||||||
|
Default,
|
||||||
|
CatppuccinFrappe,
|
||||||
|
CatppuccinMacchiato,
|
||||||
|
CatppuccinMocha,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> eframe::Result<()> {
|
fn main() -> eframe::Result<()> {
|
||||||
@@ -95,7 +108,6 @@ fn main() -> eframe::Result<()> {
|
|||||||
inner_size: Position { x: 5120.0, y: 20.0 },
|
inner_size: Position { x: 5120.0, y: 20.0 },
|
||||||
position: Position { x: 0.0, y: 0.0 },
|
position: Position { x: 0.0, y: 0.0 },
|
||||||
outer_margin: Position { x: 10.0, y: 10.0 },
|
outer_margin: Position { x: 10.0, y: 10.0 },
|
||||||
transparent: false,
|
|
||||||
monitor_index: 0,
|
monitor_index: 0,
|
||||||
font_family: Some(String::from("JetBrains Mono")),
|
font_family: Some(String::from("JetBrains Mono")),
|
||||||
monitor_work_area_offset: Some(komorebi_client::Rect {
|
monitor_work_area_offset: Some(komorebi_client::Rect {
|
||||||
@@ -120,6 +132,20 @@ fn main() -> eframe::Result<()> {
|
|||||||
enable: true,
|
enable: true,
|
||||||
show_data: true,
|
show_data: true,
|
||||||
},
|
},
|
||||||
|
komorebi: KomorebiConfig {
|
||||||
|
enable: true,
|
||||||
|
monitor_index: 0,
|
||||||
|
workspaces: KomorebiWorkspacesConfig {
|
||||||
|
enable: true,
|
||||||
|
hide_empty_workspaces: true,
|
||||||
|
},
|
||||||
|
layout: KomorebiLayoutConfig { enable: false },
|
||||||
|
focused_window: KomorebiFocusedWindowConfig {
|
||||||
|
enable: true,
|
||||||
|
show_icon: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
theme: Theme::CatppuccinMacchiato,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: ensure that config.monitor_index represents a valid komorebi monitor index
|
// TODO: ensure that config.monitor_index represents a valid komorebi monitor index
|
||||||
@@ -127,7 +153,7 @@ fn main() -> eframe::Result<()> {
|
|||||||
let native_options = eframe::NativeOptions {
|
let native_options = eframe::NativeOptions {
|
||||||
viewport: ViewportBuilder::default()
|
viewport: ViewportBuilder::default()
|
||||||
.with_decorations(false)
|
.with_decorations(false)
|
||||||
.with_transparent(config.transparent)
|
// .with_transparent(config.transparent)
|
||||||
.with_position(config.position)
|
.with_position(config.position)
|
||||||
.with_taskbar(false)
|
.with_taskbar(false)
|
||||||
.with_inner_size(config.inner_size),
|
.with_inner_size(config.inner_size),
|
||||||
@@ -218,14 +244,85 @@ fn main() -> eframe::Result<()> {
|
|||||||
|
|
||||||
struct Komobar {
|
struct Komobar {
|
||||||
config: Config,
|
config: Config,
|
||||||
state_receiver: Receiver<komorebi_client::Notification>,
|
komorebi_notification_state: Rc<RefCell<KomorebiNotificationState>>,
|
||||||
|
left_widgets: Vec<Box<dyn BarWidget>>,
|
||||||
|
right_widgets: Vec<Box<dyn BarWidget>>,
|
||||||
|
rx_gui: Receiver<komorebi_client::Notification>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct KomorebiNotificationState {
|
||||||
|
monitor_index: usize,
|
||||||
|
workspaces: Vec<String>,
|
||||||
selected_workspace: String,
|
selected_workspace: String,
|
||||||
focused_window_title: String,
|
focused_window_title: String,
|
||||||
focused_window_pid: Option<u32>,
|
focused_window_pid: Option<u32>,
|
||||||
focused_window_icon: Option<RgbaImage>,
|
focused_window_icon: Option<RgbaImage>,
|
||||||
layout: String,
|
layout: String,
|
||||||
workspaces: Vec<String>,
|
hide_empty_workspaces: bool,
|
||||||
right_widgets: Vec<Box<dyn BarWidget>>,
|
}
|
||||||
|
|
||||||
|
impl KomorebiNotificationState {
|
||||||
|
fn handle_notification(&mut self, rx_gui: Receiver<komorebi_client::Notification>) {
|
||||||
|
if let Ok(notification) = rx_gui.try_recv() {
|
||||||
|
let monitor = ¬ification.state.monitors.elements()[self.monitor_index];
|
||||||
|
let focused_workspace_idx = monitor.focused_workspace_idx();
|
||||||
|
|
||||||
|
let mut workspaces = vec![];
|
||||||
|
self.selected_workspace = monitor.workspaces()[focused_workspace_idx]
|
||||||
|
.name()
|
||||||
|
.to_owned()
|
||||||
|
.unwrap_or_else(|| format!("{}", focused_workspace_idx + 1));
|
||||||
|
|
||||||
|
for (i, ws) in monitor.workspaces().iter().enumerate() {
|
||||||
|
let should_add = if self.hide_empty_workspaces {
|
||||||
|
focused_workspace_idx == i || !ws.containers().is_empty()
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
if should_add {
|
||||||
|
workspaces.push(ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.workspaces = workspaces;
|
||||||
|
self.layout = match monitor.workspaces()[focused_workspace_idx].layout() {
|
||||||
|
komorebi_client::Layout::Default(layout) => layout.to_string(),
|
||||||
|
komorebi_client::Layout::Custom(_) => String::from("Custom"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(container) = monitor.workspaces()[focused_workspace_idx].focused_container()
|
||||||
|
{
|
||||||
|
if let Some(window) = container.focused_window() {
|
||||||
|
if let Ok(title) = window.title() {
|
||||||
|
self.focused_window_title.clone_from(&title);
|
||||||
|
self.focused_window_pid = Some(window.process_id());
|
||||||
|
let img = windows_icons::get_icon_by_process_id(window.process_id());
|
||||||
|
self.focused_window_icon = Some(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.focused_window_title.clear();
|
||||||
|
self.focused_window_icon = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_custom_font(ctx: &egui::Context, name: &str) {
|
fn add_custom_font(ctx: &egui::Context, name: &str) {
|
||||||
@@ -268,10 +365,27 @@ impl Komobar {
|
|||||||
add_custom_font(&cc.egui_ctx, font_family);
|
add_custom_font(&cc.egui_ctx, font_family);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match config.theme {
|
||||||
|
Theme::Default => {}
|
||||||
|
Theme::CatppuccinFrappe => {
|
||||||
|
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::FRAPPE);
|
||||||
|
}
|
||||||
|
Theme::CatppuccinMacchiato => {
|
||||||
|
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::MACCHIATO);
|
||||||
|
}
|
||||||
|
Theme::CatppuccinMocha => {
|
||||||
|
catppuccin_egui::set_theme(&cc.egui_ctx, catppuccin_egui::MOCHA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals.
|
// Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_visuals.
|
||||||
// Restore app state using cc.storage (requires the "persistence" feature).
|
// Restore app state using cc.storage (requires the "persistence" feature).
|
||||||
// Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
|
// Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
|
||||||
// for e.g. egui::PaintCallback.
|
// for e.g. egui::PaintCallback.
|
||||||
|
let komorebi_workspaces = Komorebi::from(config.komorebi);
|
||||||
|
let komorebi_notification_state = komorebi_workspaces.komorebi_notification_state.clone();
|
||||||
|
|
||||||
|
let left_widgets: Vec<Box<dyn BarWidget>> = vec![Box::new(komorebi_workspaces.clone())];
|
||||||
|
|
||||||
let mut right_widgets: Vec<Box<dyn BarWidget>> = vec![
|
let mut right_widgets: Vec<Box<dyn BarWidget>> = vec![
|
||||||
Box::new(Media::from(config.media)),
|
Box::new(Media::from(config.media)),
|
||||||
@@ -287,14 +401,10 @@ impl Komobar {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
config: config.deref().clone(),
|
config: config.deref().clone(),
|
||||||
state_receiver: rx,
|
komorebi_notification_state,
|
||||||
selected_workspace: String::new(),
|
left_widgets,
|
||||||
focused_window_title: String::new(),
|
|
||||||
focused_window_pid: None,
|
|
||||||
focused_window_icon: None,
|
|
||||||
layout: String::new(),
|
|
||||||
workspaces: vec![],
|
|
||||||
right_widgets,
|
right_widgets,
|
||||||
|
rx_gui: rx,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -305,71 +415,19 @@ fn img_to_texture(ctx: &Context, rgba_image: &RgbaImage) -> TextureHandle {
|
|||||||
ctx.load_texture("icon", color_image, egui::TextureOptions::default())
|
ctx.load_texture("icon", color_image, egui::TextureOptions::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Komobar {
|
|
||||||
fn handle_komorebi_notification(&mut self) {
|
|
||||||
if let Ok(notification) = self.state_receiver.try_recv() {
|
|
||||||
let monitor = ¬ification.state.monitors.elements()[self.config.monitor_index];
|
|
||||||
let focused_workspace_idx = monitor.focused_workspace_idx();
|
|
||||||
|
|
||||||
let mut workspaces = vec![];
|
|
||||||
self.selected_workspace = monitor.workspaces()[focused_workspace_idx]
|
|
||||||
.name()
|
|
||||||
.to_owned()
|
|
||||||
.unwrap_or_else(|| format!("{}", focused_workspace_idx + 1));
|
|
||||||
|
|
||||||
for (i, ws) in monitor.workspaces().iter().enumerate() {
|
|
||||||
workspaces.push(ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
self.workspaces = workspaces;
|
|
||||||
self.layout = match monitor.workspaces()[focused_workspace_idx].layout() {
|
|
||||||
komorebi_client::Layout::Default(layout) => layout.to_string(),
|
|
||||||
komorebi_client::Layout::Custom(_) => String::from("Custom"),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(container) = monitor.workspaces()[focused_workspace_idx].focused_container()
|
|
||||||
{
|
|
||||||
if let Some(window) = container.focused_window() {
|
|
||||||
if let Ok(title) = window.title() {
|
|
||||||
self.focused_window_title.clone_from(&title);
|
|
||||||
self.focused_window_pid = Some(window.process_id());
|
|
||||||
let img = windows_icons::get_icon_by_process_id(window.process_id());
|
|
||||||
self.focused_window_icon = Some(img);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.focused_window_title.clear();
|
|
||||||
self.focused_window_icon = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl eframe::App for Komobar {
|
impl eframe::App for Komobar {
|
||||||
// TODO: I think this is needed for transparency??
|
// TODO: I think this is needed for transparency??
|
||||||
fn clear_color(&self, _visuals: &Visuals) -> [f32; 4] {
|
// fn clear_color(&self, _visuals: &Visuals) -> [f32; 4] {
|
||||||
let mut background = egui::Color32::from_gray(18).to_normalized_gamma_f32();
|
// egui::Rgba::TRANSPARENT.to_array()
|
||||||
background[3] = 0.9;
|
// let mut background = Color32::from_gray(18).to_normalized_gamma_f32();
|
||||||
background
|
// background[3] = 0.9;
|
||||||
}
|
// background
|
||||||
|
// }
|
||||||
|
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
|
||||||
self.handle_komorebi_notification();
|
self.komorebi_notification_state
|
||||||
|
.borrow_mut()
|
||||||
|
.handle_notification(self.rx_gui.clone());
|
||||||
|
|
||||||
egui::CentralPanel::default()
|
egui::CentralPanel::default()
|
||||||
.frame(
|
.frame(
|
||||||
@@ -381,68 +439,17 @@ impl eframe::App for Komobar {
|
|||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal_centered(|ui| {
|
||||||
ui.with_layout(Layout::left_to_right(Align::Center), |ui| {
|
ui.with_layout(Layout::left_to_right(Align::Center), |ui| {
|
||||||
// TODO: maybe this should be a widget??
|
for w in &mut self.left_widgets {
|
||||||
for (i, ws) in self.workspaces.iter().enumerate() {
|
w.render(ctx, ui);
|
||||||
if ui
|
|
||||||
.add(egui::SelectableLabel::new(
|
|
||||||
self.selected_workspace.eq(ws),
|
|
||||||
ws.to_string(),
|
|
||||||
))
|
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
self.selected_workspace = ws.to_string();
|
|
||||||
komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(
|
|
||||||
false,
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
komorebi_client::send_message(
|
|
||||||
&SocketMessage::FocusWorkspaceNumber(i),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
// TODO: store MFF value from state and restore that here instead of "true"
|
|
||||||
komorebi_client::send_message(&SocketMessage::MouseFollowsFocus(
|
|
||||||
true,
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
komorebi_client::send_message(&SocketMessage::Retile).unwrap();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if let Some(img) = &self.focused_window_icon {
|
|
||||||
ui.add(
|
|
||||||
egui::Image::from(&img_to_texture(ctx, img))
|
|
||||||
.maintain_aspect_ratio(true)
|
|
||||||
.max_height(15.0),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.add(Label::new(&self.focused_window_title).selectable(false));
|
|
||||||
|
|
||||||
ui.add_space(10.0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: make the order configurable
|
// TODO: make the order configurable
|
||||||
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||||
for w in &mut self.right_widgets {
|
for w in &mut self.right_widgets {
|
||||||
w.render(ui);
|
w.render(ctx, ui);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::widget::BarWidget;
|
use crate::widget::BarWidget;
|
||||||
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::Sense;
|
use eframe::egui::Sense;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
@@ -39,9 +40,7 @@ impl Media {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl BarWidget for Media {
|
|
||||||
fn output(&mut self) -> Vec<String> {
|
fn output(&mut self) -> Vec<String> {
|
||||||
if let Ok(session) = self.session_manager.GetCurrentSession() {
|
if let Ok(session) = self.session_manager.GetCurrentSession() {
|
||||||
if let Ok(operation) = session.TryGetMediaPropertiesAsync() {
|
if let Ok(operation) = session.TryGetMediaPropertiesAsync() {
|
||||||
@@ -63,8 +62,10 @@ impl BarWidget for Media {
|
|||||||
|
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, ui: &mut Ui) {
|
impl BarWidget for Media {
|
||||||
|
fn render(&mut self, _ctx: &Context, ui: &mut Ui) {
|
||||||
if self.enable {
|
if self.enable {
|
||||||
for output in self.output() {
|
for output in self.output() {
|
||||||
if ui
|
if ui
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::widget::BarWidget;
|
use crate::widget::BarWidget;
|
||||||
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::Sense;
|
use eframe::egui::Sense;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
@@ -34,7 +35,7 @@ impl From<MemoryConfig> for Memory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BarWidget for Memory {
|
impl Memory {
|
||||||
fn output(&mut self) -> Vec<String> {
|
fn output(&mut self) -> Vec<String> {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if now.duration_since(self.last_updated) > Duration::from_secs(10) {
|
if now.duration_since(self.last_updated) > Duration::from_secs(10) {
|
||||||
@@ -46,8 +47,10 @@ impl BarWidget for Memory {
|
|||||||
let total = self.system.total_memory();
|
let total = self.system.total_memory();
|
||||||
vec![format!("RAM: {}%", (used * 100) / total)]
|
vec![format!("RAM: {}%", (used * 100) / total)]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, ui: &mut Ui) {
|
impl BarWidget for Memory {
|
||||||
|
fn render(&mut self, _ctx: &Context, ui: &mut Ui) {
|
||||||
if self.enable {
|
if self.enable {
|
||||||
for output in self.output() {
|
for output in self.output() {
|
||||||
if ui
|
if ui
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::widget::BarWidget;
|
use crate::widget::BarWidget;
|
||||||
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::Sense;
|
use eframe::egui::Sense;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
@@ -55,7 +56,7 @@ pub struct Network {
|
|||||||
last_updated: Instant,
|
last_updated: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BarWidget for Network {
|
impl Network {
|
||||||
fn output(&mut self) -> Vec<String> {
|
fn output(&mut self) -> Vec<String> {
|
||||||
let mut outputs = self.last_state.clone();
|
let mut outputs = self.last_state.clone();
|
||||||
|
|
||||||
@@ -88,8 +89,10 @@ impl BarWidget for Network {
|
|||||||
|
|
||||||
outputs
|
outputs
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, ui: &mut Ui) {
|
impl BarWidget for Network {
|
||||||
|
fn render(&mut self, _ctx: &Context, ui: &mut Ui) {
|
||||||
if self.enable {
|
if self.enable {
|
||||||
let output = self.output();
|
let output = self.output();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::widget::BarWidget;
|
use crate::widget::BarWidget;
|
||||||
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::Sense;
|
use eframe::egui::Sense;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
@@ -28,7 +29,7 @@ pub struct Storage {
|
|||||||
last_updated: Instant,
|
last_updated: Instant,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BarWidget for Storage {
|
impl Storage {
|
||||||
fn output(&mut self) -> Vec<String> {
|
fn output(&mut self) -> Vec<String> {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if now.duration_since(self.last_updated) > Duration::from_secs(10) {
|
if now.duration_since(self.last_updated) > Duration::from_secs(10) {
|
||||||
@@ -56,8 +57,10 @@ impl BarWidget for Storage {
|
|||||||
|
|
||||||
disks
|
disks
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, ui: &mut Ui) {
|
impl BarWidget for Storage {
|
||||||
|
fn render(&mut self, _ctx: &Context, ui: &mut Ui) {
|
||||||
if self.enable {
|
if self.enable {
|
||||||
for output in self.output() {
|
for output in self.output() {
|
||||||
if ui
|
if ui
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::widget::BarWidget;
|
use crate::widget::BarWidget;
|
||||||
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Label;
|
use eframe::egui::Label;
|
||||||
use eframe::egui::Sense;
|
use eframe::egui::Sense;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
@@ -46,14 +47,16 @@ pub struct Time {
|
|||||||
pub format: TimeFormat,
|
pub format: TimeFormat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BarWidget for Time {
|
impl Time {
|
||||||
fn output(&mut self) -> Vec<String> {
|
fn output(&mut self) -> Vec<String> {
|
||||||
vec![chrono::Local::now()
|
vec![chrono::Local::now()
|
||||||
.format(&self.format.fmt_string())
|
.format(&self.format.fmt_string())
|
||||||
.to_string()]
|
.to_string()]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, ui: &mut Ui) {
|
impl BarWidget for Time {
|
||||||
|
fn render(&mut self, _ctx: &Context, ui: &mut Ui) {
|
||||||
if self.enable {
|
if self.enable {
|
||||||
for output in self.output() {
|
for output in self.output() {
|
||||||
if ui
|
if ui
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
use eframe::egui::Context;
|
||||||
use eframe::egui::Ui;
|
use eframe::egui::Ui;
|
||||||
|
|
||||||
pub trait BarWidget {
|
pub trait BarWidget {
|
||||||
fn output(&mut self) -> Vec<String>;
|
fn render(&mut self, ctx: &Context, ui: &mut Ui);
|
||||||
fn render(&mut self, ui: &mut Ui);
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user