diff --git a/Cargo.lock b/Cargo.lock index 1fa54b7..11d5175 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -766,14 +766,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "ferris-pad" -version = "0.1.0" -dependencies = [ - "iced", - "rfd", -] - [[package]] name = "find-msvc-tools" version = "0.1.6" @@ -3926,6 +3918,14 @@ dependencies = [ "syn", ] +[[package]] +name = "zoeae" +version = "0.1.0" +dependencies = [ + "iced", + "rfd", +] + [[package]] name = "zvariant" version = "5.8.0" diff --git a/README.md b/README.md index 4f49a5c..e403b90 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,6 @@ # Zoeae - - ⚠️ Under Construction ⚠️ - - This is a desktop application that I am building to learn about desktop development and the Rust programming language. My goal is to hopefully end up with something that can replace Notepad, but includes the ability to preview markdown. diff --git a/src/file.rs b/src/file.rs new file mode 100644 index 0000000..00487d9 --- /dev/null +++ b/src/file.rs @@ -0,0 +1,55 @@ +use std::{ + fs::{read_to_string, write}, + path::PathBuf, +}; + +pub fn save_file_to_disk(path: PathBuf, text: String) { + let save_result = write(path, text); + + match save_result { + Ok(_) => println!("Save file successfully"), + Err(err) => eprintln!("Error: {}", err), + } +} + +pub fn load_file_from_disk(path: PathBuf) -> String { + let read_result = read_to_string(path); + + match read_result { + Ok(contents) => contents, + Err(_) => String::new(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs::{self, remove_file}; + + #[test] + fn test_save_file_to_disk() { + let test_path = PathBuf::from("test_file.txt"); + let test_content = String::from("Hello, Chat!"); + + save_file_to_disk(test_path.clone(), test_content.clone()); + + let saved_content = fs::read_to_string(&test_path).unwrap(); + + assert_eq!(saved_content, test_content); + + let _ = remove_file(test_path); + } + + #[test] + fn test_load_file_from_disk() { + let test_path = PathBuf::from("test_file.txt"); + let test_content = String::from("Hello, Chat!"); + let _ = fs::write(test_path.clone(), test_content.clone()); + + let saved_content = load_file_from_disk(test_path.clone()); + + assert_eq!(saved_content, test_content); + + let _ = remove_file(test_path); + } +} diff --git a/src/fonts/CaskaydiaCoveNFM-Regular.ttf b/src/fonts/CaskaydiaCoveNFM-Regular.ttf new file mode 100644 index 0000000..e133aa4 Binary files /dev/null and b/src/fonts/CaskaydiaCoveNFM-Regular.ttf differ diff --git a/src/main.rs b/src/main.rs index ddaad54..5966b50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +1,37 @@ -use iced::Element; -use iced::widget::{column, pick_list, text_editor}; +mod file; + +use std::path::PathBuf; + +use iced::widget::{column, container, pick_list, row, text, text_editor}; +use iced::{Element, Font, Length, Theme}; use rfd::FileDialog; +const CUSTOM_FONT: Font = Font::with_name("CaskaydiaCove Nerd Font Mono"); + #[derive(Default)] struct State { + file_path: Option, content: text_editor::Content, selected_file_action: Option, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum FileAction { + Save, SaveAs, + Open, } impl FileAction { - const ALL: &'static [FileAction] = &[FileAction::SaveAs]; + const ALL: &'static [FileAction] = &[FileAction::SaveAs, FileAction::Open, FileAction::Save]; } impl std::fmt::Display for FileAction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + FileAction::Save => write!(f, "Save"), FileAction::SaveAs => write!(f, "Save As... "), + FileAction::Open => write!(f, "Open"), } } } @@ -31,6 +42,10 @@ enum Message { FileActionSelected(FileAction), } +fn theme(_state: &State) -> Theme { + Theme::Dark +} + fn view(state: &State) -> Element<'_, Message> { let file_menu = pick_list( FileAction::ALL, @@ -39,11 +54,73 @@ fn view(state: &State) -> Element<'_, Message> { ) .placeholder("File"); + let action_bar = container(row![file_menu]); + let editor = text_editor(&state.content) - .placeholder("Type something here...") + .height(Length::Fill) .on_action(Message::Edit); - column![file_menu, editor].into() + let editor_container = container(row![editor]).height(Length::Fill); + + let cursor_position = state.content.cursor().position; + let cursor_display_text = format!( + "Ln {}, Col {}", + cursor_position.line, cursor_position.column + ); + let cursor_text = text(cursor_display_text); + + let file_path_display_text = match &state.file_path { + Some(path) => path.to_string_lossy().to_string(), + None => String::new(), + }; + + let file_path_text = text(file_path_display_text); + + let status_bar = container(row![file_path_text, cursor_text].spacing(10)); + + container(column![action_bar, editor_container, status_bar].spacing(10)) + .padding(10) + .into() +} + +fn save_file(path: Option, text: String) -> Option { + let mut save_path = path.clone(); + + if path == None { + save_path = FileDialog::new().set_directory("/").save_file(); + } + + match save_path { + Some(p) => { + let sp = p.clone(); + file::save_file_to_disk(sp, text); + Some(p) + } + None => None, + } +} + +fn save_file_as(text: String) { + let files = FileDialog::new().set_directory("/").save_file(); + + match files { + Some(path) => file::save_file_to_disk(path, text), + None => { + println!("No file to save to"); + } + } +} + +fn open_file() -> (PathBuf, String) { + let file = FileDialog::new().set_directory("/").pick_file(); + + match file { + Some(path) => { + let content = file::load_file_from_disk(path.clone()); + (path, content) + } + None => (PathBuf::new(), String::new()), + } } fn update(state: &mut State, message: Message) { @@ -56,15 +133,31 @@ fn update(state: &mut State, message: Message) { state.selected_file_action = None; match action { - FileAction::SaveAs => { - let files = FileDialog::new().set_directory("/").save_file(); - println!("Selected file: {:?}", files); + FileAction::SaveAs => save_file_as(state.content.text()), + FileAction::Open => { + let (path, content) = open_file(); + state.content = text_editor::Content::with_text(&content); + state.file_path = Some(path); + } + FileAction::Save => { + state.file_path = save_file(state.file_path.clone(), state.content.text()); } } } } } +fn boot() -> State { + State::default() +} + pub fn main() -> iced::Result { - iced::run(update, view) + iced::application(boot, update, view) + .font(include_bytes!("./fonts/CaskaydiaCoveNFM-Regular.ttf")) + .theme(theme) + .settings(iced::Settings { + default_font: CUSTOM_FONT, + ..Default::default() + }) + .run() }