canary-rs/apps/sandbox/src/main.rs

214 lines
7.0 KiB
Rust

// 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<String> = 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<PanelWindow>,
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);
}
});
}
}