1
0
Fork 0

Request resizes after loading state

If the editor is open.
This commit is contained in:
Robbert van der Helm 2023-03-03 18:52:57 +01:00
parent c294afbf62
commit ae3356dca8
4 changed files with 42 additions and 30 deletions

View file

@ -1784,7 +1784,12 @@ impl<P: ClapPlugin> Wrapper<P> {
let task_posted = self.schedule_gui(Task::ParameterValuesChanged); let task_posted = self.schedule_gui(Task::ParameterValuesChanged);
nih_debug_assert!(task_posted, "The task queue is full, dropping task..."); nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
// TODO: Check for GUI size changes and resize accordingly // TODO: Right now there's no way to know if loading the state changed the GUI's size. We
// could keep track of the last known size and compare the GUI's current size against
// that but that also seems brittle.
if self.editor_handle.lock().is_some() {
self.request_resize();
}
success success
} }

View file

@ -1,8 +1,7 @@
use crossbeam::channel;
use std::sync::Arc; use std::sync::Arc;
use super::backend::Backend; use super::backend::Backend;
use super::wrapper::{GuiTask, Task, Wrapper}; use super::wrapper::{Task, Wrapper};
use crate::context::gui::GuiContext; use crate::context::gui::GuiContext;
use crate::context::init::InitContext; use crate::context::init::InitContext;
use crate::context::process::{ProcessContext, Transport}; use crate::context::process::{ProcessContext, Transport};
@ -35,10 +34,6 @@ pub(crate) struct WrapperProcessContext<'a, P: Plugin, B: Backend<P>> {
/// with the host for things like setting parameters. /// with the host for things like setting parameters.
pub(crate) struct WrapperGuiContext<P: Plugin, B: Backend<P>> { pub(crate) struct WrapperGuiContext<P: Plugin, B: Backend<P>> {
pub(super) wrapper: Arc<Wrapper<P, B>>, pub(super) wrapper: Arc<Wrapper<P, B>>,
/// This allows us to send tasks to the parent view that will be handled at the start of its
/// next frame.
pub(super) gui_task_sender: channel::Sender<GuiTask>,
} }
impl<P: Plugin, B: Backend<P>> InitContext<P> for WrapperInitContext<'_, P, B> { impl<P: Plugin, B: Backend<P>> InitContext<P> for WrapperInitContext<'_, P, B> {
@ -110,16 +105,7 @@ impl<P: Plugin, B: Backend<P>> GuiContext for WrapperGuiContext<P, B> {
} }
fn request_resize(&self) -> bool { fn request_resize(&self) -> bool {
let (unscaled_width, unscaled_height) = self.wrapper.request_resize();
self.wrapper.editor.borrow().as_ref().unwrap().lock().size();
// This will cause the editor to be resized at the start of the next frame
let push_successful = self
.gui_task_sender
.send(GuiTask::Resize(unscaled_width, unscaled_height))
.is_ok();
nih_debug_assert!(push_successful, "Could not queue window resize");
true true
} }

View file

@ -1,6 +1,6 @@
use atomic_refcell::AtomicRefCell; use atomic_refcell::AtomicRefCell;
use baseview::{EventStatus, Window, WindowHandler, WindowOpenOptions}; use baseview::{EventStatus, Window, WindowHandler, WindowOpenOptions};
use crossbeam::channel; use crossbeam::channel::{self, Sender};
use crossbeam::queue::ArrayQueue; use crossbeam::queue::ArrayQueue;
use parking_lot::Mutex; use parking_lot::Mutex;
use raw_window_handle::HasRawWindowHandle; use raw_window_handle::HasRawWindowHandle;
@ -46,6 +46,8 @@ pub struct Wrapper<P: Plugin, B: Backend<P>> {
/// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when /// to instantiate this in advance so we don't need to lock the entire [`Plugin`] object when
/// creating an editor. Wrapped in an `AtomicRefCell` because it needs to be initialized late. /// creating an editor. Wrapped in an `AtomicRefCell` because it needs to be initialized late.
pub editor: AtomicRefCell<Option<Arc<Mutex<Box<dyn Editor>>>>>, pub editor: AtomicRefCell<Option<Arc<Mutex<Box<dyn Editor>>>>>,
/// A channel for sending tasks to the GUI window, if the plugin has a GUI. Set in `run()`.
gui_tasks_sender: AtomicRefCell<Option<Sender<GuiTask>>>,
/// A realtime-safe task queue so the plugin can schedule tasks that need to be run later on the /// A realtime-safe task queue so the plugin can schedule tasks that need to be run later on the
/// GUI thread. See the same field in the VST3 wrapper for more information on why this looks /// GUI thread. See the same field in the VST3 wrapper for more information on why this looks
@ -220,6 +222,8 @@ impl<P: Plugin, B: Backend<P>> Wrapper<P, B> {
params, params,
// Initialized later as it needs a reference to the wrapper for the async executor // Initialized later as it needs a reference to the wrapper for the async executor
editor: AtomicRefCell::new(None), editor: AtomicRefCell::new(None),
// Set in `run()`
gui_tasks_sender: AtomicRefCell::new(None),
// Also initialized later as it also needs a reference to the wrapper // Also initialized later as it also needs a reference to the wrapper
event_loop: AtomicRefCell::new(None), event_loop: AtomicRefCell::new(None),
@ -302,6 +306,7 @@ impl<P: Plugin, B: Backend<P>> Wrapper<P, B> {
/// could not be opened. /// could not be opened.
pub fn run(self: Arc<Self>) -> Result<(), WrapperError> { pub fn run(self: Arc<Self>) -> Result<(), WrapperError> {
let (gui_task_sender, gui_task_receiver) = channel::bounded(512); let (gui_task_sender, gui_task_receiver) = channel::bounded(512);
*self.gui_tasks_sender.borrow_mut() = Some(gui_task_sender.clone());
// We'll spawn a separate thread to handle IO and to process audio. This audio thread should // We'll spawn a separate thread to handle IO and to process audio. This audio thread should
// terminate together with this function. // terminate together with this function.
@ -309,13 +314,12 @@ impl<P: Plugin, B: Backend<P>> Wrapper<P, B> {
let audio_thread = { let audio_thread = {
let this = self.clone(); let this = self.clone();
let terminate_audio_thread = terminate_audio_thread.clone(); let terminate_audio_thread = terminate_audio_thread.clone();
let gui_task_sender = gui_task_sender.clone();
thread::spawn(move || this.run_audio_thread(terminate_audio_thread, gui_task_sender)) thread::spawn(move || this.run_audio_thread(terminate_audio_thread, gui_task_sender))
}; };
match self.editor.borrow().clone() { match self.editor.borrow().clone() {
Some(editor) => { Some(editor) => {
let context = self.clone().make_gui_context(gui_task_sender); let context = self.clone().make_gui_context();
// DPI scaling should not be used on macOS since the OS handles it there // DPI scaling should not be used on macOS since the OS handles it there
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@ -450,6 +454,20 @@ impl<P: Plugin, B: Backend<P>> Wrapper<P, B> {
event_loop.schedule_gui(task) event_loop.schedule_gui(task)
} }
/// Request the outer window to be resized to the editor's current size.
pub fn request_resize(&self) {
if let Some(gui_tasks_sender) = self.gui_tasks_sender.borrow().as_ref() {
let (unscaled_width, unscaled_height) =
self.editor.borrow().as_ref().unwrap().lock().size();
// This will cause the editor to be resized at the start of the next frame
let push_successful = gui_tasks_sender
.send(GuiTask::Resize(unscaled_width, unscaled_height))
.is_ok();
nih_debug_assert!(push_successful, "Could not queue window resize");
}
}
/// The audio thread. This should be called from another thread, and it will run until /// The audio thread. This should be called from another thread, and it will run until
/// `should_terminate` is `true`. /// `should_terminate` is `true`.
fn run_audio_thread( fn run_audio_thread(
@ -535,14 +553,8 @@ impl<P: Plugin, B: Backend<P>> Wrapper<P, B> {
); );
} }
fn make_gui_context( fn make_gui_context(self: Arc<Self>) -> Arc<WrapperGuiContext<P, B>> {
self: Arc<Self>, Arc::new(WrapperGuiContext { wrapper: self })
gui_task_sender: channel::Sender<GuiTask>,
) -> Arc<WrapperGuiContext<P, B>> {
Arc::new(WrapperGuiContext {
wrapper: self,
gui_task_sender,
})
} }
fn make_init_context(&self) -> WrapperInitContext<'_, P, B> { fn make_init_context(&self) -> WrapperInitContext<'_, P, B> {
@ -623,7 +635,10 @@ impl<P: Plugin, B: Backend<P>> Wrapper<P, B> {
let task_posted = self.schedule_gui(Task::ParameterValuesChanged); let task_posted = self.schedule_gui(Task::ParameterValuesChanged);
nih_debug_assert!(task_posted, "The task queue is full, dropping task..."); nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
// TODO: Check for GUI size changes and resize accordingly // TODO: Right now there's no way to know if loading the state changed the GUI's size. We
// could keep track of the last known size and compare the GUI's current size against
// that but that also seems brittle.
self.request_resize();
success success
} }

View file

@ -594,7 +594,13 @@ impl<P: Vst3Plugin> WrapperInner<P> {
let task_posted = self.schedule_gui(Task::ParameterValuesChanged); let task_posted = self.schedule_gui(Task::ParameterValuesChanged);
nih_debug_assert!(task_posted, "The task queue is full, dropping task..."); nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
// TODO: Check for GUI size changes and resize accordingly // TODO: Right now there's no way to know if loading the state changed the GUI's size. We
// could keep track of the last known size and compare the GUI's current size against
// that but that also seems brittle.
if self.plug_view.read().is_some() {
let task_posted = self.schedule_gui(Task::RequestResize);
nih_debug_assert!(task_posted, "The task queue is full, dropping task...");
}
success success
} }