--- a/ui/gtk/list.c Sat Apr 05 17:57:04 2025 +0200 +++ b/ui/gtk/list.c Sun Jul 20 22:04:39 2025 +0200 @@ -48,6 +48,15 @@ return column == 0 ? elm : NULL; } +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); + } + return NULL; +} + /* static GtkTargetEntry targetentries[] = { @@ -73,6 +82,7 @@ typedef struct _ObjWrapper { GObject parent_instance; void *data; + int i; } ObjWrapper; typedef struct _ObjWrapperClass { @@ -89,9 +99,10 @@ self->data = NULL; } -ObjWrapper* obj_wrapper_new(void* data) { +ObjWrapper* obj_wrapper_new(void* data, int i) { ObjWrapper *obj = g_object_new(obj_wrapper_get_type(), NULL); obj->data = data; + obj->i = i; return obj; } @@ -122,20 +133,21 @@ static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { UiColData *col = userdata; + UiList *list = col->listview->var ? col->listview->var->value : NULL; ObjWrapper *obj = gtk_list_item_get_item(item); UiModel *model = col->listview->model; UiModelType type = model->types[col->model_column]; - void *data = model->getvalue(obj->data, col->data_column); + UiBool freevalue = FALSE; + void *data = model_getvalue(model, list, obj->data, obj->i, col->data_column, &freevalue); GtkWidget *child = gtk_list_item_get_child(item); - bool freevalue = TRUE; switch(type) { + case UI_STRING_FREE: { + freevalue = TRUE; + } case UI_STRING: { - freevalue = FALSE; - } - case UI_STRING_FREE: { gtk_label_set_label(GTK_LABEL(child), data); if(freevalue) { free(data); @@ -157,10 +169,13 @@ break; } case UI_ICON_TEXT: { - freevalue = FALSE; + } case UI_ICON_TEXT_FREE: { - void *data2 = model->getvalue(obj->data, col->data_column+1); + void *data2 = model_getvalue(model, list, obj->data, obj->i, col->data_column+1, &freevalue); + if(type == UI_ICON_TEXT_FREE) { + freevalue = TRUE; + } GtkWidget *image = g_object_get_data(G_OBJECT(child), "image"); GtkWidget *label = g_object_get_data(G_OBJECT(child), "label"); if(data && image) { @@ -189,34 +204,41 @@ return selection_model; } -static UiListView* create_listview(UiObject *obj, UiListArgs args) { +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.ondropsdata; + 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) { +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); - model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue; - args.model = model; + 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); @@ -230,10 +252,10 @@ g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns); g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns); - GtkSelectionModel *selection_model = create_selection_model(listview, ls, args.multiselection); + GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection); GtkWidget *view = gtk_list_view_new(GTK_SELECTION_MODEL(selection_model), factory); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); // init listview listview->widget = view; @@ -256,19 +278,23 @@ list->setselection = ui_listview_setselection2; 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); + } 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 ui_update_liststore_static(ls, listview->elements, listview->nelm); } // event handling - if(args.onactivate) { + if(args->onactivate) { // columnview and listview can use the same callback function, because // the first parameter (which is technically a different pointer type) // is ignored g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), listview); } + if(args->contextmenu) { + UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, view); + ui_widget_set_contextmenu(view, menu); + } // add widget to parent GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); @@ -278,8 +304,8 @@ GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS SCROLLEDWINDOW_SET_CHILD(scroll_area, view); - UI_APPLY_LAYOUT1(current, args); - current->container->add(current->container, scroll_area, FALSE); + UI_APPLY_LAYOUT2(current, args); + 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 @@ -288,14 +314,21 @@ return scroll_area; } -UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) { +UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { UiObject* current = uic_current_obj(obj); // 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); - model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue; - args.model = model; + 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); @@ -312,7 +345,7 @@ GtkWidget *view = gtk_drop_down_new(G_LIST_MODEL(ls), NULL); gtk_drop_down_set_factory(GTK_DROP_DOWN(view), factory); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); // init listview listview->widget = view; @@ -335,20 +368,20 @@ list->setselection = ui_combobox_setselection; 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); + } 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 ui_update_liststore_static(ls, listview->elements, listview->nelm); } // event handling - if(args.onactivate) { + if(args->onactivate) { g_signal_connect(view, "notify::selected", G_CALLBACK(ui_dropdown_notify), listview); } // add widget to parent - UI_APPLY_LAYOUT1(current, args); - current->container->add(current->container, view, FALSE); + UI_APPLY_LAYOUT2(current, args); + current->container->add(current->container, view); return view; } @@ -361,7 +394,7 @@ gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), index); } -UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { +UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { UiObject* current = uic_current_obj(obj); GListStore *ls = g_list_store_new(G_TYPE_OBJECT); @@ -371,10 +404,10 @@ // and list updates UiListView *tableview = create_listview(obj, args); - GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args.multiselection); + GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args->multiselection); GtkWidget *view = gtk_column_view_new(GTK_SELECTION_MODEL(selection_model)); - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); // init tableview tableview->widget = view; @@ -389,7 +422,7 @@ // create columns from UiModel - UiModel *model = args.model; + UiModel *model = args->model; int columns = model ? model->columns : 0; tableview->columns = calloc(columns, sizeof(UiColData)); @@ -435,9 +468,13 @@ } // event handling - if(args.onactivate) { + if(args->onactivate) { g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), tableview); } + if(args->contextmenu) { + UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, view); + ui_widget_set_contextmenu(view, menu); + } // add widget to parent GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); @@ -447,8 +484,8 @@ GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS SCROLLEDWINDOW_SET_CHILD(scroll_area, view); - UI_APPLY_LAYOUT1(current, args); - current->container->add(current->container, scroll_area, FALSE); + UI_APPLY_LAYOUT2(current, args); + 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 @@ -474,6 +511,7 @@ event.window = event.obj->window; event.intval = view->selection.count; event.eventdata = &view->selection; + event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION; event.set = ui_get_setop(); if(cb) { cb(&event, cbdata); @@ -517,6 +555,7 @@ event.window = event.obj->window; event.intval = index; event.eventdata = eventdata->data; + event.eventdatatype = UI_EVENT_DATA_LIST_ELM; event.set = ui_get_setop(); view->onactivate(&event, view->onactivatedata); } @@ -554,6 +593,7 @@ event.window = event.obj->window; event.intval = view->selection.count; event.eventdata = &view->selection; + event.eventdatatype = UI_EVENT_DATA_LIST_SELECTION; event.set = ui_get_setop(); view->onactivate(&event, view->onactivatedata); } @@ -561,9 +601,10 @@ void ui_update_liststore(GListStore *liststore, UiList *list) { g_list_store_remove_all(liststore); + int i = 0; void *elm = list->first(list); while(elm) { - ObjWrapper *obj = obj_wrapper_new(elm); + ObjWrapper *obj = obj_wrapper_new(elm, i++); g_list_store_append(liststore, obj); elm = list->next(list); } @@ -572,7 +613,7 @@ void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm) { g_list_store_remove_all(liststore); for(int i=0;i<nelm;i++) { - ObjWrapper *obj = obj_wrapper_new(elm[i]); + ObjWrapper *obj = obj_wrapper_new(elm[i], i); g_list_store_append(liststore, obj); } } @@ -584,10 +625,15 @@ } else { void *value = list->get(list, i); if(value) { - ObjWrapper *obj = obj_wrapper_new(value); + ObjWrapper *obj = obj_wrapper_new(value, i); // TODO: if index i is selected, the selection is lost // is it possible to update the item without removing it? - g_list_store_splice(view->liststore, i, 1, (void **)&obj, 1); + 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); + } } } } @@ -649,19 +695,22 @@ #else -static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *model, void *elm) { +static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *model, UiList *list, void *elm, int row) { // set column values int c = 0; for(int i=0;i<model->columns;i++,c++) { - void *data = model->getvalue(elm, c); + UiBool freevalue = FALSE; + void *data = model_getvalue(model, list, elm, row, c, &freevalue); GValue value = G_VALUE_INIT; switch(model->types[i]) { - case UI_STRING: case UI_STRING_FREE: { + freevalue = TRUE; + } + case UI_STRING: { g_value_init(&value, G_TYPE_STRING); g_value_set_string(&value, data); - if(model->types[i] == UI_STRING_FREE) { + if(freevalue) { free(data); } break; @@ -714,11 +763,12 @@ } #endif c++; - - char *str = model->getvalue(elm, c); + + freevalue = FALSE; + char *str = model_getvalue(model, list, elm, row, c, &freevalue); g_value_init(&value, G_TYPE_STRING); g_value_set_string(&value, str); - if(model->types[i] == UI_ICON_TEXT_FREE) { + if(model->types[i] == UI_ICON_TEXT_FREE || freevalue) { free(str); } break; @@ -751,12 +801,13 @@ if(list) { void *elm = list->first(list); + int i = 0; while(elm) { // insert new row GtkTreeIter iter; gtk_list_store_insert (store, &iter, -1); - update_list_row(store, &iter, model, elm); + update_list_row(store, &iter, model, list, elm, i++); // next row elm = list->next(list); @@ -767,13 +818,13 @@ } -UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) { +UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { UiObject* current = uic_current_obj(obj); // create treeview GtkWidget *view = gtk_tree_view_new(); - ui_set_name_and_style(view, args.name, args.style_class); - ui_set_widget_groups(obj->ctx, view, args.groups); + ui_set_name_and_style(view, args->name, args->style_class); + ui_set_widget_groups(obj->ctx, view, args->groups); GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(NULL, renderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(view), column); @@ -790,9 +841,16 @@ #endif UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue; + 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; + } - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + 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); @@ -822,24 +880,24 @@ // add callback UiTreeEventData *event = malloc(sizeof(UiTreeEventData)); event->obj = obj; - event->activate = args.onactivate; - event->activatedata = args.onactivatedata; - event->selection = args.onselection; - event->selectiondata = args.onselectiondata; + event->activate = args->onactivate; + event->activatedata = args->onactivatedata; + event->selection = args->onselection; + event->selectiondata = args->onselectiondata; g_signal_connect( view, "destroy", G_CALLBACK(ui_destroy_userdata), event); - if(args.onactivate) { + if(args->onactivate) { g_signal_connect( view, "row-activated", G_CALLBACK(ui_listview_activate_event), event); } - if(args.onselection) { + if(args->onselection) { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(view)); g_signal_connect( @@ -848,8 +906,8 @@ G_CALLBACK(ui_listview_selection_event), event); } - if(args.contextmenu) { - UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, view); + if(args->contextmenu) { + UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, view); ui_widget_set_contextmenu(view, menu); } @@ -862,7 +920,7 @@ GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS SCROLLEDWINDOW_SET_CHILD(scroll_area, view); - UI_APPLY_LAYOUT1(current, args); + UI_APPLY_LAYOUT2(current, args); current->container->add(current->container, scroll_area, FALSE); // ct->current should point to view, not scroll_area, to make it possible @@ -883,13 +941,13 @@ gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index); } -UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { +UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) { UiObject* current = uic_current_obj(obj); // create treeview GtkWidget *view = gtk_tree_view_new(); - UiModel *model = args.model; + UiModel *model = args->model; int columns = model ? model->columns : 0; int addi = 0; @@ -946,7 +1004,7 @@ #endif - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + 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); @@ -964,12 +1022,12 @@ 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.ondropsdata; + 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; g_signal_connect( @@ -987,18 +1045,18 @@ // add callback UiTreeEventData *event = ui_malloc(obj->ctx, sizeof(UiTreeEventData)); event->obj = obj; - event->activate = args.onactivate; - event->selection = args.onselection; - event->activatedata = args.onactivatedata; - event->selectiondata = args.onselectiondata; - if(args.onactivate) { + event->activate = args->onactivate; + event->selection = args->onselection; + event->activatedata = args->onactivatedata; + event->selectiondata = args->onselectiondata; + if(args->onactivate) { g_signal_connect( view, "row-activated", G_CALLBACK(ui_listview_activate_event), event); } - if(args.onselection) { + if(args->onselection) { GtkTreeSelection *selection = gtk_tree_view_get_selection( GTK_TREE_VIEW(view)); g_signal_connect( @@ -1010,15 +1068,15 @@ // TODO: destroy callback - if(args.ondragstart) { - ui_listview_add_dnd(tableview, &args); + if(args->ondragstart) { + ui_listview_add_dnd(tableview, args); } - if(args.ondrop) { - ui_listview_enable_drop(tableview, &args); + if(args->ondrop) { + ui_listview_enable_drop(tableview, args); } GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(view)); - if(args.multiselection) { + if(args->multiselection) { gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE); } @@ -1030,8 +1088,8 @@ GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS SCROLLEDWINDOW_SET_CHILD(scroll_area, view); - if(args.contextmenu) { - UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, scroll_area); + if(args->contextmenu) { + UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, scroll_area); #if GTK_MAJOR_VERSION >= 4 ui_widget_set_contextmenu(scroll_area, menu); #else @@ -1039,7 +1097,7 @@ #endif } - UI_APPLY_LAYOUT1(current, args); + UI_APPLY_LAYOUT2(current, args); current->container->add(current->container, scroll_area, FALSE); // ct->current should point to view, not scroll_area, to make it possible @@ -1062,7 +1120,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, elm); + update_list_row(GTK_LIST_STORE(store), &iter, view->model, list, elm, i); } } } @@ -1089,18 +1147,25 @@ /* --------------------------- ComboBox --------------------------- */ -UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) { +UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { UiObject* current = uic_current_obj(obj); UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); - model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue; - - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST); + 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 = 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_LAYOUT1(current, args); + 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->current = combobox; return combobox; @@ -1323,6 +1388,7 @@ event.window = event.obj->window; event.document = event.obj->ctx->document; event.eventdata = dnd; + event.eventdatatype = UI_EVENT_DATA_DND; event.intval = 0; event.set = ui_get_setop(); listview->ondragstart(&event, listview->ondragstartdata); @@ -1359,6 +1425,7 @@ event.window = event.obj->window; event.document = event.obj->ctx->document; event.eventdata = &dnd; + event.eventdatatype = UI_EVENT_DATA_DND; event.intval = 0; event.set = ui_get_setop(); listview->ondragcomplete(&event, listview->ondragcompletedata); @@ -1388,6 +1455,7 @@ event.window = event.obj->window; event.document = event.obj->ctx->document; event.eventdata = &dnd; + event.eventdatatype = UI_EVENT_DATA_DND; event.intval = 0; event.set = ui_get_setop(); listview->ondrop(&event, listview->ondropdata); @@ -1745,7 +1813,7 @@ cxListAdd(sublists, &uisublist); } -UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) { +UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { UiObject* current = uic_current_obj(obj); #ifdef UI_GTK3 @@ -1753,7 +1821,7 @@ #else GtkWidget *listbox = gtk_list_box_new(); #endif - if(!args.style_class) { + if(!args->style_class) { #if GTK_MAJOR_VERSION >= 4 WIDGET_ADD_CSS_CLASS(listbox, "navigation-sidebar"); #else @@ -1764,31 +1832,32 @@ GtkWidget *scroll_area = SCROLLEDWINDOW_NEW(); SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox); - ui_set_name_and_style(listbox, args.name, args.style_class); - ui_set_widget_groups(obj->ctx, listbox, args.groups); - UI_APPLY_LAYOUT1(current, args); - current->container->add(current->container, scroll_area, TRUE); + ui_set_name_and_style(listbox, args->name, args->style_class); + ui_set_widget_groups(obj->ctx, listbox, args->groups); + UI_APPLY_LAYOUT2(current, args); + current->container->add(current->container, scroll_area); UiListBox *uilistbox = malloc(sizeof(UiListBox)); uilistbox->obj = obj; uilistbox->listbox = GTK_LIST_BOX(listbox); - uilistbox->getvalue = args.getvalue; - uilistbox->onactivate = args.onactivate; - uilistbox->onactivatedata = args.onactivatedata; - uilistbox->onbuttonclick = args.onbuttonclick; - uilistbox->onbuttonclickdata = args.onbuttonclickdata; + uilistbox->getvalue = args->getvalue; + uilistbox->getvaluedata = args->getvaluedata; + uilistbox->onactivate = args->onactivate; + uilistbox->onactivatedata = args->onactivatedata; + uilistbox->onbuttonclick = args->onbuttonclick; + uilistbox->onbuttonclickdata = args->onbuttonclickdata; uilistbox->sublists = cxArrayListCreateSimple(sizeof(UiListBoxSubList), 4); uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy; uilistbox->sublists->collection.destructor_data = obj; uilistbox->first_row = NULL; - if(args.sublists) { + if(args->sublists) { // static sublist initalization - if(args.numsublists == 0 && args.sublists) { - args.numsublists = INT_MAX; + if(args->numsublists == 0 && args->sublists) { + args->numsublists = INT_MAX; } - for(int i=0;i<args.numsublists;i++) { - UiSubList sublist = args.sublists[i]; + for(int i=0;i<args->numsublists;i++) { + UiSubList sublist = args->sublists[i]; if(!sublist.varname && !sublist.value) { break; } @@ -1799,13 +1868,13 @@ // fill items ui_listbox_update(uilistbox, 0, cxListSize(uilistbox->sublists)); } else { - UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.dynamic_sublist, args.varname, UI_VAR_LIST); + UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->dynamic_sublist, args->varname, UI_VAR_LIST); if(var) { UiList *list = var->value; list->obj = uilistbox; list->update = ui_listbox_dynamic_update; - ui_listbox_dynamic_update(list, 0); + ui_listbox_dynamic_update(list, -1); } } @@ -1821,7 +1890,7 @@ G_CALLBACK(ui_destroy_sourcelist), uilistbox); - if(args.onactivate) { + if(args->onactivate) { g_signal_connect( listbox, "row-activated", @@ -1838,6 +1907,15 @@ // unbind/free previous list vars CxIterator i = cxListIterator(uilistbox->sublists); cx_foreach(UiListBoxSubList *, s, i) { + // TODO: "unbind/free previous list vars" will also remove + // the widget list. This makes the widget optimization + // in ui_listbox_update_sublist pointless + // Is it actually possible to not recreate the whole list? + CxIterator r = cxListIterator(s->widgets); + cx_foreach(GtkWidget*, widget, r) { + LISTBOX_REMOVE(uilistbox->listbox, widget); + } + if(s->var) { UiList *sl = s->var->value; sl->obj = NULL; @@ -1936,7 +2014,11 @@ void *elm = list->first(list); while(elm) { UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; - listbox->getvalue(sublist->userdata, elm, index, &item); + if(listbox->getvalue) { + listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata); + } else { + item.label = strdup(elm); + } // create listbox item GtkWidget *row = create_listbox_row(listbox, sublist, &item, (int)index); @@ -1987,7 +2069,7 @@ eventdata.sublist_index = sublist->index; eventdata.row_index = data->value0; eventdata.sublist_userdata = sublist->userdata; - eventdata.row_data = ui_list_get(eventdata.list, eventdata.row_index); + eventdata.row_data = eventdata.list->get(eventdata.list, eventdata.row_index); eventdata.event_data = data->customdata2; UiEvent event; @@ -1995,6 +2077,7 @@ 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();