2022-03-19 01:26:19 +11:00
|
|
|
//! Widgets and utilities for making widgets to integrate VIZIA with NIH-plug.
|
|
|
|
//!
|
|
|
|
//! # Note
|
|
|
|
//!
|
|
|
|
//! None of these widgets are finalized, and their sizes or looks can change at any point. Feel free
|
|
|
|
//! to copy the widgets and modify them to your personal taste.
|
|
|
|
|
2023-03-08 07:22:15 +11:00
|
|
|
use crossbeam::atomic::AtomicCell;
|
|
|
|
use nih_plug::nih_debug_assert_eq;
|
2022-03-23 22:46:46 +11:00
|
|
|
use nih_plug::prelude::{GuiContext, Param, ParamPtr};
|
2022-03-19 01:26:19 +11:00
|
|
|
use std::sync::Arc;
|
2022-06-18 09:59:53 +10:00
|
|
|
use vizia::prelude::*;
|
2022-03-28 05:38:49 +11:00
|
|
|
|
|
|
|
use super::ViziaState;
|
2022-03-19 05:12:38 +11:00
|
|
|
|
2022-03-23 03:06:54 +11:00
|
|
|
mod generic_ui;
|
2022-11-07 05:13:08 +11:00
|
|
|
pub mod param_base;
|
2022-11-05 01:36:23 +11:00
|
|
|
mod param_button;
|
2022-03-19 11:17:13 +11:00
|
|
|
mod param_slider;
|
2022-03-22 08:28:54 +11:00
|
|
|
mod peak_meter;
|
2022-03-28 09:32:53 +11:00
|
|
|
mod resize_handle;
|
2022-03-19 10:38:26 +11:00
|
|
|
pub mod util;
|
|
|
|
|
2022-03-23 04:50:50 +11:00
|
|
|
pub use generic_ui::GenericUi;
|
2022-11-05 01:50:03 +11:00
|
|
|
pub use param_button::{ParamButton, ParamButtonExt};
|
2022-03-20 04:49:49 +11:00
|
|
|
pub use param_slider::{ParamSlider, ParamSliderExt, ParamSliderStyle};
|
2022-03-22 08:28:54 +11:00
|
|
|
pub use peak_meter::PeakMeter;
|
2022-03-28 09:32:53 +11:00
|
|
|
pub use resize_handle::ResizeHandle;
|
2022-03-19 11:17:13 +11:00
|
|
|
|
2022-03-19 05:12:38 +11:00
|
|
|
/// Register the default theme for the widgets exported by this module. This is automatically called
|
|
|
|
/// for you when using [`create_vizia_editor()`][super::create_vizia_editor()].
|
|
|
|
pub fn register_theme(cx: &mut Context) {
|
2022-03-23 03:33:59 +11:00
|
|
|
cx.add_theme(include_str!("../assets/widgets.css"));
|
2022-03-19 05:12:38 +11:00
|
|
|
}
|
2022-03-19 01:26:19 +11:00
|
|
|
|
|
|
|
/// An event that updates a parameter's value. Since NIH-plug manages the parameters, interacting
|
|
|
|
/// with parameter values with VIZIA works a little different from updating any other state. These
|
|
|
|
/// events are automatically handled by `nih_plug_vizia`.
|
2022-03-19 01:52:22 +11:00
|
|
|
///
|
2022-10-20 21:21:24 +11:00
|
|
|
/// Call the [`upcast()`][Self::upcast()] method to be able to emit this event through an
|
|
|
|
/// [`EventContext`][EventContext].
|
2022-03-19 01:26:19 +11:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
2022-03-19 01:52:22 +11:00
|
|
|
pub enum ParamEvent<'a, P: Param> {
|
|
|
|
/// Begin an automation gesture for a parameter.
|
|
|
|
BeginSetParameter(&'a P),
|
|
|
|
/// Set a parameter to a new normalized value. This needs to be surrounded by a matching
|
|
|
|
/// `BeginSetParameter` and `EndSetParameter`.
|
|
|
|
SetParameter(&'a P, P::Plain),
|
|
|
|
/// Set a parameter to a new normalized value. This needs to be surrounded by a matching
|
|
|
|
/// `BeginSetParameter` and `EndSetParameter`.
|
|
|
|
SetParameterNormalized(&'a P, f32),
|
|
|
|
/// End an automation gesture for a parameter.
|
|
|
|
EndSetParameter(&'a P),
|
|
|
|
}
|
|
|
|
|
2023-03-19 00:17:24 +11:00
|
|
|
/// The same as [`ParamEvent`], but type erased. Use `ParamEvent` as an easier way to construct
|
|
|
|
/// these if you are working with regular parameter objects.
|
2022-03-19 01:52:22 +11:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
2022-03-19 03:45:27 +11:00
|
|
|
pub enum RawParamEvent {
|
2022-03-19 01:26:19 +11:00
|
|
|
/// 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),
|
|
|
|
/// End an automation gesture for a parameter.
|
|
|
|
EndSetParameter(ParamPtr),
|
2022-11-16 02:43:13 +11:00
|
|
|
/// Sent by the wrapper to indicate that one or more parameter values have changed. Useful when
|
|
|
|
/// using properties based on a parameter's value that are computed inside of an event handler.
|
|
|
|
ParametersChanged,
|
2022-03-19 01:26:19 +11:00
|
|
|
}
|
|
|
|
|
2023-03-19 00:17:24 +11:00
|
|
|
/// Events that directly interact with the [`GuiContext`]. Used to trigger resizes.
|
|
|
|
pub enum GuiContextEvent {
|
|
|
|
/// Resize the window to match the current size reported by the [`ViziaState`]'s size function.
|
|
|
|
/// By changing the plugin's state that is used to determine the window's size before emitting
|
|
|
|
/// this event, the window can be resized in a declarative and predictable way:
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # use std::sync::Arc;
|
|
|
|
/// # use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
/// # use nih_plug_vizia::ViziaState;
|
|
|
|
/// # use nih_plug_vizia::vizia::prelude::*;
|
|
|
|
/// # use nih_plug_vizia::widgets::GuiContextEvent;
|
|
|
|
/// // Assuming there is some kind of state variable passed to the editor, likely stored as a
|
|
|
|
/// // `#[persist]` field in the `Params` struct:
|
|
|
|
/// let window_state = Arc::new(AtomicBool::new(false));
|
|
|
|
///
|
|
|
|
/// // And this is the `ViziaState` passed to `create_vizia_editor()`:
|
|
|
|
/// ViziaState::new(move || {
|
|
|
|
/// if window_state.load(Ordering::Relaxed) {
|
|
|
|
/// (800, 400)
|
|
|
|
/// } else {
|
|
|
|
/// (400, 400)
|
|
|
|
/// }
|
|
|
|
/// });
|
|
|
|
///
|
|
|
|
/// // Then the window's size can be toggled between the two sizes like so:
|
|
|
|
/// fn toggle_window_size(cx: &mut EventContext, window_state: Arc<AtomicBool>) {
|
|
|
|
/// window_state.fetch_xor(true, Ordering::Relaxed);
|
|
|
|
///
|
|
|
|
/// // This will cause NIH-plug to query the size from the `ViziaState` again and resize the
|
|
|
|
/// // windo to that size
|
|
|
|
/// cx.emit(GuiContextEvent::Resize);
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
Resize,
|
|
|
|
}
|
|
|
|
|
2022-03-19 01:26:19 +11:00
|
|
|
/// Handles parameter updates for VIZIA GUIs. Registered in
|
|
|
|
/// [`ViziaEditor::spawn()`][super::ViziaEditor::spawn()].
|
|
|
|
pub(crate) struct ParamModel {
|
|
|
|
pub context: Arc<dyn GuiContext>,
|
|
|
|
}
|
|
|
|
|
2022-03-28 05:38:49 +11:00
|
|
|
/// Handles interactions through `WindowEvent` for VIZIA GUIs by updating the `ViziaState`.
|
|
|
|
/// Registered in [`ViziaEditor::spawn()`][super::ViziaEditor::spawn()].
|
2022-03-28 09:32:53 +11:00
|
|
|
#[derive(Lens)]
|
2022-03-28 05:38:49 +11:00
|
|
|
pub(crate) struct WindowModel {
|
|
|
|
pub context: Arc<dyn GuiContext>,
|
|
|
|
pub vizia_state: Arc<ViziaState>,
|
2023-03-08 07:22:15 +11:00
|
|
|
|
|
|
|
/// The last known unscaled logical window size. Used to prevent sending duplicate resize
|
|
|
|
/// requests.
|
|
|
|
pub last_inner_window_size: AtomicCell<(u32, u32)>,
|
2022-03-28 05:38:49 +11:00
|
|
|
}
|
|
|
|
|
2022-03-19 01:26:19 +11:00
|
|
|
impl Model for ParamModel {
|
2022-09-23 03:52:11 +10:00
|
|
|
fn event(&mut self, _cx: &mut EventContext, event: &mut Event) {
|
2022-04-25 02:12:06 +10:00
|
|
|
// `ParamEvent` gets downcast into `NormalizedParamEvent` by the `Message`
|
|
|
|
// implementation below
|
|
|
|
event.map(|param_event, _| match *param_event {
|
|
|
|
RawParamEvent::BeginSetParameter(p) => unsafe {
|
|
|
|
self.context.raw_begin_set_parameter(p)
|
|
|
|
},
|
|
|
|
RawParamEvent::SetParameterNormalized(p, v) => unsafe {
|
|
|
|
self.context.raw_set_parameter_normalized(p, v)
|
|
|
|
},
|
|
|
|
RawParamEvent::EndSetParameter(p) => unsafe { self.context.raw_end_set_parameter(p) },
|
2022-11-16 02:43:13 +11:00
|
|
|
// This can be used by widgets to be notified when parameter values have changed
|
|
|
|
RawParamEvent::ParametersChanged => (),
|
2022-04-25 02:12:06 +10:00
|
|
|
});
|
2022-03-19 01:52:22 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-28 05:38:49 +11:00
|
|
|
impl Model for WindowModel {
|
2022-09-23 03:52:11 +10:00
|
|
|
fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
|
2023-03-19 00:17:24 +11:00
|
|
|
event.map(|gui_context_event, meta| match gui_context_event {
|
|
|
|
GuiContextEvent::Resize => {
|
|
|
|
// This will trigger a `WindowEvent::GeometryChanged`, which in turn causes the
|
|
|
|
// handler below this to be fired
|
|
|
|
let (width, height) = self.vizia_state.inner_logical_size();
|
|
|
|
cx.set_window_size(WindowSize { width, height });
|
|
|
|
|
|
|
|
meta.consume();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-04-06 21:33:01 +10:00
|
|
|
// This gets fired whenever the inner window gets resized
|
2022-04-25 02:12:06 +10:00
|
|
|
event.map(|window_event, _| {
|
2023-01-13 04:49:55 +11:00
|
|
|
if let WindowEvent::GeometryChanged { .. } = window_event {
|
2022-06-18 09:59:53 +10:00
|
|
|
let logical_size = (cx.window_size().width, cx.window_size().height);
|
2023-03-08 07:22:15 +11:00
|
|
|
// `self.vizia_state.inner_logical_size()` should match `logical_size`. Since it's
|
|
|
|
// computed we need to store the last logical size on this object.
|
|
|
|
nih_debug_assert_eq!(
|
|
|
|
logical_size,
|
|
|
|
self.vizia_state.inner_logical_size(),
|
|
|
|
"The window size set on the vizia context does not match the size returned by \
|
|
|
|
'ViziaState::size_fn'"
|
|
|
|
);
|
2022-04-25 02:12:06 +10:00
|
|
|
let old_logical_size @ (old_logical_width, old_logical_height) =
|
2023-03-08 07:22:15 +11:00
|
|
|
self.last_inner_window_size.load();
|
2022-06-18 09:59:53 +10:00
|
|
|
let scale_factor = cx.user_scale_factor();
|
2022-04-25 02:12:06 +10:00
|
|
|
let old_user_scale_factor = self.vizia_state.scale_factor.load();
|
|
|
|
|
|
|
|
// Don't do anything if the current size already matches the new size, this could
|
|
|
|
// otherwise also cause a feedback loop on resize failure
|
|
|
|
if logical_size == old_logical_size && scale_factor == old_user_scale_factor {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Our embedded baseview window will have already been resized. If the host does not
|
|
|
|
// accept our new size, then we'll try to undo that
|
2023-03-08 07:22:15 +11:00
|
|
|
self.last_inner_window_size.store(logical_size);
|
2022-04-25 02:12:06 +10:00
|
|
|
self.vizia_state.scale_factor.store(scale_factor);
|
|
|
|
if !self.context.request_resize() {
|
2023-03-08 07:22:15 +11:00
|
|
|
self.last_inner_window_size.store(old_logical_size);
|
2022-04-25 02:12:06 +10:00
|
|
|
self.vizia_state.scale_factor.store(old_user_scale_factor);
|
|
|
|
|
|
|
|
// This will cause the window's size to be reverted on the next event loop
|
2023-03-08 07:22:15 +11:00
|
|
|
// NOTE: Is resizing back the correct behavior now that the size is computed?
|
2022-06-18 09:59:53 +10:00
|
|
|
cx.set_window_size(WindowSize {
|
|
|
|
width: old_logical_width,
|
|
|
|
height: old_logical_height,
|
|
|
|
});
|
|
|
|
cx.set_user_scale_factor(old_user_scale_factor);
|
2022-04-25 02:12:06 +10:00
|
|
|
}
|
2022-04-06 21:33:01 +10:00
|
|
|
}
|
2022-04-25 02:12:06 +10:00
|
|
|
});
|
2022-03-28 05:38:49 +11:00
|
|
|
}
|
|
|
|
}
|
2022-04-06 21:33:01 +10:00
|
|
|
|
2022-03-19 03:45:27 +11:00
|
|
|
impl<P: Param> From<ParamEvent<'_, P>> for RawParamEvent {
|
2022-03-19 01:52:22 +11:00
|
|
|
fn from(event: ParamEvent<'_, P>) -> Self {
|
|
|
|
match event {
|
2022-03-19 03:45:27 +11:00
|
|
|
ParamEvent::BeginSetParameter(p) => RawParamEvent::BeginSetParameter(p.as_ptr()),
|
2022-03-19 01:52:22 +11:00
|
|
|
ParamEvent::SetParameter(p, v) => {
|
2022-03-19 03:45:27 +11:00
|
|
|
RawParamEvent::SetParameterNormalized(p.as_ptr(), p.preview_normalized(v))
|
2022-03-19 01:52:22 +11:00
|
|
|
}
|
|
|
|
ParamEvent::SetParameterNormalized(p, v) => {
|
2022-03-19 03:45:27 +11:00
|
|
|
RawParamEvent::SetParameterNormalized(p.as_ptr(), v)
|
2022-03-19 01:26:19 +11:00
|
|
|
}
|
2022-03-19 03:45:27 +11:00
|
|
|
ParamEvent::EndSetParameter(p) => RawParamEvent::EndSetParameter(p.as_ptr()),
|
2022-03-19 01:26:19 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-03-19 01:52:22 +11:00
|
|
|
|
|
|
|
impl<P: Param> ParamEvent<'_, P> {
|
|
|
|
/// Convert this event into a type erased version of itself that can be emitted through
|
2022-10-20 21:21:24 +11:00
|
|
|
/// [`EventContext::emit()`][EventContext::emit()].
|
2022-03-19 01:52:22 +11:00
|
|
|
///
|
|
|
|
/// TODO: Think of a better, clearer term for this
|
2022-03-19 03:45:27 +11:00
|
|
|
pub fn upcast(self) -> RawParamEvent {
|
2022-03-19 01:52:22 +11:00
|
|
|
self.into()
|
|
|
|
}
|
|
|
|
}
|