This commit is contained in:
2025-07-17 02:18:29 +01:00
parent fe8bbae68e
commit 224300f3ea
29 changed files with 1880 additions and 1439 deletions
+119 -144
View File
@@ -1,20 +1,22 @@
use std::{fs, path::PathBuf, sync::LazyLock};
use std::{path::PathBuf, sync::LazyLock};
use egui::{RichText, ScrollArea};
use egui::ScrollArea;
mod error;
mod editors;
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, main_editor::MainEditor, note::Note};
mod util;
use crate::{
editors::{
asset_editor::Asset, content_editor, note_editor, object_editor::ObjectInstance, tags::Tag,
template_editor::Template,
},
explorer::Explorer,
};
static VERSION: &str = "0.1.0";
static PROJECT_FOLDER: LazyLock<PathBuf> = LazyLock::new(|| {
let mut path = std::env::current_dir().unwrap();
path.push("project");
@@ -23,7 +25,6 @@ static PROJECT_FOLDER: LazyLock<PathBuf> = LazyLock::new(|| {
fn main() {
let app = Interface::new();
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
..Default::default()
@@ -33,111 +34,74 @@ fn main() {
}
pub struct Interface {
dialog: Option<egui_file::FileDialog>,
// dialog: Option<egui_file::FileDialog>,
right_panel_content: RightPanelContent,
editor: main_editor::MainEditor,
editor: content_editor::MainEditor,
scene: scene::EditorScene,
explorer: Explorer,
}
impl eframe::App for Interface {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui_extras::install_image_loaders(ctx);
self.configure_appearance(ctx);
self.render_top_panel(ctx);
self.render_left_panel(ctx);
self.render_right_panel(ctx);
self.render_main_content(ctx);
}
}
pub enum RightPanelContent {
Template(Box<Template>),
Object(Box<ObjectInstance>),
Note(Box<note_editor::Note>),
Tag(Tag),
Asset(Box<Asset>),
None,
}
impl RightPanelContent {
fn template(template: Option<Template>) -> Self {
Self::Template(Box::new(template.unwrap_or_default()))
}
fn object(instance: Option<ObjectInstance>) -> Self {
Self::Object(Box::new(instance.unwrap_or_default()))
}
fn note(note: Option<note_editor::Note>) -> Self {
Self::Note(Box::new(note.unwrap_or_default()))
}
}
impl Interface {
#[must_use]
pub fn new() -> Self {
Self {
dialog: None,
right_panel_content: RightPanelContent::None,
editor: main_editor::MainEditor::new(),
editor: content_editor::MainEditor::new(),
scene: scene::EditorScene::new(),
explorer: Explorer::new(),
}
}
}
impl Default for Interface {
fn default() -> Self {
Self::new()
}
}
// /home/zxq5/Pictures/logos and pfps/YT profile picture background.png
impl eframe::App for Interface {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui_extras::install_image_loaders(ctx);
{
let mut visuals = egui::Visuals::dark();
visuals.window_fill = egui::Color32::from_rgb(20, 20, 20);
visuals.panel_fill = egui::Color32::from_rgb(20, 20, 20);
ctx.set_visuals(visuals);
}
if let Some(dialog) = &mut self.dialog {
if dialog.show(ctx).selected() {
if let Some(path) = dialog.path() {
if dialog.dialog_type() == DialogType::OpenFile {
// Handle file dialog for loading templates/instances
if let Ok(object) =
ObjectInstance::load(path.file_stem().unwrap().to_str().unwrap())
{
// Instance
self.right_panel_content = RightPanelContent::instance(Some(object));
self.dialog = None;
} else if let Ok(template) =
Template::load(path.file_stem().unwrap().to_str().unwrap())
{
// 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
if template.save().is_err() {
eprintln!("Failed to save template");
} else {
self.dialog = None;
}
}
}
}
}
}
fn render_top_panel(&self, ctx: &egui::Context) {
// Top bar with actions
egui::TopBottomPanel::top("top").show(ctx, |ui| {
ui.horizontal(|ui| {
// Open Markdown Editor button
if ui.button("📝 Open Editor").clicked() {
self.editor.show_editor = true;
}
// title widget
ui.heading("WorldCoder");
ui.separator();
// create new template
if ui.button("New Template").clicked() {
self.right_panel_content = RightPanelContent::Template {
template: Box::new(Template::default()),
new_field_name: String::new(),
new_field_type: FieldType::SingleLine,
new_field_required: false,
new_field_description: String::new(),
};
}
// load instance or template from file
if ui.button("Load Template/Instance").clicked() {
self.dialog = Some(egui_file::FileDialog::open_file(Some(
PROJECT_FOLDER.clone(),
)));
self.dialog.as_mut().unwrap().open();
}
// version
ui.label(VERSION)
});
});
}
fn render_left_panel(&mut self, ctx: &egui::Context) {
// Left panel - File browser
egui::SidePanel::left("file_browser")
.resizable(true)
@@ -147,7 +111,7 @@ impl eframe::App for Interface {
ui.separator();
let mut to_load: Option<RightPanelContent> = None;
let mut load_doc: Option<MainEditor> = None;
let mut load_doc: Option<content_editor::MainEditor> = None;
ScrollArea::vertical().show(ui, |ui| {
self.explorer.ui(&mut to_load, &mut load_doc, ui);
@@ -163,21 +127,28 @@ impl eframe::App for Interface {
self.editor.show_preview = true;
}
});
}
fn render_right_panel(&mut self, ctx: &egui::Context) {
// Main content area
egui::SidePanel::right("templates").show(ctx, |ui| {
let mut new_instance: Option<RightPanelContent> = None;
match &mut self.right_panel_content {
// an instance of a template
RightPanelContent::Object { object } => {
RightPanelContent::Object(object) => {
// load template from path
let mut right_panel = None;
let template = Template::load(&object.template_id).unwrap();
ScrollArea::vertical().show(ui, |ui| {
object.ui(ui, &template, &mut right_panel);
object.ui(
ui,
&template,
&mut right_panel,
&mut self.explorer.objects(),
);
});
if let Some(right_panel) = right_panel {
@@ -186,22 +157,15 @@ impl eframe::App for Interface {
}
// an editable template
RightPanelContent::Template {
template,
new_field_name,
new_field_type,
new_field_required,
new_field_description,
} => template.ui(
ui,
&mut new_instance,
new_field_name,
new_field_type,
new_field_required,
new_field_description,
),
RightPanelContent::Template(template) => {
template.ui(ui, &mut new_instance);
}
RightPanelContent::Note { note } => note.ui(ui),
RightPanelContent::Note(note) => note.ui(ui),
RightPanelContent::Tag(tag) => tag.ui(ui),
RightPanelContent::Asset(asset) => asset.ui(ui),
RightPanelContent::None => {
ui.centered_and_justified(|ui| {
@@ -217,44 +181,55 @@ impl eframe::App for Interface {
self.right_panel_content = new;
}
});
}
fn render_main_content(&mut self, ctx: &egui::Context) {
self.editor.ui(ctx);
self.scene.ui(ctx);
}
}
pub enum RightPanelContent {
Template {
template: Box<Template>,
// fields to edit
new_field_name: String,
new_field_type: FieldType,
new_field_required: bool,
new_field_description: String,
},
Object {
object: Box<ObjectInstance>,
},
Note {
note: Box<Note>,
},
None,
}
fn configure_appearance(&self, ctx: &egui::Context) {
let mut visuals = egui::Visuals::dark();
visuals.window_fill = egui::Color32::from_rgb(20, 20, 20);
visuals.panel_fill = egui::Color32::from_rgb(20, 20, 20);
visuals.widgets.inactive.fg_stroke =
egui::Stroke::from((2.0, egui::Color32::from_rgb(255, 255, 255)));
visuals.widgets.inactive.bg_stroke =
egui::Stroke::from((2.0, egui::Color32::from_rgb(60, 60, 60)));
visuals.widgets.inactive.corner_radius = egui::CornerRadius::from(4);
visuals.widgets.inactive.bg_fill = egui::Color32::from_rgb(20, 20, 20);
visuals.widgets.inactive.weak_bg_fill = egui::Color32::from_rgb(20, 20, 20);
visuals.widgets.inactive.expansion = 2.0;
impl RightPanelContent {
fn template(template: Option<Template>) -> Self {
Self::Template {
template: Box::new(template.unwrap_or_default()),
new_field_name: String::new(),
new_field_type: FieldType::SingleLine,
new_field_required: false,
new_field_description: String::new(),
}
}
ctx.set_visuals(visuals);
fn instance(instance: Option<ObjectInstance>) -> Self {
Self::Object {
object: Box::new(instance.unwrap_or_default()),
}
let mut fonts = egui::FontDefinitions::default();
fonts.font_data.insert(
"JetBrains Mono Nerd Font".to_string(),
std::sync::Arc::new(egui::FontData::from_static(include_bytes!(
"/usr/local/share/fonts/j/JetBrainsMonoNerdFont_Regular.ttf",
))),
);
fonts
.families
.entry(egui::FontFamily::Proportional)
.or_default()
.insert(0, "JetBrains Mono Nerd Font".to_string());
fonts
.families
.entry(egui::FontFamily::Monospace)
.or_default()
.insert(0, "JetBrains Mono Nerd Font".to_string());
ctx.set_fonts(fonts);
}
}
impl Default for Interface {
fn default() -> Self {
Self::new()
}
}