Skip to content

Commit 6d96cdf

Browse files
committed
port gain example GUI to new API
1 parent 79dd7cf commit 6d96cdf

File tree

2 files changed

+189
-10
lines changed

2 files changed

+189
-10
lines changed

examples/gain/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ formats = ["clap", "vst3"]
1010

1111
[dependencies]
1212
coupler = { workspace = true, features = ["derive"] }
13-
coupler-reflector = { path = "../../coupler-reflector" }
13+
reflector = { git = "https://github.com/coupler-rs/reflector", rev = "eb40c59a71470077b4f58a26685796d1609ea8ed" }
1414
serde = { version = "1.0", features = ["derive"] }
1515
serde_json = "1.0"

examples/gain/src/lib.rs

+188-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1+
use std::cell::RefCell;
12
use std::io::{self, Read, Write};
3+
use std::rc::Rc;
24

35
use serde::{Deserialize, Serialize};
46

57
use coupler::format::clap::*;
68
use coupler::format::vst3::*;
9+
use coupler::params::{ParamId, ParamValue};
10+
use coupler::view::{ParentWindow, RawParent, Size, View};
711
use coupler::{buffers::*, bus::*, engine::*, events::*, host::*, params::*, plugin::*, view::*};
812

9-
use coupler_reflector::PluginWindow;
13+
use reflector::graphics::Renderer;
14+
use reflector::platform::{
15+
App, AppMode, AppOptions, Bitmap, Cursor, MouseButton, Point, RawWindow, Response, Result,
16+
Window, WindowContext, WindowOptions,
17+
};
1018

1119
#[derive(Params, Serialize, Deserialize, Clone)]
1220
struct GainParams {
@@ -26,7 +34,7 @@ pub struct Gain {
2634

2735
impl Plugin for Gain {
2836
type Engine = GainEngine;
29-
type View = PluginWindow;
37+
type View = GainView;
3038

3139
fn info() -> PluginInfo {
3240
PluginInfo {
@@ -84,13 +92,8 @@ impl Plugin for Gain {
8492
}
8593
}
8694

87-
fn view(&mut self, _host: ViewHost, parent: &ParentWindow) -> Self::View {
88-
let size = Size {
89-
width: 512.0,
90-
height: 512.0,
91-
};
92-
93-
PluginWindow::open(parent, size).unwrap()
95+
fn view(&mut self, host: ViewHost, parent: &ParentWindow) -> Self::View {
96+
GainView::open(host, parent, &self.params).unwrap()
9497
}
9598
}
9699

@@ -146,3 +149,179 @@ impl Engine for GainEngine {
146149
}
147150
}
148151
}
152+
153+
struct Gesture {
154+
start_mouse_pos: Point,
155+
start_value: f32,
156+
}
157+
158+
struct ViewState {
159+
host: ViewHost,
160+
params: Rc<RefCell<GainParams>>,
161+
renderer: Renderer,
162+
framebuffer: Vec<u32>,
163+
mouse_pos: Point,
164+
gesture: Option<Gesture>,
165+
}
166+
167+
impl ViewState {
168+
fn new(host: ViewHost, params: Rc<RefCell<GainParams>>) -> ViewState {
169+
ViewState {
170+
host,
171+
params,
172+
renderer: Renderer::new(),
173+
framebuffer: Vec::new(),
174+
mouse_pos: Point { x: -1.0, y: -1.0 },
175+
gesture: None,
176+
}
177+
}
178+
179+
fn update_cursor(&self, window: &Window) {
180+
let pos = self.mouse_pos;
181+
if pos.x >= 96.0 && pos.x < 160.0 && pos.y >= 96.0 && pos.y < 160.0 {
182+
window.set_cursor(Cursor::SizeNs);
183+
} else {
184+
window.set_cursor(Cursor::Arrow);
185+
}
186+
}
187+
188+
fn handle_event(&mut self, cx: &WindowContext, event: reflector::platform::Event) -> Response {
189+
use reflector::graphics::{Affine, Color, Path, Point};
190+
use reflector::platform::Event;
191+
192+
match event {
193+
Event::Frame => {
194+
let scale = cx.window().scale();
195+
let size = cx.window().size();
196+
let width = (size.width * scale) as usize;
197+
let height = (size.height * scale) as usize;
198+
self.framebuffer.resize(width * height, 0xFF000000);
199+
200+
let mut canvas = self.renderer.canvas(&mut self.framebuffer, width, height);
201+
202+
canvas.clear(Color::rgba(21, 26, 31, 255));
203+
204+
let transform = Affine::scale(scale as f32);
205+
206+
let value = self.params.borrow().gain;
207+
208+
let center = Point::new(128.0, 128.0);
209+
let radius = 32.0;
210+
let angle1 = 0.75 * std::f32::consts::PI;
211+
let angle2 = angle1 + value * 1.5 * std::f32::consts::PI;
212+
let mut path = Path::new();
213+
path.move_to(center + radius * Point::new(angle1.cos(), angle1.sin()));
214+
path.arc(radius, angle1, angle2);
215+
path.line_to(center + (radius - 4.0) * Point::new(angle2.cos(), angle2.sin()));
216+
path.arc(radius - 4.0, angle2, angle1);
217+
path.close();
218+
canvas.fill_path(&path, transform, Color::rgba(240, 240, 245, 255));
219+
220+
let center = Point::new(128.0, 128.0);
221+
let radius = 32.0;
222+
let angle = 0.75 * std::f32::consts::PI;
223+
let span = 1.5 * std::f32::consts::PI;
224+
let mut path = Path::new();
225+
path.move_to(center + radius * Point::new(angle.cos(), angle.sin()));
226+
path.arc(radius, angle, angle + span);
227+
path.line_to(center + (radius - 4.0) * Point::new(-angle.cos(), angle.sin()));
228+
path.arc(radius - 4.0, angle + span, angle);
229+
path.close();
230+
canvas.stroke_path(&path, 1.0, transform, Color::rgba(240, 240, 245, 255));
231+
232+
cx.window().present(Bitmap::new(&self.framebuffer, width, height));
233+
}
234+
Event::MouseMove(pos) => {
235+
self.mouse_pos = pos;
236+
if let Some(gesture) = &self.gesture {
237+
let delta = -0.005 * (pos.y - gesture.start_mouse_pos.y) as f32;
238+
let new_value = (gesture.start_value + delta).clamp(0.0, 1.0);
239+
self.host.set_param(0, new_value as f64);
240+
self.params.borrow_mut().gain = new_value;
241+
} else {
242+
self.update_cursor(cx.window());
243+
}
244+
}
245+
Event::MouseDown(button) => {
246+
if button == MouseButton::Left {
247+
let pos = self.mouse_pos;
248+
if pos.x >= 96.0 && pos.x < 160.0 && pos.y >= 96.0 && pos.y < 160.0 {
249+
cx.window().set_cursor(Cursor::SizeNs);
250+
self.host.begin_gesture(0);
251+
let value = self.params.borrow().gain;
252+
self.host.set_param(0, value as f64);
253+
self.params.borrow_mut().gain = value;
254+
self.gesture = Some(Gesture {
255+
start_mouse_pos: pos,
256+
start_value: value,
257+
});
258+
return Response::Capture;
259+
}
260+
}
261+
}
262+
Event::MouseUp(button) => {
263+
if button == MouseButton::Left {
264+
if self.gesture.is_some() {
265+
self.host.end_gesture(0);
266+
self.gesture = None;
267+
self.update_cursor(cx.window());
268+
return Response::Capture;
269+
}
270+
}
271+
}
272+
_ => {}
273+
}
274+
275+
Response::Ignore
276+
}
277+
}
278+
279+
pub struct GainView {
280+
#[allow(unused)]
281+
app: App,
282+
window: Window,
283+
params: Rc<RefCell<GainParams>>,
284+
}
285+
286+
impl GainView {
287+
fn open(host: ViewHost, parent: &ParentWindow, params: &GainParams) -> Result<GainView> {
288+
let app = AppOptions::new().mode(AppMode::Guest).build()?;
289+
290+
let mut options = WindowOptions::new();
291+
options.size(reflector::platform::Size::new(256.0, 256.0));
292+
293+
let raw_parent = match parent.as_raw() {
294+
RawParent::Win32(window) => RawWindow::Win32(window),
295+
RawParent::Cocoa(view) => RawWindow::Cocoa(view),
296+
RawParent::X11(window) => RawWindow::X11(window),
297+
};
298+
unsafe { options.raw_parent(raw_parent) };
299+
300+
let params = Rc::new(RefCell::new(params.clone()));
301+
let mut state = ViewState::new(host, Rc::clone(&params));
302+
let window = options.open(app.handle(), move |cx, event| state.handle_event(cx, event))?;
303+
304+
window.show();
305+
306+
Ok(GainView {
307+
app,
308+
window,
309+
params,
310+
})
311+
}
312+
}
313+
314+
impl View for GainView {
315+
fn size(&self) -> Size {
316+
let size = self.window.size();
317+
318+
Size {
319+
width: size.width,
320+
height: size.height,
321+
}
322+
}
323+
324+
fn param_changed(&mut self, id: ParamId, value: ParamValue) {
325+
self.params.borrow_mut().set_param(id, value);
326+
}
327+
}

0 commit comments

Comments
 (0)