--- a/ui/gtk/list.c Tue Sep 08 16:08:59 2026 +0200 +++ b/ui/gtk/list.c Wed Sep 09 18:57:52 2026 +0200 @@ -55,7 +55,7 @@ return elm; } -static void* null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { +void* ui_null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) { return NULL; } @@ -117,23 +117,61 @@ tableview->getvalue = getvalue_wrapper; tableview->getvaluedata = (void*)args->getvalue; } else { - tableview->getvalue = null_getvalue; + tableview->getvalue = ui_null_getvalue; } return tableview; } +static UiListView* create_treeview(UiObject *obj, UiTreeArgs *args) { + UiListView *treeview = malloc(sizeof(UiListView)); + memset(treeview, 0, sizeof(UiListView)); + treeview->obj = obj; + treeview->model = args->model; + treeview->multiselection = args->multiselection; + treeview->onactivate = args->onactivate; + treeview->onactivatedata = args->onactivatedata; + treeview->onactivate_action = args->onactivate_action ? strdup(args->onactivate_action) : NULL; + treeview->onselection = args->onselection; + treeview->onselectiondata = args->onselectiondata; + treeview->onselection_action = args->onselection_action ? strdup(args->onselection_action) : NULL; + treeview->ondragstart = args->ondragstart; + treeview->ondragstartdata = args->ondragstartdata; + treeview->ondragstart_action = args->ondragstart_action ? strdup(args->ondragstart_action) : NULL; + treeview->ondragcomplete = args->ondragcomplete; + treeview->ondragcompletedata = args->ondragcompletedata; + treeview->ondragcomplete_action = args->ondragcomplete_action ? strdup(args->ondragcomplete_action) : NULL; + treeview->ondrop = args->ondrop; + treeview->ondropdata = args->ondropdata; + treeview->ondrop_action = args->ondrop_action ? strdup(args->ondrop_action) : NULL; + treeview->selection.count = 0; + treeview->selection.rows = NULL; + treeview->current_row = -1; + +#if GTK_CHECK_VERSION(4, 0, 0) + treeview->coldata.listview = treeview; + treeview->coldata.column = 0; +#endif + + if(args->getvalue != NULL) { + treeview->getvalue = args->getvalue; + treeview->getvaluedata = args->getvaluedata; + } else { + args->getvalue = ui_null_getvalue; + } + + treeview->istreeview = TRUE; + treeview->getdepth = args->getdepth; + treeview->getdepthdata = args->getdepthdata; + + return treeview; +} + #if GTK_CHECK_VERSION(4, 10, 0) /* BEGIN GObject wrapper for generic pointers */ -typedef struct _ObjWrapper { - GObject parent_instance; - void *data; - int i; -} ObjWrapper; - typedef struct _ObjWrapperClass { GObjectClass parent_class; } ObjWrapperClass; @@ -148,7 +186,7 @@ self->data = NULL; } -ObjWrapper* obj_wrapper_new(void* data, int i) { +ObjWrapper* ui_gobj_wrapper_new(void* data, int i) { ObjWrapper *obj = g_object_new(obj_wrapper_get_type(), NULL); obj->data = data; obj->i = i; @@ -760,6 +798,110 @@ return scroll_area; } + + +UIWIDGET ui_treeview_create(UiObject *obj, UiTreeArgs *args) { + GListStore *ls = g_list_store_new(G_TYPE_OBJECT); + //g_list_store_append(ls, v1); + + // create obj to store all relevant data we need for handling events + // and list updates + UiListView *tableview = create_treeview(obj, args); + + GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args->multiselection); + GtkWidget *view = gtk_column_view_new(GTK_SELECTION_MODEL(selection_model)); + if(!args->show_header) { + gtk_widget_set_visible(gtk_widget_get_first_child(view), FALSE); + } + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); + + // init tableview + tableview->widget = view; + tableview->var = var; + tableview->liststore = ls; + tableview->selectionmodel = selection_model; + g_signal_connect( + view, + "destroy", + G_CALLBACK(ui_listview_destroy), + tableview); + + + // create columns from UiModel + UiModel *model = args->model; + int columns = 0; + if(model) { + columns = model->columns; + ui_model_add_observer(model, ui_listview_update_model, tableview); + } + + tableview->columns = calloc(columns, sizeof(int)); + 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++) { + tableview->columns[i] = i+addi; + + if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) { + // icon+text has 2 data columns + addi++; + } + + add_column(tableview, i); + } + + // bind listview to list + if(var && var->value) { + UiList *list = var->value; + + list->obj = tableview; + list->update = ui_listview_update2; + list->getselection = ui_listview_getselection2; + list->setselection = ui_listview_setselection2; + + ui_update_liststore(ls, list); + } + + // event handling + if(args->onactivate || args->onactivate_action) { + 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(); + gtk_scrolled_window_set_policy( + GTK_SCROLLED_WINDOW(scroll_area), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS + SCROLLEDWINDOW_SET_CHILD(scroll_area, view); + + if(args->width > 0 || args->height > 0) { + int width = args->width; + int height = args->height; + if(width == 0) { + width = -1; + } + if(height == 0) { + height = -1; + } + gtk_widget_set_size_request(scroll_area, width, height); + } + + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, scroll_area, &layout); + + return scroll_area; +} + void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index) { UiListView *listview = userdata; if(insert_index >= listview->numcolumns) { @@ -918,7 +1060,7 @@ int i = 0; void *elm = list->first(list); while(elm) { - ObjWrapper *obj = obj_wrapper_new(elm, i++); + ObjWrapper *obj = ui_gobj_wrapper_new(elm, i++); g_list_store_append(liststore, obj); elm = list->next(list); } @@ -927,7 +1069,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], i); + ObjWrapper *obj = ui_gobj_wrapper_new(elm[i], i); g_list_store_append(liststore, obj); } } @@ -2123,10 +2265,6 @@ #endif -GtkWidget* ui_get_tree_widget(UIWIDGET widget) { - return SCROLLEDWINDOW_GET_CHILD(widget); -} - static char** targets2array(char *target0, va_list ap, int *nelm) { int al = 16; char **targets = calloc(16, sizeof(char*));