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 crossbeam::atomic::AtomicCell;
|
||||
use nih_plug::prelude::{Editor, GuiContext, ParamSetter, ParentWindowHandle};
|
||||
use std::marker::PhantomData;
|
||||
use nih_plug::prelude::{Editor, GuiContext, ParentWindowHandle};
|
||||
use std::fmt::Debug;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -25,24 +25,73 @@ pub use iced_baseview::*;
|
|||
/// field on your parameters struct.
|
||||
///
|
||||
/// See [`IcedState::from_size()`].
|
||||
pub fn create_iced_editor<A>(
|
||||
pub fn create_iced_editor<E: IcedEditor>(
|
||||
iced_state: Arc<IcedState>,
|
||||
initialization_flags: A::Flags,
|
||||
) -> Option<Box<dyn Editor>>
|
||||
where
|
||||
A: Application + 'static + Send + Sync,
|
||||
A::Flags: Clone + Sync,
|
||||
{
|
||||
Some(Box::new(IcedEditor::<A> {
|
||||
initialization_flags: E::InitializationFlags,
|
||||
) -> Option<Box<dyn Editor>> {
|
||||
Some(Box::new(IcedEditorWrapper::<E> {
|
||||
iced_state,
|
||||
initialization_flags,
|
||||
|
||||
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
|
||||
// to make this serializable (only restoring the size of course) so it can be persisted.
|
||||
pub struct IcedState {
|
||||
|
@ -73,31 +122,16 @@ impl IcedState {
|
|||
}
|
||||
|
||||
/// An [`Editor`] implementation that renders an iced [`Application`].
|
||||
struct IcedEditor<A>
|
||||
where
|
||||
A: Application + 'static + Send + Sync,
|
||||
A::Flags: Clone + Sync,
|
||||
{
|
||||
struct IcedEditorWrapper<E: IcedEditor> {
|
||||
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
|
||||
/// should use the system scaling factor instead.
|
||||
scaling_factor: AtomicCell<Option<f32>>,
|
||||
|
||||
_phantom: PhantomData<A>,
|
||||
}
|
||||
|
||||
impl<A> Editor for IcedEditor<A>
|
||||
where
|
||||
A: Application + 'static + Send + Sync,
|
||||
A::Flags: Clone + Sync,
|
||||
{
|
||||
impl<E: IcedEditor> Editor for IcedEditorWrapper<E> {
|
||||
fn spawn(
|
||||
&self,
|
||||
parent: ParentWindowHandle,
|
||||
|
@ -108,9 +142,10 @@ where
|
|||
|
||||
let (unscaled_width, unscaled_height) = self.iced_state.size();
|
||||
let scaling_factor = self.scaling_factor.load();
|
||||
|
||||
// TODO: iced_baseview does not have gracefuly error handling for context creation failures.
|
||||
// This will panic if the context could not be created.
|
||||
let window = IcedWindow::<A>::open_parented(
|
||||
let window = IcedWindow::<IcedEditorWrapperApplication<E>>::open_parented(
|
||||
&parent,
|
||||
Settings {
|
||||
window: WindowOpenOptions {
|
||||
|
@ -147,7 +182,8 @@ where
|
|||
#[cfg(not(feature = "opengl"))]
|
||||
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> {
|
||||
iced_state: Arc<IcedState>,
|
||||
window: iced_baseview::WindowHandle<Message>,
|
||||
|
@ -185,3 +221,57 @@ impl<Message: Send> Drop for IcedEditorHandle<Message> {
|
|||
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
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use nih_plug::prelude::Editor;
|
||||
use nih_plug_iced::{create_iced_editor, Application, Command, Element, IcedState};
|
||||
use nih_plug::prelude::{Editor, GuiContext};
|
||||
use nih_plug_iced::{create_iced_editor, Command, Element, IcedEditor, IcedState};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -35,16 +35,20 @@ pub fn create(
|
|||
|
||||
struct DiopserEditor {
|
||||
params: Pin<Arc<DiopserParams>>,
|
||||
context: Arc<dyn GuiContext>,
|
||||
}
|
||||
|
||||
impl Application for DiopserEditor {
|
||||
impl IcedEditor for DiopserEditor {
|
||||
type Executor = nih_plug_iced::executor::Default;
|
||||
// TODO:
|
||||
type Message = ();
|
||||
type Flags = Pin<Arc<DiopserParams>>;
|
||||
type InitializationFlags = Pin<Arc<DiopserParams>>;
|
||||
|
||||
fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) {
|
||||
let editor = DiopserEditor { params: flags };
|
||||
fn new(
|
||||
params: Self::InitializationFlags,
|
||||
context: Arc<dyn GuiContext>,
|
||||
) -> (Self, Command<Self::Message>) {
|
||||
let editor = DiopserEditor { params, context };
|
||||
|
||||
(editor, Command::none())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue