From 10bd7f46eafadd32e05ea17935f26ffac4d14533 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Tue, 22 Mar 2022 17:06:54 +0100 Subject: [PATCH] Add a generic UI widget for vizia This only needs a scroll bar. --- nih_plug_vizia/assets/theme.css | 19 ++++++ nih_plug_vizia/src/widgets.rs | 2 + nih_plug_vizia/src/widgets/generic_ui.rs | 85 ++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 nih_plug_vizia/src/widgets/generic_ui.rs diff --git a/nih_plug_vizia/assets/theme.css b/nih_plug_vizia/assets/theme.css index a6bec9e2..03ae51c8 100644 --- a/nih_plug_vizia/assets/theme.css +++ b/nih_plug_vizia/assets/theme.css @@ -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; diff --git a/nih_plug_vizia/src/widgets.rs b/nih_plug_vizia/src/widgets.rs index 22cd1eb2..ba19be1c 100644 --- a/nih_plug_vizia/src/widgets.rs +++ b/nih_plug_vizia/src/widgets.rs @@ -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; diff --git a/nih_plug_vizia/src/widgets/generic_ui.rs b/nih_plug_vizia/src/widgets/generic_ui.rs new file mode 100644 index 00000000..8e4a2aca --- /dev/null +++ b/nih_plug_vizia/src/widgets/generic_ui.rs @@ -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(cx: &mut Context, params: L) -> Handle<'_, GenericUI> + where + L: Lens> + Copy, + PsPtr: 'static + Deref, + 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( + cx: &mut Context, + params: L, + mut make_widget: impl FnMut(&mut Context, ParamPtr), + ) -> Handle<'_, GenericUI> + where + L: Lens> + Copy, + PsPtr: 'static + Deref, + 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 { + Some(String::from("generic-ui")) + } +}