2022-03-18 09:14:45 +11:00
|
|
|
//! [VIZIA](https://github.com/vizia/vizia) editor support for NIH plug.
|
|
|
|
|
2022-07-01 23:50:23 +10:00
|
|
|
// See the comment in the main `nih_plug` crate
|
|
|
|
#![allow(clippy::type_complexity)]
|
|
|
|
|
2022-03-18 09:14:45 +11:00
|
|
|
use crossbeam::atomic::AtomicCell;
|
2022-10-20 21:26:12 +11:00
|
|
|
use nih_plug::params::persist::PersistentField;
|
2022-11-16 02:18:38 +11:00
|
|
|
use nih_plug::prelude::{Editor, GuiContext};
|
2022-07-14 07:07:59 +10:00
|
|
|
use serde::{Deserialize, Serialize};
|
2022-03-18 09:14:45 +11:00
|
|
|
use std::sync::atomic::{AtomicBool, Ordering};
|
|
|
|
use std::sync::Arc;
|
2022-06-18 09:59:53 +10:00
|
|
|
use vizia::prelude::*;
|
2022-03-18 09:14:45 +11:00
|
|
|
|
|
|
|
// Re-export for convenience
|
|
|
|
pub use vizia;
|
|
|
|
|
2022-03-18 10:22:58 +11:00
|
|
|
pub mod assets;
|
2022-11-16 02:18:38 +11:00
|
|
|
mod editor;
|
2022-11-07 00:02:48 +11:00
|
|
|
pub mod vizia_assets;
|
2022-03-19 01:26:19 +11:00
|
|
|
pub mod widgets;
|
2022-03-18 09:14:45 +11:00
|
|
|
|
|
|
|
/// Create an [`Editor`] instance using a [`vizia`][::vizia] GUI. The [`ViziaState`] passed to this
|
|
|
|
/// function contains the GUI's intitial size, and this is kept in sync whenever the GUI gets
|
|
|
|
/// resized. You can also use this to know if the GUI is open, so you can avoid performing
|
|
|
|
/// potentially expensive calculations while the GUI is not open. If you want this size to be
|
|
|
|
/// persisted when restoring a plugin instance, then you can store it in a `#[persist = "key"]`
|
|
|
|
/// field on your parameters struct.
|
|
|
|
///
|
2022-04-08 00:14:34 +10:00
|
|
|
/// The [`GuiContext`] is also passed to the app function. This is only meant for saving and
|
|
|
|
/// restoring state as part of your plugin's preset handling. You should not interact with this
|
|
|
|
/// directly to set parameters. Use the `ParamEvent`s instead.
|
|
|
|
///
|
2022-11-06 23:48:12 +11:00
|
|
|
/// The `theming` argument controls what level of theming to apply. If you use
|
|
|
|
/// [`ViziaTheming::Custom`], then you **need** to call
|
|
|
|
/// [`nih_plug_vizia::assets::register_noto_sans_light()`][assets::register_noto_sans_light()] at
|
2022-11-07 00:09:13 +11:00
|
|
|
/// the start of your app function. Vizia's included fonts are also not registered by default. If
|
|
|
|
/// you use the Roboto font that normally comes with Vizia or any of its emoji or icon fonts, you
|
|
|
|
/// also need to register those using the functions in
|
|
|
|
/// [`nih_plug_vizia::vizia_assets`][crate::vizia_assets].
|
2022-11-06 23:48:12 +11:00
|
|
|
///
|
2022-03-18 09:14:45 +11:00
|
|
|
/// See [VIZIA](https://github.com/vizia/vizia)'s repository for examples on how to use this.
|
2022-11-06 23:26:32 +11:00
|
|
|
pub fn create_vizia_editor<F>(
|
|
|
|
vizia_state: Arc<ViziaState>,
|
|
|
|
theming: ViziaTheming,
|
|
|
|
app: F,
|
|
|
|
) -> Option<Box<dyn Editor>>
|
2022-03-18 09:14:45 +11:00
|
|
|
where
|
2022-04-08 00:14:34 +10:00
|
|
|
F: Fn(&mut Context, Arc<dyn GuiContext>) + 'static + Send + Sync,
|
2022-03-18 09:14:45 +11:00
|
|
|
{
|
2022-11-16 02:18:38 +11:00
|
|
|
Some(Box::new(editor::ViziaEditor {
|
2022-03-18 09:14:45 +11:00
|
|
|
vizia_state,
|
|
|
|
app: Arc::new(app),
|
2022-11-06 23:26:32 +11:00
|
|
|
theming,
|
2022-03-27 11:58:40 +11:00
|
|
|
|
2022-04-28 02:33:08 +10:00
|
|
|
// TODO: We can't get the size of the window when baseview does its own scaling, so if the
|
|
|
|
// host does not set a scale factor on Windows or Linux we should just use a factor of
|
|
|
|
// 1. That may make the GUI tiny but it also prevents it from getting cut off.
|
|
|
|
#[cfg(target_os = "macos")]
|
2022-03-27 11:58:40 +11:00
|
|
|
scaling_factor: AtomicCell::new(None),
|
2022-04-28 02:33:08 +10:00
|
|
|
#[cfg(not(target_os = "macos"))]
|
|
|
|
scaling_factor: AtomicCell::new(Some(1.0)),
|
2022-03-27 11:58:40 +11:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2022-11-06 23:26:32 +11:00
|
|
|
/// Controls what level of theming to apply to the editor.
|
|
|
|
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
|
|
|
|
pub enum ViziaTheming {
|
|
|
|
/// Disable both `nih_plug_vizia`'s and vizia's built-in theming.
|
|
|
|
None,
|
2022-11-07 00:09:13 +11:00
|
|
|
/// Disable `nih_plug_vizia`'s custom theming. Vizia's included fonts are also not registered by
|
|
|
|
/// default. If you use the Roboto font that normally comes with Vizia or any of its emoji or
|
|
|
|
/// icon fonts, you need to register those using the functions in
|
|
|
|
/// [`nih_plug_vizia::vizia_assets`][crate::vizia_assets].
|
2022-11-06 23:26:32 +11:00
|
|
|
Builtin,
|
2022-11-06 23:48:12 +11:00
|
|
|
/// Apply `nih_plug_vizia`'s custom theming. This is the default. You **need** to call
|
|
|
|
/// [`nih_plug_vizia::assets::register_noto_sans_light()`][assets::register_noto_sans_light()]
|
|
|
|
/// at the start of your app function for the font to work correctly.
|
2022-11-06 23:26:32 +11:00
|
|
|
#[default]
|
|
|
|
Custom,
|
2022-03-18 09:14:45 +11:00
|
|
|
}
|
|
|
|
|
2022-07-14 07:07:59 +10:00
|
|
|
/// State for an `nih_plug_vizia` editor. The scale factor can be manipulated at runtime by changing
|
2022-04-12 00:23:34 +10:00
|
|
|
/// `cx.user_scale_factor`.
|
2022-07-14 07:07:59 +10:00
|
|
|
#[derive(Serialize, Deserialize)]
|
2022-03-18 09:14:45 +11:00
|
|
|
pub struct ViziaState {
|
2022-03-28 05:38:49 +11:00
|
|
|
/// The window's size in logical pixels before applying `scale_factor`.
|
2022-10-20 21:26:12 +11:00
|
|
|
#[serde(with = "nih_plug::params::persist::serialize_atomic_cell")]
|
2022-03-18 09:14:45 +11:00
|
|
|
size: AtomicCell<(u32, u32)>,
|
2022-03-28 05:38:49 +11:00
|
|
|
/// A scale factor that should be applied to `size` separate from from any system HiDPI scaling.
|
|
|
|
/// This can be used to allow GUIs to be scaled uniformly.
|
2022-10-20 21:26:12 +11:00
|
|
|
#[serde(with = "nih_plug::params::persist::serialize_atomic_cell")]
|
2022-03-28 05:38:49 +11:00
|
|
|
scale_factor: AtomicCell<f64>,
|
2022-07-14 07:07:59 +10:00
|
|
|
/// Whether the editor's window is currently open.
|
|
|
|
#[serde(skip)]
|
2022-03-18 09:14:45 +11:00
|
|
|
open: AtomicBool,
|
|
|
|
}
|
|
|
|
|
2022-07-14 07:07:59 +10:00
|
|
|
impl<'a> PersistentField<'a, ViziaState> for Arc<ViziaState> {
|
|
|
|
fn set(&self, new_value: ViziaState) {
|
|
|
|
self.size.store(new_value.size.load());
|
|
|
|
self.scale_factor.store(new_value.scale_factor.load());
|
|
|
|
}
|
|
|
|
|
|
|
|
fn map<F, R>(&self, f: F) -> R
|
|
|
|
where
|
|
|
|
F: Fn(&ViziaState) -> R,
|
|
|
|
{
|
|
|
|
f(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-18 09:14:45 +11:00
|
|
|
impl ViziaState {
|
2022-03-28 05:38:49 +11:00
|
|
|
/// Initialize the GUI's state. This value can be passed to [`create_vizia_editor()`]. The
|
|
|
|
/// window size is in logical pixels, so before it is multiplied by the DPI scaling factor.
|
2022-03-18 09:14:45 +11:00
|
|
|
pub fn from_size(width: u32, height: u32) -> Arc<ViziaState> {
|
|
|
|
Arc::new(ViziaState {
|
|
|
|
size: AtomicCell::new((width, height)),
|
2022-03-28 05:38:49 +11:00
|
|
|
scale_factor: AtomicCell::new(1.0),
|
2022-03-18 09:14:45 +11:00
|
|
|
open: AtomicBool::new(false),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-03-28 05:38:49 +11:00
|
|
|
/// The same as [`from_size()`][Self::from_size()], but with a separate initial scale factor.
|
|
|
|
/// This scale factor gets applied on top of any HiDPI scaling, and it can be modified at
|
2022-04-12 00:23:34 +10:00
|
|
|
/// runtime by changing `cx.user_scale_factor`.
|
2022-03-28 05:38:49 +11:00
|
|
|
pub fn from_size_with_scale(width: u32, height: u32, scale_factor: f64) -> Arc<ViziaState> {
|
|
|
|
Arc::new(ViziaState {
|
|
|
|
size: AtomicCell::new((width, height)),
|
|
|
|
scale_factor: AtomicCell::new(scale_factor),
|
|
|
|
open: AtomicBool::new(false),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-04-27 03:39:03 +10:00
|
|
|
/// Returns a `(width, height)` pair for the current size of the GUI in logical pixels, after
|
2022-03-28 05:38:49 +11:00
|
|
|
/// applying the user scale factor.
|
|
|
|
pub fn scaled_logical_size(&self) -> (u32, u32) {
|
|
|
|
let (logical_width, logical_height) = self.size.load();
|
|
|
|
let scale_factor = self.scale_factor.load();
|
|
|
|
|
|
|
|
(
|
|
|
|
(logical_width as f64 * scale_factor).round() as u32,
|
|
|
|
(logical_height as f64 * scale_factor).round() as u32,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-04-27 03:39:03 +10:00
|
|
|
/// Returns a `(width, height)` pair for the current size of the GUI in logical pixels before
|
2022-03-28 05:38:49 +11:00
|
|
|
/// applying the user scale factor.
|
|
|
|
pub fn inner_logical_size(&self) -> (u32, u32) {
|
2022-03-18 09:14:45 +11:00
|
|
|
self.size.load()
|
|
|
|
}
|
|
|
|
|
2022-03-28 05:38:49 +11:00
|
|
|
/// Get the non-DPI related uniform scaling factor the GUI's size will be multiplied with. This
|
2022-04-12 00:23:34 +10:00
|
|
|
/// can be changed by changing `cx.user_scale_factor`.
|
2022-03-28 05:38:49 +11:00
|
|
|
pub fn user_scale_factor(&self) -> f64 {
|
|
|
|
self.scale_factor.load()
|
|
|
|
}
|
|
|
|
|
2022-03-18 09:14:45 +11:00
|
|
|
/// Whether the GUI is currently visible.
|
|
|
|
// Called `is_open()` instead of `open()` to avoid the ambiguity.
|
|
|
|
pub fn is_open(&self) -> bool {
|
|
|
|
self.open.load(Ordering::Acquire)
|
|
|
|
}
|
|
|
|
}
|