# HG changeset patch # User Olaf Wintermann # Date 1789054566 -7200 # Node ID 4163776eef4981f145ad413af7f13de243be75ed # Parent a29b53f97d1a11fc01c5187e24ecd665d57149fa define sourcelist2 widget diff -r a29b53f97d1a -r 4163776eef49 application/main.c --- a/application/main.c Wed Sep 09 18:57:52 2026 +0200 +++ b/application/main.c Thu Sep 10 17:36:06 2026 +0200 @@ -575,6 +575,11 @@ return node->name; } +void sourcelist2_getvalue(UiList *list, void *elm, int index, UiSubListItem *item, void *userdata) { + TreeNode *node = elm; + item->label = node->name; +} + int tree_getdepth(UiList *list, void *elm, int row, void *userdata) { TreeNode *node = elm; return node->depth; @@ -950,8 +955,10 @@ ui_drawingarea(obj, .fill = TRUE, .draw = draw); } ui_tab(obj, "Tab 13") { - UiModel *model = ui_model(obj->ctx, UI_STRING, "test", -1); - ui_treeview(obj, .fill = TRUE, .model = model, .show_header = TRUE, .getvalue = tree_getvalue, .getdepth = tree_getdepth, .varname = "tree"); + //UiModel *model = ui_model(obj->ctx, UI_STRING, "test", -1); + //ui_treeview(obj, .fill = TRUE, .model = model, .show_header = TRUE, .getvalue = tree_getvalue, .getdepth = tree_getdepth, .varname = "tree"); + + ui_sourcelist2(obj, .fill = TRUE, .getvalue = sourcelist2_getvalue, .getdepth = tree_getdepth, .varname = "tree"); } } diff -r a29b53f97d1a -r 4163776eef49 ui/gtk/list.c --- 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 #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;icolumns[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;icallback(&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) { + +} diff -r a29b53f97d1a -r 4163776eef49 ui/gtk/list.h --- a/ui/gtk/list.h Wed Sep 09 18:57:52 2026 +0200 +++ b/ui/gtk/list.h Thu Sep 10 17:36:06 2026 +0200 @@ -115,7 +115,8 @@ char *selection_action; } UiTreeEventData; -typedef struct UiListBox UiListBox; +typedef struct UiListBox UiListBox; +typedef struct UiListBox2 UiListBox2; typedef struct UiListBoxSubList { UiVar *var; @@ -144,11 +145,38 @@ UiSubListEventData current_eventdata; }; +struct UiListBox2 { + UiObject *obj; + GtkListBox *listbox; + UiVar *var; + CxList *rows; + ui_sourcelist_getvalue_func getvalue; + void *getvaluedata; + ui_tree_getdepthfunc getdepth; + void *getdepthdata; + + ui_callback onactivate; + void *onactivatedata; + char *onactivate_action; + ui_callback onbuttonclick; + void *onbuttonclickdata; + char *onbuttonclick_action; +}; + +typedef struct UiListBox2Row { + UiListBox2 *listbox; + CxList *children; // type: int (index) + int index; + int depth; + int expanded; +} UiListBox2Row; + void* ui_null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult); #if GTK_CHECK_VERSION(4, 10, 0) void ui_update_liststore(GListStore *liststore, UiList *list); +void ui_update_tree_liststore(GListStore *liststore, UiList *list); void ui_update_liststore_static(GListStore *liststore, char **elm, size_t nelm); void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index); @@ -214,17 +242,14 @@ void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data); -#if GTK_CHECK_VERSION(4, 10, 0) + +/* ------------------------------ SourceList2 ------------------------------ */ -typedef struct _ObjWrapper { - GObject parent_instance; - void *data; - int i; -} ObjWrapper; +void ui_listbox2_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data); -ObjWrapper* ui_gobj_wrapper_new(void* data, int i); - -#endif +void ui_listbox2_update(UiList *list, int i); +UiListSelection ui_listbox2_getselection(UiList *list); +void ui_listbox2_setselection(UiList *list, UiListSelection selection); #ifdef __cplusplus } diff -r a29b53f97d1a -r 4163776eef49 ui/gtk/tree.c --- a/ui/gtk/tree.c Wed Sep 09 18:57:52 2026 +0200 +++ b/ui/gtk/tree.c Thu Sep 10 17:36:06 2026 +0200 @@ -32,4 +32,46 @@ #include "../common/context.h" - +UiListView* ui_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; +} diff -r a29b53f97d1a -r 4163776eef49 ui/gtk/tree.h --- a/ui/gtk/tree.h Wed Sep 09 18:57:52 2026 +0200 +++ b/ui/gtk/tree.h Thu Sep 10 17:36:06 2026 +0200 @@ -30,46 +30,14 @@ #define TREE_H #include "../ui/tree.h" +#include "list.h" #include "toolkit.h" #ifdef __cplusplus extern "C" { #endif -typedef struct UiTreeView UiTreeView; - -struct UiTreeView { - UiObject *obj; - GtkWidget *widget; - UiVar *var; - UiModel *model; - UiBool multiselection; - ui_tree_getdepthfunc getdepth; - void *getdepthdata; - ui_getvaluefunc2 getvalue; - void *getvaluedata; - - ui_callback onactivate; - void *onactivatedata; - char *onactivate_action; - ui_callback onselection; - void *onselectiondata; - char *onselection_action; - ui_callback ondragstart; - void *ondragstartdata; - char *ondragstart_action; - ui_callback ondragcomplete; - void *ondragcompletedata; - char *ondragcomplete_action; - ui_callback ondrop; - void *ondropdata; - char *ondrop_action; -}; - -typedef struct UiTreeColData { - UiTreeView *treeview; - int column; -} UiTreeColData; +UiListView* ui_create_treeview(UiObject *obj, UiTreeArgs *args); #ifdef __cplusplus diff -r a29b53f97d1a -r 4163776eef49 ui/ui/list.h --- a/ui/ui/list.h Wed Sep 09 18:57:52 2026 +0200 +++ b/ui/ui/list.h Thu Sep 10 17:36:06 2026 +0200 @@ -35,15 +35,16 @@ extern "C" { #endif -typedef struct UiModel UiModel; -typedef struct UiListCallbacks UiListCallbacks; -typedef struct UiListDnd UiListDnd; +typedef struct UiModel UiModel; +typedef struct UiListCallbacks UiListCallbacks; +typedef struct UiListDnd UiListDnd; -typedef struct UiListArgs UiListArgs; -typedef struct UiSourceListArgs UiSourceListArgs; +typedef struct UiListArgs UiListArgs; +typedef struct UiSourceListArgs UiSourceListArgs; +typedef struct UiSourceList2Args UiSourceList2Args; -typedef struct UiSubList UiSubList; -typedef struct UiSubListItem UiSubListItem; +typedef struct UiSubList UiSubList; +typedef struct UiSubListItem UiSubListItem; typedef enum UiModelType { UI_STRING = 0, @@ -65,6 +66,8 @@ UiModelType type; } UiCellValue; +typedef int (*ui_tree_getdepthfunc)(UiList *list, void *elm, int row, void *userdata); + typedef UiBool (*ui_list_savefunc)(UiList *list, int row, int col, UiCellValue *value, void *userdata); typedef void (*ui_model_update_func)(UiModel *model, void *userdata, int insert_index, int delete_index); @@ -191,6 +194,8 @@ typedef void (*ui_sublist_getvalue_func)(UiList *list, void *sublist_userdata, void *rowdata, int index, UiSubListItem *item, void *userdata); +typedef void (*ui_sourcelist_getvalue_func)(UiList *list, void *elm, int index, UiSubListItem *item, void *userdata); + struct UiSubList { UiList *value; const char *varname; @@ -306,6 +311,70 @@ const int *visibility_states;; }; +struct UiSourceList2Args { + UiBool fill; + UiBool hexpand; + UiBool vexpand; + UiBool hfill; + UiBool vfill; + UiBool override_defaults; + int margin; + int margin_left; + int margin_right; + int margin_top; + int margin_bottom; + int colspan; + int rowspan; + int width; + int height; + const char *name; + const char *style_class; + + UiList *list; + const char* varname; + + /* + * callback for each list item, that should fill all necessary + * UiSubListItem fields + */ + ui_sourcelist_getvalue_func getvalue; + + /* + * getvalue func userdata + */ + void *getvaluedata; + + /* + * callback for each list item, that determines the depth + * and indirectly the parent for the item + */ + ui_tree_getdepthfunc getdepth; + + /* + * getdepth func userdata + */ + void *getdepthdata; + + /* + * activated when a list item is selected + */ + ui_callback onactivate; + void *onactivatedata; + const char *onactivate_action; + + /* + * activated, when the additional list item button is clicked + */ + ui_callback onbuttonclick; + void *onbuttonclickdata; + const char *onbuttonclick_action; + + UiMenuBuilder *contextmenu; + + const int *states; + const int *visibility_states;; +}; + #define UI_SUBLIST(...) (UiSubList){ __VA_ARGS__ } #define UI_SUBLISTS(...) (UiSubList[]){ __VA_ARGS__, (UiSubList){NULL,NULL,NULL,0} } @@ -334,6 +403,7 @@ #define ui_dropdown(obj, ...) ui_dropdown_create(obj, &(UiListArgs) { __VA_ARGS__ } ) #define ui_breadcrumbbar(obj, ...) ui_breadcrumbbar_create(obj, &(UiListArgs) { __VA_ARGS__ } ) #define ui_sourcelist(obj, ...) ui_sourcelist_create(obj, &(UiSourceListArgs) { __VA_ARGS__ } ) +#define ui_sourcelist2(obj, ...) ui_sourcelist2_create(obj, &(UiSourceList2Args) { __VA_ARGS__ } ) UIEXPORT UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args); UIEXPORT UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args); @@ -346,6 +416,7 @@ UIEXPORT int ui_dropdown_selection(UIWIDGET dropdown); UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args); +UIEXPORT UIWIDGET ui_sourcelist2_create(UiObject *obj, UiSourceList2Args *args); UIEXPORT void ui_sublist_item_set_icon(UiSubListItem *item, const char *icon); UIEXPORT void ui_sublist_item_set_label(UiSubListItem *item, const char *label); diff -r a29b53f97d1a -r 4163776eef49 ui/ui/tree.h --- a/ui/ui/tree.h Wed Sep 09 18:57:52 2026 +0200 +++ b/ui/ui/tree.h Thu Sep 10 17:36:06 2026 +0200 @@ -37,7 +37,6 @@ typedef struct UiTreeArgs UiTreeArgs; -typedef int (*ui_tree_getdepthfunc)(UiList *list, void *elm, int row, void *userdata); struct UiTreeArgs { UiBool fill;