use std::path::PathBuf; use std::sync::Arc; use druid::im::{HashMap, Vector}; use druid::text::RichText; use druid::{Data, ImageBuf, Lens, WindowHandle, WindowId}; use dtmt_shared::ModConfig; use nexusmods::Mod as NexusMod; use super::SelectedModLens; #[derive(Copy, Clone, Data, Debug, PartialEq)] pub(crate) enum View { Mods, Settings, } impl Default for View { fn default() -> Self { Self::Mods } } #[derive(Clone, Data, Debug, PartialEq)] pub struct PackageInfo { pub name: String, pub files: Vector, } impl PackageInfo { pub fn new(name: String, files: Vector) -> Self { Self { name, files } } } #[derive(Clone, Debug, PartialEq)] pub(crate) struct ModResourceInfo { pub init: PathBuf, pub data: Option, pub localization: Option, } #[derive(Clone, Data, Debug, PartialEq)] pub(crate) enum ModOrder { Before, After, } #[derive(Clone, Data, Debug, PartialEq)] pub(crate) struct ModDependency { pub id: String, pub order: ModOrder, } impl From for ModDependency { fn from(value: dtmt_shared::ModDependency) -> Self { match value { dtmt_shared::ModDependency::ID(id) => ModDependency { id, order: ModOrder::Before, }, dtmt_shared::ModDependency::Config { id, order } => ModDependency { id, order: match order { dtmt_shared::ModOrder::Before => ModOrder::Before, dtmt_shared::ModOrder::After => ModOrder::After, }, }, } } } #[derive(Clone, Data, Debug, Lens, serde::Serialize, serde::Deserialize)] pub(crate) struct NexusInfo { pub id: u64, pub name: String, pub version: String, pub author: String, pub summary: Arc, pub description: Arc, } impl From for NexusInfo { fn from(value: NexusMod) -> Self { Self { id: value.mod_id, name: value.name, version: value.version, author: value.author, summary: Arc::new(value.summary), description: Arc::new(value.description), } } } #[derive(Clone, Data, Debug, Lens)] pub(crate) struct ModInfo { pub id: String, pub name: String, pub summary: Arc, pub description: Option>, pub categories: Vector, pub author: Option, pub image: Option, pub version: String, pub enabled: bool, #[lens(ignore)] #[data(ignore)] pub packages: Vector>, #[lens(ignore)] #[data(ignore)] pub resources: ModResourceInfo, pub depends: Vector, pub bundled: bool, #[data(ignore)] pub nexus: Option, } impl ModInfo { pub fn new( cfg: ModConfig, packages: Vector>, image: Option, nexus: Option, ) -> Self { Self { id: cfg.id, name: cfg.name, summary: Arc::new(cfg.summary), description: cfg.description.map(Arc::new), author: cfg.author, version: cfg.version, enabled: false, packages, bundled: cfg.bundled, image, categories: cfg.categories.into_iter().collect(), resources: ModResourceInfo { init: cfg.resources.init, data: cfg.resources.data, localization: cfg.resources.localization, }, depends: cfg.depends.into_iter().map(ModDependency::from).collect(), nexus, } } } #[derive(Clone, Data, Lens)] pub(crate) struct State { pub current_view: View, pub mods: Vector>, pub selected_mod_index: Option, pub dirty: bool, pub is_deployment_in_progress: bool, pub is_reset_in_progress: bool, pub is_save_in_progress: bool, pub is_next_save_pending: bool, pub is_update_in_progress: bool, pub is_io_enabled: bool, pub game_dir: Arc, pub data_dir: Arc, pub nexus_api_key: Arc, pub log: Vector, // True, when the initial loading of configuration and mods is still in progress pub loading: bool, #[lens(ignore)] #[data(ignore)] pub config_path: Arc, #[lens(ignore)] #[data(ignore)] pub windows: HashMap, #[lens(ignore)] #[data(ignore)] pub ctx: Arc, } impl State { #[allow(non_upper_case_globals)] pub const selected_mod: SelectedModLens = SelectedModLens; pub fn new() -> Self { let ctx = sdk::Context::new(); Self { ctx: Arc::new(ctx), current_view: View::default(), mods: Vector::new(), selected_mod_index: None, dirty: false, is_deployment_in_progress: false, is_reset_in_progress: false, is_save_in_progress: false, is_next_save_pending: false, is_update_in_progress: false, is_io_enabled: false, config_path: Arc::new(PathBuf::new()), game_dir: Arc::new(PathBuf::new()), data_dir: Arc::new(PathBuf::new()), nexus_api_key: Arc::new(String::new()), log: Vector::new(), windows: HashMap::new(), loading: true, } } pub fn select_mod(&mut self, index: usize) { self.selected_mod_index = Some(index); } pub fn add_mod(&mut self, info: Arc) { if let Some(pos) = self.mods.iter().position(|i| i.id == info.id) { self.mods.set(pos, info); self.selected_mod_index = Some(pos); } else { self.mods.push_back(info); self.selected_mod_index = Some(self.mods.len() - 1); } } pub fn can_move_mod_down(&self) -> bool { self.selected_mod_index .map(|i| i < (self.mods.len().saturating_sub(1))) .unwrap_or(false) } pub fn can_move_mod_up(&self) -> bool { self.selected_mod_index.map(|i| i > 0).unwrap_or(false) } }