refactor(setup): redesign launcher picking screen
Replaced the flat form layout with a polished card-based design: - Section cards with subtle borders matching the dashboard style - Blue accent labels for section headings (Launcher, Install Location) - Hint text explaining the Wine prefix folder - Error status styled in orange-red instead of plain text - Next button right-aligned with top spacing - Header with muted subtitle Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+98
-25
@@ -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::{Color, Element, Length, Subscription, Task, Theme};
|
||||
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Subscription, Task, Theme};
|
||||
use std::ffi::OsString;
|
||||
use std::io::{BufRead, BufReader, Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
@@ -352,10 +352,40 @@ fn view(state: &State) -> Element<'_, Message> {
|
||||
view_install(state)
|
||||
}
|
||||
|
||||
fn view_picking(state: &State) -> Element<'_, Message> {
|
||||
let header = text("Add a Launcher").size(24);
|
||||
let sub = text("Choose a launcher and where to install it.").size(13);
|
||||
fn section_card<'a>(content: impl Into<Element<'a, Message>>) -> Element<'a, Message> {
|
||||
container(content)
|
||||
.padding(Padding::from([12, 16]))
|
||||
.width(Length::Fill)
|
||||
.style(|theme: &Theme| {
|
||||
let p = theme.extended_palette();
|
||||
container::Style {
|
||||
background: Some(Background::Color(p.background.weak.color)),
|
||||
border: Border {
|
||||
color: p.background.strong.color,
|
||||
width: 1.0,
|
||||
radius: 6.0.into(),
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.into()
|
||||
}
|
||||
|
||||
fn view_picking(state: &State) -> Element<'_, Message> {
|
||||
// ── Header ───────────────────────────────────────────────────────────────
|
||||
let header = container(
|
||||
column![
|
||||
text("Add a Launcher").size(26),
|
||||
text("Choose a launcher and set its install location.").size(13)
|
||||
.style(|_: &Theme| text::Style {
|
||||
color: Some(Color::from_rgb(0.6, 0.6, 0.6)),
|
||||
}),
|
||||
]
|
||||
.spacing(4),
|
||||
)
|
||||
.padding(Padding { top: 24.0, right: 24.0, bottom: 8.0, left: 24.0 });
|
||||
|
||||
// ── Launcher picker card ──────────────────────────────────────────────────
|
||||
let picker = pick_list(
|
||||
state.template_options.as_slice(),
|
||||
state.selected_template.clone(),
|
||||
@@ -364,7 +394,18 @@ fn view_picking(state: &State) -> Element<'_, Message> {
|
||||
.placeholder("Select a launcher…")
|
||||
.width(Length::Fill);
|
||||
|
||||
let prefix_input = text_input("/home/user/Games/battlenet", &state.prefix_input)
|
||||
let launcher_card = section_card(
|
||||
column![
|
||||
text("Launcher").size(12).style(|_: &Theme| text::Style {
|
||||
color: Some(Color::from_rgb(0.55, 0.75, 1.0)),
|
||||
}),
|
||||
picker,
|
||||
]
|
||||
.spacing(6),
|
||||
);
|
||||
|
||||
// ── Install location card ─────────────────────────────────────────────────
|
||||
let prefix_input = text_input("e.g. /home/user/Games/battlenet", &state.prefix_input)
|
||||
.on_input(Message::PrefixChanged)
|
||||
.padding(8)
|
||||
.width(Length::Fill);
|
||||
@@ -373,30 +414,62 @@ fn view_picking(state: &State) -> Element<'_, Message> {
|
||||
.on_press(Message::BrowsePrefix)
|
||||
.style(button::secondary);
|
||||
|
||||
let location_card = section_card(
|
||||
column![
|
||||
text("Install Location").size(12).style(|_: &Theme| text::Style {
|
||||
color: Some(Color::from_rgb(0.55, 0.75, 1.0)),
|
||||
}),
|
||||
row![prefix_input, browse_btn]
|
||||
.spacing(8)
|
||||
.align_y(Alignment::Center),
|
||||
text("The folder where the launcher's Wine prefix will be created.")
|
||||
.size(11)
|
||||
.style(|_: &Theme| text::Style {
|
||||
color: Some(Color::from_rgb(0.45, 0.45, 0.45)),
|
||||
}),
|
||||
]
|
||||
.spacing(6),
|
||||
);
|
||||
|
||||
// ── Status / error ────────────────────────────────────────────────────────
|
||||
let status_el: Element<Message> = if !state.status.is_empty() {
|
||||
container(
|
||||
text(&state.status).size(13).style(|_: &Theme| text::Style {
|
||||
color: Some(Color::from_rgb(1.0, 0.55, 0.45)),
|
||||
}),
|
||||
)
|
||||
.padding(Padding::from([6, 0]))
|
||||
.into()
|
||||
} else {
|
||||
text("").into()
|
||||
};
|
||||
|
||||
// ── Next button ───────────────────────────────────────────────────────────
|
||||
let can_confirm =
|
||||
state.selected_template.is_some() && !state.prefix_input.trim().is_empty();
|
||||
|
||||
let confirm_btn = button(text("Next →"))
|
||||
.on_press_maybe(can_confirm.then_some(Message::ConfirmLauncher))
|
||||
.style(button::primary);
|
||||
let next_btn = container(
|
||||
button(text("Next →").size(14))
|
||||
.on_press_maybe(can_confirm.then_some(Message::ConfirmLauncher))
|
||||
.style(button::primary),
|
||||
)
|
||||
.padding(Padding { top: 4.0, right: 0.0, bottom: 0.0, left: 0.0 })
|
||||
.width(Length::Fill)
|
||||
.align_x(Alignment::End);
|
||||
|
||||
let mut body = column![
|
||||
header,
|
||||
sub,
|
||||
text("Launcher:").size(13),
|
||||
picker,
|
||||
text("Install location (Wine prefix):").size(13),
|
||||
row![prefix_input, browse_btn].spacing(8).align_y(iced::Alignment::Center),
|
||||
confirm_btn,
|
||||
]
|
||||
.spacing(10)
|
||||
.padding(20);
|
||||
|
||||
if !state.status.is_empty() {
|
||||
body = body.push(text(state.status.clone()).size(13));
|
||||
}
|
||||
|
||||
container(body).into()
|
||||
container(
|
||||
column![
|
||||
header,
|
||||
container(
|
||||
column![launcher_card, location_card, status_el, next_btn]
|
||||
.spacing(12)
|
||||
.padding(Padding { top: 8.0, right: 24.0, bottom: 24.0, left: 24.0 }),
|
||||
),
|
||||
]
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
fn view_install(state: &State) -> Element<'_, Message> {
|
||||
|
||||
Reference in New Issue
Block a user