feat: added saving existing file or new file

This commit is contained in:
Stevan Freeborn
2026-01-09 07:18:43 -06:00
parent 12bcf1b521
commit 8e8409a7b5
5 changed files with 165 additions and 21 deletions
Generated
+8 -8
View File
@@ -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"
-4
View File
@@ -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.
+55
View File
@@ -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);
}
}
Binary file not shown.
+102 -9
View File
@@ -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<PathBuf>,
content: text_editor::Content,
selected_file_action: Option<FileAction>,
}
#[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<PathBuf>, text: String) -> Option<PathBuf> {
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()
}