1
0
Fork 0

Add a dedicated message and handler for parameters

Parameter widgets can send these messages to update parameters.
This commit is contained in:
Robbert van der Helm 2022-03-13 13:31:28 +01:00
parent 43590db8d0
commit 9f8bdfcfc4
3 changed files with 68 additions and 6 deletions

View file

@ -9,9 +9,13 @@ use std::fmt::Debug;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use crate::widgets::ParamMessage;
/// Re-export for convenience. /// Re-export for convenience.
pub use iced_baseview::*; pub use iced_baseview::*;
pub mod widgets;
/// Create an [`Editor`] instance using [iced](https://github.com/iced-rs/iced). The rough idea is /// 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`] /// 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 /// trait except that it receives the [`GuiContext`] alongside its initialization flags so it can
@ -42,7 +46,7 @@ pub fn create_iced_editor<E: IcedEditor>(
pub trait IcedEditor: 'static + Send + Sync + Sized { pub trait IcedEditor: 'static + Send + Sync + Sized {
/// See [`Application::Executor`]. You'll likely want to use [`crate::executor::Default`]. /// See [`Application::Executor`]. You'll likely want to use [`crate::executor::Default`].
type Executor: Executor; type Executor: Executor;
/// See [`Application::Message`]. /// See [`Application::Message`]. You should have one variant containing a [`ParamMessage`].
type Message: 'static + Clone + Debug + Send; type Message: 'static + Clone + Debug + Send;
/// See [`Application::Flags`]. /// See [`Application::Flags`].
type InitializationFlags: 'static + Clone + Send + Sync; type InitializationFlags: 'static + Clone + Send + Sync;
@ -53,7 +57,14 @@ pub trait IcedEditor: 'static + Send + Sync + Sized {
context: Arc<dyn GuiContext>, context: Arc<dyn GuiContext>,
) -> (Self, Command<Self::Message>); ) -> (Self, Command<Self::Message>);
/// 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( fn update(
&mut self, &mut self,
window: &mut WindowQueue, window: &mut WindowQueue,
@ -87,6 +98,23 @@ pub trait IcedEditor: 'static + Send + Sync + Sized {
fn renderer_settings() -> iced_baseview::renderer::settings::Settings { fn renderer_settings() -> iced_baseview::renderer::settings::Settings {
iced_baseview::renderer::settings::Settings::default() 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 // TODO: Once we add resizing, we may want to be able to remember the GUI size. In that case we need

View file

@ -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),
}

View file

@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
use nih_plug::prelude::{Editor, GuiContext}; use nih_plug::prelude::{Editor, GuiContext};
use nih_plug_iced::widgets::ParamMessage;
use nih_plug_iced::{create_iced_editor, Command, Element, IcedEditor, IcedState}; use nih_plug_iced::{create_iced_editor, Command, Element, IcedEditor, IcedState};
use std::pin::Pin; use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
@ -38,10 +39,14 @@ struct DiopserEditor {
context: Arc<dyn GuiContext>, context: Arc<dyn GuiContext>,
} }
#[derive(Debug, Clone, Copy)]
enum EditorMessage {
ParamUpdate(ParamMessage),
}
impl IcedEditor for DiopserEditor { impl IcedEditor for DiopserEditor {
type Executor = nih_plug_iced::executor::Default; type Executor = nih_plug_iced::executor::Default;
// TODO: type Message = EditorMessage;
type Message = ();
type InitializationFlags = Pin<Arc<DiopserParams>>; type InitializationFlags = Pin<Arc<DiopserParams>>;
fn new( fn new(
@ -53,12 +58,19 @@ impl IcedEditor for DiopserEditor {
(editor, Command::none()) (editor, Command::none())
} }
fn context(&self) -> &dyn GuiContext {
self.context.as_ref()
}
fn update( fn update(
&mut self, &mut self,
window: &mut nih_plug_iced::WindowQueue, _window: &mut nih_plug_iced::WindowQueue,
message: Self::Message, message: Self::Message,
) -> Command<Self::Message> { ) -> Command<Self::Message> {
// TODO: match message {
EditorMessage::ParamUpdate(message) => self.handle_param_message(message),
}
Command::none() Command::none()
} }