add API spec for new treeview default tip

Wed, 09 Sep 2026 18:57:52 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Wed, 09 Sep 2026 18:57:52 +0200
changeset 1244
a29b53f97d1a
parent 1243
59d97ac069f0

add API spec for new treeview

application/Makefile file | annotate | diff | comparison | revisions
application/main.c file | annotate | diff | comparison | revisions
client/Makefile file | annotate | diff | comparison | revisions
ui/common/Makefile file | annotate | diff | comparison | revisions
ui/gtk/Makefile file | annotate | diff | comparison | revisions
ui/gtk/list.c file | annotate | diff | comparison | revisions
ui/gtk/list.h file | annotate | diff | comparison | revisions
ui/gtk/objs.mk file | annotate | diff | comparison | revisions
ui/gtk/tree.c file | annotate | diff | comparison | revisions
ui/gtk/tree.h file | annotate | diff | comparison | revisions
ui/ui/list.h file | annotate | diff | comparison | revisions
ui/ui/tree.h file | annotate | diff | comparison | revisions
ui/ui/ui.h file | annotate | diff | comparison | revisions
--- a/application/Makefile	Tue Sep 08 16:08:59 2026 +0200
+++ b/application/Makefile	Wed Sep 09 18:57:52 2026 +0200
@@ -57,9 +57,8 @@
 ../build/application/demo_bindings$(OBJ_EXT): demo_bindings.c \
  demo_bindings.h
 	$(CC) -o $@ $(CFLAGS) $(TK_CFLAGS) -c $<
-	
-../build/application/demo_states$(OBJ_EXT): demo_states.c \
- demo_states.h
+
+../build/application/demo_states$(OBJ_EXT): demo_states.c demo_states.h
 	$(CC) -o $@ $(CFLAGS) $(TK_CFLAGS) -c $<
 
 ../build/application/main$(OBJ_EXT): main.c
--- a/application/main.c	Tue Sep 08 16:08:59 2026 +0200
+++ b/application/main.c	Wed Sep 09 18:57:52 2026 +0200
@@ -203,6 +203,7 @@
     UiString *link_label;
     UiString *link_uri;
     UiList *submenulist;
+    UiList *tree;
     
     UiModel *model;
 } MyDocument;
@@ -218,6 +219,11 @@
 
 static UiThreadpool *threadpool;
 
+typedef struct TreeNode {
+    char *name;
+    int depth;
+} TreeNode;
+
 int threadfunc(void *data) {
     printf("thr wait for data...\n");
     ui_condvar_wait(cond);
@@ -389,6 +395,33 @@
     
     ui_int_new(docctx, "menu_radio");
     
+    doc->tree = ui_list_new(docctx, "tree");
+    TreeNode *root = malloc(sizeof(TreeNode));
+    root->name = "Root";
+    root->depth = 0;
+    ui_list_append(doc->tree, root);
+    
+    TreeNode *node_a = malloc(sizeof(TreeNode));
+    node_a->name = "Folder";
+    node_a->depth = 1;
+    ui_list_append(doc->tree, node_a);
+    
+    TreeNode *node_a0 = malloc(sizeof(TreeNode));
+    node_a0->name = "Leaf 1";
+    node_a0->depth = 2;
+    ui_list_append(doc->tree, node_a0);
+    
+    TreeNode *node_a1 = malloc(sizeof(TreeNode));
+    node_a1->name = "Leaf 2";
+    node_a1->depth = 2;
+    ui_list_append(doc->tree, node_a1);
+    
+    TreeNode *node_1 = malloc(sizeof(TreeNode));
+    node_1->name = "Leaf 3";
+    node_1->depth = 1;
+    ui_list_append(doc->tree, node_1);
+    
+    
     //doc->text = ui_text_new(docctx, "text");
     return doc;
 }
@@ -537,6 +570,16 @@
     return FALSE;
 }
 
+void* tree_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
+    TreeNode *node = elm;
+    return node->name;
+}
+
+int tree_getdepth(UiList *list, void *elm, int row, void *userdata) {
+    TreeNode *node = elm;
+    return node->depth;
+}
+
 void sourcelist_getvalue(UiList *list, void *sublistdata, void *rowdata, int index, UiSubListItem *item, void *userdata) {
     ui_menubuilder_ref(sourcelist_menu);
     item->label = strdup(rowdata);
@@ -906,6 +949,10 @@
         ui_tab(obj, "Tab 12") {
             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");
+        }
     }
     
     /*
--- a/client/Makefile	Tue Sep 08 16:08:59 2026 +0200
+++ b/client/Makefile	Wed Sep 09 18:57:52 2026 +0200
@@ -59,7 +59,8 @@
  ../ui/common/../ui/button.h ../ui/common/../ui/entry.h \
  ../ui/common/../ui/menu.h ../ui/common/../ui/toolbar.h \
  ../ui/common/../ui/list.h ../ui/common/../ui/text.h \
- ../ui/common/../ui/webview.h ../ui/common/../ui/widget.h
+ ../ui/common/../ui/webview.h ../ui/common/../ui/image.h \
+ ../ui/common/../ui/widget.h
 	$(CC) -o $@ $(CFLAGS) $(TK_CFLAGS) $(CLIENT_CFLAGS) -c $<
 
 ../build/client/main$(OBJ_EXT): main.c main.h ../ui/common/message.h \
@@ -73,6 +74,7 @@
  ../ui/common/../ui/button.h ../ui/common/../ui/entry.h \
  ../ui/common/../ui/menu.h ../ui/common/../ui/toolbar.h \
  ../ui/common/../ui/list.h ../ui/common/../ui/text.h \
- ../ui/common/../ui/webview.h ../ui/common/../ui/widget.h
+ ../ui/common/../ui/webview.h ../ui/common/../ui/image.h \
+ ../ui/common/../ui/widget.h
 	$(CC) -o $@ $(CFLAGS) $(TK_CFLAGS) $(CLIENT_CFLAGS) -c $<
 
--- a/ui/common/Makefile	Tue Sep 08 16:08:59 2026 +0200
+++ b/ui/common/Makefile	Wed Sep 09 18:57:52 2026 +0200
@@ -40,7 +40,8 @@
  common/../ui/window.h common/../ui/toolkit.h common/../ui/container.h \
  common/../ui/display.h common/../ui/button.h common/../ui/entry.h \
  common/../ui/menu.h common/../ui/toolbar.h common/../ui/list.h \
- common/../ui/text.h common/../ui/webview.h common/../ui/widget.h
+ common/../ui/text.h common/../ui/webview.h common/../ui/image.h \
+ common/../ui/widget.h
 	$(CC) -o $@ $(CFLAGS) -I../ucx $(SHLIB_CFLAGS) $(TK_CFLAGS) -c $<
 
 $(COMMON_OBJPRE)uic_condvar$(OBJ_EXT): common/condvar.c common/condvar.h \
--- a/ui/gtk/Makefile	Tue Sep 08 16:08:59 2026 +0200
+++ b/ui/gtk/Makefile	Wed Sep 09 18:57:52 2026 +0200
@@ -105,7 +105,8 @@
  gtk/../common/toolbar.h gtk/../common/../ui/toolbar.h \
  gtk/../common/menu.h gtk/../common/../ui/menu.h \
  gtk/../common/../ui/toolkit.h gtk/button.h gtk/../ui/button.h gtk/menu.h \
- gtk/../ui/menu.h gtk/../common/menu.h gtk/../ui/properties.h
+ gtk/../ui/menu.h gtk/../common/menu.h gtk/../ui/widget.h \
+ gtk/../ui/properties.h
 	$(CC) -o $@ $(CFLAGS) -I../ucx $(SHLIB_CFLAGS) $(TK_CFLAGS) -c $<
 
 $(GTK_OBJPRE)icon.o: gtk/icon.c gtk/toolkit.h gtk/../ui/toolkit.h \
@@ -120,7 +121,8 @@
  gtk/../common/context.h gtk/../common/../ui/toolkit.h \
  gtk/../common/action.h gtk/../common/object.h gtk/container.h \
  gtk/../ui/container.h gtk/menu.h gtk/../ui/menu.h gtk/../common/menu.h \
- gtk/../common/../ui/menu.h gtk/widget.h gtk/../ui/widget.h
+ gtk/../common/../ui/menu.h gtk/widget.h gtk/../ui/widget.h \
+ gtk/../common/action.h
 	$(CC) -o $@ $(CFLAGS) -I../ucx $(SHLIB_CFLAGS) $(TK_CFLAGS) -c $<
 
 $(GTK_OBJPRE)list.o: gtk/list.c gtk/../common/context.h \
@@ -180,6 +182,13 @@
  gtk/../common/menu.h gtk/../common/threadpool.h gtk/../common/app.h
 	$(CC) -o $@ $(CFLAGS) -I../ucx $(SHLIB_CFLAGS) $(TK_CFLAGS) -c $<
 
+$(GTK_OBJPRE)tree.o: gtk/tree.c gtk/tree.h gtk/../ui/tree.h \
+ gtk/../ui/list.h gtk/../ui/toolkit.h gtk/toolkit.h gtk/../ui/toolkit.h \
+ gtk/../common/context.h gtk/../common/../ui/toolkit.h \
+ gtk/../common/action.h gtk/../common/object.h gtk/../common/container.h \
+ gtk/../common/../ui/container.h gtk/../common/../ui/toolkit.h
+	$(CC) -o $@ $(CFLAGS) -I../ucx $(SHLIB_CFLAGS) $(TK_CFLAGS) -c $<
+
 $(GTK_OBJPRE)webview.o: gtk/webview.c gtk/toolkit.h gtk/../ui/toolkit.h \
  gtk/../common/context.h gtk/../common/../ui/toolkit.h \
  gtk/../common/action.h gtk/../common/object.h gtk/container.h \
@@ -204,6 +213,6 @@
  gtk/../ui/toolkit.h gtk/../common/object.h gtk/toolbar.h \
  gtk/../ui/toolbar.h gtk/list.h gtk/../ui/list.h gtk/container.h \
  gtk/../ui/container.h gtk/headerbar.h gtk/button.h gtk/../ui/button.h \
- gtk/window.h
+ gtk/window.h gtk/widget.h gtk/../ui/widget.h
 	$(CC) -o $@ $(CFLAGS) -I../ucx $(SHLIB_CFLAGS) $(TK_CFLAGS) -c $<
 
--- 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*));
--- a/ui/gtk/list.h	Tue Sep 08 16:08:59 2026 +0200
+++ b/ui/gtk/list.h	Wed Sep 09 18:57:52 2026 +0200
@@ -30,6 +30,7 @@
 #define	LIST_H
 
 #include "../ui/list.h"
+#include "../ui/tree.h"
 #include "toolkit.h"
 
 #include <cx/array_list.h>
@@ -98,6 +99,10 @@
     void              *onsavedata;
     UiListSelection   selection;
     
+    ui_tree_getdepthfunc   getdepth;
+    void                   *getdepthdata;
+    UiBool                 istreeview;
+    
 };
 
 typedef struct UiTreeEventData {
@@ -139,6 +144,7 @@
     UiSubListEventData       current_eventdata;
 };
 
+void* ui_null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult);
 
 #if GTK_CHECK_VERSION(4, 10, 0)
 
@@ -160,10 +166,6 @@
 
 void* ui_strmodel_getvalue(void *elm, int column);
 
-UIWIDGET ui_listview_var(UiObject *obj, UiVar *var, ui_getvaluefunc getvalue, ui_callback f, void *udata);
-UIWIDGET ui_table_var(UiObject *obj, UiVar *var, UiModel *model, UiListCallbacks cb);
-
-GtkWidget* ui_get_tree_widget(UIWIDGET widget);
 
 void ui_listview_update_model(UiModel *model, void *userdata, int insert_index, int delete_index);
 void ui_listview_update(UiList *list, int i);
@@ -211,6 +213,18 @@
 UiListSelection ui_listbox_list_getselection(UiList *list);
 
 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data);
+
+#if GTK_CHECK_VERSION(4, 10, 0)
+
+typedef struct _ObjWrapper {
+    GObject parent_instance;
+    void *data;
+    int i;
+} ObjWrapper;
+
+ObjWrapper* ui_gobj_wrapper_new(void* data, int i);
+
+#endif
         
 #ifdef	__cplusplus
 }
--- a/ui/gtk/objs.mk	Tue Sep 08 16:08:59 2026 +0200
+++ b/ui/gtk/objs.mk	Wed Sep 09 18:57:52 2026 +0200
@@ -39,6 +39,7 @@
 GTKOBJ += display.o
 GTKOBJ += text.o
 GTKOBJ += list.o
+GTKOBJ += tree.o
 GTKOBJ += image.o
 GTKOBJ += icon.o
 GTKOBJ += graphics.o
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/gtk/tree.c	Wed Sep 09 18:57:52 2026 +0200
@@ -0,0 +1,35 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "tree.h"
+#include "list.h"
+#include "container.h"
+
+#include "../common/context.h"
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/gtk/tree.h	Wed Sep 09 18:57:52 2026 +0200
@@ -0,0 +1,80 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef TREE_H
+#define TREE_H
+
+#include "../ui/tree.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;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* TREE_H */
+
--- a/ui/ui/list.h	Tue Sep 08 16:08:59 2026 +0200
+++ b/ui/ui/list.h	Wed Sep 09 18:57:52 2026 +0200
@@ -26,8 +26,8 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#ifndef UI_TREE_H
-#define	UI_TREE_H
+#ifndef UI_LIST_H
+#define	UI_LIST_H
 
 #include "toolkit.h"
 
@@ -372,5 +372,5 @@
 }
 #endif
 
-#endif	/* UI_TREE_H */
+#endif	/* UI_LIST_H */
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/ui/tree.h	Wed Sep 09 18:57:52 2026 +0200
@@ -0,0 +1,101 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2026 Olaf Wintermann. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   1. Redistributions of source code must retain the above copyright
+ *      notice, this list of conditions and the following disclaimer.
+ *
+ *   2. Redistributions in binary form must reproduce the above copyright
+ *      notice, this list of conditions and the following disclaimer in the
+ *      documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UI_TREE_H
+#define UI_TREE_H
+
+#include "list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+    
+typedef struct UiTreeArgs UiTreeArgs;
+    
+typedef int (*ui_tree_getdepthfunc)(UiList *list, void *elm, int row, void *userdata);
+
+struct UiTreeArgs {
+    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;
+    UiModel *model;
+    ui_tree_getdepthfunc getdepth;
+    void *getdepthdata;
+    ui_getvaluefunc2 getvalue;
+    void *getvaluedata;
+    
+    ui_callback onactivate;
+    void *onactivatedata;
+    const char *onactivate_action;
+    ui_callback onselection;
+    void *onselectiondata;
+    const char *onselection_action;
+    ui_callback ondragstart;
+    void *ondragstartdata;
+    const char *ondragstart_action;
+    ui_callback ondragcomplete;
+    void *ondragcompletedata;
+    const char *ondragcomplete_action;
+    ui_callback ondrop;
+    void *ondropdata;
+    const char *ondrop_action;
+    UiBool multiselection;
+    UiBool show_header;
+    UiMenuBuilder *contextmenu;
+    
+    const int *states;
+    const int *visibility_states;
+};
+
+#define ui_treeview(obj, ...) ui_treeview_create(obj, &(UiTreeArgs) { __VA_ARGS__ } )
+
+UIEXPORT UIWIDGET ui_treeview_create(UiObject *obj, UiTreeArgs *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* UI_TREE_H */
+
--- a/ui/ui/ui.h	Tue Sep 08 16:08:59 2026 +0200
+++ b/ui/ui/ui.h	Wed Sep 09 18:57:52 2026 +0200
@@ -39,6 +39,7 @@
 #include "text.h"
 #include "properties.h"
 #include "list.h"
+#include "tree.h"
 #include "graphics.h"
 #include "entry.h"
 #include "range.h"

mercurial