__init__.py
Python script, ASCII text executable
1""" 2D-Bus notifier applet for the Panorama panel. 3Copyright 2025, roundabout-host.com <vlad@roundabout-host.com> 4 5This program is free software: you can redistribute it and/or modify 6it under the terms of the GNU General Public Licence as published by 7the Free Software Foundation, either version 3 of the Licence, or 8(at your option) any later version. 9 10This program is distributed in the hope that it will be useful, 11but WITHOUT ANY WARRANTY; without even the implied warranty of 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13GNU General Public Licence for more details. 14 15You should have received a copy of the GNU General Public Licence 16along with this program. If not, see <https://www.gnu.org/licenses/>. 17""" 18 19from __future__ import annotations 20 21import panorama_panel 22import locale 23from pathlib import Path 24from pydbus import SessionBus 25 26import gi 27gi.require_version("Gtk", "4.0") 28 29from gi.repository import Gtk, Gdk, GLib 30 31 32module_directory = Path(__file__).resolve().parent 33 34locale.bindtextdomain("panorama-app-menu", module_directory / "locale") 35_ = lambda x: locale.dgettext("panorama-app-menu", x) 36 37 38custom_css = """ 39.notification-button { 40padding: 0; 41} 42""" 43 44css_provider = Gtk.CssProvider() 45css_provider.load_from_data(custom_css) 46Gtk.StyleContext.add_provider_for_display( 47Gdk.Display.get_default(), 48css_provider, 49Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION 50) 51 52 53class NotificationsService: 54""" 55<node> 56<interface name="org.freedesktop.Notifications"> 57<method name="GetCapabilities"> 58<arg type="as" name="caps" direction="out"/> 59</method> 60<method name="GetServerInformation"> 61<arg type="s" name="name" direction="out"/> 62<arg type="s" name="vendor" direction="out"/> 63<arg type="s" name="version" direction="out"/> 64<arg type="s" name="spec_version" direction="out"/> 65</method> 66<method name="Notify"> 67<arg type="s" name="app_name" direction="in"/> 68<arg type="u" name="replaces_id" direction="in"/> 69<arg type="s" name="app_icon" direction="in"/> 70<arg type="s" name="summary" direction="in"/> 71<arg type="s" name="body" direction="in"/> 72<arg type="as" name="actions" direction="in"/> 73<arg type="a{sv}" name="hints" direction="in"/> 74<arg type="i" name="expire_timeout" direction="in"/> 75<arg type="u" name="id" direction="out"/> 76</method> 77</interface> 78</node> 79""" 80 81def __init__(self, applet: NotifierApplet): 82self._next_id = 1 83self.applet = applet 84 85def GetCapabilities(self): 86return ["body"] 87 88def GetServerInformation(self): 89# name, vendor, version, spec_version 90return "panorama-panel", "panorama", "0.1", "1.2" 91 92def Notify(self, *args): 93notification_id = self._next_id 94self._next_id += 1 95self.applet.push_notification(*args) 96return notification_id 97 98 99class NotifierApplet(panorama_panel.Applet): 100name = _("Notification centre") 101description = _("Get desktop notifications") 102 103def __init__(self, orientation=Gtk.Orientation.HORIZONTAL, config=None): 104super().__init__(orientation=orientation, config=config) 105if config is None: 106config = {} 107 108self.button = Gtk.MenuButton() 109self.button.set_has_frame(False) 110self.button.add_css_class("notification-button") 111self.stack = Gtk.Stack() 112self.button.set_child(self.stack) 113self.notification_indicator = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) 114self.notification_indicator.append(Gtk.Image.new_from_icon_name("")) 115self.stack.add_child(self.notification_indicator) 116self.stack.set_transition_type(Gtk.StackTransitionType.SLIDE_UP_DOWN) 117self.stack.set_transition_duration(500) 118# TODO: support the other parameters; add a popover with a history 119self.append(self.button) 120 121self.service = NotificationsService(self) 122bus = SessionBus() 123self.publishing = bus.publish("org.freedesktop.Notifications", self.service) 124 125def push_notification(self, app_name, replaces_id, app_icon, summary, body, actions, hints, expire_timeout): 126new_child = Gtk.Label.new(_(summary)) 127self.stack.add_child(new_child) 128self.stack.set_visible_child(new_child) 129def remove_notification(): 130self.stack.set_visible_child(self.stack.get_visible_child().get_prev_sibling()) 131def remove_child(): 132self.stack.remove(new_child) 133GLib.timeout_add(self.stack.get_transition_duration(), remove_child) 134return False 135if expire_timeout <= 0: 136expire_timeout = 2500 137GLib.timeout_add(expire_timeout, remove_notification) 138# TODO: notify that it was closed 139 140def get_config(self): 141return {} 142 143def shutdown(self): 144self.publishing.unpublish() 145