feat(wm): track focused floating window

This commit changes the `floating_windows` from a `Vec<Window>` to a
`Ring<Window>` which allows us to keep track of the focused floating
window.

This combined with the existing layer switch allows us to know when we
should focus the focused container or the focused floating window.
This commit is contained in:
alex-ds13
2025-03-21 15:27:09 +00:00
committed by LGUG2Z
parent d897890032
commit 5cc688dc6b
5 changed files with 91 additions and 60 deletions

View File

@@ -370,20 +370,20 @@ impl Monitor {
.position(|w| w.hwnd == foreground_hwnd);
if let Some(idx) = floating_window_index {
let window = workspace.floating_windows_mut().remove(idx);
if let Some(window) = workspace.floating_windows_mut().remove(idx) {
let workspaces = self.workspaces_mut();
#[allow(clippy::option_if_let_else)]
let target_workspace = match workspaces.get_mut(target_workspace_idx) {
None => {
workspaces.resize(target_workspace_idx + 1, Workspace::default());
workspaces.get_mut(target_workspace_idx).unwrap()
}
Some(workspace) => workspace,
};
let workspaces = self.workspaces_mut();
#[allow(clippy::option_if_let_else)]
let target_workspace = match workspaces.get_mut(target_workspace_idx) {
None => {
workspaces.resize(target_workspace_idx + 1, Workspace::default());
workspaces.get_mut(target_workspace_idx).unwrap()
}
Some(workspace) => workspace,
};
target_workspace.floating_windows_mut().push(window);
target_workspace.set_layer(WorkspaceLayer::Floating);
target_workspace.floating_windows_mut().push_back(window);
target_workspace.set_layer(WorkspaceLayer::Floating);
}
} else {
let container = workspace
.remove_focused_container()

View File

@@ -402,7 +402,7 @@ impl WindowManager {
matches!(workspace.layer, WorkspaceLayer::Floating)
&& !should_float
&& workspace.tile;
workspace.floating_windows_mut().push(window);
workspace.floating_windows_mut().push_back(window);
workspace.set_layer(WorkspaceLayer::Floating);
if center_spawned_floats {
let mut floating_window = window;
@@ -630,7 +630,7 @@ impl WindowManager {
window.focus(self.mouse_follows_focus)?;
}
} else if window_management_behaviour.float_override {
workspace.floating_windows_mut().push(window);
workspace.floating_windows_mut().push_back(window);
self.update_focused_workspace(false, false)?;
} else {
match window_management_behaviour.current_behaviour {

View File

@@ -76,4 +76,30 @@ macro_rules! impl_ring_elements {
}
}
};
($name:ty, $element:ident, $el_name:literal) => {
paste::paste! {
impl $name {
pub const fn [<$el_name:lower s>](&self) -> &VecDeque<$element> {
self.[<$el_name:lower s>].elements()
}
pub fn [<$el_name:lower s_mut>](&mut self) -> &mut VecDeque<$element> {
self.[<$el_name:lower s>].elements_mut()
}
#[allow(dead_code)]
pub fn [<focused_ $el_name:lower>](&self) -> Option<&$element> {
self.[<$el_name:lower s>].focused()
}
pub const fn [<focused_ $el_name:lower _idx>](&self) -> usize {
self.[<$el_name:lower s>].focused_idx()
}
pub fn [<focused_ $el_name:lower _mut>](&mut self) -> Option<&mut $element> {
self.[<$el_name:lower s>].focused_mut()
}
}
}
};
}

View File

@@ -968,7 +968,7 @@ impl WindowManager {
if op.floating {
target_workspace
.floating_windows_mut()
.push(Window::from(op.hwnd));
.push_back(Window::from(op.hwnd));
} else {
//TODO(alex-ds13): should this take into account the target workspace
//`window_container_behaviour`?
@@ -1145,18 +1145,18 @@ impl WindowManager {
// There is no need to physically move the floating window between areas with
// `move_to_area` because the user already did that, so we only need to transfer the
// window to the target `floating_windows`
let floating_window = origin_workspace.floating_windows_mut().remove(idx);
if let Some(floating_window) = origin_workspace.floating_windows_mut().remove(idx) {
let target_workspace = self
.monitors_mut()
.get_mut(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?
.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no focused workspace for this monitor"))?;
let target_workspace = self
.monitors_mut()
.get_mut(target_monitor_idx)
.ok_or_else(|| anyhow!("there is no monitor at this idx"))?
.focused_workspace_mut()
.ok_or_else(|| anyhow!("there is no focused workspace for this monitor"))?;
target_workspace
.floating_windows_mut()
.push(floating_window);
target_workspace
.floating_windows_mut()
.push_back(floating_window);
}
} else if origin_workspace
.monocle_container()
.as_ref()
@@ -1830,7 +1830,7 @@ impl WindowManager {
.position(|w| w.hwnd == foreground_hwnd);
let floating_window =
floating_window_index.map(|idx| workspace.floating_windows_mut().remove(idx));
floating_window_index.and_then(|idx| workspace.floating_windows_mut().remove(idx));
let container = if floating_window_index.is_none() {
Some(
workspace
@@ -1859,7 +1859,7 @@ impl WindowManager {
.ok_or_else(|| anyhow!("there is no focused workspace on target monitor"))?;
if let Some(window) = floating_window {
target_workspace.floating_windows_mut().push(window);
target_workspace.floating_windows_mut().push_back(window);
target_workspace.set_layer(WorkspaceLayer::Floating);
Window::from(window.hwnd)
.move_to_area(&current_area, target_monitor.work_area_size())?;
@@ -1978,7 +1978,7 @@ impl WindowManager {
direction: OperationDirection,
) -> Result<()> {
let mouse_follows_focus = self.mouse_follows_focus;
let focused_workspace = self.focused_workspace()?;
let focused_workspace = self.focused_workspace_mut()?;
let mut target_idx = None;
let len = focused_workspace.floating_windows().len();
@@ -2123,7 +2123,7 @@ impl WindowManager {
window.focus(mouse_follows_focus)?;
cross_monitor_monocle_or_max = true;
}
} else {
} else if focused_workspace.layer() == &WorkspaceLayer::Tiling {
match direction {
OperationDirection::Left => match focused_workspace.layout() {
Layout::Default(layout) => {
@@ -2159,8 +2159,26 @@ impl WindowManager {
}
if !cross_monitor_monocle_or_max {
if let Ok(focused_window) = self.focused_window_mut() {
focused_window.focus(self.mouse_follows_focus)?;
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)?;
}
}
}
@@ -2875,7 +2893,7 @@ impl WindowManager {
let window = workspace
.floating_windows_mut()
.last_mut()
.back_mut()
.ok_or_else(|| anyhow!("there is no floating window"))?;
window.center(&work_area)?;

View File

@@ -62,8 +62,7 @@ pub struct Workspace {
#[serde(skip_serializing_if = "Option::is_none")]
#[getset(get_copy = "pub", set = "pub")]
pub maximized_window_restore_idx: Option<usize>,
#[getset(get = "pub", get_mut = "pub")]
pub floating_windows: Vec<Window>,
pub floating_windows: Ring<Window>,
#[getset(get = "pub", get_mut = "pub", set = "pub")]
pub layout: Layout,
#[getset(get = "pub", get_mut = "pub", set = "pub")]
@@ -117,6 +116,7 @@ impl Display for WorkspaceLayer {
}
impl_ring_elements!(Workspace, Container);
impl_ring_elements!(Workspace, Window, "floating_window");
impl Default for Workspace {
fn default() -> Self {
@@ -127,7 +127,7 @@ impl Default for Workspace {
maximized_window: None,
maximized_window_restore_idx: None,
monocle_container_restore_idx: None,
floating_windows: Vec::default(),
floating_windows: Ring::default(),
layout: Layout::Default(DefaultLayout::BSP),
layout_rules: vec![],
layout_flip: None,
@@ -325,13 +325,13 @@ impl Workspace {
} else if let Some(maximized_window) = self.maximized_window() {
maximized_window.restore();
maximized_window.focus(mouse_follows_focus)?;
} else if let Some(floating_window) = self.floating_windows().first() {
} else if let Some(floating_window) = self.focused_floating_window() {
floating_window.focus(mouse_follows_focus)?;
}
} else if let Some(maximized_window) = self.maximized_window() {
maximized_window.restore();
maximized_window.focus(mouse_follows_focus)?;
} else if let Some(floating_window) = self.floating_windows().first() {
} else if let Some(floating_window) = self.focused_floating_window() {
floating_window.focus(mouse_follows_focus)?;
}
@@ -1113,7 +1113,7 @@ impl Workspace {
window
};
self.floating_windows_mut().push(window);
self.floating_windows_mut().push_back(window);
Ok(())
}
@@ -1431,24 +1431,11 @@ impl Workspace {
pub fn new_maximized_window(&mut self) -> Result<()> {
let focused_idx = self.focused_container_idx();
let foreground_hwnd = WindowsApi::foreground_window()?;
let mut floating_window = None;
if !self.floating_windows().is_empty() {
let mut focused_floating_window_idx = None;
for (i, w) in self.floating_windows().iter().enumerate() {
if w.hwnd == foreground_hwnd {
focused_floating_window_idx = Option::from(i);
}
}
if let Some(idx) = focused_floating_window_idx {
floating_window = Option::from(self.floating_windows_mut().remove(idx));
}
}
if let Some(floating_window) = floating_window {
self.set_maximized_window(Option::from(floating_window));
if matches!(self.layer, WorkspaceLayer::Floating) {
let floating_window_idx = self.focused_floating_window_idx();
let floating_window = self.floating_windows_mut().remove(floating_window_idx);
self.set_maximized_window(floating_window);
self.set_maximized_window_restore_idx(Option::from(focused_idx));
if let Some(window) = self.maximized_window() {
window.maximize();
@@ -1563,7 +1550,7 @@ impl Workspace {
let hwnd = WindowsApi::foreground_window().ok()?;
let mut idx = None;
for (i, window) in self.floating_windows.iter().enumerate() {
for (i, window) in self.floating_windows().iter().enumerate() {
if hwnd == window.hwnd {
idx = Option::from(i);
}
@@ -1572,8 +1559,8 @@ impl Workspace {
match idx {
None => None,
Some(idx) => {
if self.floating_windows.get(idx).is_some() {
Option::from(self.floating_windows_mut().remove(idx))
if self.floating_windows().get(idx).is_some() {
self.floating_windows_mut().remove(idx)
} else {
None
}