use std::path::PathBuf;
use crate::views::selectable_text::text;
use iced::widget::{button, column, pick_list, row, scrollable, text_input};
use iced::{Alignment, Element, Length, color};
use crate::action_button::{ButtonAction, DescribedButtonExt};
use crate::app::{Message, NexusAuthStatus, SettingsState};
use crate::semantics;
pub fn view(state: SettingsState) -> Element<'static, Message> {
let title = text("Settings").size(20);
let nexus_status_widget: Element<'static, Message> = match &state.nexus_status {
Some(NexusAuthStatus::Checking) => {
text("Checking...").size(12).color(color!(0xAAAA44)).into()
}
Some(NexusAuthStatus::Valid {
username,
is_premium,
}) => {
let tier = if *is_premium { "Premium" } else { "Standard" };
text(format!("Logged in as {username} ({tier})"))
.size(12)
.color(color!(0x88CC88))
.into()
}
Some(NexusAuthStatus::Invalid(err)) => text(format!("Invalid: {err}"))
.size(12)
.color(color!(0xFF4444))
.into(),
None => text("Not validated").size(12).into(),
};
let game_path_str = state
.game_install_paths
.first()
.map(|install| install.path.display().to_string())
.unwrap_or_default();
let download_dir_str = state
.download_dir
.as_ref()
.map(|p| p.display().to_string())
.unwrap_or_default();
let nexus_source = state
.nexus_api_key_source
.as_ref()
.map(|source| format!("Using {}", source.label()))
.unwrap_or_else(|| "No key configured".to_string());
let show_hide_label = if state.nexus_api_key_visible {
"Hide"
} else {
"Show"
};
let api_key_section = column![
text("Nexus Mods API Key").size(14),
text("Required for downloading mods and browsing collections.").size(11),
row![
text_input("Enter your API key...", &state.nexus_api_key_draft)
.id(semantics::widget_id("settings.nexus_api_key"))
.secure(!state.nexus_api_key_visible)
.on_input(Message::SetNexusApiKeyDraft)
.padding(8)
.width(Length::Fill),
semantics::test_id(
"settings.nexus_api_key.toggle_visibility",
button(text(show_hide_label).size(13))
.style(button::secondary)
.padding([6, 12])
.on_action(ButtonAction::ToggleNexusApiKeyVisibility),
),
]
.spacing(8)
.align_y(Alignment::Center),
row![
semantics::test_id(
"settings.nexus_api_key.replace",
button(text("Replace").size(13))
.style(button::secondary)
.padding([6, 12])
.on_action(ButtonAction::ReplaceNexusApiKey),
),
semantics::test_id(
"settings.nexus_api_key.remove_modde_config",
button(text("Remove modde config").size(13))
.style(button::secondary)
.padding([6, 12])
.on_action(ButtonAction::RemoveNexusConfigKey),
),
semantics::test_id(
"settings.nexus_api_key.validate",
button(text("Validate").size(13))
.style(button::primary)
.padding([6, 12])
.on_action(ButtonAction::ValidateNexusKey),
),
]
.spacing(8)
.align_y(Alignment::Center),
text(nexus_source).size(12),
text(if state.nexus_config_key_exists {
"modde config key exists"
} else {
"No modde config key"
})
.size(11),
nexus_status_widget,
]
.spacing(4);
let game_path_section = column![
text("Game Install Path").size(14),
text("Root directory of the game installation.").size(11),
row![
text_input("/path/to/game", &game_path_str,)
.id(semantics::widget_id("settings.game_path"))
.on_input(|s| Message::SetGamePath {
game_id: "default".to_string(),
path: PathBuf::from(s)
})
.padding(8)
.width(Length::Fill),
semantics::test_id(
"settings.game_path.browse",
button(text("Browse").size(13))
.style(button::secondary)
.padding([6, 12])
.on_action(ButtonAction::BrowseGamePath),
),
]
.spacing(8)
.align_y(Alignment::Center),
]
.spacing(4);
let download_dir_section = column![
text("Download Directory").size(14),
text("Where downloaded mod archives are stored.").size(11),
row![
text_input("/path/to/downloads", &download_dir_str,)
.id(semantics::widget_id("settings.download_dir"))
.on_input(|s| Message::SetDownloadDir(PathBuf::from(s)))
.padding(8)
.width(Length::Fill),
semantics::test_id(
"settings.download_dir.browse",
button(text("Browse").size(13))
.style(button::secondary)
.padding([6, 12])
.on_action(ButtonAction::BrowseDownloadDir),
),
]
.spacing(8)
.align_y(Alignment::Center),
]
.spacing(4);
let stock_game_section = column![
text("Stock Game Snapshot").size(14),
text("Create a snapshot of your clean game install for virtual deployment.").size(11),
row![
semantics::test_id(
"settings.stock_snapshot.create",
button(text("Create Snapshot").size(13))
.style(button::primary)
.padding([6, 14])
.on_action(ButtonAction::CreateStockSnapshot),
),
semantics::test_id(
"settings.stock_snapshot.verify",
button(text("Verify Snapshot").size(13))
.style(button::secondary)
.padding([6, 14])
.on_action(ButtonAction::VerifyStockSnapshot),
),
text(if state.has_stock_snapshot {
"Snapshot exists"
} else {
"No snapshot created"
})
.size(12),
]
.spacing(12)
.align_y(Alignment::Center),
]
.spacing(4);
let theme_options = vec![
"Dark".to_string(),
"Light".to_string(),
"Dracula".to_string(),
"Nord".to_string(),
"Gruvbox Dark".to_string(),
"Catppuccin Mocha".to_string(),
];
let theme_section = column![
text("Theme").size(14),
pick_list(
theme_options,
Some(state.theme_name.clone()),
Message::SetTheme,
)
.width(Length::Fixed(200.0)),
]
.spacing(4);
let content = scrollable(
column![
api_key_section,
iced::widget::rule::horizontal(1),
game_path_section,
iced::widget::rule::horizontal(1),
download_dir_section,
iced::widget::rule::horizontal(1),
stock_game_section,
iced::widget::rule::horizontal(1),
theme_section,
]
.spacing(16)
.padding(16),
)
.height(Length::Fill);
column![title, iced::widget::rule::horizontal(1), content,]
.spacing(8)
.padding(16)
.width(Length::Fill)
.height(Length::Fill)
.into()
}