1
0
Fork 0

Add a generic UI widget for vizia

This only needs a scroll bar.
This commit is contained in:
Robbert van der Helm 2022-03-22 17:06:54 +01:00
parent 4e09491dd5
commit 10bd7f46ea
3 changed files with 106 additions and 0 deletions

View file

@ -1,5 +1,24 @@
/* Default styling for the widgets included in nih_plug_vizia */
generic-ui {
child-space: 10px;
col-between: 5px;
layout-type: column;
row-between: 5px;
}
generic-ui .row {
col-between: 6px;
layout-type: row;
}
generic-ui .label {
left: 1s;
top: 1s;
bottom: 1s;
right: 0;
}
param-slider {
height: 30px;
width: 180px;

View file

@ -11,10 +11,12 @@ use std::sync::Arc;
use vizia::{Context, Model};
mod generic_ui;
mod param_slider;
mod peak_meter;
pub mod util;
pub use generic_ui::GenericUI;
pub use param_slider::{ParamSlider, ParamSliderExt, ParamSliderStyle};
pub use peak_meter::PeakMeter;

View file

@ -0,0 +1,85 @@
//! Generic UIs for NIH-plug using VIZIA.
use std::{ops::Deref, pin::Pin};
use nih_plug::param::internals::ParamPtr;
use nih_plug::prelude::Params;
use vizia::*;
use super::{ParamSlider, ParamSliderExt, ParamSliderStyle};
/// Shows a generic UI for a [`Params`] object. For additional flexibility you can either use the
/// [`new()`][`Self::new()`] method to have the generic UI decide which widget to use for your
/// parmaeters, or you can use the [`new_custom()`][`Self::new_custom()`] method to determine this
/// yourself.
pub struct GenericUI;
impl GenericUI {
/// Creates a new [`GenericUi`] for all provided parameters. Use
/// [`new_custom()`][Self::new_custom()] to decide which widget gets used for each parameter.
pub fn new<L, PsPtr, Ps>(cx: &mut Context, params: L) -> Handle<'_, GenericUI>
where
L: Lens<Target = Pin<PsPtr>> + Copy,
PsPtr: 'static + Deref<Target = Ps>,
Ps: Params,
{
// Basic styling is done in the `theme.css` style sheet
// TODO: Scrolling
Self::new_custom(cx, params, |cx, param_ptr| {
HStack::new(cx, |cx| {
// Align this on the right
Label::new(cx, unsafe { param_ptr.name() }).class("label");
// TODO: Come up with a less hacky way to iterate over the mapping like this while
// keeping the clean interface on the `ParamSlider` widget
unsafe {
match param_ptr {
ParamPtr::FloatParam(p) => ParamSlider::new(cx, params, move |_| &*p),
ParamPtr::IntParam(p) => ParamSlider::new(cx, params, move |_| &*p),
ParamPtr::BoolParam(p) => ParamSlider::new(cx, params, move |_| &*p),
ParamPtr::EnumParam(p) => ParamSlider::new(cx, params, move |_| &*p),
}
}
.set_style(match unsafe { param_ptr.step_count() } {
Some(step_count) if step_count <= 3 => ParamSliderStyle::CurrentStepLabeled,
Some(step_count) if step_count <= 64 => ParamSliderStyle::CurrentStep,
Some(_) => ParamSliderStyle::FromLeft,
// This is already the default, but continuous parameters should be drawn from
// the center if the default is also centered, or from the left if it is not
None => ParamSliderStyle::Centered,
})
.class("widget");
})
.class("row");
})
}
/// Creates a new [`GenericUi`] for all provided parameters using a custom closure that receives
/// a function that should draw some widget for each parameter.
pub fn new_custom<L, PsPtr, Ps>(
cx: &mut Context,
params: L,
mut make_widget: impl FnMut(&mut Context, ParamPtr),
) -> Handle<'_, GenericUI>
where
L: Lens<Target = Pin<PsPtr>> + Copy,
PsPtr: 'static + Deref<Target = Ps>,
Ps: Params,
{
// Basic styling is done in the `theme.css` style sheet
Self.build2(cx, |cx| {
// Rust does not have existential types, otherwise we could have passed functions that
// map `params` to some `impl Param` and everything would have been a lot neater
let param_map = &*params.map(|params| params.as_ref().param_map()).get(cx);
for (_, param_ptr, _) in param_map {
make_widget(cx, *param_ptr);
}
})
}
}
impl View for GenericUI {
fn element(&self) -> Option<String> {
Some(String::from("generic-ui"))
}
}