diff --git a/komorebi/src/window_manager.rs b/komorebi/src/window_manager.rs index f758f18e..23e860ea 100644 --- a/komorebi/src/window_manager.rs +++ b/komorebi/src/window_manager.rs @@ -1985,38 +1985,244 @@ impl WindowManager { if len > 1 { let focused_hwnd = WindowsApi::foreground_window()?; - for (idx, window) in focused_workspace.floating_windows().iter().enumerate() { - if window.hwnd == focused_hwnd { - match direction { - OperationDirection::Left => {} - OperationDirection::Right => {} - OperationDirection::Up => { - if idx == len - 1 { - target_idx = Some(0) - } else { - target_idx = Some(idx + 1) - } - } - OperationDirection::Down => { - if idx == 0 { - target_idx = Some(len - 1) - } else { - target_idx = Some(idx - 1) - } - } + let focused_rect = WindowsApi::window_rect(focused_hwnd)?; + match direction { + OperationDirection::Left => { + let mut windows_in_direction = focused_workspace + .floating_windows() + .iter() + .enumerate() + .flat_map(|(idx, w)| { + (w.hwnd != focused_hwnd) + .then_some(WindowsApi::window_rect(w.hwnd).ok().map(|r| (idx, r))) + }) + .flatten() + .flat_map(|(idx, r)| { + (r.left < focused_rect.left) + .then_some((idx, i32::abs(r.left - focused_rect.left))) + }) + .collect::>(); + + // Sort by distance to focused + windows_in_direction.sort_by_key(|(_, d)| (*d as f32 * 1000.0).trunc() as i32); + + if let Some((idx, _)) = windows_in_direction.first() { + target_idx = Some(*idx); } } - } + OperationDirection::Right => { + let mut windows_in_direction = focused_workspace + .floating_windows() + .iter() + .enumerate() + .flat_map(|(idx, w)| { + (w.hwnd != focused_hwnd) + .then_some(WindowsApi::window_rect(w.hwnd).ok().map(|r| (idx, r))) + }) + .flatten() + .flat_map(|(idx, r)| { + (r.left > focused_rect.left) + .then_some((idx, i32::abs(r.left - focused_rect.left))) + }) + .collect::>(); - if target_idx.is_none() { - target_idx = Some(0); - } + // Sort by distance to focused + windows_in_direction.sort_by_key(|(_, d)| (*d as f32 * 1000.0).trunc() as i32); + + if let Some((idx, _)) = windows_in_direction.first() { + target_idx = Some(*idx); + } + } + OperationDirection::Up => { + let mut windows_in_direction = focused_workspace + .floating_windows() + .iter() + .enumerate() + .flat_map(|(idx, w)| { + (w.hwnd != focused_hwnd) + .then_some(WindowsApi::window_rect(w.hwnd).ok().map(|r| (idx, r))) + }) + .flatten() + .flat_map(|(idx, r)| { + (r.top < focused_rect.top) + .then_some((idx, i32::abs(r.top - focused_rect.top))) + }) + .collect::>(); + + // Sort by distance to focused + windows_in_direction.sort_by_key(|(_, d)| (*d as f32 * 1000.0).trunc() as i32); + + if let Some((idx, _)) = windows_in_direction.first() { + target_idx = Some(*idx); + } + } + OperationDirection::Down => { + let mut windows_in_direction = focused_workspace + .floating_windows() + .iter() + .enumerate() + .flat_map(|(idx, w)| { + (w.hwnd != focused_hwnd) + .then_some(WindowsApi::window_rect(w.hwnd).ok().map(|r| (idx, r))) + }) + .flatten() + .flat_map(|(idx, r)| { + (r.top > focused_rect.top) + .then_some((idx, i32::abs(r.top - focused_rect.top))) + }) + .collect::>(); + + // Sort by distance to focused + windows_in_direction.sort_by_key(|(_, d)| (*d as f32 * 1000.0).trunc() as i32); + + if let Some((idx, _)) = windows_in_direction.first() { + target_idx = Some(*idx); + } + } + }; } if let Some(idx) = target_idx { + focused_workspace.floating_windows.focus(idx); if let Some(window) = focused_workspace.floating_windows().get(idx) { window.focus(mouse_follows_focus)?; } + return Ok(()); + } + + let mut cross_monitor_monocle_or_max = false; + + let workspace_idx = self.focused_workspace_idx()?; + + // this is for when we are scrolling across workspaces like PaperWM + if matches!( + self.cross_boundary_behaviour, + CrossBoundaryBehaviour::Workspace + ) && matches!( + direction, + OperationDirection::Left | OperationDirection::Right + ) { + let workspace_count = if let Some(monitor) = self.focused_monitor() { + monitor.workspaces().len() + } else { + 1 + }; + + let next_idx = match direction { + OperationDirection::Left => match workspace_idx { + 0 => workspace_count - 1, + n => n - 1, + }, + OperationDirection::Right => match workspace_idx { + n if n == workspace_count - 1 => 0, + n => n + 1, + }, + _ => workspace_idx, + }; + + self.focus_workspace(next_idx)?; + + if let Ok(focused_workspace) = self.focused_workspace_mut() { + if focused_workspace.monocle_container().is_none() { + match direction { + OperationDirection::Left => match focused_workspace.layout() { + Layout::Default(layout) => { + let target_index = + layout.rightmost_index(focused_workspace.containers().len()); + focused_workspace.focus_container(target_index); + } + Layout::Custom(_) => { + focused_workspace.focus_container( + focused_workspace.containers().len().saturating_sub(1), + ); + } + }, + OperationDirection::Right => match focused_workspace.layout() { + Layout::Default(layout) => { + let target_index = + layout.leftmost_index(focused_workspace.containers().len()); + focused_workspace.focus_container(target_index); + } + Layout::Custom(_) => { + focused_workspace.focus_container(0); + } + }, + _ => {} + }; + } + } + + return Ok(()); + } + + // if there is no floating_window in that direction for this workspace + let monitor_idx = self + .monitor_idx_in_direction(direction) + .ok_or_else(|| anyhow!("there is no container or monitor in this direction"))?; + + self.focus_monitor(monitor_idx)?; + let mouse_follows_focus = self.mouse_follows_focus; + + if let Ok(focused_workspace) = self.focused_workspace_mut() { + if let Some(window) = focused_workspace.maximized_window() { + window.focus(mouse_follows_focus)?; + cross_monitor_monocle_or_max = true; + } else if let Some(monocle) = focused_workspace.monocle_container() { + if let Some(window) = monocle.focused_window() { + window.focus(mouse_follows_focus)?; + cross_monitor_monocle_or_max = true; + } + } else if focused_workspace.layer() == &WorkspaceLayer::Tiling { + match direction { + OperationDirection::Left => match focused_workspace.layout() { + Layout::Default(layout) => { + let target_index = + layout.rightmost_index(focused_workspace.containers().len()); + focused_workspace.focus_container(target_index); + } + Layout::Custom(_) => { + focused_workspace.focus_container( + focused_workspace.containers().len().saturating_sub(1), + ); + } + }, + OperationDirection::Right => match focused_workspace.layout() { + Layout::Default(layout) => { + let target_index = + layout.leftmost_index(focused_workspace.containers().len()); + focused_workspace.focus_container(target_index); + } + Layout::Custom(_) => { + focused_workspace.focus_container(0); + } + }, + _ => {} + }; + } + } + + if !cross_monitor_monocle_or_max { + let ws = self.focused_workspace_mut()?; + if ws.is_empty() { + // This is to remove focus from the previous monitor + let desktop_window = Window::from(WindowsApi::desktop_window()?); + + match WindowsApi::raise_and_focus_window(desktop_window.hwnd) { + Ok(()) => {} + Err(error) => { + tracing::warn!("{} {}:{}", error, file!(), line!()); + } + } + } else if ws.layer() == &WorkspaceLayer::Floating && !ws.floating_windows().is_empty() { + if let Some(window) = ws.focused_floating_window() { + window.focus(self.mouse_follows_focus)?; + } + } else { + ws.set_layer(WorkspaceLayer::Tiling); + if let Ok(focused_window) = self.focused_window() { + focused_window.focus(self.mouse_follows_focus)?; + } + } } Ok(())