diff options
| author | user <user@clank> | 2026-08-08 22:08:09 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-08 22:08:09 +0200 |
| commit | 1417b7295863945c540d669f18a913cde6ee13b0 (patch) | |
| tree | 54fae0390f116a1dec7f57d10e765333a838236e | |
| parent | sl0p: fix double-click-to-rename, which never fired (diff) | |
| download | sl0ppij-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.rs | 22 | ||||
| -rw-r--r-- | zellij-server/src/tab/mouse_handler.rs | 23 |
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, |
