9134d3bab0
- 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>
64 lines
2.2 KiB
Rust
64 lines
2.2 KiB
Rust
use iced::futures::channel::oneshot;
|
|
|
|
/// Run a blocking closure on a thread pool thread and await its result.
|
|
/// Used to offload blocking work (HTTP, disk, process spawning) without
|
|
/// stalling the iced event loop.
|
|
pub async fn async_blocking<T, F>(f: F) -> T
|
|
where
|
|
T: Send + 'static,
|
|
F: FnOnce() -> T + Send + 'static,
|
|
{
|
|
let (tx, rx) = oneshot::channel();
|
|
std::thread::spawn(move || {
|
|
let _ = tx.send(f());
|
|
});
|
|
rx.await.expect("blocking task panicked")
|
|
}
|
|
|
|
/// Open a native folder picker dialog and return the chosen path, or None if
|
|
/// the user cancelled. Tries zenity (GNOME/GTK) then kdialog (KDE) in order.
|
|
pub fn pick_folder(title: &str) -> Option<String> {
|
|
for (cmd, args) in [
|
|
("zenity", vec!["--file-selection", "--directory", "--title", title]),
|
|
("kdialog", vec!["--getexistingdirectory", "/home", "--title", title]),
|
|
] {
|
|
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
|
|
}
|
|
|
|
/// 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
|
|
}
|