// Copyright (c) 2022 Marceline Cramer // SPDX-License-Identifier: AGPL-3.0-or-later use canary::{CursorEventKind, Panel, Runtime, Script, PX_PER_MM}; use eframe::egui; 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 Sandbox", native_options, Box::new(move |cc| { cc.egui_ctx.set_visuals(egui::Visuals::dark()); Box::new(App::new(&module_path)) }), ); } struct App { script: Script, panels: Vec, next_idx: usize, last_update: Instant, protocol_buf: String, bind_message_buf: String, panel_bg: egui::Color32, } impl App { pub fn new(module_path: &str) -> Self { let backend = canary::backend::make_default_backend().unwrap(); let runtime = Runtime::new(backend).unwrap(); let module = std::fs::read(module_path).unwrap(); let script = runtime.load_module(&module).unwrap(); Self { script, panels: vec![], next_idx: 0, last_update: Instant::now(), protocol_buf: String::new(), bind_message_buf: String::new(), panel_bg: egui::Color32::TRANSPARENT, } } } impl eframe::App for App { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { ctx.request_repaint(); egui::SidePanel::left("left_panel").show(ctx, |ui| { ui.heading("New Panel"); ui.label("Protocol name:"); ui.text_edit_singleline(&mut self.protocol_buf); ui.label("Bind message:"); 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 panel = self.script.create_panel(&self.protocol_buf, msg).unwrap(); let index = self.next_idx; self.next_idx += 1; let panel = PanelWindow { panel, index, msg_buf: String::new(), show_msg: false, current_size: Default::default(), }; self.panels.push(panel); } ui.separator(); ui.heading("Global Settings"); ui.horizontal(|ui| { ui.label("Panel background color: "); ui.color_edit_button_srgba(&mut self.panel_bg); }); }); let dt = self.last_update.elapsed().as_secs_f32(); self.last_update = Instant::now(); for panel in self.panels.iter_mut() { panel.panel.update(dt); panel.show(self.panel_bg, ctx); } } } pub struct PanelWindow { pub panel: Panel, pub index: usize, pub msg_buf: String, pub show_msg: bool, pub current_size: egui::Vec2, } impl PanelWindow { pub fn show(&mut self, bg: egui::Color32, ctx: &egui::Context) { let frame = egui::Frame::window(&ctx.style()).fill(bg); let window_id = egui::Id::new(format!("panel_{}", self.index)); egui::Window::new("Panel") .frame(frame) .id(window_id) .show(ctx, |ui| { egui::menu::bar(ui, |ui| { ui.checkbox(&mut self.show_msg, "Show Message Editor"); }); let sense = egui::Sense { click: true, drag: true, focusable: true, }; let desired_size = ui.available_size(); let response = ui.allocate_response(desired_size, sense); let rect = response.rect; if rect.size() != self.current_size { let size = rect.size(); self.current_size = size; let size = canary::Vec2::new(size.x, size.y); self.panel.on_resize(size * PX_PER_MM); } if let Some(hover_pos) = response.hover_pos() { let local = (hover_pos - rect.left_top()) * PX_PER_MM; let pos = canary::Vec2::new(local.x, local.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 }; self.panel.on_cursor_event(kind, pos); } let texture = egui::TextureId::Managed(0); let uv = egui::pos2(0.0, 0.0); let mut mesh = egui::Mesh::with_texture(texture); let commands = self.panel.draw(); for command in commands.into_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 = v.position / PX_PER_MM; let pos = egui::pos2(pos.x, pos.y); let pos = pos + rect.left_top().to_vec2(); 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(); self.panel.on_message(msg); } }); } }