diff --git a/examples/gain/Cargo.toml b/examples/gain/Cargo.toml index cb4255b..66b1e77 100644 --- a/examples/gain/Cargo.toml +++ b/examples/gain/Cargo.toml @@ -10,6 +10,6 @@ formats = ["clap", "vst3"] [dependencies] coupler = { workspace = true, features = ["derive"] } -coupler-reflector = { path = "../../coupler-reflector" } +reflector = { git = "https://github.com/coupler-rs/reflector", rev = "eb40c59a71470077b4f58a26685796d1609ea8ed" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/examples/gain/src/lib.rs b/examples/gain/src/lib.rs index 0984900..0916228 100644 --- a/examples/gain/src/lib.rs +++ b/examples/gain/src/lib.rs @@ -1,12 +1,20 @@ +use std::cell::RefCell; use std::io::{self, Read, Write}; +use std::rc::Rc; use serde::{Deserialize, Serialize}; use coupler::format::clap::*; use coupler::format::vst3::*; +use coupler::params::{ParamId, ParamValue}; +use coupler::view::{ParentWindow, RawParent, Size, View}; use coupler::{buffers::*, bus::*, engine::*, events::*, host::*, params::*, plugin::*, view::*}; -use coupler_reflector::PluginWindow; +use reflector::graphics::Renderer; +use reflector::platform::{ + App, AppMode, AppOptions, Bitmap, Cursor, MouseButton, Point, RawWindow, Response, Result, + Window, WindowContext, WindowOptions, +}; #[derive(Params, Serialize, Deserialize, Clone)] struct GainParams { @@ -26,7 +34,7 @@ pub struct Gain { impl Plugin for Gain { type Engine = GainEngine; - type View = PluginWindow; + type View = GainView; fn info() -> PluginInfo { PluginInfo { @@ -84,13 +92,8 @@ impl Plugin for Gain { } } - fn view(&mut self, _host: ViewHost, parent: &ParentWindow) -> Self::View { - let size = Size { - width: 512.0, - height: 512.0, - }; - - PluginWindow::open(parent, size).unwrap() + fn view(&mut self, host: ViewHost, parent: &ParentWindow) -> Self::View { + GainView::open(host, parent, &self.params).unwrap() } } @@ -146,3 +149,179 @@ impl Engine for GainEngine { } } } + +struct Gesture { + start_mouse_pos: Point, + start_value: f32, +} + +struct ViewState { + host: ViewHost, + params: Rc>, + renderer: Renderer, + framebuffer: Vec, + mouse_pos: Point, + gesture: Option, +} + +impl ViewState { + fn new(host: ViewHost, params: Rc>) -> ViewState { + ViewState { + host, + params, + renderer: Renderer::new(), + framebuffer: Vec::new(), + mouse_pos: Point { x: -1.0, y: -1.0 }, + gesture: None, + } + } + + fn update_cursor(&self, window: &Window) { + let pos = self.mouse_pos; + if pos.x >= 96.0 && pos.x < 160.0 && pos.y >= 96.0 && pos.y < 160.0 { + window.set_cursor(Cursor::SizeNs); + } else { + window.set_cursor(Cursor::Arrow); + } + } + + fn handle_event(&mut self, cx: &WindowContext, event: reflector::platform::Event) -> Response { + use reflector::graphics::{Affine, Color, Path, Point}; + use reflector::platform::Event; + + match event { + Event::Frame => { + let scale = cx.window().scale(); + let size = cx.window().size(); + let width = (size.width * scale) as usize; + let height = (size.height * scale) as usize; + self.framebuffer.resize(width * height, 0xFF000000); + + let mut canvas = self.renderer.canvas(&mut self.framebuffer, width, height); + + canvas.clear(Color::rgba(21, 26, 31, 255)); + + let transform = Affine::scale(scale as f32); + + let value = self.params.borrow().gain; + + let center = Point::new(128.0, 128.0); + let radius = 32.0; + let angle1 = 0.75 * std::f32::consts::PI; + let angle2 = angle1 + value * 1.5 * std::f32::consts::PI; + let mut path = Path::new(); + path.move_to(center + radius * Point::new(angle1.cos(), angle1.sin())); + path.arc(radius, angle1, angle2); + path.line_to(center + (radius - 4.0) * Point::new(angle2.cos(), angle2.sin())); + path.arc(radius - 4.0, angle2, angle1); + path.close(); + canvas.fill_path(&path, transform, Color::rgba(240, 240, 245, 255)); + + let center = Point::new(128.0, 128.0); + let radius = 32.0; + let angle = 0.75 * std::f32::consts::PI; + let span = 1.5 * std::f32::consts::PI; + let mut path = Path::new(); + path.move_to(center + radius * Point::new(angle.cos(), angle.sin())); + path.arc(radius, angle, angle + span); + path.line_to(center + (radius - 4.0) * Point::new(-angle.cos(), angle.sin())); + path.arc(radius - 4.0, angle + span, angle); + path.close(); + canvas.stroke_path(&path, 1.0, transform, Color::rgba(240, 240, 245, 255)); + + cx.window().present(Bitmap::new(&self.framebuffer, width, height)); + } + Event::MouseMove(pos) => { + self.mouse_pos = pos; + if let Some(gesture) = &self.gesture { + let delta = -0.005 * (pos.y - gesture.start_mouse_pos.y) as f32; + let new_value = (gesture.start_value + delta).clamp(0.0, 1.0); + self.host.set_param(0, new_value as f64); + self.params.borrow_mut().gain = new_value; + } else { + self.update_cursor(cx.window()); + } + } + Event::MouseDown(button) => { + if button == MouseButton::Left { + let pos = self.mouse_pos; + if pos.x >= 96.0 && pos.x < 160.0 && pos.y >= 96.0 && pos.y < 160.0 { + cx.window().set_cursor(Cursor::SizeNs); + self.host.begin_gesture(0); + let value = self.params.borrow().gain; + self.host.set_param(0, value as f64); + self.params.borrow_mut().gain = value; + self.gesture = Some(Gesture { + start_mouse_pos: pos, + start_value: value, + }); + return Response::Capture; + } + } + } + Event::MouseUp(button) => { + if button == MouseButton::Left { + if self.gesture.is_some() { + self.host.end_gesture(0); + self.gesture = None; + self.update_cursor(cx.window()); + return Response::Capture; + } + } + } + _ => {} + } + + Response::Ignore + } +} + +pub struct GainView { + #[allow(unused)] + app: App, + window: Window, + params: Rc>, +} + +impl GainView { + fn open(host: ViewHost, parent: &ParentWindow, params: &GainParams) -> Result { + let app = AppOptions::new().mode(AppMode::Guest).build()?; + + let mut options = WindowOptions::new(); + options.size(reflector::platform::Size::new(256.0, 256.0)); + + let raw_parent = match parent.as_raw() { + RawParent::Win32(window) => RawWindow::Win32(window), + RawParent::Cocoa(view) => RawWindow::Cocoa(view), + RawParent::X11(window) => RawWindow::X11(window), + }; + unsafe { options.raw_parent(raw_parent) }; + + let params = Rc::new(RefCell::new(params.clone())); + let mut state = ViewState::new(host, Rc::clone(¶ms)); + let window = options.open(app.handle(), move |cx, event| state.handle_event(cx, event))?; + + window.show(); + + Ok(GainView { + app, + window, + params, + }) + } +} + +impl View for GainView { + fn size(&self) -> Size { + let size = self.window.size(); + + Size { + width: size.width, + height: size.height, + } + } + + fn param_changed(&mut self, id: ParamId, value: ParamValue) { + self.params.borrow_mut().set_param(id, value); + } +}