Use new IcedEditor trait that forwards GuiContext
This commit is contained in:
parent
59f14a0361
commit
1213d59ae7
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
use baseview::{Size, WindowOpenOptions, WindowScalePolicy};
|
use baseview::{Size, WindowOpenOptions, WindowScalePolicy};
|
||||||
use crossbeam::atomic::AtomicCell;
|
use crossbeam::atomic::AtomicCell;
|
||||||
use nih_plug::prelude::{Editor, GuiContext, ParamSetter, ParentWindowHandle};
|
use nih_plug::prelude::{Editor, GuiContext, ParentWindowHandle};
|
||||||
use std::marker::PhantomData;
|
use std::fmt::Debug;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -25,24 +25,73 @@ pub use iced_baseview::*;
|
||||||
/// field on your parameters struct.
|
/// field on your parameters struct.
|
||||||
///
|
///
|
||||||
/// See [`IcedState::from_size()`].
|
/// See [`IcedState::from_size()`].
|
||||||
pub fn create_iced_editor<A>(
|
pub fn create_iced_editor<E: IcedEditor>(
|
||||||
iced_state: Arc<IcedState>,
|
iced_state: Arc<IcedState>,
|
||||||
initialization_flags: A::Flags,
|
initialization_flags: E::InitializationFlags,
|
||||||
) -> Option<Box<dyn Editor>>
|
) -> Option<Box<dyn Editor>> {
|
||||||
where
|
Some(Box::new(IcedEditorWrapper::<E> {
|
||||||
A: Application + 'static + Send + Sync,
|
|
||||||
A::Flags: Clone + Sync,
|
|
||||||
{
|
|
||||||
Some(Box::new(IcedEditor::<A> {
|
|
||||||
iced_state,
|
iced_state,
|
||||||
initialization_flags,
|
initialization_flags,
|
||||||
|
|
||||||
scaling_factor: AtomicCell::new(None),
|
scaling_factor: AtomicCell::new(None),
|
||||||
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A plugin editor using `iced`. This wraps around [`Application`] with the only change being that
|
||||||
|
/// the usual `new()` function now additionally takes a `Arc<dyn GuiContext>` that the editor can
|
||||||
|
/// store to interact with the parameters. The editor should have a `Pin<Arc<impl Params>>` as part
|
||||||
|
/// of their [`Flags`][Self::Flags] so it can read the current parameter values. See [`Application`]
|
||||||
|
/// for more information.
|
||||||
|
pub trait IcedEditor: 'static + Send + Sync + Sized {
|
||||||
|
/// See [`Application::Executor`]. You'll likely want to use [`crate::executor::Default`].
|
||||||
|
type Executor: Executor;
|
||||||
|
/// See [`Application::Message`].
|
||||||
|
type Message: 'static + Clone + Debug + Send;
|
||||||
|
/// See [`Application::Flags`].
|
||||||
|
type InitializationFlags: 'static + Clone + Send + Sync;
|
||||||
|
|
||||||
|
/// See [`Application::new`]. This also receivs the GUI context in addition to the flags.
|
||||||
|
fn new(
|
||||||
|
initialization_fags: Self::InitializationFlags,
|
||||||
|
context: Arc<dyn GuiContext>,
|
||||||
|
) -> (Self, Command<Self::Message>);
|
||||||
|
|
||||||
|
/// See [`Application::update`].
|
||||||
|
fn update(
|
||||||
|
&mut self,
|
||||||
|
window: &mut WindowQueue,
|
||||||
|
message: Self::Message,
|
||||||
|
) -> Command<Self::Message>;
|
||||||
|
|
||||||
|
/// See [`Application::subscription`].
|
||||||
|
fn subscription(
|
||||||
|
&self,
|
||||||
|
_window_subs: &mut WindowSubs<Self::Message>,
|
||||||
|
) -> Subscription<Self::Message> {
|
||||||
|
Subscription::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Application::view`].
|
||||||
|
fn view(&mut self) -> Element<'_, Self::Message>;
|
||||||
|
|
||||||
|
/// See [`Application::background_color`].
|
||||||
|
fn background_color(&self) -> Color {
|
||||||
|
Color::WHITE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Application::scale_policy`].
|
||||||
|
///
|
||||||
|
/// TODO: Is this needed? Editors shouldn't change the scale policy.
|
||||||
|
fn scale_policy(&self) -> WindowScalePolicy {
|
||||||
|
WindowScalePolicy::SystemScaleFactor
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [`Application::renderer_settings`].
|
||||||
|
fn renderer_settings() -> iced_baseview::renderer::settings::Settings {
|
||||||
|
iced_baseview::renderer::settings::Settings::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Once we add resizing, we may want to be able to remember the GUI size. In that case we need
|
// TODO: Once we add resizing, we may want to be able to remember the GUI size. In that case we need
|
||||||
// to make this serializable (only restoring the size of course) so it can be persisted.
|
// to make this serializable (only restoring the size of course) so it can be persisted.
|
||||||
pub struct IcedState {
|
pub struct IcedState {
|
||||||
|
@ -73,31 +122,16 @@ impl IcedState {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An [`Editor`] implementation that renders an iced [`Application`].
|
/// An [`Editor`] implementation that renders an iced [`Application`].
|
||||||
struct IcedEditor<A>
|
struct IcedEditorWrapper<E: IcedEditor> {
|
||||||
where
|
|
||||||
A: Application + 'static + Send + Sync,
|
|
||||||
A::Flags: Clone + Sync,
|
|
||||||
{
|
|
||||||
iced_state: Arc<IcedState>,
|
iced_state: Arc<IcedState>,
|
||||||
initialization_flags: A::Flags,
|
initialization_flags: E::InitializationFlags,
|
||||||
|
|
||||||
// FIXME:
|
|
||||||
// /// The plugin's state. This is kept in between editor openenings.
|
|
||||||
// user_state: Arc<RwLock<T>>,
|
|
||||||
// update: Arc<dyn Fn(&Context, &ParamSetter, &mut T) + 'static + Send + Sync>,
|
|
||||||
//
|
|
||||||
/// The scaling factor reported by the host, if any. On macOS this will never be set and we
|
/// 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.
|
/// should use the system scaling factor instead.
|
||||||
scaling_factor: AtomicCell<Option<f32>>,
|
scaling_factor: AtomicCell<Option<f32>>,
|
||||||
|
|
||||||
_phantom: PhantomData<A>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A> Editor for IcedEditor<A>
|
impl<E: IcedEditor> Editor for IcedEditorWrapper<E> {
|
||||||
where
|
|
||||||
A: Application + 'static + Send + Sync,
|
|
||||||
A::Flags: Clone + Sync,
|
|
||||||
{
|
|
||||||
fn spawn(
|
fn spawn(
|
||||||
&self,
|
&self,
|
||||||
parent: ParentWindowHandle,
|
parent: ParentWindowHandle,
|
||||||
|
@ -108,9 +142,10 @@ where
|
||||||
|
|
||||||
let (unscaled_width, unscaled_height) = self.iced_state.size();
|
let (unscaled_width, unscaled_height) = self.iced_state.size();
|
||||||
let scaling_factor = self.scaling_factor.load();
|
let scaling_factor = self.scaling_factor.load();
|
||||||
|
|
||||||
// TODO: iced_baseview does not have gracefuly error handling for context creation failures.
|
// TODO: iced_baseview does not have gracefuly error handling for context creation failures.
|
||||||
// This will panic if the context could not be created.
|
// This will panic if the context could not be created.
|
||||||
let window = IcedWindow::<A>::open_parented(
|
let window = IcedWindow::<IcedEditorWrapperApplication<E>>::open_parented(
|
||||||
&parent,
|
&parent,
|
||||||
Settings {
|
Settings {
|
||||||
window: WindowOpenOptions {
|
window: WindowOpenOptions {
|
||||||
|
@ -147,7 +182,8 @@ where
|
||||||
#[cfg(not(feature = "opengl"))]
|
#[cfg(not(feature = "opengl"))]
|
||||||
gl_config: None,
|
gl_config: None,
|
||||||
},
|
},
|
||||||
flags: self.initialization_flags.clone(),
|
// We use this wrapper to be able to pass the GUI context to the editor
|
||||||
|
flags: (context, self.initialization_flags.clone()),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -168,7 +204,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The window handle used for [`IcedEditor`].
|
/// The window handle used for [`IcedEditorWrapper`].
|
||||||
struct IcedEditorHandle<Message: 'static + Send> {
|
struct IcedEditorHandle<Message: 'static + Send> {
|
||||||
iced_state: Arc<IcedState>,
|
iced_state: Arc<IcedState>,
|
||||||
window: iced_baseview::WindowHandle<Message>,
|
window: iced_baseview::WindowHandle<Message>,
|
||||||
|
@ -185,3 +221,57 @@ impl<Message: Send> Drop for IcedEditorHandle<Message> {
|
||||||
self.window.close_window();
|
self.window.close_window();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wraps an `iced_baseview` [`Application`] around [`IcedEditor`]. Needed to allow editors to
|
||||||
|
/// always receive a copy of the GUI context.
|
||||||
|
struct IcedEditorWrapperApplication<E> {
|
||||||
|
editor: E,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: IcedEditor> Application for IcedEditorWrapperApplication<E> {
|
||||||
|
type Executor = E::Executor;
|
||||||
|
type Message = E::Message;
|
||||||
|
type Flags = (Arc<dyn GuiContext>, E::InitializationFlags);
|
||||||
|
|
||||||
|
fn new((context, flags): Self::Flags) -> (Self, Command<Self::Message>) {
|
||||||
|
let (editor, command) = E::new(flags, context);
|
||||||
|
(Self { editor }, command)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn update(
|
||||||
|
&mut self,
|
||||||
|
window: &mut WindowQueue,
|
||||||
|
message: Self::Message,
|
||||||
|
) -> Command<Self::Message> {
|
||||||
|
self.editor.update(window, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn subscription(
|
||||||
|
&self,
|
||||||
|
window_subs: &mut WindowSubs<Self::Message>,
|
||||||
|
) -> Subscription<Self::Message> {
|
||||||
|
self.editor.subscription(window_subs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn view(&mut self) -> Element<'_, Self::Message> {
|
||||||
|
self.editor.view()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn background_color(&self) -> Color {
|
||||||
|
self.editor.background_color()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn scale_policy(&self) -> WindowScalePolicy {
|
||||||
|
self.editor.scale_policy()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn renderer_settings() -> iced_baseview::renderer::settings::Settings {
|
||||||
|
E::renderer_settings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use nih_plug::prelude::Editor;
|
use nih_plug::prelude::{Editor, GuiContext};
|
||||||
use nih_plug_iced::{create_iced_editor, Application, Command, Element, IcedState};
|
use nih_plug_iced::{create_iced_editor, Command, Element, IcedEditor, IcedState};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -35,16 +35,20 @@ pub fn create(
|
||||||
|
|
||||||
struct DiopserEditor {
|
struct DiopserEditor {
|
||||||
params: Pin<Arc<DiopserParams>>,
|
params: Pin<Arc<DiopserParams>>,
|
||||||
|
context: Arc<dyn GuiContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Application for DiopserEditor {
|
impl IcedEditor for DiopserEditor {
|
||||||
type Executor = nih_plug_iced::executor::Default;
|
type Executor = nih_plug_iced::executor::Default;
|
||||||
// TODO:
|
// TODO:
|
||||||
type Message = ();
|
type Message = ();
|
||||||
type Flags = Pin<Arc<DiopserParams>>;
|
type InitializationFlags = Pin<Arc<DiopserParams>>;
|
||||||
|
|
||||||
fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) {
|
fn new(
|
||||||
let editor = DiopserEditor { params: flags };
|
params: Self::InitializationFlags,
|
||||||
|
context: Arc<dyn GuiContext>,
|
||||||
|
) -> (Self, Command<Self::Message>) {
|
||||||
|
let editor = DiopserEditor { params, context };
|
||||||
|
|
||||||
(editor, Command::none())
|
(editor, Command::none())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue