From: Olaf Wintermann Date: Thu, 16 Apr 2026 16:36:50 +0000 (+0200) Subject: implement tableview X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;h=refs%2Fheads%2Fmain;p=note.git implement tableview --- diff --git a/application/src/main.rs b/application/src/main.rs index 2a12395..f845c50 100644 --- a/application/src/main.rs +++ b/application/src/main.rs @@ -1,5 +1,5 @@ use ui_rs::{ui, UiModel}; -use ui_rs::ui::{ListValue, UiList, UiModel}; +use ui_rs::ui::*; fn main() { ui::app_init("note"); @@ -31,7 +31,9 @@ fn create_window() { println!("Button clicked: {}", e.data.i); e.data.i += 1; }).create(); - obj.listview_builder::().fill(true).varname("list").getvalue(|elm, _row, _col| { + let mut model = TableModel::new(); + model.add_column("Name", ColumnType::String, -1); + obj.tableview_builder::().fill(true).varname("list").model(&model).getvalue(|elm, _row, _col| { ListValue::String(elm.to_string()) }).create(); }); diff --git a/ui-rs/src/ui/ffi.rs b/ui-rs/src/ui/ffi.rs index 75aa128..0bb02f2 100644 --- a/ui-rs/src/ui/ffi.rs +++ b/ui-rs/src/ui/ffi.rs @@ -126,3 +126,7 @@ pub struct UiListArgs { _private: [u8; 0], } +#[repr(C)] +pub struct UiModel { + _private: [u8; 0], +} \ No newline at end of file diff --git a/ui-rs/src/ui/list.rs b/ui-rs/src/ui/list.rs index 21ec495..cb6a14e 100644 --- a/ui-rs/src/ui/list.rs +++ b/ui-rs/src/ui/list.rs @@ -13,7 +13,7 @@ pub enum ListValue<'a> { String(String) } -pub struct ListBuilder<'a, T, E> { +pub struct ListViewBuilder<'a, T, E> { args: *mut UiListArgs, obj: &'a mut toolkit::UiObject, create: ListBuilderCreate, @@ -21,14 +21,54 @@ pub struct ListBuilder<'a, T, E> { _marker: PhantomData } +pub struct TableViewBuilder<'a, T, E> { + args: *mut UiListArgs, + obj: &'a mut toolkit::UiObject, + create: ListBuilderCreate, + model: *mut ffi::UiModel, + has_getvalue_func: bool, + _marker: PhantomData +} + +pub struct TableModel { + pub handle: *mut ffi::UiModel, +} + +pub enum ColumnType { + String = 0, + // skip UI_STRING_FREE + Integer = 2, + Icon = 3, + IconText = 4, + // skip UI_ICON_TEXT_FREE + EditableString = 6, + EditableBool = 7 +} + pub type ListBuilderCreate = fn(*const UiObject, *const UiListArgs); impl toolkit::UiObject { - widget_typed_fn!(listview, listview_builder, ListBuilder); - pub fn listview_builder<'a, E>(&'a mut self) -> ListBuilder<'a, T, E> { + widget_typed_fn!(listview, listview_builder, ListViewBuilder); + widget_typed_fn!(tableview, tableview_builder, TableViewBuilder); + + pub fn listview_builder<'a, E>(&'a mut self) -> ListViewBuilder<'a, T, E> { unsafe { let args = ui_list_args_new(); - ListBuilder { args: args, obj: self, create: list_create, has_getvalue_func: false, _marker: PhantomData } + ListViewBuilder { args: args, obj: self, create: list_create, has_getvalue_func: false, _marker: PhantomData } + } + } + + pub fn dropdown_builder<'a, E>(&'a mut self) -> ListViewBuilder<'a, T, E> { + unsafe { + let args = ui_list_args_new(); + ListViewBuilder { args: args, obj: self, create: dropdown_create, has_getvalue_func: false, _marker: PhantomData } + } + } + + pub fn tableview_builder<'a, E>(&'a mut self) -> TableViewBuilder<'a, T, E> { + unsafe { + let args = ui_list_args_new(); + TableViewBuilder { args: args, obj: self, create: tableview_create, model: std::ptr::null_mut(), has_getvalue_func: false, _marker: PhantomData } } } } @@ -39,7 +79,19 @@ fn list_create(obj: *const UiObject, args: *const UiListArgs) { } } -impl<'a, T, E> Drop for ListBuilder<'a, T, E> { +fn dropdown_create(obj: *const UiObject, args: *const UiListArgs) { + unsafe { + ui_dropdown_create(obj, args); + } +} + +fn tableview_create(obj: *const UiObject, args: *const UiListArgs) { + unsafe { + ui_table_create(obj, args); + } +} + +impl<'a, T, E> Drop for ListViewBuilder<'a, T, E> { fn drop(&mut self) { unsafe { ui_list_args_free(self.args); @@ -47,14 +99,191 @@ impl<'a, T, E> Drop for ListBuilder<'a, T, E> { } } +impl<'a, T, E> ListViewBuilder<'a, T, E> { + pub fn create(&mut self) { + if !self.has_getvalue_func { + unsafe { + ui_list_args_set_getvalue_func2(self.args, null_getvalue_wrapper); + } + } + (self.create)(self.obj.ptr, self.args); + } + + pub fn fill(&mut self, fill: bool) -> &mut Self { + unsafe { + ui_list_args_set_fill(self.args, if fill { 1 } else { 0 }); + } + self + } + + pub fn hexpand(&mut self, value: bool) -> &mut Self { + unsafe { + ui_list_args_set_hexpand(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn vexpand(&mut self, value: bool) -> &mut Self { + unsafe { + ui_list_args_set_vexpand(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn hfill(&mut self, value: bool) -> &mut Self { + unsafe { + ui_list_args_set_hfill(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn vfill(&mut self, value: bool) -> &mut Self { + unsafe { + ui_list_args_set_vfill(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn override_defaults(&mut self, value: bool) -> &mut Self { + unsafe { + ui_list_args_set_override_defaults(self.args, if value { 1 } else { 0 }); + } + self + } + + pub fn margin(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_margin(self.args, value); + } + self + } + + pub fn margin_left(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_margin_left(self.args, value); + } + self + } + + pub fn margin_right(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_margin_right(self.args, value); + } + self + } + + pub fn margin_top(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_margin_top(self.args, value); + } + self + } + + pub fn margin_bottom(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_margin_bottom(self.args, value); + } + self + } + + pub fn colspan(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_colspan(self.args, value); + } + self + } + + pub fn rowspan(&mut self, value: i32) -> &mut Self { + unsafe { + ui_list_args_set_rowspan(self.args, value); + } + self + } + + pub fn name(&mut self, value: &str) -> &mut Self { + let cstr = CString::new(value).unwrap(); + unsafe { + ui_list_args_set_name(self.args, cstr.as_ptr()); + } + self + } + + pub fn style_class(&mut self, value: &str) -> &mut Self { + let cstr = CString::new(value).unwrap(); + unsafe { + ui_list_args_set_style_class(self.args, cstr.as_ptr()); + } + self + } + + pub fn visibility_states(&mut self, states: &[i32]) -> &mut Self { + unsafe { + ui_list_args_set_visibility_states(self.args, states.as_ptr(), states.len() as c_int); + } + self + } + + pub fn varname(&mut self, varname: &str) -> &mut Self { + let cstr = CString::new(varname).unwrap(); + unsafe { + ui_list_args_set_varname(self.args, cstr.as_ptr()); + } + self + } + pub fn value(&mut self, value: &toolkit::UiList) -> &mut Self { + unsafe { + ui_list_args_set_value(self.args, value.ptr); + } + self + } + + pub fn getvalue(&mut self, f: F) -> &mut Self + where F: Fn(&E, i32, i32) -> ListValue<'a> + 'static { + unsafe { + let wrapper = Box::new(GetValueWrapper { callback: Box::new(f) }); + let ptr = self.obj.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); + } + self.has_getvalue_func = true; + self + } + + pub fn states(&mut self, states: &[i32]) -> &mut Self { + unsafe { + ui_list_args_set_states(self.args, states.as_ptr(), states.len() as c_int); + } + self + } +} + + + +impl<'a, T, E> Drop for TableViewBuilder<'a, T, E> { + fn drop(&mut self) { + unsafe { + ui_list_args_free(self.args); + if !self.model.is_null() { + ui_model_unref(self.model); + } + } + } +} -impl<'a, T, E> ListBuilder<'a, T, E> { +impl<'a, T, E> TableViewBuilder<'a, T, E> { pub fn create(&mut self) { if !self.has_getvalue_func { unsafe { ui_list_args_set_getvalue_func2(self.args, null_getvalue_wrapper); } } + if !self.model.is_null() { + // The table view create function takes ownership of the passed UiModel and doesn't + // increase the reference counter + unsafe { + ui_model_ref(self.model); + } + } (self.create)(self.obj.ptr, self.args); } @@ -198,6 +427,20 @@ impl<'a, T, E> ListBuilder<'a, T, E> { self } + pub fn model(&mut self, model: &TableModel) -> &mut Self { + unsafe { + if !self.model.is_null() { + ui_model_unref(self.model); + } + ui_list_args_set_model(self.args, model.handle); + if !model.handle.is_null() { + ui_model_ref(model.handle); + } + } + self.model = model.handle; + self + } + pub fn states(&mut self, states: &[i32]) -> &mut Self { unsafe { ui_list_args_set_states(self.args, states.as_ptr(), states.len() as c_int); @@ -206,6 +449,35 @@ impl<'a, T, E> ListBuilder<'a, T, E> { } } + +// TableModel implementation + +impl TableModel { + pub fn new() -> Self { + unsafe { + TableModel { handle: ui_model_new(std::ptr::null_mut()) } + } + } + + pub fn add_column(&mut self, title: &str, ctype: ColumnType, width: i32) { + let cstr = CString::new(title).unwrap(); + unsafe { + ui_model_add_column(self.handle, ctype as i32, cstr.as_ptr(), width); + } + } +} + +impl Drop for TableModel { + fn drop(&mut self) { + unsafe { + ui_model_unref(self.handle); + } + } +} + + +/* ------------------------------- getvalue func wrapper -------------------------------- */ + type GetValueFunc = extern "C" fn(list: *const ffi::UiList, elm_ptr: *const c_void, row: i32, col: i32, wrapper: *const c_void, free: *mut bool) -> *mut c_void; pub struct GetValueWrapper<'a, T> { @@ -253,6 +525,8 @@ extern "C" { fn malloc(size: usize) -> *mut c_void; fn ui_listview_create(obj: *const UiObject, args: *const UiListArgs); + fn ui_dropdown_create(obj: *const UiObject, args: *const UiListArgs); + fn ui_table_create(obj: *const UiObject, args: *const UiListArgs); fn ui_list_args_new() -> *mut UiListArgs; fn ui_list_args_set_fill(args: *mut UiListArgs, fill: c_int); @@ -274,6 +548,7 @@ extern "C" { fn ui_list_args_set_onchangedata(args: *mut UiListArgs, data: *mut c_void); fn ui_list_args_set_varname(args: *mut UiListArgs, varname: *const c_char); fn ui_list_args_set_value(args: *mut UiListArgs, ivalue: *mut UiList); + fn ui_list_args_set_model(args: *mut UiListArgs, model: *mut ffi::UiModel); fn ui_list_args_set_getvalue_func2(args: *mut UiListArgs, func: GetValueFunc); fn ui_list_args_set_getvalue_data(args: *mut UiListArgs, data: *mut c_void); fn ui_list_args_set_enablestate(args: *mut UiListArgs, state: c_int); @@ -281,4 +556,9 @@ extern "C" { fn ui_list_args_set_visibility_states(args: *mut UiListArgs, states: *const c_int, numstates: c_int); fn ui_list_args_free(args: *mut UiListArgs); + fn ui_model_new(ctx: *mut ffi::UiContext) -> *mut ffi::UiModel; + fn ui_model_add_column(model: *mut ffi::UiModel, coltype: c_int, title: *const c_char, width: c_int); + fn ui_model_ref(model: *mut ffi::UiModel); + fn ui_model_unref(model: *mut ffi::UiModel); + } \ No newline at end of file diff --git a/ui-rs/src/ui/toolkit.rs b/ui-rs/src/ui/toolkit.rs index 01c3760..87e5d76 100644 --- a/ui-rs/src/ui/toolkit.rs +++ b/ui-rs/src/ui/toolkit.rs @@ -117,23 +117,6 @@ impl Default for UiList { } } -/* -------------------------------- C functions -------------------------------- */ - -extern "C" { - fn ui_init(appname: *const c_char, argc: c_int, argv: *const *const c_char); - - 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_text_get(value: *const ffi::UiText) -> *mut c_char; - fn ui_text_set(value: *const ffi::UiText, str: *const c_char); - fn ui_string_get(value: *const ffi::UiString) -> *mut c_char; - fn ui_string_set(value: *const ffi::UiString, str: *const c_char); - fn ui_int_get(value: *const ffi::UiInteger) -> i64; - fn ui_int_set(value: *const ffi::UiInteger, i: i64); -} - pub fn app_init(appname: &str) { let s; let c_str = if appname.len() > 0 { @@ -156,7 +139,7 @@ impl UiText { self.ptr = ui_text_new(ctx.ptr, c_str); } } - + pub fn get(&self) -> String { unsafe { let cstr = ui_text_get(self.ptr); @@ -207,7 +190,7 @@ impl UiInteger { self.ptr = ui_int_new(ctx.ptr, c_str); } } - + pub fn get(&self) -> i64 { unsafe { ui_int_get(self.ptr) @@ -362,20 +345,37 @@ extern "C" fn list_count(list: *mut ffi::UiList) -> c_int { } } -type UiListInitFunc = extern "C" fn(*mut ffi::UiContext, *mut ffi::UiList, *mut c_void); -type UiListDestroyFunc = extern "C" fn(*mut ffi::UiContext, *mut ffi::UiList, *mut c_void); -type UiListFirstFunc = extern "C" fn(*mut ffi::UiList) -> *mut c_void; -type UiListNextFunc = extern "C" fn(*mut ffi::UiList) -> *mut c_void; -type UiListGetFunc = extern "C" fn(*mut ffi::UiList, c_int) -> *mut c_void; -type UiListCountFunc = extern "C" fn(*mut ffi::UiList) -> c_int; +/* -------------------------------- C functions -------------------------------- */ extern "C" { + fn ui_init(appname: *const c_char, argc: c_int, argv: *const *const c_char); + + 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_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; fn ui_range_new(ctx: *mut ffi::UiContext, name: *const c_char) -> *mut ffi::UiRange; fn ui_string_new(ctx: *mut ffi::UiContext, name: *const c_char) -> *mut ffi::UiString; fn ui_text_new(ctx: *mut ffi::UiContext, name: *const c_char) -> *mut ffi::UiText; + fn ui_text_get(value: *const ffi::UiText) -> *mut c_char; + fn ui_text_set(value: *const ffi::UiText, str: *const c_char); + fn ui_string_get(value: *const ffi::UiString) -> *mut c_char; + fn ui_string_set(value: *const ffi::UiString, str: *const c_char); + fn ui_int_get(value: *const ffi::UiInteger) -> i64; + fn ui_int_set(value: *const ffi::UiInteger, i: i64); +} + +type UiListInitFunc = extern "C" fn(*mut ffi::UiContext, *mut ffi::UiList, *mut c_void); +type UiListDestroyFunc = extern "C" fn(*mut ffi::UiContext, *mut ffi::UiList, *mut c_void); +type UiListFirstFunc = extern "C" fn(*mut ffi::UiList) -> *mut c_void; +type UiListNextFunc = extern "C" fn(*mut ffi::UiList) -> *mut c_void; +type UiListGetFunc = extern "C" fn(*mut ffi::UiList, c_int) -> *mut c_void; +type UiListCountFunc = extern "C" fn(*mut ffi::UiList) -> c_int; + +extern "C" { fn ui_list_new2(ctx: *mut ffi::UiContext, name: *const c_char, init: UiListInitFunc, userdata: *mut c_void) -> *mut ffi::UiList; fn ui_list_class_set_first(list: *mut ffi::UiList, func: UiListFirstFunc); fn ui_list_class_set_next(list: *mut ffi::UiList, func: UiListNextFunc); diff --git a/ui/common/context.c b/ui/common/context.c index 37249d7..6936de9 100644 --- a/ui/common/context.c +++ b/ui/common/context.c @@ -692,26 +692,30 @@ void* ui_cx_mempool(UiContext *ctx) { } void* ui_malloc(UiContext *ctx, size_t size) { - return ctx ? cxMalloc(ctx->allocator, size) : NULL; + return ctx ? cxMalloc(ctx->allocator, size) : malloc(size); } void* ui_calloc(UiContext *ctx, size_t nelem, size_t elsize) { - return ctx ? cxCalloc(ctx->allocator, nelem, elsize) : NULL; + return ctx ? cxCalloc(ctx->allocator, nelem, elsize) : calloc(nelem, elsize); } void ui_free(UiContext *ctx, void *ptr) { - if(ctx && ptr) { - cxFree(ctx->allocator, ptr); + if(ptr) { + if(ctx) { + cxFree(ctx->allocator, ptr); + } else { + free(ptr); + } } } void* ui_realloc(UiContext *ctx, void *ptr, size_t size) { - return ctx ? cxRealloc(ctx->allocator, ptr, size) : NULL; + return ctx ? cxRealloc(ctx->allocator, ptr, size) : realloc(ptr, size); } char* ui_strdup(UiContext *ctx, const char *str) { if(!ctx) { - return NULL; + return strdup(str ? str : ""); } cxstring s = cx_str(str); cxmutstr d = cx_strdup_a(ctx->allocator, s); diff --git a/ui/common/types.c b/ui/common/types.c index d0251da..ea6a378 100644 --- a/ui/common/types.c +++ b/ui/common/types.c @@ -224,6 +224,7 @@ typedef struct { UiModel* ui_model(UiContext *ctx, ...) { UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel)); info->ctx = ctx; + info->ref = 1; va_list ap; va_start(ap, ctx); @@ -275,6 +276,7 @@ static void model_notify_observer(UiModel *model, int insert, int delete) { UiModel* ui_model_new(UiContext *ctx) { UiModel *info = ui_calloc(ctx, 1, sizeof(UiModel)); info->ctx = ctx; + info->ref = 1; info->alloc = UI_MODEL_DEFAULT_ALLOC_SIZE; info->types = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(UiModelType)); info->titles = ui_calloc(ctx, UI_MODEL_DEFAULT_ALLOC_SIZE, sizeof(char*)); @@ -371,7 +373,7 @@ void ui_model_remove_observer(UiModel *model, void *data) { void ui_model_free(UiModel *mi) { UiContext *ctx = mi->ctx; - const CxAllocator* a = ctx->allocator; + const CxAllocator* a = ctx ? ctx->allocator : cxDefaultAllocator; for(int i=0;icolumns;i++) { ui_free(ctx, mi->titles[i]); }