Skip to content

Middle mouse click to close the tab #4151

Description

@page-down

Is your feature request related to a problem? Please describe.
Currently, you can easily double-click to create a new Tab and click to switch Tabs, but you cannot close Tabs with the mouse.

Describe the solution you'd like
Close the tab by clicking the middle mouse button.

Describe alternatives you've considered
n/a

Additional context
Here is my attempt to implement it. However I don't know any better way to disable the middle click event on the tab bar, which by default will paste the clipboard text.

diff --git a/kitty/boss.py b/kitty/boss.py
index 6e56288e..f8398fa8 100755
--- a/kitty/boss.py
+++ b/kitty/boss.py
@@ -591,10 +591,10 @@ def start(self, first_os_window_id: int) -> None:
             run_update_check(get_options().update_check_interval * 60 * 60)
             self.update_check_started = True
 
-    def activate_tab_at(self, os_window_id: int, x: int, is_double: bool = False) -> int:
+    def activate_tab_at(self, os_window_id: int, x: int, is_double: bool = False, is_middle: bool = False) -> int:
         tm = self.os_window_map.get(os_window_id)
         if tm is not None:
-            tm.activate_tab_at(x, is_double)
+            tm.activate_tab_at(x, is_double, is_middle)
 
     def on_window_resize(self, os_window_id: int, w: int, h: int, dpi_changed: bool) -> None:
         if dpi_changed:
diff --git a/kitty/mouse.c b/kitty/mouse.c
index b8ea84e4..426ae12f 100644
--- a/kitty/mouse.c
+++ b/kitty/mouse.c
@@ -517,11 +517,22 @@ HANDLER(handle_event) {
 static void
 handle_tab_bar_mouse(int button, int UNUSED modifiers) {
     static monotonic_t last_click_at = 0;
-    if (button != GLFW_MOUSE_BUTTON_LEFT || !global_state.callback_os_window->mouse_button_pressed[button]) return;
+    bool is_middle = false;
+    switch (button) {
+        case GLFW_MOUSE_BUTTON_MIDDLE:
+            is_middle = true;
+        case GLFW_MOUSE_BUTTON_LEFT:
+            if (!global_state.callback_os_window->mouse_button_pressed[button]) return;
+            break;
+        default:
+            return;
+    }
     monotonic_t now = monotonic();
     bool is_double = now - last_click_at <= OPT(click_interval);
     last_click_at = is_double ? 0 : now;
-    call_boss(activate_tab_at, "KdO", global_state.callback_os_window->id, global_state.callback_os_window->mouse_x, is_double ? Py_True : Py_False);
+    call_boss(activate_tab_at, "KdOO", global_state.callback_os_window->id, global_state.callback_os_window->mouse_x, is_double ? Py_True : Py_False, is_middle ? Py_True : Py_False);
+    // hack: disable middle click event on tab bar
+    if (is_middle) global_state.callback_os_window->mouse_button_pressed[button] = false;
 }
 
 static bool
diff --git a/kitty/tabs.py b/kitty/tabs.py
index cbef1aed..2551e4f1 100644
--- a/kitty/tabs.py
+++ b/kitty/tabs.py
@@ -965,13 +965,17 @@ def tab_bar_data(self) -> List[TabBarData]:
             ))
         return ans
 
-    def activate_tab_at(self, x: int, is_double: bool = False) -> None:
+    def activate_tab_at(self, x: int, is_double: bool = False, is_middle: bool = False) -> None:
         i = self.tab_bar.tab_at(x)
         if i is None:
             if is_double:
                 self.new_tab()
         else:
             self.set_active_tab_idx(i)
+            if is_middle:
+                tab = self.active_tab
+                if tab is not None:
+                    get_boss().confirm_tab_close(tab)
 
     @property
     def tab_bar_rects(self) -> Tuple[Border, ...]:

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions