diff --git a/Cargo.lock b/Cargo.lock index bda603ac..3519da0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -181,7 +181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.7.0", + "bitflags 2.8.0", "cc", "cesu8", "jni", @@ -630,9 +630,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" dependencies = [ "serde", ] @@ -757,7 +757,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "log", "polling", "rustix", @@ -1899,7 +1899,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b903b73e45dc0c6c596f2d37eccece7c1c8bb6e4407b001096387c63d0d93724" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "libc", "libgit2-sys", "log", @@ -1947,7 +1947,7 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec69412a0bf07ea7607e638b415447857a808846c2b685a43c8aa18bc6d5e499" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "cfg_aliases 0.2.1", "cgl", "core-foundation 0.9.4", @@ -2013,7 +2013,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "gpu-alloc-types", ] @@ -2023,7 +2023,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", ] [[package]] @@ -2032,7 +2032,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "gpu-descriptor-types", "hashbrown", ] @@ -2043,7 +2043,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", ] [[package]] @@ -2615,7 +2615,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" name = "komorebi" version = "0.1.34" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "clap", "color-eyre", "crossbeam-channel", @@ -2840,7 +2840,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "libc", "redox_syscall 0.5.8", ] @@ -2967,7 +2967,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -3088,7 +3088,7 @@ checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.7.0", + "bitflags 2.8.0", "cfg_aliases 0.1.1", "codespan-reporting", "hexf-parse", @@ -3133,7 +3133,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "jni-sys", "log", "ndk-sys 0.6.0+11769913", @@ -3212,7 +3212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "483325d4bfef65699214858f097d504eb812c38ce7077d165f301ec406c3066e" dependencies = [ "anyhow", - "bitflags 2.7.0", + "bitflags 2.8.0", "byteorder", "libc", "log", @@ -3255,7 +3255,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "cfg-if 1.0.0", "cfg_aliases 0.2.1", "libc", @@ -3290,7 +3290,7 @@ version = "6.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6205bd8bb1e454ad2e27422015fb5e4f2bcc7e08fa8f27058670d208324a4d2d" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "crossbeam-channel", "filetime", "fsevent-sys", @@ -3486,7 +3486,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "block2", "libc", "objc2", @@ -3502,7 +3502,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-core-location", @@ -3526,7 +3526,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -3568,7 +3568,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "block2", "dispatch", "libc", @@ -3593,7 +3593,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -3605,7 +3605,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -3628,7 +3628,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-cloud-kit", @@ -3660,7 +3660,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-core-location", @@ -3688,7 +3688,7 @@ version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "cfg-if 1.0.0", "foreign-types 0.3.2", "libc", @@ -4200,7 +4200,7 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", ] [[package]] @@ -4348,7 +4348,7 @@ version = "0.38.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -4479,7 +4479,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -4529,9 +4529,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" dependencies = [ "itoa", "memchr", @@ -4719,7 +4719,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -4780,7 +4780,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", ] [[package]] @@ -4925,7 +4925,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5634,7 +5634,7 @@ version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "rustix", "wayland-backend", "wayland-scanner", @@ -5646,7 +5646,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "cursor-icon", "wayland-backend", ] @@ -5668,7 +5668,7 @@ version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5680,7 +5680,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5693,7 +5693,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5799,7 +5799,7 @@ checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a" dependencies = [ "arrayvec", "bit-vec", - "bitflags 2.7.0", + "bitflags 2.8.0", "cfg_aliases 0.1.1", "document-features", "indexmap", @@ -5825,7 +5825,7 @@ dependencies = [ "android_system_properties", "arrayvec", "ash", - "bitflags 2.7.0", + "bitflags 2.8.0", "bytemuck", "cfg_aliases 0.1.1", "core-graphics-types", @@ -5862,7 +5862,7 @@ version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "js-sys", "web-sys", ] @@ -6291,7 +6291,7 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.7.0", + "bitflags 2.8.0", "block2", "bytemuck", "calloop", @@ -6434,7 +6434,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.7.0", + "bitflags 2.8.0", "dlib", "log", "once_cell", diff --git a/docs/komorebi.bar.example.json b/docs/komorebi.bar.example.json index 292f7df4..9ea41479 100644 --- a/docs/komorebi.bar.example.json +++ b/docs/komorebi.bar.example.json @@ -1,14 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/LGUG2Z/komorebi/v0.1.33/schema.bar.json", - "monitor": { - "index": 0, - "work_area_offset": { - "left": 0, - "top": 40, - "right": 0, - "bottom": 40 - } - }, + "monitor": 0, "font_family": "JetBrains Mono", "theme": { "palette": "Base16", diff --git a/komorebi-bar/src/bar.rs b/komorebi-bar/src/bar.rs index 08b9fc21..b3bcddaf 100644 --- a/komorebi-bar/src/bar.rs +++ b/komorebi-bar/src/bar.rs @@ -1,5 +1,7 @@ +use crate::config::get_individual_spacing; use crate::config::KomobarConfig; use crate::config::KomobarTheme; +use crate::config::MonitorConfigOrIndex; use crate::config::Position; use crate::config::PositionConfig; use crate::komorebi::Komorebi; @@ -11,12 +13,15 @@ use crate::render::RenderConfig; use crate::render::RenderExt; use crate::widget::BarWidget; use crate::widget::WidgetConfig; +use crate::KomorebiEvent; use crate::BAR_HEIGHT; +use crate::DEFAULT_PADDING; use crate::MAX_LABEL_WIDTH; use crate::MONITOR_LEFT; use crate::MONITOR_RIGHT; use crate::MONITOR_TOP; use crossbeam_channel::Receiver; +use crossbeam_channel::TryRecvError; use eframe::egui::Align; use eframe::egui::Align2; use eframe::egui::Area; @@ -34,6 +39,7 @@ use eframe::egui::Margin; use eframe::egui::Rgba; use eframe::egui::Style; use eframe::egui::TextStyle; +use eframe::egui::Vec2; use eframe::egui::Visuals; use font_loader::system_fonts; use font_loader::system_fonts::FontPropertyBuilder; @@ -52,18 +58,20 @@ use std::sync::Arc; pub struct Komobar { pub hwnd: Option, + pub monitor_index: usize, pub config: Arc, pub render_config: Rc>, pub komorebi_notification_state: Option>>, pub left_widgets: Vec>, pub center_widgets: Vec>, pub right_widgets: Vec>, - pub rx_gui: Receiver, + pub rx_gui: Receiver, pub rx_config: Receiver, pub bg_color: Rc>, pub bg_color_with_alpha: Rc>, pub scale_factor: f32, pub size_rect: komorebi_client::Rect, + pub work_area_offset: komorebi_client::Rect, applied_theme_on_first_frame: bool, } @@ -199,7 +207,7 @@ impl Komobar { // Update the `size_rect` so that the bar position can be changed on the EGUI update // function - self.update_size_rect(config.position.clone()); + self.update_size_rect(config); self.try_apply_theme(config, ctx); @@ -295,23 +303,64 @@ impl Komobar { self.center_widgets = center_widgets; self.right_widgets = right_widgets; - if let (Some(prev_rect), Some(new_rect)) = ( - &self.config.monitor.work_area_offset, - &config.monitor.work_area_offset, - ) { + let (monitor_index, config_work_area_offset) = match &config.monitor { + MonitorConfigOrIndex::MonitorConfig(monitor_config) => { + (monitor_config.index, monitor_config.work_area_offset) + } + MonitorConfigOrIndex::Index(idx) => (*idx, None), + }; + self.monitor_index = monitor_index; + + if let (prev_rect, Some(new_rect)) = (&self.work_area_offset, &config_work_area_offset) { if new_rect != prev_rect { + self.work_area_offset = *new_rect; + if let Err(error) = komorebi_client::send_message( + &SocketMessage::MonitorWorkAreaOffset(self.monitor_index, *new_rect), + ) { + tracing::error!( + "error applying work area offset to monitor '{}': {}", + self.monitor_index, + error, + ); + } else { + tracing::info!( + "work area offset applied to monitor: {}", + self.monitor_index + ); + } + } + } else if let Some(height) = config.height.or(Some(BAR_HEIGHT)) { + // We only add the `bottom_margin` to the work_area_offset since the top margin is + // already considered on the `size_rect.top` + let bottom_margin = config + .margin + .as_ref() + .map_or(0, |v| v.to_individual(0.0).bottom as i32); + let new_rect = komorebi_client::Rect { + left: 0, + top: (height as i32) + + (self.size_rect.top - MONITOR_TOP.load(Ordering::SeqCst)) + + bottom_margin, + right: 0, + bottom: (height as i32) + + (self.size_rect.top - MONITOR_TOP.load(Ordering::SeqCst)) + + bottom_margin, + }; + + if new_rect != self.work_area_offset { + self.work_area_offset = new_rect; if let Err(error) = komorebi_client::send_message( - &SocketMessage::MonitorWorkAreaOffset(config.monitor.index, *new_rect), + &SocketMessage::MonitorWorkAreaOffset(self.monitor_index, new_rect), ) { tracing::error!( "error applying work area offset to monitor '{}': {}", - config.monitor.index, + self.monitor_index, error, ); } else { tracing::info!( "work area offset applied to monitor: {}", - config.monitor.index + self.monitor_index ); } } @@ -325,8 +374,8 @@ impl Komobar { } /// Updates the `size_rect` field. Returns a bool indicating if the field was changed or not - fn update_size_rect(&mut self, position: Option) { - let position = position.unwrap_or(PositionConfig { + fn update_size_rect(&mut self, config: &KomobarConfig) { + let position = config.position.clone().unwrap_or(PositionConfig { start: Some(Position { x: MONITOR_LEFT.load(Ordering::SeqCst) as f32, y: MONITOR_TOP.load(Ordering::SeqCst) as f32, @@ -337,16 +386,26 @@ impl Komobar { }), }); - let start = position.start.unwrap_or(Position { + let mut start = position.start.unwrap_or(Position { x: MONITOR_LEFT.load(Ordering::SeqCst) as f32, y: MONITOR_TOP.load(Ordering::SeqCst) as f32, }); - let end = position.end.unwrap_or(Position { + let mut end = position.end.unwrap_or(Position { x: MONITOR_RIGHT.load(Ordering::SeqCst) as f32, y: BAR_HEIGHT, }); + if let Some(height) = config.height { + end.y = height; + } + + let margin = get_individual_spacing(0.0, &config.margin); + + start.y += margin.top; + start.x += margin.left; + end.x -= margin.left + margin.right; + if end.y == 0.0 { tracing::warn!("position.end.y is set to 0.0 which will make your bar invisible on a config reload - this is usually set to 50.0 by default") } @@ -447,12 +506,13 @@ impl Komobar { pub fn new( cc: &eframe::CreationContext<'_>, - rx_gui: Receiver, + rx_gui: Receiver, rx_config: Receiver, config: Arc, ) -> Self { let mut komobar = Self { hwnd: process_hwnd(), + monitor_index: 0, config: config.clone(), render_config: Rc::new(RefCell::new(RenderConfig::new())), komorebi_notification_state: None, @@ -465,6 +525,7 @@ impl Komobar { bg_color_with_alpha: Rc::new(RefCell::new(Style::default().visuals.panel_fill)), scale_factor: cc.egui_ctx.native_pixels_per_point().unwrap_or(1.0), size_rect: komorebi_client::Rect::default(), + work_area_offset: komorebi_client::Rect::default(), applied_theme_on_first_frame: false, }; @@ -573,20 +634,51 @@ impl eframe::App for Komobar { ); } - if let Some(komorebi_notification_state) = &self.komorebi_notification_state { - komorebi_notification_state - .borrow_mut() - .handle_notification( - ctx, - self.config.monitor.index, - self.rx_gui.clone(), - self.bg_color.clone(), - self.bg_color_with_alpha.clone(), - self.config.transparency_alpha, - self.config.grouping, - self.config.theme, - self.render_config.clone(), - ); + match self.rx_gui.try_recv() { + Err(error) => match error { + TryRecvError::Empty => {} + TryRecvError::Disconnected => { + tracing::error!( + "failed to receive komorebi notification on gui thread: {error}" + ); + } + }, + Ok(KomorebiEvent::Notification(notification)) => { + if let Some(komorebi_notification_state) = &self.komorebi_notification_state { + komorebi_notification_state + .borrow_mut() + .handle_notification( + ctx, + self.monitor_index, + notification, + self.bg_color.clone(), + self.bg_color_with_alpha.clone(), + self.config.transparency_alpha, + self.config.grouping, + self.config.theme, + self.render_config.clone(), + ); + } + } + Ok(KomorebiEvent::Reconnect) => { + if let Err(error) = + komorebi_client::send_message(&SocketMessage::MonitorWorkAreaOffset( + self.monitor_index, + self.work_area_offset, + )) + { + tracing::error!( + "error applying work area offset to monitor '{}': {}", + self.monitor_index, + error, + ); + } else { + tracing::info!( + "work area offset applied to monitor: {}", + self.monitor_index + ); + } + } } if !self.applied_theme_on_first_frame { @@ -619,40 +711,78 @@ impl eframe::App for Komobar { } } - let frame = if let Some(frame) = &self.config.frame { - Frame::none() - .inner_margin(Margin::symmetric( - frame.inner_margin.x, - frame.inner_margin.y, - )) - .fill(*self.bg_color_with_alpha.borrow()) - } else { - Frame::none().fill(*self.bg_color_with_alpha.borrow()) + let frame = match &self.config.padding { + None => { + if let Some(frame) = &self.config.frame { + Frame::none() + .inner_margin(Margin::symmetric( + frame.inner_margin.x, + frame.inner_margin.y, + )) + .fill(*self.bg_color_with_alpha.borrow()) + } else { + Frame::none() + .inner_margin(Margin::same(0.0)) + .fill(*self.bg_color_with_alpha.borrow()) + } + } + Some(padding) => { + let padding = padding.to_individual(DEFAULT_PADDING); + Frame::none() + .inner_margin(Margin { + top: padding.top, + bottom: padding.bottom, + left: padding.left, + right: padding.right, + }) + .fill(*self.bg_color_with_alpha.borrow()) + } }; let mut render_config = self.render_config.borrow_mut(); let frame = render_config.change_frame_on_bar(frame, &ctx.style()); - CentralPanel::default().frame(frame).show(ctx, |_| { + CentralPanel::default().frame(frame).show(ctx, |ui| { // Apply grouping logic for the bar as a whole let area_frame = if let Some(frame) = &self.config.frame { - Frame::none().inner_margin(Margin::symmetric(0.0, frame.inner_margin.y)) + Frame::none() + .inner_margin(Margin::symmetric(0.0, frame.inner_margin.y)) + .outer_margin(Margin::same(0.0)) } else { Frame::none() + .inner_margin(Margin::same(0.0)) + .outer_margin(Margin::same(0.0)) }; + let available_height = ui.max_rect().max.y; + ctx.style_mut(|style| { + style.spacing.interact_size.y = available_height; + }); + if !self.left_widgets.is_empty() { // Left-aligned widgets layout Area::new(Id::new("left_panel")) .anchor(Align2::LEFT_CENTER, [0.0, 0.0]) // Align in the left center of the window .show(ctx, |ui| { let mut left_area_frame = area_frame; - if let Some(frame) = &self.config.frame { + if let Some(padding) = self + .config + .padding + .as_ref() + .map(|s| s.to_individual(DEFAULT_PADDING)) + { + left_area_frame.inner_margin.left = padding.left; + left_area_frame.inner_margin.top = padding.top; + left_area_frame.inner_margin.bottom = padding.bottom; + } else if let Some(frame) = &self.config.frame { left_area_frame.inner_margin.left = frame.inner_margin.x; + left_area_frame.inner_margin.top = frame.inner_margin.y; + left_area_frame.inner_margin.bottom = frame.inner_margin.y; } + left_area_frame.show(ui, |ui| { - ui.with_layout(Layout::left_to_right(Align::Center), |ui| { + ui.horizontal(|ui| { let mut render_conf = render_config.clone(); render_conf.alignment = Some(Alignment::Left); @@ -672,20 +802,40 @@ impl eframe::App for Komobar { .anchor(Align2::RIGHT_CENTER, [0.0, 0.0]) // Align in the right center of the window .show(ctx, |ui| { let mut right_area_frame = area_frame; - if let Some(frame) = &self.config.frame { + if let Some(padding) = self + .config + .padding + .as_ref() + .map(|s| s.to_individual(DEFAULT_PADDING)) + { + right_area_frame.inner_margin.right = padding.right; + right_area_frame.inner_margin.top = padding.top; + right_area_frame.inner_margin.bottom = padding.bottom; + } else if let Some(frame) = &self.config.frame { right_area_frame.inner_margin.right = frame.inner_margin.x; + right_area_frame.inner_margin.top = frame.inner_margin.y; + right_area_frame.inner_margin.bottom = frame.inner_margin.y; } - right_area_frame.show(ui, |ui| { - ui.with_layout(Layout::right_to_left(Align::Center), |ui| { - let mut render_conf = render_config.clone(); - render_conf.alignment = Some(Alignment::Right); - render_config.apply_on_alignment(ui, |ui| { - for w in &mut self.right_widgets { - w.render(ctx, ui, &mut render_conf); - } - }); - }); + right_area_frame.show(ui, |ui| { + let initial_size = Vec2 { + x: ui.available_size_before_wrap().x, + y: ui.spacing().interact_size.y, + }; + ui.allocate_ui_with_layout( + initial_size, + Layout::right_to_left(Align::Center), + |ui| { + let mut render_conf = render_config.clone(); + render_conf.alignment = Some(Alignment::Right); + + render_config.apply_on_alignment(ui, |ui| { + for w in &mut self.right_widgets { + w.render(ctx, ui, &mut render_conf); + } + }); + }, + ); }); }); } @@ -695,9 +845,22 @@ impl eframe::App for Komobar { Area::new(Id::new("center_panel")) .anchor(Align2::CENTER_CENTER, [0.0, 0.0]) // Align in the center of the window .show(ctx, |ui| { - let center_area_frame = area_frame; + let mut center_area_frame = area_frame; + if let Some(padding) = self + .config + .padding + .as_ref() + .map(|s| s.to_individual(DEFAULT_PADDING)) + { + center_area_frame.inner_margin.top = padding.top; + center_area_frame.inner_margin.bottom = padding.bottom; + } else if let Some(frame) = &self.config.frame { + center_area_frame.inner_margin.top = frame.inner_margin.y; + center_area_frame.inner_margin.bottom = frame.inner_margin.y; + } + center_area_frame.show(ui, |ui| { - ui.with_layout(Layout::left_to_right(Align::Center), |ui| { + ui.horizontal(|ui| { let mut render_conf = render_config.clone(); render_conf.alignment = Some(Alignment::Center); diff --git a/komorebi-bar/src/config.rs b/komorebi-bar/src/config.rs index 1f3ee36a..0e153dae 100644 --- a/komorebi-bar/src/config.rs +++ b/komorebi-bar/src/config.rs @@ -1,5 +1,6 @@ use crate::render::Grouping; use crate::widget::WidgetConfig; +use crate::DEFAULT_PADDING; use eframe::egui::Pos2; use eframe::egui::TextBuffer; use eframe::egui::Vec2; @@ -14,13 +15,65 @@ use std::path::PathBuf; #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] /// The `komorebi.bar.json` configuration file reference for `v0.1.34` pub struct KomobarConfig { + /// Bar height (default: 50) + pub height: Option, + /// Bar padding. Use one value for all sides or use a grouped padding for horizontal and/or + /// vertical definition which can each take a single value for a symmetric padding or two + /// values for each side, i.e.: + /// ```json + /// "padding": { + /// "horizontal": 10 + /// } + /// ``` + /// or: + /// ```json + /// "padding": { + /// "horizontal": [left, right] + /// } + /// ``` + /// You can also set individual padding on each side like this: + /// ```json + /// "padding": { + /// "top": 10, + /// "bottom": 10, + /// "left": 10, + /// "right": 10, + /// } + /// ``` + /// By default, padding is set to 10 on all sides. + pub padding: Option, + /// Bar margin. Use one value for all sides or use a grouped margin for horizontal and/or + /// vertical definition which can each take a single value for a symmetric margin or two + /// values for each side, i.e.: + /// ```json + /// "margin": { + /// "horizontal": 10 + /// } + /// ``` + /// or: + /// ```json + /// "margin": { + /// "vertical": [top, bottom] + /// } + /// ``` + /// You can also set individual margin on each side like this: + /// ```json + /// "margin": { + /// "top": 10, + /// "bottom": 10, + /// "left": 10, + /// "right": 10, + /// } + /// ``` + /// By default, margin is set to 0 on all sides. + pub margin: Option, /// Bar positioning options #[serde(alias = "viewport")] pub position: Option, /// Frame options (see: https://docs.rs/egui/latest/egui/containers/frame/struct.Frame.html) pub frame: Option, - /// Monitor options - pub monitor: MonitorConfig, + /// The monitor index or the full monitor options + pub monitor: MonitorConfigOrIndex, /// Font family pub font_family: Option, /// Font size (default: 12.5) @@ -90,6 +143,15 @@ pub struct FrameConfig { pub inner_margin: Position, } +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(untagged)] +pub enum MonitorConfigOrIndex { + /// The monitor index where you want the bar to show + Index(usize), + /// The full monitor options with the index and an optional work_area_offset + MonitorConfig(MonitorConfig), +} + #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] pub struct MonitorConfig { /// Komorebi monitor index of the monitor on which to render the bar @@ -98,6 +160,154 @@ pub struct MonitorConfig { pub work_area_offset: Option, } +pub type Padding = SpacingKind; +pub type Margin = SpacingKind; + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(untagged)] +// WARNING: To any developer messing with this code in the future: The order here matters! +// `Grouped` needs to come last, otherwise serde might mistaken an `IndividualSpacingConfig` for a +// `GroupedSpacingConfig` with both `vertical` and `horizontal` set to `None` ignoring the +// individual values. +pub enum SpacingKind { + All(f32), + Individual(IndividualSpacingConfig), + Grouped(GroupedSpacingConfig), +} + +impl SpacingKind { + pub fn to_individual(&self, default: f32) -> IndividualSpacingConfig { + match self { + SpacingKind::All(m) => IndividualSpacingConfig::all(*m), + SpacingKind::Grouped(grouped_spacing_config) => { + let vm = grouped_spacing_config.vertical.as_ref().map_or( + IndividualSpacingConfig::vertical(default), + |vm| match vm { + GroupedSpacingOptions::Symmetrical(m) => { + IndividualSpacingConfig::vertical(*m) + } + GroupedSpacingOptions::Split(tm, bm) => { + IndividualSpacingConfig::vertical(*tm).bottom(*bm) + } + }, + ); + let hm = grouped_spacing_config.horizontal.as_ref().map_or( + IndividualSpacingConfig::horizontal(default), + |hm| match hm { + GroupedSpacingOptions::Symmetrical(m) => { + IndividualSpacingConfig::horizontal(*m) + } + GroupedSpacingOptions::Split(lm, rm) => { + IndividualSpacingConfig::horizontal(*lm).right(*rm) + } + }, + ); + IndividualSpacingConfig { + top: vm.top, + bottom: vm.bottom, + left: hm.left, + right: hm.right, + } + } + SpacingKind::Individual(m) => *m, + } + } +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct GroupedSpacingConfig { + pub vertical: Option, + pub horizontal: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(untagged)] +pub enum GroupedSpacingOptions { + Symmetrical(f32), + Split(f32, f32), +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)] +pub struct IndividualSpacingConfig { + pub top: f32, + pub bottom: f32, + pub left: f32, + pub right: f32, +} + +#[allow(dead_code)] +impl IndividualSpacingConfig { + pub const ZERO: Self = IndividualSpacingConfig { + top: 0.0, + bottom: 0.0, + left: 0.0, + right: 0.0, + }; + + pub fn all(value: f32) -> Self { + IndividualSpacingConfig { + top: value, + bottom: value, + left: value, + right: value, + } + } + + pub fn horizontal(value: f32) -> Self { + IndividualSpacingConfig { + top: 0.0, + bottom: 0.0, + left: value, + right: value, + } + } + + pub fn vertical(value: f32) -> Self { + IndividualSpacingConfig { + top: value, + bottom: value, + left: 0.0, + right: 0.0, + } + } + + pub fn top(self, value: f32) -> Self { + IndividualSpacingConfig { top: value, ..self } + } + + pub fn bottom(self, value: f32) -> Self { + IndividualSpacingConfig { + bottom: value, + ..self + } + } + + pub fn left(self, value: f32) -> Self { + IndividualSpacingConfig { + left: value, + ..self + } + } + + pub fn right(self, value: f32) -> Self { + IndividualSpacingConfig { + right: value, + ..self + } + } +} + +pub fn get_individual_spacing( + default: f32, + spacing: &Option, +) -> IndividualSpacingConfig { + spacing + .as_ref() + .map_or(IndividualSpacingConfig::all(default), |s| { + s.to_individual(default) + }) +} + impl KomobarConfig { pub fn read(path: &PathBuf) -> color_eyre::Result { let content = std::fs::read_to_string(path)?; @@ -108,7 +318,10 @@ impl KomobarConfig { if value.frame.is_none() { value.frame = Some(FrameConfig { - inner_margin: Position { x: 10.0, y: 10.0 }, + inner_margin: Position { + x: DEFAULT_PADDING, + y: DEFAULT_PADDING, + }, }); } diff --git a/komorebi-bar/src/komorebi.rs b/komorebi-bar/src/komorebi.rs index f0adc581..ece42251 100644 --- a/komorebi-bar/src/komorebi.rs +++ b/komorebi-bar/src/komorebi.rs @@ -10,8 +10,6 @@ use crate::widget::BarWidget; use crate::ICON_CACHE; use crate::MAX_LABEL_WIDTH; use crate::MONITOR_INDEX; -use crossbeam_channel::Receiver; -use crossbeam_channel::TryRecvError; use eframe::egui::vec2; use eframe::egui::Color32; use eframe::egui::ColorImage; @@ -496,7 +494,7 @@ impl KomorebiNotificationState { &mut self, ctx: &Context, monitor_index: usize, - rx_gui: Receiver, + notification: komorebi_client::Notification, bg_color: Rc>, bg_color_with_alpha: Rc>, transparency_alpha: Option, @@ -504,119 +502,104 @@ impl KomorebiNotificationState { default_theme: Option, render_config: Rc>, ) { - match rx_gui.try_recv() { - Err(error) => match error { - TryRecvError::Empty => {} - TryRecvError::Disconnected => { - tracing::error!( - "failed to receive komorebi notification on gui thread: {error}" - ); - } - }, - Ok(notification) => { - match notification.event { - NotificationEvent::WindowManager(_) => {} - NotificationEvent::Socket(message) => match message { - SocketMessage::ReloadStaticConfiguration(path) => { - if let Ok(config) = komorebi_client::StaticConfig::read(&path) { - if let Some(theme) = config.theme { - apply_theme( - ctx, - KomobarTheme::from(theme), - bg_color.clone(), - bg_color_with_alpha.clone(), - transparency_alpha, - grouping, - render_config, - ); - tracing::info!("applied theme from updated komorebi.json"); - } else if let Some(default_theme) = default_theme { - apply_theme( - ctx, - default_theme, - bg_color.clone(), - bg_color_with_alpha.clone(), - transparency_alpha, - grouping, - render_config, - ); - tracing::info!("removed theme from updated komorebi.json and applied default theme"); - } else { - tracing::warn!("theme was removed from updated komorebi.json but there was no default theme to apply"); - } - } - } - SocketMessage::Theme(theme) => { + match notification.event { + NotificationEvent::WindowManager(_) => {} + NotificationEvent::Socket(message) => match message { + SocketMessage::ReloadStaticConfiguration(path) => { + if let Ok(config) = komorebi_client::StaticConfig::read(&path) { + if let Some(theme) = config.theme { apply_theme( ctx, KomobarTheme::from(theme), - bg_color, + bg_color.clone(), bg_color_with_alpha.clone(), transparency_alpha, grouping, render_config, ); - tracing::info!("applied theme from komorebi socket message"); + tracing::info!("applied theme from updated komorebi.json"); + } else if let Some(default_theme) = default_theme { + apply_theme( + ctx, + default_theme, + bg_color.clone(), + bg_color_with_alpha.clone(), + transparency_alpha, + grouping, + render_config, + ); + tracing::info!("removed theme from updated komorebi.json and applied default theme"); + } else { + tracing::warn!("theme was removed from updated komorebi.json but there was no default theme to apply"); } - _ => {} - }, + } } + SocketMessage::Theme(theme) => { + apply_theme( + ctx, + KomobarTheme::from(theme), + bg_color, + bg_color_with_alpha.clone(), + transparency_alpha, + grouping, + render_config, + ); + tracing::info!("applied theme from komorebi socket message"); + } + _ => {} + }, + } - self.monitor_index = monitor_index; - - self.mouse_follows_focus = notification.state.mouse_follows_focus; - - let monitor = ¬ification.state.monitors.elements()[monitor_index]; - self.work_area_offset = - notification.state.monitors.elements()[monitor_index].work_area_offset(); + self.monitor_index = monitor_index; - let focused_workspace_idx = monitor.focused_workspace_idx(); + self.mouse_follows_focus = notification.state.mouse_follows_focus; - let mut workspaces = vec![]; - self.selected_workspace = monitor.workspaces()[focused_workspace_idx] - .name() - .to_owned() - .unwrap_or_else(|| format!("{}", focused_workspace_idx + 1)); + let monitor = ¬ification.state.monitors.elements()[monitor_index]; + self.work_area_offset = + notification.state.monitors.elements()[monitor_index].work_area_offset(); - for (i, ws) in monitor.workspaces().iter().enumerate() { - let should_show = if self.hide_empty_workspaces { - focused_workspace_idx == i || !ws.containers().is_empty() - } else { - true - }; + let focused_workspace_idx = monitor.focused_workspace_idx(); - if should_show { - workspaces.push(( - ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)), - ws.into(), - )); - } - } + let mut workspaces = vec![]; + self.selected_workspace = monitor.workspaces()[focused_workspace_idx] + .name() + .to_owned() + .unwrap_or_else(|| format!("{}", focused_workspace_idx + 1)); - self.workspaces = workspaces; - - if monitor.workspaces()[focused_workspace_idx] - .monocle_container() - .is_some() - { - self.layout = KomorebiLayout::Monocle; - } else if !*monitor.workspaces()[focused_workspace_idx].tile() { - self.layout = KomorebiLayout::Floating; - } else if notification.state.is_paused { - self.layout = KomorebiLayout::Paused; - } else { - self.layout = match monitor.workspaces()[focused_workspace_idx].layout() { - komorebi_client::Layout::Default(layout) => { - KomorebiLayout::Default(*layout) - } - komorebi_client::Layout::Custom(_) => KomorebiLayout::Custom, - }; - } + for (i, ws) in monitor.workspaces().iter().enumerate() { + let should_show = if self.hide_empty_workspaces { + focused_workspace_idx == i || !ws.containers().is_empty() + } else { + true + }; - self.focused_container_information = - (&monitor.workspaces()[focused_workspace_idx]).into(); + if should_show { + workspaces.push(( + ws.name().to_owned().unwrap_or_else(|| format!("{}", i + 1)), + ws.into(), + )); } } + + self.workspaces = workspaces; + + if monitor.workspaces()[focused_workspace_idx] + .monocle_container() + .is_some() + { + self.layout = KomorebiLayout::Monocle; + } else if !*monitor.workspaces()[focused_workspace_idx].tile() { + self.layout = KomorebiLayout::Floating; + } else if notification.state.is_paused { + self.layout = KomorebiLayout::Paused; + } else { + self.layout = match monitor.workspaces()[focused_workspace_idx].layout() { + komorebi_client::Layout::Default(layout) => KomorebiLayout::Default(*layout), + komorebi_client::Layout::Custom(_) => KomorebiLayout::Custom, + }; + } + + self.focused_container_information = (&monitor.workspaces()[focused_workspace_idx]).into(); } } diff --git a/komorebi-bar/src/main.rs b/komorebi-bar/src/main.rs index f4701862..887710ec 100644 --- a/komorebi-bar/src/main.rs +++ b/komorebi-bar/src/main.rs @@ -21,6 +21,7 @@ use crate::config::KomobarConfig; use crate::config::Position; use crate::config::PositionConfig; use clap::Parser; +use config::MonitorConfigOrIndex; use eframe::egui::ViewportBuilder; use font_loader::system_fonts; use hotwatch::EventKind; @@ -57,6 +58,7 @@ pub static MONITOR_TOP: AtomicI32 = AtomicI32::new(0); pub static MONITOR_RIGHT: AtomicI32 = AtomicI32::new(0); pub static MONITOR_INDEX: AtomicUsize = AtomicUsize::new(0); pub static BAR_HEIGHT: f32 = 50.0; +pub static DEFAULT_PADDING: f32 = 10.0; pub static ICON_CACHE: LazyLock>> = LazyLock::new(|| Mutex::new(HashMap::new())); @@ -113,6 +115,11 @@ fn process_hwnd() -> Option { } } +pub enum KomorebiEvent { + Notification(komorebi_client::Notification), + Reconnect, +} + fn main() -> color_eyre::Result<()> { unsafe { SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) }?; @@ -230,32 +237,39 @@ fn main() -> color_eyre::Result<()> { &SocketMessage::State, )?)?; + let (monitor_index, work_area_offset) = match &config.monitor { + MonitorConfigOrIndex::MonitorConfig(monitor_config) => { + (monitor_config.index, monitor_config.work_area_offset) + } + MonitorConfigOrIndex::Index(idx) => (*idx, None), + }; + MONITOR_RIGHT.store( - state.monitors.elements()[config.monitor.index].size().right, + state.monitors.elements()[monitor_index].size().right, Ordering::SeqCst, ); MONITOR_TOP.store( - state.monitors.elements()[config.monitor.index].size().top, + state.monitors.elements()[monitor_index].size().top, Ordering::SeqCst, ); - MONITOR_TOP.store( - state.monitors.elements()[config.monitor.index].size().left, + MONITOR_LEFT.store( + state.monitors.elements()[monitor_index].size().left, Ordering::SeqCst, ); - MONITOR_INDEX.store(config.monitor.index, Ordering::SeqCst); + MONITOR_INDEX.store(monitor_index, Ordering::SeqCst); match config.position { None => { config.position = Some(PositionConfig { start: Some(Position { - x: state.monitors.elements()[config.monitor.index].size().left as f32, - y: state.monitors.elements()[config.monitor.index].size().top as f32, + x: state.monitors.elements()[monitor_index].size().left as f32, + y: state.monitors.elements()[monitor_index].size().top as f32, }), end: Some(Position { - x: state.monitors.elements()[config.monitor.index].size().right as f32, + x: state.monitors.elements()[monitor_index].size().right as f32, y: 50.0, }), }) @@ -263,14 +277,14 @@ fn main() -> color_eyre::Result<()> { Some(ref mut position) => { if position.start.is_none() { position.start = Some(Position { - x: state.monitors.elements()[config.monitor.index].size().left as f32, - y: state.monitors.elements()[config.monitor.index].size().top as f32, + x: state.monitors.elements()[monitor_index].size().left as f32, + y: state.monitors.elements()[monitor_index].size().top as f32, }); } if position.end.is_none() { position.end = Some(Position { - x: state.monitors.elements()[config.monitor.index].size().right as f32, + x: state.monitors.elements()[monitor_index].size().right as f32, y: 50.0, }) } @@ -287,15 +301,9 @@ fn main() -> color_eyre::Result<()> { ..Default::default() }; - if let Some(rect) = &config.monitor.work_area_offset { - komorebi_client::send_message(&SocketMessage::MonitorWorkAreaOffset( - config.monitor.index, - *rect, - ))?; - tracing::info!( - "work area offset applied to monitor: {}", - config.monitor.index - ); + if let Some(rect) = &work_area_offset { + komorebi_client::send_message(&SocketMessage::MonitorWorkAreaOffset(monitor_index, *rect))?; + tracing::info!("work area offset applied to monitor: {}", monitor_index); } let (tx_gui, rx_gui) = crossbeam_channel::unbounded(); @@ -330,8 +338,6 @@ fn main() -> color_eyre::Result<()> { "komorebi-bar", native_options, Box::new(|cc| { - let config_cl = config_arc.clone(); - let ctx_repainter = cc.egui_ctx.clone(); std::thread::spawn(move || loop { std::thread::sleep(Duration::from_secs(1)); @@ -374,18 +380,12 @@ fn main() -> color_eyre::Result<()> { tracing::info!("reconnected to komorebi"); - if let Some(rect) = &config_cl.monitor.work_area_offset { - while komorebi_client::send_message( - &SocketMessage::MonitorWorkAreaOffset( - config_cl.monitor.index, - *rect, - ), - ) - .is_err() - { - std::thread::sleep(Duration::from_secs(1)); - } + if let Err(error) = tx_gui.send(KomorebiEvent::Reconnect) { + tracing::error!("could not send komorebi reconnect event to gui thread: {error}") } + + ctx_komorebi.request_repaint(); + continue; } match String::from_utf8(buffer) { @@ -396,7 +396,7 @@ fn main() -> color_eyre::Result<()> { Ok(notification) => { tracing::debug!("received notification from komorebi"); - if let Err(error) = tx_gui.send(notification) { + if let Err(error) = tx_gui.send(KomorebiEvent::Notification(notification)) { tracing::error!("could not send komorebi notification update to gui thread: {error}") } diff --git a/komorebi-bar/src/render.rs b/komorebi-bar/src/render.rs index d6edc81c..87e447ed 100644 --- a/komorebi-bar/src/render.rs +++ b/komorebi-bar/src/render.rs @@ -1,5 +1,6 @@ use crate::bar::Alignment; use crate::config::KomobarConfig; +use crate::config::MonitorConfigOrIndex; use eframe::egui::Color32; use eframe::egui::Context; use eframe::egui::FontId; @@ -81,8 +82,13 @@ impl RenderExt for &KomobarConfig { let mut icon_font_id = text_font_id.clone(); icon_font_id.size *= icon_scale.unwrap_or(1.4).clamp(1.0, 2.0); + let monitor_idx = match &self.monitor { + MonitorConfigOrIndex::MonitorConfig(monitor_config) => monitor_config.index, + MonitorConfigOrIndex::Index(idx) => *idx, + }; + RenderConfig { - monitor_idx: self.monitor.index, + monitor_idx, spacing: self.widget_spacing.unwrap_or(10.0), grouping: self.grouping.unwrap_or(Grouping::None), background_color, diff --git a/komorebi/src/core/mod.rs b/komorebi/src/core/mod.rs index a9706608..f9a994be 100644 --- a/komorebi/src/core/mod.rs +++ b/komorebi/src/core/mod.rs @@ -333,6 +333,7 @@ pub enum StateQuery { FocusedWorkspaceIndex, FocusedContainerIndex, FocusedWindowIndex, + FocusedWorkspaceName, } #[derive( diff --git a/komorebi/src/monitor.rs b/komorebi/src/monitor.rs index d4a151a2..d38ffeb3 100644 --- a/komorebi/src/monitor.rs +++ b/komorebi/src/monitor.rs @@ -107,6 +107,13 @@ impl Monitor { workspace_names: Default::default(), } } + + pub fn focused_workspace_name(&self) -> Option { + self.focused_workspace() + .map(|w| w.name().clone()) + .unwrap_or(None) + } + pub fn load_focused_workspace(&mut self, mouse_follows_focus: bool) -> Result<()> { let focused_idx = self.focused_workspace_idx(); for (i, workspace) in self.workspaces_mut().iter_mut().enumerate() { diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 8489e87f..20f101ba 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -1110,19 +1110,29 @@ impl WindowManager { } SocketMessage::Query(query) => { let response = match query { - StateQuery::FocusedMonitorIndex => self.focused_monitor_idx(), + StateQuery::FocusedMonitorIndex => self.focused_monitor_idx().to_string(), StateQuery::FocusedWorkspaceIndex => self .focused_monitor() .ok_or_else(|| anyhow!("there is no monitor"))? - .focused_workspace_idx(), - StateQuery::FocusedContainerIndex => { - self.focused_workspace()?.focused_container_idx() - } + .focused_workspace_idx() + .to_string(), + StateQuery::FocusedContainerIndex => self + .focused_workspace()? + .focused_container_idx() + .to_string(), StateQuery::FocusedWindowIndex => { - self.focused_container()?.focused_window_idx() + self.focused_container()?.focused_window_idx().to_string() } - } - .to_string(); + StateQuery::FocusedWorkspaceName => { + let focused_monitor = self + .focused_monitor() + .ok_or_else(|| anyhow!("there is no monitor"))?; + + focused_monitor + .focused_workspace_name() + .unwrap_or_else(|| focused_monitor.focused_workspace_idx().to_string()) + } + }; reply.write_all(response.as_bytes())?; } diff --git a/schema.bar.json b/schema.bar.json index 903ec221..718029a9 100644 --- a/schema.bar.json +++ b/schema.bar.json @@ -1214,6 +1214,63 @@ } ] }, + "height": { + "description": "Bar height (default: 50)", + "type": "number", + "format": "float" + }, + "horizontal_margin": { + "description": "Bar horizontal margin. Use one value for symmetric margin or use `[left, right]` to specify a different margin on each side (default: 0)", + "anyOf": [ + { + "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", + "type": "number", + "format": "float" + }, + { + "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, + "horizontal_padding": { + "description": "Bar horizontal padding. Use one value for symmetric padding or use `[left, right]` to specify a different padding on each side (default: 10)", + "anyOf": [ + { + "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", + "type": "number", + "format": "float" + }, + { + "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, "icon_scale": { "description": "Scale of the icons relative to the font_size [[1.0-2.0]]. (default: 1.4)", "type": "number", @@ -2090,51 +2147,62 @@ "format": "float" }, "monitor": { - "description": "Monitor options", - "type": "object", - "required": [ - "index" - ], - "properties": { - "index": { - "description": "Komorebi monitor index of the monitor on which to render the bar", + "description": "The monitor index or the full monitor options", + "anyOf": [ + { + "description": "The monitor index where you want the bar to show", "type": "integer", "format": "uint", "minimum": 0.0 }, - "work_area_offset": { - "description": "Automatically apply a work area offset for this monitor to accommodate the bar", + { + "description": "The full monitor options with the index and an optional work_area_offset", "type": "object", "required": [ - "bottom", - "left", - "right", - "top" + "index" ], "properties": { - "bottom": { - "description": "The bottom point in a Win32 Rect", - "type": "integer", - "format": "int32" - }, - "left": { - "description": "The left point in a Win32 Rect", + "index": { + "description": "Komorebi monitor index of the monitor on which to render the bar", "type": "integer", - "format": "int32" - }, - "right": { - "description": "The right point in a Win32 Rect", - "type": "integer", - "format": "int32" + "format": "uint", + "minimum": 0.0 }, - "top": { - "description": "The top point in a Win32 Rect", - "type": "integer", - "format": "int32" + "work_area_offset": { + "description": "Automatically apply a work area offset for this monitor to accommodate the bar", + "type": "object", + "required": [ + "bottom", + "left", + "right", + "top" + ], + "properties": { + "bottom": { + "description": "The bottom point in a Win32 Rect", + "type": "integer", + "format": "int32" + }, + "left": { + "description": "The left point in a Win32 Rect", + "type": "integer", + "format": "int32" + }, + "right": { + "description": "The right point in a Win32 Rect", + "type": "integer", + "format": "int32" + }, + "top": { + "description": "The top point in a Win32 Rect", + "type": "integer", + "format": "int32" + } + } } } } - } + ] }, "position": { "description": "Bar positioning options", @@ -3427,6 +3495,58 @@ "format": "uint8", "minimum": 0.0 }, + "vertical_margin": { + "description": "Bar vertical margin. Use one value for symmetric margin or use `[top, bottom]` to specify a different margin on each side (default: 0)", + "anyOf": [ + { + "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", + "type": "number", + "format": "float" + }, + { + "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, + "vertical_padding": { + "description": "Bar vertical padding. Use one value for symmetric padding or use `[top, bottom]` to specify a different padding on each side (default: 10)", + "anyOf": [ + { + "description": "A symmetric spacing on some axis (horizontal or vertical), with the same size on both sides", + "type": "number", + "format": "float" + }, + { + "description": "A detailed spacing on some axis (horizontal or vertical), with a size for each side. If an horizontal spacing, then it will be (left, right), if vertical it will be (top, bottom)", + "type": "array", + "items": [ + { + "type": "number", + "format": "float" + }, + { + "type": "number", + "format": "float" + } + ], + "maxItems": 2, + "minItems": 2 + } + ] + }, "widget_spacing": { "description": "Spacing between widgets (default: 10.0)", "type": "number",