Add a setting tab_activity_symbol

Fixes #2515
This commit is contained in:
Kovid Goyal 2020-09-08 21:54:56 +05:30
parent 83315f7999
commit 9a54da84dc
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
8 changed files with 48 additions and 3 deletions

View File

@ -27,6 +27,10 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
- Allow specifying watchers in session files and via a command line argument - Allow specifying watchers in session files and via a command line argument
(:iss:`2933`) (:iss:`2933`)
- Add a setting :opt:`tab_activity_symbol` to show a symbol in the tab title
if one of the windows has some activity after it was last focused
(:iss:`2515`)
- macOS: Switch to using the User Notifications framework for notifications. - macOS: Switch to using the User Notifications framework for notifications.
The current notifications framework has been deprecated in Big Sur. The new The current notifications framework has been deprecated in Big Sur. The new
framework only allows notifications from signed and notarized applications, framework only allows notifications from signed and notarized applications,

View File

@ -883,6 +883,17 @@ o('tab_separator', '"{}"'.format(default_tab_separator), option_type=tab_separat
The separator between tabs in the tab bar when using :code:`separator` as the :opt:`tab_bar_style`.''')) The separator between tabs in the tab bar when using :code:`separator` as the :opt:`tab_bar_style`.'''))
def tab_activity_symbol(x: str) -> Optional[str]:
if x == 'none':
return None
return x or None
o('tab_activity_symbol', 'none', option_type=tab_activity_symbol, long_text=_('''
Some text or a unicode symbol to show on the tab if a window in the tab that does
not have focus has some activity.'''))
def tab_title_template(x: str) -> str: def tab_title_template(x: str) -> str:
if x: if x:
for q in '\'"': for q in '\'"':

View File

@ -1069,6 +1069,9 @@ class Screen:
def has_focus(self) -> bool: def has_focus(self) -> bool:
pass pass
def has_activity_since_last_focus(self) -> bool:
pass
def set_tab_bar_render_data( def set_tab_bar_render_data(
os_window_id: int, xstart: float, ystart: float, dx: float, dy: float, os_window_id: int, xstart: float, ystart: float, dx: float, dy: float,

View File

@ -444,6 +444,9 @@ draw_combining_char(Screen *self, char_type ch) {
void void
screen_draw(Screen *self, uint32_t och) { screen_draw(Screen *self, uint32_t och) {
if (is_ignored_char(och)) return; if (is_ignored_char(och)) return;
if (!self->has_activity_since_last_focus && !self->has_focus) {
self->has_activity_since_last_focus = true;
}
uint32_t ch = och < 256 ? self->g_charset[och] : och; uint32_t ch = och < 256 ? self->g_charset[och] : och;
bool is_cc = is_combining_char(ch); bool is_cc = is_combining_char(ch);
if (UNLIKELY(is_cc)) { if (UNLIKELY(is_cc)) {
@ -2531,6 +2534,7 @@ focus_changed(Screen *self, PyObject *has_focus_) {
bool has_focus = PyObject_IsTrue(has_focus_) ? true : false; bool has_focus = PyObject_IsTrue(has_focus_) ? true : false;
if (has_focus != previous) { if (has_focus != previous) {
self->has_focus = has_focus; self->has_focus = has_focus;
if (has_focus) self->has_activity_since_last_focus = false;
if (self->modes.mFOCUS_TRACKING) write_escape_code_to_child(self, CSI, has_focus ? "I" : "O"); if (self->modes.mFOCUS_TRACKING) write_escape_code_to_child(self, CSI, has_focus ? "I" : "O");
Py_RETURN_TRUE; Py_RETURN_TRUE;
} }
@ -2543,6 +2547,11 @@ has_focus(Screen *self, PyObject *args UNUSED) {
Py_RETURN_FALSE; Py_RETURN_FALSE;
} }
static PyObject*
has_activity_since_last_focus(Screen *self, PyObject *args UNUSED) {
if (self->has_activity_since_last_focus) Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
WRAP2(cursor_position, 1, 1) WRAP2(cursor_position, 1, 1)
@ -2628,6 +2637,7 @@ static PyMethodDef methods[] = {
MND(paste_bytes, METH_O) MND(paste_bytes, METH_O)
MND(focus_changed, METH_O) MND(focus_changed, METH_O)
MND(has_focus, METH_NOARGS) MND(has_focus, METH_NOARGS)
MND(has_activity_since_last_focus, METH_NOARGS)
MND(copy_colors_from, METH_O) MND(copy_colors_from, METH_O)
MND(set_marker, METH_VARARGS) MND(set_marker, METH_VARARGS)
MND(marked_cells, METH_NOARGS) MND(marked_cells, METH_NOARGS)

View File

@ -122,6 +122,7 @@ typedef struct {
DisableLigature disable_ligatures; DisableLigature disable_ligatures;
PyObject *marker; PyObject *marker;
bool has_focus; bool has_focus;
bool has_activity_since_last_focus;
} Screen; } Screen;

View File

@ -24,6 +24,7 @@ class TabBarData(NamedTuple):
needs_attention: bool needs_attention: bool
num_windows: int num_windows: int
layout_name: str layout_name: str
has_activity_since_last_focus: bool
class DrawData(NamedTuple): class DrawData(NamedTuple):
@ -40,6 +41,7 @@ class DrawData(NamedTuple):
default_bg: Color default_bg: Color
title_template: str title_template: str
active_title_template: Optional[str] active_title_template: Optional[str]
tab_activity_symbol: Optional[str]
def as_rgb(x: int) -> int: def as_rgb(x: int) -> int:
@ -65,6 +67,12 @@ def draw_title(draw_data: DrawData, screen: Screen, tab: TabBarData, index: int)
screen.cursor.fg = draw_data.bell_fg screen.cursor.fg = draw_data.bell_fg
screen.draw('🔔 ') screen.draw('🔔 ')
screen.cursor.fg = fg screen.cursor.fg = fg
if tab.has_activity_since_last_focus and draw_data.tab_activity_symbol:
fg = screen.cursor.fg
screen.cursor.fg = draw_data.bell_fg
screen.draw(draw_data.tab_activity_symbol)
screen.cursor.fg = fg
template = draw_data.title_template template = draw_data.title_template
if tab.is_active and draw_data.active_title_template is not None: if tab.is_active and draw_data.active_title_template is not None:
template = draw_data.active_title_template template = draw_data.active_title_template
@ -226,7 +234,8 @@ class TabBar:
self.opts.tab_fade, self.opts.active_tab_foreground, self.opts.active_tab_background, self.opts.tab_fade, self.opts.active_tab_foreground, self.opts.active_tab_background,
self.opts.inactive_tab_foreground, self.opts.inactive_tab_background, self.opts.inactive_tab_foreground, self.opts.inactive_tab_background,
self.opts.tab_bar_background or self.opts.background, self.opts.tab_title_template, self.opts.tab_bar_background or self.opts.background, self.opts.tab_title_template,
self.opts.active_tab_title_template self.opts.active_tab_title_template,
self.opts.tab_activity_symbol
) )
if self.opts.tab_bar_style == 'separator': if self.opts.tab_bar_style == 'separator':
self.draw_func = draw_tab_with_separator self.draw_func = draw_tab_with_separator

View File

@ -736,13 +736,16 @@ class TabManager: # {{{
for t in self.tabs: for t in self.tabs:
title = (t.name or t.title or appname).strip() title = (t.name or t.title or appname).strip()
needs_attention = False needs_attention = False
has_activity_since_last_focus = False
for w in t: for w in t:
if w.needs_attention: if w.needs_attention:
needs_attention = True needs_attention = True
break if w.has_activity_since_last_focus:
has_activity_since_last_focus = True
ans.append(TabBarData( ans.append(TabBarData(
title, t is at, needs_attention, title, t is at, needs_attention,
len(t), t.current_layout.name or '' len(t), t.current_layout.name or '',
has_activity_since_last_focus
)) ))
return ans return ans

View File

@ -514,6 +514,10 @@ class Window:
def is_active(self) -> bool: def is_active(self) -> bool:
return get_boss().active_window is self return get_boss().active_window is self
@property
def has_activity_since_last_focus(self) -> bool:
return self.screen.has_activity_since_last_focus()
def on_bell(self) -> None: def on_bell(self) -> None:
if self.opts.command_on_bell and self.opts.command_on_bell != ['none']: if self.opts.command_on_bell and self.opts.command_on_bell != ['none']:
import subprocess import subprocess