aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2026-01-24 16:30:14 +0000
committers-ol <s+removethis@s-ol.nu>2026-01-24 17:03:27 +0000
commit360e88a0c899781a80b5215b7ee3851fb66188d9 (patch)
treeaba35d9242109624a8a370bf18f4e163bdb76ada /src
parentmove file state into App (diff)
downloadnodetoy-360e88a0c899781a80b5215b7ee3851fb66188d9.tar.gz
nodetoy-360e88a0c899781a80b5215b7ee3851fb66188d9.zip
add LICENSE, about modal
Diffstat (limited to 'src')
-rw-r--r--src/main.rs79
1 files changed, 77 insertions, 2 deletions
diff --git a/src/main.rs b/src/main.rs
index 8b3241e..6dfd833 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -229,6 +229,8 @@ pub struct App {
viewer: Viewer,
preview: preview::Preview,
+ show_about: bool,
+
#[cfg(target_arch = "wasm32")]
save_as_dialog: Option<String>,
}
@@ -289,26 +291,89 @@ impl App {
.and_then(|data| serde_json::from_str(&data).ok())
.unwrap_or_default();
+ let show_about = cx
+ .storage
+ .and_then(|storage| storage.get_string("about_closed"))
+ .map(|_| false)
+ .unwrap_or(true);
+
Self {
snarl,
filename: None,
viewer: Default::default(),
preview: preview::Preview::new(cx).expect("Failed to init preview"),
+ show_about,
#[cfg(target_arch = "wasm32")]
save_as_dialog: None,
}
}
+
+ fn about_modal(ui: &mut egui::Ui) {
+ ui.heading("About Nodetoy");
+
+ ui.horizontal_wrapped(|ui| {
+ ui.spacing_mut().item_spacing.x = 0.0;
+ ui.label("Nodetoy is a node-based Fragment Shader editor inspired by ");
+ ui.hyperlink_to("Shadertoy", "https://shadertoy.com/");
+ ui.label(".");
+ });
+
+ ui.horizontal_wrapped(|ui| {
+ ui.spacing_mut().item_spacing.x = 0.0;
+ ui.label("Unlike most node-based shader editors, it's not intended as a high-level tool for \
+ authoring materials in standard graphics rendering pipelines, but rather follows the \
+ shading language GLSL closely to allow learning from technical resources like ");
+ ui.hyperlink_to("the Book of Shaders", "https://thebookofshaders.com/");
+ ui.label(" or ");
+ ui.hyperlink_to("Inigo Quilez' articles","https://iquilezles.org/articles/");
+ ui.label(" on Signed Distance Fields and Raymarching without needing to write in the textual \
+ syntax of the language.");
+ });
+
+ ui.horizontal_wrapped(|ui| {
+ ui.spacing_mut().item_spacing.x = 0.0;
+ ui.label(
+ "Nodetoy is (MIT licensed) open-source software. The source code is available ",
+ );
+ ui.hyperlink_to("here", "https://git.s-ol.nu/nodetoy/");
+ ui.label(".");
+ });
+
+ ui.with_layout(egui::Layout::top_down(egui::Align::Max), |ui| {
+ ui.set_opacity(0.7);
+ ui.spacing_mut().item_spacing.y = 0.0;
+ ui.label("© 2025 s-ol bekic");
+ ui.hyperlink_to("https://s-ol.nu", "https://s-ol.nu");
+ ui.hyperlink_to("@s_ol@merveilles.town", "https://merveilles.town/@s_ol");
+ });
+
+ ui.separator();
+
+ egui::Sides::new().show(
+ ui,
+ |_ui| {},
+ |ui| {
+ if ui.button("Close").clicked() {
+ ui.close();
+ }
+ },
+ );
+ }
}
impl eframe::App for App {
- fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
+ fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
egui::MenuBar::new().ui(ui, |ui| {
egui::widgets::global_theme_preference_switch(ui);
let mut do_save = false;
+ if ui.button("About").clicked() {
+ self.show_about = true;
+ }
+
ui.menu_button("File", |ui| {
if ui.button("Open").clicked() {
#[cfg(not(target_arch = "wasm32"))]
@@ -378,7 +443,6 @@ impl eframe::App for App {
self.snarl = Snarl::default();
self.filename = None;
}
-
#[cfg(not(target_arch = "wasm32"))]
if ui.button("Quit").clicked() {
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
@@ -487,6 +551,17 @@ impl eframe::App for App {
}
});
});
+
+ if self.show_about {
+ let id = egui::Id::new("about");
+ let modal = egui::Modal::new(id).show(ctx, Self::about_modal);
+ if modal.should_close() {
+ self.show_about = false;
+ frame
+ .storage_mut()
+ .map(|storage| storage.set_string("about_closed", "true".to_string()));
+ }
+ }
}
fn save(&mut self, storage: &mut dyn eframe::Storage) {