summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralyx <alyx@aleteoryx.me>2024-04-16 13:26:18 -0400
committeralyx <alyx@aleteoryx.me>2024-04-16 13:26:18 -0400
commitc6ba7674e6ccbed249c8633f5fbc1b735a57df17 (patch)
tree1174dffb66143f0eafdd1cee991b051ed450308f
parent8c64f6c4627d99b22a3fbe86d321aefe5a6e4a24 (diff)
downloadeframe-canvas-template-c6ba7674e6ccbed249c8633f5fbc1b735a57df17.tar.gz
eframe-canvas-template-c6ba7674e6ccbed249c8633f5fbc1b735a57df17.tar.bz2
eframe-canvas-template-c6ba7674e6ccbed249c8633f5fbc1b735a57df17.zip
-rw-r--r--Cargo.toml11
-rw-r--r--build.rs147
-rw-r--r--src/main.rs8
-rw-r--r--src/template.rs18
4 files changed, 184 insertions, 0 deletions
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..e01b707
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "eframe-template"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+anyhow = "1.0.82"
+eframe = "0.22"
+
+[build-dependencies]
+syn = { version = "2.0.59", features = ["full"] }
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..f67402f
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,147 @@
+#![feature(iter_intersperse)]
+
+use std::env;
+use std::fs;
+use std::panic;
+use std::io::Write;
+use std::path::Path;
+
+fn main() {
+ let src_dir = Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("src");
+ let tpl_loc = src_dir.join("template.rs");
+ let tpl_contents = fs::read_to_string(&tpl_loc).unwrap();
+ let mut tpl_indent = None;
+ let (tpl_head, tpl_tail): (Vec<_>, Vec<_>) = {
+ let mut tpl_filter = false;
+ let mut tpl_end = false;
+
+ tpl_contents.lines()
+ .filter(|line|
+ if !tpl_filter {
+ if line.trim().starts_with("// CODEGEN START") {
+ tpl_indent = Some(line.len() - line.trim_start().len());
+ tpl_filter = true;
+ }
+ true
+ } else {
+ if line.trim().starts_with("// CODEGEN END") {
+ tpl_filter = false;
+ true
+ } else { false }
+ })
+ .partition(|line|
+ if !tpl_end {
+ if line.trim().starts_with("// CODEGEN START") {
+ tpl_end = true;
+ }
+ true
+ } else {
+ false
+ })
+ };
+ let tpl_indent = " ".repeat(tpl_indent.unwrap());
+
+ let default_hook = panic::take_hook();
+ let panic_body = tpl_head.iter().chain(&tpl_tail).map(|a| *a).intersperse("\n").collect::<String>() + "\n";
+ let tpl_loc2 = tpl_loc.clone();
+ panic::set_hook(Box::new(move |pi| {
+ {
+ let mut tpl_fp = fs::File::create(&tpl_loc2).unwrap();
+ write!(tpl_fp, "{panic_body}").unwrap();
+ }
+ default_hook(pi);
+ }));
+
+
+ let mut tpl_fp = fs::File::create(&tpl_loc).unwrap();
+ writeln!(tpl_fp, "{}", tpl_head.join("\n")).unwrap();
+
+ let tpl_dir = src_dir.join("template");
+ for (n, ent) in tpl_dir.read_dir().unwrap().enumerate() {
+ let ent = ent.unwrap();
+ if !ent.file_type().unwrap().is_file() { continue; }
+ if !ent.file_name().as_encoded_bytes().ends_with(b".rs") { continue; }
+
+ let modname = format!("mod{n}");
+
+ let path = ent.path();
+ writeln!(tpl_fp, "{tpl_indent}#[path = \"{}\"] mod {modname};", path.display()).unwrap();
+
+ let file = syn::parse_file(&fs::read_to_string(ent.path()).unwrap()).unwrap();
+ for item in &file.items {
+ let syn::Item::Fn(func) = item
+ else { continue; };
+ let syn::Visibility::Public(_) = func.vis
+ else { continue; };
+
+ let name = func.sig.ident.to_string();
+
+ println!("{}: {}", path.display(), func.sig.ident);
+
+ if name.starts_with("win_") {
+ assert_eq!(func.sig.inputs.len(), 1, "Window function may only take one arg!");
+ let syn::FnArg::Typed(syn::PatType { ref ty, .. }) = func.sig.inputs[0]
+ else { panic!("Window function must take `_: &[mut] egui::Ui`, not `self`!"); };
+ let syn::Type::Reference(syn::TypeReference{ elem: ref ty, .. }) = **ty
+ else { panic!("Window function must take `_: &[mut] egui::Ui`!"); };
+ let syn::Type::Path(syn::TypePath{ path: syn::Path{ ref segments, .. }, .. }) = **ty
+ else { panic!("Window function must take `_: &[mut] egui::Ui`!"); };
+ assert_eq!(segments.last().unwrap().ident, "Ui", "Window function must take `_: &[mut] egui::Ui`!");
+
+ let name_tok = file.items.iter().find_map(|i| {
+ if let syn::Item::Const(c) = i {
+ let syn::Visibility::Public(_) = c.vis
+ else { return None; };
+ if c.ident == name.to_uppercase() { return Some(c.ident.to_string()); }
+ }
+ if let syn::Item::Static(s) = i {
+ let syn::Visibility::Public(_) = s.vis
+ else { return None; };
+ if s.ident == name.to_uppercase() { return Some(s.ident.to_string()); }
+ }
+ None
+ }).map(|a| modname.clone() + "::" + &a).unwrap_or_else(|| format!("\"{name}\""));
+
+ writeln!(tpl_fp, "{tpl_indent}egui::Window::new({name_tok}).show(ctx, {modname}::{name});").unwrap();
+ }
+ else if name.starts_with("ctx_") {
+ assert_eq!(func.sig.inputs.len(), 1, "Context function may only take one arg!");
+ let syn::FnArg::Typed(syn::PatType { ref ty, .. }) = func.sig.inputs[0]
+ else { panic!("Context function must take `_: &egui::Context`, not `self`!"); };
+ let syn::Type::Reference(syn::TypeReference{ mutability, elem: ref ty, .. }) = **ty
+ else { panic!("Context function must take `_: &egui::Context`!"); };
+ let syn::Type::Path(syn::TypePath{ path: syn::Path{ ref segments, .. }, .. }) = **ty
+ else { panic!("Context function must take `_: &egui::Context`!"); };
+ assert_eq!(segments.last().unwrap().ident, "Context", "Window function must take `_: &egui::Context`!");
+ assert!(mutability.is_none(), "Context function must take immutable reference!");
+
+ writeln!(tpl_fp, "{tpl_indent}{modname}::{name}(ctx);").unwrap();
+ }
+ else if name.starts_with("paint_bg_") {
+ assert_eq!(func.sig.inputs.len(), 1, "Painter function may only take one arg!");
+ let syn::FnArg::Typed(syn::PatType { ref ty, .. }) = func.sig.inputs[0]
+ else { panic!("Painter function must take `_: egui::Painter`, not `self`!"); };
+ let syn::Type::Path(syn::TypePath{ path: syn::Path{ ref segments, .. }, .. }) = **ty
+ else { panic!("Painter function must take `_: egui::Painter`!"); };
+ assert_eq!(segments.last().unwrap().ident, "Painter", "Painter function must take `_: egui::Painter`!");
+
+ writeln!(tpl_fp, "{tpl_indent}{modname}::{name}(ctx.layer_painter(egui::LayerId::background()));").unwrap();
+ }
+ else if name.starts_with("paint_dbg_") {
+ assert_eq!(func.sig.inputs.len(), 1, "Painter function may only take one arg!");
+ let syn::FnArg::Typed(syn::PatType { ref ty, .. }) = func.sig.inputs[0]
+ else { panic!("Painter function must take `_: egui::Painter`, not `self`!"); };
+ let syn::Type::Path(syn::TypePath{ path: syn::Path{ ref segments, .. }, .. }) = **ty
+ else { panic!("Painter function must take `_: egui::Painter`!"); };
+ assert_eq!(segments.last().unwrap().ident, "Painter", "Painter function must take `_: egui::Painter`!");
+
+ writeln!(tpl_fp, "{tpl_indent}{modname}::{name}(ctx.layer_painter(egui::LayerId::debug()));").unwrap();
+ }
+ }
+ }
+
+ writeln!(tpl_fp, "{}", tpl_tail.join("\n")).unwrap();
+
+ println!("cargo::rerun-if-changed=src/template/");
+ println!("cargo::rerun-if-changed=src/template.rs");
+}
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..76b3ff7
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,8 @@
+use eframe::Result;
+
+mod template;
+
+fn main() -> Result<()> {
+ let native_options = eframe::NativeOptions::default();
+ eframe::run_native("`eframe` Template", native_options, Box::new(template::TemplateApp::generate))
+}
diff --git a/src/template.rs b/src/template.rs
new file mode 100644
index 0000000..9e3695d
--- /dev/null
+++ b/src/template.rs
@@ -0,0 +1,18 @@
+#![allow(unused_variables)]
+
+use eframe::egui;
+
+pub struct TemplateApp;
+
+impl TemplateApp {
+ pub fn generate(cc: &eframe::CreationContext<'_>) -> Box<dyn eframe::App> {
+ Box::new(TemplateApp)
+ }
+}
+
+impl eframe::App for TemplateApp {
+ fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
+ // CODEGEN START - PUT CUSTOM CODE ABOVE AND BELOW
+ // CODEGEN END
+ }
+}