From 5c3340cb29f1c7a696322fffc88b829c7c9988ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduard=20M=C3=BCller?= Date: Mon, 23 Mar 2026 14:19:59 +0100 Subject: [PATCH 1/2] Implement `new_event_loop_proxy` for PluginCanvasPlatform ... which is needed for `slint::invoke_from_event_loop` and co --- plugin-canvas-slint/src/editor.rs | 2 +- plugin-canvas-slint/src/platform.rs | 40 ++++++++++++++++++++--- plugin-canvas-slint/src/window_adapter.rs | 9 ++++- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/plugin-canvas-slint/src/editor.rs b/plugin-canvas-slint/src/editor.rs index 6b4dce9..1ac51a6 100644 --- a/plugin-canvas-slint/src/editor.rs +++ b/plugin-canvas-slint/src/editor.rs @@ -51,7 +51,7 @@ impl SlintEditor { ).unwrap(); // It's ok if this fails as it just means it has already been set - slint::platform::set_platform(Box::new(PluginCanvasPlatform)).ok(); + slint::platform::set_platform(Box::new(PluginCanvasPlatform::new())).ok(); let window = Arc::new(window); WINDOW_TO_SLINT.set(Some(window.clone())); diff --git a/plugin-canvas-slint/src/platform.rs b/plugin-canvas-slint/src/platform.rs index 8f4a739..1cd6e8e 100644 --- a/plugin-canvas-slint/src/platform.rs +++ b/plugin-canvas-slint/src/platform.rs @@ -1,13 +1,45 @@ -use std::rc::Rc; +use std::{rc::Rc, collections::VecDeque, sync::{Arc, Mutex}}; use i_slint_core::{platform::{Platform, PlatformError}, window::WindowAdapter}; +use slint::{EventLoopError, platform::EventLoopProxy}; use crate::window_adapter::PluginCanvasWindowAdapter; -pub struct PluginCanvasPlatform; +pub(crate) type CallbackQueue = Arc>>>; + +struct PluginCanvasEventLoopProxy { + queue: CallbackQueue, +} + +impl EventLoopProxy for PluginCanvasEventLoopProxy { + fn quit_event_loop(&self) -> Result<(), EventLoopError> { + Ok(()) + } + + fn invoke_from_event_loop(&self, event: Box) -> Result<(), EventLoopError> { + self.queue.lock().unwrap().push_back(event); + Ok(()) + } +} + +pub struct PluginCanvasPlatform { + callback_queue: CallbackQueue, +} + +impl PluginCanvasPlatform { + pub fn new() -> Self { + Self { callback_queue: Arc::new(Mutex::new(VecDeque::new())) } + } +} impl Platform for PluginCanvasPlatform { fn create_window_adapter(&self) -> Result, PlatformError> { - PluginCanvasWindowAdapter::new() + PluginCanvasWindowAdapter::new(self.callback_queue.clone()) + } + + fn new_event_loop_proxy(&self) -> Option> { + Some(Box::new(PluginCanvasEventLoopProxy { + queue: self.callback_queue.clone(), + })) } -} +} diff --git a/plugin-canvas-slint/src/window_adapter.rs b/plugin-canvas-slint/src/window_adapter.rs index de0fc7e..68774ec 100644 --- a/plugin-canvas-slint/src/window_adapter.rs +++ b/plugin-canvas-slint/src/window_adapter.rs @@ -4,6 +4,8 @@ use std::rc::Rc; use std::sync::atomic::{AtomicBool, Ordering, AtomicUsize}; use std::sync::Arc; +use crate::platform::CallbackQueue; + use cursor_icon::CursorIcon; use i_slint_core::{window::{WindowAdapter, WindowAdapterInternal}, renderer::Renderer, platform::{PlatformError, WindowEvent}}; use i_slint_renderer_skia::{SkiaRenderer, SkiaSharedContext}; @@ -35,11 +37,12 @@ pub struct PluginCanvasWindowAdapter { pending_mouse_exit: AtomicBool, modifiers: RefCell, + callback_queue: CallbackQueue, } impl PluginCanvasWindowAdapter { #[allow(clippy::new_ret_no_self)] - pub fn new() -> Result, PlatformError> { + pub fn new(callback_queue: CallbackQueue) -> Result, PlatformError> { let plugin_canvas_window = WINDOW_TO_SLINT.take().unwrap(); let window_attributes = plugin_canvas_window.attributes(); @@ -80,6 +83,7 @@ impl PluginCanvasWindowAdapter { pending_mouse_exit: Default::default(), modifiers: Default::default(), + callback_queue, } }); @@ -122,6 +126,9 @@ impl PluginCanvasWindowAdapter { let built_in_response = match event { plugin_canvas::Event::Draw => { + let callbacks: Vec<_> = self.callback_queue.lock().unwrap().drain(..).collect(); + for cb in callbacks { cb(); } + match self.plugin_canvas_window.poll_events() { Ok(_) => {}, Err(e) => { From 2cd426f48142e37ad73b481201a56ee09c2e5569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduard=20M=C3=BCller?= Date: Wed, 22 Apr 2026 17:33:46 +0200 Subject: [PATCH 2/2] Comment why the event loop proxy is global and shared accross all windows --- plugin-canvas-slint/src/platform.rs | 1 + plugin-canvas-slint/src/window_adapter.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugin-canvas-slint/src/platform.rs b/plugin-canvas-slint/src/platform.rs index 1cd6e8e..57923b7 100644 --- a/plugin-canvas-slint/src/platform.rs +++ b/plugin-canvas-slint/src/platform.rs @@ -38,6 +38,7 @@ impl Platform for PluginCanvasPlatform { } fn new_event_loop_proxy(&self) -> Option> { + // Shared with all adapters - see window_adapter.rs draw event handling. Some(Box::new(PluginCanvasEventLoopProxy { queue: self.callback_queue.clone(), })) diff --git a/plugin-canvas-slint/src/window_adapter.rs b/plugin-canvas-slint/src/window_adapter.rs index 68774ec..50ac380 100644 --- a/plugin-canvas-slint/src/window_adapter.rs +++ b/plugin-canvas-slint/src/window_adapter.rs @@ -126,8 +126,9 @@ impl PluginCanvasWindowAdapter { let built_in_response = match event { plugin_canvas::Event::Draw => { + // Note: this may invoke callbacks from other windows as well, not just ours, because the callback queue is owned by PluginCanvasPlatform. Events are added via `slint::invoke_from_event_loop` which has no window context. let callbacks: Vec<_> = self.callback_queue.lock().unwrap().drain(..).collect(); - for cb in callbacks { cb(); } + for callback in callbacks { callback(); } match self.plugin_canvas_window.poll_events() { Ok(_) => {},