Skip to content

Commit 4bef6ac

Browse files
authored
fix: menu config updates not reflecting immediately in UI (#45)
Root cause: tray.set_menu() doesn't reliably update macOS NSStatusBar menu. Similar to previous set_icon() bug fixed in commit 2edacb4. Changes: - TrayManager::update_menu() now rebuilds entire tray instead of using set_menu() - Uses MenuItem::with_id() and CheckMenuItem::with_id() for proper ID assignment - All menu event handlers pass mutable TrayManager reference This ensures menu checkmarks update immediately when users change settings, without requiring app restart. Fixes the issue where clicking menu config items would save to file but not update the visual checkmarks in the menu. Signed-off-by: Marcin Skalski <skalskimarcin33@gmail.com>
1 parent 2505a86 commit 4bef6ac

File tree

2 files changed

+37
-23
lines changed

2 files changed

+37
-23
lines changed

src/main.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ async fn main() -> Result<()> {
223223
// Helper to save config, log, and update menu after config changes
224224
fn save_and_update(
225225
config: &config::Config,
226-
tray_manager: &tray::TrayManager,
226+
tray_manager: &mut tray::TrayManager,
227227
success_msg: &str,
228228
requires_restart: bool,
229229
) -> Result<()> {
@@ -283,15 +283,18 @@ async fn main() -> Result<()> {
283283
tray::TrayCommand::UpdateHotkey { modifiers, key } => {
284284
config.hotkey.modifiers = modifiers;
285285
config.hotkey.key = key;
286-
if let Err(e) = save_and_update(&config, &tray_manager, "Hotkey updated", true)
286+
if let Err(e) =
287+
save_and_update(&config, &mut tray_manager, "Hotkey updated", true)
287288
{
288289
tracing::error!("failed to update config: {:?}", e);
289290
println!("⚠ Failed to save config: {}", e);
290291
}
291292
}
292293
tray::TrayCommand::UpdateModel { model_type } => {
293294
config.model.model_type = model_type;
294-
if let Err(e) = save_and_update(&config, &tray_manager, "Model updated", true) {
295+
if let Err(e) =
296+
save_and_update(&config, &mut tray_manager, "Model updated", true)
297+
{
295298
tracing::error!("failed to update config: {:?}", e);
296299
println!("⚠ Failed to save config: {}", e);
297300
}
@@ -300,7 +303,7 @@ async fn main() -> Result<()> {
300303
config.model.threads = threads;
301304
if let Err(e) = save_and_update(
302305
&config,
303-
&tray_manager,
306+
&mut tray_manager,
304307
&format!("Threads updated to {}", threads),
305308
false,
306309
) {
@@ -312,7 +315,7 @@ async fn main() -> Result<()> {
312315
config.model.beam_size = beam;
313316
if let Err(e) = save_and_update(
314317
&config,
315-
&tray_manager,
318+
&mut tray_manager,
316319
&format!("Beam size updated to {}", beam),
317320
false,
318321
) {
@@ -326,7 +329,7 @@ async fn main() -> Result<()> {
326329
|l| format!("Language updated to {l}"),
327330
);
328331
config.model.language = lang;
329-
if let Err(e) = save_and_update(&config, &tray_manager, &msg, false) {
332+
if let Err(e) = save_and_update(&config, &mut tray_manager, &msg, false) {
330333
tracing::error!("failed to update config: {:?}", e);
331334
println!("⚠ Failed to save config: {}", e);
332335
}
@@ -335,7 +338,7 @@ async fn main() -> Result<()> {
335338
config.audio.buffer_size = size;
336339
if let Err(e) = save_and_update(
337340
&config,
338-
&tray_manager,
341+
&mut tray_manager,
339342
&format!("Buffer size updated to {}", size),
340343
true,
341344
) {
@@ -353,7 +356,7 @@ async fn main() -> Result<()> {
353356
"disabled"
354357
}
355358
);
356-
if let Err(e) = save_and_update(&config, &tray_manager, &msg, true) {
359+
if let Err(e) = save_and_update(&config, &mut tray_manager, &msg, true) {
357360
tracing::error!("failed to update config: {:?}", e);
358361
println!("⚠ Failed to save config: {}", e);
359362
}
@@ -368,7 +371,7 @@ async fn main() -> Result<()> {
368371
"disabled"
369372
}
370373
);
371-
if let Err(e) = save_and_update(&config, &tray_manager, &msg, true) {
374+
if let Err(e) = save_and_update(&config, &mut tray_manager, &msg, true) {
372375
tracing::error!("failed to update config: {:?}", e);
373376
println!("⚠ Failed to save config: {}", e);
374377
}

src/tray.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -366,15 +366,15 @@ impl TrayManager {
366366
let hotkey_submenu = Submenu::new("Hotkey", true);
367367
for hotkey in &menu_config.hotkeys {
368368
let label = Self::format_label_with_checkmark(&hotkey.label, hotkey.selected);
369-
hotkey_submenu.append(&MenuItem::new(&label, true, None))?;
369+
hotkey_submenu.append(&MenuItem::with_id(&hotkey.label, &label, true, None))?;
370370
}
371371
menu.append(&hotkey_submenu)?;
372372

373373
// Models
374374
let model_submenu = Submenu::new("Model", true);
375375
for model in &menu_config.models {
376376
let label = Self::format_label_with_checkmark(&model.name, model.selected);
377-
model_submenu.append(&MenuItem::new(&label, true, None))?;
377+
model_submenu.append(&MenuItem::with_id(&model.name, &label, true, None))?;
378378
}
379379
menu.append(&model_submenu)?;
380380

@@ -385,15 +385,15 @@ impl TrayManager {
385385
let threads_submenu = Submenu::new("Threads", true);
386386
for thread in &menu_config.threads {
387387
let label = Self::format_label_with_checkmark(&thread.label, thread.selected);
388-
threads_submenu.append(&MenuItem::new(&label, true, None))?;
388+
threads_submenu.append(&MenuItem::with_id(&thread.label, &label, true, None))?;
389389
}
390390
opt_submenu.append(&threads_submenu)?;
391391

392392
// Beam sizes
393393
let beam_submenu = Submenu::new("Beam Size", true);
394394
for beam in &menu_config.beam_sizes {
395395
let label = Self::format_label_with_checkmark(&beam.label, beam.selected);
396-
beam_submenu.append(&MenuItem::new(&label, true, None))?;
396+
beam_submenu.append(&MenuItem::with_id(&beam.label, &label, true, None))?;
397397
}
398398
opt_submenu.append(&beam_submenu)?;
399399

@@ -403,48 +403,59 @@ impl TrayManager {
403403
let lang_submenu = Submenu::new("Language", true);
404404
for lang in &menu_config.languages {
405405
let label = Self::format_label_with_checkmark(&lang.display_name, lang.selected);
406-
lang_submenu.append(&MenuItem::new(&label, true, None))?;
406+
lang_submenu.append(&MenuItem::with_id(&lang.display_name, &label, true, None))?;
407407
}
408408
menu.append(&lang_submenu)?;
409409

410410
// Buffer sizes
411411
let buffer_submenu = Submenu::new("Audio Buffer", true);
412412
for buffer in &menu_config.buffer_sizes {
413413
let label = Self::format_label_with_checkmark(&buffer.label, buffer.selected);
414-
buffer_submenu.append(&MenuItem::new(&label, true, None))?;
414+
buffer_submenu.append(&MenuItem::with_id(&buffer.label, &label, true, None))?;
415415
}
416416
menu.append(&buffer_submenu)?;
417417

418418
// Toggles
419419
menu.append(&PredefinedMenuItem::separator())?;
420-
menu.append(&CheckMenuItem::new(
420+
menu.append(&CheckMenuItem::with_id(
421+
"Preload Model",
421422
"Preload Model",
422-
menu_config.preload_enabled,
423423
true,
424+
menu_config.preload_enabled,
424425
None,
425426
))?;
426-
menu.append(&CheckMenuItem::new(
427+
menu.append(&CheckMenuItem::with_id(
428+
"Telemetry",
427429
"Telemetry",
428-
menu_config.telemetry_enabled,
429430
true,
431+
menu_config.telemetry_enabled,
430432
None,
431433
))?;
432434

433435
// Actions
434436
menu.append(&PredefinedMenuItem::separator())?;
435-
menu.append(&MenuItem::new("Open Config File", true, None))?;
437+
menu.append(&MenuItem::with_id(
438+
"Open Config File",
439+
"Open Config File",
440+
true,
441+
None,
442+
))?;
436443
menu.append(&PredefinedMenuItem::quit(None))?;
437444

438445
Ok(menu)
439446
}
440447

441-
pub fn update_menu(&self, config: &Config) -> Result<()> {
448+
pub fn update_menu(&mut self, config: &Config) -> Result<()> {
442449
let current_state = *self
443450
.state
444451
.lock()
445452
.map_err(|e| anyhow!("state lock poisoned: {}", e))?;
446-
let new_menu = Self::build_menu(config, Some(current_state))?;
447-
self.tray.set_menu(Some(Box::new(new_menu)));
453+
454+
// Rebuild entire tray with new menu (workaround for macOS set_menu() bug)
455+
let new_tray = Self::build_tray(config, current_state, &self.cached_icons)?;
456+
self.tray = new_tray;
457+
458+
tracing::debug!("✓ tray menu rebuilt with config changes");
448459
Ok(())
449460
}
450461

0 commit comments

Comments
 (0)