2026-01-09 07:18:43 -06:00
|
|
|
mod file;
|
|
|
|
|
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
|
|
use iced::widget::{column, container, pick_list, row, text, text_editor};
|
2026-01-09 07:51:53 -06:00
|
|
|
use iced::window;
|
|
|
|
|
use iced::window::icon;
|
2026-01-09 07:18:43 -06:00
|
|
|
use iced::{Element, Font, Length, Theme};
|
2026-01-06 07:19:44 -06:00
|
|
|
use rfd::FileDialog;
|
|
|
|
|
|
2026-01-09 07:18:43 -06:00
|
|
|
const CUSTOM_FONT: Font = Font::with_name("CaskaydiaCove Nerd Font Mono");
|
|
|
|
|
|
2026-01-06 07:19:44 -06:00
|
|
|
#[derive(Default)]
|
|
|
|
|
struct State {
|
2026-01-09 07:18:43 -06:00
|
|
|
file_path: Option<PathBuf>,
|
2026-01-06 07:19:44 -06:00
|
|
|
content: text_editor::Content,
|
|
|
|
|
selected_file_action: Option<FileAction>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
enum FileAction {
|
2026-01-09 07:18:43 -06:00
|
|
|
Save,
|
2026-01-06 07:19:44 -06:00
|
|
|
SaveAs,
|
2026-01-09 07:18:43 -06:00
|
|
|
Open,
|
2026-01-06 07:19:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FileAction {
|
2026-01-09 07:18:43 -06:00
|
|
|
const ALL: &'static [FileAction] = &[FileAction::SaveAs, FileAction::Open, FileAction::Save];
|
2026-01-06 07:19:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl std::fmt::Display for FileAction {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
|
match self {
|
2026-01-09 07:18:43 -06:00
|
|
|
FileAction::Save => write!(f, "Save"),
|
2026-01-06 07:19:44 -06:00
|
|
|
FileAction::SaveAs => write!(f, "Save As... "),
|
2026-01-09 07:18:43 -06:00
|
|
|
FileAction::Open => write!(f, "Open"),
|
2026-01-06 07:19:44 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
enum Message {
|
|
|
|
|
Edit(text_editor::Action),
|
|
|
|
|
FileActionSelected(FileAction),
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-09 07:18:43 -06:00
|
|
|
fn theme(_state: &State) -> Theme {
|
|
|
|
|
Theme::Dark
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-06 07:19:44 -06:00
|
|
|
fn view(state: &State) -> Element<'_, Message> {
|
|
|
|
|
let file_menu = pick_list(
|
|
|
|
|
FileAction::ALL,
|
|
|
|
|
state.selected_file_action,
|
|
|
|
|
Message::FileActionSelected,
|
|
|
|
|
)
|
|
|
|
|
.placeholder("File");
|
|
|
|
|
|
2026-01-09 07:18:43 -06:00
|
|
|
let action_bar = container(row![file_menu]);
|
|
|
|
|
|
2026-01-06 07:19:44 -06:00
|
|
|
let editor = text_editor(&state.content)
|
2026-01-09 07:18:43 -06:00
|
|
|
.height(Length::Fill)
|
2026-01-06 07:19:44 -06:00
|
|
|
.on_action(Message::Edit);
|
|
|
|
|
|
2026-01-09 07:18:43 -06:00
|
|
|
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()),
|
|
|
|
|
}
|
2026-01-06 07:19:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn update(state: &mut State, message: Message) {
|
|
|
|
|
match message {
|
|
|
|
|
Message::Edit(action) => {
|
|
|
|
|
state.content.perform(action);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Message::FileActionSelected(action) => {
|
|
|
|
|
state.selected_file_action = None;
|
|
|
|
|
|
|
|
|
|
match action {
|
2026-01-09 07:18:43 -06:00
|
|
|
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());
|
2026-01-06 07:19:44 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-09 07:18:43 -06:00
|
|
|
fn boot() -> State {
|
|
|
|
|
State::default()
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-06 07:19:44 -06:00
|
|
|
pub fn main() -> iced::Result {
|
2026-01-09 07:18:43 -06:00
|
|
|
iced::application(boot, update, view)
|
|
|
|
|
.font(include_bytes!("./fonts/CaskaydiaCoveNFM-Regular.ttf"))
|
|
|
|
|
.theme(theme)
|
|
|
|
|
.settings(iced::Settings {
|
|
|
|
|
default_font: CUSTOM_FONT,
|
|
|
|
|
..Default::default()
|
|
|
|
|
})
|
2026-01-09 07:51:53 -06:00
|
|
|
.window(window::Settings {
|
|
|
|
|
icon: Some(
|
|
|
|
|
icon::from_file_data(include_bytes!("./images/icon.ico"), None)
|
|
|
|
|
.expect("Failed to load icon"),
|
|
|
|
|
),
|
|
|
|
|
..window::Settings::default()
|
|
|
|
|
})
|
2026-01-09 07:18:43 -06:00
|
|
|
.run()
|
2026-01-06 07:19:44 -06:00
|
|
|
}
|