added notes, improved other features and removed most bugs

This commit is contained in:
2025-07-15 00:40:12 +01:00
parent 35ab726206
commit 76ec44d4e6
18 changed files with 777 additions and 488 deletions
+40 -123
View File
@@ -2,13 +2,19 @@ use std::{fs, path::PathBuf, sync::LazyLock};
use egui::{RichText, ScrollArea};
mod error;
mod explorer;
mod main_editor;
mod note;
mod object;
mod scene;
mod template;
use egui_file::DialogType;
use object::ObjectInstance;
use template::{FieldType, Template};
use crate::{explorer::Explorer, note::Note};
static PROJECT_FOLDER: LazyLock<PathBuf> = LazyLock::new(|| {
let mut path = std::env::current_dir().unwrap();
path.push("project");
@@ -27,98 +33,24 @@ fn main() {
}
pub struct Interface {
text: String,
dialog: Option<egui_file::FileDialog>,
right_panel_content: RightPanelContent,
editor: main_editor::MainEditor,
scene: scene::EditorScene,
explorer: Explorer,
}
impl Interface {
#[must_use]
pub fn new() -> Self {
Self {
text: "".to_string(),
dialog: None,
right_panel_content: RightPanelContent::None,
editor: main_editor::MainEditor::new(),
scene: scene::EditorScene::new(),
explorer: Explorer::new(),
}
}
fn show_directory(
&mut self,
ui: &mut egui::Ui,
path: &PathBuf,
depth: usize,
) -> std::io::Result<()> {
if !path.exists() {
return Ok(());
}
let indent = " ".repeat(depth);
let entries = fs::read_dir(path)?;
let mut dirs = Vec::new();
let mut files = Vec::new();
for entry in entries {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
dirs.push(path);
} else if let Some(ext) = path.extension() {
if ext == "json" {
// Only show JSON files
files.push(path);
}
}
}
// Sort directories and files alphabetically
dirs.sort();
files.sort();
// Show directories first
for dir in dirs {
let dir_name = dir
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("<invalid>")
.to_owned()
+ "/";
if egui::CollapsingHeader::new(dir_name.clone())
.default_open(depth < 1)
.show(ui, |ui| self.show_directory(ui, &dir, depth + 1))
.body_returned
.is_none()
{
ui.label(RichText::new(format!(
"{indent}❌ Error reading {dir_name}"
)));
}
}
// Then show files
for file in files {
if let Some(file_name) = file.file_name().and_then(|n| n.to_str()) {
let response = ui.horizontal(|ui| {
ui.label(" ".repeat(depth));
ui.selectable_label(false, file_name)
});
if response.inner.clicked() {
if let Ok(instance) = ObjectInstance::load(file.clone()) {
self.right_panel_content =
RightPanelContent::instance(Some(file.clone()), Some(instance));
} else if let Ok(template) = Template::load(file.clone()) {
self.right_panel_content =
RightPanelContent::template(Some(file.clone()), Some(template));
}
}
}
}
Ok(())
}
}
impl Default for Interface {
@@ -145,29 +77,25 @@ impl eframe::App for Interface {
if let Some(path) = dialog.path() {
if dialog.dialog_type() == DialogType::OpenFile {
// Handle file dialog for loading templates/instances
if let Ok(instance) = ObjectInstance::load(path.to_path_buf()) {
if let Ok(object) =
ObjectInstance::load(path.file_stem().unwrap().to_str().unwrap())
{
// Instance
self.right_panel_content = RightPanelContent::instance(
Some(path.to_path_buf()),
Some(instance),
);
self.right_panel_content = RightPanelContent::instance(Some(object));
self.dialog = None;
} else if let Ok(template) = Template::load(path.to_path_buf()) {
} else if let Ok(template) =
Template::load(path.file_stem().unwrap().to_str().unwrap())
{
// Template
self.right_panel_content = RightPanelContent::template(
Some(path.to_path_buf()),
Some(template),
);
self.right_panel_content = RightPanelContent::template(Some(template));
self.dialog = None;
}
} else if dialog.dialog_type() == DialogType::SaveFile {
// Handle file dialog for saving templates/instances
if let RightPanelContent::Template { template, .. } =
&mut self.right_panel_content
{
// set the save location and save
template.path = Some(path.to_path_buf());
if template.save().is_err() {
eprintln!("Failed to save template");
} else {
@@ -218,14 +146,15 @@ impl eframe::App for Interface {
ui.heading("Project Files");
ui.separator();
let mut to_load: Option<RightPanelContent> = None;
ScrollArea::vertical().show(ui, |ui| {
if let Err(e) = self.show_directory(ui, &PROJECT_FOLDER, 0) {
ui.label(
RichText::new(format!("Error reading directory: {e}"))
.color(egui::Color32::RED),
);
}
self.explorer.ui(&mut to_load, ui);
});
if let Some(to_load) = to_load {
self.right_panel_content = to_load;
}
});
// Main content area
@@ -234,19 +163,11 @@ impl eframe::App for Interface {
match &mut self.right_panel_content {
// an instance of a template
RightPanelContent::Instance { instance, path: _ } => {
RightPanelContent::Object { object } => {
// load template from path
let mut temp_path = instance.template_path.clone();
// check if path is relative
if !temp_path.is_absolute() {
temp_path = PROJECT_FOLDER.join(temp_path);
}
let template = Template::load(temp_path).unwrap();
let template = Template::load(&object.template_id).unwrap();
ScrollArea::vertical().show(ui, |ui| {
instance.ui(ui, &template);
object.ui(ui, &template);
});
}
@@ -266,6 +187,8 @@ impl eframe::App for Interface {
new_field_description,
),
RightPanelContent::Note { note } => note.ui(ui),
RightPanelContent::None => {
ui.centered_and_justified(|ui| {
ui.label("No template loaded to edit.");
@@ -283,6 +206,7 @@ impl eframe::App for Interface {
});
self.editor.ui(ctx);
self.scene.ui(ctx);
}
}
@@ -295,15 +219,17 @@ pub enum RightPanelContent {
new_field_required: bool,
new_field_description: String,
},
Instance {
instance: Box<ObjectInstance>,
path: Option<PathBuf>,
Object {
object: Box<ObjectInstance>,
},
Note {
note: Box<Note>,
},
None,
}
impl RightPanelContent {
fn template(path: Option<PathBuf>, template: Option<Template>) -> Self {
fn template(template: Option<Template>) -> Self {
Self::Template {
template: Box::new(template.unwrap_or_default()),
new_field_name: String::new(),
@@ -313,18 +239,9 @@ impl RightPanelContent {
}
}
fn instance(path: Option<PathBuf>, instance: Option<ObjectInstance>) -> Self {
Self::Instance {
instance: Box::new(instance.unwrap_or_default()),
path,
}
}
fn is_saved(&self) -> bool {
match self {
RightPanelContent::Instance { instance, path: _ } => instance.saved,
RightPanelContent::Template { template, .. } => template.path.is_some(),
RightPanelContent::None => false,
fn instance(instance: Option<ObjectInstance>) -> Self {
Self::Object {
object: Box::new(instance.unwrap_or_default()),
}
}
}