1
0
Fork 0

Move nih_plug_vizia editor to its own module

This commit is contained in:
Robbert van der Helm 2022-11-15 16:18:38 +01:00
parent c92c2a189b
commit 6ebc759706
2 changed files with 129 additions and 119 deletions

View file

@ -0,0 +1,126 @@
//! The [`Editor`] trait implementation for Vizia editors.
use baseview::{WindowHandle, WindowScalePolicy};
use crossbeam::atomic::AtomicCell;
use nih_plug::prelude::{Editor, GuiContext, ParentWindowHandle};
use std::sync::atomic::Ordering;
use std::sync::Arc;
use vizia::prelude::*;
use crate::{assets, widgets, ViziaState, ViziaTheming};
/// An [`Editor`] implementation that calls an vizia draw loop.
pub(crate) struct ViziaEditor {
pub(crate) vizia_state: Arc<ViziaState>,
/// The user's app function.
pub(crate) app: Arc<dyn Fn(&mut Context, Arc<dyn GuiContext>) + 'static + Send + Sync>,
/// What level of theming to apply. See [`ViziaEditorTheming`].
pub(crate) theming: ViziaTheming,
/// The scaling factor reported by the host, if any. On macOS this will never be set and we
/// should use the system scaling factor instead.
pub(crate) scaling_factor: AtomicCell<Option<f32>>,
}
impl Editor for ViziaEditor {
fn spawn(
&self,
parent: ParentWindowHandle,
context: Arc<dyn GuiContext>,
) -> Box<dyn std::any::Any + Send> {
let app = self.app.clone();
let vizia_state = self.vizia_state.clone();
let theming = self.theming;
let (unscaled_width, unscaled_height) = vizia_state.inner_logical_size();
let system_scaling_factor = self.scaling_factor.load();
let user_scale_factor = vizia_state.user_scale_factor();
let mut application = Application::new(move |cx| {
// Set some default styles to match the iced integration
if theming >= ViziaTheming::Custom {
// NOTE: vizia's font rendering looks way too dark and thick. Going one font weight
// lower seems to compensate for this.
cx.set_default_font(assets::NOTO_SANS_LIGHT);
cx.add_theme(include_str!("../assets/theme.css"));
// There doesn't seem to be any way to bundle styles with a widget, so we'll always
// include the style sheet for our custom widgets at context creation
widgets::register_theme(cx);
}
// Any widget can change the parameters by emitting `ParamEvent` events. This model will
// handle them automatically.
widgets::ParamModel {
context: context.clone(),
}
.build(cx);
// And we'll link `WindowEvent::ResizeWindow` and `WindowEvent::SetScale` events to our
// `ViziaState`. We'll notify the host when any of these change.
widgets::WindowModel {
context: context.clone(),
vizia_state: vizia_state.clone(),
}
.build(cx);
app(cx, context.clone())
})
.with_scale_policy(
system_scaling_factor
.map(|factor| WindowScalePolicy::ScaleFactor(factor as f64))
.unwrap_or(WindowScalePolicy::SystemScaleFactor),
)
.inner_size((unscaled_width, unscaled_height))
.user_scale_factor(user_scale_factor);
// This way the plugin can decide to use none of the built in theming
if theming == ViziaTheming::None {
application = application.ignore_default_theme();
}
let window = application.open_parented(&parent);
self.vizia_state.open.store(true, Ordering::Release);
Box::new(ViziaEditorHandle {
vizia_state: self.vizia_state.clone(),
window,
})
}
fn size(&self) -> (u32, u32) {
// This includes the user scale factor if set, but not any HiDPI scaling
self.vizia_state.scaled_logical_size()
}
fn set_scale_factor(&self, factor: f32) -> bool {
// We're making things a bit more complicated by having both a system scale factor, which is
// used for HiDPI and also known to the host, and a user scale factor that the user can use
// to arbitrarily resize the GUI
self.scaling_factor.store(Some(factor));
true
}
fn param_values_changed(&self) {
// TODO: Update the GUI when this happens, right now this happens automatically as a result
// of of the reactivity
}
}
/// The window handle used for [`ViziaEditor`].
struct ViziaEditorHandle {
vizia_state: Arc<ViziaState>,
window: WindowHandle,
}
/// The window handle enum stored within 'WindowHandle' contains raw pointers. Is there a way around
/// having this requirement?
unsafe impl Send for ViziaEditorHandle {}
impl Drop for ViziaEditorHandle {
fn drop(&mut self) {
self.vizia_state.open.store(false, Ordering::Release);
// XXX: This should automatically happen when the handle gets dropped, but apparently not
self.window.close();
}
}

View file

@ -3,10 +3,9 @@
// See the comment in the main `nih_plug` crate // See the comment in the main `nih_plug` crate
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
use baseview::{WindowHandle, WindowScalePolicy};
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use nih_plug::params::persist::PersistentField; use nih_plug::params::persist::PersistentField;
use nih_plug::prelude::{Editor, GuiContext, ParentWindowHandle}; use nih_plug::prelude::{Editor, GuiContext};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
@ -16,6 +15,7 @@ use vizia::prelude::*;
pub use vizia; pub use vizia;
pub mod assets; pub mod assets;
mod editor;
pub mod vizia_assets; pub mod vizia_assets;
pub mod widgets; pub mod widgets;
@ -47,7 +47,7 @@ pub fn create_vizia_editor<F>(
where where
F: Fn(&mut Context, Arc<dyn GuiContext>) + 'static + Send + Sync, F: Fn(&mut Context, Arc<dyn GuiContext>) + 'static + Send + Sync,
{ {
Some(Box::new(ViziaEditor { Some(Box::new(editor::ViziaEditor {
vizia_state, vizia_state,
app: Arc::new(app), app: Arc::new(app),
theming, theming,
@ -161,119 +161,3 @@ impl ViziaState {
self.open.load(Ordering::Acquire) self.open.load(Ordering::Acquire)
} }
} }
/// An [`Editor`] implementation that calls an vizia draw loop.
struct ViziaEditor {
vizia_state: Arc<ViziaState>,
/// The user's app function.
app: Arc<dyn Fn(&mut Context, Arc<dyn GuiContext>) + 'static + Send + Sync>,
/// What level of theming to apply. See [`ViziaEditorTheming`].
theming: ViziaTheming,
/// The scaling factor reported by the host, if any. On macOS this will never be set and we
/// should use the system scaling factor instead.
scaling_factor: AtomicCell<Option<f32>>,
}
impl Editor for ViziaEditor {
fn spawn(
&self,
parent: ParentWindowHandle,
context: Arc<dyn GuiContext>,
) -> Box<dyn std::any::Any + Send> {
let app = self.app.clone();
let vizia_state = self.vizia_state.clone();
let theming = self.theming;
let (unscaled_width, unscaled_height) = vizia_state.inner_logical_size();
let system_scaling_factor = self.scaling_factor.load();
let user_scale_factor = vizia_state.user_scale_factor();
let mut application = Application::new(move |cx| {
// Set some default styles to match the iced integration
if theming >= ViziaTheming::Custom {
// NOTE: vizia's font rendering looks way too dark and thick. Going one font weight
// lower seems to compensate for this.
cx.set_default_font(assets::NOTO_SANS_LIGHT);
cx.add_theme(include_str!("../assets/theme.css"));
// There doesn't seem to be any way to bundle styles with a widget, so we'll always
// include the style sheet for our custom widgets at context creation
widgets::register_theme(cx);
}
// Any widget can change the parameters by emitting `ParamEvent` events. This model will
// handle them automatically.
widgets::ParamModel {
context: context.clone(),
}
.build(cx);
// And we'll link `WindowEvent::ResizeWindow` and `WindowEvent::SetScale` events to our
// `ViziaState`. We'll notify the host when any of these change.
widgets::WindowModel {
context: context.clone(),
vizia_state: vizia_state.clone(),
}
.build(cx);
app(cx, context.clone())
})
.with_scale_policy(
system_scaling_factor
.map(|factor| WindowScalePolicy::ScaleFactor(factor as f64))
.unwrap_or(WindowScalePolicy::SystemScaleFactor),
)
.inner_size((unscaled_width, unscaled_height))
.user_scale_factor(user_scale_factor);
// This way the plugin can decide to use none of the built in theming
if theming == ViziaTheming::None {
application = application.ignore_default_theme();
}
let window = application.open_parented(&parent);
self.vizia_state.open.store(true, Ordering::Release);
Box::new(ViziaEditorHandle {
vizia_state: self.vizia_state.clone(),
window,
})
}
fn size(&self) -> (u32, u32) {
// This includes the user scale factor if set, but not any HiDPI scaling
self.vizia_state.scaled_logical_size()
}
fn set_scale_factor(&self, factor: f32) -> bool {
// We're making things a bit more complicated by having both a system scale factor, which is
// used for HiDPI and also known to the host, and a user scale factor that the user can use
// to arbitrarily resize the GUI
self.scaling_factor.store(Some(factor));
true
}
fn param_values_changed(&self) {
// TODO: Update the GUI when this happens, right now this happens automatically as a result
// of of the reactivity
}
}
/// The window handle used for [`ViziaEditor`].
struct ViziaEditorHandle {
vizia_state: Arc<ViziaState>,
window: WindowHandle,
}
/// The window handle enum stored within 'WindowHandle' contains raw pointers. Is there a way around
/// having this requirement?
unsafe impl Send for ViziaEditorHandle {}
impl Drop for ViziaEditorHandle {
fn drop(&mut self) {
self.vizia_state.open.store(false, Ordering::Release);
// XXX: This should automatically happen when the handle gets dropped, but apparently not
self.window.close();
}
}