From f07880b7fe15a9c51a0fe1f4a9b3fde8db34e6f5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 11 Mar 2025 09:33:13 +0530 Subject: [PATCH] macOS: When the program running in kitty reports progress information for a task, show a progress bar on the kitty dock icon --- docs/changelog.rst | 2 ++ kitty/boss.py | 19 +++++++++++++++++++ kitty/cocoa_window.m | 2 +- kitty/tabs.py | 22 ++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 48001630d..b995b739d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -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] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/kitty/boss.py b/kitty/boss.py index cf652559f..64e44768b 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -3090,3 +3090,22 @@ def toggle_tab(self, match_expression: str) -> None: 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() diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index 89d21c9b7..5d57b6c3c 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -1312,7 +1312,7 @@ - (void)drawRect:(NSRect)dirtyRect { } [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; } diff --git a/kitty/tabs.py b/kitty/tabs.py index ae5576f00..0b04f385e 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -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 @@ def update_progress(self) -> None: 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 @@ def update_progress(self) -> None: 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 @@ def make_active(self) -> None: 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 @@ def handle_click_on_tab(self, x: int, button: int, modifiers: int, action: int) 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 ()