From de596d4fca9ca54f9248e788685eee1820bb2381 Mon Sep 17 00:00:00 2001 From: cashmere Date: Wed, 29 Jul 2026 11:31:51 +0200 Subject: configurable focused window widget, tray hidden list, global coords for tray context menu --- README.md | 9 ++++++++ src/bar.rs | 52 ++++++++++++++++++++++++++++++------------- src/config.rs | 29 +++++++++++++++++++++--- src/tray.rs | 4 +++- src/wm_info_provider.rs | 3 +++ src/wm_info_provider/mango.rs | 11 +++++++++ 6 files changed, 89 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 909ac19..0904f91 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,15 @@ expanded = false # start collapsed; the ▸/▾ button toggles the tray show_icon = true # show the item icon show_name = true # show the item title show_description = false # append the item's tooltip description after the title +hidden = [] # item ids or titles to never show, matched case-insensitively + # (left click: Activate, middle click: SecondaryActivate, right click: ContextMenu) + +# Focused window display (next to the tags) +[focused_window] +enabled = true +show_icon = true # show the client's icon (looked up by app_id) +show_app_id = true # show the client's app_id +show_title = true # show the window title # WM-specific options [wm.river] diff --git a/src/bar.rs b/src/bar.rs index 84bbecc..115641d 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -14,7 +14,7 @@ use crate::protocol::*; use crate::shared_state::SharedState; use crate::state::State; use crate::text::{self, ComputedText, RenderOptions}; -use crate::tray::TrayCommand; +use crate::tray::{TrayCommand, TrayItem}; use crate::wm_info_provider::{FocusedWindow, Tag}; pub struct Bar { @@ -159,11 +159,17 @@ impl Bar { let cmd = match button { PointerBtn::Left => Some(TrayCommand::Activate { key }), PointerBtn::Middle => Some(TrayCommand::SecondaryActivate { key }), - PointerBtn::Right => Some(TrayCommand::ContextMenu { - key, - x: x as i32, - y: y as i32, - }), + PointerBtn::Right => { + let (ox, oy) = ss + .wm_info_provider + .get_output_position(&self.output) + .unwrap_or((0, 0)); + Some(TrayCommand::ContextMenu { + key, + x: x as i32 + ox, + y: y as i32 + oy, + }) + } _ => None, }; if let Some(cmd) = cmd { @@ -333,10 +339,13 @@ impl Bar { } // Display focused window - if ss.config.show_focused_window + let fw_cfg = &ss.config.focused_window; + if fw_cfg.enabled && let Some(focused_window) = &self.focused_window { - if let Some(icon) = ss.icon_cache.get(&focused_window.app_id, self.height) { + if fw_cfg.show_icon + && let Some(icon) = ss.icon_cache.get(&focused_window.app_id, self.height) + { let icon_size = height_f - 4.0; let scale = icon_size / (icon.width() as f64).max(icon.height() as f64); let icon_w = icon.width() as f64 * scale; @@ -353,10 +362,12 @@ impl Bar { offset_left += icon_size + 5.0; } let text = self.focused_window_computed.get_or_insert_with(|| { - let label = if focused_window.title.is_empty() { - focused_window.app_id.clone() - } else { - format!("{} - {}", focused_window.app_id, focused_window.title) + let show_title = fw_cfg.show_title && !focused_window.title.is_empty(); + let label = match (fw_cfg.show_app_id, show_title) { + (true, true) => format!("{} - {}", focused_window.app_id, focused_window.title), + (true, false) => focused_window.app_id.clone(), + (false, true) => focused_window.title.clone(), + (false, false) => String::new(), }; ComputedText::new( &label, @@ -469,11 +480,22 @@ impl Bar { full_height: f64, ) -> f64 { self.tray_btns.clear(); - if ss.tray_items.is_empty() { + + let cfg = &ss.config.tray; + let visible: Vec<&TrayItem> = ss + .tray_items + .iter() + .filter(|item| { + !cfg.hidden.iter().any(|hidden| { + hidden.eq_ignore_ascii_case(&item.id) + || hidden.eq_ignore_ascii_case(&item.title) + }) + }) + .collect(); + if visible.is_empty() { return 0.0; } - let cfg = ss.config.tray; let expanded = ss.tray_expanded; let icon_size = full_height - 4.0; let spacing = 8.0; @@ -502,7 +524,7 @@ impl Bar { let mut rendered = Vec::new(); if expanded { - for item in &ss.tray_items { + for item in &visible { let label = match (cfg.show_name, cfg.show_description && !item.tooltip.is_empty()) { (true, true) if item.tooltip != item.title => { format!("{} \u{2014} {}", item.title, item.tooltip) diff --git a/src/config.rs b/src/config.rs index f93d7a8..4e41069 100644 --- a/src/config.rs +++ b/src/config.rs @@ -46,10 +46,11 @@ pub struct Config { pub invert_touchpad_scrolling: bool, pub show_tags: bool, pub show_layout_name: bool, - pub show_focused_window: bool, pub blend: bool, pub show_mode: bool, pub start_hidden: bool, + // focused window + pub focused_window: FocusedWindowConfig, // tray pub tray: TrayConfig, // wm-specific @@ -95,11 +96,11 @@ impl Default for Config { invert_touchpad_scrolling: true, show_tags: true, show_layout_name: true, - show_focused_window: true, blend: true, show_mode: true, start_hidden: false, + focused_window: FocusedWindowConfig::default(), tray: TrayConfig::default(), wm: WmConfig { @@ -193,7 +194,7 @@ impl From for zwlr_layer_shell_v1::Layer { } } -#[derive(Deserialize, Debug, Clone, Copy)] +#[derive(Deserialize, Debug, Clone)] #[serde(deny_unknown_fields, default)] pub struct TrayConfig { pub enabled: bool, @@ -201,6 +202,7 @@ pub struct TrayConfig { pub show_icon: bool, pub show_name: bool, pub show_description: bool, + pub hidden: Vec, } impl Default for TrayConfig { @@ -211,6 +213,27 @@ impl Default for TrayConfig { show_icon: true, show_name: true, show_description: false, + hidden: Vec::new(), + } + } +} + +#[derive(Deserialize, Debug, Clone, Copy)] +#[serde(deny_unknown_fields, default)] +pub struct FocusedWindowConfig { + pub enabled: bool, + pub show_icon: bool, + pub show_app_id: bool, + pub show_title: bool, +} + +impl Default for FocusedWindowConfig { + fn default() -> Self { + Self { + enabled: true, + show_icon: true, + show_app_id: true, + show_title: true, } } } diff --git a/src/tray.rs b/src/tray.rs index 69fa9e2..1caeb3a 100644 --- a/src/tray.rs +++ b/src/tray.rs @@ -13,6 +13,7 @@ use zbus::{Connection, interface, proxy}; #[derive(Clone)] pub struct TrayItem { pub key: String, + pub id: String, pub title: String, pub icon_name: String, pub icon_theme_path: String, @@ -398,10 +399,11 @@ async fn run( unique_name, announced, status, - id, + id: id.clone(), proxy, item: TrayItem { key, + id, title, icon_name, icon_theme_path, diff --git a/src/wm_info_provider.rs b/src/wm_info_provider.rs index f46c4e0..4e27312 100644 --- a/src/wm_info_provider.rs +++ b/src/wm_info_provider.rs @@ -50,6 +50,9 @@ pub trait WmInfoProvider: Any { fn get_focused_window(&self, _: &Output) -> Option { None } + fn get_output_position(&self, _: &Output) -> Option<(i32, i32)> { + None + } fn click_on_tag( &mut self, diff --git a/src/wm_info_provider/mango.rs b/src/wm_info_provider/mango.rs index 5476f5e..9b2c9cc 100644 --- a/src/wm_info_provider/mango.rs +++ b/src/wm_info_provider/mango.rs @@ -27,6 +27,10 @@ struct MangoTag { struct MangoMonitor { name: String, active: bool, + #[serde(default)] + x: i32, + #[serde(default)] + y: i32, tags: Vec, layout_symbol: String, #[serde(default)] @@ -179,6 +183,13 @@ impl WmInfoProvider for MangoInfoProvider { }) } + fn get_output_position(&self, output: &Output) -> Option<(i32, i32)> { + self.all_monitors + .iter() + .find(|m| m.name == output.name) + .map(|m| (m.x, m.y)) + } + fn click_on_tag( &mut self, _conn: &mut Connection, -- cgit v1.3.1