parent
83315f7999
commit
9a54da84dc
@ -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,
|
||||||
|
|||||||
@ -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 '\'"':
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user