Files
worldcoder/src/main.rs
T
2025-07-17 02:18:29 +01:00

236 lines
7.1 KiB
Rust

use std::{path::PathBuf, sync::LazyLock};
use egui::ScrollArea;
mod editors;
mod explorer;
mod scene;
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");
path
});
fn main() {
let app = Interface::new();
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]),
..Default::default()
};
let _ = eframe::run_native("Code Editor", options, Box::new(|_cc| Ok(Box::new(app))));
}
pub struct Interface {
// dialog: Option<egui_file::FileDialog>,
right_panel_content: RightPanelContent,
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 {
right_panel_content: RightPanelContent::None,
editor: content_editor::MainEditor::new(),
scene: scene::EditorScene::new(),
explorer: Explorer::new(),
}
}
fn render_top_panel(&self, ctx: &egui::Context) {
// Top bar with actions
egui::TopBottomPanel::top("top").show(ctx, |ui| {
ui.horizontal(|ui| {
// title widget
ui.heading("WorldCoder");
ui.separator();
// version
ui.label(VERSION)
});
});
}
fn render_left_panel(&mut self, ctx: &egui::Context) {
// Left panel - File browser
egui::SidePanel::left("file_browser")
.resizable(true)
.default_width(250.0)
.show(ctx, |ui| {
ui.heading("Project Files");
ui.separator();
let mut to_load: Option<RightPanelContent> = 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);
});
if let Some(to_load) = to_load {
self.right_panel_content = to_load;
}
if let Some(load_doc) = load_doc {
self.editor = load_doc;
self.editor.show_editor = true;
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) => {
// 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,
&mut self.explorer.objects(),
);
});
if let Some(right_panel) = right_panel {
self.right_panel_content = right_panel;
}
}
// an editable template
RightPanelContent::Template(template) => {
template.ui(ui, &mut new_instance);
}
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| {
ui.label("No template loaded to edit.");
if ui.button("Back").clicked() {
self.right_panel_content = RightPanelContent::None;
}
});
}
};
if let Some(new) = new_instance {
self.right_panel_content = new;
}
});
}
fn render_main_content(&mut self, ctx: &egui::Context) {
self.editor.ui(ctx);
self.scene.ui(ctx);
}
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;
ctx.set_visuals(visuals);
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()
}
}