]> uap-core.de Git - note.git/commitdiff
implement tableview
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 16 Apr 2026 16:36:50 +0000 (18:36 +0200)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Thu, 16 Apr 2026 16:36:50 +0000 (18:36 +0200)
application/src/main.rs
ui-rs/src/ui/ffi.rs
ui-rs/src/ui/list.rs
ui-rs/src/ui/toolkit.rs
ui/common/context.c
ui/common/types.c

index 2a12395e5a744576464521cddb06d156382ad477..f845c5004777073da4548dd454f9057e3526a8c3 100644 (file)
@@ -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::<i32>().fill(true).varname("list").getvalue(|elm, _row, _col| {
+        let mut model = TableModel::new();
+        model.add_column("Name", ColumnType::String, -1);
+        obj.tableview_builder::<i32>().fill(true).varname("list").model(&model).getvalue(|elm, _row, _col| {
             ListValue::String(elm.to_string())
         }).create();
     });
index 75aa1284d13a4514eba8bc6ffadaad264aab2a8b..0bb02f2dcc160b893fad48461895a4a85ef43980 100644 (file)
@@ -126,3 +126,7 @@ pub struct UiListArgs {
     _private: [u8; 0],
 }
 
+#[repr(C)]
+pub struct UiModel {
+    _private: [u8; 0],
+}
\ No newline at end of file
index 21ec4955390d1eda5af3ff53a4d80c3571c84908..cb6a14ed8a7f88ec310e797bc543dd07ea8e6324 100644 (file)
@@ -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<T>,
     create: ListBuilderCreate,
@@ -21,14 +21,54 @@ pub struct ListBuilder<'a, T, E> {
     _marker: PhantomData<E>
 }
 
+pub struct TableViewBuilder<'a, T, E> {
+    args: *mut UiListArgs,
+    obj: &'a mut toolkit::UiObject<T>,
+    create: ListBuilderCreate,
+    model: *mut ffi::UiModel,
+    has_getvalue_func: bool,
+    _marker: PhantomData<E>
+}
+
+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<T> toolkit::UiObject<T> {
-    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<C>(&mut self, value: &toolkit::UiList<C>) -> &mut Self {
+        unsafe {
+            ui_list_args_set_value(self.args, value.ptr);
+        }
+        self
+    }
+
+    pub fn getvalue<F>(&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::<T>);
+            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
index 01c3760cf9b03b0a135e0935921ff6bd52f962e1..87e5d76d21e7550d8f0aa377eeb38b778fb2d8b5 100644 (file)
@@ -117,23 +117,6 @@ impl<T> Default for UiList<T> {
     }
 }
 
-/* -------------------------------- 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<T>(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);
index 37249d7f25b5d170cd87477edda5e9b84e23576d..6936de941a7d8a2ce89d09ce6d07f2996f39fdf2 100644 (file)
@@ -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);
index d0251dacce80f5934d7143a0c857516faa60128e..ea6a3781bdb33917007020a864a6fa5da3400651 100644 (file)
@@ -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;i<mi->columns;i++) {
         ui_free(ctx, mi->titles[i]);
     }