feat(gui): auto-detect games in Wine prefix and browse for exe

- detect::scan_games_in_prefix() walks drive_c/Program Files and
  Program Files (x86) up to depth 4, skipping Windows system dirs,
  the launcher's own exe, and already-configured games
- Empty game list now shows "No games added yet." with two actions:
    · Scan for games — async scan of the Wine prefix, results appear
      as a clickable list with + buttons to add each found exe
    · Browse exe… — native file picker (zenity/kdialog) opening in
      drive_c/, computes the relative path automatically
- Add-game form gains a Browse… button to pick exe from the prefix
- util::pick_file() added alongside pick_folder()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-04-18 19:50:48 -07:00
parent 74f21b6b75
commit 9134d3bab0
3 changed files with 296 additions and 5 deletions
+27
View File
@@ -34,3 +34,30 @@ pub fn pick_folder(title: &str) -> Option<String> {
}
None
}
/// Open a native file picker dialog starting in `start_dir`, or None if
/// the user cancelled. Tries zenity then kdialog.
pub fn pick_file(title: &str, start_dir: &str) -> Option<String> {
// zenity uses --filename with a trailing slash to open a directory
let start_slash = format!("{}/", start_dir.trim_end_matches('/'));
let zenity_args = vec![
"--file-selection",
"--title",
title,
"--filename",
&start_slash,
];
let kdialog_args = vec!["--getopenfilename", start_dir, "--title", title];
for (cmd, args) in [("zenity", zenity_args.as_slice()), ("kdialog", kdialog_args.as_slice())] {
let Ok(out) = std::process::Command::new(cmd).args(args).output() else {
continue;
};
if out.status.success() {
let s = String::from_utf8_lossy(&out.stdout).trim().to_string();
if !s.is_empty() {
return Some(s);
}
}
}
None
}