Compare commits

..

2 Commits

Author SHA1 Message Date
funman300 d4f0515a82 refactor(setup): clean up setup wizard UX
- Hide raw filesystem paths from all status messages; use launcher
  display names instead (e.g. "Downloading Battle.net installer…")
- Simplify installer source label: show "✓ Official installer detected"
  badge when URL is auto-filled
- Replace separate "Download / Prepare" + "Run installer" buttons with
  a single context-aware action button whose label tracks the stage:
  Download → / Downloading… / Install → / Installing…
- Remove auto-download on launcher confirm; show confirmation prompt
  "Ready to download the official X installer. Press Download → to begin."
- Soften the exe-not-found error message; remove raw path and config
  editing advice — direct users to the log instead
- Rename "Launch now" → "Open launcher" for clarity
- Show "✓ X installed successfully!" success banner above Close/Open
  buttons when install completes and the exe is present

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 23:06:32 -07:00
funman300 156bb460a0 feat(gui): replace settings ⚙ placeholder with Bootstrap cog icon
Add iced_aw + iced_fonts (bootstrap feature) to load the Bootstrap Icons
font. The settings button and settings header now render the gear-fill
glyph (U+F3F8) via text("\u{F3F8}").font("bootstrap-icons") instead of
the raw ⚙ character which was not in iced's bundled Inter font.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 23:06:22 -07:00
4 changed files with 403 additions and 73 deletions
Generated
+313 -25
View File
@@ -753,6 +753,15 @@ dependencies = [
"libc",
]
[[package]]
name = "core_maths"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30"
dependencies = [
"libm",
]
[[package]]
name = "cosmic-text"
version = "0.12.1"
@@ -760,14 +769,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2"
dependencies = [
"bitflags 2.11.1",
"fontdb",
"fontdb 0.16.2",
"log",
"rangemap",
"rayon",
"rustc-hash 1.1.0",
"rustybuzz",
"self_cell",
"swash",
"swash 0.1.19",
"sys-locale",
"ttf-parser 0.21.1",
"unicode-bidi",
@@ -776,6 +785,30 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "cosmic-text"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "173852283a9a57a3cbe365d86e74dc428a09c50421477d5ad6fe9d9509e37737"
dependencies = [
"bitflags 2.11.1",
"fontdb 0.23.0",
"harfrust",
"linebender_resource_handle",
"log",
"rangemap",
"rustc-hash 1.1.0",
"self_cell",
"skrifa 0.37.0",
"smol_str",
"swash 0.2.7",
"sys-locale",
"unicode-bidi",
"unicode-linebreak",
"unicode-script",
"unicode-segmentation",
]
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -1233,6 +1266,24 @@ dependencies = [
"bytemuck",
]
[[package]]
name = "font-types"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5"
dependencies = [
"bytemuck",
]
[[package]]
name = "font-types"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b38ad915f6dadd993ced50848a8291a543bd41ca62bc10740d5e64e2ab4cfd7"
dependencies = [
"bytemuck",
]
[[package]]
name = "fontconfig-parser"
version = "0.5.8"
@@ -1256,6 +1307,20 @@ dependencies = [
"ttf-parser 0.20.0",
]
[[package]]
name = "fontdb"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905"
dependencies = [
"fontconfig-parser",
"log",
"memmap2",
"slotmap",
"tinyvec",
"ttf-parser 0.25.1",
]
[[package]]
name = "foreign-types"
version = "0.3.2"
@@ -1594,6 +1659,19 @@ dependencies = [
"zerocopy",
]
[[package]]
name = "harfrust"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92c020db12c71d8a12a3fe7607873cade3a01a6287e29d540c8723276221b9d8"
dependencies = [
"bitflags 2.11.1",
"bytemuck",
"core_maths",
"read-fonts 0.35.0",
"smallvec",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
@@ -1798,14 +1876,27 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88acfabc84ec077eaf9ede3457ffa3a104626d79022a9bf7f296093b1d60c73f"
dependencies = [
"iced_core",
"iced_futures",
"iced_renderer",
"iced_widget",
"iced_core 0.13.2",
"iced_futures 0.13.2",
"iced_renderer 0.13.0",
"iced_widget 0.13.4",
"iced_winit",
"thiserror 1.0.69",
]
[[package]]
name = "iced_aw"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e676b8d322a419c7eef29cac1aa78e8afda6a9d2d4597483d42de3e90378fe2"
dependencies = [
"cfg-if",
"iced_core 0.14.0",
"iced_fonts",
"iced_widget 0.14.2",
"web-time",
]
[[package]]
name = "iced_core"
version = "0.13.2"
@@ -1826,6 +1917,47 @@ dependencies = [
"web-time",
]
[[package]]
name = "iced_core"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ab1937d699403e7e69252ae743a902bcee9f4ab2052cc4c9a46fcf34729d85"
dependencies = [
"bitflags 2.11.1",
"bytes",
"glam",
"lilt",
"log",
"num-traits",
"rustc-hash 2.1.2",
"smol_str",
"thiserror 2.0.18",
"web-time",
]
[[package]]
name = "iced_fonts"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "214cff7c8499e328774216690e58e315a1a5f8f6fdd1035aed6298e62ffc4c1d"
dependencies = [
"iced_core 0.14.0",
"iced_fonts_macros",
"iced_widget 0.14.2",
]
[[package]]
name = "iced_fonts_macros"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef5125e110cb19cd1910a28298661c98c5d9ab02eef43594968352940e8752e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.117",
"ttf-parser 0.25.1",
]
[[package]]
name = "iced_futures"
version = "0.13.2"
@@ -1833,7 +1965,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c04a6745ba2e80f32cf01e034fd00d853aa4f4cd8b91888099cb7aaee0d5d7c"
dependencies = [
"futures",
"iced_core",
"iced_core 0.13.2",
"log",
"rustc-hash 2.1.2",
"tokio",
@@ -1841,13 +1973,27 @@ dependencies = [
"wasm-timer",
]
[[package]]
name = "iced_futures"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c0c85ccad42dfbec7293c36c018af0ea0dbcc52d137a4a9a0b0f6822a3fdf0a"
dependencies = [
"futures",
"iced_core 0.14.0",
"log",
"rustc-hash 2.1.2",
"wasm-bindgen-futures",
"wasmtimer",
]
[[package]]
name = "iced_glyphon"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41c3bb56f1820ca252bc1d0994ece33d233a55657c0c263ea7cb16895adbde82"
dependencies = [
"cosmic-text",
"cosmic-text 0.12.1",
"etagere",
"lru",
"rustc-hash 2.1.2",
@@ -1862,10 +2008,10 @@ checksum = "ba25a18cfa6d5cc160aca7e1b34f73ccdff21680fa8702168c09739767b6c66f"
dependencies = [
"bitflags 2.11.1",
"bytemuck",
"cosmic-text",
"cosmic-text 0.12.1",
"half",
"iced_core",
"iced_futures",
"iced_core 0.13.2",
"iced_futures 0.13.2",
"log",
"once_cell",
"raw-window-handle",
@@ -1874,19 +2020,49 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "iced_graphics"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234ca1c2cec4155055f68fa5fad1b5242c496ac8238d80a259bca382fb44a102"
dependencies = [
"bitflags 2.11.1",
"bytemuck",
"cosmic-text 0.15.0",
"half",
"iced_core 0.14.0",
"iced_futures 0.14.0",
"log",
"raw-window-handle",
"rustc-hash 2.1.2",
"thiserror 2.0.18",
"unicode-segmentation",
]
[[package]]
name = "iced_renderer"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73558208059f9e622df2bf434e044ee2f838ce75201a023cf0ca3e1244f46c2a"
dependencies = [
"iced_graphics",
"iced_graphics 0.13.0",
"iced_tiny_skia",
"iced_wgpu",
"log",
"thiserror 1.0.69",
]
[[package]]
name = "iced_renderer"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "250cc0802408e8c077986ec56c7d07c65f423ee658a4b9fd795a1f2aae5dac05"
dependencies = [
"iced_graphics 0.14.0",
"log",
"thiserror 2.0.18",
]
[[package]]
name = "iced_runtime"
version = "0.13.2"
@@ -1894,8 +2070,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "348b5b2c61c934d88ca3b0ed1ed913291e923d086a66fa288ce9669da9ef62b5"
dependencies = [
"bytes",
"iced_core",
"iced_futures",
"iced_core 0.13.2",
"iced_futures 0.13.2",
"raw-window-handle",
"thiserror 1.0.69",
]
@@ -1907,8 +2083,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c625d368284fcc43b0b36b176f76eff1abebe7959dd58bd8ce6897d641962a50"
dependencies = [
"bytemuck",
"cosmic-text",
"iced_graphics",
"cosmic-text 0.12.1",
"iced_graphics 0.13.0",
"kurbo",
"log",
"rustc-hash 2.1.2",
@@ -1928,7 +2104,7 @@ dependencies = [
"glam",
"guillotiere",
"iced_glyphon",
"iced_graphics",
"iced_graphics 0.13.0",
"log",
"once_cell",
"rustc-hash 2.1.2",
@@ -1942,7 +2118,7 @@ version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81429e1b950b0e4bca65be4c4278fea6678ea782030a411778f26fa9f8983e1d"
dependencies = [
"iced_renderer",
"iced_renderer 0.13.0",
"iced_runtime",
"num-traits",
"once_cell",
@@ -1951,14 +2127,28 @@ dependencies = [
"unicode-segmentation",
]
[[package]]
name = "iced_widget"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1596afa0d3109c2618e8bc12bae6c11d3064df8f95c42dfce570397dbe957ab"
dependencies = [
"iced_renderer 0.14.0",
"log",
"num-traits",
"rustc-hash 2.1.2",
"thiserror 2.0.18",
"unicode-segmentation",
]
[[package]]
name = "iced_winit"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f44cd4e1c594b6334f409282937bf972ba14d31fedf03c23aa595d982a2fda28"
dependencies = [
"iced_futures",
"iced_graphics",
"iced_futures 0.13.2",
"iced_graphics 0.13.0",
"iced_runtime",
"log",
"rustc-hash 2.1.2",
@@ -2307,6 +2497,21 @@ dependencies = [
"redox_syscall 0.7.4",
]
[[package]]
name = "lilt"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67562e5eff6b20553fa9be1c503356768420994e28f67e3eafe6f41910e57ad"
dependencies = [
"web-time",
]
[[package]]
name = "linebender_resource_handle"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4"
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
@@ -3320,7 +3525,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f"
dependencies = [
"bytemuck",
"font-types",
"font-types 0.7.3",
]
[[package]]
name = "read-fonts"
version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358"
dependencies = [
"bytemuck",
"core_maths",
"font-types 0.10.1",
]
[[package]]
name = "read-fonts"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5"
dependencies = [
"bytemuck",
"font-types 0.11.3",
]
[[package]]
@@ -3772,7 +3998,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e1c44ad1f6c5bdd4eefed8326711b7dbda9ea45dfd36068c427d332aa382cbe"
dependencies = [
"bytemuck",
"read-fonts",
"read-fonts 0.22.7",
]
[[package]]
name = "skrifa"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841"
dependencies = [
"bytemuck",
"read-fonts 0.35.0",
]
[[package]]
name = "skrifa"
version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac"
dependencies = [
"bytemuck",
"read-fonts 0.37.0",
]
[[package]]
@@ -3967,9 +4213,20 @@ version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cbd59f3f359ddd2c95af4758c18270eddd9c730dde98598023cdabff472c2ca2"
dependencies = [
"skrifa",
"yazi",
"zeno",
"skrifa 0.22.3",
"yazi 0.1.6",
"zeno 0.2.3",
]
[[package]]
name = "swash"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "842f3cd369c2ba38966204f983eaa5e54a8e84a7d7159ed36ade2b6c335aae64"
dependencies = [
"skrifa 0.40.0",
"yazi 0.2.1",
"zeno 0.3.3",
]
[[package]]
@@ -4397,6 +4654,9 @@ name = "ttf-parser"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
dependencies = [
"core_maths",
]
[[package]]
name = "typenum"
@@ -4424,6 +4684,8 @@ dependencies = [
"directories",
"dirs 5.0.1",
"iced",
"iced_aw",
"iced_fonts",
"ksni",
"owo-colors",
"reqwest",
@@ -4687,6 +4949,20 @@ dependencies = [
"semver",
]
[[package]]
name = "wasmtimer"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b"
dependencies = [
"futures",
"js-sys",
"parking_lot 0.12.5",
"pin-utils",
"slab",
"wasm-bindgen",
]
[[package]]
name = "wayland-backend"
version = "0.3.15"
@@ -5463,6 +5739,12 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1"
[[package]]
name = "yazi"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5"
[[package]]
name = "yoke"
version = "0.8.2"
@@ -5554,6 +5836,12 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
[[package]]
name = "zeno"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524"
[[package]]
name = "zerocopy"
version = "0.8.48"
+2
View File
@@ -44,3 +44,5 @@ reqwest = { version = "0.12", features = ["blocking", "json"] }
# GUI for the setup wizard
iced = { version = "0.13", features = ["tokio"] }
iced_aw = { version = "0.13", default-features = false }
iced_fonts = { version = "0.3", features = ["bootstrap"] }
+3 -2
View File
@@ -611,7 +611,7 @@ fn view(state: &Dashboard) -> Element<'_, Message> {
return view_settings(state);
}
let settings_btn = button(text("").size(16))
let settings_btn = button(text("\u{F3F8}").font(iced::Font::with_name("bootstrap-icons")).size(16))
.on_press(Message::ShowSettings)
.style(button::secondary);
@@ -1086,7 +1086,7 @@ fn diagnose_card<'a>(
fn view_settings(state: &Dashboard) -> Element<'_, Message> {
let header = row![
text(" Settings").size(24),
row![text("\u{F3F8}").font(iced::Font::with_name("bootstrap-icons")).size(20), text(" Settings").size(24)].align_y(Alignment::Center),
iced::widget::horizontal_space(),
button(text("← Back").size(13))
.on_press(Message::HideSettings)
@@ -1167,6 +1167,7 @@ pub fn run(config: &Config) -> Result<()> {
iced::application(|_: &Dashboard| String::from("umutray"), update, view)
.subscription(subscription)
.theme(|_| Theme::Dark)
.font(iced_aw::iced_fonts::BOOTSTRAP_FONT_BYTES)
.window(iced::window::Settings {
size: iced::Size::new(600.0, 560.0),
..Default::default()
+85 -46
View File
@@ -3,7 +3,7 @@ use anyhow::Result;
use iced::widget::{
button, column, container, pick_list, progress_bar, row, scrollable, text, text_input, Column,
};
use iced::{Element, Length, Subscription, Task, Theme};
use iced::{Color, Element, Length, Subscription, Task, Theme};
use std::ffi::OsString;
use std::io::{BufRead, BufReader, Read, Write};
use std::path::{Path, PathBuf};
@@ -99,8 +99,8 @@ impl State {
fn new_install(config: Config, launcher: Launcher) -> Self {
let status = format!(
"Paste an installer URL or a local .exe path. It will install into {}.",
launcher.prefix_dir.display()
"Paste an installer URL or a local .exe path for {}.",
launcher.display
);
let template_options = config::presets()
.iter()
@@ -171,30 +171,21 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
preset.prefix_dir = PathBuf::from(&prefix);
// If we know the official installer URL, pre-fill source and
// kick off the download automatically so the user doesn't have
// to find or paste anything.
// let the user confirm before downloading.
if let Some(url) = preset.installer_url.clone() {
state.source = url.clone();
state.stage = Stage::Busy;
state.source = url;
state.stage = Stage::Idle;
state.status = format!(
"Found official installer — downloading to {}",
preset.prefix_dir.display()
"Ready to download the official {} installer. Press Download to begin.",
preset.display
);
if let Ok(mut p) = state.download.lock() {
*p = DownloadProgress::default();
}
let name = preset.name.clone();
let progress = state.download.clone();
state.launcher = Some(preset);
return Task::perform(
async_blocking(move || download_blocking(&url, &name, progress)),
Message::PrepareDone,
);
return Task::none();
}
state.status = format!(
"Paste an installer URL or a local .exe path. It will install into {}.",
preset.prefix_dir.display()
"Paste an installer URL or a local .exe path for {}.",
preset.display
);
state.launcher = Some(preset);
state.stage = Stage::Idle;
@@ -228,7 +219,12 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
if path.is_file() {
state.installer = Some(path.clone());
state.stage = Stage::Ready;
state.status = format!("Ready: {}", path.display());
let display = state
.launcher
.as_ref()
.map(|l| l.display.as_str())
.unwrap_or("installer");
state.status = format!("Local installer ready for {}. Press Install → to continue.", display);
return Task::none();
}
if !src.starts_with("http://") && !src.starts_with("https://") {
@@ -236,7 +232,12 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
return Task::none();
}
state.stage = Stage::Busy;
state.status = format!("Downloading {src}");
let display_name = state
.launcher
.as_ref()
.map(|l| l.display.clone())
.unwrap_or_else(|| "installer".to_string());
state.status = format!("Downloading {} installer…", display_name);
if let Ok(mut p) = state.download.lock() {
*p = DownloadProgress::default();
}
@@ -256,7 +257,12 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
state.downloaded_temp = Some(path.clone());
state.installer = Some(path.clone());
state.stage = Stage::Ready;
state.status = format!("Downloaded to {}", path.display());
let display_name = state
.launcher
.as_ref()
.map(|l| l.display.as_str())
.unwrap_or("installer");
state.status = format!("Download complete. Press Install → to run the {} installer.", display_name);
Task::none()
}
Message::PrepareDone(Err(e)) => {
@@ -309,11 +315,9 @@ fn update(state: &mut State, message: Message) -> Task<Message> {
"✓ Installer finished (exit {code}). {} is ready.",
launcher.display,
),
Ok(code) => format!(
"umu-run exited {code} but the expected exe is not at {}.\n\
Check the installer's destination path, or edit exe_path in config.",
exe.display(),
),
Ok(_) => "Installation finished but the launcher wasn't found where expected. \
Check the log below for details."
.to_string(),
Err(e) => format!("Install failed: {e}"),
};
Task::none()
@@ -430,7 +434,7 @@ fn view_install(state: &State) -> Element<'_, Message> {
let source_label = if launcher.installer_url.is_some()
&& state.source == launcher.installer_url.as_deref().unwrap_or("")
{
"Official installer URL (auto-filled — or paste your own):"
"Official installer detected — or paste a custom URL:"
} else {
"Installer URL or local .exe path:"
};
@@ -439,18 +443,37 @@ fn view_install(state: &State) -> Element<'_, Message> {
.on_input(Message::SourceChanged)
.padding(8);
let prepare_enabled = matches!(state.stage, Stage::Idle | Stage::Ready | Stage::Finished);
let install_enabled = matches!(state.stage, Stage::Ready);
let finished = matches!(state.stage, Stage::Finished);
let install_success = finished
&& launcher
.full_exe_path()
.exists();
let install_success = finished && launcher.full_exe_path().exists();
let prepare_btn = button(text("Download / Prepare"))
.on_press_maybe(prepare_enabled.then_some(Message::PreparePressed));
let install_btn = button(text("Run installer"))
.on_press_maybe(install_enabled.then_some(Message::InstallPressed));
// Single context-aware action button (changes 3 & 4)
let action_btn: Option<Element<Message>> = match state.stage {
Stage::Idle => {
let enabled = !state.source.trim().is_empty();
Some(
button(text("Download →"))
.on_press_maybe(enabled.then_some(Message::PreparePressed))
.into(),
)
}
Stage::Busy => Some(
button(text("Downloading…"))
.on_press_maybe(None::<Message>)
.into(),
),
Stage::Ready => Some(
button(text("Install →"))
.on_press(Message::InstallPressed)
.into(),
),
Stage::Installing => Some(
button(text("Installing…"))
.on_press_maybe(None::<Message>)
.into(),
),
Stage::Finished => None,
Stage::Picking => None,
};
let status = text(state.status.clone());
@@ -496,7 +519,7 @@ fn view_install(state: &State) -> Element<'_, Message> {
let close_btn = button(text("Close").size(13))
.on_press(Message::Close)
.style(button::secondary);
let launch_btn = button(text("Launch now").size(13))
let launch_btn = button(text("Open launcher").size(13))
.on_press_maybe(install_success.then_some(Message::LaunchNow))
.style(button::primary);
row![close_btn, launch_btn].spacing(10).into()
@@ -504,21 +527,37 @@ fn view_install(state: &State) -> Element<'_, Message> {
text("").into()
};
let body = column![
// Success banner (change 7)
let success_banner: Element<Message> = if finished && install_success {
text(format!("{} installed successfully!", launcher.display))
.size(14)
.color(Color::from_rgb(0.4, 0.9, 0.4))
.into()
} else {
text("").into()
};
let mut body = column![
header,
prefix,
expected,
text(source_label).size(12),
input,
row![prepare_btn, install_btn].spacing(12),
progress_row,
status,
finished_row,
log_pane,
]
.spacing(12)
.padding(20);
if let Some(btn) = action_btn {
body = body.push(btn);
}
let body = body
.push(progress_row)
.push(status)
.push(success_banner)
.push(finished_row)
.push(log_pane);
container(body).into()
}