// Copyright (c) 2022 Marceline Cramer // SPDX-License-Identifier: AGPL-3.0-or-later use canary::{CursorEventKind, ScriptInstance}; use eframe::egui; use std::sync::{Arc, RwLock}; use std::time::Instant; fn main() { let args: Vec = std::env::args().collect(); let module_path = args .get(1) .expect("Please pass a path to a Canary script!") .to_owned(); let native_options = eframe::NativeOptions { ..Default::default() }; eframe::run_native( "Canary egui Harness", native_options, Box::new(move |cc| { cc.egui_ctx.set_visuals(egui::Visuals::dark()); Box::new(App::new(&module_path)) }), ); } type Script = Arc>>; struct App { script: Script, panels: Vec, next_idx: usize, last_update: Instant, bind_message_buf: String, } impl App { pub fn new(module_path: &str) -> Self { let runtime = canary::WasmtimeRuntime::new().unwrap(); let abi = canary::ScriptAbiImpl::default(); let module = std::fs::read(module_path).unwrap(); let script = runtime.load_module(abi, &module).unwrap(); Self { script: Arc::new(RwLock::new(script)), panels: vec![], next_idx: 0, last_update: Instant::now(), bind_message_buf: String::new(), } } } impl eframe::App for App { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { ctx.request_repaint(); let mut script = self.script.write().unwrap(); egui::SidePanel::left("left_panel").show(ctx, |ui| { let text_edit = egui::TextEdit::multiline(&mut self.bind_message_buf).code_editor(); ui.add(text_edit); if ui.button("Bind Panel").clicked() { let msg = self.bind_message_buf.as_bytes().to_vec(); let id = script.bind_panel(msg); let index = self.next_idx; self.next_idx += 1; let panel = Panel { script: self.script.to_owned(), id, index, msg_buf: String::new(), show_msg: false, }; self.panels.push(panel); } }); let dt = self.last_update.elapsed().as_secs_f32(); self.last_update = Instant::now(); for panel in self.panels.iter_mut() { script.update(panel.id, dt); panel.show(&mut *script, ctx); } } } pub struct Panel { pub script: Script, pub id: canary::PanelId, pub index: usize, pub msg_buf: String, pub show_msg: bool, } impl Panel { pub fn show(&mut self, script: &mut impl canary::ScriptInstance, ctx: &egui::Context) { let window_id = egui::Id::new(format!("panel_{}", self.index)); egui::Window::new("Panel").id(window_id).show(ctx, |ui| { egui::menu::bar(ui, |ui| { ui.checkbox(&mut self.show_msg, "Show Message Editor"); }); let size = egui::vec2(800.0, 800.0); let sense = egui::Sense { click: true, drag: true, focusable: true, }; let (rect, response) = ui.allocate_at_least(size, sense); if let Some(hover_pos) = response.hover_pos() { let local = (hover_pos - rect.left_top()) / rect.size(); let norm = local * 2.0 - egui::vec2(1.0, 1.0); let x = norm.x; let y = -norm.y; let pos = canary::Vec2 { x, y }; let kind = if response.drag_started() { CursorEventKind::Select } else if response.drag_released() { CursorEventKind::Deselect } else if response.dragged() { CursorEventKind::Drag } else { CursorEventKind::Hover }; script.on_cursor_event(self.id, kind, pos); } let texture = egui::TextureId::Managed(0); let uv = egui::pos2(0.0, 0.0); let mut mesh = egui::Mesh::with_texture(texture); script.draw(self.id, |commands| { for command in commands.iter() { let voff = mesh.vertices.len() as u32; match command { canary::DrawCommand::Mesh { vertices, indices } => { for v in vertices.iter() { use egui::epaint::Vertex; let pos = egui::pos2(v.position.x, -v.position.y); let pos = pos.to_vec2() / 2.0 + egui::vec2(0.5, 0.5); let pos = rect.left_top() + pos * rect.size(); let (r, g, b, a) = v.color.to_rgba_unmultiplied(); let color = egui::Color32::from_rgba_unmultiplied(r, g, b, a); let v = Vertex { pos, uv, color }; mesh.vertices.push(v); } for i in indices.iter() { mesh.indices.push(i + voff); } } _ => unimplemented!(), } } }); let painter = ui.painter_at(rect); let shape = egui::Shape::mesh(mesh); painter.add(shape); response }); let msg_edit_id = egui::Id::new(format!("msg_edit_{}", self.index)); egui::Window::new("Message Editor") .open(&mut self.show_msg) .id(msg_edit_id) .show(ctx, |ui| { let text_edit = egui::TextEdit::multiline(&mut self.msg_buf).code_editor(); ui.add(text_edit); if ui.button("Send Message").clicked() { let msg = self.msg_buf.as_bytes().to_vec(); script.on_message(self.id, msg); } }); } }