diff --git a/komorebi/src/container.rs b/komorebi/src/container.rs index 01f4c947..6bae851e 100644 --- a/komorebi/src/container.rs +++ b/komorebi/src/container.rs @@ -7,20 +7,13 @@ use serde::Deserialize; use serde::Serialize; use crate::ring::Ring; -use crate::stackbar::Stackbar; use crate::window::Window; -use crate::WindowsApi; -use crate::STACKBAR_MODE; -use komorebi_core::StackbarMode; #[derive(Debug, Clone, Serialize, Deserialize, Getters, JsonSchema)] pub struct Container { #[getset(get = "pub")] id: String, windows: Ring, - #[serde(skip)] - #[getset(get = "pub", get_mut = "pub")] - stackbar: Option, } impl_ring_elements!(Container, Window); @@ -30,10 +23,6 @@ impl Default for Container { Self { id: nanoid!(), windows: Ring::default(), - stackbar: match STACKBAR_MODE.load() { - StackbarMode::Always => Stackbar::create().ok(), - StackbarMode::Never | StackbarMode::OnStack => None, - }, } } } @@ -46,10 +35,6 @@ impl PartialEq for Container { impl Container { pub fn hide(&self, omit: Option) { - if let Some(stackbar) = self.stackbar() { - stackbar.hide(); - } - for window in self.windows().iter().rev() { let mut should_hide = omit.is_none(); @@ -68,10 +53,6 @@ impl Container { } pub fn restore(&self) { - if let Some(stackbar) = self.stackbar() { - stackbar.restore(); - } - if let Some(window) = self.focused_window() { window.restore(); } @@ -124,13 +105,6 @@ impl Container { pub fn remove_window_by_idx(&mut self, idx: usize) -> Option { let window = self.windows_mut().remove(idx); - if matches!(STACKBAR_MODE.load(), StackbarMode::OnStack) && self.windows().len() <= 1 { - if let Some(stackbar) = &self.stackbar { - let _ = WindowsApi::close_window(stackbar.hwnd()); - self.stackbar = None; - } - } - if idx != 0 { self.focus_window(idx - 1); }; @@ -145,14 +119,6 @@ impl Container { pub fn add_window(&mut self, window: Window) { self.windows_mut().push_back(window); - - if matches!(STACKBAR_MODE.load(), StackbarMode::OnStack) - && self.windows().len() > 1 - && self.stackbar.is_none() - { - self.stackbar = Stackbar::create().ok(); - } - self.focus_window(self.windows().len() - 1); } @@ -161,41 +127,4 @@ impl Container { tracing::info!("focusing window"); self.windows.focus(idx); } - - pub fn set_stackbar_mode(&mut self, mode: StackbarMode) { - match mode { - StackbarMode::Always => { - if self.stackbar.is_none() { - self.stackbar = Stackbar::create().ok(); - } - } - StackbarMode::Never => { - if let Some(stackbar) = &self.stackbar { - let _ = WindowsApi::close_window(stackbar.hwnd()); - } - - self.stackbar = None - } - StackbarMode::OnStack => { - if self.windows().len() > 1 && self.stackbar().is_none() { - self.stackbar = Stackbar::create().ok(); - } - - if let Some(stackbar) = &self.stackbar { - if self.windows().len() == 1 { - let _ = WindowsApi::close_window(stackbar.hwnd()); - self.stackbar = None; - } - } - } - } - } - - pub fn renew_stackbar(&mut self) { - if let Some(stackbar) = &self.stackbar { - if !WindowsApi::is_window(stackbar.hwnd()) { - self.stackbar = Stackbar::create().ok() - } - } - } } diff --git a/komorebi/src/lib.rs b/komorebi/src/lib.rs index c20088fe..260b16a8 100644 --- a/komorebi/src/lib.rs +++ b/komorebi/src/lib.rs @@ -10,7 +10,7 @@ pub mod process_command; pub mod process_event; pub mod process_movement; pub mod set_window_position; -pub mod stackbar; +pub mod stackbar_manager; pub mod static_config; pub mod styles; pub mod window; @@ -40,7 +40,6 @@ use std::sync::Arc; pub use colour::*; pub use process_command::*; pub use process_event::*; -pub use stackbar::*; pub use static_config::*; pub use window::*; pub use window_manager::*; @@ -49,7 +48,6 @@ pub use windows_api::WindowsApi; pub use windows_api::*; use color_eyre::Result; -use crossbeam_utils::atomic::AtomicCell; use komorebi_core::config_generation::IdWithIdentifier; use komorebi_core::config_generation::MatchingRule; use komorebi_core::config_generation::MatchingStrategy; @@ -57,8 +55,6 @@ use komorebi_core::ApplicationIdentifier; use komorebi_core::HidingBehaviour; use komorebi_core::Rect; use komorebi_core::SocketMessage; -use komorebi_core::StackbarLabel; -use komorebi_core::StackbarMode; use os_info::Version; use parking_lot::Mutex; use regex::Regex; @@ -215,14 +211,6 @@ pub static SESSION_ID: AtomicU32 = AtomicU32::new(0); pub static REMOVE_TITLEBARS: AtomicBool = AtomicBool::new(false); -pub static STACKBAR_FOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(16777215); // white -pub static STACKBAR_UNFOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(11776947); // gray text -pub static STACKBAR_TAB_BACKGROUND_COLOUR: AtomicU32 = AtomicU32::new(3355443); // gray -pub static STACKBAR_TAB_HEIGHT: AtomicI32 = AtomicI32::new(40); -pub static STACKBAR_TAB_WIDTH: AtomicI32 = AtomicI32::new(200); -pub static STACKBAR_LABEL: AtomicCell = AtomicCell::new(StackbarLabel::Process); -pub static STACKBAR_MODE: AtomicCell = AtomicCell::new(StackbarMode::Never); - #[must_use] pub fn current_virtual_desktop() -> Option> { let hkcu = RegKey::predef(HKEY_CURRENT_USER); diff --git a/komorebi/src/main.rs b/komorebi/src/main.rs index 8b80af7f..6ba1663e 100644 --- a/komorebi/src/main.rs +++ b/komorebi/src/main.rs @@ -30,6 +30,7 @@ use komorebi::process_command::listen_for_commands; use komorebi::process_command::listen_for_commands_tcp; use komorebi::process_event::listen_for_events; use komorebi::process_movement::listen_for_movements; +use komorebi::stackbar_manager; use komorebi::static_config::StaticConfig; use komorebi::window_manager::WindowManager; use komorebi::windows_api::WindowsApi; @@ -254,6 +255,7 @@ fn main() -> Result<()> { } border_manager::listen_for_notifications(wm.clone()); + stackbar_manager::listen_for_notifications(wm.clone()); workspace_reconciliator::listen_for_notifications(wm.clone()); monitor_reconciliator::listen_for_notifications(wm.clone())?; diff --git a/komorebi/src/process_command.rs b/komorebi/src/process_command.rs index 0a2e99ba..47d93628 100644 --- a/komorebi/src/process_command.rs +++ b/komorebi/src/process_command.rs @@ -43,6 +43,7 @@ use crate::border_manager::STYLE; use crate::colour::Rgb; use crate::current_virtual_desktop; use crate::notify_subscribers; +use crate::stackbar_manager; use crate::static_config::StaticConfig; use crate::window::RuleDebug; use crate::window::Window; @@ -64,18 +65,18 @@ use crate::MONITOR_INDEX_PREFERENCES; use crate::NO_TITLEBAR; use crate::OBJECT_NAME_CHANGE_ON_LAUNCH; use crate::REMOVE_TITLEBARS; -use crate::STACKBAR_FOCUSED_TEXT_COLOUR; -use crate::STACKBAR_LABEL; -use crate::STACKBAR_MODE; -use crate::STACKBAR_TAB_BACKGROUND_COLOUR; -use crate::STACKBAR_TAB_HEIGHT; -use crate::STACKBAR_TAB_WIDTH; -use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR; use crate::SUBSCRIPTION_PIPES; use crate::SUBSCRIPTION_SOCKETS; use crate::TCP_CONNECTIONS; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::WORKSPACE_RULES; +use stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR; +use stackbar_manager::STACKBAR_LABEL; +use stackbar_manager::STACKBAR_MODE; +use stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR; +use stackbar_manager::STACKBAR_TAB_HEIGHT; +use stackbar_manager::STACKBAR_TAB_WIDTH; +use stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR; #[tracing::instrument] pub fn listen_for_commands(wm: Arc>) { @@ -1245,14 +1246,6 @@ impl WindowManager { } SocketMessage::StackbarMode(mode) => { STACKBAR_MODE.store(mode); - - for m in self.monitors_mut() { - for w in m.workspaces_mut() { - for c in w.containers_mut() { - c.set_stackbar_mode(mode); - } - } - } } SocketMessage::StackbarLabel(label) => { STACKBAR_LABEL.store(label); @@ -1342,6 +1335,7 @@ impl WindowManager { notify_subscribers(&serde_json::to_string(¬ification)?)?; border_manager::event_tx().send(border_manager::Notification)?; + stackbar_manager::event_tx().send(stackbar_manager::Notification)?; tracing::info!("processed"); Ok(()) diff --git a/komorebi/src/process_event.rs b/komorebi/src/process_event.rs index 821ee4b2..94701736 100644 --- a/komorebi/src/process_event.rs +++ b/komorebi/src/process_event.rs @@ -11,7 +11,6 @@ use parking_lot::Mutex; use komorebi_core::OperationDirection; use komorebi_core::Rect; use komorebi_core::Sizing; -use komorebi_core::StackbarLabel; use komorebi_core::WindowContainerBehaviour; use crate::border_manager; @@ -19,12 +18,12 @@ use crate::border_manager::BORDER_OFFSET; use crate::border_manager::BORDER_WIDTH; use crate::current_virtual_desktop; use crate::notify_subscribers; +use crate::stackbar_manager; use crate::window::should_act; use crate::window::RuleDebug; use crate::window_manager::WindowManager; use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; -use crate::winevent::WinEvent; use crate::workspace_reconciliator; use crate::workspace_reconciliator::ALT_TAB_HWND; use crate::workspace_reconciliator::ALT_TAB_HWND_INSTANT; @@ -33,7 +32,6 @@ use crate::NotificationEvent; use crate::DATA_DIR; use crate::HIDDEN_HWNDS; use crate::REGEX_IDENTIFIERS; -use crate::STACKBAR_LABEL; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; #[tracing::instrument] @@ -269,23 +267,6 @@ impl WindowManager { WindowManagerEvent::Show(_, window) | WindowManagerEvent::Manage(window) | WindowManagerEvent::Uncloak(_, window) => { - if matches!( - event, - WindowManagerEvent::Show(WinEvent::ObjectNameChange, _) - ) { - if matches!(STACKBAR_LABEL.load(), StackbarLabel::Title) { - for m in self.monitors() { - for ws in m.workspaces() { - if let Some(container) = ws.container_for_window(window.hwnd) { - if let Some(stackbar) = container.stackbar() { - stackbar.update(container.windows(), window.hwnd)?; - } - } - } - } - } - } - let focused_monitor_idx = self.focused_monitor_idx(); let focused_workspace_idx = self.focused_workspace_idx_for_monitor_idx(focused_monitor_idx)?; @@ -461,10 +442,7 @@ impl WindowManager { // If we have moved across the monitors, use that override, otherwise determine // if a move has taken place by ruling out a resize - let right_bottom_constant = ((BORDER_WIDTH.load(Ordering::SeqCst) - + BORDER_OFFSET.load(Ordering::SeqCst)) - * 2) - .abs(); + let right_bottom_constant = 0; let is_move = moved_across_monitors || resize.right.abs() == right_bottom_constant @@ -640,6 +618,7 @@ impl WindowManager { notify_subscribers(&serde_json::to_string(¬ification)?)?; border_manager::event_tx().send(border_manager::Notification)?; + stackbar_manager::event_tx().send(stackbar_manager::Notification)?; tracing::info!("processed: {}", event.window().to_string()); Ok(()) diff --git a/komorebi/src/stackbar.rs b/komorebi/src/stackbar.rs deleted file mode 100644 index 5d15fa5e..00000000 --- a/komorebi/src/stackbar.rs +++ /dev/null @@ -1,275 +0,0 @@ -use std::collections::VecDeque; -use std::sync::atomic::Ordering; -use std::time::Duration; - -use color_eyre::eyre::Result; -use schemars::JsonSchema; -use serde::Deserialize; -use serde::Serialize; -use windows::core::PCWSTR; -use windows::Win32::Foundation::COLORREF; -use windows::Win32::Foundation::HWND; -use windows::Win32::Foundation::LPARAM; -use windows::Win32::Foundation::LRESULT; -use windows::Win32::Foundation::WPARAM; -use windows::Win32::Graphics::Gdi::CreateFontIndirectW; -use windows::Win32::Graphics::Gdi::CreatePen; -use windows::Win32::Graphics::Gdi::CreateSolidBrush; -use windows::Win32::Graphics::Gdi::DrawTextW; -use windows::Win32::Graphics::Gdi::GetDC; -use windows::Win32::Graphics::Gdi::ReleaseDC; -use windows::Win32::Graphics::Gdi::SelectObject; -use windows::Win32::Graphics::Gdi::SetBkColor; -use windows::Win32::Graphics::Gdi::SetTextColor; -use windows::Win32::Graphics::Gdi::DT_CENTER; -use windows::Win32::Graphics::Gdi::DT_END_ELLIPSIS; -use windows::Win32::Graphics::Gdi::DT_SINGLELINE; -use windows::Win32::Graphics::Gdi::DT_VCENTER; -use windows::Win32::Graphics::Gdi::FONT_QUALITY; -use windows::Win32::Graphics::Gdi::FW_BOLD; -use windows::Win32::Graphics::Gdi::LOGFONTW; -use windows::Win32::Graphics::Gdi::PROOF_QUALITY; -use windows::Win32::Graphics::Gdi::PS_SOLID; -use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW; -use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW; -use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW; -use windows::Win32::UI::WindowsAndMessaging::GetMessageW; -use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage; -use windows::Win32::UI::WindowsAndMessaging::RegisterClassW; -use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes; -use windows::Win32::UI::WindowsAndMessaging::TranslateMessage; -use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW; -use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW; -use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY; -use windows::Win32::UI::WindowsAndMessaging::MSG; -use windows::Win32::UI::WindowsAndMessaging::SW_SHOW; -use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY; -use windows::Win32::UI::WindowsAndMessaging::WM_LBUTTONDOWN; -use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW; -use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED; -use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW; -use windows::Win32::UI::WindowsAndMessaging::WS_POPUP; -use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE; - -use komorebi_core::Rect; - -use crate::window::Window; -use crate::windows_api::WindowsApi; -use crate::StackbarLabel; -use crate::DEFAULT_CONTAINER_PADDING; -use crate::STACKBAR_FOCUSED_TEXT_COLOUR; -use crate::STACKBAR_LABEL; -use crate::STACKBAR_TAB_BACKGROUND_COLOUR; -use crate::STACKBAR_TAB_HEIGHT; -use crate::STACKBAR_TAB_WIDTH; -use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR; -use crate::WINDOWS_BY_BAR_HWNDS; - -#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)] -pub struct Stackbar { - pub hwnd: isize, -} - -impl Stackbar { - unsafe extern "system" fn window_proc( - hwnd: HWND, - msg: u32, - w_param: WPARAM, - l_param: LPARAM, - ) -> LRESULT { - match msg { - WM_LBUTTONDOWN => { - let win_hwnds_by_topbar = WINDOWS_BY_BAR_HWNDS.lock(); - if let Some(win_hwnds) = win_hwnds_by_topbar.get(&hwnd.0) { - let x = l_param.0 as i32 & 0xFFFF; - let y = (l_param.0 as i32 >> 16) & 0xFFFF; - - let width = STACKBAR_TAB_WIDTH.load(Ordering::SeqCst); - let height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst); - let gap = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst); - - for (index, win_hwnd) in win_hwnds.iter().enumerate() { - let left = gap + (index as i32 * (width + gap)); - let right = left + width; - let top = 0; - let bottom = height; - - if x >= left && x <= right && y >= top && y <= bottom { - let window = Window { hwnd: *win_hwnd }; - window.restore(); - if let Err(err) = window.focus(false) { - tracing::error!("Stackbar focus error: HWND:{} {}", *win_hwnd, err); - } - } - } - } - - WINDOWS_BY_BAR_HWNDS.force_unlock(); - LRESULT(0) - } - WM_DESTROY => { - PostQuitMessage(0); - LRESULT(0) - } - _ => DefWindowProcW(hwnd, msg, w_param, l_param), - } - } - - pub const fn hwnd(&self) -> HWND { - HWND(self.hwnd) - } - - pub fn create() -> Result { - let name: Vec = "komorebi_stackbar\0".encode_utf16().collect(); - let class_name = PCWSTR(name.as_ptr()); - - let h_module = WindowsApi::module_handle_w()?; - - let wnd_class = WNDCLASSW { - style: CS_HREDRAW | CS_VREDRAW, - lpfnWndProc: Some(Self::window_proc), - hInstance: h_module.into(), - lpszClassName: class_name, - hbrBackground: WindowsApi::create_solid_brush(0), - ..Default::default() - }; - - unsafe { - RegisterClassW(&wnd_class); - } - - let (hwnd_sender, hwnd_receiver) = crossbeam_channel::bounded::(1); - - let name_cl = name.clone(); - std::thread::spawn(move || -> Result<()> { - unsafe { - let hwnd = CreateWindowExW( - WS_EX_TOOLWINDOW | WS_EX_LAYERED, - PCWSTR(name_cl.as_ptr()), - PCWSTR(name_cl.as_ptr()), - WS_POPUP | WS_VISIBLE, - 0, - 0, - 0, - 0, - None, - None, - h_module, - None, - ); - - SetLayeredWindowAttributes(hwnd, COLORREF(0), 0, LWA_COLORKEY)?; - hwnd_sender.send(hwnd)?; - - let mut msg = MSG::default(); - while GetMessageW(&mut msg, hwnd, 0, 0).into() { - TranslateMessage(&msg); - DispatchMessageW(&msg); - std::thread::sleep(Duration::from_millis(10)); - } - } - - Ok(()) - }); - - Ok(Self { - hwnd: hwnd_receiver.recv()?.0, - }) - } - - pub fn set_position(&self, layout: &Rect, top: bool) -> Result<()> { - WindowsApi::position_window(self.hwnd(), layout, top) - } - - pub fn get_position_from_container_layout(&self, layout: &Rect) -> Rect { - Rect { - bottom: STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst), - ..*layout - } - } - - pub fn update(&self, windows: &VecDeque, focused_hwnd: isize) -> Result<()> { - let width = STACKBAR_TAB_WIDTH.load(Ordering::SeqCst); - let height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst); - let gap = DEFAULT_CONTAINER_PADDING.load(Ordering::SeqCst); - let background = STACKBAR_TAB_BACKGROUND_COLOUR.load(Ordering::SeqCst); - let focused_text_colour = STACKBAR_FOCUSED_TEXT_COLOUR.load(Ordering::SeqCst); - let unfocused_text_colour = STACKBAR_UNFOCUSED_TEXT_COLOUR.load(Ordering::SeqCst); - - unsafe { - let hdc = GetDC(self.hwnd()); - - let hpen = CreatePen(PS_SOLID, 0, COLORREF(background)); - let hbrush = CreateSolidBrush(COLORREF(background)); - - SelectObject(hdc, hpen); - SelectObject(hdc, hbrush); - SetBkColor(hdc, COLORREF(background)); - - let hfont = CreateFontIndirectW(&LOGFONTW { - lfWeight: FW_BOLD.0 as i32, - lfQuality: FONT_QUALITY(PROOF_QUALITY.0), - ..Default::default() - }); - - SelectObject(hdc, hfont); - - for (i, window) in windows.iter().enumerate() { - if window.hwnd == focused_hwnd { - SetTextColor(hdc, COLORREF(focused_text_colour)); - } else { - SetTextColor(hdc, COLORREF(unfocused_text_colour)); - } - - let left = gap + (i as i32 * (width + gap)); - let mut tab_box = Rect { - top: 0, - left, - right: left + width, - bottom: height, - }; - - WindowsApi::round_rect(hdc, &tab_box, 8); - - let label = match STACKBAR_LABEL.load() { - StackbarLabel::Process => { - let exe = window.exe()?; - exe.trim_end_matches(".exe").to_string() - } - StackbarLabel::Title => window.title()?, - }; - - let mut tab_title: Vec = label.encode_utf16().collect(); - - tab_box.left_padding(10); - tab_box.right_padding(10); - - DrawTextW( - hdc, - &mut tab_title, - &mut tab_box.into(), - DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS, - ); - } - - ReleaseDC(self.hwnd(), hdc); - } - - let mut windows_hwdns: VecDeque = VecDeque::new(); - for window in windows { - windows_hwdns.push_back(window.hwnd); - } - - WINDOWS_BY_BAR_HWNDS.lock().insert(self.hwnd, windows_hwdns); - - Ok(()) - } - - pub fn hide(&self) { - WindowsApi::hide_window(self.hwnd()) - } - - pub fn restore(&self) { - WindowsApi::show_window(self.hwnd(), SW_SHOW) - } -} diff --git a/komorebi/src/stackbar_manager/mod.rs b/komorebi/src/stackbar_manager/mod.rs new file mode 100644 index 00000000..1cf3d13f --- /dev/null +++ b/komorebi/src/stackbar_manager/mod.rs @@ -0,0 +1,189 @@ +mod stackbar; + +use crate::container::Container; +use crate::stackbar_manager::stackbar::Stackbar; +use crate::WindowManager; +use crate::WindowsApi; +use crate::DEFAULT_CONTAINER_PADDING; +use crossbeam_channel::Receiver; +use crossbeam_channel::Sender; +use crossbeam_utils::atomic::AtomicCell; +use crossbeam_utils::atomic::AtomicConsume; +use komorebi_core::StackbarLabel; +use komorebi_core::StackbarMode; +use lazy_static::lazy_static; +use parking_lot::Mutex; +use std::collections::hash_map::Entry; +use std::collections::HashMap; +use std::sync::atomic::AtomicI32; +use std::sync::atomic::AtomicU32; +use std::sync::Arc; +use std::sync::OnceLock; +use windows::Win32::Foundation::HWND; + +pub static STACKBAR_FOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(16777215); // white +pub static STACKBAR_UNFOCUSED_TEXT_COLOUR: AtomicU32 = AtomicU32::new(11776947); // gray text +pub static STACKBAR_TAB_BACKGROUND_COLOUR: AtomicU32 = AtomicU32::new(3355443); // gray +pub static STACKBAR_TAB_HEIGHT: AtomicI32 = AtomicI32::new(40); +pub static STACKBAR_TAB_WIDTH: AtomicI32 = AtomicI32::new(200); +pub static STACKBAR_LABEL: AtomicCell = AtomicCell::new(StackbarLabel::Process); +pub static STACKBAR_MODE: AtomicCell = AtomicCell::new(StackbarMode::OnStack); + +lazy_static! { + pub static ref STACKBAR_STATE: Mutex> = Mutex::new(HashMap::new()); + static ref STACKBARS_MONITORS: Mutex> = Mutex::new(HashMap::new()); + static ref STACKBARS_CONTAINERS: Mutex> = Mutex::new(HashMap::new()); +} + +pub struct Notification; + +static CHANNEL: OnceLock<(Sender, Receiver)> = OnceLock::new(); + +pub fn channel() -> &'static (Sender, Receiver) { + CHANNEL.get_or_init(crossbeam_channel::unbounded) +} + +pub fn event_tx() -> Sender { + channel().0.clone() +} + +pub fn event_rx() -> Receiver { + channel().1.clone() +} + +pub fn should_have_stackbar(window_count: usize) -> bool { + match STACKBAR_MODE.load() { + StackbarMode::Always => true, + StackbarMode::OnStack => window_count > 1, + StackbarMode::Never => false, + } +} + +pub fn listen_for_notifications(wm: Arc>) { + std::thread::spawn(move || loop { + match handle_notifications(wm.clone()) { + Ok(()) => { + tracing::warn!("restarting finished thread"); + } + Err(error) => { + tracing::warn!("restarting failed thread: {}", error); + } + } + }); +} + +pub fn handle_notifications(wm: Arc>) -> color_eyre::Result<()> { + tracing::info!("listening"); + + let receiver = event_rx(); + + 'receiver: for _ in receiver { + let mut stackbars = STACKBAR_STATE.lock(); + let mut stackbars_monitors = STACKBARS_MONITORS.lock(); + + // Check the wm state every time we receive a notification + let mut state = wm.lock(); + + // If stackbars are disabled + if matches!(STACKBAR_MODE.load(), StackbarMode::Never) { + for (_, stackbar) in stackbars.iter() { + stackbar.destroy()?; + } + + stackbars.clear(); + continue 'receiver; + } + + for (monitor_idx, m) in state.monitors_mut().iter_mut().enumerate() { + // Only operate on the focused workspace of each monitor + if let Some(ws) = m.focused_workspace_mut() { + // Workspaces with tiling disabled don't have stackbars + if !ws.tile() { + let mut to_remove = vec![]; + for (id, border) in stackbars.iter() { + if stackbars_monitors.get(id).copied().unwrap_or_default() == monitor_idx { + border.destroy()?; + to_remove.push(id.clone()); + } + } + + for id in &to_remove { + stackbars.remove(id); + } + + continue 'receiver; + } + + let is_maximized = WindowsApi::is_zoomed(HWND( + WindowsApi::foreground_window().unwrap_or_default(), + )); + + // Handle the monocle container separately + if ws.monocle_container().is_some() || is_maximized { + // Destroy any stackbars associated with the focused workspace + let mut to_remove = vec![]; + for (id, stackbar) in stackbars.iter() { + if stackbars_monitors.get(id).copied().unwrap_or_default() == monitor_idx { + stackbar.destroy()?; + to_remove.push(id.clone()); + } + } + + for id in &to_remove { + stackbars.remove(id); + } + + continue 'receiver; + } + + let container_padding = ws + .container_padding() + .unwrap_or_else(|| DEFAULT_CONTAINER_PADDING.load_consume()); + + 'containers: for container in ws.containers_mut() { + let should_add_stackbar = match STACKBAR_MODE.load() { + StackbarMode::Always => true, + StackbarMode::OnStack => container.windows().len() > 1, + StackbarMode::Never => false, + }; + + if !should_add_stackbar { + if let Some(stackbar) = stackbars.get(container.id()) { + stackbar.destroy()? + } + + stackbars.remove(container.id()); + stackbars_monitors.remove(container.id()); + continue 'containers; + } + + // Get the stackbar entry for this container from the map or create one + let stackbar = match stackbars.entry(container.id().clone()) { + Entry::Occupied(entry) => entry.into_mut(), + Entry::Vacant(entry) => { + if let Ok(stackbar) = Stackbar::create(container.id()) { + entry.insert(stackbar) + } else { + continue 'receiver; + } + } + }; + + stackbars_monitors.insert(container.id().clone(), monitor_idx); + + let rect = WindowsApi::window_rect( + container + .focused_window() + .copied() + .unwrap_or_default() + .hwnd(), + )?; + + stackbar.update(container_padding, container, &rect)?; + } + } + } + } + + Ok(()) +} diff --git a/komorebi/src/stackbar_manager/stackbar.rs b/komorebi/src/stackbar_manager/stackbar.rs new file mode 100644 index 00000000..a6c5621b --- /dev/null +++ b/komorebi/src/stackbar_manager/stackbar.rs @@ -0,0 +1,328 @@ +use crate::border_manager::BORDER_OFFSET; +use crate::border_manager::BORDER_WIDTH; +use crate::border_manager::STYLE; +use crate::container::Container; +use crate::stackbar_manager::STACKBARS_CONTAINERS; +use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR; +use crate::stackbar_manager::STACKBAR_LABEL; +use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR; +use crate::stackbar_manager::STACKBAR_TAB_HEIGHT; +use crate::stackbar_manager::STACKBAR_TAB_WIDTH; +use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR; +use crate::WindowsApi; +use crate::DEFAULT_CONTAINER_PADDING; +use crate::WINDOWS_11; +use crossbeam_utils::atomic::AtomicConsume; +use komorebi_core::BorderStyle; +use komorebi_core::Rect; +use komorebi_core::StackbarLabel; +use std::sync::mpsc; +use std::time::Duration; +use windows::core::PCWSTR; +use windows::Win32::Foundation::COLORREF; +use windows::Win32::Foundation::HWND; +use windows::Win32::Foundation::LPARAM; +use windows::Win32::Foundation::LRESULT; +use windows::Win32::Foundation::WPARAM; +use windows::Win32::Graphics::Gdi::CreateFontIndirectW; +use windows::Win32::Graphics::Gdi::CreatePen; +use windows::Win32::Graphics::Gdi::CreateSolidBrush; +use windows::Win32::Graphics::Gdi::DrawTextW; +use windows::Win32::Graphics::Gdi::GetDC; +use windows::Win32::Graphics::Gdi::Rectangle; +use windows::Win32::Graphics::Gdi::ReleaseDC; +use windows::Win32::Graphics::Gdi::RoundRect; +use windows::Win32::Graphics::Gdi::SelectObject; +use windows::Win32::Graphics::Gdi::SetBkColor; +use windows::Win32::Graphics::Gdi::SetTextColor; +use windows::Win32::Graphics::Gdi::DT_CENTER; +use windows::Win32::Graphics::Gdi::DT_END_ELLIPSIS; +use windows::Win32::Graphics::Gdi::DT_SINGLELINE; +use windows::Win32::Graphics::Gdi::DT_VCENTER; +use windows::Win32::Graphics::Gdi::FONT_QUALITY; +use windows::Win32::Graphics::Gdi::FW_BOLD; +use windows::Win32::Graphics::Gdi::LOGFONTW; +use windows::Win32::Graphics::Gdi::PROOF_QUALITY; +use windows::Win32::Graphics::Gdi::PS_SOLID; +use windows::Win32::UI::WindowsAndMessaging::CreateWindowExW; +use windows::Win32::UI::WindowsAndMessaging::DefWindowProcW; +use windows::Win32::UI::WindowsAndMessaging::DispatchMessageW; +use windows::Win32::UI::WindowsAndMessaging::GetMessageW; +use windows::Win32::UI::WindowsAndMessaging::PostQuitMessage; +use windows::Win32::UI::WindowsAndMessaging::SetLayeredWindowAttributes; +use windows::Win32::UI::WindowsAndMessaging::TranslateMessage; +use windows::Win32::UI::WindowsAndMessaging::CS_HREDRAW; +use windows::Win32::UI::WindowsAndMessaging::CS_VREDRAW; +use windows::Win32::UI::WindowsAndMessaging::LWA_COLORKEY; +use windows::Win32::UI::WindowsAndMessaging::MSG; +use windows::Win32::UI::WindowsAndMessaging::WM_DESTROY; +use windows::Win32::UI::WindowsAndMessaging::WM_LBUTTONDOWN; +use windows::Win32::UI::WindowsAndMessaging::WNDCLASSW; +use windows::Win32::UI::WindowsAndMessaging::WS_EX_LAYERED; +use windows::Win32::UI::WindowsAndMessaging::WS_EX_TOOLWINDOW; +use windows::Win32::UI::WindowsAndMessaging::WS_POPUP; +use windows::Win32::UI::WindowsAndMessaging::WS_VISIBLE; + +#[derive(Debug)] +pub struct Stackbar { + pub hwnd: isize, +} + +impl From for Stackbar { + fn from(value: isize) -> Self { + Self { hwnd: value } + } +} + +impl Stackbar { + pub const fn hwnd(&self) -> HWND { + HWND(self.hwnd) + } + + pub fn create(id: &str) -> color_eyre::Result { + let name: Vec = format!("komostackbar-{id}\0").encode_utf16().collect(); + let class_name = PCWSTR(name.as_ptr()); + + let h_module = WindowsApi::module_handle_w()?; + + let window_class = WNDCLASSW { + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(Self::callback), + hInstance: h_module.into(), + lpszClassName: class_name, + hbrBackground: WindowsApi::create_solid_brush(0), + ..Default::default() + }; + + let _ = WindowsApi::register_class_w(&window_class); + + let (hwnd_sender, hwnd_receiver) = mpsc::channel(); + + let name_cl = name.clone(); + std::thread::spawn(move || -> color_eyre::Result<()> { + unsafe { + let hwnd = CreateWindowExW( + WS_EX_TOOLWINDOW | WS_EX_LAYERED, + PCWSTR(name_cl.as_ptr()), + PCWSTR(name_cl.as_ptr()), + WS_POPUP | WS_VISIBLE, + 0, + 0, + 0, + 0, + None, + None, + h_module, + None, + ); + + SetLayeredWindowAttributes(hwnd, COLORREF(0), 0, LWA_COLORKEY)?; + hwnd_sender.send(hwnd)?; + + let mut msg = MSG::default(); + while GetMessageW(&mut msg, hwnd, 0, 0).into() { + TranslateMessage(&msg); + DispatchMessageW(&msg); + std::thread::sleep(Duration::from_millis(10)); + } + } + + Ok(()) + }); + + Ok(Self { + hwnd: hwnd_receiver.recv()?.0, + }) + } + + pub fn destroy(&self) -> color_eyre::Result<()> { + WindowsApi::close_window(self.hwnd()) + } + + pub fn update( + &self, + container_padding: i32, + container: &mut Container, + layout: &Rect, + ) -> color_eyre::Result<()> { + let width = STACKBAR_TAB_WIDTH.load_consume(); + let height = STACKBAR_TAB_HEIGHT.load_consume(); + let gap = DEFAULT_CONTAINER_PADDING.load_consume(); + let background = STACKBAR_TAB_BACKGROUND_COLOUR.load_consume(); + let focused_text_colour = STACKBAR_FOCUSED_TEXT_COLOUR.load_consume(); + let unfocused_text_colour = STACKBAR_UNFOCUSED_TEXT_COLOUR.load_consume(); + + let mut stackbars_containers = STACKBARS_CONTAINERS.lock(); + stackbars_containers.insert(self.hwnd, container.clone()); + + let mut layout = *layout; + let workspace_specific_offset = + BORDER_WIDTH.load_consume() + BORDER_OFFSET.load_consume() + container_padding; + + layout.top -= workspace_specific_offset + STACKBAR_TAB_HEIGHT.load_consume(); + layout.left -= workspace_specific_offset; + + WindowsApi::position_window(self.hwnd(), &layout, false)?; + + unsafe { + let hdc = GetDC(self.hwnd()); + + let hpen = CreatePen(PS_SOLID, 0, COLORREF(background)); + let hbrush = CreateSolidBrush(COLORREF(background)); + + SelectObject(hdc, hpen); + SelectObject(hdc, hbrush); + SetBkColor(hdc, COLORREF(background)); + + let hfont = CreateFontIndirectW(&LOGFONTW { + lfWeight: FW_BOLD.0 as i32, + lfQuality: FONT_QUALITY(PROOF_QUALITY.0), + ..Default::default() + }); + + SelectObject(hdc, hfont); + + for (i, window) in container.windows().iter().enumerate() { + if window.hwnd == container.focused_window().copied().unwrap_or_default().hwnd { + SetTextColor(hdc, COLORREF(focused_text_colour)); + } else { + SetTextColor(hdc, COLORREF(unfocused_text_colour)); + } + + let left = gap + (i as i32 * (width + gap)); + let mut rect = Rect { + top: 0, + left, + right: left + width, + bottom: height, + }; + + match *STYLE.lock() { + BorderStyle::System => { + if *WINDOWS_11 { + RoundRect(hdc, rect.left, rect.top, rect.right, rect.bottom, 20, 20); + } else { + Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom); + } + } + BorderStyle::Rounded => { + RoundRect(hdc, rect.left, rect.top, rect.right, rect.bottom, 20, 20); + } + BorderStyle::Square => { + Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom); + } + } + + let label = match STACKBAR_LABEL.load() { + StackbarLabel::Process => { + let exe = window.exe()?; + exe.trim_end_matches(".exe").to_string() + } + StackbarLabel::Title => window.title()?, + }; + + let mut tab_title: Vec = label.encode_utf16().collect(); + + rect.left_padding(10); + rect.right_padding(10); + + DrawTextW( + hdc, + &mut tab_title, + &mut rect.into(), + DT_SINGLELINE | DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS, + ); + } + + ReleaseDC(self.hwnd(), hdc); + } + + Ok(()) + } + + pub fn get_position_from_container_layout(&self, layout: &Rect) -> Rect { + Rect { + bottom: STACKBAR_TAB_HEIGHT.load_consume(), + ..*layout + } + } + + unsafe extern "system" fn callback( + hwnd: HWND, + msg: u32, + w_param: WPARAM, + l_param: LPARAM, + ) -> LRESULT { + unsafe { + match msg { + WM_LBUTTONDOWN => { + let stackbars_containers = STACKBARS_CONTAINERS.lock(); + if let Some(container) = stackbars_containers.get(&hwnd.0) { + let x = l_param.0 as i32 & 0xFFFF; + let y = (l_param.0 as i32 >> 16) & 0xFFFF; + + let width = STACKBAR_TAB_WIDTH.load_consume(); + let height = STACKBAR_TAB_HEIGHT.load_consume(); + let gap = DEFAULT_CONTAINER_PADDING.load_consume(); + + let focused_window_idx = container.focused_window_idx(); + let focused_window_rect = WindowsApi::window_rect( + container + .focused_window() + .cloned() + .unwrap_or_default() + .hwnd(), + ) + .unwrap_or_default(); + + for (index, window) in container.windows().iter().enumerate() { + let left = gap + (index as i32 * (width + gap)); + let right = left + width; + let top = 0; + let bottom = height; + + if x >= left && x <= right && y >= top && y <= bottom { + // If we are focusing a window that isn't currently focused in the + // stackbar, make sure we update its location so that it doesn't render + // on top of other tiles before eventually ending up in the correct + // tile + if index != focused_window_idx { + if let Err(err) = + window.set_position(&focused_window_rect, false) + { + tracing::error!( + "stackbar WM_LBUTTONDOWN repositioning error: hwnd {} ({})", + *window, + err + ); + } + } + + // Restore the window corresponding to the tab we have clicked + window.restore(); + if let Err(err) = window.focus(false) { + tracing::error!( + "stackbar WMLBUTTONDOWN focus error: hwnd {} ({})", + *window, + err + ); + } + } else { + // Hide any windows in the stack that don't correspond to the window + // we have clicked + window.hide(); + } + } + } + + LRESULT(0) + } + WM_DESTROY => { + PostQuitMessage(0); + LRESULT(0) + } + _ => DefWindowProcW(hwnd, msg, w_param, l_param), + } + } + } +} diff --git a/komorebi/src/static_config.rs b/komorebi/src/static_config.rs index 4c73b410..146a94a8 100644 --- a/komorebi/src/static_config.rs +++ b/komorebi/src/static_config.rs @@ -7,6 +7,13 @@ use crate::current_virtual_desktop; use crate::monitor::Monitor; use crate::monitor_reconciliator; use crate::ring::Ring; +use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR; +use crate::stackbar_manager::STACKBAR_LABEL; +use crate::stackbar_manager::STACKBAR_MODE; +use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR; +use crate::stackbar_manager::STACKBAR_TAB_HEIGHT; +use crate::stackbar_manager::STACKBAR_TAB_WIDTH; +use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR; use crate::window_manager::WindowManager; use crate::window_manager_event::WindowManagerEvent; use crate::windows_api::WindowsApi; @@ -22,13 +29,6 @@ use crate::MANAGE_IDENTIFIERS; use crate::MONITOR_INDEX_PREFERENCES; use crate::OBJECT_NAME_CHANGE_ON_LAUNCH; use crate::REGEX_IDENTIFIERS; -use crate::STACKBAR_FOCUSED_TEXT_COLOUR; -use crate::STACKBAR_LABEL; -use crate::STACKBAR_MODE; -use crate::STACKBAR_TAB_BACKGROUND_COLOUR; -use crate::STACKBAR_TAB_HEIGHT; -use crate::STACKBAR_TAB_WIDTH; -use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::WORKSPACE_RULES; use komorebi_core::StackbarLabel; @@ -760,16 +760,6 @@ impl StaticConfig { value.apply_globals()?; - let stackbar_mode = STACKBAR_MODE.load(); - - for m in wm.monitors_mut() { - for w in m.workspaces_mut() { - for c in w.containers_mut() { - c.set_stackbar_mode(stackbar_mode); - } - } - } - if let Some(monitors) = value.monitors { for (i, monitor) in monitors.iter().enumerate() { if let Some(m) = wm.monitors_mut().get_mut(i) { diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index 99fcc69e..1b6b8caf 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -46,6 +46,12 @@ use crate::current_virtual_desktop; use crate::load_configuration; use crate::monitor::Monitor; use crate::ring::Ring; +use crate::stackbar_manager::STACKBAR_FOCUSED_TEXT_COLOUR; +use crate::stackbar_manager::STACKBAR_MODE; +use crate::stackbar_manager::STACKBAR_TAB_BACKGROUND_COLOUR; +use crate::stackbar_manager::STACKBAR_TAB_HEIGHT; +use crate::stackbar_manager::STACKBAR_TAB_WIDTH; +use crate::stackbar_manager::STACKBAR_UNFOCUSED_TEXT_COLOUR; use crate::static_config::StaticConfig; use crate::window::Window; use crate::window_manager_event::WindowManagerEvent; @@ -68,12 +74,6 @@ use crate::MONITOR_INDEX_PREFERENCES; use crate::NO_TITLEBAR; use crate::OBJECT_NAME_CHANGE_ON_LAUNCH; use crate::REMOVE_TITLEBARS; -use crate::STACKBAR_FOCUSED_TEXT_COLOUR; -use crate::STACKBAR_MODE; -use crate::STACKBAR_TAB_BACKGROUND_COLOUR; -use crate::STACKBAR_TAB_HEIGHT; -use crate::STACKBAR_TAB_WIDTH; -use crate::STACKBAR_UNFOCUSED_TEXT_COLOUR; use crate::TRAY_AND_MULTI_WINDOW_IDENTIFIERS; use crate::WORKSPACE_RULES; use komorebi_core::StackbarMode; @@ -1131,7 +1131,6 @@ impl WindowManager { let new_idx = workspace.new_idx_for_direction(direction); - // TODO: clean this up, this is awful let mut cross_monitor_monocle = false; // if there is no container in that direction for this workspace @@ -1162,22 +1161,9 @@ impl WindowManager { } } - // When switching workspaces and landing focus on a window that is not stack, but a stack - // exists, and there is a stackbar visible, when changing focus to that container stack, - // the focused text colour will not be applied until the stack has been cycled at least once - // - // With this piece of code, we check if we have changed focus to a container stack with - // a stackbar, and if we have, we run a quick update to make sure the focused text colour - // has been applied if !cross_monitor_monocle { if let Ok(focused_window) = self.focused_window_mut() { - let focused_window_hwnd = focused_window.hwnd; focused_window.focus(self.mouse_follows_focus)?; - - let focused_container = self.focused_container()?; - if let Some(stackbar) = focused_container.stackbar() { - stackbar.update(focused_container.windows(), focused_window_hwnd)?; - } } } @@ -1580,16 +1566,6 @@ impl WindowManager { self.update_focused_workspace(true, true)?; - // TODO: fix this ugly hack to restore stackbar after monocle is toggled off - let workspace = self.focused_workspace()?; - if workspace.monocle_container().is_none() { - if let Some(container) = workspace.focused_container() { - if container.stackbar().is_some() { - self.retile_all(true)?; - }; - } - }; - Ok(()) } @@ -1600,12 +1576,7 @@ impl WindowManager { let workspace = self.focused_workspace_mut()?; workspace.new_monocle_container()?; - if let Some(monocle) = workspace.monocle_container_mut() { - monocle.set_stackbar_mode(StackbarMode::Never); - } - for container in workspace.containers_mut() { - container.set_stackbar_mode(StackbarMode::Never); container.hide(None); } @@ -1618,12 +1589,7 @@ impl WindowManager { let workspace = self.focused_workspace_mut()?; - if let Some(monocle) = workspace.monocle_container_mut() { - monocle.set_stackbar_mode(STACKBAR_MODE.load()); - } - for container in workspace.containers_mut() { - container.set_stackbar_mode(STACKBAR_MODE.load()); container.restore(); } diff --git a/komorebi/src/workspace.rs b/komorebi/src/workspace.rs index ed1e4603..94860fc3 100644 --- a/komorebi/src/workspace.rs +++ b/komorebi/src/workspace.rs @@ -24,6 +24,8 @@ use crate::border_manager::BORDER_OFFSET; use crate::border_manager::BORDER_WIDTH; use crate::container::Container; use crate::ring::Ring; +use crate::stackbar_manager; +use crate::stackbar_manager::STACKBAR_TAB_HEIGHT; use crate::static_config::WorkspaceConfig; use crate::window::Window; use crate::window::WindowDetails; @@ -33,7 +35,6 @@ use crate::DEFAULT_WORKSPACE_PADDING; use crate::INITIAL_CONFIGURATION_LOADED; use crate::NO_TITLEBAR; use crate::REMOVE_TITLEBARS; -use crate::STACKBAR_TAB_HEIGHT; #[allow(clippy::struct_field_names)] #[derive( @@ -304,7 +305,7 @@ impl Workspace { } else if let Some(window) = self.maximized_window_mut() { window.maximize(); } else if !self.containers().is_empty() { - let layouts = self.layout().as_boxed_arrangement().calculate( + let mut layouts = self.layout().as_boxed_arrangement().calculate( &adjusted_work_area, NonZeroUsize::new(self.containers().len()).ok_or_else(|| { anyhow!( @@ -319,24 +320,14 @@ impl Workspace { let should_remove_titlebars = REMOVE_TITLEBARS.load(Ordering::SeqCst); let no_titlebar = NO_TITLEBAR.lock().clone(); - let focused_hwnd = self - .focused_container() - .ok_or_else(|| anyhow!("couldn't find a focused container"))? - .focused_window() - .ok_or_else(|| anyhow!("couldn't find a focused window"))? - .hwnd; - let container_padding = self.container_padding().unwrap_or(0); let containers = self.containers_mut(); for (i, container) in containers.iter_mut().enumerate() { - container.renew_stackbar(); - - let container_windows = container.windows().clone(); - let container_stackbar = container.stackbar().clone(); + let window_count = container.windows().len(); if let (Some(window), Some(layout)) = - (container.focused_window_mut(), layouts.get(i)) + (container.focused_window_mut(), layouts.get_mut(i)) { if should_remove_titlebars && no_titlebar.contains(&window.exe()?) { window.remove_title_bar()?; @@ -350,33 +341,23 @@ impl Workspace { WindowsApi::restore_window(window.hwnd()); } - let mut rect = *layout; { let border_offset = BORDER_OFFSET.load(Ordering::SeqCst); - rect.add_padding(border_offset); + layout.add_padding(border_offset); let width = BORDER_WIDTH.load(Ordering::SeqCst); - rect.add_padding(width); + layout.add_padding(width); } - if let Some(stackbar) = container_stackbar { - if stackbar - .set_position( - &stackbar.get_position_from_container_layout(layout), - false, - ) - .is_ok() - { - stackbar.update(&container_windows, focused_hwnd)?; - let tab_height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst); - let total_height = tab_height + container_padding; + if stackbar_manager::should_have_stackbar(window_count) { + let tab_height = STACKBAR_TAB_HEIGHT.load(Ordering::SeqCst); + let total_height = tab_height + container_padding; - rect.top += total_height; - rect.bottom -= total_height; - } + layout.top += total_height; + layout.bottom -= total_height; } - window.set_position(&rect, false)?; + window.set_position(&layout, false)?; } } @@ -405,17 +386,10 @@ impl Workspace { let containers = self.containers_mut(); for container in containers.iter_mut() { - let container_windows = container.windows().clone(); - let container_topbar = container.stackbar().clone(); - if let Some(idx) = container.idx_for_window(hwnd) { container.focus_window(idx); container.restore(); } - - if let Some(stackbar) = container_topbar { - stackbar.update(&container_windows, hwnd)?; - } } Ok(()) }