diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index d8d58d9a2..75ea4a0a6 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -2536,6 +2536,7 @@ Valid values: * +qt+: Use Qt's native notification presenter. Switching from or to this value requires a restart of qutebrowser. * +libnotify+: Use a libnotify-compatible presenter if DBus is available, show an error (and fall back to Qt's presenter) otherwise. * +systray+: Use a notification presenter based on a systray icon. This is a reimplementation of the `qt` setting value, but with the possibility to switch to it at runtime. + * +messages+: Show notifications as qutebrowser messages. Most features (images, clicking/closing notifications) aren't available. Default: +pass:[auto]+ diff --git a/qutebrowser/browser/webengine/notification.py b/qutebrowser/browser/webengine/notification.py index d46a3013f..17f2b3f9b 100644 --- a/qutebrowser/browser/webengine/notification.py +++ b/qutebrowser/browser/webengine/notification.py @@ -27,10 +27,11 @@ https://specifications.freedesktop.org/notification-spec/notification-spec-lates import html import dataclasses +import itertools from typing import Any, Dict, Optional, TYPE_CHECKING from PyQt5.QtCore import (Qt, QObject, QVariant, QMetaType, QByteArray, pyqtSlot, - pyqtSignal) + pyqtSignal, QTimer) from PyQt5.QtGui import QImage, QIcon, QPixmap from PyQt5.QtDBus import (QDBusConnection, QDBusInterface, QDBus, QDBusArgument, QDBusMessage, QDBusError) @@ -129,6 +130,8 @@ class NotificationBridgePresenter(QObject): self._adapter = SystrayNotificationAdapter() elif setting == "systray": self._adapter = SystrayNotificationAdapter() + elif setting == "messages": + self._adapter = MessagesNotificationAdapter() else: raise utils.Unreachable(setting) @@ -181,9 +184,8 @@ class NotificationBridgePresenter(QObject): f"new id {notification_id}.") log.webview.debug(f"Sent out notification {notification_id}") - self._active_notifications[notification_id] = qt_notification - qt_notification.show() + self._active_notifications[notification_id] = qt_notification def _find_replaces_id( self, @@ -290,6 +292,43 @@ class SystrayNotificationAdapter(AbstractNotificationAdapter): self.on_clicked.emit(self.NOTIFICATION_ID) +class MessagesNotificationAdapter(AbstractNotificationAdapter): + + """Shows notifications using qutebrowser messages. + + This is mostly used as a fallback if no other method is available. Most notification + features are not supported. + """ + + def __init__(self, parent: QObject = None) -> None: + super().__init__(parent) + self._id_gen = itertools.count(1) + + def present( + self, + qt_notification: "QWebEngineNotification", + *, + replaces_id: Optional[int], + ) -> int: + url = html.escape(qt_notification.origin().toDisplayString()) + title = html.escape(qt_notification.title()) + body = html.escape(qt_notification.message()) + hint = "" if qt_notification.icon().isNull() else " (image not shown)" + + new_id = replaces_id if replaces_id is not None else next(self._id_gen) + markup = ( + f"Notification from {url}:{hint}

" + f"{title}
" + f"{body}" + ) + message.info(markup, replace=f'notifications-{new_id}') + + # Faking closing, timing might not be 100% accurate + QTimer.singleShot(config.val.messages.timeout, lambda: self.close_id.emit(new_id)) + + return new_id + + @dataclasses.dataclass class _ServerQuirks: diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index 0ef9f2fad..b828732ce 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -926,6 +926,8 @@ content.notifications.presenter: - systray: Use a notification presenter based on a systray icon. This is a reimplementation of the `qt` setting value, but with the possibility to switch to it at runtime. + - messages: Show notifications as qutebrowser messages. Most features (images, + clicking/closing notifications) aren't available. backend: QtWebEngine: Qt 5.14 QtWebKit: false