--- a/ui/gtk/list.c Sun Jul 20 22:04:39 2025 +0200 +++ b/ui/gtk/list.c Sun Aug 24 15:24:16 2025 +0200 @@ -39,21 +39,22 @@ #include <cx/linked_list.h> #include "list.h" +#include "button.h" #include "icon.h" #include "menu.h" #include "dnd.h" -void* ui_strmodel_getvalue(void *elm, int column) { - return column == 0 ? elm : NULL; +static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata; + return getvalue(elm, col); } -static void* model_getvalue(UiModel *model, UiList *list, void *elm, int row, int col, UiBool *freeResult) { - if(model->getvalue2) { - return model->getvalue2(list, elm, row, col, model->getvalue2data, freeResult); - } else if(model->getvalue) { - return model->getvalue(elm, col); - } +static void* str_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { + return elm; +} + +static void* null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { return NULL; } @@ -74,6 +75,37 @@ } } +static UiListView* create_listview(UiObject *obj, UiListArgs *args) { + UiListView *tableview = malloc(sizeof(UiListView)); + memset(tableview, 0, sizeof(UiListView)); + tableview->obj = obj; + tableview->model = args->model; + tableview->onactivate = args->onactivate; + tableview->onactivatedata = args->onactivatedata; + tableview->onselection = args->onselection; + tableview->onselectiondata = args->onselectiondata; + tableview->ondragstart = args->ondragstart; + tableview->ondragstartdata = args->ondragstartdata; + tableview->ondragcomplete = args->ondragcomplete; + tableview->ondragcompletedata = args->ondragcompletedata; + tableview->ondrop = args->ondrop; + tableview->ondropdata = args->ondropdata; + tableview->selection.count = 0; + tableview->selection.rows = NULL; + + if(args->getvalue2) { + tableview->getvalue = args->getvalue2; + tableview->getvaluedata = args->getvalue2data; + } else if(args->getvalue) { + tableview->getvalue = getvalue_wrapper; + tableview->getvaluedata = (void*)args->getvalue; + } else { + tableview->getvalue = null_getvalue; + } + + return tableview; +} + #if GTK_CHECK_VERSION(4, 10, 0) @@ -131,16 +163,17 @@ } } -static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { +static void column_factory_bind(GtkListItemFactory *factory, 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]; UiBool freevalue = FALSE; - void *data = model_getvalue(model, list, obj->data, obj->i, col->data_column, &freevalue); + void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue); GtkWidget *child = gtk_list_item_get_child(item); switch(type) { @@ -172,7 +205,7 @@ } case UI_ICON_TEXT_FREE: { - void *data2 = model_getvalue(model, list, obj->data, obj->i, col->data_column+1, &freevalue); + void *data2 = listview->getvalue(list, obj->data, obj->i, col->data_column+1, listview->getvaluedata, &freevalue); if(type == UI_ICON_TEXT_FREE) { freevalue = TRUE; } @@ -199,55 +232,32 @@ selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(G_LIST_MODEL(liststore))); } else { selection_model = GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(liststore))); + gtk_single_selection_set_can_unselect(GTK_SINGLE_SELECTION(selection_model), TRUE); + gtk_single_selection_set_autoselect(GTK_SINGLE_SELECTION(selection_model), FALSE); } g_signal_connect(selection_model, "selection-changed", G_CALLBACK(ui_listview_selection_changed), listview); return selection_model; } -static UiListView* create_listview(UiObject *obj, UiListArgs *args) { - UiListView *tableview = malloc(sizeof(UiListView)); - memset(tableview, 0, sizeof(UiListView)); - tableview->obj = obj; - tableview->model = args->model; - tableview->onactivate = args->onactivate; - tableview->onactivatedata = args->onactivatedata; - tableview->onselection = args->onselection; - tableview->onselectiondata = args->onselectiondata; - tableview->ondragstart = args->ondragstart; - tableview->ondragstartdata = args->ondragstartdata; - tableview->ondragcomplete = args->ondragcomplete; - tableview->ondragcompletedata = args->ondragcompletedata; - tableview->ondrop = args->ondrop; - tableview->ondropdata = args->ondropdata; - tableview->selection.count = 0; - tableview->selection.rows = NULL; - return tableview; -} - UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { UiObject* current = uic_current_obj(obj); // to simplify things and share code with ui_table_create, we also // use a UiModel for the listview UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; - } args->model = model; GListStore *ls = g_list_store_new(G_TYPE_OBJECT); UiListView *listview = create_listview(obj, args); + if(!args->getvalue && !args->getvalue2) { + listview->getvalue = str_getvalue; + } listview->columns = malloc(sizeof(UiColData)); listview->columns->listview = listview; listview->columns->data_column = 0; listview->columns->model_column = 0; - + 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); @@ -280,7 +290,7 @@ ui_update_liststore(ls, list); } else if (args->static_elements && args->static_nelm > 0) { listview_copy_static_elements(listview, args->static_elements, args->static_nelm); - listview->model->getvalue = ui_strmodel_getvalue; // force strmodel + listview->getvalue = str_getvalue; // force string values ui_update_liststore_static(ls, listview->elements, listview->nelm); } @@ -320,19 +330,15 @@ // to simplify things and share code with ui_tableview_create, we also // use a UiModel for the listview UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; - } args->model = model; GListStore *ls = g_list_store_new(G_TYPE_OBJECT); UiListView *listview = create_listview(obj, args); + if(!args->getvalue && !args->getvalue2) { + listview->getvalue = str_getvalue; + } + listview->columns = malloc(sizeof(UiColData)); listview->columns->listview = listview; listview->columns->data_column = 0; @@ -370,7 +376,7 @@ ui_update_liststore(ls, list); } else if (args->static_elements && args->static_nelm > 0) { listview_copy_static_elements(listview, args->static_elements, args->static_nelm); - listview->model->getvalue = ui_strmodel_getvalue; // force strmodel + listview->getvalue = str_getvalue; // force string values ui_update_liststore_static(ls, listview->elements, listview->nelm); } @@ -626,14 +632,20 @@ 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); } + if(sel.count > 0) { + list->setselection(list, sel); + } + ui_listselection_free(sel); } } } @@ -695,12 +707,13 @@ #else -static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *model, UiList *list, void *elm, int row) { +static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIter *iter, UiList *list, void *elm, int row) { + UiModel *model = listview->model; // set column values int c = 0; for(int i=0;i<model->columns;i++,c++) { UiBool freevalue = FALSE; - void *data = model_getvalue(model, list, elm, row, c, &freevalue); + void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue); GValue value = G_VALUE_INIT; switch(model->types[i]) { @@ -765,7 +778,7 @@ c++; freevalue = FALSE; - char *str = model_getvalue(model, list, elm, row, c, &freevalue); + char *str = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue); g_value_init(&value, G_TYPE_STRING); g_value_set_string(&value, str); if(model->types[i] == UI_ICON_TEXT_FREE || freevalue) { @@ -779,7 +792,8 @@ } } -static GtkListStore* create_list_store(UiList *list, UiModel *model) { +static GtkListStore* create_list_store(UiListView *listview, UiList *list) { + UiModel *model = listview->model; int columns = model->columns; GType types[2*columns]; int c = 0; @@ -807,7 +821,7 @@ GtkTreeIter iter; gtk_list_store_insert (store, &iter, -1); - update_list_row(store, &iter, model, list, elm, i++); + update_list_row(listview, store, &iter, list, elm, i++); // next row elm = list->next(list); @@ -841,36 +855,29 @@ #endif UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; + + UiListView *listview = create_listview(obj, args); + if(!args->getvalue && !args->getvalue2) { + listview->getvalue = str_getvalue; } - - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); - - UiList *list = var ? var->value : NULL; - GtkListStore *listmodel = create_list_store(list, model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); - g_object_unref(listmodel); - - UiListView *listview = malloc(sizeof(UiListView)); - memset(listview, 0, sizeof(UiListView)); - listview->obj = obj; - listview->widget = view; - listview->var = var; listview->model = model; - listview->selection.count = 0; - listview->selection.rows = NULL; g_signal_connect( view, "destroy", G_CALLBACK(ui_listview_destroy), listview); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); + + // init listview + listview->widget = view; + listview->var = var; + + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(listview, list); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); + g_object_unref(listmodel); + // bind var list->update = ui_listview_update; list->getselection = ui_listview_getselection; @@ -921,7 +928,7 @@ SCROLLEDWINDOW_SET_CHILD(scroll_area, view); UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, scroll_area, FALSE); + current->container->add(current->container, scroll_area); // ct->current should point to view, not scroll_area, to make it possible // to add a context menu @@ -1006,36 +1013,23 @@ UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); - UiList *list = var ? var->value : NULL; - GtkListStore *listmodel = create_list_store(list, model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); - g_object_unref(listmodel); - //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL); //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL); // add TreeView as observer to the UiList to update the TreeView if the // data changes - UiListView *tableview = malloc(sizeof(UiListView)); - memset(tableview, 0, sizeof(UiListView)); - tableview->obj = obj; - tableview->widget = view; - tableview->var = var; - tableview->model = model; - tableview->ondragstart = args->ondragstart; - tableview->ondragstartdata = args->ondragstartdata; - tableview->ondragcomplete = args->ondragcomplete; - tableview->ondragcompletedata = args->ondragcompletedata; - tableview->ondrop = args->ondrop; - tableview->ondropdata = args->ondropdata; - tableview->selection.count = 0; - tableview->selection.rows = NULL; + UiListView *tableview = create_listview(obj, args); g_signal_connect( view, "destroy", G_CALLBACK(ui_listview_destroy), tableview); + UiList *list = var ? var->value : NULL; + GtkListStore *listmodel = create_list_store(tableview, list); + gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel)); + g_object_unref(listmodel); + // bind var list->update = ui_listview_update; list->getselection = ui_listview_getselection; @@ -1098,7 +1092,7 @@ } UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, scroll_area, FALSE); + current->container->add(current->container, scroll_area); // ct->current should point to view, not scroll_area, to make it possible // to add a context menu @@ -1112,7 +1106,7 @@ void ui_listview_update(UiList *list, int i) { UiListView *view = list->obj; if(i < 0) { - GtkListStore *store = create_list_store(list, view->model); + GtkListStore *store = create_list_store(view, list); gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store)); g_object_unref(G_OBJECT(store)); } else { @@ -1120,7 +1114,7 @@ GtkTreeModel *store = gtk_tree_view_get_model(GTK_TREE_VIEW(view->widget)); GtkTreeIter iter; if(gtk_tree_model_iter_nth_child(store, &iter, NULL, i)) { - update_list_row(GTK_LIST_STORE(store), &iter, view->model, list, elm, i); + update_list_row(view, GTK_LIST_STORE(store), &iter, list, elm, i); } } } @@ -1150,46 +1144,41 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { UiObject* current = uic_current_obj(obj); - UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - if(args->getvalue2) { - model->getvalue2 = args->getvalue2; - model->getvalue2data = args->getvalue2data; - } else if(args->getvalue) { - model->getvalue = args->getvalue; - } else { - model->getvalue = ui_strmodel_getvalue; - } + GtkWidget *combobox = gtk_combo_box_new(); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); - - GtkWidget *combobox = ui_create_combobox(obj, model, var, args->static_elements, args->static_nelm, args->onactivate, args->onactivatedata); ui_set_name_and_style(combobox, args->name, args->style_class); ui_set_widget_groups(obj->ctx, combobox, args->groups); UI_APPLY_LAYOUT2(current, args); - current->container->add(current->container, combobox, FALSE); + current->container->add(current->container, combobox); current->container->current = combobox; - return combobox; -} - -GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, ui_callback f, void *udata) { - GtkWidget *combobox = gtk_combo_box_new(); - - UiListView *uicbox = malloc(sizeof(UiListView)); - memset(uicbox, 0, sizeof(UiListView)); - uicbox->obj = obj; - uicbox->widget = combobox; + + UiListView *listview = create_listview(obj, args); + listview->widget = combobox; + listview->model = ui_model(obj->ctx, UI_STRING, "", -1); + g_signal_connect( + combobox, + "destroy", + G_CALLBACK(ui_listview_destroy), + listview); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); UiList *list = var ? var->value : NULL; - GtkListStore *listmodel = create_list_store(list, model); - - if(!list && elm && nelm > 0) { - listview_copy_static_elements(uicbox, elm, nelm); - for(int i=0;i<nelm;i++) { + GtkListStore *listmodel = create_list_store(listview, list); + if(var) { + listview->var = var; + list->update = ui_combobox_modelupdate; + list->getselection = ui_combobox_getselection; + list->setselection = ui_combobox_setselection; + list->obj = listview; + list->update(list, -1); + } else if(args->static_nelm > 0) { + listview_copy_static_elements(listview, args->static_elements, args->static_nelm); + for(int i=0;i<args->static_nelm;i++) { GtkTreeIter iter; GValue value = G_VALUE_INIT; gtk_list_store_insert(listmodel, &iter, -1); g_value_init(&value, G_TYPE_STRING); - g_value_set_string(&value, uicbox->elements[i]); + g_value_set_string(&value, listview->elements[i]); gtk_list_store_set_value(listmodel, &iter, 0, &value); } } @@ -1199,23 +1188,6 @@ g_object_unref(listmodel); } - uicbox->var = var; - uicbox->model = model; - - g_signal_connect( - combobox, - "destroy", - G_CALLBACK(ui_combobox_destroy), - uicbox); - - // bind var - if(list) { - list->update = ui_combobox_modelupdate; - list->getselection = ui_combobox_getselection; - list->setselection = ui_combobox_setselection; - list->obj = uicbox; - } - GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, TRUE); gtk_cell_layout_set_attributes( @@ -1227,13 +1199,13 @@ gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0); // add callback - if(f) { + if(args->onactivate) { UiEventData *event = ui_malloc(obj->ctx, sizeof(UiEventData)); event->obj = obj; - event->userdata = udata; - event->callback = f; + event->userdata = args->onactivatedata; + event->callback = args->onactivate; event->value = 0; - event->customdata = uicbox; + event->customdata = listview; g_signal_connect( combobox, @@ -1268,7 +1240,7 @@ void ui_combobox_modelupdate(UiList *list, int i) { UiListView *view = list->obj; - GtkListStore *store = create_list_store(view->var->value, view->model); + GtkListStore *store = create_list_store(view, list); gtk_combo_box_set_model(GTK_COMBO_BOX(view->widget), GTK_TREE_MODEL(store)); g_object_unref(store); } @@ -1709,19 +1681,6 @@ free(v); } -void ui_combobox_destroy(GtkWidget *w, UiListView *v) { - if(v->var) { - ui_destroy_boundvar(v->obj->ctx, v->var); - } - if(v->elements) { - for(int i=0;i<v->nelm;i++) { - free(v->elements[i]); - } - free(v->elements); - } - free(v); -} - /* ------------------------------ Source List ------------------------------ */ @@ -1801,16 +1760,16 @@ uisublist.listbox = uilistbox; uisublist.userdata = sublist->userdata; uisublist.index = cxListSize(sublists); - + uisublist.startpos = 0; + cxListAdd(sublists, &uisublist); + // bind UiList UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(sublists)-1); - UiList *list = uisublist.var->value; - if(list) { + if(uisublist.var && uisublist.var->value) { + UiList *list = uisublist.var->value; list->obj = sublist_ptr; list->update = ui_listbox_list_update; } - - cxListAdd(sublists, &uisublist); } UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { @@ -1883,6 +1842,11 @@ g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox); g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox); + if(args->contextmenu) { + UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, listbox); + ui_widget_set_contextmenu(listbox, menu); + } + // signals g_signal_connect( listbox, @@ -1952,11 +1916,37 @@ } // reload sublist + sublist->startpos = pos; ui_listbox_update_sublist(listbox, sublist, pos); pos += sublist->numitems; } } +static void listbox_button_clicked(GtkWidget *widget, UiEventDataExt *data) { + UiListBoxSubList *sublist = data->customdata0; + + UiSubListEventData eventdata; + eventdata.list = sublist->var->value; + eventdata.sublist_index = sublist->index; + eventdata.row_index = data->value0; + eventdata.sublist_userdata = sublist->userdata; + eventdata.row_data = eventdata.list->get(eventdata.list, eventdata.row_index); + eventdata.event_data = data->customdata2; + + UiEvent event; + event.obj = data->obj; + event.window = event.obj->window; + event.document = event.obj->ctx->document; + event.eventdata = &eventdata; + event.eventdatatype = UI_EVENT_DATA_SUBLIST; + event.intval = data->value0; + event.set = ui_get_setop(); + + if(data->callback2) { + data->callback2(&event, data->userdata2); + } +} + static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) { GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); if(item->icon) { @@ -1966,7 +1956,9 @@ GtkWidget *label = gtk_label_new(item->label); gtk_widget_set_halign(label, GTK_ALIGN_START); BOX_ADD_EXPAND(hbox, label); - // TODO: badge, button + if(item->badge) { + + } GtkWidget *row = gtk_list_box_row_new(); LISTBOX_ROW_SET_CHILD(row, hbox); @@ -1991,6 +1983,34 @@ g_object_set_data(G_OBJECT(row), "ui-listbox-row-eventdata", event); + // badge + if(item->badge) { + GtkWidget *badge = gtk_label_new(item->badge); + WIDGET_ADD_CSS_CLASS(badge, "ui-badge"); +#if GTK_CHECK_VERSION(4, 0, 0) + gtk_widget_set_valign(badge, GTK_ALIGN_CENTER); + BOX_ADD(hbox, badge); +#else + GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0); + gtk_container_add(GTK_CONTAINER(align), badge); + BOX_ADD(hbox, align); +#endif + } + // button + if(item->button_icon || item->button_label) { + GtkWidget *button = gtk_button_new(); + gtk_button_set_label(GTK_BUTTON(button), item->button_label); + ui_button_set_icon_name(button, item->button_icon); + WIDGET_ADD_CSS_CLASS(button, "flat"); + BOX_ADD(hbox, button); + g_signal_connect( + button, + "clicked", + G_CALLBACK(listbox_button_clicked), + event + ); + } + return row; } @@ -2005,6 +2025,9 @@ sublist->numitems = 0; // create items for each UiList element + if(!sublist->var) { + return; + } UiList *list = sublist->var->value; if(!list) { return; @@ -2012,6 +2035,20 @@ size_t index = 0; void *elm = list->first(list); + + if(!elm && sublist->header) { + // empty row for header + GtkWidget *row = gtk_list_box_row_new(); + cxListAdd(sublist->widgets, row); + g_object_set_data(G_OBJECT(row), "ui_listbox", listbox); + g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist); + intptr_t rowindex = listbox_insert_index + index; + g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex); + gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index); + sublist->numitems = 1; + return; + } + while(elm) { UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; if(listbox->getvalue) { @@ -2055,6 +2092,14 @@ void ui_listbox_list_update(UiList *list, int i) { UiListBoxSubList *sublist = list->obj; + ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos); + size_t pos = 0; + CxIterator it = cxListIterator(sublist->listbox->sublists); + cx_foreach(UiListBoxSubList *, ls, it) { + ls->startpos = pos; + pos += sublist->numitems; + } + } void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {