Watchers: A new event for global watchers corresponding to the tab bar being changed

Fixes #8842
This commit is contained in:
Kovid Goyal
2025-07-23 09:24:12 +05:30
parent 45b2678db1
commit 8a39976449
5 changed files with 29 additions and 2 deletions

View File

@@ -120,6 +120,8 @@ Detailed list of changes
- Allow using backspace to move the cursor onto the previous line in cooked mode. This is indicated by the `bw` propert in kitty's terminfo (:iss:`8841`)
- Watchers: A new event for global watchers corresponding to the tab bar being changed (:disc:`8842`)
0.42.2 [2025-07-16]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -175,6 +175,17 @@ create :file:`~/.config/kitty/mywatcher.py` and use :option:`launch --watcher` =
# code received from the program running in the window
...
def on_tab_bar_dirty(boss: Boss, window: Window, data: dict[str, Any]) -> None:
# called when any changes happen to the tab bar, such a new tabs being
# created, tab titles changing, tabs moving, etc. Useful to display the
# tab bar externally to kitty. This is called even if the tab bar is
# hidden. Note that this is called only in *global watchers*, that is
# watchers defined in kitty.conf or using the --watcher command line
# flag. data contains tab_manager which is the object responsible for
# managing all tabs in a single OS Window.
...
Every callback is passed a reference to the global ``Boss`` object as well as
the ``Window`` object the action is occurring on. The ``data`` object is a dict
that contains event dependent data. You have full access to kitty internals in

View File

@@ -529,6 +529,9 @@ def load_watch_modules(watchers: Iterable[str]) -> Watchers | None:
w = m.get('on_color_scheme_preference_change')
if callable(w):
ans.on_color_scheme_preference_change.append(w)
w = m.get('on_tab_bar_dirty')
if callable(w):
ans.on_tab_bar_dirty.append(w)
return ans

View File

@@ -55,7 +55,7 @@ from .tab_bar import TabBar, TabBarData
from .types import ac
from .typing_compat import EdgeLiteral, SessionTab, SessionType, TypedDict
from .utils import cmdline_for_hold, log_error, platform_window_id, resolved_shell, shlex_split, which
from .window import CwdRequest, Watchers, Window, WindowDict
from .window import CwdRequest, Watchers, Window, WindowDict, global_watchers
from .window_list import WindowList
@@ -1007,6 +1007,12 @@ class TabManager: # {{{
def mark_tab_bar_dirty(self) -> None:
if self.tab_bar_should_be_visible and not self.tab_bar_hidden:
mark_tab_bar_dirty(self.os_window_id)
boss = get_boss()
w = self.active_window
data = {'tab_manager': self}
for g in global_watchers():
for watcher in g.on_tab_bar_dirty:
watcher(boss, w, data)
def update_tab_bar_data(self) -> None:
self.tab_bar.update(self.tab_bar_data)

View File

@@ -297,6 +297,7 @@ class Watchers:
on_title_change: list[Watcher]
on_cmd_startstop: list[Watcher]
on_color_scheme_preference_change: list[Watcher]
on_tab_bar_dirty: list[Watcher]
def __init__(self) -> None:
self.on_resize = []
@@ -306,6 +307,7 @@ class Watchers:
self.on_title_change = []
self.on_cmd_startstop = []
self.on_color_scheme_preference_change = []
self.on_tab_bar_dirty = []
def add(self, others: 'Watchers') -> None:
def merge(base: list[Watcher], other: list[Watcher]) -> None:
@@ -319,11 +321,13 @@ class Watchers:
merge(self.on_title_change, others.on_title_change)
merge(self.on_cmd_startstop, others.on_cmd_startstop)
merge(self.on_color_scheme_preference_change, others.on_color_scheme_preference_change)
merge(self.on_tab_bar_dirty, others.on_tab_bar_dirty)
def clear(self) -> None:
del self.on_close[:], self.on_resize[:], self.on_focus_change[:]
del self.on_set_user_var[:], self.on_title_change[:], self.on_cmd_startstop[:]
del self.on_color_scheme_preference_change[:]
del self.on_tab_bar_dirty[:]
def copy(self) -> 'Watchers':
ans = Watchers()
@@ -334,12 +338,13 @@ class Watchers:
ans.on_title_change = self.on_title_change[:]
ans.on_cmd_startstop = self.on_cmd_startstop[:]
ans.on_color_scheme_preference_change = self.on_color_scheme_preference_change[:]
ans.on_tab_bar_dirty = self.on_tab_bar_dirty[:]
return ans
@property
def has_watchers(self) -> bool:
return bool(self.on_close or self.on_resize or self.on_focus_change or self.on_color_scheme_preference_change
or self.on_set_user_var or self.on_title_change or self.on_cmd_startstop)
or self.on_set_user_var or self.on_title_change or self.on_cmd_startstop or self.on_tab_bar_dirty)
def call_watchers(windowref: Callable[[], Optional['Window']], which: str, data: dict[str, Any]) -> None: