--- a/ui/gtk/list.c Wed Sep 09 18:57:52 2026 +0200 +++ b/ui/gtk/list.c Thu Sep 10 17:36:06 2026 +0200 @@ -40,6 +40,7 @@ #include <cx/linked_list.h> #include "list.h" +#include "tree.h" #include "button.h" #include "icon.h" #include "menu.h" @@ -123,55 +124,18 @@ 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; + GListStore *children; + void *data; + int i; +} ObjWrapper; + typedef struct _ObjWrapperClass { GObjectClass parent_class; } ObjWrapperClass; @@ -183,6 +147,7 @@ } static void obj_wrapper_init(ObjWrapper *self) { + self->children = NULL; self->data = NULL; } @@ -802,13 +767,12 @@ 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); + UiListView *treeview = ui_create_treeview(obj, args); - GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args->multiselection); + GtkSelectionModel *selection_model = create_selection_model(treeview, 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); @@ -817,15 +781,15 @@ 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; + treeview->widget = view; + treeview->var = var; + treeview->liststore = ls; + treeview->selectionmodel = selection_model; g_signal_connect( view, "destroy", G_CALLBACK(ui_listview_destroy), - tableview); + treeview); // create columns from UiModel @@ -833,42 +797,42 @@ int columns = 0; if(model) { columns = model->columns; - ui_model_add_observer(model, ui_listview_update_model, tableview); + ui_model_add_observer(model, ui_listview_update_model, treeview); } - tableview->columns = calloc(columns, sizeof(int)); - tableview->numcolumns = columns; + treeview->columns = calloc(columns, sizeof(int)); + treeview->numcolumns = columns; - tableview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128); - tableview->bound_rows->collection.simple_destructor = (cx_destructor_func)free; + treeview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128); + treeview->bound_rows->collection.simple_destructor = (cx_destructor_func)free; int addi = 0; for(int i=0;i<columns;i++) { - tableview->columns[i] = i+addi; + treeview->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); + add_column(treeview, i); } // bind listview to list if(var && var->value) { UiList *list = var->value; - list->obj = tableview; + list->obj = treeview; list->update = ui_listview_update2; list->getselection = ui_listview_getselection2; list->setselection = ui_listview_setselection2; - ui_update_liststore(ls, list); + ui_update_tree_liststore(ls, list); } // event handling if(args->onactivate || args->onactivate_action) { - g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), tableview); + g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), treeview); } if(args->contextmenu) { UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, view); @@ -1066,6 +1030,10 @@ } } +void ui_update_tree_liststore(GListStore *liststore, UiList *list) { + +} + 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++) { @@ -3068,3 +3036,147 @@ data->callback(&event, data->userdata); } } + +/* ------------------------------ SourceList2 ------------------------------ */ + +static void ui_destroy_sourcelist2(GtkWidget *w, UiListBox2 *v) { + cxListFree(v->rows); + free(v->onactivate_action); + free(v->onbuttonclick_action); + free(v); +} + +UIWIDGET ui_sourcelist2_create(UiObject *obj, UiSourceList2Args *args) { +#ifdef UI_GTK3 + GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(), NULL); +#else + GtkWidget *listbox = gtk_list_box_new(); +#endif + if(!args->style_class) { +#if GTK_MAJOR_VERSION >= 4 + WIDGET_ADD_CSS_CLASS(listbox, "navigation-sidebar"); +#else + WIDGET_ADD_CSS_CLASS(listbox, "sidebar"); +#endif + } + 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_states(obj->ctx, listbox, args->states); + UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; + UiLayout layout = UI_ARGS2LAYOUT(args); + ct->add(ct, scroll_area, &layout); + + UiListBox2 *uilistbox = malloc(sizeof(UiListBox2)); + uilistbox->obj = obj; + uilistbox->listbox = GTK_LIST_BOX(listbox); + uilistbox->rows = cxArrayListCreate(NULL, CX_STORE_POINTERS, 64); + uilistbox->getvalue = args->getvalue; + uilistbox->getvaluedata = args->getvaluedata; + uilistbox->getdepth = args->getdepth; + uilistbox->getdepthdata = args->getdepthdata; + uilistbox->onactivate = args->onactivate; + uilistbox->onactivatedata = args->onactivatedata; + uilistbox->onactivate_action = args->onactivate_action ? strdup(args->onactivate_action) : NULL; + uilistbox->onbuttonclick = args->onbuttonclick; + uilistbox->onbuttonclickdata = args->onbuttonclickdata; + uilistbox->onbuttonclick_action = args->onbuttonclick_action ? strdup(args->onbuttonclick_action) : NULL; + + UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname, UI_VAR_LIST); + uilistbox->var = var; + if(var) { + UiList *list = var->value; + list->update = ui_listbox2_update; + list->getselection = ui_listbox2_getselection; + list->setselection = ui_listbox2_setselection; + list->obj = uilistbox; + ui_listbox2_update(list, -1); + } + + // register uilistbox for both widgets, so it doesn't matter which + // widget is used later + 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, + "destroy", + G_CALLBACK(ui_destroy_sourcelist2), + uilistbox); + + if(args->onactivate) { + g_signal_connect( + listbox, + "row-activated", + G_CALLBACK(ui_listbox2_row_activate), + NULL); + } + + return scroll_area; +} + + +static int listbox2_update_item(UiListBox2 *listbox, GtkWidget *row, UiList *list, void *elm, int index) { + UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; + if(listbox->getvalue) { + listbox->getvalue(list,elm, index, &item, listbox->getvaluedata); + } + + GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); + + return 0; +} + +static void listbox2_update_all(UiListBox2 *listbox) { + gtk_list_box_remove_all(listbox->listbox); + cxListClear(listbox->rows); + if(!listbox->var) { + return; + } + UiList *list = listbox->var->value; + if(!list) { + return; + } + + + int index = 0; + void *elm = list->first(list); + while(elm) { + GtkWidget *row = gtk_list_box_row_new(); + cxListAdd(listbox->rows, row); + gtk_list_box_append(listbox->listbox, row); + int node_depth = listbox2_update_item(listbox, row, list, elm, index); + + elm = list->next(list); + index++; + } +} + +void ui_listbox2_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { + +} + + +void ui_listbox2_update(UiList *list, int i) { + UiListBox2 *listbox = list->obj; + if(i >= 0) { + // TODO + } else { + listbox2_update_all(listbox); + } +} + +UiListSelection ui_listbox2_getselection(UiList *list) { + +} + +void ui_listbox2_setselection(UiList *list, UiListSelection selection) { + +}