A menu bar / tray app for rig on Windows #355
gaborcsardi
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Rig.appgives macOS users a menu bar item that shows the current default R version and switches it in one click. Windows has nothing equivalent. This is a write-up of what it would take, including the two things that do not translate cleanly.The backend is already there
The macOS app is ~680 lines of Swift under
Rig.app/Rig/, talking to thelibriglib.astatic library through 11extern "C"functions insrc/lib.rs. That file is gated#![cfg(target_os = "macos")]at line 1.Every function behind that C API already has a Windows counterpart:
src/lib.rsC APIrig_list,rig_list_with_versionssc_get_listinsrc/windows/mod.rsrig_get_defaultsc_get_defaultinsrc/windows/mod.rsrig_set_defaultsc_set_defaultinsrc/windows/mod.rsrig_r_rootget_r_rootinsrc/windows/mod.rsrig_start_rstudiosc_rstudio_insrc/windows/mod.rsrig_library_list,rig_lib_set_defaultsrc/library.rs, already cross-platformrig_modeget_modeinsrc/utils.rsSo no new backend logic is needed, only a front end.
Suggested approach: a second Rust binary, no C API
On macOS the split into a static library plus a Swift app exists because the UI has to be Swift. On Windows there is no such constraint, so the C API is pure overhead. A tray app can just be another binary in the same crate that calls
windows::sc_get_list()and friends directly — no FFI, nochar*buffers, no second toolchain.tray-iconcrate (from the Tauri project, wrapsShell_NotifyIcon), withtaoorwinitfor the event loop.#![windows_subsystem = "windows"]so no console window appears.notifycrate, which usesReadDirectoryChangesW. This replacesDirectoryWatcher.swift(kqueue) and would watchget_r_root(), exactly likeupdateWatcher()does today.src/config.rsinstead of theUserDefaultsstorage thatRigPreferences.swiftuses.Roughly 400–600 lines of Rust and three new dependencies. Notably it needs no Xcode equivalent — everything builds with the existing
cargo buildon Windows.Constraint 1: the Windows notification area has no text label
The macOS status item title is currently something like
R 4.5.1 (mylib). There is no way to reproduce that on Windows, and it is not a Rust limitation.Shell_NotifyIconis the only supported systray API, andNOTIFYICONDATAhas exactly two display fields:hIconandszTip(the tooltip). There is no label field. The taskbar is rendered byexplorer.exe, not by the application, so WinFormsNotifyIcon, WPF, WinUI 3 and plain Win32 all wrap the same call and hit the same wall.NSStatusItemis different because it hands you a realNSButtonthat you own and draw into.The historical escape hatches are gone:
IDeskBand2) were the only supported way to put arbitrary text in the taskbar. Deprecated in Windows 10, non-functional in Windows 11.What does work: generating the icon at runtime. Draw the version into a bitmap and build the
HICONon the fly rather than loading a static.ico, redrawing whenever the default changes. This is what CPU and battery monitors do. The budget is small — 16×16 logical, up to 32×32 at 200% DPI — so realistically two or three glyphs:4.5fits, either condensed or stacked on two lines4.5.1does not fit4.5.1 (mylib)is nowhere closeSo the proposal is: icon shows the abbreviated version, the tooltip carries the full
R 4.5.1 (mylib)string, and the full version plus library name stay in the menu, where they are already listed. This can be done in Rust with GDI via thewindowscrate (CreateDIBSection,DrawText,CreateIconIndirect), or by drawing withtiny-skia/ab_glyph;tray-iconaccepts an icon built from a raw RGBA buffer either way.Also worth knowing: Windows 11 hides new tray icons in the overflow flyout by default, and the user has to drag one onto the taskbar to pin it. The installer or a first-run notice should probably mention this.
Constraint 2: elevation in admin mode
This is the bigger problem.
sc_set_defaultinsrc/windows/mod.rscallsescalate("setting the default R version"). In admin mode it writesR.bat,RS.batandRscript.batintoC:\Program Files\R\binand updates HKLM, so switching versions means a UAC prompt every single time the user picks a version from the tray menu. That defeats the purpose of a one-click switcher.User mode has none of this: quick links go to
%USERPROFILE%\.local\bin, registry writes go to HKCU, andescalateis a no-op.Options:
get_mode()returnsAdmin, show the menu disabled with a pointer torig system user-mode. This seems like the right call — it is honest about the constraint and nudges toward the mode that actually works well here.Option 1 is the recommendation.
Packaging
rig.iss(Inno Setup) would need to installrig-tray.exe, add a Start Menu shortcut, and optionally register autostart underHKCU\Software\Microsoft\Windows\CurrentVersion\Run. An.icois also needed —Resources/currently only holds macOS assets.Side benefit
Because
tray-iconis cross-platform, the same code could eventually replace the Swift app on macOS and Linux and leave rig with a single UI codebase. The cost of doing that on macOS would be losing the text label in the menu bar, so it is probably not worth it there — but it is an option worth keeping in mind rather than a door this closes.All reactions