diff --git a/crates/egui/src/main.rs b/crates/egui/src/main.rs index c318d6b..68c5201 100644 --- a/crates/egui/src/main.rs +++ b/crates/egui/src/main.rs @@ -1,5 +1,6 @@ use canary::{CursorEventKind, ScriptInstance}; use eframe::egui; +use std::sync::{Arc, RwLock}; use std::time::Instant; fn main() { @@ -10,7 +11,6 @@ fn main() { .to_owned(); let native_options = eframe::NativeOptions { - multisampling: 8, ..Default::default() }; @@ -24,9 +24,12 @@ fn main() { ); } +type Script = Arc>>; + struct App { - script: canary::WasmtimeScript, - panels: Vec, + script: Script, + panels: Vec, + next_idx: usize, last_update: Instant, bind_message_buf: String, } @@ -39,8 +42,9 @@ impl App { let script = runtime.load_module(abi, &module).unwrap(); Self { - script, + script: Arc::new(RwLock::new(script)), panels: vec![], + next_idx: 0, last_update: Instant::now(), bind_message_buf: String::new(), } @@ -51,13 +55,26 @@ 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 panel = self.script.bind_panel(msg); + 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); } }); @@ -65,76 +82,107 @@ impl eframe::App for App { let dt = self.last_update.elapsed().as_secs_f32(); self.last_update = Instant::now(); - for (idx, panel) in self.panels.iter().enumerate() { - self.script.update(*panel, dt); - - let window_id = egui::Id::new(format!("panel_{}", idx)); - egui::Window::new("Panel").id(window_id).show(ctx, |ui| { - 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 - }; - - self.script.on_cursor_event(*panel, kind, pos); - } - - let texture = egui::TextureId::Managed(0); - let uv = egui::pos2(0.0, 0.0); - let mut mesh = egui::Mesh::with_texture(texture); - - self.script.draw(*panel, |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 - }); + 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); + } + }); + } +} diff --git a/src/lib.rs b/src/lib.rs index 5b619a6..cc3e300 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,7 @@ pub trait ScriptInstance { fn update(&mut self, panel: PanelId, dt: f32); fn draw(&mut self, panel: PanelId, f: impl FnOnce(&[DrawCommand])); fn on_cursor_event(&mut self, panel: PanelId, kind: CursorEventKind, at: Vec2); + fn on_message(&mut self, panel: PanelId, msg: Vec); } #[non_exhaustive] @@ -77,6 +78,7 @@ impl WasmtimeRuntime { let update = instance.get_typed_func(&mut store, "update")?; let draw = instance.get_typed_func(&mut store, "draw")?; let on_cursor_event = instance.get_typed_func(&mut store, "on_cursor_event")?; + let on_message = instance.get_typed_func(&mut store, "on_message")?; Ok(WasmtimeScript { store, @@ -85,6 +87,7 @@ impl WasmtimeRuntime { update, draw, on_cursor_event, + on_message, }) } } @@ -96,6 +99,7 @@ pub struct WasmtimeScript { update: wasmtime::TypedFunc<(u32, f32), ()>, draw: wasmtime::TypedFunc, on_cursor_event: wasmtime::TypedFunc<(u32, u32, f32, f32), ()>, + on_message: wasmtime::TypedFunc<(u32, u32), ()>, } impl WasmtimeScript { @@ -245,6 +249,7 @@ impl ScriptInstance for WasmtimeScript { let args = (id as u32, msg); let data = self.bind_panel.call(&mut self.store, args).unwrap(); *self.panel_datas.get_mut(id).unwrap() = data; + self.store.data().message_free(msg); PanelId(id) } @@ -266,6 +271,13 @@ impl ScriptInstance for WasmtimeScript { .call(&mut self.store, (data, kind as u32, at.x, at.y)) .unwrap(); } + + fn on_message(&mut self, panel: PanelId, msg: Vec) { + let data = self.get_panel_data(panel); + let msg = self.store.data().message_new(msg); + self.on_message.call(&mut self.store, (data, msg)).unwrap(); + self.store.data().message_free(msg); + } } /// The standard [ScriptAbi] implementation to use.