diff --git a/src/contexts.rs b/src/contexts.rs index 34d9d148..1e70d9d4 100644 --- a/src/contexts.rs +++ b/src/contexts.rs @@ -258,13 +258,21 @@ impl<'a> WmCtx<'a> { /// Use this for interactive operations (move/resize drags) so later /// restacks do not drop the dragged floating window behind others. pub fn raise_interactive(&mut self, win: WindowId) { + let mut mid_to_restack = None; if let Some(mid) = self.g().clients.get(&win).map(|c| c.monitor_id) { if let Some(mon) = self.g_mut().monitor_mut(mid) { mon.stack.retain(|&w| w != win); mon.stack.push(win); + mid_to_restack = Some(mid); } } - self.backend().raise_window(win); + + if let Some(mid) = mid_to_restack { + crate::layouts::restack(self, mid); + } else { + self.backend().raise_window(win); + self.flush(); + } } pub fn restack(&self, wins: &[WindowId]) { diff --git a/src/focus.rs b/src/focus.rs index 881f3988..818ba567 100644 --- a/src/focus.rs +++ b/src/focus.rs @@ -199,20 +199,16 @@ impl<'a> FocusBackendOps for WaylandFocusBackend<'a> { return; } - // Only explicitly restack if the focused window is tiled. - // Floating windows should not automatically pop to the top just - // from being hovered, otherwise they flicker rapidly when overlapping. - if core.g.clients.get(¤t).is_some_and(|c| c.is_floating) { - return; - } - - let mut stack = Vec::new(); - let mut floating = Vec::new(); + let mut tiled_stack = Vec::new(); + let mut floating_stack = Vec::new(); let Some(monitor) = core.g.monitor(monitor_id) else { return; }; let selected_tags = monitor.selected_tags(); let bar_win = monitor.bar_win; + + let is_tiling = monitor.current_layout().is_tiling(); + for &win in &monitor.stack { let Some(client) = core.g.clients.get(&win) else { continue; @@ -221,17 +217,22 @@ impl<'a> FocusBackendOps for WaylandFocusBackend<'a> { continue; } if client.is_floating { - floating.push(win); + floating_stack.push(win); } else { - stack.push(win); + tiled_stack.push(win); } } - if let Some(idx) = stack.iter().position(|&win| win == current) { - let selected = stack.remove(idx); - stack.push(selected); + + if is_tiling { + if let Some(idx) = tiled_stack.iter().position(|&win| win == current) { + let selected = tiled_stack.remove(idx); + tiled_stack.push(selected); + } } + + let mut stack = tiled_stack; stack.push(bar_win); - stack.extend(floating); + stack.extend(floating_stack); self.wayland.backend.restack(&stack); } } @@ -326,8 +327,10 @@ pub(crate) fn focus_soft_x11( /// handlers where focus failures should not abort the operation. pub fn focus_soft(ctx: &mut crate::contexts::WmCtx, win: Option) { use crate::contexts::WmCtx::*; + let previous_sel = ctx.selected_client(); + match ctx { - X11(x11_ctx) => { + X11(ref mut x11_ctx) => { let systray = x11_ctx.systray.as_deref(); if let Err(e) = focus_x11( &mut x11_ctx.core, @@ -339,12 +342,18 @@ pub fn focus_soft(ctx: &mut crate::contexts::WmCtx, win: Option) { log::warn!("focus_soft X11({:?}) failed: {}", win, e); } } - Wayland(wayland_ctx) => { + Wayland(ref mut wayland_ctx) => { if let Err(e) = focus_wayland(&mut wayland_ctx.core, &wayland_ctx.wayland, win) { log::warn!("focus_soft Wayland({:?}) failed: {}", win, e); } } } + + let current_sel = ctx.selected_client(); + if previous_sel != current_sel { + let monitor_id = ctx.g().selected_monitor_id(); + crate::layouts::restack(ctx, monitor_id); + } } /// Backend-agnostic unfocus - does match internally. diff --git a/src/layouts/manager.rs b/src/layouts/manager.rs index 0f30aeb3..e0e7b090 100644 --- a/src/layouts/manager.rs +++ b/src/layouts/manager.rs @@ -134,55 +134,35 @@ pub fn restack(ctx: &mut WmCtx<'_>, monitor_id: MonitorId) { return; } - let selected_window = match monitor.sel { - Some(win) => win, - None => return, - }; + let selected_window = monitor.sel; let layout = monitor.current_layout(); let is_tiling = layout.is_tiling(); - let is_monocle = layout.is_monocle(); let selected_tags = monitor.selected_tags(); let bar_win = monitor.bar_win; - if !is_tiling { - ctx.raise(selected_window); - ctx.flush(); - return; - } - let mut tiled_stack = Vec::new(); let mut floating_stack = Vec::new(); - if let Some(m) = ctx.g().monitor(monitor_id) { - for &win in &m.stack { - if let Some(c) = ctx.client(win) { - if c.is_visible_on_tags(selected_tags) { - if c.is_floating { - floating_stack.push(win); - } else { - tiled_stack.push(win); - } + + for &win in &monitor.stack { + if let Some(c) = ctx.client(win) { + if c.is_visible_on_tags(selected_tags) { + if c.is_floating { + floating_stack.push(win); + } else { + tiled_stack.push(win); } } } } - if let Some(idx) = floating_stack - .iter() - .position(|&win| win == selected_window) - { - let selected = floating_stack.remove(idx); - floating_stack.push(selected); - } else { - // In monocle every tiled client occupies the full work area, so the - // focused tiled client must be the last tiled element in z-order. - // Keeping this explicit also makes the generic tiled case easier to read. - if let Some(idx) = tiled_stack.iter().position(|&win| win == selected_window) { - let selected = tiled_stack.remove(idx); - tiled_stack.push(selected); - } - if is_monocle && tiled_stack.last().copied() != Some(selected_window) { - tiled_stack.retain(|&win| win != selected_window); - tiled_stack.push(selected_window); + // The only exception: if the currently selected window is tiled, + // it should be raised over all other tiling windows. + if let Some(sel) = selected_window { + if is_tiling { + if let Some(idx) = tiled_stack.iter().position(|&win| win == sel) { + let selected = tiled_stack.remove(idx); + tiled_stack.push(selected); + } } }