1use crossbeam_utils::atomic::AtomicCell;
2use iced_baseview::baseview::{Size, WindowOpenOptions, WindowScalePolicy};
3use iced_baseview::{
4 IcedBaseviewSettings, PollSubNotifier, Program, message, shell::window::WindowHandle,
5};
6use nice_plug_core::context::gui::{GuiContext, ParamSetter};
7use nice_plug_core::{
8 editor::{Editor, ParentWindowHandle},
9 params::persist::PersistentField,
10};
11use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
12use serde::{Deserialize, Serialize};
13use std::sync::{
14 Arc, Mutex,
15 atomic::{AtomicBool, Ordering},
16};
17
18use crate::{EditorSettings, application::EditorState};
19
20pub(crate) struct IcedEditor<P: Program + 'static, EState: Send + 'static>
21where
22 <P as Program>::Message: message::MaybeDebug + message::MaybeClone,
23{
24 pub(crate) window_state: Arc<WindowState>,
25 pub(crate) editor_state: Arc<Mutex<Option<EState>>>,
26
27 pub(crate) build: Arc<dyn Fn(EditorState<EState>, NiceGuiContext) -> P + 'static + Send + Sync>,
29 pub(crate) notifier: PollSubNotifier,
30
31 pub(crate) settings: Arc<EditorSettings>,
32
33 pub(crate) scaling_factor: AtomicCell<Option<f32>>,
36}
37
38impl<P: Program + 'static, State: Send + 'static> Editor for IcedEditor<P, State> {
39 fn spawn(
40 &self,
41 parent: ParentWindowHandle,
42 context: Arc<dyn GuiContext>,
43 ) -> Box<dyn std::any::Any + Send> {
44 let nice_ctx = NiceGuiContext {
45 context: context.clone(),
46 window_state: self.window_state.clone(),
47 };
48
49 let build = self.build.clone();
50 let editor_state = EditorState::from_shared(&self.editor_state);
51
52 let (unscaled_width, unscaled_height) = self.window_state.logical_size();
53 let scaling_factor = self.scaling_factor.load();
54
55 #[allow(clippy::needless_update)]
56 let window = iced_baseview::open_parented(
57 &ParentWindowHandleAdapter(parent),
58 IcedBaseviewSettings {
59 window: WindowOpenOptions {
60 title: String::from("iced window"),
61 size: Size::new(unscaled_width as f64, unscaled_height as f64),
63 scale: scaling_factor
66 .map(|factor| WindowScalePolicy::ScaleFactor(factor as f64))
67 .unwrap_or(WindowScalePolicy::SystemScaleFactor),
68 ..Default::default()
69 },
70 ignore_non_modifier_keys: self.settings.ignore_non_modifier_keys,
71 always_redraw: self.settings.always_redraw,
72 },
73 self.notifier.clone(),
74 move || (build)(editor_state, nice_ctx),
75 );
76
77 self.window_state.open.store(true, Ordering::Release);
78
79 Box::new(IcedEditorHandle {
80 iced_state: self.window_state.clone(),
81 _window: window,
82 })
83 }
84
85 fn size(&self) -> (u32, u32) {
87 let new_size = self.window_state.requested_logical_size.load();
88 if let Some(new_size) = new_size {
91 new_size
92 } else {
93 self.window_state.logical_size()
94 }
95 }
96
97 fn set_scale_factor(&self, factor: f32) -> bool {
98 if self.window_state.is_open() {
101 return false;
102 }
103
104 self.scaling_factor.store(Some(factor));
105 true
106 }
107
108 fn param_value_changed(&self, _id: &str, _normalized_value: f32) {
109 self.notifier.notify();
110 }
111
112 fn param_modulation_changed(&self, _id: &str, _modulation_offset: f32) {
113 self.notifier.notify();
114 }
115
116 fn param_values_changed(&self) {
117 self.notifier.notify();
118 }
119}
120
121struct IcedEditorHandle<Message: 'static + Send> {
123 iced_state: Arc<WindowState>,
124 _window: WindowHandle<Message>,
125}
126
127unsafe impl<Message: 'static + Send> Send for IcedEditorHandle<Message> {}
130
131impl<Message: 'static + Send> Drop for IcedEditorHandle<Message> {
132 fn drop(&mut self) {
133 self.iced_state.open.store(false, Ordering::Release);
134 }
135}
136
137#[derive(Debug, Serialize, Deserialize)]
139pub struct WindowState {
140 #[serde(with = "nice_plug_core::params::persist::serialize_atomic_cell")]
142 pub(crate) logical_size: AtomicCell<(u32, u32)>,
143
144 #[serde(skip)]
146 pub(crate) requested_logical_size: AtomicCell<Option<(u32, u32)>>,
147
148 #[serde(skip)]
150 pub(crate) open: AtomicBool,
151}
152
153impl<'a> PersistentField<'a, WindowState> for Arc<WindowState> {
154 fn set(&self, new_value: WindowState) {
155 self.logical_size.store(new_value.logical_size.load());
156 }
157
158 fn map<F, R>(&self, f: F) -> R
159 where
160 F: Fn(&WindowState) -> R,
161 {
162 f(self)
163 }
164}
165
166impl WindowState {
167 pub fn from_logical_size(width: u32, height: u32) -> Arc<WindowState> {
171 Arc::new(WindowState {
172 logical_size: AtomicCell::new((width, height)),
173 requested_logical_size: Default::default(),
174 open: AtomicBool::new(false),
175 })
176 }
177
178 pub fn logical_size(&self) -> (u32, u32) {
180 self.logical_size.load()
181 }
182
183 pub fn is_open(&self) -> bool {
186 self.open.load(Ordering::Acquire)
187 }
188
189 pub fn set_requested_logical_size(&self, new_size: (u32, u32)) {
191 self.requested_logical_size.store(Some(new_size));
192 }
193}
194
195#[derive(Clone)]
196pub struct NiceGuiContext {
197 pub context: Arc<dyn GuiContext>,
198 window_state: Arc<WindowState>,
199}
200
201impl NiceGuiContext {
202 pub fn logical_size(&self) -> (u32, u32) {
204 self.window_state.logical_size()
205 }
206
207 pub fn is_open(&self) -> bool {
210 self.window_state.is_open()
211 }
212
213 pub fn set_requested_logical_size(&self, new_size: (u32, u32)) {
215 self.window_state.set_requested_logical_size(new_size);
216
217 if self.context.request_resize() {
219 self.window_state.logical_size.store(new_size);
220
221 }
223 }
224
225 pub fn param_setter<'a>(&'a self) -> ParamSetter<'a> {
226 ParamSetter {
227 raw_context: &*self.context,
228 }
229 }
230}
231
232struct ParentWindowHandleAdapter(ParentWindowHandle);
235
236unsafe impl HasRawWindowHandle for ParentWindowHandleAdapter {
237 fn raw_window_handle(&self) -> RawWindowHandle {
238 match self.0 {
239 ParentWindowHandle::X11Window(window) => {
240 let mut handle = raw_window_handle::XcbWindowHandle::empty();
241 handle.window = window;
242 RawWindowHandle::Xcb(handle)
243 }
244 ParentWindowHandle::AppKitNsView(ns_view) => {
245 let mut handle = raw_window_handle::AppKitWindowHandle::empty();
246 handle.ns_view = ns_view;
247 RawWindowHandle::AppKit(handle)
248 }
249 ParentWindowHandle::Win32Hwnd(hwnd) => {
250 let mut handle = raw_window_handle::Win32WindowHandle::empty();
251 handle.hwnd = hwnd;
252 RawWindowHandle::Win32(handle)
253 }
254 }
255 }
256}