1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! An [`Application`] wrapper around an [`IcedEditor`] to bridge between `iced_baseview` and
//! `nih_plug_iced`.

use crossbeam::channel;
use nih_plug::prelude::GuiContext;
use std::sync::Arc;

use crate::futures::FutureExt;
use crate::{
    futures, subscription, Application, Color, Command, Element, IcedEditor, ParameterUpdate,
    Subscription, WindowQueue, WindowScalePolicy, WindowSubs,
};

/// Wraps an `iced_baseview` [`Application`] around [`IcedEditor`]. Needed to allow editors to
/// always receive a copy of the GUI context.
pub(crate) struct IcedEditorWrapperApplication<E: IcedEditor> {
    editor: E,

    /// We will receive notifications about parameters being changed on here. Whenever a parameter
    /// update gets sent, we will trigger a [`Message::parameterUpdate`] which causes the UI to be
    /// redrawn.
    parameter_updates_receiver: Arc<channel::Receiver<ParameterUpdate>>,
}

/// This wraps around `E::Message` to add a parameter update message which can be handled directly
/// by this wrapper. That parameter update message simply forces a redraw of the GUI whenever there
/// is a parameter update.
pub enum Message<E: IcedEditor> {
    EditorMessage(E::Message),
    ParameterUpdate,
}

impl<E: IcedEditor> std::fmt::Debug for Message<E> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::EditorMessage(arg0) => f.debug_tuple("EditorMessage").field(arg0).finish(),
            Self::ParameterUpdate => write!(f, "ParameterUpdate"),
        }
    }
}

impl<E: IcedEditor> Clone for Message<E> {
    fn clone(&self) -> Self {
        match self {
            Self::EditorMessage(arg0) => Self::EditorMessage(arg0.clone()),
            Self::ParameterUpdate => Self::ParameterUpdate,
        }
    }
}

impl<E: IcedEditor> Application for IcedEditorWrapperApplication<E> {
    type Executor = E::Executor;
    type Message = Message<E>;
    type Flags = (
        Arc<dyn GuiContext>,
        Arc<channel::Receiver<ParameterUpdate>>,
        E::InitializationFlags,
    );

    fn new(
        (context, parameter_updates_receiver, flags): Self::Flags,
    ) -> (Self, Command<Self::Message>) {
        let (editor, command) = E::new(flags, context);

        (
            Self {
                editor,
                parameter_updates_receiver,
            },
            command.map(Message::EditorMessage),
        )
    }

    #[inline]
    fn update(
        &mut self,
        window: &mut WindowQueue,
        message: Self::Message,
    ) -> Command<Self::Message> {
        match message {
            Message::EditorMessage(message) => self
                .editor
                .update(window, message)
                .map(Message::EditorMessage),
            // This message only exists to force a redraw
            Message::ParameterUpdate => Command::none(),
        }
    }

    #[inline]
    fn subscription(
        &self,
        window_subs: &mut WindowSubs<Self::Message>,
    ) -> Subscription<Self::Message> {
        // Since we're wrapping around `E::Message`, we need to do this transformation ourselves
        let mut editor_window_subs = WindowSubs {
            on_frame: match &window_subs.on_frame {
                Some(Message::EditorMessage(message)) => Some(message.clone()),
                _ => None,
            },
            on_window_will_close: match &window_subs.on_window_will_close {
                Some(Message::EditorMessage(message)) => Some(message.clone()),
                _ => None,
            },
        };

        let subscription = Subscription::batch([
            // For some reason there's no adapter to just convert `futures::channel::mpsc::Receiver`
            // into a stream that doesn't require consuming that receiver (which wouldn't work in
            // this case since the subscriptions function gets called repeatedly). So we'll just use
            // a crossbeam queue and this unfold instead.
            subscription::unfold(
                "parameter updates",
                self.parameter_updates_receiver.clone(),
                |parameter_updates_receiver| match parameter_updates_receiver.try_recv() {
                    Ok(_) => futures::future::ready((
                        Some(Message::ParameterUpdate),
                        parameter_updates_receiver,
                    ))
                    .boxed(),
                    Err(_) => futures::future::pending().boxed(),
                },
            ),
            self.editor
                .subscription(&mut editor_window_subs)
                .map(Message::EditorMessage),
        ]);

        if let Some(message) = editor_window_subs.on_frame {
            window_subs.on_frame = Some(Message::EditorMessage(message));
        }
        if let Some(message) = editor_window_subs.on_window_will_close {
            window_subs.on_window_will_close = Some(Message::EditorMessage(message));
        }

        subscription
    }

    #[inline]
    fn view(&mut self) -> Element<'_, Self::Message> {
        self.editor.view().map(Message::EditorMessage)
    }

    #[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::backend::settings::Settings {
        E::renderer_settings()
    }
}