2024-08-24 13:03:27 +01:00
use std ::fmt ::format ;
2024-08-21 23:03:56 +01:00
use std ::path ::PathBuf ;
2024-08-24 03:03:43 +01:00
use std ::str ::FromStr ;
2024-08-21 23:03:56 +01:00
use iced ::widget ::{
button , checkbox , column , container , horizontal_rule , pick_list , progress_bar , row , scrollable ,
2024-08-24 02:17:07 +01:00
slider , text , text_input , toggler , vertical_rule , vertical_space , Column ,
2024-08-21 23:03:56 +01:00
} ;
2024-08-24 02:17:07 +01:00
use iced ::{ executor , Application , Command , Executor } ;
2024-08-21 23:03:56 +01:00
use iced ::{ Alignment , Element , Length , Sandbox , Settings , Theme } ;
use mcmpmgr ::profiles ::{ self , Profile } ;
2024-08-24 03:03:43 +01:00
use mcmpmgr ::providers ::DownloadSide ;
2024-08-21 23:03:56 +01:00
pub fn main ( ) -> iced ::Result {
2024-09-01 21:55:58 +01:00
ManagerGUI ::run ( Settings {
window : iced ::window ::Settings {
size : iced ::Size {
width : 800.0 ,
height : 300.0 ,
} ,
min_size : Some ( iced ::Size {
width : 800.0 ,
height : 300.0 ,
} ) ,
.. Default ::default ( )
} ,
.. Default ::default ( )
} )
2024-08-21 23:03:56 +01:00
}
#[ derive(Default) ]
struct ManagerGUI {
theme : Theme ,
selected_profile : Option < String > ,
userdata : profiles ::Data ,
2024-08-24 02:17:07 +01:00
userdata_load_error : Option < String > ,
2024-08-21 23:03:56 +01:00
current_view : ManagerView ,
previous_view : ManagerView ,
profile_edit_settings : ProfileSettings ,
profile_save_error : Option < String > ,
2024-09-01 12:45:44 +01:00
current_install_status : ProfileInstallStatus ,
2024-08-21 23:03:56 +01:00
}
#[ derive(Debug, Clone) ]
/// The current application view
enum ManagerView {
ProfileSelect ,
ProfileView { profile : String } ,
AddProfile ,
EditProfile { profile : String } ,
}
#[ derive(Debug, Clone) ]
/// The current application view
struct ProfileSettings {
name : String ,
2024-09-01 12:45:44 +01:00
instance_dir : Option < PathBuf > ,
2024-08-21 23:03:56 +01:00
pack_source : String ,
2024-08-24 03:03:43 +01:00
side : DownloadSide ,
2024-08-21 23:03:56 +01:00
}
impl Default for ProfileSettings {
fn default ( ) -> Self {
Self {
name : Default ::default ( ) ,
2024-09-01 12:45:44 +01:00
instance_dir : Default ::default ( ) ,
2024-08-21 23:03:56 +01:00
pack_source : Default ::default ( ) ,
2024-08-24 03:03:43 +01:00
side : DownloadSide ::Client ,
2024-08-21 23:03:56 +01:00
}
}
}
2024-08-24 03:03:43 +01:00
impl TryFrom < ProfileSettings > for profiles ::Profile {
type Error = String ;
fn try_from ( value : ProfileSettings ) -> Result < Self , Self ::Error > {
2024-09-01 12:45:44 +01:00
let instance_dir = value
. instance_dir
. ok_or ( format! ( " An instance directory is required " ) ) ? ;
if ! instance_dir . join ( " mods " ) . exists ( ) {
return Err ( format! ( " Instance folder {} does not seem to contain a mods directory. Are you sure this is a valid instance directory? " , instance_dir . display ( ) ) ) ;
}
2024-08-24 03:03:43 +01:00
let pack_source = value . pack_source ;
Ok ( profiles ::Profile ::new (
2024-09-01 12:45:44 +01:00
& instance_dir ,
2024-08-24 03:03:43 +01:00
profiles ::PackSource ::from_str ( & pack_source ) ? ,
value . side ,
) )
}
}
2024-08-21 23:03:56 +01:00
impl Default for ManagerView {
fn default ( ) -> Self {
Self ::ProfileSelect
}
}
#[ derive(Debug, Clone) ]
enum Message {
SwitchView ( ManagerView ) ,
2024-09-01 12:45:44 +01:00
BrowseInstanceDir ,
2024-08-21 23:03:56 +01:00
EditProfileName ( String ) ,
EditPackSource ( String ) ,
SaveProfile ,
2024-08-24 02:23:38 +01:00
DeleteProfile ( String ) ,
2024-08-24 13:03:27 +01:00
InstallProfile ( String ) ,
2024-09-01 12:45:44 +01:00
ProfileInstalled ( ProfileInstallStatus ) ,
2024-08-24 13:03:27 +01:00
}
#[ derive(Debug, Clone) ]
enum ProfileInstallStatus {
NotStarted ,
Installing ,
Success ,
2024-09-01 12:45:44 +01:00
Error ( String ) ,
2024-08-24 13:03:27 +01:00
}
impl Default for ProfileInstallStatus {
fn default ( ) -> Self {
Self ::NotStarted
}
2024-08-21 23:03:56 +01:00
}
2024-08-24 02:17:07 +01:00
impl Application for ManagerGUI {
2024-08-21 23:03:56 +01:00
type Message = Message ;
2024-08-24 02:17:07 +01:00
type Executor = executor ::Default ;
type Theme = Theme ;
type Flags = ( ) ;
2024-08-21 23:03:56 +01:00
2024-08-24 02:17:07 +01:00
fn new ( _flags : Self ::Flags ) -> ( Self , Command < Self ::Message > ) {
2024-08-21 23:03:56 +01:00
let mut gui = ManagerGUI ::default ( ) ;
gui . theme = Theme ::GruvboxDark ;
2024-08-24 02:17:07 +01:00
let loaded_userdata = profiles ::Data ::load ( ) ;
match loaded_userdata {
Ok ( userdata ) = > {
gui . userdata = userdata ;
}
Err ( err ) = > {
gui . userdata_load_error = Some ( err . to_string ( ) ) ;
}
} ;
( gui , Command ::none ( ) )
2024-08-21 23:03:56 +01:00
}
fn title ( & self ) -> String {
String ::from ( " Minecraft Modpack Manager " )
}
2024-08-24 02:17:07 +01:00
fn update ( & mut self , message : Message ) -> Command < Message > {
2024-08-21 23:03:56 +01:00
match message {
Message ::SwitchView ( view ) = > {
2024-09-01 12:20:23 +01:00
self . current_install_status = ProfileInstallStatus ::NotStarted ;
2024-08-21 23:03:56 +01:00
match & view {
ManagerView ::AddProfile = > {
self . profile_save_error = None ;
self . profile_edit_settings = ProfileSettings ::default ( ) ;
}
2024-08-24 02:17:07 +01:00
ManagerView ::ProfileSelect = > {
let loaded_userdata = profiles ::Data ::load ( ) ;
match loaded_userdata {
Ok ( userdata ) = > {
self . userdata = userdata ;
self . userdata_load_error = None ;
}
Err ( err ) = > {
self . userdata_load_error = Some ( err . to_string ( ) ) ;
}
} ;
}
2024-08-24 03:22:39 +01:00
ManagerView ::EditProfile { profile } = > {
let loaded_profile = self . userdata . get_profile ( profile ) ;
self . profile_edit_settings . name = profile . trim ( ) . into ( ) ;
if let Some ( loaded_profile ) = loaded_profile {
self . profile_edit_settings . name = profile . into ( ) ;
2024-09-01 12:45:44 +01:00
self . profile_edit_settings . instance_dir =
Some ( loaded_profile . instance_folder . clone ( ) ) ;
2024-08-24 03:22:39 +01:00
self . profile_edit_settings . pack_source =
loaded_profile . pack_source . to_string ( ) ;
self . profile_edit_settings . side = loaded_profile . side ;
} else {
eprintln! ( " Failed to load existing profile data for {profile} " ) ;
}
}
2024-08-21 23:03:56 +01:00
_ = > { }
} ;
self . current_view = view ;
2024-08-24 02:17:07 +01:00
Command ::none ( )
2024-08-21 23:03:56 +01:00
}
2024-09-01 12:45:44 +01:00
Message ::BrowseInstanceDir = > {
self . profile_edit_settings . instance_dir = rfd ::FileDialog ::new ( )
. set_title ( " Select your instance folder " )
2024-08-21 23:03:56 +01:00
. pick_folder ( ) ;
2024-08-24 02:17:07 +01:00
Command ::none ( )
}
Message ::EditProfileName ( name ) = > {
self . profile_edit_settings . name = name ;
Command ::none ( )
}
Message ::EditPackSource ( pack_source ) = > {
self . profile_edit_settings . pack_source = pack_source ;
Command ::none ( )
2024-08-21 23:03:56 +01:00
}
Message ::SaveProfile = > {
2024-08-24 03:03:43 +01:00
let profile : Result < profiles ::Profile , String > =
profiles ::Profile ::try_from ( self . profile_edit_settings . clone ( ) ) ;
if let Ok ( profile ) = profile {
if self . profile_edit_settings . name . trim ( ) . len ( ) = = 0 {
self . profile_save_error =
format! ( " Invalid profile name {} " , self . profile_edit_settings . name )
. into ( ) ;
} else {
self . userdata
. add_profile ( self . profile_edit_settings . name . trim ( ) , profile ) ;
2024-08-24 03:13:59 +01:00
let save_result = self . userdata . save ( ) ;
if let Err ( err ) = save_result {
self . profile_save_error =
format! ( " Unable to save profile: {err:#?} " ) . into ( ) ;
} else {
self . current_view = ManagerView ::ProfileView {
profile : self . profile_edit_settings . name . trim ( ) . into ( ) ,
}
}
2024-08-24 03:03:43 +01:00
}
} else if let Err ( err ) = profile {
self . profile_save_error = err . into ( ) ;
} ;
2024-08-24 02:17:07 +01:00
Command ::none ( )
2024-08-21 23:03:56 +01:00
}
2024-08-24 02:23:38 +01:00
Message ::DeleteProfile ( name ) = > {
self . userdata . remove_profile ( & name ) ;
let save_result = self . userdata . save ( ) ;
if let Err ( err ) = save_result {
self . profile_save_error = Some ( err . to_string ( ) ) ;
} else {
self . current_view = ManagerView ::ProfileSelect ;
}
Command ::none ( )
}
2024-08-24 13:03:27 +01:00
Message ::InstallProfile ( name ) = > {
self . current_install_status = ProfileInstallStatus ::Installing ;
let profile_name = name . clone ( ) ;
let profile = self . userdata . get_profile ( & name ) . cloned ( ) ;
Command ::perform (
async move {
if let Some ( profile ) = profile {
let result = profile . install ( ) . await ;
if let Err ( err ) = result {
ProfileInstallStatus ::Error ( format! ( " {} " , err ) )
2024-09-01 12:45:44 +01:00
} else {
2024-08-24 13:03:27 +01:00
ProfileInstallStatus ::Success
}
2024-09-01 12:45:44 +01:00
} else {
2024-08-24 13:03:27 +01:00
ProfileInstallStatus ::Error ( format! ( " Profile ' {} ' doesn't exist " , name ) )
}
} ,
2024-09-01 12:45:44 +01:00
Message ::ProfileInstalled ,
2024-08-24 13:03:27 +01:00
)
2024-09-01 12:45:44 +01:00
}
2024-08-24 13:03:27 +01:00
Message ::ProfileInstalled ( result ) = > {
2024-09-01 12:45:44 +01:00
self . current_install_status = result ;
2024-08-24 13:03:27 +01:00
Command ::none ( )
2024-09-01 12:45:44 +01:00
}
2024-08-21 23:03:56 +01:00
}
}
fn view ( & self ) -> Element < Message > {
let contents = match & self . current_view {
ManagerView ::ProfileSelect = > self . view_profile_select ( ) ,
ManagerView ::ProfileView { profile } = > self . view_profile_view ( & profile ) ,
2024-08-24 03:03:43 +01:00
ManagerView ::AddProfile = > self . view_profile_edit ( " " , ManagerView ::ProfileSelect , true ) ,
2024-08-21 23:03:56 +01:00
ManagerView ::EditProfile { profile } = > self . view_profile_edit (
& profile ,
ManagerView ::ProfileView {
profile : profile . clone ( ) ,
} ,
2024-08-24 03:03:43 +01:00
false ,
2024-08-21 23:03:56 +01:00
) ,
} ;
container ( contents )
. width ( Length ::Fill )
. height ( Length ::Fill )
. center_x ( )
. into ( )
}
fn theme ( & self ) -> Theme {
self . theme . clone ( )
}
}
impl ManagerGUI {
fn view_profile_select ( & self ) -> Element < Message > {
2024-08-24 02:17:07 +01:00
let mut profile_select = column! [ text ( " Profile Select " ) , ] ;
let mut profiles_list : Column < Message > = column! ( ) ;
let mut profile_names = self . userdata . get_profile_names ( ) ;
profile_names . sort ( ) ;
for profile_name in profile_names . iter ( ) {
profiles_list = profiles_list . push (
button ( text ( profile_name ) )
. on_press ( Message ::SwitchView ( ManagerView ::ProfileView {
profile : profile_name . into ( ) ,
} ) )
. width ( Length ::Fill ) ,
) ;
}
profile_select =
profile_select . push ( profiles_list . align_items ( Alignment ::Center ) . spacing ( 1 ) ) ;
profile_select = profile_select
. push ( button ( " Add profile " ) . on_press ( Message ::SwitchView ( ManagerView ::AddProfile ) ) ) ;
scrollable (
profile_select
. spacing ( 10 )
. align_items ( Alignment ::Center )
. padding ( 10 ) ,
)
2024-08-21 23:03:56 +01:00
. into ( )
}
fn view_profile_view ( & self , profile_name : & str ) -> Element < Message > {
2024-08-24 03:03:43 +01:00
let mut profile_view = if let Some ( profile ) = self . userdata . get_profile ( profile_name ) {
column! [
text ( format! ( " Modpack Profile: {profile_name} " ) )
. horizontal_alignment ( iced ::alignment ::Horizontal ::Center ) ,
row! [
" Modpack source " ,
text_input ( " Modpack source " , & profile . pack_source . to_string ( ) ) ,
]
. spacing ( 5 ) ,
row! [
2024-09-01 12:45:44 +01:00
" Instance folder " ,
text_input (
" Instance folder " ,
& profile . instance_folder . display ( ) . to_string ( )
) ,
2024-08-24 03:03:43 +01:00
]
. spacing ( 20 ) ,
row! [ " Mods to download " , text ( profile . side ) , ] . spacing ( 5 ) ,
2024-08-24 13:03:27 +01:00
button ( " Install " ) . on_press ( Message ::InstallProfile ( profile_name . into ( ) ) ) ,
2024-08-24 03:03:43 +01:00
row! [
button ( " Back " ) . on_press ( Message ::SwitchView ( ManagerView ::ProfileSelect ) ) ,
button ( " Edit profile " ) . on_press ( Message ::SwitchView (
ManagerView ::EditProfile {
profile : profile_name . into ( )
}
) ) ,
button ( " Delete profile " ) . on_press ( Message ::DeleteProfile ( profile_name . into ( ) ) )
]
. spacing ( 5 )
]
} else {
column! [
text ( format! ( " Unable to load profile: {profile_name} " ) ) ,
2024-08-24 02:17:07 +01:00
button ( " Back " ) . on_press ( Message ::SwitchView ( ManagerView ::ProfileSelect ) ) ,
]
2024-08-24 03:03:43 +01:00
} ;
2024-08-24 02:17:07 +01:00
if let Some ( err ) = & self . userdata_load_error {
profile_view = profile_view . push ( text ( err ) ) ;
}
2024-08-24 13:03:27 +01:00
match & self . current_install_status {
2024-09-01 12:45:44 +01:00
ProfileInstallStatus ::NotStarted = > { }
2024-08-24 13:03:27 +01:00
ProfileInstallStatus ::Installing = > {
profile_view = profile_view . push ( text ( " Installing... " ) ) ;
2024-09-01 12:45:44 +01:00
}
2024-08-24 13:03:27 +01:00
ProfileInstallStatus ::Success = > {
profile_view = profile_view . push ( text ( " Installed " ) ) ;
2024-09-01 12:45:44 +01:00
}
2024-08-24 13:03:27 +01:00
ProfileInstallStatus ::Error ( err ) = > {
2024-09-01 12:45:44 +01:00
profile_view =
profile_view . push ( text ( format! ( " Failed to install profile: {} " , err ) ) ) ;
}
} ;
2024-08-24 13:03:27 +01:00
2024-08-24 03:03:43 +01:00
profile_view
. align_items ( Alignment ::Center )
. spacing ( 10 )
. padding ( 20 )
. into ( )
2024-08-21 23:03:56 +01:00
}
fn view_profile_edit (
& self ,
profile_name : & str ,
previous_view : ManagerView ,
2024-08-24 03:03:43 +01:00
can_edit_name : bool ,
2024-08-21 23:03:56 +01:00
) -> Element < Message > {
2024-09-01 12:45:44 +01:00
let current_instance_directory_display = match & self . profile_edit_settings . instance_dir {
Some ( instance_dir ) = > instance_dir . display ( ) . to_string ( ) ,
2024-08-24 03:28:48 +01:00
None = > String ::from ( " " ) ,
} ;
2024-08-21 23:03:56 +01:00
let mut profile_editor = column! [
2024-08-24 02:17:07 +01:00
text ( " Profile Add/Edit " ) . horizontal_alignment ( iced ::alignment ::Horizontal ::Center ) ,
2024-08-21 23:03:56 +01:00
row! [
" Profile name " ,
2024-08-24 03:03:43 +01:00
if can_edit_name {
text_input ( " Enter your profile name " , & self . profile_edit_settings . name )
. on_input ( Message ::EditProfileName )
} else {
text_input ( " Profile name " , & self . profile_edit_settings . name )
}
2024-08-21 23:03:56 +01:00
]
. spacing ( 5 ) ,
row! [
" Modpack source " ,
text_input (
" Enter a modpack source. E.g git+https://github.com/WarrenHood/SomeModPack " ,
& self . profile_edit_settings . pack_source
)
. on_input ( Message ::EditPackSource )
]
. spacing ( 5 ) ,
row! [
2024-09-01 12:45:44 +01:00
" Instance directory " ,
2024-08-24 03:28:48 +01:00
text_input (
2024-09-01 12:45:44 +01:00
" Browse for your MC instance directory (contains your mods folder) " ,
& current_instance_directory_display
2024-08-24 02:17:07 +01:00
) ,
2024-09-01 12:45:44 +01:00
button ( " Browse " ) . on_press ( Message ::BrowseInstanceDir )
2024-08-21 23:03:56 +01:00
]
. spacing ( 5 ) ,
row! [
button ( " Back " ) . on_press ( Message ::SwitchView ( previous_view ) ) ,
button ( " Save " ) . on_press ( Message ::SaveProfile )
]
. spacing ( 10 )
]
2024-08-24 02:17:07 +01:00
. align_items ( Alignment ::Center )
2024-08-21 23:03:56 +01:00
. spacing ( 10 )
. padding ( 20 ) ;
if let Some ( save_error ) = & self . profile_save_error {
profile_editor =
profile_editor . extend ( [ row! [ " Save error " , text ( save_error ) ] . spacing ( 10 ) . into ( ) ] ) ;
} ;
profile_editor . into ( )
}
2024-08-20 20:17:06 +01:00
}