From ea2c74797804736cb43d7b1bd991ad9396a4eb19 Mon Sep 17 00:00:00 2001 From: deltragon Date: Sun, 31 Dec 2023 00:02:06 +0100 Subject: [PATCH] trayicon: migrate from libappindicator to statusnotifier --- README.md | 4 +- debian/control | 2 +- safeeyes/plugins/trayicon/plugin.py | 691 +++++++++++++++++++--------- 3 files changed, 474 insertions(+), 223 deletions(-) diff --git a/README.md b/README.md index 813043e..550b524 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ sudo apt-get install safeeyes ### Fedora ```bash -sudo dnf install libappindicator-gtk3 python3-psutil python3-packaging cairo-devel python3-devel gobject-introspection-devel cairo-gobject-devel +sudo dnf install python3-psutil python3-packaging cairo-devel python3-devel gobject-introspection-devel cairo-gobject-devel sudo pip3 install safeeyes sudo gtk-update-icon-cache /usr/share/icons/hicolor ``` @@ -105,9 +105,7 @@ flatpak install flathub io.github.slgobinath.SafeEyes Ensure to meet the following dependencies: -- gir1.2-appindicator3-0.1 or gir1.2-ayatanaappindicator3-0.1 - gir1.2-notify-0.7 -- libappindicator-gtk3 - python3-psutil - xprintidle (optional) - wlrctl (wayland optional) diff --git a/debian/control b/debian/control index e72dac3..c07cbb8 100644 --- a/debian/control +++ b/debian/control @@ -9,7 +9,7 @@ Homepage: https://github.com/slgobinath/SafeEyes/ Package: safeeyes Architecture: all -Depends: ${misc:Depends}, ${python3:Depends}, gir1.2-ayatanaappindicator3-0.1, python3 (>= 3.10.0), python3-xlib, gir1.2-notify-0.7, python3-babel, x11-utils, xprintidle, alsa-utils, python3-psutil, python3-croniter, python3-packaging +Depends: ${misc:Depends}, ${python3:Depends}, python3 (>= 3.10.0), python3-xlib, gir1.2-notify-0.7, python3-babel, x11-utils, xprintidle, alsa-utils, python3-psutil, python3-croniter, python3-packaging Description: Safe Eyes Safe Eyes is a simple tool to remind you to take periodic breaks for your eyes. This is essential for anyone spending more time on the computer to avoid eye strain and other physical problems. . diff --git a/safeeyes/plugins/trayicon/plugin.py b/safeeyes/plugins/trayicon/plugin.py index f8ba1e3..5c5a6bb 100644 --- a/safeeyes/plugins/trayicon/plugin.py +++ b/safeeyes/plugins/trayicon/plugin.py @@ -20,14 +20,7 @@ import datetime from safeeyes.model import BreakType import gi gi.require_version('Gtk', '3.0') -try: - gi.require_version('AppIndicator3', '0.1') - from gi.repository import AppIndicator3 as appindicator -except: - #fall back to Ayatana - gi.require_version('AyatanaAppIndicator3', '0.1') - from gi.repository import AyatanaAppIndicator3 as appindicator -from gi.repository import Gtk +from gi.repository import Gio, GLib import logging from safeeyes import utility import threading @@ -37,11 +30,291 @@ import time Safe Eyes tray icon plugin """ -APPINDICATOR_ID = 'safeeyes_2' context = None tray_icon = None safeeyes_config = None +SNI_NODE_INFO = Gio.DBusNodeInfo.new_for_xml(""" + + + + + + + + + + + + + + + +""").interfaces[0] + +MENU_NODE_INFO = Gio.DBusNodeInfo.new_for_xml(""" + + + + + + + + + + + + + + + + + + + + + + + + + +""").interfaces[0] + +class DBusService: + def __init__(self, interface_info, object_path, bus): + self.interface_info = interface_info + self.bus = bus + self.object_path = object_path + self.registration_id = None + + def register(self): + self.registration_id = self.bus.register_object( + object_path=self.object_path, + interface_info=self.interface_info, + method_call_closure=self.on_method_call, + get_property_closure=self.on_get_property + ) + + if not self.registration_id: + raise GLib.Error(f"Failed to register object with path {self.object_path}") + + self.interface_info.cache_build() + + def unregister(self): + self.interface_info.cache_release() + + if self.registration_id is not None: + self.bus.unregister_object(self.registration_id) + self.registration_id = None + + def on_method_call(self, _connection, _sender, _path, _interface_name, method_name, parameters, invocation): + method_info = self.interface_info.lookup_method(method_name) + method = getattr(self, method_name) + result = method(*parameters.unpack()) + out_arg_types = "".join([arg.signature for arg in method_info.out_args]) + return_value = None + + if method_info.out_args: + return_value = GLib.Variant(f"({out_arg_types})", result) + + invocation.return_value(return_value) + + def on_get_property(self, _connection, _sender, _path, _interface_name, property_name): + property_info = self.interface_info.lookup_property(property_name) + return GLib.Variant(property_info.signature, getattr(self, property_name)) + + def emit_signal(self, signal_name, args = None): + signal_info = self.interface_info.lookup_signal(signal_name) + if len(signal_info.args) == 0: + parameters = None + else: + arg_types = "".join([arg.signature for arg in signal_info.args]) + parameters = GLib.Variant(f"({arg_types})", args) + self.bus.emit_signal( + destination_bus_name=None, + object_path=self.object_path, + interface_name=self.interface_info.name, + signal_name=signal_name, + parameters=parameters + ) + +class DBusMenuService(DBusService): + DBUS_SERVICE_PATH = '/io/github/slgobinath/SafeEyes/Menu' + + revision = 0 + + items = [] + idToCallback = {} + + def __init__(self, session_bus, context, items): + super().__init__( + interface_info=MENU_NODE_INFO, + object_path=self.DBUS_SERVICE_PATH, + bus=session_bus + ) + + self.set_items(items) + + def set_items(self, items): + self.items = items + + self.idToCallback = self.getItemCallbacks(items, {}) + + self.revision += 1 + + self.LayoutUpdated(self.revision, 0) + + @staticmethod + def getItemCallbacks(items, idToCallback): + for item in items: + if item.get('hidden', False) == True: + continue + if 'callback' in item: + idToCallback[item['id']] = item['callback'] + if 'children' in item: + idToCallback = DBusMenuService.getItemCallbacks(item['children'], idToCallback) + + return idToCallback + + @staticmethod + def itemToDbus(item, recursion_depth): + result = {} + + if item.get('hidden', False) == True: + return None + + string_props = ['label', 'icon-name', 'type', 'children-display'] + for key in string_props: + if key in item: + result[key] = GLib.Variant('s', item[key]) + + bool_props = ['enabled'] + for key in bool_props: + if key in item: + result[key] = GLib.Variant('b', item[key]) + + children = [] + if recursion_depth > 1 or recursion_depth == -1: + if "children" in item: + children = [DBusMenuService.itemToDbus(item, recursion_depth - 1) for item in item['children']] + children = [i for i in children if i is not None] + + return GLib.Variant("(ia{sv}av)", (item['id'], result, children)) + + def findItemsWithParent(self, parent_id, items): + for item in items: + if item.get('hidden', False) == True: + continue + if 'children' in item: + if item['id'] == parent_id: + return item['children'] + else: + ret = self.findItemsWithParent(parent_id, item['children']) + if ret is not None: + return ret + return None + + def GetLayout(self, parent_id, recursion_depth, property_names): + children = [] + + if parent_id == 0: + children = self.items + else: + children = self.findItemsWithParent(parent_id, self.items) + if children is None: + children = [] + + children = [self.itemToDbus(item, recursion_depth) for item in children] + children = [i for i in children if i is not None] + + ret = ( + self.revision, + ( + 0, + { 'children-display': GLib.Variant('s', 'submenu') }, + children + ) + ) + + return ret + + def Event(self, idx, event_id, data, timestamp): + if event_id != "clicked": + return + + if idx in self.idToCallback: + self.idToCallback[idx]() + + def AboutToShow(self, item_id): + return (False,) + + def LayoutUpdated(self, revision, parent): + self.emit_signal( + 'LayoutUpdated', + (revision, parent) + ) + +class StatusNotifierItemService(DBusService): + DBUS_SERVICE_PATH = '/io/github/slgobinath/SafeEyes' + + Category = 'ApplicationStatus' + Id = 'io.github.slgobinath.SafeEyes' + Title = _('Safe Eyes') + Status = 'Active' + IconName = 'io.github.slgobinath.SafeEyes-enabled' + IconThemePath = '' + ToolTip = ('', [], _('Safe Eyes'), '') + ItemIsMenu = True + Menu = None + + def __init__(self, session_bus, context, menu_items): + super().__init__( + interface_info=SNI_NODE_INFO, + object_path=self.DBUS_SERVICE_PATH, + bus=session_bus + ) + + self.bus = session_bus + + self._menu = DBusMenuService(session_bus, context, menu_items) + self.Menu = self._menu.DBUS_SERVICE_PATH + + def register(self): + self._menu.register() + super().register() + + watcher = Gio.DBusProxy.new_sync( + connection=self.bus, + flags=Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES, + info=None, + name='org.kde.StatusNotifierWatcher', + object_path='/StatusNotifierWatcher', + interface_name='org.kde.StatusNotifierWatcher', + cancellable=None, + ) + + watcher.RegisterStatusNotifierItem('(s)', self.DBUS_SERVICE_PATH) + + def unregister(self): + super().unregister() + self._menu.unregister() + + def set_items(self, items): + self._menu.set_items(items) + + def set_icon(self, icon): + self.IconName = icon + + self.emit_signal( + 'NewIcon' + ) + + def set_tooltip(self, title, description): + self.ToolTip = ('', [], title, description) + + self.emit_signal( + 'NewTooltip' + ) class TrayIcon: """ @@ -66,185 +339,185 @@ class TrayIcon: self.lock = threading.Lock() self.allow_disabling = plugin_config['allow_disabling'] self.animate = False + self.menu_locked = False - # Construct the tray icon - self.indicator = appindicator.Indicator.new( - APPINDICATOR_ID, "io.github.slgobinath.SafeEyes-enabled", appindicator.IndicatorCategory.APPLICATION_STATUS) - self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE) + session_bus = Gio.bus_get_sync(Gio.BusType.SESSION) - # Construct the context menu - self.menu = Gtk.Menu() + self.sni_service = StatusNotifierItemService( + session_bus, + context, + menu_items = self.get_items() + ) + self.sni_service.register() - # Next break info menu item - self.item_info = Gtk.ImageMenuItem() - img_timer = Gtk.Image() - img_timer.set_from_icon_name("io.github.slgobinath.SafeEyes-timer", 16) - self.item_info.set_image(img_timer) - - self.item_separator = Gtk.SeparatorMenuItem() - - self.item_enable = Gtk.MenuItem() - self.item_enable.connect('activate', self.on_enable_clicked) - - self.item_disable = Gtk.MenuItem() - self.sub_menu_disable = Gtk.Menu() - self.sub_menu_disable_items = [] - - # Read disable options and build the sub menu - for disable_option in plugin_config['disable_options']: - time_in_minutes = disable_option['time'] - label = [] - # Validate time value - if not isinstance(time_in_minutes, int) or time_in_minutes <= 0: - logging.error('Invalid time in disable option: ' + str(time_in_minutes)) - continue - time_unit = disable_option['unit'].lower() - if time_unit == 'seconds' or time_unit == 'second': - time_in_minutes = int(time_in_minutes / 60) - label = ['For %d Second', 'For %d Seconds'] - elif time_unit == 'minutes' or time_unit == 'minute': - time_in_minutes = int(time_in_minutes * 1) - label = ['For %d Minute', 'For %d Minutes'] - elif time_unit == 'hours' or time_unit == 'hour': - time_in_minutes = int(time_in_minutes * 60) - label = ['For %d Hour', 'For %d Hours'] - else: - # Invalid unit - logging.error('Invalid unit in disable option: ' + str(disable_option)) - continue - - # Create submenu - sub_menu_item = Gtk.MenuItem() - sub_menu_item.connect('activate', self.on_disable_clicked, time_in_minutes) - self.sub_menu_disable_items.append([sub_menu_item, label, disable_option['time']]) - self.sub_menu_disable.append(sub_menu_item) - - # Disable until restart submenu - self.sub_menu_item_until_restart = Gtk.MenuItem() - self.sub_menu_item_until_restart.connect('activate', self.on_disable_clicked, -1) - self.sub_menu_disable.append(self.sub_menu_item_until_restart) - - # Add the sub menu to the enable/disable menu - self.item_disable.set_submenu(self.sub_menu_disable) - - # Manual break menu item - self.item_manual_break = Gtk.MenuItem() - - self.sub_menu_manual_next_break = Gtk.MenuItem() - self.sub_menu_manual_next_break.connect('activate', self.on_manual_break_clicked, None) - self.sub_menu_manual_next_short_break = Gtk.MenuItem() - self.sub_menu_manual_next_short_break.connect('activate', self.on_manual_break_clicked, BreakType.SHORT_BREAK) - self.sub_menu_manual_next_long_break = Gtk.MenuItem() - self.sub_menu_manual_next_long_break.connect('activate', self.on_manual_break_clicked, BreakType.LONG_BREAK) - - self.sub_menu_manual_break = Gtk.Menu() - self.sub_menu_manual_break.append(self.sub_menu_manual_next_break) - self.sub_menu_manual_break.append(self.sub_menu_manual_next_short_break) - self.sub_menu_manual_break.append(self.sub_menu_manual_next_long_break) - self.item_manual_break.set_submenu(self.sub_menu_manual_break) - - # Settings menu item - self.item_settings = Gtk.MenuItem() - self.item_settings.connect('activate', self.show_settings) - - # About menu item - self.item_about = Gtk.MenuItem() - self.item_about.connect('activate', self.show_about) - - # Quit menu item - self.item_quit = Gtk.MenuItem() - self.item_quit.connect('activate', self.quit_safe_eyes) - - self.set_labels() - - # At startup, no need for activate menu - self.item_enable.set_sensitive(False) - - # Append all menu items and show the menu - self.menu.append(self.item_info) - self.menu.append(self.item_separator) - self.menu.append(self.item_enable) - self.menu.append(self.item_disable) - self.menu.append(self.item_manual_break) - self.menu.append(self.item_settings) - self.menu.append(self.item_about) - self.menu.append(self.item_quit) - self.menu.show_all() - - self.item_enable.set_visible(self.allow_disabling) - self.item_disable.set_visible(self.allow_disabling) - self.item_quit.set_visible(self.allow_disabling) - self.item_quit.set_visible(self.allow_disabling) - - self.indicator.set_menu(self.menu) + self.update_tooltip() def initialize(self, plugin_config): """ Initialize the tray icon by setting the config. """ self.plugin_config = plugin_config - self.set_labels() self.allow_disabling = plugin_config['allow_disabling'] - self.item_enable.set_visible(self.allow_disabling) - self.item_disable.set_visible(self.allow_disabling) - self.item_quit.set_visible(self.allow_disabling) - self.item_quit.set_visible(self.allow_disabling) - def set_labels(self): - """ - Update the text of menu items based on the selected language. - """ - for entry in self.sub_menu_disable_items: - # print(self.context['locale'].ngettext('For %d Hour', 'For %d Hours', 1) % 1) - entry[0].set_label(self.context['locale'].ngettext(entry[1][0], entry[1][1], entry[2]) % entry[2]) - - self.sub_menu_item_until_restart.set_label(_('Until restart')) - self.item_enable.set_label(_('Enable Safe Eyes')) - self.item_disable.set_label(_('Disable Safe Eyes')) + self.update_menu() + self.update_tooltip() + def get_items(self): breaks_found = self.has_breaks() + + info_message = _('No breaks available') + if breaks_found: if self.active: - if self.date_time: - self.__set_next_break_info() - self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled") + next_break = self.get_next_break_time() + + if next_break is not None: + (next_time, next_long_time, next_is_long) = next_break + + if next_long_time: + if next_is_long: + info_message = _('Next long break at %s') % (next_long_time) + else: + info_message = _('Next breaks at %s/%s') % (next_time, next_long_time) + else: + info_message = _('Next break at %s') % (next_time) else: if self.wakeup_time: - self.item_info.set_label(_('Disabled until %s') % utility.format_time(self.wakeup_time)) + info_message = _('Disabled until %s') % utility.format_time(self.wakeup_time) else: - self.item_info.set_label(_('Disabled until restart')) - self.indicator.set_label('', '') - self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled") + info_message = _('Disabled until restart') + + disable_items = [] + + if self.allow_disabling: + disable_option_dynamic_id = 13 + + for disable_option in self.plugin_config['disable_options']: + time_in_minutes = time_in_x = disable_option['time'] + label = [] + # Validate time value + if not isinstance(time_in_minutes, int) or time_in_minutes <= 0: + logging.error('Invalid time in disable option: ' + str(time_in_minutes)) + continue + time_unit = disable_option['unit'].lower() + if time_unit == 'seconds' or time_unit == 'second': + time_in_minutes = int(time_in_minutes / 60) + label = ['For %d Second', 'For %d Seconds'] + elif time_unit == 'minutes' or time_unit == 'minute': + time_in_minutes = int(time_in_minutes * 1) + label = ['For %d Minute', 'For %d Minutes'] + elif time_unit == 'hours' or time_unit == 'hour': + time_in_minutes = int(time_in_minutes * 60) + label = ['For %d Hour', 'For %d Hours'] + else: + # Invalid unit + logging.error('Invalid unit in disable option: ' + str(disable_option)) + continue + + label = self.context['locale'].ngettext(label[0], label[1], time_in_x) % time_in_x + + disable_items.append({ + 'id': disable_option_dynamic_id, + 'label': label, + 'callback': lambda: self.on_disable_clicked(time_in_minutes), + }) + + disable_option_dynamic_id += 1 + + disable_items.append({ + 'id': 12, + 'label': _('Until restart'), + 'callback': lambda: self.on_disable_clicked(-1), + }) + + return [ + { + 'id': 1, + 'label': info_message, + 'icon-name': "io.github.slgobinath.SafeEyes-timer", + 'enabled': breaks_found and self.active, + }, + { + 'id': 2, + 'type': "separator", + }, + { + 'id': 3, + 'label': _("Enable Safe Eyes"), + 'enabled': breaks_found and not self.active, + 'callback': self.on_enable_clicked, + 'hidden': not self.allow_disabling, + }, + { + 'id': 4, + 'label': _("Disable Safe Eyes"), + 'enabled': breaks_found and self.active and not self.menu_locked, + 'children-display': 'submenu', + 'children': disable_items, + 'hidden': not self.allow_disabling, + }, + { + 'id': 5, + 'label': _('Take a break now'), + 'enabled': breaks_found and self.active and not self.menu_locked, + 'children-display': 'submenu', + 'children': [ + { + 'id': 9, + 'label': _('Any Break'), + 'callback': lambda: self.on_manual_break_clicked(None), + }, + { + 'id': 10, + 'label': _('Short Break'), + 'callback': lambda: self.on_manual_break_clicked(BreakType.SHORT_BREAK), + }, + { + 'id': 11, + 'label': _('Long Break'), + 'callback': lambda: self.on_manual_break_clicked(BreakType.LONG_BREAK), + }, + ] + }, + { + 'id': 6, + 'label': _('Settings'), + 'enabled': not self.menu_locked, + 'callback': self.show_settings, + }, + { + 'id': 7, + 'label': _('About'), + 'callback': self.show_about, + }, + { + 'id': 8, + 'label': _('Quit'), + 'enabled': not self.menu_locked, + 'callback': self.quit_safe_eyes, + 'hidden': not self.allow_disabling, + }, + ] + + def update_menu(self): + self.sni_service.set_items(self.get_items()) + + def update_tooltip(self): + next_break = self.get_next_break_time() + + if next_break is not None and self.plugin_config.get('show_time_in_tray', False): + (next_time, next_long_time, _next_is_long) = next_break + + if next_long_time and self.plugin_config.get('show_long_time_in_tray', False): + description = next_long_time + else: + description = next_time else: - self.item_info.set_label(_('No breaks available')) - self.indicator.set_label('', '') - self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled") - self.item_info.set_sensitive(breaks_found and self.active) - self.item_enable.set_sensitive(breaks_found and not self.active) - self.item_disable.set_sensitive(breaks_found and self.active) - self.item_manual_break.set_sensitive(breaks_found and self.active) + description = '' - self.item_manual_break.set_label(_('Take a break now')) - self.sub_menu_manual_next_break.set_label(_('Any break')) - self.sub_menu_manual_next_short_break.set_label(_('Short break')) - self.sub_menu_manual_next_long_break.set_label(_('Long break')) - self.item_settings.set_label(_('Settings')) - self.item_about.set_label(_('About')) - self.item_quit.set_label(_('Quit')) + self.sni_service.set_tooltip(_('Safe Eyes'), description) - def show_icon(self): - """ - Show the tray icon. - """ - self.indicator.set_status(appindicator.IndicatorStatus.ACTIVE) - - def hide_icon(self): - """ - Hide the tray icon. - """ - self.indicator.set_status(appindicator.IndicatorStatus.PASSIVE) - - def quit_safe_eyes(self, *args): + def quit_safe_eyes(self): """ Handle Quit menu action. This action terminates the application. @@ -257,14 +530,14 @@ class TrayIcon: self.idle_condition.release() self.quit() - def show_settings(self, *args): + def show_settings(self): """ Handle Settings menu action. This action shows the Settings dialog. """ self.on_show_settings() - def show_about(self, *args): + def show_about(self): """ Handle About menu action. This action shows the About dialog. @@ -277,50 +550,37 @@ class TrayIcon: """ logging.info("Update next break information") self.date_time = dateTime - self.__set_next_break_info() + self.update_menu() + self.update_tooltip() + + def get_next_break_time(self): + if not (self.has_breaks() and self.active and self.date_time): + return None - def __set_next_break_info(self): - """ - A private method to be called within this class to update the next break information using self.dateTime. - """ formatted_time = utility.format_time(self.get_break_time()) long_time = self.get_break_time(BreakType.LONG_BREAK) if long_time: long_time = utility.format_time(long_time) if long_time == formatted_time: - message = _('Next long break at %s') % (long_time) + return (long_time, long_time, True) else: - message = _('Next breaks at %s/%s') % (formatted_time, long_time) - else: - message = _('Next break at %s') % (formatted_time) + return (formatted_time, long_time, False) - # Update the menu item label - utility.execute_main_thread(self.item_info.set_label, message) + return (formatted_time, None, False) - # Update the tray icon label - if self.plugin_config.get('show_time_in_tray', False): - show_long = long_time and self.plugin_config.get('show_long_time_in_tray', False) - self.indicator.set_label(long_time if show_long else formatted_time, '') - else: - self.indicator.set_label('', '') - def on_manual_break_clicked(self, *args): + def on_manual_break_clicked(self, break_type): """ Trigger a break manually. """ - if len(args) > 1: - break_type = args[1] - self.take_break(break_type) - else: - self.take_break() + self.take_break(break_type) - def on_enable_clicked(self, *args): + def on_enable_clicked(self): """ Handle 'Enable Safe Eyes' menu action. This action enables the application if it is currently disabled. """ - # active = self.item_enable.get_active() if not self.active: with self.lock: self.enable_ui() @@ -330,41 +590,40 @@ class TrayIcon: self.idle_condition.notify_all() self.idle_condition.release() - def on_disable_clicked(self, *args): + def on_disable_clicked(self, time_to_wait): """ Handle the menu actions of all the sub menus of 'Disable Safe Eyes'. This action disables the application if it is currently active. """ - # active = self.item_enable.get_active() - if self.active and len(args) > 1: + if self.active: self.disable_ui() - time_to_wait = args[1] if time_to_wait <= 0: info = _('Disabled until restart') self.disable_safeeyes(info) self.wakeup_time = None - self.item_info.set_label(info) else: self.wakeup_time = datetime.datetime.now() + datetime.timedelta(minutes=time_to_wait) info = _('Disabled until %s') % utility.format_time(self.wakeup_time) self.disable_safeeyes(info) - self.item_info.set_label(info) utility.start_thread(self.__schedule_resume, time_minutes=time_to_wait) + self.update_menu() def lock_menu(self): """ This method is called by the core to prevent user from disabling Safe Eyes after the notification. """ if self.active: - self.menu.set_sensitive(False) + self.menu_locked = True + self.update_menu() def unlock_menu(self): """ This method is called by the core to activate the menu after the the break. """ if self.active: - self.menu.set_sensitive(True) + self.menu_locked = False + self.update_menu() def disable_ui(self): """ @@ -373,13 +632,9 @@ class TrayIcon: if self.active: logging.info('Disable Safe Eyes') self.active = False - self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled") - self.item_info.set_label(_('Disabled until restart')) - self.indicator.set_label('', '') - self.item_info.set_sensitive(False) - self.item_enable.set_sensitive(True) - self.item_disable.set_sensitive(False) - self.item_manual_break.set_sensitive(False) + + self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled") + self.update_menu() def enable_ui(self): """ @@ -388,11 +643,9 @@ class TrayIcon: if not self.active: logging.info('Enable Safe Eyes') self.active = True - self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled") - self.item_info.set_sensitive(True) - self.item_enable.set_sensitive(False) - self.item_disable.set_sensitive(True) - self.item_manual_break.set_sensitive(True) + + self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled") + self.update_menu() def __schedule_resume(self, time_minutes): """ @@ -404,14 +657,14 @@ class TrayIcon: with self.lock: if not self.active: - utility.execute_main_thread(self.item_enable.activate) + utility.execute_main_thread(self.on_enable_clicked) def start_animation(self): if not self.active or not self.animate: return - utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled")) + utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled")) time.sleep(0.5) - utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled")) + utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled")) if self.animate and self.active: time.sleep(0.5) if self.animate and self.active: @@ -420,9 +673,9 @@ class TrayIcon: def stop_animation(self): self.animate = False if self.active: - utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-enabled")) + utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-enabled")) else: - utility.execute_main_thread(lambda: self.indicator.set_icon("io.github.slgobinath.SafeEyes-disabled")) + utility.execute_main_thread(lambda: self.sni_service.set_icon("io.github.slgobinath.SafeEyes-disabled")) def init(ctx, safeeyes_cfg, plugin_config): """