diff --git a/dodo/app.py b/dodo/app.py index e01369d..f47c472 100644 --- a/dodo/app.py +++ b/dodo/app.py @@ -24,6 +24,7 @@ import os import signal import fcntl +import platform import subprocess from typing import Optional, Literal import logging @@ -165,6 +166,8 @@ def _handle_signal_wakeup(self) -> None: self._cleanup_sync() self.quit() + self.update_dock_badge() + def show_help(self) -> None: """Show help window""" @@ -363,6 +366,8 @@ def refresh_panels(self) -> None: w = self.tabs.currentWidget() if w and isinstance(w, panel.Panel): w.refresh() + self.update_dock_badge() + def update_single_thread(self, thread_id: str, msg_id: str|None=None): current = self.tabs.currentWidget() for i in range(self.num_panels()): @@ -372,6 +377,34 @@ def update_single_thread(self, thread_id: str, msg_id: str|None=None): if w == current and w.dirty: w.refresh() + self.update_dock_badge() + + def update_dock_badge(self) -> None: + """Update the macOS dock icon badge with the unread count + + Uses the query in :func:`~dodo.settings.macos_dock_badge_query` to count + unread threads and display the count as a native dock badge. + Requires ``pyobjc-framework-Cocoa`` on macOS; silently does nothing + on other platforms or if pyobjc is not installed.""" + + if platform.system() != 'Darwin' or not settings.macos_dock_badge_query: + return + + try: + from AppKit import NSApp # type: ignore[import-untyped] + except ImportError: + return + + try: + r = subprocess.run( + ['notmuch', 'count', '--output=threads', '--', *settings.macos_dock_badge_query.split()], + stdout=subprocess.PIPE, timeout=5) + count = int(r.stdout.decode('utf-8').strip()) + label = str(count) if count > 0 else '' + NSApp.dockTile().setBadgeLabel_(label) + except Exception: + pass + def _cleanup_sync(self) -> None: """Stop the sync timer and terminate any running sync thread""" if self.sync_timer is not None: diff --git a/dodo/settings.py b/dodo/settings.py index c112eb2..df31ed6 100644 --- a/dodo/settings.py +++ b/dodo/settings.py @@ -173,6 +173,15 @@ tag:trash"` and use `query:inbox` as a search term. """ +macos_dock_badge_query = 'tag:unread AND tag:inbox' +"""Notmuch query whose thread count is shown as the macOS dock badge + +Set this to a notmuch search query. The number of matching threads will be +displayed as a badge on the dock icon. Set to an empty string to disable. + +Requires the ``pyobjc-framework-Cocoa`` package on macOS. +""" + no_hooks_on_send = True """disable/enable calling notmuch hooks when sending email diff --git a/setup.py b/setup.py index 45cf93e..60160bf 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,12 @@ packages=["dodo"], package_data={'': ['*.svg']}, data_files=data_files, - install_requires=["PyQt6>=6.2", "PyQt6-WebEngine>=6.2", "bleach>=5.0"], + install_requires=[ + "PyQt6>=6.2", + "PyQt6-WebEngine>=6.2", + "bleach>=5.0", + "pyobjc-framework-Cocoa; sys_platform == 'darwin'", + ], python_requires=">=3.7", entry_points={'console_scripts': 'dodo=dodo.app:main'}, )