diff --git a/nih_plug_iced/src/lib.rs b/nih_plug_iced/src/lib.rs index e8532e12..91c60f4f 100644 --- a/nih_plug_iced/src/lib.rs +++ b/nih_plug_iced/src/lib.rs @@ -9,9 +9,13 @@ use std::fmt::Debug; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use crate::widgets::ParamMessage; + /// Re-export for convenience. pub use iced_baseview::*; +pub mod widgets; + /// Create an [`Editor`] instance using [iced](https://github.com/iced-rs/iced). The rough idea is /// that you implement [`IcedEditor`], which is roughly analogous to iced's regular [`Application`] /// trait except that it receives the [`GuiContext`] alongside its initialization flags so it can @@ -42,7 +46,7 @@ pub fn create_iced_editor( pub trait IcedEditor: 'static + Send + Sync + Sized { /// See [`Application::Executor`]. You'll likely want to use [`crate::executor::Default`]. type Executor: Executor; - /// See [`Application::Message`]. + /// See [`Application::Message`]. You should have one variant containing a [`ParamMessage`]. type Message: 'static + Clone + Debug + Send; /// See [`Application::Flags`]. type InitializationFlags: 'static + Clone + Send + Sync; @@ -53,7 +57,14 @@ pub trait IcedEditor: 'static + Send + Sync + Sized { context: Arc, ) -> (Self, Command); - /// See [`Application::update`]. + /// Return a reference to the GUI context. + /// [`handle_param_message()`][Self::handle_param_message()] uses this to interact with the + /// parameters. + fn context(&self) -> &dyn GuiContext; + + /// See [`Application::update`]. When receiving the variant that contains a + /// [`widgets::ParamMessage`] you can call + /// [`handle_param_message()`][Self::handle_param_message()] to handle the parameter update. fn update( &mut self, window: &mut WindowQueue, @@ -87,6 +98,23 @@ pub trait IcedEditor: 'static + Send + Sync + Sized { fn renderer_settings() -> iced_baseview::renderer::settings::Settings { iced_baseview::renderer::settings::Settings::default() } + + /// Handle a parameter update using the GUI context. + fn handle_param_message(&self, message: ParamMessage) { + // We can't use the fancy ParamSetter here because this needs to be type erased + let context = self.context(); + match message { + ParamMessage::BeginSetParameter(p) => unsafe { context.raw_begin_set_parameter(p) }, + ParamMessage::SetParameterNormalized(p, v) => unsafe { + context.raw_set_parameter_normalized(p, v) + }, + ParamMessage::ResetParameter(p) => unsafe { + let default_value = context.raw_default_normalized_param_value(p); + context.raw_set_parameter_normalized(p, default_value); + }, + ParamMessage::EndSetParameter(p) => unsafe { context.raw_end_set_parameter(p) }, + } + } } // TODO: Once we add resizing, we may want to be able to remember the GUI size. In that case we need diff --git a/nih_plug_iced/src/widgets.rs b/nih_plug_iced/src/widgets.rs new file mode 100644 index 00000000..2cee3172 --- /dev/null +++ b/nih_plug_iced/src/widgets.rs @@ -0,0 +1,22 @@ +//! Widgets and utilities for making widgets to integrate iced with NIH-plug. + +use nih_plug::param::internals::ParamPtr; + +/// A message to update a parameter value. Since NIH-plug manages the parameters, interacting with +/// parameter values with iced works a little different from updating any other state. This main +/// [`IcedEditor`][super::IcedEditor] should have a [`Message`][super::IcedEditor::Message] variant +/// containing this `ParamMessage`. When it receives one of those messages, it can pass it through +/// to [`self.handle_param_message()`][super::IcedEditor::handle_param_message]. +#[derive(Debug, Clone, Copy)] +pub enum ParamMessage { + /// Begin an automation gesture for a parameter. + BeginSetParameter(ParamPtr), + /// Set a parameter to a new normalized value. This needs to be surrounded by a matching + /// `BeginSetParameter` and `EndSetParameter`. + SetParameterNormalized(ParamPtr, f32), + /// Reset a parameter to its default value. This needs to be surrounded by a matching + /// `BeginSetParameter` and `EndSetParameter`. + ResetParameter(ParamPtr), + /// End an automation gesture for a parameter. + EndSetParameter(ParamPtr), +} diff --git a/plugins/diopser/src/editor.rs b/plugins/diopser/src/editor.rs index a7111101..9d264215 100644 --- a/plugins/diopser/src/editor.rs +++ b/plugins/diopser/src/editor.rs @@ -15,6 +15,7 @@ // along with this program. If not, see . use nih_plug::prelude::{Editor, GuiContext}; +use nih_plug_iced::widgets::ParamMessage; use nih_plug_iced::{create_iced_editor, Command, Element, IcedEditor, IcedState}; use std::pin::Pin; use std::sync::Arc; @@ -38,10 +39,14 @@ struct DiopserEditor { context: Arc, } +#[derive(Debug, Clone, Copy)] +enum EditorMessage { + ParamUpdate(ParamMessage), +} + impl IcedEditor for DiopserEditor { type Executor = nih_plug_iced::executor::Default; - // TODO: - type Message = (); + type Message = EditorMessage; type InitializationFlags = Pin>; fn new( @@ -53,12 +58,19 @@ impl IcedEditor for DiopserEditor { (editor, Command::none()) } + fn context(&self) -> &dyn GuiContext { + self.context.as_ref() + } + fn update( &mut self, - window: &mut nih_plug_iced::WindowQueue, + _window: &mut nih_plug_iced::WindowQueue, message: Self::Message, ) -> Command { - // TODO: + match message { + EditorMessage::ParamUpdate(message) => self.handle_param_message(message), + } + Command::none() }