From b075d1b1bbe9cc6f0614b9a82262117a9a6d29d9 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Sat, 5 Feb 2022 17:11:24 +0100 Subject: [PATCH] Modify the editor API to use trait objects The alternative isn't really feasible without specialization, and NIH-plug should work on the stable compiler. --- plugins/examples/gain/src/lib.rs | 6 ++-- plugins/examples/sine/src/lib.rs | 6 ++-- src/lib.rs | 2 +- src/plugin.rs | 48 ++++++++++++-------------------- 4 files changed, 23 insertions(+), 39 deletions(-) diff --git a/plugins/examples/gain/src/lib.rs b/plugins/examples/gain/src/lib.rs index 1c28a521..164eac6f 100644 --- a/plugins/examples/gain/src/lib.rs +++ b/plugins/examples/gain/src/lib.rs @@ -18,8 +18,8 @@ extern crate nih_plug; use nih_plug::{ - formatters, util, Buffer, BufferConfig, BusConfig, NoEditor, Plugin, ProcessContext, - ProcessStatus, Vst3Plugin, + formatters, util, Buffer, BufferConfig, BusConfig, Plugin, ProcessContext, ProcessStatus, + Vst3Plugin, }; use nih_plug::{BoolParam, FloatParam, Param, Params, Range, Smoother, SmoothingStyle}; use parking_lot::RwLock; @@ -86,8 +86,6 @@ impl Default for GainParams { } impl Plugin for Gain { - type Editor = NoEditor; - const NAME: &'static str = "Gain"; const VENDOR: &'static str = "Moist Plugins GmbH"; const URL: &'static str = "https://youtu.be/dQw4w9WgXcQ"; diff --git a/plugins/examples/sine/src/lib.rs b/plugins/examples/sine/src/lib.rs index b54a8fda..7ecb3e51 100644 --- a/plugins/examples/sine/src/lib.rs +++ b/plugins/examples/sine/src/lib.rs @@ -18,8 +18,8 @@ extern crate nih_plug; use nih_plug::{ - formatters, util, Buffer, BufferConfig, BusConfig, NoEditor, Plugin, ProcessContext, - ProcessStatus, Vst3Plugin, + formatters, util, Buffer, BufferConfig, BusConfig, Plugin, ProcessContext, ProcessStatus, + Vst3Plugin, }; use nih_plug::{BoolParam, FloatParam, Param, Params, Range, Smoother, SmoothingStyle}; use std::f32::consts; @@ -123,8 +123,6 @@ impl Sine { } impl Plugin for Sine { - type Editor = NoEditor; - const NAME: &'static str = "Sine Test Tone"; const VENDOR: &'static str = "Moist Plugins GmbH"; const URL: &'static str = "https://youtu.be/dQw4w9WgXcQ"; diff --git a/src/lib.rs b/src/lib.rs index 95cd2431..71ec93b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,7 +33,7 @@ pub use param::range::Range; pub use param::smoothing::{Smoother, SmoothingStyle}; pub use param::{BoolParam, FloatParam, IntParam, Param}; pub use plugin::{ - BufferConfig, BusConfig, Editor, NoEditor, NoteEvent, Plugin, ProcessStatus, Vst3Plugin, + BufferConfig, BusConfig, Editor, NoteEvent, Plugin, ProcessStatus, RawWindowHandle, Vst3Plugin, }; // The rest is either internal or already re-exported diff --git a/src/plugin.rs b/src/plugin.rs index bf3f6833..550c502b 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -14,13 +14,16 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use raw_window_handle::RawWindowHandle; use std::pin::Pin; +use std::sync::Arc; use crate::buffer::Buffer; use crate::context::{GuiContext, ProcessContext}; use crate::param::internals::Params; +/// A raw window handle for platform and GUI framework agnostic editors. +pub use raw_window_handle::RawWindowHandle; + /// Basic functionality that needs to be implemented by a plugin. The wrappers will use this to /// expose the plugin in a particular plugin format. /// @@ -41,11 +44,6 @@ use crate::param::internals::Params; /// - GUIs #[allow(unused_variables)] pub trait Plugin: Default + Send + Sync + 'static { - /// The type of the GUI editor instance belonging to this plugin. Use [NoEditor] when you don't - /// need an editor. Make sure to implement both the [Self::create_editor()] and - /// [Self::editor_size()] functions when you do add an editor. - type Editor: Editor; - const NAME: &'static str; const VENDOR: &'static str; const URL: &'static str; @@ -71,11 +69,13 @@ pub trait Plugin: Default + Send + Sync + 'static { /// plugin receives an update. fn params(&self) -> Pin<&dyn Params>; - /// Create an editor for this plugin and embed it in the parent window. The idea is that you - /// take a reference to your [Params] in your editor to be able to read the current values. Then - /// whenever you need to change any of those values, you can use the methods on the [GuiContext] - /// that's passed to this function. When you change a parameter value there it will be - /// broadcasted to the host and also updated in your [Params] struct. + /// Create an editor for this plugin and embed it in the parent window. A plugin editor will + /// likely want to interact with the plugin's parameters, so the idea is that you take a + /// reference to your [Params] object in your editor as well as the [GuiContext] that's passed + /// to this function. You can then read the parameter values directly from your [Params] object, + /// and modifying the values can be done using the functions on [GuiContext::setter()]. When you + /// change a parameter value that way it will be broadcasted to the host and also updated in + /// your [Params] struct. // // TODO: Think of how this would work with the event loop. On Linux the wrapper must provide a // timer using VST3's `IRunLoop` interface, but on Window and macOS the window would @@ -83,15 +83,16 @@ pub trait Plugin: Default + Send + Sync + 'static { // otherwise be basically impossible to have this still be GUI-framework agnostic. Any // callback that deos involve actual GUI operations will still be spooled to the IRunLoop // instance. - fn create_editor( - &self, + fn create_editor<'a, 'context: 'a>( + &'a self, parent: RawWindowHandle, - context: &impl GuiContext, - ) -> Option { + context: Arc, + ) -> Option> { None } - /// Return the current size of the plugin's editor, if it has one. + /// Return the current size of the plugin's editor, if it has one. This is also used to check + /// whether the plugin has an editor without creating one. fn editor_size(&self) -> Option<(u32, u32)> { None } @@ -149,13 +150,12 @@ pub trait Vst3Plugin: Plugin { } /// An editor for a [Plugin]. The [Drop] implementation gets called when the host closes the editor. -/// If you don't have or need an editor, then you can use the [NoEditor] struct as a placeholder. // // XXX: Requiring a [Drop] bound is a bit unorthodox, but together with [Plugin::create_editor] it // encodes the lifecycle of an editor perfectly as you cannot have duplicate (or missing) // initialize and close calls. Maybe think this over again later. #[allow(drop_bounds)] -pub trait Editor: Drop { +pub trait Editor: Drop + Send + Sync { /// Return the (currnent) size of the editor in pixels as a `(width, height)` pair. fn size(&self) -> (u32, u32); @@ -163,18 +163,6 @@ pub trait Editor: Drop { // TODO: Resizing } -pub struct NoEditor; - -impl Editor for NoEditor { - fn size(&self) -> (u32, u32) { - (0, 0) - } -} - -impl Drop for NoEditor { - fn drop(&mut self) {} -} - /// We only support a single main input and output bus at the moment. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct BusConfig {