From 6a6cc939191fcc6e5b74bdfd8e9fbe87b230b1e9 Mon Sep 17 00:00:00 2001 From: "devin-ai-integration[bot]" <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 7 Oct 2026 17:12:01 -0700 Subject: [PATCH] feat(tray): remove Start on Login and About from the tray menu (#905) * feat(tray): remove Start on Login and About from the tray menu Both entries already live in Settings: the autostart switch is in the general page and About is a dedicated sidebar page, so the tray items are redundant. View Logs stays. Removes the Start on Login checkbox item along with its _update_autostart_checkbox/_on_autostart_toggled wiring and the now unused _add_menu_checkbox helper, plus the About item and _on_about_clicked. _show_settings_page("about") stays for the Update Available item. autostart_manager is untouched since Settings uses it. Fixes #654 * fix(settings): sync autostart switch with the real desktop entry The tray's removed _update_autostart_checkbox also reconciled general.autostart with the actual login desktop entry at startup. Move that check into the settings load so the Start on Login switch still reflects reality when the entry changes outside the app. --------- Co-authored-by: Jatin K Malik --- src/vocalinux/ui/settings_dialog.py | 11 ++++- src/vocalinux/ui/tray_indicator.py | 63 ----------------------------- tests/test_tray_indicator.py | 18 ++++----- tests/test_tray_indicator_ext.py | 1 - 4 files changed, 19 insertions(+), 74 deletions(-) diff --git a/src/vocalinux/ui/settings_dialog.py b/src/vocalinux/ui/settings_dialog.py index 093e0c2..eb744a5 100644 --- a/src/vocalinux/ui/settings_dialog.py +++ b/src/vocalinux/ui/settings_dialog.py @@ -7320,7 +7320,16 @@ class SettingsDialog(Gtk.Dialog): text_injection_settings = self.config_manager.get_settings().get("text_injection", {}) history_settings = self.config_manager.get_settings().get("history", {}) - autostart_enabled = general_settings.get("autostart", False) + # The login desktop entry can be added or removed outside the app; the + # real file state wins over the stored preference (previously synced by + # the tray menu's Start on Login item). + from . import autostart_manager + + autostart_enabled = autostart_manager.is_autostart_enabled() + if autostart_enabled != general_settings.get("autostart", False): + self.config_manager.set("general", "autostart", autostart_enabled) + self.config_manager.save_settings() + start_minimized = ui_settings.get("start_minimized", False) show_missing_tray_warning = ui_settings.get("show_missing_tray_warning", True) show_overlay = ui_settings.get("show_overlay", True) diff --git a/src/vocalinux/ui/tray_indicator.py b/src/vocalinux/ui/tray_indicator.py index a5abe1e..3bf517d 100644 --- a/src/vocalinux/ui/tray_indicator.py +++ b/src/vocalinux/ui/tray_indicator.py @@ -179,7 +179,6 @@ class TrayIndicator: # settings toggle, resume) must keep honoring the fallback rather # than reapplying a disable_internal_hotkey setting D-Bus cannot serve. self._external_activation_unavailable = False - self._syncing_autostart_menu = False self._history_menu_item = None # Refresh the history submenu whenever the history changes. The change @@ -584,12 +583,6 @@ class TrayIndicator: self._refresh_history_menu() self._add_menu_separator() - self._autostart_menu_item = self._add_menu_checkbox( - "Start on Login", self._on_autostart_toggled - ) - self._update_autostart_checkbox() - - self._add_menu_separator() if self.dictation_pad is not None: self._add_menu_item("Dictation Pad", self._on_dictation_pad_clicked) self._add_menu_item("Settings", self._on_settings_clicked) @@ -606,7 +599,6 @@ class TrayIndicator: ) self._update_menu_item.set_no_show_all(True) self._update_menu_item.hide() - self._add_menu_item("About", self._on_about_clicked) self._add_menu_item("Quit", self._on_quit_clicked) # Set the indicator menu @@ -827,56 +819,6 @@ class TrayIndicator: separator = Gtk.SeparatorMenuItem() self.menu.append(separator) - def _add_menu_checkbox(self, label: str, callback: Callable) -> Gtk.CheckMenuItem: - """ - Add a checkbox menu item to the indicator menu. - - Args: - label: The label for the menu item - callback: The callback function to call when the item is toggled - - Returns: - The checkbox menu item - """ - item = Gtk.CheckMenuItem.new_with_label(label) - item.connect("toggled", callback) - self.menu.append(item) - return item - - def _update_autostart_checkbox(self): - """Update the autostart checkbox state based on current config.""" - from . import autostart_manager - - autostart_enabled = autostart_manager.is_autostart_enabled() - config_enabled = self.config_manager.get_bool("general", "autostart", False) - if config_enabled != autostart_enabled: - self.config_manager.set("general", "autostart", autostart_enabled) - self.config_manager.save_settings() - - self._syncing_autostart_menu = True - self._autostart_menu_item.set_active(autostart_enabled) - self._syncing_autostart_menu = False - - def _on_autostart_toggled(self, widget): - """Handle toggle of the Start on Login menu item.""" - if self._syncing_autostart_menu: - return - - enabled = widget.get_active() - logger.info(f"Autostart toggled: {enabled}") - - from . import autostart_manager - - if autostart_manager.set_autostart(enabled): - self.config_manager.set("general", "autostart", enabled) - self.config_manager.save_settings() - status = "enabled" if enabled else "disabled" - logger.info(f"Autostart {status}") - else: - self._syncing_autostart_menu = True - widget.set_active(not enabled) - self._syncing_autostart_menu = False - def _on_recognition_state_changed(self, state: RecognitionState): """ Handle changes in the speech recognition state. @@ -1552,11 +1494,6 @@ class TrayIndicator: logger.debug("No changes needed - shortcut and mode unchanged") return True - def _on_about_clicked(self, widget): - """Handle click on the About menu item.""" - logger.debug("About clicked") - self._show_settings_page("about") - def _get_auto_pause_config(self): """Return (enabled, apps, poll_interval_seconds) for AutoPauseMonitor.""" enabled = self.config_manager.get_bool("auto_pause", "enabled", False) diff --git a/tests/test_tray_indicator.py b/tests/test_tray_indicator.py index 8c9a032..5104364 100644 --- a/tests/test_tray_indicator.py +++ b/tests/test_tray_indicator.py @@ -280,8 +280,8 @@ class TestTrayIndicator(unittest.TestCase): mock_dialog_instance.present_with_time.assert_called_once() mock_dialog_instance.navigate_to_page.assert_not_called() - def test_about_reuses_open_settings_dialog(self): - """About focuses the existing Settings window and switches to the About page.""" + def test_update_available_reuses_open_settings_dialog(self): + """Update Available focuses the existing Settings window on the About page.""" import vocalinux.ui.tray_indicator as tray_module mock_dialog_instance = MagicMock() @@ -289,7 +289,7 @@ class TestTrayIndicator(unittest.TestCase): with patch.object(tray_module, "SettingsDialog", mock_dialog_class): self.tray_indicator._on_settings_clicked(None) - self.tray_indicator._on_about_clicked(None) + self.tray_indicator._on_update_available_clicked(None) mock_dialog_class.assert_called_once() mock_dialog_instance.navigate_to_page.assert_called_once_with("about") @@ -312,13 +312,13 @@ class TestTrayIndicator(unittest.TestCase): second_dialog.show.assert_called_once() first_dialog.present_with_time.assert_not_called() - def test_about_dialog(self): - """Test About opens Settings focused on the About page.""" + def test_update_available_opens_about_page(self): + """Test Update Available opens Settings focused on the About page.""" with patch("vocalinux.ui.tray_indicator.SettingsDialog") as mock_dialog_class: mock_dialog_instance = MagicMock() mock_dialog_class.return_value = mock_dialog_instance - self.tray_indicator._on_about_clicked(None) + self.tray_indicator._on_update_available_clicked(None) mock_dialog_class.assert_called_once() kwargs = mock_dialog_class.call_args.kwargs @@ -661,13 +661,13 @@ class TestTrayIndicator(unittest.TestCase): self.tray_indicator.run() mock_quit.assert_called_once() - def test_about_opens_settings_without_raising(self): - """Test About menu item opens settings even if dialog construction is mocked.""" + def test_update_available_opens_settings_without_raising(self): + """Test Update Available opens settings even if dialog construction is mocked.""" with patch("vocalinux.ui.tray_indicator.SettingsDialog") as mock_dialog_class: mock_dialog_instance = MagicMock() mock_dialog_class.return_value = mock_dialog_instance - self.tray_indicator._on_about_clicked(None) + self.tray_indicator._on_update_available_clicked(None) mock_dialog_instance.connect.assert_called() mock_dialog_instance.show.assert_called_once() diff --git a/tests/test_tray_indicator_ext.py b/tests/test_tray_indicator_ext.py index 41c6f02..f91ed04 100644 --- a/tests/test_tray_indicator_ext.py +++ b/tests/test_tray_indicator_ext.py @@ -112,7 +112,6 @@ class TestTrayIndicatorInitialization(unittest.TestCase): assert indicator.text_injector == mock_text_injector assert indicator.config_manager == mock_config_inst assert indicator.shortcut_manager == mock_keyboard_inst - assert indicator._syncing_autostart_menu is False # Verify that idle_add was called to initialize the indicator mock_glib.idle_add.assert_called()