diff --git a/emulator/Cargo.toml b/emulator/Cargo.toml index 722882e..f4cf832 100644 --- a/emulator/Cargo.toml +++ b/emulator/Cargo.toml @@ -17,14 +17,23 @@ required-features = ["config"] common = { path = "../common" } assembler = { path = "../assembler" } dsa_editor = { path = "../dsa_editor" } -eframe = "0.31.1" +eframe = { version = "0.31.1" } egui = "0.31.1" -rfd = "0.15.3" dirs = "6.0.0" discord-presence = { version = "1.6.0", optional = true } toml = { version = "0.8.23", optional = true } serde = { version = "1.0.219", features = ["derive"], optional = true } +egui_file = "0.22.1" + +# Add support for Android for the fun of it. +[target.'cfg(target_os = "android")'.dependencies] winit = { version = "0.30.11", features = ["android-native-activity"] } +# jni = "0.21.1" + + +[target.'cfg(target_os = "android")'.dependencies.eframe] +version = "0.31.1" +features = ["android-native-activity"] [features] default = ["config"] diff --git a/emulator/src/emulator/ui/editor.rs b/emulator/src/emulator/ui/editor.rs index 98cc1c2..45a74bd 100644 --- a/emulator/src/emulator/ui/editor.rs +++ b/emulator/src/emulator/ui/editor.rs @@ -1,10 +1,14 @@ -use std::{ffi::OsStr, path::PathBuf, sync::mpsc::Sender}; +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, + sync::mpsc::Sender, +}; use common::prelude::Instruction; use egui::{Align, Context, Key, Layout, Ui}; -use rfd::FileDialog; use dsa_editor::{CodeEditor, ColorTheme, Syntax}; +use egui_file::FileDialog; use crate::emulator::{ system::model::{Command, State}, @@ -29,6 +33,10 @@ pub struct Editor { cursor_col: usize, cursor_line: usize, + // file dialogs + open_file_dialog: Option, + save_file_dialog: Option, + // other visible: bool, sender: Sender, @@ -98,6 +106,8 @@ impl Editor { load_offset: 0, offset_str: String::new(), error: None, + open_file_dialog: None, + save_file_dialog: None, } } @@ -130,36 +140,31 @@ impl Editor { } fn save(&mut self) { - let work_dir = std::env::current_dir().unwrap_or_else(|_| { - dirs::home_dir().expect( - "Couldn't get your current working directory or your home directory.", - ) - }); - - if let Some(path) = &self.path { - if let Err(why) = std::fs::write(path, &self.text) { - self.error = Some(format!("Failed to save file: {why}")); - return; - } - - self.buffer = self.text.clone(); - self.unsaved = false; - return; + if self.open_file_dialog.is_some() { + // TODO: Flash an error stating you can only have one menu open at once. + self.open_file_dialog = None; } - if let Some(path) = FileDialog::new() - .add_filter("Assembly Files or Binaries", &["dsa", "dsb"]) - .add_filter("all", &["*"]) - .set_directory(&work_dir) - .save_file() - { - if let Err(why) = std::fs::write(&path, &self.text) { + if let Some(path) = &self.path { + // Save to existing path + if let Err(why) = std::fs::write(path, &self.text) { self.error = Some(format!("Failed to save file: {why}")); } else { - self.path = Some(path); - self.buffer = self.text.clone(); self.unsaved = false; } + } else { + // Open the save dialog. + let work_dir = std::env::current_dir().unwrap_or_else(|_| { + dirs::home_dir().expect( + "Couldn't get your current working directory or your home directory.", + ) + }); + + if self.save_file_dialog.is_none() { + let mut dialog = FileDialog::save_file(Some(work_dir)); + dialog.open(); + self.save_file_dialog = Some(dialog); + } } } @@ -170,26 +175,93 @@ impl Editor { ) }); - if let Some(path) = FileDialog::new() - .add_filter("Assembly Files or Binaries", &["dsa", "dsb"]) - .add_filter("all", &["*"]) - .set_directory(&work_dir) - .pick_file() - { - if let Ok(contents) = std::fs::read_to_string(&path) { - self.path = Some(path.clone()); - self.text.clone_from(&contents); - self.buffer = contents; - self.unsaved = false; - } + if self.save_file_dialog.is_some() { + // TODO: Flash an error stating you can only have one menu open at once. + self.save_file_dialog = None; + } - std::env::set_current_dir( - path.parent().expect("A file should be in a directory!"), - ) - .expect("ERROR: Failed to set current working directory."); + if self.open_file_dialog.is_none() { + if let Some(p) = &self.path { + let path = p.parent().map(Path::to_path_buf); + let mut dialog = FileDialog::open_file(path); + dialog.open(); + self.open_file_dialog = Some(dialog); + } else { + let mut dialog = FileDialog::open_file(Some(work_dir)); + dialog.open(); + self.open_file_dialog = Some(dialog); + } } } + fn handle_file_dialogs(&mut self, ctx: &egui::Context) { + // Handle open dialog + if let Some(dialog) = &mut self.open_file_dialog { + if dialog.show(ctx).selected() { + if let Some(file) = dialog.path() { + match std::fs::read_to_string(file) { + Ok(content) => { + self.text = content; + self.path = Some(file.to_path_buf()); + self.unsaved = false; + self.error = None; + } + Err(e) => { + self.error = Some(format!("Failed to read file: {e}")); + } + } + } + self.open_file_dialog = None; + } + } + + // Handle save dialog + if let Some(dialog) = &mut self.save_file_dialog { + if dialog.show(ctx).selected() { + if let Some(file) = dialog.path() { + match std::fs::write(file, &self.text) { + Ok(()) => { + self.path = Some(file.to_path_buf()); + self.unsaved = false; + self.error = None; + } + Err(e) => { + self.error = Some(format!("Failed to save file: {e}")); + } + } + } + self.save_file_dialog = None; + } + } + } + + // fn open(&mut self) { + // let work_dir = std::env::current_dir().unwrap_or_else(|_| { + // dirs::home_dir().expect( + // "Couldn't get your current working directory or your home directory.", + // ) + // }); + + // if let Some(path) = FileDialog::new() + // .add_filter("Assembly Files or Binaries", &["dsa", "dsb"]) + // .add_filter("all", &["*"]) + // .set_directory(&work_dir) + // .pick_file() + // { + // if let Ok(contents) = std::fs::read_to_string(&path) { + // self.path = Some(path.clone()); + // self.text.clone_from(&contents); + // self.buffer = contents; + // self.unsaved = false; + // } + + // std::env::set_current_dir( + // path.parent().expect("A file should be in a directory!"), + // ) + // .expect("ERROR: Failed to set current working directory."); + // } + // } + fn render_output(&self, _state: &mut State, ui: &mut Ui, _ctx: &Context) { // Output area with synchronized scrolling egui::ScrollArea::vertical() @@ -316,7 +388,9 @@ impl Editor { }); } - fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) { + fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, ctx: &Context) { + self.handle_file_dialogs(ctx); + ui.horizontal(|ui| { ui.label(format!("File type: {}", self.extension())); ui.label(format!("Filename: {}", self.filename()));