From dc09a5183a01b95f4128973afa4e739804a7d362 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 31 Oct 2021 11:37:38 +0530 Subject: [PATCH] Exclude the currently active window when visually selecting --- kitty/boss.py | 29 ++++++++++++++++++++--------- kitty/rc/select_window.py | 14 ++++++++++++-- kitty/tabs.py | 14 +++++++++++--- 3 files changed, 43 insertions(+), 14 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index ce453821a..193f00a65 100755 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -9,7 +9,8 @@ from contextlib import suppress from functools import partial from gettext import gettext as _ from typing import ( - Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union, cast + Any, Callable, Container, Dict, Iterable, Iterator, List, Optional, Tuple, + Union, cast ) from weakref import WeakValueDictionary @@ -859,29 +860,38 @@ class Boss: self.current_visual_select.cancel() self.current_visual_select = None - def visual_window_select_action(self, tab: Tab, callback: Callable[[Optional[Tab], Optional[Window]], None], choose_msg: str) -> None: + def visual_window_select_action( + self, tab: Tab, + callback: Callable[[Optional[Tab], Optional[Window]], None], + choose_msg: str, + only_window_ids: Container[int] = () + ) -> None: self.cancel_current_visual_select() tm = tab.tab_manager_ref() if tm is not None: tm.set_active_tab(tab) self.current_visual_select = VisualSelect(tab.id, tab.os_window_id, choose_msg, callback) if tab.current_layout.only_active_window_visible: - self.select_window_in_tab_using_overlay(tab, choose_msg) + self.select_window_in_tab_using_overlay(tab, choose_msg, only_window_ids) return pending_sequences: SubSequenceMap = {} fmap = get_name_to_functional_number_map() + num = 0 for idx, window in tab.windows.iter_windows_with_number(only_visible=True): - if idx > 9: - break - num = idx + 1 + if only_window_ids and window.id not in only_window_ids: + continue ac = KeyAction('visual_window_select_action_trigger', (window.id,)) - if num == 10: + num += 1 + is_last = num == 10 + if is_last: num = 0 window.screen.set_window_number(num) self.current_visual_select.window_ids.append(window.id) for mods in (0, GLFW_MOD_CONTROL, GLFW_MOD_CONTROL | GLFW_MOD_SHIFT, GLFW_MOD_SUPER, GLFW_MOD_ALT, GLFW_MOD_SHIFT): pending_sequences[(SingleKey(mods=mods, key=ord(str(num))),)] = ac pending_sequences[(SingleKey(mods=mods, key=fmap[f'KP_{num}']),)] = ac + if is_last: + break if len(self.current_visual_select.window_ids) > 1: self.set_pending_sequences(pending_sequences, default_pending_action=KeyAction('visual_window_select_action_trigger', (0,))) redirect_mouse_handling(True) @@ -914,8 +924,9 @@ class Boss: ev = WindowSystemMouseEvent(in_tab_bar, window_id, action, modifiers, button, currently_pressed_button, x, y) self.mouse_handler(ev) - def select_window_in_tab_using_overlay(self, tab: Tab, msg: str) -> None: - windows = tuple((w.id, w.title) for i, w in tab.windows.iter_windows_with_number(only_visible=False)) + def select_window_in_tab_using_overlay(self, tab: Tab, msg: str, only_window_ids: Container[int] = ()) -> None: + windows = tuple((w.id, w.title) for i, w in tab.windows.iter_windows_with_number(only_visible=False) + if not only_window_ids or w.id in only_window_ids) if len(windows) < 1: self.visual_window_select_action_trigger(windows[0][0] if windows else 0) if get_options().enable_audio_bell: diff --git a/kitty/rc/select_window.py b/kitty/rc/select_window.py index aa1c8fd6d..a78d18963 100644 --- a/kitty/rc/select_window.py +++ b/kitty/rc/select_window.py @@ -19,6 +19,7 @@ class SelectWindow(RemoteCommand): match: The tab to open the new window in self: Boolean, if True use tab the command was run in title: A title for this selection + exclude_active: Exclude the currently active window from the list to pick ''' short_desc = 'Visually select a window in the specified tab' @@ -41,11 +42,16 @@ instead of the active tab. --title A title that will be displayed to the user to describe what this selection is for + + +--exclude-active +type=bool-set +Exclude the currently active window from the list of windows to pick ''' is_asynchronous = True def message_to_kitty(self, global_opts: RCOptions, opts: 'CLIOptions', args: ArgsType) -> PayloadType: - ans = {'self': opts.self, 'match': opts.match, 'title': opts.title} + ans = {'self': opts.self, 'match': opts.match, 'title': opts.title, 'exclude_active': opts.exclude_active} return ans def response_from_kitty(self, boss: Boss, window: Optional[Window], payload_get: PayloadGetType) -> ResponseType: @@ -58,7 +64,11 @@ A title that will be displayed to the user to describe what this selection is fo responder.send_error('No window selected') for tab in self.tabs_for_match_payload(boss, window, payload_get): if tab: - boss.visual_window_select_action(tab, callback, payload_get('title') or 'Choose window') + if payload_get('exclude_active'): + wids = tab.all_window_ids_except_active_window + else: + wids = set() + boss.visual_window_select_action(tab, callback, payload_get('title') or 'Choose window', only_window_ids=wids) break return no_response diff --git a/kitty/tabs.py b/kitty/tabs.py index 0808a0070..8e785b09a 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -11,7 +11,7 @@ from operator import attrgetter from time import monotonic from typing import ( Any, Deque, Dict, Generator, Iterable, Iterator, List, NamedTuple, - Optional, Pattern, Sequence, Tuple, Union, cast + Optional, Pattern, Sequence, Set, Tuple, Union, cast ) from .borders import Border, Borders @@ -596,6 +596,14 @@ class Tab: # {{{ if self.current_layout.move_window_to_group(self.windows, group.id): self.relayout() + @property + def all_window_ids_except_active_window(self) -> Set[int]: + all_window_ids = {w.id for w in self} + aw = self.active_window + if aw is not None: + all_window_ids.discard(aw.id) + return all_window_ids + @ac('win', ''' Focus a visible window by pressing the number of the window. Window numbers are displayed over the windows for easy selection in this mode. @@ -605,14 +613,14 @@ class Tab: # {{{ if tab and window: tab.set_active_window(window) - get_boss().visual_window_select_action(self, callback, 'Choose window to switch to') + get_boss().visual_window_select_action(self, callback, 'Choose window to switch to', only_window_ids=self.all_window_ids_except_active_window) @ac('win', 'Swap the current window with another window in the current tab, selected visually') def swap_with_window(self) -> None: def callback(tab: Optional[Tab], window: Optional[Window]) -> None: if tab and window: tab.swap_active_window_with(window.id) - get_boss().visual_window_select_action(self, callback, 'Choose window to swap with') + get_boss().visual_window_select_action(self, callback, 'Choose window to swap with', only_window_ids=self.all_window_ids_except_active_window) @ac('win', 'Move active window to the top (make it the first window)') def move_window_to_top(self) -> None: