progress
This commit is contained in:
+192
-56
@@ -1,18 +1,36 @@
|
||||
use egui::RichText;
|
||||
|
||||
use crate::{
|
||||
PROJECT_FOLDER, RightPanelContent,
|
||||
main_editor::MainEditor,
|
||||
note::Note,
|
||||
object::ObjectInstance,
|
||||
template::{FieldType, Template},
|
||||
content_editor::MainEditor,
|
||||
editors::{
|
||||
asset_editor::Asset, content_editor::ContentSection, object_editor::ObjectInstance,
|
||||
tags::Tag, template_editor::Template,
|
||||
},
|
||||
note_editor::Note,
|
||||
};
|
||||
|
||||
pub struct Explorer {}
|
||||
pub struct Explorer {
|
||||
templates: Vec<Template>,
|
||||
objects: Vec<ObjectInstance>,
|
||||
notes: Vec<Note>,
|
||||
documents: Vec<MainEditor>,
|
||||
tags: Vec<Tag>,
|
||||
assets: Vec<Asset>,
|
||||
}
|
||||
|
||||
impl Explorer {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
Self {
|
||||
templates: Vec::new(),
|
||||
objects: Vec::new(),
|
||||
notes: Vec::new(),
|
||||
documents: Vec::new(),
|
||||
tags: Vec::new(),
|
||||
assets: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn objects(&self) -> Vec<ObjectInstance> {
|
||||
self.objects.clone()
|
||||
}
|
||||
|
||||
pub fn ui(
|
||||
@@ -21,14 +39,11 @@ impl Explorer {
|
||||
load_doc: &mut Option<MainEditor>,
|
||||
ui: &mut egui::Ui,
|
||||
) {
|
||||
let (templates, objects) = match Self::load_templates() {
|
||||
Ok((templates, objects)) => (templates, objects),
|
||||
Err(e) => {
|
||||
eprintln!("Failed to load project: {e}");
|
||||
ui.label(RichText::new("Failed to load project").color(egui::Color32::RED));
|
||||
return;
|
||||
}
|
||||
};
|
||||
self.load_templates().expect("Failed to load templates");
|
||||
self.load_objects().expect("Failed to load objects");
|
||||
self.load_notes().expect("Failed to load notes");
|
||||
self.load_documents().expect("Failed to load documents");
|
||||
self.load_assets().expect("Failed to load assets");
|
||||
|
||||
ui.vertical(|ui| {
|
||||
egui::collapsing_header::CollapsingState::load_with_default_open(
|
||||
@@ -40,18 +55,12 @@ impl Explorer {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Templates");
|
||||
if ui.button("+").clicked() {
|
||||
*to_load = Some(RightPanelContent::Template {
|
||||
template: Box::new(Template::default()),
|
||||
new_field_name: Default::default(),
|
||||
new_field_type: FieldType::SingleLine,
|
||||
new_field_required: false,
|
||||
new_field_description: Default::default(),
|
||||
});
|
||||
*to_load = Some(RightPanelContent::template(Some(Template::default())));
|
||||
}
|
||||
});
|
||||
})
|
||||
.body(|ui| {
|
||||
for template in &templates {
|
||||
for template in &self.templates {
|
||||
let id = ui.make_persistent_id(template.name.clone());
|
||||
egui::collapsing_header::CollapsingState::load_with_default_open(
|
||||
ui.ctx(),
|
||||
@@ -66,22 +75,21 @@ impl Explorer {
|
||||
|
||||
// create a new object based on this template
|
||||
if ui.button("+").clicked() {
|
||||
*to_load = Some(RightPanelContent::Object {
|
||||
object: Box::new(ObjectInstance::new(template)),
|
||||
});
|
||||
*to_load = Some(RightPanelContent::object(Some(ObjectInstance::new(
|
||||
template,
|
||||
))));
|
||||
}
|
||||
})
|
||||
.body(|ui| {
|
||||
for object in &objects {
|
||||
for object in &self.objects {
|
||||
if object.template_id == template.id {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(10.0);
|
||||
|
||||
// load the object
|
||||
if ui.selectable_label(false, &object.name).clicked() {
|
||||
*to_load = Some(RightPanelContent::Object {
|
||||
object: Box::new(object.clone()),
|
||||
});
|
||||
*to_load =
|
||||
Some(RightPanelContent::object(Some(object.clone())));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -90,8 +98,6 @@ impl Explorer {
|
||||
}
|
||||
});
|
||||
|
||||
let notes = Self::load_notes().unwrap();
|
||||
|
||||
egui::collapsing_header::CollapsingState::load_with_default_open(
|
||||
ui.ctx(),
|
||||
ui.make_persistent_id("notes"),
|
||||
@@ -101,37 +107,93 @@ impl Explorer {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Notes");
|
||||
if ui.button("+").clicked() {
|
||||
*to_load = Some(RightPanelContent::Note {
|
||||
note: Box::new(Note::default()),
|
||||
});
|
||||
*to_load = Some(RightPanelContent::note(Some(Note::default())));
|
||||
}
|
||||
});
|
||||
})
|
||||
.body(|ui| {
|
||||
for note in ¬es {
|
||||
for note in &self.notes {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(10.0);
|
||||
|
||||
// load the note
|
||||
if ui.selectable_label(false, ¬e.name).clicked() {
|
||||
*to_load = Some(RightPanelContent::Note {
|
||||
note: Box::new(note.clone()),
|
||||
});
|
||||
*to_load = Some(RightPanelContent::note(Some(note.clone())));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let documents = Self::load_documents().unwrap();
|
||||
egui::collapsing_header::CollapsingState::load_with_default_open(
|
||||
ui.ctx(),
|
||||
ui.make_persistent_id("projects"),
|
||||
true,
|
||||
)
|
||||
.show_header(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Projects");
|
||||
if ui.button("+").clicked() {
|
||||
*load_doc = Some(MainEditor::open(ContentSection::new()));
|
||||
}
|
||||
});
|
||||
})
|
||||
.body(|ui| {
|
||||
// Convert MainEditor vec to ContentSection vec
|
||||
let content_sections: Vec<ContentSection> = self
|
||||
.documents
|
||||
.iter()
|
||||
.map(|doc| doc.content.clone())
|
||||
.collect();
|
||||
|
||||
egui::CollapsingHeader::new("Projects").show(ui, |ui| {
|
||||
for document in &documents {
|
||||
Self::render_document_tree(ui, &content_sections, None, load_doc);
|
||||
});
|
||||
|
||||
self.tags = Tag::load_all();
|
||||
|
||||
egui::collapsing_header::CollapsingState::load_with_default_open(
|
||||
ui.ctx(),
|
||||
ui.make_persistent_id("tags"),
|
||||
true,
|
||||
)
|
||||
.show_header(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Tags");
|
||||
if ui.button("+").clicked() {
|
||||
*to_load = Some(RightPanelContent::Tag(Tag::default()));
|
||||
}
|
||||
});
|
||||
})
|
||||
.body(|ui| {
|
||||
for tag in &mut self.tags {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(10.0);
|
||||
|
||||
// load the document
|
||||
if ui.selectable_label(false, &document.name).clicked() {
|
||||
*load_doc = Some(document.clone());
|
||||
// load the tag
|
||||
if tag.list_ui(ui).clicked() {
|
||||
*to_load = Some(RightPanelContent::Tag(tag.clone()));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
egui::collapsing_header::CollapsingState::load_with_default_open(
|
||||
ui.ctx(),
|
||||
ui.make_persistent_id("assets"),
|
||||
true,
|
||||
)
|
||||
.show_header(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Assets");
|
||||
});
|
||||
})
|
||||
.body(|ui| {
|
||||
for asset in &mut self.assets {
|
||||
ui.horizontal(|ui| {
|
||||
ui.add_space(10.0);
|
||||
|
||||
// load the asset
|
||||
if ui.selectable_label(false, &asset.name).clicked() {
|
||||
*to_load = Some(RightPanelContent::Asset(Box::new(asset.clone())));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -139,10 +201,56 @@ impl Explorer {
|
||||
});
|
||||
}
|
||||
|
||||
fn load_templates() -> std::io::Result<(Vec<Template>, Vec<ObjectInstance>)> {
|
||||
let mut templates = Vec::new();
|
||||
let mut objects = Vec::new();
|
||||
/// Recursively renders a tree of documents.
|
||||
///
|
||||
/// Each document is represented by a single element in the `documents` array.
|
||||
/// The `parent_id` parameter is used to filter out documents that do not have the current
|
||||
/// parent. If `parent_id` is `None`, all documents are rendered.
|
||||
///
|
||||
/// `load_doc` is a mutable reference to a `MainEditor`. When a document is clicked, it
|
||||
/// is loaded into the `MainEditor` and returned as `Some`.
|
||||
fn render_document_tree(
|
||||
ui: &mut egui::Ui,
|
||||
documents: &[ContentSection],
|
||||
parent_id: Option<&str>,
|
||||
load_doc: &mut Option<MainEditor>,
|
||||
) {
|
||||
// Filter documents that have the current parent (or no parent if this is the root)
|
||||
let child_docs: Vec<&ContentSection> = documents
|
||||
.iter()
|
||||
.filter(|doc| doc.parent.as_deref() == parent_id)
|
||||
.collect();
|
||||
|
||||
for doc in child_docs {
|
||||
egui::collapsing_header::CollapsingState::load_with_default_open(
|
||||
ui.ctx(),
|
||||
ui.make_persistent_id(&doc.id),
|
||||
false,
|
||||
)
|
||||
.show_header(ui, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
// Document title
|
||||
if ui.selectable_label(false, &doc.title).clicked() {
|
||||
*load_doc = Some(MainEditor::open(doc.clone()));
|
||||
}
|
||||
|
||||
// Add child button
|
||||
if ui.button("+").clicked() {
|
||||
let child = doc.create_child();
|
||||
*load_doc = Some(MainEditor::open(child));
|
||||
}
|
||||
});
|
||||
})
|
||||
.body(|ui| {
|
||||
// recursive call to render the next level of documents
|
||||
Self::render_document_tree(ui, documents, Some(&doc.id), load_doc);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// load templates from the templates folder
|
||||
fn load_templates(&mut self) -> std::io::Result<()> {
|
||||
let mut templates = Vec::new();
|
||||
for entry in std::fs::read_dir(PROJECT_FOLDER.join("templates")).unwrap() {
|
||||
let path = entry.unwrap().path();
|
||||
match Template::load(path.file_stem().unwrap().to_str().unwrap()) {
|
||||
@@ -150,7 +258,14 @@ impl Explorer {
|
||||
Err(err) => eprintln!("Could not parse file {path:?}: {err}"),
|
||||
}
|
||||
}
|
||||
self.templates = templates;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// load objects from the objects folder
|
||||
fn load_objects(&mut self) -> std::io::Result<()> {
|
||||
let mut objects = Vec::new();
|
||||
for entry in std::fs::read_dir(PROJECT_FOLDER.join("objects")).unwrap() {
|
||||
let path = entry.unwrap().path();
|
||||
match ObjectInstance::load(path.file_stem().unwrap().to_str().unwrap()) {
|
||||
@@ -158,11 +273,13 @@ impl Explorer {
|
||||
Err(err) => eprintln!("Could not parse file {path:?}: {err}"),
|
||||
}
|
||||
}
|
||||
self.objects = objects;
|
||||
|
||||
Ok((templates, objects))
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_notes() -> std::io::Result<Vec<Note>> {
|
||||
// load notes from the notes folder
|
||||
fn load_notes(&mut self) -> std::io::Result<()> {
|
||||
let mut notes = Vec::new();
|
||||
|
||||
for entry in std::fs::read_dir(PROJECT_FOLDER.join("notes")).unwrap() {
|
||||
@@ -173,20 +290,39 @@ impl Explorer {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(notes)
|
||||
self.notes = notes;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_documents() -> std::io::Result<Vec<MainEditor>> {
|
||||
// load documents from the documents folder
|
||||
fn load_documents(&mut self) -> std::io::Result<()> {
|
||||
let mut documents = Vec::new();
|
||||
|
||||
for entry in std::fs::read_dir(PROJECT_FOLDER.join("documents")).unwrap() {
|
||||
let path = entry.unwrap().path();
|
||||
match MainEditor::load(path.file_stem().unwrap().to_str().unwrap()) {
|
||||
Ok(document) => documents.push(document),
|
||||
match ContentSection::load(path.file_stem().unwrap().to_str().unwrap()) {
|
||||
Ok(document) => documents.push(MainEditor::open(document)),
|
||||
Err(err) => eprintln!("Could not parse file {path:?}: {err}"),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(documents)
|
||||
self.documents = documents;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_assets(&mut self) -> std::io::Result<()> {
|
||||
let mut assets = Vec::new();
|
||||
for entry in std::fs::read_dir(PROJECT_FOLDER.join("assets")).unwrap() {
|
||||
let path = entry.unwrap().path();
|
||||
assets.push(Asset::open(
|
||||
path.file_stem().unwrap().to_str().unwrap().to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.assets = assets;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user