Sat, 06 Sep 2025 12:40:04 +0200
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