From 9f77dc82628de477c5f8030ee4f247c3a4811114 Mon Sep 17 00:00:00 2001 From: lilithium-hydride Date: Wed, 2 Nov 2022 22:25:04 -0400 Subject: [PATCH 001/114] Add license checker script --- check_licenses.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 check_licenses.sh diff --git a/check_licenses.sh b/check_licenses.sh new file mode 100644 index 0000000..7cc4621 --- /dev/null +++ b/check_licenses.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +! rg --multiline --files-without-match --glob '*.rs' --pcre2 '(? Date: Wed, 2 Nov 2022 22:29:34 -0400 Subject: [PATCH 002/114] Add deps to check_licenses.sh --- check_licenses.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/check_licenses.sh b/check_licenses.sh index 7cc4621..c02b162 100644 --- a/check_licenses.sh +++ b/check_licenses.sh @@ -1,3 +1,4 @@ #!/bin/sh +# Depends on: `rg` (ripgrep) ! rg --multiline --files-without-match --glob '*.rs' --pcre2 '(? Date: Wed, 2 Nov 2022 20:35:12 -0600 Subject: [PATCH 003/114] Add executable modifier to check_licenses.sh --- check_licenses.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 check_licenses.sh diff --git a/check_licenses.sh b/check_licenses.sh old mode 100644 new mode 100755 From 4b2396d137b459fc520d2fea470a8b685ae2fa1a Mon Sep 17 00:00:00 2001 From: lilithium-hydride Date: Thu, 3 Nov 2022 00:37:18 -0400 Subject: [PATCH 004/114] Initial on_resize() implementation --- apps/magpie/src/service/window.rs | 13 +++++++++++-- crates/script/src/api/abi.rs | 6 ++++++ crates/script/src/api/mod.rs | 6 ++++++ scripts/music-player/src/lib.rs | 4 ++++ src/backend/mod.rs | 3 +++ src/backend/wasmtime.rs | 10 ++++++++++ src/lib.rs | 4 ++++ 7 files changed, 44 insertions(+), 2 deletions(-) diff --git a/apps/magpie/src/service/window.rs b/apps/magpie/src/service/window.rs index ee19c99..1a28262 100644 --- a/apps/magpie/src/service/window.rs +++ b/apps/magpie/src/service/window.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::path::PathBuf; use std::time::Instant; -use canary::{Panel, Runtime}; +use canary::{Panel, Runtime, Vec2}; use glium::backend::glutin::DisplayCreationError; use glium::{glutin, Surface}; use glutin::event::{Event, WindowEvent}; @@ -66,6 +66,10 @@ impl Window { pub fn send_message(&mut self, msg: Vec) { self.panel.on_message(msg); } + + pub fn resize(&mut self, new_size: Vec2) { + self.panel.on_resize(new_size); + } } pub struct WindowStore { @@ -100,7 +104,12 @@ impl WindowStore { Event::WindowEvent { window_id, event } => { if let Some(window) = self.windows.get_mut(&window_id) { match event { - WindowEvent::Resized(_) => window.request_redraw(), + WindowEvent::Resized(size) => { + window.resize( + Vec2::new(size.width as f32, size.height as f32) * canary::PX_PER_MM + ); + window.request_redraw() + }, _ => {} } } diff --git a/crates/script/src/api/abi.rs b/crates/script/src/api/abi.rs index 737e6da..0e70bc5 100644 --- a/crates/script/src/api/abi.rs +++ b/crates/script/src/api/abi.rs @@ -28,6 +28,12 @@ pub fn draw(panel_data: u32) { panel.draw(); } +pub fn on_resize(panel_data: u32, width: f32, height: f32) { + let panel = unsafe { &mut PANEL_IMPLS[panel_data as usize] }; + let new_size = Vec2::new(width, height); + panel.on_resize(new_size); +} + pub fn on_cursor_event(panel_data: u32, kind: u32, x: f32, y: f32) { let panel = unsafe { &mut PANEL_IMPLS[panel_data as usize] }; let at = Vec2::new(x, y); diff --git a/crates/script/src/api/mod.rs b/crates/script/src/api/mod.rs index dfa0aef..71bd490 100644 --- a/crates/script/src/api/mod.rs +++ b/crates/script/src/api/mod.rs @@ -25,6 +25,11 @@ macro_rules! export_abi { ::canary_script::api::abi::draw(panel_data) } + #[no_mangle] + pub extern "C" fn on_resize(panel_data: u32, width: f32, height: f32) { + ::canary_script::api::abi::on_resize(panel_data, width, height) + } + #[no_mangle] pub extern "C" fn on_cursor_event(panel_data: u32, kind: u32, x: f32, y: f32) { ::canary_script::api::abi::on_cursor_event(panel_data, kind, x, y) @@ -44,6 +49,7 @@ pub trait BindPanel { pub trait PanelImpl { fn update(&mut self, dt: f32); fn draw(&mut self); + fn on_resize(&mut self, new_size: Vec2); fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2); fn on_message(&mut self, msg: Message); } diff --git a/scripts/music-player/src/lib.rs b/scripts/music-player/src/lib.rs index 998aaf1..5d310a8 100644 --- a/scripts/music-player/src/lib.rs +++ b/scripts/music-player/src/lib.rs @@ -39,6 +39,10 @@ impl PanelImpl for MusicPlayerPanel { self.label.draw(&ctx, offset, size, color); } + fn on_resize(&mut self, new_size: Vec2) { + + } + fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) {} fn on_message(&mut self, msg: Message) { diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 5b75ed8..ce76b8b 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -50,7 +50,10 @@ pub trait Instance { fn draw(&self, panel_ud: u32) -> Vec; + fn on_resize(&self, panel_ud: u32, new_size: Vec2); + fn on_cursor_event(&self, panel_ud: u32, kind: CursorEventKind, at: Vec2); fn on_message(&self, panel_ud: u32, msg: Vec); + } diff --git a/src/backend/wasmtime.rs b/src/backend/wasmtime.rs index 6551565..569dcbb 100644 --- a/src/backend/wasmtime.rs +++ b/src/backend/wasmtime.rs @@ -37,6 +37,7 @@ impl Backend for WasmtimeBackend { let bind_panel = instance.get_typed_func(&mut store, "bind_panel")?; let update = instance.get_typed_func(&mut store, "update")?; let draw = instance.get_typed_func(&mut store, "draw")?; + let on_resize = instance.get_typed_func(&mut store, "on_resize")?; let on_cursor_event = instance.get_typed_func(&mut store, "on_cursor_event")?; let on_message = instance.get_typed_func(&mut store, "on_message")?; @@ -45,6 +46,7 @@ impl Backend for WasmtimeBackend { bind_panel, update, draw, + on_resize, on_cursor_event, on_message, }; @@ -60,6 +62,7 @@ pub struct WasmtimeInstance { bind_panel: wasmtime::TypedFunc<(u32, u32), u32>, update: wasmtime::TypedFunc<(u32, f32), ()>, draw: wasmtime::TypedFunc, + on_resize: wasmtime::TypedFunc<(u32, f32, f32), ()>, on_cursor_event: wasmtime::TypedFunc<(u32, u32, f32, f32), ()>, on_message: wasmtime::TypedFunc<(u32, u32), ()>, } @@ -209,6 +212,13 @@ impl Instance for WasmtimeInstance { cmds } + fn on_resize(&self, panel_ud: u32, new_size: Vec2) { + let mut store = self.store.lock(); + self.on_resize + .call(store.deref_mut(), (panel_ud, new_size.x, new_size.y)) + .unwrap(); + } + fn on_cursor_event(&self, panel_ud: u32, kind: CursorEventKind, at: Vec2) { let mut store = self.store.lock(); self.on_cursor_event diff --git a/src/lib.rs b/src/lib.rs index 33ccf07..c46fc26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,10 @@ impl Panel { self.instance.draw(self.userdata) } + pub fn on_resize(&self, new_size: Vec2) { + self.instance.on_resize(self.userdata, new_size); + } + pub fn on_cursor_event(&self, kind: CursorEventKind, at: Vec2) { self.instance.on_cursor_event(self.userdata, kind, at); } From 27d7bdeb91dd0bed55aa1eb0a5fe27c44fc34218 Mon Sep 17 00:00:00 2001 From: lilithium-hydride Date: Thu, 3 Nov 2022 00:40:54 -0400 Subject: [PATCH 005/114] Fix styling --- src/backend/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index ce76b8b..8b8723d 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -55,5 +55,4 @@ pub trait Instance { fn on_cursor_event(&self, panel_ud: u32, kind: CursorEventKind, at: Vec2); fn on_message(&self, panel_ud: u32, msg: Vec); - } From a2d51b2fa8db346064368774024803b3f7e8b21b Mon Sep 17 00:00:00 2001 From: mars Date: Wed, 2 Nov 2022 23:00:59 -0600 Subject: [PATCH 006/114] Refactor Magpie window event handling + catch most windowing errors instead of unwrapping --- apps/magpie/Cargo.toml | 3 +- apps/magpie/src/service/ipc.rs | 2 +- apps/magpie/src/service/window.rs | 79 +++++++++++++++++++------------ 3 files changed, 52 insertions(+), 32 deletions(-) diff --git a/apps/magpie/Cargo.toml b/apps/magpie/Cargo.toml index 6d09612..ae5b420 100644 --- a/apps/magpie/Cargo.toml +++ b/apps/magpie/Cargo.toml @@ -9,6 +9,7 @@ path = "src/main.rs" required-features = ["service"] [dependencies] +anyhow = { version = "1", optional = true } byteorder = "1.4" canary = { path = "../..", optional = true } glium = { version = "0.32", optional = true} @@ -20,4 +21,4 @@ serde_json = "1" slab = { version = "0.4", optional = true} [features] -service = ["dep:canary", "dep:glium", "dep:mio", "dep:mio-signals", "dep:parking_lot", "dep:slab"] +service = ["dep:anyhow", "dep:canary", "dep:glium", "dep:mio", "dep:mio-signals", "dep:parking_lot", "dep:slab"] diff --git a/apps/magpie/src/service/ipc.rs b/apps/magpie/src/service/ipc.rs index 07d9273..735ee07 100644 --- a/apps/magpie/src/service/ipc.rs +++ b/apps/magpie/src/service/ipc.rs @@ -11,8 +11,8 @@ use mio_signals::{Signal, Signals}; use parking_lot::RwLock; use slab::Slab; -use crate::service::window::{WindowMessage, WindowMessageSender}; use crate::protocol::{CreatePanel, MagpieServerMsg, SendMessage, ServerMessenger}; +use crate::service::window::{WindowMessage, WindowMessageSender}; const SOCK_NAME: &str = "magpie.sock"; diff --git a/apps/magpie/src/service/window.rs b/apps/magpie/src/service/window.rs index 1a28262..983730f 100644 --- a/apps/magpie/src/service/window.rs +++ b/apps/magpie/src/service/window.rs @@ -12,6 +12,7 @@ use glutin::window::WindowId; use crate::service::gl::Graphics; use crate::service::ipc::{IpcMessage, IpcMessageSender}; +#[derive(Clone, Debug)] pub enum WindowMessage { OpenWindow { id: usize, script: PathBuf }, CloseWindow { id: usize }, @@ -70,6 +71,16 @@ impl Window { pub fn resize(&mut self, new_size: Vec2) { self.panel.on_resize(new_size); } + + pub fn on_event(&mut self, event: WindowEvent) { + match event { + WindowEvent::Resized(size) => { + self.resize(Vec2::new(size.width as f32, size.height as f32) * canary::PX_PER_MM); + self.request_redraw() + } + _ => {} + } + } } pub struct WindowStore { @@ -99,19 +110,43 @@ impl WindowStore { .flatten() } + pub fn on_message( + &mut self, + event_loop: &EventLoopWindowTarget, + message: WindowMessage, + ) -> anyhow::Result { + match message { + WindowMessage::OpenWindow { id, script } => { + println!("Opening window {} with script {:?}", id, script); + let module = std::fs::read(script)?; + let mut script = self.runtime.load_module(&module)?; + let panel = script.create_panel(vec![])?; + let window = Window::new(panel, &event_loop)?; + let window_id = window.get_id(); + self.windows.insert(window_id, window); + self.ipc_to_window.insert(id, window_id); + } + WindowMessage::CloseWindow { id } => { + if let Some(window_id) = self.ipc_to_window.remove(&id) { + self.windows.remove(&window_id); + } + } + WindowMessage::Quit => return Ok(true), + WindowMessage::SendMessage { id, msg } => { + if let Some(window) = self.get_ipc_window(id) { + window.send_message(msg); + } + } + }; + + Ok(false) + } + pub fn run(mut self, event_loop: EventLoop) -> ! { event_loop.run(move |event, event_loop, control_flow| match event { Event::WindowEvent { window_id, event } => { if let Some(window) = self.windows.get_mut(&window_id) { - match event { - WindowEvent::Resized(size) => { - window.resize( - Vec2::new(size.width as f32, size.height as f32) * canary::PX_PER_MM - ); - window.request_redraw() - }, - _ => {} - } + window.on_event(event); } } Event::RedrawRequested(id) => { @@ -125,27 +160,11 @@ impl WindowStore { window.request_redraw(); } } - Event::UserEvent(event) => match event { - WindowMessage::OpenWindow { id, script } => { - println!("Opening window {} with script {:?}", id, script); - let module = std::fs::read(script).unwrap(); - let mut script = self.runtime.load_module(&module).unwrap(); - let panel = script.create_panel(vec![]).unwrap(); - let window = Window::new(panel, &event_loop).unwrap(); - let window_id = window.get_id(); - self.windows.insert(window_id, window); - self.ipc_to_window.insert(id, window_id); - } - WindowMessage::CloseWindow { id } => { - if let Some(window_id) = self.ipc_to_window.remove(&id) { - self.windows.remove(&window_id); - } - } - WindowMessage::Quit => *control_flow = ControlFlow::Exit, - WindowMessage::SendMessage { id, msg } => { - if let Some(window) = self.get_ipc_window(id) { - window.send_message(msg); - } + Event::UserEvent(event) => match self.on_message(event_loop, event.clone()) { + Ok(false) => {} + Ok(true) => *control_flow = ControlFlow::Exit, + Err(err) => { + eprintln!("Error while handling message {:?}:\n{}", event, err); } }, _ => {} From 47f0894cbd1d667634e90b907ffbd324af1d2ea0 Mon Sep 17 00:00:00 2001 From: mars Date: Thu, 3 Nov 2022 17:52:32 -0600 Subject: [PATCH 007/114] Add DrawContext::draw_indexed() --- crates/script/src/api/mod.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/script/src/api/mod.rs b/crates/script/src/api/mod.rs index 71bd490..5cace2e 100644 --- a/crates/script/src/api/mod.rs +++ b/crates/script/src/api/mod.rs @@ -186,6 +186,24 @@ impl DrawContext { } } + pub fn draw_indexed(&self, vertices: &[MeshVertex], indices: &[MeshIndex]) { + let mut vertices = vertices.to_vec(); + + if let Some(offset) = self.offset { + for v in vertices.iter_mut() { + v.position += offset; + } + } + + if let Some(opacity) = self.opacity { + for v in vertices.iter_mut() { + v.color = v.color.alpha_multiply(opacity); + } + } + + self.panel.draw_indexed(&vertices, &indices); + } + pub fn draw_triangle(&self, v1: Vec2, v2: Vec2, v3: Vec2, color: Color) { if let Some(clip_rect) = self.clip_rect.as_ref() { let tri = ColoredTriangle { v1, v2, v3, color }; From 325a85eb3976e1eb736bafc73ba9db965138e005 Mon Sep 17 00:00:00 2001 From: mars Date: Thu, 3 Nov 2022 17:53:04 -0600 Subject: [PATCH 008/114] Unlock abi from wasm32 OS for the sake of LSP --- crates/script/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 39028ad..2625c12 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -6,7 +6,6 @@ use bytemuck::{Pod, Zeroable}; pub use glam::Vec2; use num_derive::{FromPrimitive, ToPrimitive}; -#[cfg(target_arch = "wasm32")] pub mod api; #[repr(C)] From aa333b0fe42c3eafbb1ec1e43997d9944046c963 Mon Sep 17 00:00:00 2001 From: mars Date: Thu, 3 Nov 2022 19:07:40 -0600 Subject: [PATCH 009/114] Add Magpie cursor input (closes #23) --- apps/magpie/src/service/window.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/apps/magpie/src/service/window.rs b/apps/magpie/src/service/window.rs index 983730f..c30dde9 100644 --- a/apps/magpie/src/service/window.rs +++ b/apps/magpie/src/service/window.rs @@ -2,10 +2,10 @@ use std::collections::HashMap; use std::path::PathBuf; use std::time::Instant; -use canary::{Panel, Runtime, Vec2}; +use canary::{CursorEventKind, Panel, Runtime, Vec2, PX_PER_MM}; use glium::backend::glutin::DisplayCreationError; use glium::{glutin, Surface}; -use glutin::event::{Event, WindowEvent}; +use glutin::event::{ElementState, Event, MouseButton, WindowEvent}; use glutin::event_loop::{ControlFlow, EventLoop, EventLoopProxy, EventLoopWindowTarget}; use glutin::window::WindowId; @@ -26,6 +26,7 @@ pub struct Window { pub graphics: Graphics, pub panel: Panel, pub last_update: Instant, + pub cursor_pos: Vec2, } impl Window { @@ -42,6 +43,7 @@ impl Window { graphics, panel, last_update, + cursor_pos: Vec2::ZERO, }) } @@ -75,9 +77,26 @@ impl Window { pub fn on_event(&mut self, event: WindowEvent) { match event { WindowEvent::Resized(size) => { - self.resize(Vec2::new(size.width as f32, size.height as f32) * canary::PX_PER_MM); + self.resize(Vec2::new(size.width as f32, size.height as f32) * PX_PER_MM); self.request_redraw() } + WindowEvent::CursorMoved { position, .. } => { + let x = position.x as f32 * PX_PER_MM; + let y = position.y as f32 * PX_PER_MM; + self.cursor_pos = Vec2::new(x, y); + } + WindowEvent::MouseInput { + state, + button: MouseButton::Left, + .. + } => { + let event = match state { + ElementState::Pressed => CursorEventKind::Select, + ElementState::Released => CursorEventKind::Deselect, + }; + + self.panel.on_cursor_event(event, self.cursor_pos); + } _ => {} } } From 7294c0402c1e589ef50b0ef9a1de9c006e1d9473 Mon Sep 17 00:00:00 2001 From: mars Date: Fri, 4 Nov 2022 16:53:23 -0600 Subject: [PATCH 010/114] Add licensing info to backend files --- src/backend/mod.rs | 3 +++ src/backend/wasmtime.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 8b8723d..9e9f408 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2022 Marceline Cramer +// SDPX-License-Identifier: LGPL-3.0-or-later + //! This module defines backends for WebAssembly execution. //! //! Canary is designed to support multiple WebAssembly runtimes for different diff --git a/src/backend/wasmtime.rs b/src/backend/wasmtime.rs index 569dcbb..74f5f85 100644 --- a/src/backend/wasmtime.rs +++ b/src/backend/wasmtime.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2022 Marceline Cramer +// SDPX-License-Identifier: LGPL-3.0-or-later + use std::ops::DerefMut; use super::{Arc, Backend, Instance, PanelId}; From 4798d43f6c23566d59ecb23e54b20e3b5fee8d99 Mon Sep 17 00:00:00 2001 From: mars Date: Fri, 4 Nov 2022 21:56:41 -0600 Subject: [PATCH 011/114] Fix Magpie monotonic delta-time bug --- apps/magpie/src/service/window.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/magpie/src/service/window.rs b/apps/magpie/src/service/window.rs index c30dde9..6d02511 100644 --- a/apps/magpie/src/service/window.rs +++ b/apps/magpie/src/service/window.rs @@ -59,6 +59,7 @@ impl Window { let now = Instant::now(); let dt = now.duration_since(self.last_update).as_secs_f32(); self.panel.update(dt); + self.last_update = now; } pub fn draw(&mut self) { From 681b884b7493dfe95dad44d5754e55cd704302b2 Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 5 Nov 2022 14:25:59 -0600 Subject: [PATCH 012/114] Add Color::lerp() --- crates/script/src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 2625c12..2c183ca 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -168,6 +168,12 @@ impl Color { pub fn with_alpha(&self, alpha: u8) -> Self { Self(self.0 & 0xffffff00 | alpha as u32) } + + pub fn lerp(self, target: Self, blend: f32) -> Self { + let s: glam::Vec4 = self.into(); + let o: glam::Vec4 = target.into(); + (o * blend + s * (1.0 - blend)).into() + } } #[repr(C)] From eb32163b24bfe67347942d66aaf1e8ca09047590 Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 5 Nov 2022 15:00:51 -0600 Subject: [PATCH 013/114] Fix Rect coordinate space --- crates/script/src/api/mod.rs | 63 ++++++++++++++-------------- crates/script/src/lib.rs | 80 +++++++++++++++++++++--------------- src/text/glyph.rs | 22 +--------- src/text/mod.rs | 28 ++----------- 4 files changed, 84 insertions(+), 109 deletions(-) diff --git a/crates/script/src/api/mod.rs b/crates/script/src/api/mod.rs index 5cace2e..f9f8f21 100644 --- a/crates/script/src/api/mod.rs +++ b/crates/script/src/api/mod.rs @@ -305,10 +305,10 @@ impl DrawContext { rect }; - let v1 = rect.bl; - let v2 = Vec2::new(rect.bl.x, rect.tr.y); - let v3 = Vec2::new(rect.tr.x, rect.bl.y); - let v4 = rect.tr; + let v1 = rect.tl; + let v2 = Vec2::new(rect.tl.x, rect.br.y); + let v3 = Vec2::new(rect.br.x, rect.tl.y); + let v4 = rect.br; self.draw_triangle_noclip(v1, v2, v3, color); self.draw_triangle_noclip(v2, v3, v4, color); @@ -333,47 +333,48 @@ impl DrawContext { let mut inner_rect = rect; let inset = rect.inset(radius); + if corners.intersects(CornerFlags::TOP) { + inner_rect.tl.y += radius; + + let mut top_edge = Rect { + tl: rect.tl, + br: Vec2::new(rect.br.x, rect.tl.y + radius), + }; + + if corners.contains(CornerFlags::TOP_LEFT) { + top_edge.tl.x += radius; + self.draw_quarter_circle(Corner::TopLeft, inset.tl, radius, color); + } + + if corners.contains(CornerFlags::TOP_RIGHT) { + top_edge.br.x -= radius; + self.draw_quarter_circle(Corner::TopRight, inset.tr(), radius, color); + } + + self.draw_rect(top_edge, color); + } + if corners.intersects(CornerFlags::BOTTOM) { - inner_rect.bl.y += radius; + inner_rect.br.y -= radius; let mut bottom_edge = Rect { - bl: rect.bl, - tr: Vec2::new(rect.tr.x, rect.bl.y + radius), + tl: Vec2::new(rect.tl.x, rect.br.y - radius), + br: rect.br, }; if corners.contains(CornerFlags::BOTTOM_LEFT) { - bottom_edge.bl.x += radius; - self.draw_quarter_circle(Corner::BottomLeft, inset.bl, radius, color); + bottom_edge.tl.x += radius; + self.draw_quarter_circle(Corner::BottomLeft, inset.bl(), radius, color); } if corners.contains(CornerFlags::BOTTOM_RIGHT) { - bottom_edge.tr.x -= radius; - self.draw_quarter_circle(Corner::BottomRight, inset.br(), radius, color); + bottom_edge.br.x -= radius; + self.draw_quarter_circle(Corner::BottomRight, inset.br, radius, color); } self.draw_rect(bottom_edge, color); } - if corners.intersects(CornerFlags::TOP) { - inner_rect.tr.y -= radius; - - let mut top_edge = Rect { - bl: Vec2::new(rect.bl.x, rect.tr.y - radius), - tr: rect.tr, - }; - - if corners.contains(CornerFlags::TOP_LEFT) { - top_edge.bl.x += radius; - self.draw_quarter_circle(Corner::TopLeft, inset.tl(), radius, color); - } - - if corners.contains(CornerFlags::TOP_RIGHT) { - top_edge.tr.x -= radius; - self.draw_quarter_circle(Corner::TopRight, inset.tr, radius, color); - } - - self.draw_rect(top_edge, color); - } self.draw_rect(inner_rect, color); } diff --git a/crates/script/src/lib.rs b/crates/script/src/lib.rs index 2c183ca..c2ada0e 100644 --- a/crates/script/src/lib.rs +++ b/crates/script/src/lib.rs @@ -11,78 +11,78 @@ pub mod api; #[repr(C)] #[derive(Copy, Clone, Debug, Default, Pod, Zeroable)] pub struct Rect { - pub bl: Vec2, - pub tr: Vec2, + pub tl: Vec2, + pub br: Vec2, } impl Rect { pub const NEG_INFINITY: Self = Self { - bl: Vec2::splat(f32::INFINITY), - tr: Vec2::splat(f32::NEG_INFINITY), + tl: Vec2::splat(f32::INFINITY), + br: Vec2::splat(f32::NEG_INFINITY), }; pub fn from_xy_size(xy: Vec2, size: Vec2) -> Self { Self { - bl: xy, - tr: xy + size, + tl: xy, + br: xy + size, } } pub fn from_circle_bounds(center: Vec2, radius: f32) -> Self { Self { - bl: center - radius, - tr: center + radius, + tl: center - radius, + br: center + radius, } } pub fn from_triangle_bounds(tri: &ColoredTriangle) -> Self { Self { - bl: tri.v1.min(tri.v2).min(tri.v3), - tr: tri.v1.max(tri.v2).max(tri.v3), + tl: tri.v1.min(tri.v2).min(tri.v3), + br: tri.v1.max(tri.v2).max(tri.v3), } } pub fn inset(&self, d: f32) -> Self { Self { - bl: self.bl + d, - tr: self.tr - d, + tl: self.tl + d, + br: self.br - d, } } - - pub fn tl(&self) -> Vec2 { - Vec2::new(self.bl.x, self.tr.y) + + pub fn bl(&self) -> Vec2 { + Vec2::new(self.tl.x, self.br.y) } - pub fn br(&self) -> Vec2 { - Vec2::new(self.tr.x, self.bl.y) + pub fn tr(&self) -> Vec2 { + Vec2::new(self.br.x, self.tl.y) } pub fn offset(&self, offset: Vec2) -> Self { Self { - bl: self.bl + offset, - tr: self.tr + offset, + tl: self.tl + offset, + br: self.br + offset, } } pub fn scale(&self, scale: f32) -> Self { Self { - bl: self.bl * scale, - tr: self.tr * scale, + tl: self.tl * scale, + br: self.br * scale, } } pub fn is_valid(&self) -> bool { - self.bl.cmplt(self.tr).all() + self.tl.cmplt(self.br).all() } pub fn intersects_rect(&self, other: &Self) -> bool { - self.bl.cmple(other.tr).all() && self.tr.cmpge(other.bl).all() + self.tl.cmple(other.br).all() && self.br.cmpge(other.tl).all() } pub fn intersection(&self, other: &Self) -> Option { let clipped = Self { - bl: self.bl.max(other.bl), - tr: self.tr.min(other.tr), + tl: self.tl.max(other.tl), + br: self.br.min(other.br), }; if clipped.is_valid() { @@ -92,27 +92,41 @@ impl Rect { } } + pub fn union(&self, other: &Self) -> Self { + Self { + tl: self.tl.min(other.tl), + br: self.br.max(other.br), + } + } + + pub fn union_point(&self, point: Vec2) -> Self { + Self { + tl: self.tl.min(point), + br: self.br.max(point), + } + } + pub fn contains_rect(&self, other: &Self) -> bool { - self.bl.x < other.bl.x - && self.bl.y < other.bl.y - && self.tr.x > other.tr.x - && self.tr.y > other.tr.y + self.tl.x < other.tl.x + && self.tl.y < other.tl.y + && self.br.x > other.br.x + && self.br.y > other.br.y } pub fn contains_point(&self, xy: Vec2) -> bool { - self.bl.x < xy.x && self.bl.y < xy.y && self.tr.x > xy.x && self.tr.y > xy.y + self.tl.x < xy.x && self.tl.y < xy.y && self.br.x > xy.x && self.br.y > xy.y } pub fn size(&self) -> Vec2 { - self.tr - self.bl + self.br - self.tl } pub fn width(&self) -> f32 { - self.tr.x - self.bl.x + self.br.x - self.tl.x } pub fn height(&self) -> f32 { - self.tr.y - self.bl.y + self.br.y - self.tl.y } } diff --git a/src/text/glyph.rs b/src/text/glyph.rs index cf862e8..f1edc3a 100644 --- a/src/text/glyph.rs +++ b/src/text/glyph.rs @@ -133,26 +133,8 @@ impl OutlineSink { fn pf_vector_to_lyon(&mut self, v: &Vector2F) -> lyon::geom::Point { let point = lyon::geom::Point::::new(v.x(), -v.y()) / self.units_per_em; - - // TODO clean this up with helper math methods? - let bb = &mut self.bounding_box; - - if point.x < bb.bl.x { - bb.bl.x = point.x; - } - - if point.x > bb.tr.x { - bb.tr.x = point.x; - } - - if point.y < bb.tr.y { - bb.tr.y = point.y; - } - - if point.y > bb.bl.y { - bb.bl.y = point.y; - } - + let glam_point = Vec2::new(point.x, point.y); + self.bounding_box = self.bounding_box.union_point(glam_point); point } } diff --git a/src/text/mod.rs b/src/text/mod.rs index 1032742..1a650cf 100644 --- a/src/text/mod.rs +++ b/src/text/mod.rs @@ -73,31 +73,9 @@ impl Font { xcur += position.hori_advance; ycur += position.vert_advance; - let xpos = xpos as f32 / units_per_em; - let ypos = ypos as f32 / units_per_em; - - let mut bb = glyphs.get(position.index as usize).unwrap().bounding_box; - bb.bl.x = bb.bl.x + xpos; - bb.bl.y = bb.bl.y + ypos; - bb.tr.x = bb.tr.x + xpos; - bb.tr.y = bb.tr.y + ypos; - - // TODO use euclid instead - if bounds.bl.x > bb.bl.x { - bounds.bl.x = bb.bl.x; - } - - if bounds.bl.y > bb.bl.y { - bounds.bl.y = bb.bl.y; - } - - if bounds.tr.x < bb.tr.x { - bounds.tr.x = bb.tr.x; - } - - if bounds.tr.y < bb.tr.y { - bounds.tr.y = bb.tr.y; - } + let pos = Vec2::new(xpos as f32, ypos as f32) / units_per_em; + let bb = glyphs.get(position.index as usize).unwrap().bounding_box; + bounds = bounds.union(&bb.offset(pos)); } TextLayout { From 75e0aca668b8bed8a092b61b84c53ce8650acf32 Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 5 Nov 2022 14:28:07 -0600 Subject: [PATCH 014/114] Add missing SAO UI on_resize() impls --- scripts/sao-ui/src/lib.rs | 4 +++- scripts/sao-ui/src/main_menu.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/sao-ui/src/lib.rs b/scripts/sao-ui/src/lib.rs index 15daa23..b24baef 100644 --- a/scripts/sao-ui/src/lib.rs +++ b/scripts/sao-ui/src/lib.rs @@ -45,9 +45,11 @@ impl PanelImpl for ConfirmationDialogPanel { self.dialog.draw(&ctx); } - fn on_cursor_event(&mut self, kind: CursorEventKind, at: canary_script::Vec2) { + fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) { self.dialog.on_cursor_event(kind, at.into()); } + fn on_resize(&mut self, _size: Vec2) {} + fn on_message(&mut self, _msg: Message) {} } diff --git a/scripts/sao-ui/src/main_menu.rs b/scripts/sao-ui/src/main_menu.rs index 1f6ce7c..5c034e2 100644 --- a/scripts/sao-ui/src/main_menu.rs +++ b/scripts/sao-ui/src/main_menu.rs @@ -31,10 +31,12 @@ impl PanelImpl for MainMenuPanel { Widget::draw(&mut self.menu, &ctx); } - fn on_cursor_event(&mut self, kind: CursorEventKind, at: canary_script::Vec2) { + fn on_cursor_event(&mut self, kind: CursorEventKind, at: Vec2) { Widget::on_cursor_event(&mut self.menu, kind, at.into()); } + fn on_resize(&mut self, _size: Vec2) {} + fn on_message(&mut self, msg: Message) {} } From ab50a8e130d2c632fc9b4ea2fec37f289b69538a Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 5 Nov 2022 15:03:30 -0600 Subject: [PATCH 015/114] Fix use of Rect in SAO UI script --- scripts/sao-ui/src/widgets/button.rs | 10 +++++----- scripts/sao-ui/src/widgets/dialog.rs | 4 ++-- scripts/sao-ui/src/widgets/menu.rs | 14 +++++++------- scripts/sao-ui/src/widgets/scroll.rs | 4 ++-- scripts/sao-ui/src/widgets/shell.rs | 8 ++++---- scripts/sao-ui/src/widgets/text.rs | 10 +++++----- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/scripts/sao-ui/src/widgets/button.rs b/scripts/sao-ui/src/widgets/button.rs index 852bebe..8404191 100644 --- a/scripts/sao-ui/src/widgets/button.rs +++ b/scripts/sao-ui/src/widgets/button.rs @@ -154,7 +154,7 @@ impl RectButton { label: Option, icon: Option, ) -> Self { - let mut label_left = rect.bl.x; + let mut label_left = rect.tl.x; let mut alignment = HorizontalAlignment::Center; let icon = icon.map(|text| { @@ -163,8 +163,8 @@ impl RectButton { alignment = HorizontalAlignment::Left; let scale = rect.height() * style.icon_scale_factor; let color = Color::BLACK; - let cx = rect.bl.x + margin / 2.0; - let cy = rect.bl.y + rect.height() / 2.0; + let cx = rect.tl.x + margin / 2.0; + let cy = rect.tl.y + rect.height() / 2.0; let center = Vec2::new(cx, cy); Icon::new(text, scale, color, center) @@ -173,8 +173,8 @@ impl RectButton { let label = label.map(|text| { let scale = rect.height() * style.label_scale_factor; let left = label_left; - let right = rect.tr.x; - let baseline = rect.bl.y; + let right = rect.br.x; + let baseline = rect.tl.y; let baseline = (rect.height() * style.label_baseline) + baseline; let color = Color::BLACK; diff --git a/scripts/sao-ui/src/widgets/dialog.rs b/scripts/sao-ui/src/widgets/dialog.rs index eaef51c..6aafe9c 100644 --- a/scripts/sao-ui/src/widgets/dialog.rs +++ b/scripts/sao-ui/src/widgets/dialog.rs @@ -226,8 +226,8 @@ impl Container for Dialog { let body_tr = Vec2::new(width2, style.body.height / 2.0); let body_rect = Rect { - bl: -body_tr, - tr: body_tr, + tl: -body_tr, + br: body_tr, }; let footer_xy = Vec2::new(-width2, -style.body.height / 2.0 - style.footer.height); diff --git a/scripts/sao-ui/src/widgets/menu.rs b/scripts/sao-ui/src/widgets/menu.rs index 00647c6..1ecaff4 100644 --- a/scripts/sao-ui/src/widgets/menu.rs +++ b/scripts/sao-ui/src/widgets/menu.rs @@ -295,8 +295,8 @@ impl TabMenu { let scroll_bar = Offset::new(scroll_bar, Vec2::new(scroll_x, -tab_list_height)); let separator_rect = Rect { - bl: Vec2::new(Self::TAB_WIDTH, -tab_list_height), - tr: Vec2::new(Self::TAB_WIDTH + Self::SEPARATOR_WIDTH, 0.0), + tl: Vec2::new(Self::TAB_WIDTH, -tab_list_height), + br: Vec2::new(Self::TAB_WIDTH + Self::SEPARATOR_WIDTH, 0.0), }; let head_width = Self::TAB_WIDTH @@ -306,13 +306,13 @@ impl TabMenu { + scroll_bar.style.margin.x * 2.0; let head_rect = Rect { - bl: Vec2::ZERO, - tr: Vec2::new(head_width, Self::HEAD_HEIGHT), + tl: Vec2::ZERO, + br: Vec2::new(head_width, Self::HEAD_HEIGHT), }; let view_rect = Rect { - bl: Vec2::new(Self::TAB_WIDTH + Self::SEPARATOR_WIDTH, -tab_list_height), - tr: Vec2::new(head_rect.tr.x, 0.0), + tl: Vec2::new(Self::TAB_WIDTH + Self::SEPARATOR_WIDTH, -tab_list_height), + br: Vec2::new(head_rect.br.x, 0.0), }; let view = ScrollView::new( @@ -322,7 +322,7 @@ impl TabMenu { |available_width: f32| Inventory::new(available_width), ); - let view = Offset::new(view, view_rect.bl); + let view = Offset::new(view, view_rect.tl); Self { pop_out, diff --git a/scripts/sao-ui/src/widgets/scroll.rs b/scripts/sao-ui/src/widgets/scroll.rs index 120f62a..2fd9ee6 100644 --- a/scripts/sao-ui/src/widgets/scroll.rs +++ b/scripts/sao-ui/src/widgets/scroll.rs @@ -50,8 +50,8 @@ impl ScrollBar { pub fn new(height: f32, content_height: f32, style: ScrollBarStyle) -> Self { let center_x = style.body_width / 2.0 + style.margin.x; let rail_rect = Rect { - bl: Vec2::new(center_x - style.rail_width / 2.0, style.margin.y), - tr: Vec2::new(center_x + style.rail_width / 2.0, height - style.margin.y), + tl: Vec2::new(center_x - style.rail_width / 2.0, style.margin.y), + br: Vec2::new(center_x + style.rail_width / 2.0, height - style.margin.y), }; let body_color_anim = Animation::new( diff --git a/scripts/sao-ui/src/widgets/shell.rs b/scripts/sao-ui/src/widgets/shell.rs index 5e3a4e6..511f202 100644 --- a/scripts/sao-ui/src/widgets/shell.rs +++ b/scripts/sao-ui/src/widgets/shell.rs @@ -132,8 +132,8 @@ impl Offset { vert_align: OffsetAlignment, ) -> Self { let bounds = inner.get_bounds(); - let x = hori_align.align(bounds.bl.x, bounds.tr.x); - let y = vert_align.align(bounds.tr.y, bounds.bl.y); + let x = hori_align.align(bounds.tl.x, bounds.br.x); + let y = vert_align.align(bounds.br.y, bounds.tl.y); let offset = anchor - Vec2::new(x, y); Self { inner, offset } } @@ -180,8 +180,8 @@ impl Popup { vert_align: OffsetAlignment, ) -> Self { let bounds = inner.get_bounds(); - let x = hori_align.align(bounds.bl.x, bounds.tr.x); - let y = vert_align.align(bounds.tr.y, bounds.bl.y); + let x = hori_align.align(bounds.tl.x, bounds.br.x); + let y = vert_align.align(bounds.br.y, bounds.tl.y); let offset = anchor - Vec2::new(x, y); Self { inner, offset } } diff --git a/scripts/sao-ui/src/widgets/text.rs b/scripts/sao-ui/src/widgets/text.rs index 5350127..19cf6cf 100644 --- a/scripts/sao-ui/src/widgets/text.rs +++ b/scripts/sao-ui/src/widgets/text.rs @@ -62,14 +62,14 @@ impl Widget for Label { let bounds = Rect::from(layout.get_bounds()).scale(self.scale); self.bounds = bounds; let xoff = match self.alignment { - HorizontalAlignment::Left => self.left - bounds.bl.x, - HorizontalAlignment::Right => self.right - bounds.tr.x, + HorizontalAlignment::Left => self.left - bounds.tl.x, + HorizontalAlignment::Right => self.right - bounds.br.x, HorizontalAlignment::Center => { let available = self.right - self.left; let halfway = available / 2.0 + self.left; - let width = bounds.tr.x - bounds.bl.x; + let width = bounds.br.x - bounds.tl.x; let left = halfway - width / 2.0; - left - bounds.bl.x + left - bounds.tl.x } }; @@ -117,7 +117,7 @@ impl Widget for Icon { let bounds = Rect::from(layout.get_bounds()).scale(self.scale); self.bounds = bounds; self.offset = self.center - bounds.size() / 2.0; - self.offset.y -= bounds.bl.y; + self.offset.y -= bounds.tl.y; self.dirty = false; self.layout = Some(layout); } From 69318a02e5b97464056d84807e537fb24894e324 Mon Sep 17 00:00:00 2001 From: mars Date: Sat, 5 Nov 2022 16:07:53 -0600 Subject: [PATCH 016/114] Fix rounded corner drawing --- crates/script/src/api/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/script/src/api/mod.rs b/crates/script/src/api/mod.rs index f9f8f21..b208388 100644 --- a/crates/script/src/api/mod.rs +++ b/crates/script/src/api/mod.rs @@ -255,10 +255,10 @@ impl DrawContext { let delta = PI / 4.0 / spoke_num; let (mut theta, limit) = match corner { - Corner::TopRight => (0.0, FRAC_PI_2), - Corner::BottomRight => (FRAC_PI_2 * 3.0, PI * 2.0), - Corner::BottomLeft => (PI, FRAC_PI_2 * 3.0), - Corner::TopLeft => (FRAC_PI_2, PI), + Corner::TopRight => (FRAC_PI_2 * 3.0, PI * 2.0), + Corner::BottomRight => (0.0, FRAC_PI_2), + Corner::BottomLeft => (FRAC_PI_2, PI), + Corner::TopLeft => (PI, FRAC_PI_2 * 3.0), }; let mut last_spoke = Vec2::from_angle(theta) * radius + center; From e39d16a5169a146a7732a6d1ff1b4b57307c52aa Mon Sep 17 00:00:00 2001 From: mars Date: Sun, 6 Nov 2022 14:59:38 -0700 Subject: [PATCH 017/114] Dialog uses new coords --- scripts/sao-ui/src/widgets/dialog.rs | 142 ++++++++++++++------------- 1 file changed, 74 insertions(+), 68 deletions(-) diff --git a/scripts/sao-ui/src/widgets/dialog.rs b/scripts/sao-ui/src/widgets/dialog.rs index 6aafe9c..c8c7415 100644 --- a/scripts/sao-ui/src/widgets/dialog.rs +++ b/scripts/sao-ui/src/widgets/dialog.rs @@ -31,7 +31,6 @@ impl DialogResponse { #[derive(Clone)] pub struct DialogStyle { - pub width: f32, pub rounding: f32, pub header: DialogHeaderStyle, pub body: DialogBodyStyle, @@ -41,8 +40,7 @@ pub struct DialogStyle { impl Default for DialogStyle { fn default() -> Self { Self { - width: 1.2, - rounding: 0.02, + rounding: 5.0, header: Default::default(), body: Default::default(), footer: Default::default(), @@ -64,7 +62,7 @@ impl Default for DialogHeaderStyle { fn default() -> Self { Self { color: Color::WHITE, - height: 0.3, + height: 20.0, text_font: Font::new(crate::DISPLAY_FONT), text_color: Color::BLACK, text_scale_factor: 0.65, @@ -76,20 +74,18 @@ impl Default for DialogHeaderStyle { #[derive(Clone)] pub struct DialogBodyStyle { pub color: Color, - pub height: f32, pub text_font: Font, pub text_color: Color, - pub text_scale_factor: f32, + pub text_size: f32, } impl Default for DialogBodyStyle { fn default() -> Self { Self { color: Color::WHITE.with_alpha(0xb0), - height: 0.6, text_font: Font::new(crate::CONTENT_FONT), text_color: Color::BLACK, - text_scale_factor: 0.15, + text_size: 5.0, } } } @@ -106,9 +102,9 @@ impl Default for DialogFooterStyle { fn default() -> Self { Self { icon_font: Font::new(crate::ICON_FONT), - button_radius: 0.1, + button_radius: 7.5, color: Color::WHITE, - height: 0.25, + height: 20.0, } } } @@ -122,53 +118,16 @@ pub struct DialogInfo { pub struct Dialog { style: DialogStyle, - title: Label, - content: Label, + title: Offset