use config::{default_save_filename, load_config, save_config, Config}; use eframe::egui; use eframe::egui::Color32; use infos::{ CommandErrors, CommandManager, Difficulty, LauncherInfo, MultiManager, SingleManager, WadInfo, }; use native_dialog::{MessageDialog, MessageType}; use std::path::PathBuf; use std::process::Command; pub mod config; pub mod infos; pub mod wad; fn main() -> Result<(), eframe::Error> { let gui_options = eframe::NativeOptions { ..Default::default() }; eframe::run_native( "Rust Doom Launcher", gui_options, Box::new(|_cc| Box::::default()), ) } #[derive(PartialEq)] enum FileType { Iwad, Pwad, Launcher, } struct RustDoomLauncher { launcher_manager: SingleManager, iwad_manager: SingleManager, pwad_manager: MultiManager, add_name: String, config_filename: PathBuf, config_file_loaded: bool, command_manager: CommandManager, display_command: bool, add_stuff_window_displayed: bool, selected_file_type: FileType, selected_file_path: PathBuf, } impl Default for RustDoomLauncher { fn default() -> Self { Self { launcher_manager: SingleManager::new(), iwad_manager: SingleManager::new(), pwad_manager: MultiManager::new(), command_manager: CommandManager::new(), add_name: "".to_string(), config_filename: default_save_filename(), config_file_loaded: false, display_command: false, add_stuff_window_displayed: false, selected_file_type: FileType::Pwad, selected_file_path: PathBuf::new(), } } } impl RustDoomLauncher { // There must be a better way than this - maybe for the RustDoomLauncher to // have a config thing fn get_config_file_stuff(&mut self) { let config = load_config(&self.config_filename).unwrap(); if let Some(iwads) = config.iwads { for iwad in iwads { self.iwad_manager.add(&iwad); } } if let Some(pwads) = config.pwads { for pwad in pwads { self.pwad_manager.add(&pwad.clone()); } } if let Some(launchers) = config.launchers { for launcher in launchers { self.launcher_manager.add(&launcher); } } } fn launch_doom(&self) { match self.command_manager.generate_command() { Err(e) => { let text = match e { CommandErrors::NoIwad => "an IWAD", CommandErrors::NoLauncher => "a launcher", }; MessageDialog::new() .set_type(MessageType::Error) .set_title("I can't let you do that") .set_text(&format!("You didn't pick {}!\nPlease do that", text)) .show_alert() .unwrap(); } Ok((launcher, command)) => { // Note to self, don't hit launch button when running from emacs let result = Command::new(launcher).args(command).spawn(); match result { Err(e) => panic!("{}", e), Ok(_o) => (), } } } } fn launcher_and_iwad(&self) -> Result<(&LauncherInfo, &WadInfo), CommandErrors> { let launcher = match self.launcher_manager.get_current() { Some(l) => l, None => { return Err(CommandErrors::NoLauncher); } }; let iwad = match self.iwad_manager.get_current() { Some(iwad) => iwad, None => { return Err(CommandErrors::NoIwad); } }; Ok((launcher, iwad)) } } impl eframe::App for RustDoomLauncher { fn on_close_event(&mut self) -> bool { let config = Config { iwads: Some(self.iwad_manager.selectable.clone()), pwads: Some(self.pwad_manager.selectable.clone()), launchers: Some(self.launcher_manager.selectable.clone()), }; save_config(&self.config_filename, &config).unwrap(); true } fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { if !self.config_file_loaded { self.get_config_file_stuff(); self.config_file_loaded = true; } egui::containers::panel::CentralPanel::default().show(ctx, |ui| { ui.horizontal(|ui| { ui.heading("RustDoomLauncher"); let button = match self.launcher_and_iwad() { Err(_) => egui::Button::new("PLAY SOME DOOM!"), Ok((_, _)) => egui::Button::new("PLAY SOME DOOM!").fill(Color32::DARK_GREEN), }; if ui.add(button).clicked() { self.launch_doom(); } if ui.button("Add WAD or Launcher").clicked() { self.add_stuff_window_displayed = true; } }); ui.horizontal(|ui| { ui.vertical(|ui| { ui.set_max_width(100.0); ui.label("Launchers"); let mut remove_pos: Option = None; let mut add_pos: Option = None; for (launcher, pos, selected) in self .launcher_manager .iter_selectable_with_pos_and_selected() { ui.horizontal(|ui| { if ui .add(egui::SelectableLabel::new(selected, &launcher.name)) .clicked() { add_pos = Some(pos); } if ui.button("❌").clicked() { remove_pos = Some(pos); } }); } // I'm unsure whether there's a better way to do this. // The iterator call is a mutable borrow - thus compiler doesn't let me // do a mutable thing in the middle of that, which I guess is fair enough, // but I would like to break out of the loop as soon as that occurs... // Completely unsure if let Some(rp) = remove_pos { self.launcher_manager.remove_selectable(rp); if self.launcher_manager.get_current().is_none() { self.command_manager.remove_launcher(); } } if let Some(ap) = add_pos { self.launcher_manager.set_current(ap); self.command_manager .add_launcher(self.launcher_manager.get_current().unwrap()) } ui.separator(); ui.label("IWADs"); let mut remove_pos: Option = None; let mut add_pos: Option = None; for (launcher, pos, selected) in self.iwad_manager.iter_selectable_with_pos_and_selected() { ui.horizontal(|ui| { if ui .add(egui::SelectableLabel::new(selected, &launcher.name)) .clicked() { add_pos = Some(pos); } if ui.button("❌").clicked() { remove_pos = Some(pos); } }); } if let Some(rp) = remove_pos { self.iwad_manager.remove_selectable(rp); if self.launcher_manager.get_current().is_none() { self.command_manager.remove_launcher(); } } if let Some(ap) = add_pos { self.iwad_manager.set_current(ap); self.command_manager .add_iwad(self.iwad_manager.get_current().unwrap()); } }); ui.separator(); ui.vertical(|ui| { ui.set_min_width(100.0); ui.label("PWADs"); let mut remove_pos: Option = None; let mut add_pos: Option = None; for (pwad, pos, selected) in self.pwad_manager.iter_selectable_with_pos_and_selected() { ui.horizontal(|ui| { if ui .add(egui::SelectableLabel::new(selected, &pwad.name)) .clicked() { add_pos = Some(pos); } if ui.button("❌").clicked() { remove_pos = Some(pos); } }); } if let Some(rp) = remove_pos { self.pwad_manager.remove_selectable(rp); self.command_manager .add_pwads(&self.pwad_manager.get_current()) } if let Some(ap) = add_pos { self.pwad_manager.set_current(ap); self.command_manager .add_pwads(&self.pwad_manager.get_current()) } }); let mut text = "Placeholder for WAD info stuffs"; let window_size = frame.info().window_info.size; ui.add( egui::TextEdit::multiline(&mut text) .desired_width(window_size[0] / 1.6) .desired_rows(20), ); }); ui.separator(); ui.horizontal_wrapped(|ui| { ui.horizontal(|ui| { ui.set_max_width(180.0); let warp_label = ui.label("warp"); let r = ui.text_edit_singleline(&mut self.command_manager.warp) .labelled_by(warp_label.id); if r.changed() { let _ = self.command_manager.generate_command_str(); } }); ui.separator(); ui.horizontal(|ui| { ui.horizontal(|ui| { ui.label("difficulty"); ui.set_max_width(180.0); eframe::egui::ComboBox::new("difficulty", "") .selected_text(format!("{}", self.command_manager.difficulty)) .show_ui(ui, |ui| { for diff in Difficulty::iterator() { let r = ui.selectable_value( &mut self.command_manager.difficulty, *diff, diff.to_string(), ); if r.clicked() { let _ = self.command_manager.generate_command_str(); } } }); }); ui.separator(); ui.horizontal(|ui| { ui.label("fast"); let r = ui.add(egui::Checkbox::without_text(&mut self.command_manager.fast_monsters)); if r.clicked() { let _ = self.command_manager.generate_command_str(); } }); ui.separator(); ui.horizontal(|ui| { ui.label("respawn"); let r = ui.add(egui::Checkbox::without_text(&mut self.command_manager.respawning_monsters)); if r.clicked() { let _ = self.command_manager.generate_command_str(); } }); }); }); ui.separator(); ui.horizontal(|ui| { ui.label("Command"); ui.add(egui::Checkbox::without_text(&mut self.display_command)); }); ui.horizontal_wrapped(|ui| { if let Some(l) = self.launcher_manager.get_current() { if ui.add(egui::SelectableLabel::new(false, &l.name)).clicked() { self.launcher_manager.remove_current(); self.command_manager.remove_launcher(); } } else { ui.label("Select a launcher plz"); } if let Some(iwad) = self.iwad_manager.get_current() { if ui .add(egui::SelectableLabel::new(false, &iwad.name)) .clicked() { self.iwad_manager.remove_current(); self.command_manager.remove_iwad(); } } else { ui.label("Select an iwad plz"); } let mut remove: Option = None; for (pos, pwad) in self.pwad_manager.get_current().iter().enumerate() { if ui .add(egui::SelectableLabel::new(false, &pwad.name)) .clicked() { remove = Some(pos); break; } } if let Some(r) = remove { self.pwad_manager.remove_current(r); self.command_manager .add_pwads(&self.pwad_manager.get_current()) } }); if self.display_command { ui.horizontal(|ui| { let mut text = self.command_manager.command_string.clone(); let window_size = frame.info().window_info.size; ui.add(egui::TextEdit::multiline(&mut text).desired_width(window_size[0])); }); } }); egui::Window::new("Add WAD or Launcher") .open(&mut self.add_stuff_window_displayed) .show(ctx, |ui| { ui.horizontal(|ui| { let name_label = ui.label("Name"); ui.text_edit_singleline(&mut self.add_name) .labelled_by(name_label.id); }); ui.horizontal(|ui| { ui.label("Path"); ui.label( self.selected_file_path .clone() .into_os_string() .into_string() .unwrap(), ); if ui.button("Find").clicked() { if let Some(path) = rfd::FileDialog::new().pick_file() { self.selected_file_path = path; } } }); ui.horizontal(|ui| { ui.label("What sort of file is it?"); if ui .add(egui::SelectableLabel::new( self.selected_file_type == FileType::Launcher, "Launcher", )) .clicked() { self.selected_file_type = FileType::Launcher } if ui .add(egui::SelectableLabel::new( self.selected_file_type == FileType::Iwad, "IWAD", )) .clicked() { self.selected_file_type = FileType::Iwad } if ui .add(egui::SelectableLabel::new( self.selected_file_type == FileType::Pwad, "PWAD", )) .clicked() { self.selected_file_type = FileType::Pwad } }); if ui.button("Add!").clicked() { if self.add_name.is_empty() || self.selected_file_path.as_os_str().is_empty() { return; } match self.selected_file_type { FileType::Iwad => { self.iwad_manager.add(&WadInfo { name: self.add_name.clone(), path: self.selected_file_path.clone(), }); } FileType::Pwad => { self.pwad_manager.add(&WadInfo { name: self.add_name.clone(), path: self.selected_file_path.clone(), }); } FileType::Launcher => { self.launcher_manager.add(&LauncherInfo { name: self.add_name.clone(), path: self.selected_file_path.clone(), }); } } } }); } }