diff --git a/docs/index.rst b/docs/index.rst index 6522ad2af..36bb4e117 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -175,13 +175,17 @@ previously active windows for negative numbers. .. _detach_window: -Finally you can define shortcuts to detach the current window and +You can define shortcuts to detach the current window and move it to another tab or another OS window:: map ctrl+f2 detach_window # moves the window into a new OS window map ctrl+f3 detach_window new-tab # moves the window into a new Tab map ctrl+f4 detach_window ask # asks which tab to move the window into +Similarly, you can detach the current tab, with:: + + map ctrl+f2 detach_tab # moves the tab into a new OS window + map ctrl+f4 detach_tab ask # asks which OS Window to move the tab into Other keyboard shortcuts ---------------------------------- diff --git a/kitty/boss.py b/kitty/boss.py index 1facbcda1..90ca9aeae 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -1141,6 +1141,19 @@ class Boss: self._cleanup_tab_after_window_removal(src_tab) target_tab.make_active() + def _move_tab_to(self, tab=None, target_os_window_id=None): + if tab is None: + tab = self.active_tab + if tab is None: + return + if target_os_window_id is None: + target_os_window_id = self.add_os_window() + tm = self.os_window_map[target_os_window_id] + target_tab = tm.new_tab(empty_tab=True) + target_tab.take_over_from(tab) + self._cleanup_tab_after_window_removal(tab) + target_tab.make_active() + def detach_window(self, *args): if not args or args[0] == 'new': return self._move_window_to(target_os_window_id='new') @@ -1151,9 +1164,11 @@ class Boss: '' ] tab_id_map = {} + current_tab = self.active_tab for i, tab in enumerate(self.all_tabs): - tab_id_map[i + 1] = tab.id - lines.append('{} {}'.format(i + 1, tab.title)) + if tab is not current_tab: + tab_id_map[i + 1] = tab.id + lines.append('{} {}'.format(i + 1, tab.title)) def done(data, target_window_id, self): target_window = None @@ -1167,3 +1182,34 @@ class Boss: self._run_kitten( 'hints', args=('--type=regex', r'--regex=(?m)^\d+ .+$',), input_data='\r\n'.join(lines).encode('utf-8'), custom_callback=done) + + def detach_tab(self, *args): + if not args or args[0] == 'new': + return self._move_tab_to() + + lines = [ + 'Choose an OS window to move the tab to', + '' + ] + os_window_id_map = {} + current_os_window = getattr(self.active_tab, 'os_window_id', 0) + for i, osw in enumerate(self.os_window_map): + tm = self.os_window_map[osw] + if current_os_window != osw and tm.active_tab and tm.active_tab: + os_window_id_map[i + 1] = osw + lines.append('{} {}'.format(i + 1, tm.active_tab.title)) + + def done(data, target_window_id, self): + target_tab = None + for w in self.all_windows: + if w.id == target_window_id: + target_tab = w.tabref() + break + os_window_id = os_window_id_map[int(data['match'][0].partition(' ')[0])] + if target_tab and target_tab.os_window_id == os_window_id: + return + self._move_tab_to(tab=target_tab, target_os_window_id=os_window_id) + + self._run_kitten( + 'hints', args=('--type=regex', r'--regex=(?m)^\d+ .+$',), + input_data='\r\n'.join(lines).encode('utf-8'), custom_callback=done) diff --git a/kitty/config.py b/kitty/config.py index 70cdc1104..308507b4c 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -99,6 +99,13 @@ def detach_window_parse(func, rest): return func, (rest,) +@func_with_args('detach_tab') +def detach_tab_parse(func, rest): + if rest not in ('new', 'ask'): + rest = 'new' + return func, (rest,) + + @func_with_args('set_background_opacity', 'goto_layout', 'kitty_shell') def simple_parse(func, rest): return func, [rest] diff --git a/kitty/tabs.py b/kitty/tabs.py index 5c7d1d310..7b0b60a8d 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -73,6 +73,26 @@ class Tab: # {{{ self._set_current_layout(l0) self.startup(session_tab) + def take_over_from(self, other_tab): + self.name = getattr(other_tab, 'name', '') + self.enabled_layouts = list(other_tab.enabled_layouts) + self._set_current_layout(other_tab._current_layout_name) + self._last_used_layout = other_tab._last_used_layout + + orig_windows = deque(other_tab.windows) + orig_history = deque(other_tab.active_window_history) + orig_active = other_tab._active_window_idx + while other_tab.windows: + underlaid_window, overlaid_window = other_tab.detach_window(other_tab.windows[0]) + if underlaid_window: + self.attach_window(underlaid_window) + if overlaid_window: + self.attach_window(overlaid_window) + + self.active_window_history = orig_history + self.windows = orig_windows + self._active_window_idx = orig_active + def _set_current_layout(self, layout_name): self._last_used_layout = self._current_layout_name self.current_layout = self.create_layout_object(layout_name)