From bc5306fe095c15174a8677f8ae43cec3e3c265f4 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Sat, 18 Apr 2026 08:08:33 +0200 Subject: [PATCH] add UiDoc --- ui-rs/src/ui/button.rs | 4 +- ui-rs/src/ui/list.rs | 4 +- ui-rs/src/ui/text.rs | 4 +- ui-rs/src/ui/toolkit.rs | 106 ++++++++++++++++++++++++++++++---------- ui-rs/src/ui/window.rs | 19 +------ 5 files changed, 88 insertions(+), 49 deletions(-) diff --git a/ui-rs/src/ui/button.rs b/ui-rs/src/ui/button.rs index 26dcf4f..846aa40 100644 --- a/ui-rs/src/ui/button.rs +++ b/ui-rs/src/ui/button.rs @@ -261,7 +261,7 @@ impl<'a, T> ButtonBuilder<'a, T> { pub fn onclick(&mut self, f: F) -> &mut Self where F: FnMut(&mut event::Event) + 'static { let wrapper = Box::new(EventWrapper { callback: Box::new(f) }); - let ptr = self.obj.reg_box(wrapper); + let ptr = self.obj.ctx.reg_box(wrapper); unsafe { ui_button_args_set_onclick(self.args, Some(event::event_wrapper::)); ui_button_args_set_onclickdata(self.args, ptr as *mut c_void); @@ -453,7 +453,7 @@ impl<'a, T> ToggleBuilder<'a, T> { pub fn onchange(&mut self, f: F) -> &mut Self where F: FnMut(&mut event::Event) + 'static { let wrapper = Box::new(EventWrapper { callback: Box::new(f) }); - let ptr = self.obj.reg_box(wrapper); + let ptr = self.obj.ctx.reg_box(wrapper); unsafe { ui_toggle_args_set_onchange(self.args, Some(event::event_wrapper::)); ui_toggle_args_set_onchangedata(self.args, ptr as *mut c_void); diff --git a/ui-rs/src/ui/list.rs b/ui-rs/src/ui/list.rs index 7398b75..94a3432 100644 --- a/ui-rs/src/ui/list.rs +++ b/ui-rs/src/ui/list.rs @@ -242,7 +242,7 @@ impl<'a, T, E> ListViewBuilder<'a, T, E> { where F: Fn(&E, i32, i32) -> ListValue<'a> + 'static { unsafe { let wrapper = Box::new(GetValueWrapper { callback: Box::new(f), is_table: false }); - let ptr = self.obj.reg_box(wrapper); + let ptr = self.obj.ctx.reg_box(wrapper); ui_list_args_set_getvalue_func2(self.args, getvalue_wrapper::); ui_list_args_set_getvalue_data(self.args, ptr as *mut c_void); } @@ -420,7 +420,7 @@ impl<'a, T, E> TableViewBuilder<'a, T, E> { where F: Fn(&E, i32, i32) -> ListValue<'a> + 'static { unsafe { let wrapper = Box::new(GetValueWrapper { callback: Box::new(f), is_table: true }); - let ptr = self.obj.reg_box(wrapper); + let ptr = self.obj.ctx.reg_box(wrapper); ui_list_args_set_getvalue_func2(self.args, getvalue_wrapper::); ui_list_args_set_getvalue_data(self.args, ptr as *mut c_void); } diff --git a/ui-rs/src/ui/text.rs b/ui-rs/src/ui/text.rs index 9d9dbda..fde4232 100644 --- a/ui-rs/src/ui/text.rs +++ b/ui-rs/src/ui/text.rs @@ -164,7 +164,7 @@ impl<'a, T> TextAreaBuilder<'a, T> { pub fn onchange(&mut self, f: F) -> &mut Self where F: FnMut(&mut event::Event) + 'static { let wrapper = Box::new(EventWrapper { callback: Box::new(f) }); - let ptr = self.obj.reg_box(wrapper); + let ptr = self.obj.ctx.reg_box(wrapper); unsafe { ui_textarea_args_set_onchange(self.args, Some(event::event_wrapper::)); ui_textarea_args_set_onchangedata(self.args, ptr as *mut c_void); @@ -382,7 +382,7 @@ impl<'a, T> TextFieldBuilder<'a, T> { pub fn onchange(&mut self, f: F) -> &mut Self where F: FnMut(&mut event::Event) + 'static { let wrapper = Box::new(EventWrapper { callback: Box::new(f) }); - let ptr = self.obj.reg_box(wrapper); + let ptr = self.obj.ctx.reg_box(wrapper); unsafe { ui_textfield_args_set_onchange(self.args, Some(event::event_wrapper::)); ui_textfield_args_set_onchangedata(self.args, ptr as *mut c_void); diff --git a/ui-rs/src/ui/toolkit.rs b/ui-rs/src/ui/toolkit.rs index 87e5d76..f9bed16 100644 --- a/ui-rs/src/ui/toolkit.rs +++ b/ui-rs/src/ui/toolkit.rs @@ -1,25 +1,19 @@ #![allow(dead_code)] use std::ffi::{c_char, c_int, c_void, CStr, CString}; -use crate::ui::ffi; +use crate::ui::{ffi, ui_object_get_context, ui_reg_destructor}; use std::marker::PhantomData; +use std::mem; pub struct UiContext { - pub ptr: *mut ffi::UiContext, - doc: *mut c_void + pub ptr: *mut ffi::UiContext } impl Clone for UiContext { fn clone(&self) -> Self { - unsafe { - if !self.doc.is_null() { - ui_document_ref(self.doc); - } - } UiContext { - ptr: self.ptr, - doc: self.doc + ptr: self.ptr } } } @@ -27,28 +21,35 @@ impl Clone for UiContext { impl Default for UiContext { fn default() -> Self { UiContext { - ptr: std::ptr::null_mut(), - doc: std::ptr::null_mut() + ptr: std::ptr::null_mut() } } } -impl Drop for UiContext { - fn drop(&mut self) { - if !self.doc.is_null() { - unsafe { - ui_document_unref(self.doc); - } +impl UiContext { + pub fn from_ptr(ctx: *mut ffi::UiContext) -> UiContext { + UiContext { + ptr: ctx } } -} + pub fn reg_box(&mut self, b: Box) -> *mut B { + let ptr = Box::into_raw(b); + unsafe { + ui_reg_destructor(self.ptr, ptr as *mut c_void, destroy_boxed::); + } + ptr + } -impl UiContext { - pub fn from_ptr(ctx: *mut ffi::UiContext) -> UiContext { - UiContext { - ptr: ctx, - doc: std::ptr::null_mut() + pub fn attach(&mut self, doc: &UiDoc) { + unsafe { + ui_attach_document(self.ptr, doc.ptr); + } + } + + pub fn detach(&mut self, doc: &UiDoc) { + unsafe { + ui_attach_document(self.ptr, doc.ptr); } } @@ -59,6 +60,56 @@ impl UiContext { } } +extern "C" fn destroy_boxed(data: *mut c_void) { + if data.is_null() { + return; + } + unsafe { + drop(Box::from_raw(data as *mut T)); + } +} + +pub struct UiDoc { + pub ctx: UiContext, + ptr: *mut c_void +} + +impl Clone for UiDoc { + fn clone(&self) -> Self { + unsafe { + ui_document_ref(self.ptr); + } + UiDoc { + ctx: self.ctx.clone(), + ptr: self.ptr + } + } +} + +impl Drop for UiDoc { + fn drop(&mut self) { + unsafe { + if !self.ptr.is_null() { + ui_document_unref(self.ptr); + } + } + } +} + +impl UiDoc { + pub fn new(mut data: T) -> UiDoc { + unsafe { + let doc = ui_document_new(mem::size_of::<*mut T>()); + let mut ctx = UiContext { ptr: ui_document_context(doc) }; + data.init(&ctx); + let data_ptr = ctx.reg_box(Box::new(data)); // returns *mut T + let doc_storage: *mut *mut T = doc.cast(); + *doc_storage = data_ptr; + UiDoc { ctx: ctx, ptr: doc } + } + } +} + pub trait UiModel { fn init(&mut self, ctx: &UiContext); } @@ -260,7 +311,9 @@ impl Drop for UiList { unsafe { // This does not free the UiList pointer, because it could still be in use by // UI elements, but it will have no reference to any object managed by Rust - ui_list_class_set_data(self.ptr, std::ptr::null_mut()); + if !self.ptr.is_null() { + ui_list_class_set_data(self.ptr, std::ptr::null_mut()); + } } } } @@ -353,6 +406,9 @@ extern "C" { fn ui_document_new(size: usize) -> *mut c_void; fn ui_document_ref(doc: *mut c_void); fn ui_document_unref(doc: *mut c_void); + fn ui_document_context(doc: *mut c_void) -> *mut ffi::UiContext; + fn ui_attach_document(ctx: *mut ffi::UiContext, doc: *mut c_void); + fn ui_detach_document(ctx: *mut ffi::UiContext, doc: *mut c_void); fn ui_int_new(ctx: *mut ffi::UiContext, name: *const c_char) -> *mut ffi::UiInteger; fn ui_double_new(ctx: *mut ffi::UiContext, name: *const c_char) -> *mut ffi::UiDouble; diff --git a/ui-rs/src/ui/window.rs b/ui-rs/src/ui/window.rs index a046e29..3578213 100644 --- a/ui-rs/src/ui/window.rs +++ b/ui-rs/src/ui/window.rs @@ -23,14 +23,6 @@ extern "C" { pub fn ui_reg_destructor(ctx: *mut UiContext, data: *mut c_void, destructor: UiDestructor); } -extern "C" fn destroy_boxed(data: *mut c_void) { - if data.is_null() { - return; - } - unsafe { - drop(Box::from_raw(data as *mut T)); - } -} impl toolkit::UiObject { pub fn show(&self) { @@ -38,15 +30,6 @@ impl toolkit::UiObject { ui_show(self.ptr); } } - - pub fn reg_box(&mut self, b: Box) -> *mut B { - let ptr = Box::into_raw(b); - unsafe { - let ctx = ui_object_get_context(self.ptr); - ui_reg_destructor(ctx, ptr as *mut c_void, destroy_boxed::); - } - ptr - } } enum WindowType { @@ -88,7 +71,7 @@ where F: FnOnce(&mut toolkit::UiObject, &mut T) { // store windowdata object in the UiObject let window_data = Box::new(data); - let wdata_ptr = obj.reg_box(window_data); + let wdata_ptr = obj.ctx.reg_box(window_data); unsafe { ui_object_set_windowdata(objptr, wdata_ptr as *mut c_void); } -- 2.47.3