]> uap-core.de Git - note.git/commitdiff
add support for creating a UiDoc for dyn objects main
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 16 Jul 2026 20:26:29 +0000 (22:26 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 16 Jul 2026 20:26:29 +0000 (22:26 +0200)
ui-rs/src/ui/event.rs
ui-rs/src/ui/toolkit.rs

index 3fb2104326b108ffb6390c8cad6384285c5547dd..93bf3702a5d7fe8365da81dc1bad9c2added1cf7 100644 (file)
@@ -278,10 +278,15 @@ pub struct ActionEventWrapper<T> {
     pub target: *mut T
 }
 
+pub struct ActionEventWrapperBoxed<T> {
+    pub callback: Box<dyn FnMut(&mut T, &mut ActionEvent)>,
+    pub target: *mut Box<T>
+}
+
 pub extern "C" fn action_event_wrapper<T>(e: *const ffi::UiEvent, data: *mut c_void) {
     unsafe {
         let wrapper = &mut *(data as *mut ActionEventWrapper<T>);
-        let target = &mut *(wrapper.target as *mut T);
+        let target = &mut *(wrapper.target);
 
         action_callback(e, |event| {
             (wrapper.callback)(target, event);
@@ -289,6 +294,17 @@ pub extern "C" fn action_event_wrapper<T>(e: *const ffi::UiEvent, data: *mut c_v
     }
 }
 
+pub extern "C" fn action_event_wrapper_boxed<T>(e: *const ffi::UiEvent, data: *mut c_void) {
+    unsafe {
+        let wrapper = &mut *(data as *mut ActionEventWrapperBoxed<T>);
+        let target = &mut *(wrapper.target);
+        let target_ref = target.as_mut();
+        action_callback(e, |event| {
+            (wrapper.callback)(target_ref, event);
+        });
+    }
+}
+
 fn action_callback<F>(e: *const ffi::UiEvent, f: F)
 where F: FnOnce(&mut ActionEvent) {
     unsafe {
index 39c119db9ceddbabf561b1ab2d4106cc92588d1e..63e10238f9432a8b686ffe47f8e1a5c098d71218 100644 (file)
@@ -30,7 +30,7 @@
 
 use std::any::Any;
 use std::ffi::{c_char, c_int, c_void, CStr, CString};
-use crate::ui::{action_event_wrapper, event, ffi, simple_event_wrapper, ui_object_get_context, ui_object_get_windowdata, ui_reg_destructor, ui_remove_destructor, untyped_event_wrapper, ActionEventWrapper, Event, EventWrapper, NoAppData, SimpleEventWrapper, SubList, UntypedEventWrapper, RUST_TYPED_OBJECT_BOX_ANY};
+use crate::ui::{action_event_wrapper, action_event_wrapper_boxed, event, ffi, simple_event_wrapper, ui_object_get_context, ui_object_get_windowdata, ui_reg_destructor, ui_remove_destructor, untyped_event_wrapper, ActionEventWrapper, ActionEventWrapperBoxed, Event, EventWrapper, NoAppData, SimpleEventWrapper, SubList, UntypedEventWrapper, RUST_TYPED_OBJECT_BOX_ANY};
 
 use std::marker::PhantomData;
 use std::mem;
@@ -75,15 +75,15 @@ impl UiContext {
         ptr
     }
 
-    pub fn attach<T: UiModel + UiActions>(&mut self, doc: &UiDoc<T>) {
+    pub fn attach<T: ?Sized + UiModel + UiActions>(&mut self, doc: &UiDoc<T>) {
         unsafe {
-            ui_attach_document(self.ptr, doc.ptr);
+            ui_attach_document(self.ptr, doc.ptr.cast());
         }
     }
 
-    pub fn detach<T: UiModel + UiActions>(&mut self, doc: &UiDoc<T>) {
+    pub fn detach<T: ?Sized + UiModel + UiActions>(&mut self, doc: &UiDoc<T>) {
         unsafe {
-            ui_detach_document(self.ptr, doc.ptr);
+            ui_detach_document(self.ptr, doc.ptr.cast());
         }
     }
 
@@ -164,16 +164,17 @@ impl UiContext {
         unsafe {
             let doc_ptr = ui_context_document(self.ptr) as *mut T;
             let target_ptr = if !doc_ptr.is_null() {
-                let doc_storage: *mut *mut T = doc_ptr.cast();
-                *doc_storage
+                let doc_storage: *mut *mut Box<T> = doc_ptr.cast();
+                let target_ptr = *doc_storage;
+                self.add_action_for_target_boxed(target_ptr, name, f);
             } else {
                 let obj_ptr = ui_context_obj(self.ptr);
                 assert!(!obj_ptr.is_null());
                 let obj_wdata_ptr = ui_object_get_windowdata(obj_ptr);
                 assert!(!obj_wdata_ptr.is_null());
-                obj_wdata_ptr as *mut T
+                let target_ptr = obj_wdata_ptr as *mut T;
+                self.add_action_for_target(target_ptr, name, f);
             };
-            self.add_action_for_target(target_ptr, name, f);
         }
     }
 
@@ -187,6 +188,16 @@ impl UiContext {
         }
     }
 
+    pub unsafe fn add_action_for_target_boxed<T, F>(&self, target_ptr: *mut Box<T>, name: &str, f: F)
+    where F: FnMut(&mut T, &mut event::ActionEvent) + 'static {
+        let wrapper = Box::new(ActionEventWrapperBoxed { callback: Box::new(f), target: target_ptr });
+        let ptr = self.reg_box(wrapper);
+        let cstr = CString::new(name).unwrap();
+        unsafe {
+            ui_add_action(self.ptr, cstr.as_ptr(), Some(action_event_wrapper_boxed::<T>), ptr as *mut c_void);
+        }
+    }
+
     pub fn add_action_untyped<F>(&self, name: &str, f: F)
     where F: FnMut(&mut event::ActionEvent) + 'static {
         unsafe {
@@ -247,16 +258,16 @@ pub extern "C" fn destroy_boxed<T>(data: *mut c_void) {
     }
 }
 
-pub struct UiDoc<T: UiModel + UiActions> {
+pub struct UiDoc<T: ?Sized + UiModel + UiActions> {
     pub ctx: UiContext,
-    ptr: *mut c_void,
+    ptr: *mut *mut Box<T>,
     _marker: PhantomData<T>
 }
 
-impl<T: UiModel + UiActions> Clone for UiDoc<T> {
+impl<T: ?Sized + UiModel + UiActions> Clone for UiDoc<T> {
     fn clone(&self) -> Self {
         unsafe {
-            ui_document_ref(self.ptr);
+            ui_document_ref(self.ptr.cast());
         }
         UiDoc::<T> {
             ctx: self.ctx.clone(),
@@ -266,64 +277,118 @@ impl<T: UiModel + UiActions> Clone for UiDoc<T> {
     }
 }
 
-impl<T: UiModel + UiActions> Drop for UiDoc<T> {
+impl<T: ?Sized + UiModel + UiActions> Drop for UiDoc<T> {
     fn drop(&mut self) {
         unsafe {
             if !self.ptr.is_null() {
-                ui_document_unref(self.ptr);
+                ui_document_unref(self.ptr.cast());
             }
         }
     }
 }
 
-
-impl<T: UiModel + UiActions> UiDoc<T> {
+impl<T: ?Sized + UiModel + UiActions> UiDoc<T> {
     fn from_ptr(ptr: *mut c_void) -> UiDoc<T> {
         unsafe {
             let ctx = UiContext { ptr: ui_document_context(ptr) };
             ui_document_ref(ptr);
-            UiDoc::<T> { ctx: ctx, ptr: ptr, _marker: PhantomData }
+            UiDoc::<T> { ctx: ctx, ptr: ptr.cast(), _marker: PhantomData }
         }
     }
 
-    pub fn new(data: T) -> UiDoc<T> {
-        UiDoc::new2(data, |_,_| {})
+    pub fn new_from_box(data: Box<T>) {
+        UiDoc::new_from_box2(data, |_,_| {});
     }
-    
-    pub fn new2<F>(data: T, init: F) -> UiDoc<T>
+
+    pub fn new_from_box2<F>(data: Box<T>, init: F) -> UiDoc<T>
     where F: FnOnce(&mut T, &UiDoc<T>) {
         unsafe {
-            let doc = ui_document_new(mem::size_of::<*mut T>());
+            let doc = ui_document_new(mem::size_of::<*mut Box<T>>());
             let ctx = UiContext::from_ptr(ui_document_context(doc));
             ui_app_ref();
             ui_reg_destructor(ctx.ptr, std::ptr::null_mut(), doc_app_unref);
-            let data_ptr = ctx.reg_box(Box::new(data)); // returns *mut T
-            let doc_storage: *mut *mut T = doc.cast();
+            let data_ptr = ctx.reg_box(Box::new(data)); // returns *mut Box<T>
+            let doc_storage: *mut *mut Box<T> = doc.cast();
             *doc_storage = data_ptr;
             let doc_ref = &mut *data_ptr;
             doc_ref.init(&ctx);
             doc_ref.init_actions(&ctx);
-            let doc_handle = UiDoc::<T> { ctx: ctx, ptr: doc, _marker: PhantomData };
+            let doc_handle = UiDoc::<T> { ctx: ctx, ptr: doc.cast(), _marker: PhantomData };
             init(doc_ref, &doc_handle);
             doc_handle
         }
     }
 
+    pub fn cast<U: ?Sized + UiModel + UiActions>(
+        self,
+        f: impl FnOnce(Box<T>) -> Box<U>,
+    ) -> UiDoc<U> {
+         unsafe {
+             // replace the Box container for this document
+             // the destructor for the previous Box must be removed
+             ui_remove_destructor(self.ctx.ptr, self.ptr.cast());
+             ui_document_ref(self.ptr.cast());
+
+             let outer: Box<Box<T>> = Box::from_raw(*self.ptr);
+
+             // Prevent dropping the outer box while extracting the inner box
+             let outer = std::mem::ManuallyDrop::new(outer);
+
+             let inner: Box<T> = std::ptr::read(&**outer);
+
+             let new_inner: Box<U> = f(inner);
+
+             let new_outer: Box<Box<U>> = Box::new(new_inner);
+
+             *self.ptr.cast::<*mut Box<U>>() = Box::into_raw(new_outer);
+
+             UiDoc {
+                 ctx: self.ctx.clone(),
+                 ptr: self.ptr.cast(),
+                 _marker: PhantomData,
+             }
+        }
+    }
+
     pub fn doc_ref(&self) -> UiDocRef<T> {
-        UiDocRef::from_ptr(self.ptr)
+        UiDocRef::from_ptr(self.ptr.cast())
     }
 
     pub fn doc_proxy(&self) -> UiDocProxy<T> {
-        UiDocProxy::from_ptr(self.ptr)
+        UiDocProxy::from_ptr(self.ptr.cast())
     }
 
-    pub unsafe fn get_data_ptr(&self) -> *mut T {
+    pub unsafe fn get_data_ptr(&self) -> *mut Box<T> {
+        unsafe {
+            let box_ptr: *mut *mut Box<T> = self.ptr.cast();
+            *box_ptr
+        }
+    }
+}
+
+#[macro_export]
+macro_rules! doc_cast {
+    ($doc:expr, dyn $trait:path) => {
+        $doc.cast(|b| Box::new(*b) as Box<dyn $trait>)
+    };
+}
+
+impl<T: UiModel + UiActions> UiDoc<T> {
+
+
+    pub fn new(data: T) -> UiDoc<T> {
+        UiDoc::new2(data, |_,_| {})
+    }
+    
+    pub fn new2<F>(data: T, init: F) -> UiDoc<T>
+    where F: FnOnce(&mut T, &UiDoc<T>) {
         unsafe {
-            let ptr: *mut *mut T = self.ptr.cast();
-            *ptr
+            let inner_box = Box::new(data);
+            UiDoc::new_from_box2(inner_box, init)
         }
     }
 
+    /*
     pub fn add_action<F>(&mut self, name: &str, f: F)
     where F: FnMut(&mut T, &mut event::ActionEvent) + 'static {
         unsafe {
@@ -333,6 +398,7 @@ impl<T: UiModel + UiActions> UiDoc<T> {
             ui_add_action(self.ctx.ptr, cstr.as_ptr(), Some(action_event_wrapper::<T>), ptr as *mut c_void);
         }
     }
+    */
 }
 
 extern "C" fn doc_app_unref(_: *mut c_void) {
@@ -517,13 +583,13 @@ impl<T> Drop for UiObjProxy<T> {
 
 
 
-pub struct UiDocRef<T: UiModel + UiActions> {
+pub struct UiDocRef<T: ?Sized + UiModel + UiActions> {
     ptr: Box<*mut c_void>,
     orig_ptr: *mut c_void,
     _data: PhantomData<T>
 }
 
-impl<T: UiModel + UiActions> UiDocRef<T> {
+impl<T: ?Sized + UiModel + UiActions> UiDocRef<T> {
     fn from_ptr(doc_ptr: *mut c_void) -> UiDocRef<T> {
         let obj_ref = UiDocRef { ptr: Box::new(doc_ptr), orig_ptr: doc_ptr, _data: PhantomData };
         if !doc_ptr.is_null() {
@@ -545,7 +611,7 @@ impl<T: UiModel + UiActions> UiDocRef<T> {
     }
 }
 
-impl<T: UiModel + UiActions> Drop for UiDocRef<T> {
+impl<T: ?Sized + UiModel + UiActions> Drop for UiDocRef<T> {
     fn drop(&mut self) {
         unsafe {
             let doc_ptr = *self.ptr;
@@ -582,14 +648,14 @@ impl<T: UiModel + UiActions> Default for UiDocRef<T> {
     }
 }
 
-pub struct UiDocProxy<T: UiModel + UiActions> {
+pub struct UiDocProxy<T: ?Sized + UiModel + UiActions> {
     ptr: *mut c_void,
     _data: PhantomData<T>
 }
 
-unsafe impl<T: UiModel + UiActions> Send for UiDocProxy<T> {}
+unsafe impl<T: ?Sized + UiModel + UiActions> Send for UiDocProxy<T> {}
 
-impl<T: UiModel + UiActions> UiDocProxy<T> {
+impl<T: ?Sized + UiModel + UiActions> UiDocProxy<T> {
     fn from_ptr(ptr: *mut c_void) -> UiDocProxy<T> {
         unsafe {
             ui_document_ref(ptr);
@@ -610,7 +676,7 @@ impl<T: UiModel + UiActions> UiDocProxy<T> {
     }
 }
 
-impl<T: UiModel + UiActions> Drop for UiDocProxy<T> {
+impl<T: ?Sized + UiModel + UiActions> Drop for UiDocProxy<T> {
     fn drop(&mut self) {
         unsafe {
             ui_mainthread_document_unref(self.ptr);