aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-08 22:08:09 +0200
committeruser <user@clank>2026-08-08 22:08:09 +0200
commit1417b7295863945c540d669f18a913cde6ee13b0 (patch)
tree54fae0390f116a1dec7f57d10e765333a838236e
parentsl0p: fix double-click-to-rename, which never fired (diff)
downloadsl0ppij-1417b7295863945c540d669f18a913cde6ee13b0.tar.gz
sl0ppij-1417b7295863945c540d669f18a913cde6ee13b0.tar.xz
sl0ppij-1417b7295863945c540d669f18a913cde6ee13b0.zip
sl0p: don't let hover steal focus while typing into a rename prompt
Reported after double-click-to-rename landed: rename one pane, then hovering over other panes puts them in 'edit title' state too. Cause is focus_follows_mouse, not the rename itself. RenamePane is a CLIENT mode that acts on whatever pane is ACTIVE, so with focus-follows-mouse enabled every pane the mouse passes over becomes active and inherits the prompt. The double-click entry point just made it easy to hit, since you finish the gesture with the mouse sitting on a pane frame. Focus-follows-mouse is now suppressed for a client that is in a modal text-entry mode: RenamePane, RenameTab, EnterSearch or Prompt. Those all act on the active pane/tab, so moving focus underneath them is never what the user meant. Keyboard focus movement is untouched. Baselines held: zellij-server 1429/30.
-rw-r--r--zellij-server/src/tab/mod.rs22
-rw-r--r--zellij-server/src/tab/mouse_handler.rs23
2 files changed, 44 insertions, 1 deletions
diff --git a/zellij-server/src/tab/mod.rs b/zellij-server/src/tab/mod.rs
index e501d3c1..be326868 100644
--- a/zellij-server/src/tab/mod.rs
+++ b/zellij-server/src/tab/mod.rs
@@ -7499,6 +7499,28 @@ impl Tab {
pub fn update_mouse_hover_effects(&mut self, mouse_hover_effects: bool) {
self.mouse_hover_effects = mouse_hover_effects;
}
+ /// Is this client currently typing into a modal prompt (renaming a pane or
+ /// tab, entering a search term)?
+ ///
+ /// These modes act on whatever is ACTIVE, so letting hover move the focus
+ /// underneath them drags the prompt from pane to pane: rename one pane,
+ /// then sweep the mouse across the tab and every pane you touch appears to
+ /// enter rename mode.
+ pub fn client_is_in_text_entry_mode(&self, client_id: ClientId) -> bool {
+ self.mode_info
+ .borrow()
+ .get(&client_id)
+ .map(|mode_info| {
+ matches!(
+ mode_info.mode,
+ InputMode::RenamePane
+ | InputMode::RenameTab
+ | InputMode::EnterSearch
+ | InputMode::Prompt
+ )
+ })
+ .unwrap_or(false)
+ }
pub fn update_focus_follows_mouse(&mut self, focus_follows_mouse: bool) {
self.focus_follows_mouse = focus_follows_mouse;
}
diff --git a/zellij-server/src/tab/mouse_handler.rs b/zellij-server/src/tab/mouse_handler.rs
index 3e063824..7a329e4b 100644
--- a/zellij-server/src/tab/mouse_handler.rs
+++ b/zellij-server/src/tab/mouse_handler.rs
@@ -506,7 +506,10 @@ impl MouseHandler {
frame_double_click,
pinned_selectable,
pinned_unselectable,
- focus_follows_mouse: tab.focus_follows_mouse,
+ // suppressed while typing into a rename/search prompt, so hovering
+ // does not drag the prompt onto another pane
+ focus_follows_mouse: tab.focus_follows_mouse
+ && !tab.client_is_in_text_entry_mode(client_id),
mouse_click_through: tab.mouse_click_through,
mouse_scroll_resize: tab.mouse_scroll_resize,
})
@@ -1945,6 +1948,24 @@ mod tests {
}
#[test]
+ fn hover_does_not_move_focus_when_focus_follows_mouse_is_suppressed() {
+ // Tab suppresses focus_follows_mouse while a client is typing into a
+ // rename/search prompt. Without this, hovering after renaming a pane
+ // drags the prompt onto every pane the mouse touches.
+ let mut ctx = mouse_event_context(false);
+ ctx.active_pane_id = Some(PaneId::Terminal(2));
+ ctx.pane_id_at_position = Some(PaneId::Terminal(1));
+ ctx.focus_follows_mouse = false;
+ let event = MouseEvent::new_left_motion_event(Position::new(3, 3));
+ let action = MouseHandler::determine_mouse_action(&event, &ctx).unwrap();
+ assert!(
+ !matches!(action, MouseAction::FocusOnHover { .. }),
+ "must not steal focus on hover, got {:?}",
+ action
+ );
+ }
+
+ #[test]
fn a_real_double_click_sequence_is_detected() {
// REGRESSION: a double-click is press/release/press/release. An earlier
// version reset the tracker on any event that was not a frame press,