From 8a4f2a20221fee5e62d5dfdd3dc220325a741b15 Mon Sep 17 00:00:00 2001 From: Aidan729 Date: Sat, 8 Nov 2025 00:03:10 -0500 Subject: [PATCH] Add GUI thread only resize queue with RefCell for deferred resizing This commit introduces a queued resize system that allows plugins to defer window resize operations to avoid borrow checker conflicts during event handling, while maintaining thread safety. ## Changes ### 1. Added resize queue (RefCell-based) - Added pending_resizes: RefCell> to WindowHandler - Uses RefCell instead of Arc> because WindowHandler is GUI-thread-only and never crosses thread boundaries - More ergonomic and performant than mutex for single-threaded access ### 2. New public API methods **queue_resize(width: u32, height: u32)** - Queues a resize request without immediately executing it - Allows plugins to request resizes during event handling without borrowing conflicts **process_pending_resizes(window: &mut Window) -> Option<(u32, u32)>** - Processes the most recent queued resize request - Automatically clears older requests to prevent resize lag - Returns the applied size for plugins to sync UI state - Safe to call repeatedly (returns None if queue is empty) ## Use Case This pattern is useful for plugins that need to: 1. Handle resize requests from the frontend during event callbacks 2. Sync resize state back to frontend after completion 3. Avoid borrow conflicts when calling resize during message handling ## Why RefCell over Mutex? WindowHandler is only accessed from the GUI thread (baseview guarantees this), so RefCell is appropriate and more ergonomic: - No need for .lock().unwrap() boilerplate - Better performance (no atomic operations) - Clearer intent (panic on misuse vs deadlock) - Follows Rust's 'use the weakest synchronization primitive' principle ## Backwards Compatibility These changes are fully backwards compatible: - New fields are private implementation details - New methods are additive (existing code continues to work) - No changes to existing public API surface --- src/lib.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 714b70b..d35d8e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ use nih_plug::prelude::{Editor, GuiContext, ParamSetter}; use serde_json::Value; use std::{ borrow::Cow, + cell::RefCell, sync::{ atomic::{AtomicU32, Ordering}, Arc, @@ -115,6 +116,8 @@ pub struct WindowHandler { events_receiver: Receiver, pub width: Arc, pub height: Arc, + // GUI-thread only resize queue - uses RefCell instead of Mutex since it never crosses threads + pending_resizes: RefCell>, } impl WindowHandler { @@ -134,6 +137,28 @@ impl WindowHandler { }); } + /// Queue a resize request to be processed later + /// This allows deferring resize operations to avoid borrow checker conflicts + pub fn queue_resize(&self, width: u32, height: u32) { + self.pending_resizes.borrow_mut().push((width, height)); + } + + /// Process any pending resize requests + /// Returns the size that was applied, if any + pub fn process_pending_resizes(&self, window: &mut baseview::Window) -> Option<(u32, u32)> { + let mut queue = self.pending_resizes.borrow_mut(); + if let Some((width, height)) = queue.pop() { + // Only process the most recent resize request to avoid lag + queue.clear(); + drop(queue); // Release the borrow before calling resize + + self.resize(window, width, height); + Some((width, height)) + } else { + None + } + } + pub fn send_json(&self, json: Value) { let json_str = json.to_string(); let json_str_quoted = @@ -255,6 +280,7 @@ impl Editor for WebViewEditor { mouse_handler, width, height, + pending_resizes: RefCell::new(Vec::new()), } }); return Box::new(Instance { window_handle });