improve gtk4 listview/table single row updates

Sat, 06 Sep 2025 12:40:04 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Sat, 06 Sep 2025 12:40:04 +0200
changeset 749
0f052f6f532c
parent 748
6c1fc70cd1c8
child 750
7b627710c155

improve gtk4 listview/table single row updates

ui/gtk/list.c file | annotate | diff | comparison | revisions
ui/gtk/list.h file | annotate | diff | comparison | revisions
--- a/ui/gtk/list.c	Wed Sep 03 17:48:46 2025 +0200
+++ b/ui/gtk/list.c	Sat Sep 06 12:40:04 2025 +0200
@@ -191,15 +191,29 @@
     return attr;
 }
 
-static void column_factory_bind(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
+static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, gpointer userdata) {
     UiColData *col = userdata;
     UiList *list = col->listview->var ? col->listview->var->value : NULL;
     UiListView *listview = col->listview;
-    
+     
     ObjWrapper *obj = gtk_list_item_get_item(item);
     UiModel *model = col->listview->model;
     UiModelType type = model->types[col->model_column];
     
+    // cache the GtkListItem
+    CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
+    UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
+    if(row) {
+        if(row->items[col->model_column] == NULL) {
+            row->bound++;
+        }
+    } else {
+        row = calloc(1, sizeof(UiRowItems) + listview->numcolumns * sizeof(GtkListItem*));
+        cxMapPut(listview->bound_rows, row_key, row);
+        row->bound = 1;
+    }
+    row->items[col->model_column] = item;
+    
     UiBool freevalue = FALSE;
     void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue);
     GtkWidget *child = gtk_list_item_get_child(item);
@@ -289,6 +303,21 @@
     }
 }
 
+static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) {
+    ObjWrapper *obj = gtk_list_item_get_item(item);
+    UiListView *listview = col->listview;
+    CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
+    UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
+    if(row) {
+        row->items[col->model_column] = NULL;
+        row->bound--;
+        if(row->bound == 0) {
+            cxMapRemove(listview->bound_rows, row_key);
+        }
+    } // else: should not happen
+}
+    
+
 static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) {
     GtkSelectionModel *selection_model;
     if(multiselection) {
@@ -316,10 +345,14 @@
         listview->getvalue = str_getvalue;
     }
     
+    listview->numcolumns = 1;
     listview->columns = malloc(sizeof(UiColData));
     listview->columns->listview = listview;
     listview->columns->data_column = 0;
     listview->columns->model_column = 0;
+    
+    listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
+    listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
      
     GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
     g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
@@ -402,11 +435,15 @@
         listview->getvalue = str_getvalue;
     }
     
+    listview->numcolumns = 1;
     listview->columns = malloc(sizeof(UiColData));
     listview->columns->listview = listview;
     listview->columns->data_column = 0;
     listview->columns->model_column = 0;
     
+    listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
+    listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
+    
     GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
     g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
     g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
@@ -495,6 +532,10 @@
     int columns = model ? model->columns : 0;
     
     tableview->columns = calloc(columns, sizeof(UiColData));
+    tableview->numcolumns = columns;
+    
+    tableview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
+    tableview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
     
     int addi = 0;
     for(int i=0;i<columns;i++) {
@@ -694,21 +735,20 @@
     } else {
         void *value = list->get(list, i);
         if(value) {
-            ObjWrapper *obj = obj_wrapper_new(value, i);
-            UiListSelection sel = list->getselection(list);
-            // TODO: if index i is selected, the selection is lost
-            // is it possible to update the item without removing it?
-            // workaround: save selection and reapply it
-            int count = g_list_model_get_n_items(G_LIST_MODEL(view->liststore));
-            if(count <= i) {
-                g_list_store_splice(view->liststore, i, 0, (void **)&obj, 1);
-            } else {
-                g_list_store_splice(view->liststore, i, 1, (void **)&obj, 1);
+            ObjWrapper *obj = g_list_model_get_item(G_LIST_MODEL(view->liststore), i);
+            if(obj) {
+                obj->data = value;
             }
-            if(sel.count > 0) {
-                list->setselection(list, sel);
+            
+            CxHashKey row_key = cx_hash_key(&i, sizeof(int));
+            UiRowItems *row = cxMapGet(view->bound_rows, row_key);
+            if(row) {
+                for(int c=0;c<view->numcolumns;c++) {
+                    if(row->items[c] != NULL) {
+                        column_factory_bind(NULL, row->items[c], &view->columns[c]);
+                    }
+                }
             }
-            ui_listselection_free(sel);
         }
     }
 }
@@ -1870,6 +1910,7 @@
     free(v->columns);
     pango_attr_list_unref(v->default_attributes);
     pango_attr_list_unref(v->current_row_attributes);
+    cxMapFree(v->bound_rows);
 #endif
     free(v->selection.rows);
     free(v);
--- a/ui/gtk/list.h	Wed Sep 03 17:48:46 2025 +0200
+++ b/ui/gtk/list.h	Sat Sep 06 12:40:04 2025 +0200
@@ -40,6 +40,13 @@
     
 typedef struct UiColData UiColData;
 
+#if GTK_CHECK_VERSION(4, 10, 0)
+typedef struct UiRowItems {
+    int bound;
+    GtkListItem *items[];
+} UiRowItems;
+#endif
+
 typedef struct UiListView {
     UiObject          *obj;
     GtkWidget         *widget;
@@ -47,7 +54,7 @@
     UiModel           *model;
     ui_getvaluefunc2  getvalue;
     void              *getvaluedata;
-    ui_getstylefunc      getstyle;
+    ui_getstylefunc   getstyle;
     void              *getstyledata;
     char              **elements;
     size_t            nelm;
@@ -55,9 +62,11 @@
     UiTextStyle       row_style;
     UiBool            apply_row_style;
 #if GTK_CHECK_VERSION(4, 10, 0)
+    CxMap             *bound_rows;
     GListStore        *liststore;
     GtkSelectionModel *selectionmodel;
     UiColData         *columns;
+    int               numcolumns;
     PangoAttrList     *default_attributes; // TODO: remove
     PangoAttrList     *current_row_attributes;
 #else

mercurial