diff --git a/docs/changelog.rst b/docs/changelog.rst index de9463352..916c4ae81 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -17,7 +17,7 @@ level. The kitty scrollback buffer grew support for :opt:`smooth scrolling ` and :opt:`momentum based scrolling ` for a natural, smooth and kinetic scrolling experience. -Additionally, you can now :opt:`drag kitty tabs around ` with the mouse +Additionally, you can now :opt:`drag kitty tabs around ` with the mouse to re-order them, move them to another kitty OS Window or even detach them into their own OS Window. @@ -278,7 +278,7 @@ Detailed list of changes - macOS: Implement support for Apple dictation to input text in kitty (:iss:`3732`) -- Allow dragging tabs (opt:`tab_bar_drag_threshold`) in the tab bar to re-order, move to another OS Window or +- Allow dragging tabs (opt:`drag_threshold`) in the tab bar to re-order, move to another OS Window or detach (:pull:`9296`) - Allow dragging window borders to resize kitty windows in all the different diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 1835d5c75..c9270b919 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -884,6 +884,19 @@ sets the shape when dragging in rectangular selection mode. ''' ) +opt('drag_threshold', '5', option_type='positive_int', long_text=''' +The threshold distance the mouse must move to start a drag and drop. Dragging +works for tabs and windows. You can drag tabs to re-order them, detach +them into new OS Windows or move them to another OS Window. Similarly, +by dragging the titlebar of a window (see :ac:`toggle_window_title_bars`) +you can re-order it in its layout, detach it or move it to another tab. +A value of zero disables all dragging. + +Note that on Wayland, :link:`because of poor design +` cancelling +a drag will detach the tab. This is worked around for compositors that support +:link:`xdg-toplevel-drag `. +''') # mouse.mousemap {{{ agr('mouse.mousemap', 'Mouse actions', ''' @@ -1580,15 +1593,6 @@ opt('window_title_bar_align', 'center', long_text='Horizontal alignment of the text in window title bars.' ) -opt('window_title_bar_drag_threshold', '5', - option_type='positive_int', - long_text=''' -Pixel distance the mouse must move before a window title bar drag begins. -Zero disables dragging. Drop on a title bar swaps positions; drop on a -window body inserts in the quadrant direction (left/right/top/bottom). -Drop on the tab bar moves the window to that tab; drop outside kitty -detaches it to a new OS window. -''') egr() # }}} @@ -1848,23 +1852,6 @@ the window is translucent, in which case the default background is used as it looks better. ''') -opt( - 'tab_bar_drag_threshold', - '5', - option_type='positive_int', - long_text=""" -Control when dragging of tabs to re-order them happens. -The value is the drag threshold in pixels, the distance the mouse must move -before a drag begins. A value of zero disables tab dragging entirely. -Dragging a tab to another kitty window moves it there, while dragging -outside any kitty window detaches it into a new OS window. - -Note that on Wayland, :link:`because of poor design -` cancelling -a drag will detach the tab. This is worked around for compositors that support -:link:`xdg-toplevel-drag `. -""", -) egr() # }}} diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 253f273e7..f2e6c9024 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -971,6 +971,9 @@ class Parser: def disable_ligatures(self, val: str, ans: dict[str, typing.Any]) -> None: ans['disable_ligatures'] = disable_ligatures(val) + def drag_threshold(self, val: str, ans: dict[str, typing.Any]) -> None: + ans['drag_threshold'] = positive_int(val) + def draw_minimal_borders(self, val: str, ans: dict[str, typing.Any]) -> None: ans['draw_minimal_borders'] = to_bool(val) @@ -1346,9 +1349,6 @@ class Parser: def tab_bar_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_bar_background'] = to_color_or_none(val) - def tab_bar_drag_threshold(self, val: str, ans: dict[str, typing.Any]) -> None: - ans['tab_bar_drag_threshold'] = positive_int(val) - def tab_bar_edge(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_bar_edge'] = tab_bar_edge(val) @@ -1548,9 +1548,6 @@ class Parser: choices_for_window_title_bar_align = choices_for_tab_bar_align - def window_title_bar_drag_threshold(self, val: str, ans: dict[str, typing.Any]) -> None: - ans['window_title_bar_drag_threshold'] = positive_int(val) - def window_title_bar_inactive_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_title_bar_inactive_background'] = to_color_or_none(val) diff --git a/kitty/options/types.py b/kitty/options/types.py index fa2d156f4..5faa60c2c 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -350,6 +350,7 @@ option_names = ( 'detect_urls', 'dim_opacity', 'disable_ligatures', + 'drag_threshold', 'draw_minimal_borders', 'draw_window_borders_for_single_window', 'dynamic_background_opacity', @@ -457,7 +458,6 @@ option_names = ( 'tab_activity_symbol', 'tab_bar_align', 'tab_bar_background', - 'tab_bar_drag_threshold', 'tab_bar_edge', 'tab_bar_filter', 'tab_bar_margin_color', @@ -509,7 +509,6 @@ option_names = ( 'window_title_bar_active_background', 'window_title_bar_active_foreground', 'window_title_bar_align', - 'window_title_bar_drag_threshold', 'window_title_bar_inactive_background', 'window_title_bar_inactive_foreground', 'window_title_bar_min_windows', @@ -568,6 +567,7 @@ class Options: detect_urls: bool = True dim_opacity: float = 0.4 disable_ligatures: int = 0 + drag_threshold: int = 5 draw_minimal_borders: bool = True draw_window_borders_for_single_window: bool = False dynamic_background_opacity: bool = False @@ -663,7 +663,6 @@ class Options: tab_activity_symbol: str = '' tab_bar_align: choices_for_tab_bar_align = 'left' tab_bar_background: kitty.fast_data_types.Color | None = None - tab_bar_drag_threshold: int = 5 tab_bar_edge: int = 8 tab_bar_filter: str = '' tab_bar_margin_color: kitty.fast_data_types.Color | None = None @@ -714,7 +713,6 @@ class Options: window_title_bar_active_background: kitty.fast_data_types.Color | None = None window_title_bar_active_foreground: kitty.fast_data_types.Color | None = None window_title_bar_align: choices_for_window_title_bar_align = 'center' - window_title_bar_drag_threshold: int = 5 window_title_bar_inactive_background: kitty.fast_data_types.Color | None = None window_title_bar_inactive_foreground: kitty.fast_data_types.Color | None = None window_title_bar_min_windows: int = 0 diff --git a/kitty/tabs.py b/kitty/tabs.py index f44e1d3b2..f30d245ab 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -1777,7 +1777,7 @@ class TabManager: # {{{ if button == -1: # motion dragged_tab_id, drag_started, start_x, start_y = get_tab_being_dragged() if dragged_tab_id and self.tab_for_id(dragged_tab_id) is not None and not drag_started: - threshold = get_options().tab_bar_drag_threshold + threshold = get_options().drag_threshold if threshold and math.sqrt((x-start_x)**2 + (y-start_y)**2) > threshold: set_tab_being_dragged(dragged_tab_id, True, start_x, start_y) request_callback_with_thumbnail("start_tab_drag", self.os_window_id) @@ -1827,7 +1827,7 @@ class TabManager: # {{{ if button == -1: # motion event dragged_window_id, drag_started, start_x, start_y = get_window_being_dragged() if dragged_window_id and not drag_started: - threshold = get_options().window_title_bar_drag_threshold + threshold = get_options().drag_threshold dist_sq = (x - start_x)**2 + (y - start_y)**2 if threshold and dist_sq > threshold * threshold: set_window_being_dragged(dragged_window_id, True, start_x, start_y) @@ -1840,7 +1840,7 @@ class TabManager: # {{{ if action == GLFW_PRESS: if (w := boss.window_id_map.get(window_id)) is not None: boss.set_active_window(w, switch_os_window_if_needed=True) - threshold = get_options().window_title_bar_drag_threshold + threshold = get_options().drag_threshold if threshold: set_window_being_dragged(window_id, False, x, y) return