macOS: When the program running in kitty reports progress information for a task, show a progress bar on the kitty dock icon

This commit is contained in:
Kovid Goyal
2025-03-11 09:33:13 +05:30
parent c947781bf9
commit f07880b7fe
4 changed files with 44 additions and 1 deletions

View File

@@ -104,6 +104,8 @@ Detailed list of changes
- Wayland: Allow overriding the kitty OS Window icon on compositors that implement the xdg-toplevel-icon protocol
- macOS: When the program running in kitty reports progress information for a task, show a progress bar on the kitty dock icon
0.40.0 [2025-03-08]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -3090,3 +3090,22 @@ class Boss:
tm = self.active_tab_manager_with_dispatch
if tm is not None:
tm.toggle_tab(match_expression)
def update_progress_in_dock(self) -> None:
if not is_macos:
return
has_indeterminate_progress = False
num_of_windows_with_progress = total_progress = 0
for tm in self.os_window_map.values():
if tm.num_of_windows_with_progress:
total_progress += tm.total_progress
num_of_windows_with_progress += tm.num_of_windows_with_progress
if tm.has_indeterminate_progress:
has_indeterminate_progress = True
from .fast_data_types import cocoa_show_progress_bar_on_dock_icon
if num_of_windows_with_progress:
cocoa_show_progress_bar_on_dock_icon(min(100, total_progress / num_of_windows_with_progress))
elif has_indeterminate_progress:
cocoa_show_progress_bar_on_dock_icon(101)
else:
cocoa_show_progress_bar_on_dock_icon()

View File

@@ -1312,7 +1312,7 @@ cocoa_show_progress_bar_on_dock_icon(PyObject *self UNUSED, PyObject *args) {
}
[dock_pbar setFrameSize:NSMakeSize(dockTile.size.width - 20, 20)];
[dock_pbar setFrameOrigin:NSMakePoint(10, -2)];
[dockTile setContentView:percent < 0 || percent == 100 ? nil : dock_content_view];
[dockTile setContentView:percent < 0 ? nil : dock_content_view];
[dockTile display];
Py_RETURN_NONE;
}

View File

@@ -126,6 +126,7 @@ class Tab: # {{{
confirm_close_window_id: int = 0
num_of_windows_with_progress: int = 0
total_progress: int = 0
has_indeterminate_progress: bool = False
last_focused_window_with_progress_id: int = 0
def __init__(
@@ -169,6 +170,7 @@ class Tab: # {{{
self.num_of_windows_with_progress = 0
self.total_progress = 0
self.last_focused_window_with_progress_id = 0
self.has_indeterminate_progress = False
focused_at = 0.
for window in self:
p = window.progress
@@ -177,10 +179,15 @@ class Tab: # {{{
if p.state in (ProgressState.set, ProgressState.paused):
self.total_progress += p.percent
self.num_of_windows_with_progress += 1
elif p.state is ProgressState.indeterminate:
self.has_indeterminate_progress = True
if window.last_focused_at > focused_at or (not window.last_focused_at and window.id > self.last_focused_window_with_progress_id):
focused_at = window.last_focused_at
self.last_focused_window_with_progress_id = window.id
self.mark_tab_bar_dirty()
tm = self.tab_manager_ref()
if tm is not None:
tm.update_progress()
def has_single_window_visible(self) -> bool:
if self.current_layout.only_active_window_visible:
@@ -910,6 +917,9 @@ class Tab: # {{{
class TabManager: # {{{
confirm_close_window_id: int = 0
num_of_windows_with_progress: int = 0
total_progress: int = 0
has_indeterminate_progress: bool = False
def __init__(self, os_window_id: int, args: CLIOptions, wm_class: str, wm_name: str, startup_session: SessionType | None = None):
self.os_window_id = os_window_id
@@ -1283,6 +1293,18 @@ class TabManager: # {{{
if len(self.recent_mouse_events) > 5:
self.recent_mouse_events.popleft()
def update_progress(self) -> None:
self.num_of_windows_with_progress = 0
self.total_progress = 0
self.has_indeterminate_progress = False
for tab in self:
if tab.num_of_windows_with_progress:
self.total_progress += tab.total_progress
self.num_of_windows_with_progress += tab.num_of_windows_with_progress
if tab.has_indeterminate_progress:
self.has_indeterminate_progress = True
get_boss().update_progress_in_dock()
@property
def tab_bar_rects(self) -> tuple[Border, ...]:
return self.tab_bar.blank_rects if self.tab_bar_should_be_visible else ()