diff -r 64ded9f6a6c6 -r 6606616eca9f ui/gtk/list.c --- a/ui/gtk/list.c Tue Feb 25 21:11:00 2025 +0100 +++ b/ui/gtk/list.c Sat Apr 05 16:46:11 2025 +0200 @@ -57,6 +57,14 @@ }; */ +static void listview_copy_static_elements(UiListView *listview, char **elm, size_t nelm) { + listview->elements = calloc(nelm, sizeof(char*)); + listview->nelm = nelm; + for(int i=0;ielements[i] = strdup(elm[i]); + } +} + #if GTK_CHECK_VERSION(4, 10, 0) @@ -248,6 +256,10 @@ 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); + listview->model->getvalue = ui_strmodel_getvalue; // force strmodel + ui_update_liststore_static(ls, listview->elements, listview->nelm); } // event handling @@ -323,11 +335,15 @@ 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); + listview->model->getvalue = ui_strmodel_getvalue; // force strmodel + ui_update_liststore_static(ls, listview->elements, listview->nelm); } // event handling if(args.onactivate) { - g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), listview); + g_signal_connect(view, "notify::selected", G_CALLBACK(ui_dropdown_notify), listview); } // add widget to parent @@ -336,6 +352,15 @@ return view; } +void ui_listview_select(UIWIDGET listview, int index) { + GtkSelectionModel *model = gtk_list_view_get_model(GTK_LIST_VIEW(listview)); + gtk_selection_model_select_item(model, index, TRUE); +} + +void ui_combobox_select(UIWIDGET dropdown, int index) { + gtk_drop_down_set_selected(GTK_DROP_DOWN(dropdown), index); +} + UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { UiObject* current = uic_current_obj(obj); @@ -449,6 +474,7 @@ event.window = event.obj->window; event.intval = view->selection.count; event.eventdata = &view->selection; + event.set = ui_get_setop(); if(cb) { cb(&event, cbdata); } @@ -478,6 +504,24 @@ free(newselection); } } + +void ui_dropdown_notify(GtkWidget *dropdown, GObject *pspec, gpointer userdata) { + UiListView *view = userdata; + guint index = gtk_drop_down_get_selected(GTK_DROP_DOWN(dropdown)); + GObject *item = gtk_drop_down_get_selected_item(GTK_DROP_DOWN(dropdown)); + if(item && view->onactivate) { + ObjWrapper *eventdata = (ObjWrapper*)item; + UiEvent event; + event.obj = view->obj; + event.document = event.obj->ctx->document; + event.window = event.obj->window; + event.intval = index; + event.eventdata = eventdata->data; + event.set = ui_get_setop(); + view->onactivate(&event, view->onactivatedata); + } +} + void ui_columnview_activate(void *ignore, guint position, gpointer userdata) { UiListView *view = userdata; @@ -510,6 +554,7 @@ event.window = event.obj->window; event.intval = view->selection.count; event.eventdata = &view->selection; + event.set = ui_get_setop(); view->onactivate(&event, view->onactivatedata); } } @@ -524,9 +569,27 @@ } } +void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm) { + g_list_store_remove_all(liststore); + for(int i=0;iobj; - ui_update_liststore(view->liststore, list); + if(i < 0) { + ui_update_liststore(view->liststore, list); + } else { + void *value = list->get(list, i); + if(value) { + ObjWrapper *obj = obj_wrapper_new(value); + // 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); + } + } } UiListSelection ui_listview_getselection2(UiList *list) { @@ -539,6 +602,7 @@ } void ui_listview_setselection2(UiList *list, UiListSelection selection) { + ui_setop_enable(TRUE); UiListView *view = list->obj; UiListSelection newselection; newselection.count = view->selection.count; @@ -557,6 +621,7 @@ gtk_selection_model_select_item(view->selectionmodel, selection.rows[i], FALSE); } } + ui_setop_enable(FALSE); } UiListSelection ui_combobox_getselection(UiList *list) { @@ -572,16 +637,98 @@ } void ui_combobox_setselection(UiList *list, UiListSelection selection) { + ui_setop_enable(TRUE); UiListView *view = list->obj; if(selection.count > 0) { gtk_drop_down_set_selected(GTK_DROP_DOWN(view->widget), selection.rows[0]); } else { gtk_drop_down_set_selected(GTK_DROP_DOWN(view->widget), GTK_INVALID_LIST_POSITION); } + ui_setop_enable(FALSE); } #else +static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *model, void *elm) { + // set column values + int c = 0; + for(int i=0;icolumns;i++,c++) { + void *data = model->getvalue(elm, c); + + GValue value = G_VALUE_INIT; + switch(model->types[i]) { + case UI_STRING: + case UI_STRING_FREE: { + g_value_init(&value, G_TYPE_STRING); + g_value_set_string(&value, data); + if(model->types[i] == UI_STRING_FREE) { + free(data); + } + break; + } + case UI_INTEGER: { + g_value_init(&value, G_TYPE_INT); + intptr_t intptr = (intptr_t)data; + g_value_set_int(&value, (int)intptr); + break; + } + case UI_ICON: { + g_value_init(&value, G_TYPE_OBJECT); + UiIcon *icon = data; +#if GTK_MAJOR_VERSION >= 4 + g_value_set_object(&value, icon->info); // TODO: does this work? +#else + if(!icon->pixbuf && icon->info) { + GError *error = NULL; + GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); + icon->pixbuf = pixbuf; + } + + if(icon->pixbuf) { + g_value_set_object(&value, icon->pixbuf); + } +#endif + break; + } + case UI_ICON_TEXT: + case UI_ICON_TEXT_FREE: { + UiIcon *icon = data; +#if GTK_MAJOR_VERSION >= 4 + if(icon) { + GValue iconvalue = G_VALUE_INIT; + g_value_init(&iconvalue, G_TYPE_OBJECT); + g_value_set_object(&iconvalue, ui_icon_pixbuf(icon)); + gtk_list_store_set_value(store, &iter, c, &iconvalue); + } +#else + GValue pixbufvalue = G_VALUE_INIT; + if(icon) { + if(!icon->pixbuf && icon->info) { + GError *error = NULL; + GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); + icon->pixbuf = pixbuf; + } + g_value_init(&pixbufvalue, G_TYPE_OBJECT); + g_value_set_object(&pixbufvalue, icon->pixbuf); + gtk_list_store_set_value(store, iter, c, &pixbufvalue); + } +#endif + c++; + + char *str = model->getvalue(elm, c); + g_value_init(&value, G_TYPE_STRING); + g_value_set_string(&value, str); + if(model->types[i] == UI_ICON_TEXT_FREE) { + free(str); + } + break; + } + } + + gtk_list_store_set_value(store, iter, c, &value); + } +} + static GtkListStore* create_list_store(UiList *list, UiModel *model) { int columns = model->columns; GType types[2*columns]; @@ -609,83 +756,7 @@ GtkTreeIter iter; gtk_list_store_insert (store, &iter, -1); - // set column values - int c = 0; - for(int i=0;igetvalue(elm, c); - - GValue value = G_VALUE_INIT; - switch(model->types[i]) { - case UI_STRING: - case UI_STRING_FREE: { - g_value_init(&value, G_TYPE_STRING); - g_value_set_string(&value, data); - if(model->types[i] == UI_STRING_FREE) { - free(data); - } - break; - } - case UI_INTEGER: { - g_value_init(&value, G_TYPE_INT); - intptr_t intptr = (intptr_t)data; - g_value_set_int(&value, (int)intptr); - break; - } - case UI_ICON: { - g_value_init(&value, G_TYPE_OBJECT); - UiIcon *icon = data; -#if GTK_MAJOR_VERSION >= 4 - g_value_set_object(&value, icon->info); // TODO: does this work? -#else - if(!icon->pixbuf && icon->info) { - GError *error = NULL; - GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); - icon->pixbuf = pixbuf; - } - - if(icon->pixbuf) { - g_value_set_object(&value, icon->pixbuf); - } -#endif - break; - } - case UI_ICON_TEXT: - case UI_ICON_TEXT_FREE: { - UiIcon *icon = data; -#if GTK_MAJOR_VERSION >= 4 - if(icon) { - GValue iconvalue = G_VALUE_INIT; - g_value_init(&iconvalue, G_TYPE_OBJECT); - g_value_set_object(&iconvalue, ui_icon_pixbuf(icon)); - gtk_list_store_set_value(store, &iter, c, &iconvalue); - } -#else - GValue pixbufvalue = G_VALUE_INIT; - if(icon) { - if(!icon->pixbuf && icon->info) { - GError *error = NULL; - GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error); - icon->pixbuf = pixbuf; - } - g_value_init(&pixbufvalue, G_TYPE_OBJECT); - g_value_set_object(&pixbufvalue, icon->pixbuf); - gtk_list_store_set_value(store, &iter, c, &pixbufvalue); - } -#endif - c++; - - char *str = model->getvalue(elm, c); - g_value_init(&value, G_TYPE_STRING); - g_value_set_string(&value, str); - if(model->types[i] == UI_ICON_TEXT_FREE) { - free(str); - } - break; - } - } - - gtk_list_store_set_value(store, &iter, c, &value); - } + update_list_row(store, &iter, model, elm); // next row elm = list->next(list); @@ -729,6 +800,7 @@ g_object_unref(listmodel); UiListView *listview = malloc(sizeof(UiListView)); + memset(listview, 0, sizeof(UiListView)); listview->obj = obj; listview->widget = view; listview->var = var; @@ -800,6 +872,17 @@ return scroll_area; } +void ui_listview_select(UIWIDGET listview, int index) { + GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(listview)); + GtkTreePath *path = gtk_tree_path_new_from_indicesv(&index, 1); + gtk_tree_selection_select_path(sel, path); + //g_object_unref(path); +} + +void ui_combobox_select(UIWIDGET dropdown, int index) { + gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), index); +} + UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { UiObject* current = uic_current_obj(obj); @@ -876,6 +959,7 @@ // 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; @@ -969,9 +1053,18 @@ void ui_listview_update(UiList *list, int i) { UiListView *view = list->obj; - GtkListStore *store = create_list_store(list, view->model); - gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store)); - g_object_unref(G_OBJECT(store)); + if(i < 0) { + GtkListStore *store = create_list_store(list, view->model); + gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store)); + g_object_unref(G_OBJECT(store)); + } else { + void *elm = list->get(list, i); + 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); + } + } } UiListSelection ui_listview_getselection(UiList *list) { @@ -983,11 +1076,13 @@ } void ui_listview_setselection(UiList *list, UiListSelection selection) { + ui_setop_enable(TRUE); UiListView *view = list->obj; GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view->widget)); GtkTreePath *path = gtk_tree_path_new_from_indicesv(selection.rows, selection.count); gtk_tree_selection_select_path(sel, path); //g_object_unref(path); + ui_setop_enable(FALSE); } @@ -1002,7 +1097,7 @@ 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.onactivate, args.onactivatedata); + 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); @@ -1011,16 +1106,29 @@ return combobox; } -GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f, void *udata) { +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; 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;ielements[i]); + gtk_list_store_set_value(listmodel, &iter, 0, &value); + } + } + if(listmodel) { gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(listmodel)); g_object_unref(listmodel); @@ -1060,7 +1168,7 @@ event->userdata = udata; event->callback = f; event->value = 0; - event->customdata = NULL; + event->customdata = uicbox; g_signal_connect( combobox, @@ -1073,12 +1181,23 @@ } void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) { + int index = gtk_combo_box_get_active(widget); + UiListView *listview = e->customdata; + void *eventdata = NULL; + if(listview->var && listview->var->value) { + UiList *list = listview->var->value; + eventdata = ui_list_get(list, index); + } else if(listview->elements && listview->nelm > index) { + eventdata = listview->elements[index]; + } + UiEvent event; event.obj = e->obj; event.window = event.obj->window; event.document = event.obj->ctx->document; - event.eventdata = NULL; - event.intval = gtk_combo_box_get_active(widget); + event.eventdata = eventdata; + event.intval = index; + event.set = ui_get_setop(); e->callback(&event, e->userdata); } @@ -1099,10 +1218,12 @@ } void ui_combobox_setselection(UiList *list, UiListSelection selection) { + ui_setop_enable(TRUE); UiListView *combobox = list->obj; if(selection.count > 0) { gtk_combo_box_set_active(GTK_COMBO_BOX(combobox->widget), selection.rows[0]); } + ui_setop_enable(FALSE); } @@ -1124,6 +1245,7 @@ e.document = event->obj->ctx->document; e.eventdata = &selection; e.intval = selection.count > 0 ? selection.rows[0] : -1; + e.set = ui_get_setop(); event->activate(&e, event->activatedata); if(selection.count > 0) { @@ -1143,6 +1265,7 @@ e.document = event->obj->ctx->document; e.eventdata = &selection; e.intval = selection.count > 0 ? selection.rows[0] : -1; + e.set = ui_get_setop(); event->selection(&e, event->selectiondata); if(selection.count > 0) { @@ -1201,6 +1324,7 @@ event.document = event.obj->ctx->document; event.eventdata = dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondragstart(&event, listview->ondragstartdata); } @@ -1236,6 +1360,7 @@ event.document = event.obj->ctx->document; event.eventdata = &dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondragcomplete(&event, listview->ondragcompletedata); } } @@ -1264,6 +1389,7 @@ event.document = event.obj->ctx->document; event.eventdata = &dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondrop(&event, listview->ondropdata); } @@ -1326,6 +1452,7 @@ event.document = event.obj->ctx->document; event.eventdata = &dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondragstart(&event, listview->ondragstartdata); } } @@ -1350,6 +1477,7 @@ event.document = event.obj->ctx->document; event.eventdata = &dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondragcomplete(&event, listview->ondragcompletedata); } } @@ -1395,6 +1523,7 @@ event.document = event.obj->ctx->document; event.eventdata = &dnd; event.intval = 0; + event.set = ui_get_setop(); listview->ondrop(&event, listview->ondropdata); } } @@ -1496,7 +1625,15 @@ void ui_listview_destroy(GtkWidget *w, UiListView *v) { //gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL); - ui_destroy_boundvar(v->obj->ctx, v->var); + if(v->var) { + ui_destroy_boundvar(v->obj->ctx, v->var); + } + if(v->elements) { + for(int i=0;inelm;i++) { + free(v->elements[i]); + } + free(v->elements); + } #if GTK_CHECK_VERSION(4, 10, 0) free(v->columns); #endif @@ -1505,7 +1642,15 @@ } void ui_combobox_destroy(GtkWidget *w, UiListView *v) { - ui_destroy_boundvar(v->obj->ctx, v->var); + if(v->var) { + ui_destroy_boundvar(v->obj->ctx, v->var); + } + if(v->elements) { + for(int i=0;inelm;i++) { + free(v->elements[i]); + } + free(v->elements); + } free(v); } @@ -1851,6 +1996,7 @@ event.document = event.obj->ctx->document; event.eventdata = &eventdata; event.intval = data->value0; + event.set = ui_get_setop(); if(data->callback) { data->callback(&event, data->userdata);