diff --git a/rust/limux-host-linux/src/pane.rs b/rust/limux-host-linux/src/pane.rs index 00f0753f..d60b547d 100644 --- a/rust/limux-host-linux/src/pane.rs +++ b/rust/limux-host-linux/src/pane.rs @@ -2054,8 +2054,9 @@ fn show_tab_context_menu(tab_btn: >k::Box, tab_id: &str, context: &TabContextM let menu_ref = menu.clone(); let callbacks = context.callbacks.clone(); rename_btn.connect_clicked(move |_| { + let hover_focus_guard = terminal::suspend_hover_focus(); menu_ref.popdown(); - show_rename_dialog(&lbl, &state, &tid, &callbacks); + show_rename_dialog(&lbl, &state, &tid, &callbacks, hover_focus_guard); }); } @@ -2138,6 +2139,7 @@ fn show_rename_dialog( tab_state: &Rc>, tab_id: &str, callbacks: &Rc, + hover_focus_guard: terminal::HoverFocusGuard, ) { let current_name = label.label().to_string(); @@ -2161,13 +2163,14 @@ fn show_rename_dialog( entry.grab_focus(); entry.select_region(0, -1); - // On activate (Enter) or focus-out, commit rename + // On activate (Enter) or blur, commit rename. let lbl = label.clone(); let state = tab_state.clone(); let tid = tab_id.to_string(); let parent_for_cleanup = parent.clone(); let commit = Rc::new(std::cell::Cell::new(false)); + let hover_focus_guard = Rc::new(RefCell::new(Some(hover_focus_guard))); let do_rename = { let commit = commit.clone(); @@ -2176,6 +2179,7 @@ fn show_rename_dialog( let tid = tid.clone(); let parent = parent_for_cleanup.clone(); let callbacks = callbacks.clone(); + let hover_focus_guard = hover_focus_guard.clone(); move |entry: >k::Entry| { if commit.get() { return; @@ -2191,6 +2195,7 @@ fn show_rename_dialog( } lbl.set_visible(true); parent.remove(entry); + hover_focus_guard.borrow_mut().take(); (callbacks.on_state_changed)(); } }; @@ -2203,15 +2208,18 @@ fn show_rename_dialog( } { let do_rename = do_rename.clone(); - let focus_controller = gtk::EventControllerFocus::new(); - focus_controller.connect_leave(move |ctrl| { - if let Some(widget) = ctrl.widget() { - if let Some(entry) = widget.downcast_ref::() { - do_rename(entry); - } + entry.connect_notify_local(Some("has-focus"), move |entry, _| { + if entry.has_focus() { + return; } + let do_rename = do_rename.clone(); + let entry = entry.clone(); + glib::idle_add_local_once(move || { + if !entry.has_focus() { + do_rename(&entry); + } + }); }); - entry.add_controller(focus_controller); } } diff --git a/rust/limux-host-linux/src/terminal.rs b/rust/limux-host-linux/src/terminal.rs index 4bd00101..e749eb02 100644 --- a/rust/limux-host-linux/src/terminal.rs +++ b/rust/limux-host-linux/src/terminal.rs @@ -52,6 +52,19 @@ pub struct TerminalIdentity { pub surface_id: String, } +pub(crate) struct HoverFocusGuard; + +impl Drop for HoverFocusGuard { + fn drop(&mut self) { + HOVER_FOCUS_SUPPRESS_DEPTH.with(|depth| depth.set(depth.get().saturating_sub(1))); + } +} + +pub(crate) fn suspend_hover_focus() -> HoverFocusGuard { + HOVER_FOCUS_SUPPRESS_DEPTH.with(|depth| depth.set(depth.get().saturating_add(1))); + HoverFocusGuard +} + /// Per-surface state, stored in a global registry keyed by surface pointer. struct SurfaceEntry { gl_area: gtk::GLArea, @@ -160,6 +173,7 @@ impl TerminalImeState { } thread_local! { + static HOVER_FOCUS_SUPPRESS_DEPTH: Cell = const { Cell::new(0) }; static SURFACE_MAP: RefCell> = RefCell::new(HashMap::new()); } @@ -1684,7 +1698,7 @@ pub fn create_terminal( let had_focus = had_focus.clone(); let motion = gtk::EventControllerMotion::new(); motion.connect_enter(move |ctrl, x, y| { - if (hover_focus)() { + if HOVER_FOCUS_SUPPRESS_DEPTH.with(|depth| depth.get() == 0) && (hover_focus)() { // Match common Hyprland/Omarchy-style focus-follows-mouse behavior: // as soon as the pointer enters a terminal, focus it so typing works // immediately without an extra click.