aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2026-01-17 14:43:11 +0000
committers-ol <s+removethis@s-ol.nu>2026-01-17 20:29:57 +0000
commit8fe669078bfa93283021388453725c7a45fd6149 (patch)
tree074b8607fc7152f6223976019abf5b9129f40ad5 /src
parentsplit main into smaller modules (diff)
downloadnodetoy-8fe669078bfa93283021388453725c7a45fd6149.tar.gz
nodetoy-8fe669078bfa93283021388453725c7a45fd6149.zip
file I/O
Diffstat (limited to 'src')
-rw-r--r--src/main.rs86
-rw-r--r--src/wasm.rs29
2 files changed, 88 insertions, 27 deletions
diff --git a/src/main.rs b/src/main.rs
index 0ca84de..25c6062 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -13,6 +13,9 @@ mod node;
mod preview;
mod snarl_ext;
mod types;
+#[cfg(target_arch = "wasm32")]
+mod wasm;
+
use node::{AnyNode, ConcreteNode};
use snarl_ext::{Compilable, WithSignatures};
use types::{Dimension, FloatPrecision, ScalarType, Type};
@@ -199,8 +202,6 @@ impl App {
});
// let snarl = Snarl::new();
- info!("about to init");
-
App {
snarl,
viewer: Viewer { dirty: true },
@@ -212,24 +213,57 @@ impl App {
impl eframe::App for App {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
- // The top panel is often a good place for a menu bar:
-
egui::MenuBar::new().ui(ui, |ui| {
- #[cfg(not(target_arch = "wasm32"))]
- {
- ui.menu_button("File", |ui| {
- if ui.button("Quit").clicked() {
- ctx.send_viewport_cmd(egui::ViewportCommand::Close);
+ egui::widgets::global_theme_preference_switch(ui);
+
+ ui.menu_button("File", |ui| {
+ if ui.button("Open").clicked() {
+ #[cfg(not(target_arch = "wasm32"))]
+ if let Some(path) = rfd::FileDialog::new()
+ .add_filter("Nodetoy shader", &["nt.json"])
+ .pick_file()
+ {
+ let file = std::fs::File::open(path).unwrap();
+ self.snarl =
+ serde_json::from_reader::<_, Snarl<AnyNode>>(file).unwrap();
+ self.viewer.dirty = true;
}
- });
- ui.add_space(16.0);
- }
- egui::widgets::global_theme_preference_switch(ui);
+ #[cfg(target_arch = "wasm32")]
+ web_sys::window()
+ .unwrap()
+ .alert_with_message(
+ "To open an .nt.json file, please drag it onto the canvas.",
+ )
+ .unwrap();
+ }
- if ui.button("Clear All").clicked() {
- self.snarl = Snarl::default();
- }
+ #[cfg(not(target_arch = "wasm32"))]
+ if ui.button("Save").clicked() {
+ if let Some(path) = rfd::FileDialog::new()
+ .add_filter("Nodetoy shader", &["nt.json"])
+ .save_file()
+ {
+ let file = std::fs::File::create(path).unwrap();
+ serde_json::to_writer(file, &self.snarl).unwrap();
+ }
+ }
+
+ #[cfg(target_arch = "wasm32")]
+ if ui.button("Download").clicked() {
+ wasm::download_file(&self.snarl, "shader.nt.json")
+ .expect("successful download");
+ }
+
+ if ui.button("Clear All").clicked() {
+ self.snarl = Snarl::default();
+ }
+
+ #[cfg(not(target_arch = "wasm32"))]
+ if ui.button("Quit").clicked() {
+ ctx.send_viewport_cmd(egui::ViewportCommand::Close);
+ }
+ });
});
});
@@ -254,6 +288,13 @@ impl eframe::App for App {
}
}
}
+
+ ui.input(|i| {
+ if let Some(data) = i.raw.dropped_files.first().and_then(|f| f.bytes.as_ref()) {
+ self.snarl = serde_json::from_slice(data).unwrap();
+ self.viewer.dirty = true;
+ }
+ });
});
egui::SidePanel::right("preview").show(ctx, |ui| {
@@ -283,27 +324,18 @@ fn main() -> eframe::Result<()> {
};
eframe::run_native(
- "egui-snarl demo",
+ "nodetoy",
native_options,
Box::new(|cx| Ok(Box::new(App::new(cx)))),
)
}
-#[cfg(target_arch = "wasm32")]
-fn get_canvas_element() -> Option<web_sys::HtmlCanvasElement> {
- use eframe::wasm_bindgen::JsCast;
-
- let document = web_sys::window()?.document()?;
- let canvas = document.get_element_by_id("egui_snarl_demo")?;
- canvas.dyn_into::<web_sys::HtmlCanvasElement>().ok()
-}
-
// When compiling to web using trunk:
#[cfg(target_arch = "wasm32")]
fn main() {
eframe::WebLogger::init(log::LevelFilter::Debug).ok();
- let canvas = get_canvas_element().expect("Failed to find canvas with id 'egui_snarl_demo'");
+ let canvas = wasm::get_canvas_element().expect("failed to find canvas");
let web_options = eframe::WebOptions::default();
diff --git a/src/wasm.rs b/src/wasm.rs
new file mode 100644
index 0000000..6ce45e0
--- /dev/null
+++ b/src/wasm.rs
@@ -0,0 +1,29 @@
+use web_sys::js_sys::Array;
+use web_sys::wasm_bindgen::{JsCast, JsValue};
+use web_sys::{Blob, BlobPropertyBag, HtmlAnchorElement, HtmlCanvasElement, Url};
+
+pub fn get_canvas_element() -> Option<HtmlCanvasElement> {
+ let document = web_sys::window()?.document()?;
+ let canvas = document.get_element_by_id("nodetoy")?;
+ canvas.dyn_into::<HtmlCanvasElement>().ok()
+}
+
+pub fn download_file<T: serde::Serialize>(value: &T, filename: &str) -> Result<(), JsValue> {
+ let json = serde_json::to_string(value).unwrap();
+ let parts = Array::of1(&JsValue::from_str(&json));
+ let opts = BlobPropertyBag::new();
+ opts.set_type("application/json");
+
+ let blob = Blob::new_with_str_sequence_and_options(&parts, &opts)?;
+ let url = Url::create_object_url_with_blob(&blob)?;
+
+ let document = web_sys::window().unwrap().document().unwrap();
+ let a: HtmlAnchorElement = document.create_element("a")?.dyn_into()?;
+
+ a.set_href(&url);
+ a.set_download(filename);
+ a.click();
+
+ Url::revoke_object_url(&url)?;
+ Ok(())
+}