1313
1414from PySide6 .QtCore import QObject , Signal , QThread
1515from PySide6 .QtGui import QIcon
16- from PySide6 .QtWidgets import QApplication , QMenu , QSystemTrayIcon
16+ from PySide6 .QtWidgets import (
17+ QApplication ,
18+ QButtonGroup ,
19+ QCheckBox ,
20+ QMenu ,
21+ QRadioButton ,
22+ QSystemTrayIcon ,
23+ QWidgetAction ,
24+ )
1725
1826from src .audio import AudioRecorder , default_input_device_id , list_devices , resolve_device
1927from src .config import (
@@ -170,29 +178,49 @@ def _add_choice_submenu(
170178 current : str ,
171179 on_select : Any ,
172180 ) -> None :
173- """Add a submenu of checkable actions, one per (key, label).
181+ submenu = QMenu (title , self ._menu )
182+ self ._menu .addMenu (submenu )
183+ group = QButtonGroup (submenu )
184+ group .setExclusive (True )
174185
175- The action matching *current* starts checked. Selecting one calls
176- *on_select(key)*, whose handler rebuilds the menu — that rebuild is what
177- keeps a single item checked, not a QActionGroup.
178- """
179- submenu = self ._menu .addMenu (title )
180186 for key , label in options :
181- act = submenu .addAction (label )
182- act .setCheckable (True )
183- act .setChecked (key == current )
184- act .triggered .connect (lambda checked , k = key : on_select (k ))
187+ radio = QRadioButton (label )
188+ radio .setChecked (key == current )
189+
190+ action = QWidgetAction (submenu )
191+ action .setDefaultWidget (radio )
192+ submenu .addAction (action )
193+
194+ group .addButton (radio )
195+
196+ radio .toggled .connect (
197+ lambda checked , k = key : checked and on_select (k , rebuild_menu = False )
198+ )
199+
200+ def _add_persistent_checkbox (
201+ self ,
202+ label : str ,
203+ checked : bool ,
204+ on_changed : Any ,
205+ ) -> QCheckBox :
206+ # Use QWidgetAction instead of checkable QAction so clicking the control
207+ # does not trigger QMenu's default close-on-action behavior.
208+ checkbox = QCheckBox (label )
209+ checkbox .setChecked (checked )
210+
211+ action = QWidgetAction (self ._menu )
212+ action .setDefaultWidget (checkbox )
213+ self ._menu .addAction (action )
214+
215+ checkbox .toggled .connect (on_changed )
216+ return checkbox
185217
186218 def _rebuild_menu (self ) -> None :
187219 """Rebuild the context menu from current config."""
188220 self ._menu .clear ()
189221 c = self ._config
190222
191- # Enabled toggle.
192- act_enabled = self ._menu .addAction ("Enabled" )
193- act_enabled .setCheckable (True )
194- act_enabled .setChecked (self ._enabled )
195- act_enabled .triggered .connect (self ._toggle_enabled )
223+ self ._add_persistent_checkbox ("Enabled" , self ._enabled , self ._toggle_enabled )
196224
197225 self ._menu .addSeparator ()
198226
@@ -205,12 +233,8 @@ def _rebuild_menu(self) -> None:
205233 self ._add_choice_submenu ("Hotkey" , HOTKEY_OPTIONS , c .hotkey , self ._set_hotkey )
206234 self ._add_choice_submenu ("Post-type Key" , POST_KEY_OPTIONS , c .post_type_key , self ._set_post_key )
207235
208- # AI Rewrite toggle.
209236 self ._menu .addSeparator ()
210- act_rewrite = self ._menu .addAction ("AI Rewrite" )
211- act_rewrite .setCheckable (True )
212- act_rewrite .setChecked (c .llm_enabled )
213- act_rewrite .triggered .connect (self ._toggle_rewrite )
237+ self ._add_persistent_checkbox ("AI Rewrite" , c .llm_enabled , self ._toggle_rewrite )
214238
215239 # Settings / Exit.
216240 self ._menu .addSeparator ()
@@ -355,24 +379,27 @@ def _toggle_enabled(self, checked: bool) -> None:
355379 self ._finalize_recording ()
356380 log .info ("Screamer %s" , "enabled" if checked else "disabled" )
357381
358- def _set_recording_mode (self , mode : str ) -> None :
382+ def _set_recording_mode (self , mode : str , rebuild_menu : bool = True ) -> None :
359383 self ._config .recording_mode = mode
360384 save_config (self ._config )
361385 self ._restart_hotkey ()
362- self ._rebuild_menu ()
386+ if rebuild_menu :
387+ self ._rebuild_menu ()
363388 log .info ("Recording mode set to %s" , mode )
364389
365- def _set_hotkey (self , key : str ) -> None :
390+ def _set_hotkey (self , key : str , rebuild_menu : bool = True ) -> None :
366391 self ._config .hotkey = key
367392 save_config (self ._config )
368393 self ._restart_hotkey ()
369- self ._rebuild_menu ()
394+ if rebuild_menu :
395+ self ._rebuild_menu ()
370396 log .info ("Hotkey set to %s" , key )
371397
372- def _set_post_key (self , key : str ) -> None :
398+ def _set_post_key (self , key : str , rebuild_menu : bool = True ) -> None :
373399 self ._config .post_type_key = key
374400 save_config (self ._config )
375- self ._rebuild_menu ()
401+ if rebuild_menu :
402+ self ._rebuild_menu ()
376403 log .info ("Post-type key set to %s" , key )
377404
378405 def _toggle_rewrite (self , checked : bool ) -> None :
0 commit comments