merge

Thu, 02 Oct 2025 14:49:27 +0200

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 02 Oct 2025 14:49:27 +0200
changeset 786
150a1180f7ec
parent 785
b943e3d618f0 (current diff)
parent 783
25b2da0aac15 (diff)
child 787
91205084a852

merge

--- a/application/main.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/application/main.c	Thu Oct 02 14:49:27 2025 +0200
@@ -435,7 +435,8 @@
 
 void action_sourcelist_activate(UiEvent *event, void *userdata) {
     UiSubListEventData *eventdata = event->eventdata;
-    printf("sourcelist %s index %d\n", eventdata->row_data, event->intval);
+    printf("sourcelist %s index %d\n", eventdata->row_data, eventdata->row_index);
+    ui_list_update_row(eventdata->list, eventdata->row_index);
 }
 
 void action_table_activate(UiEvent *event, void *userdata) {
@@ -458,11 +459,17 @@
         case 3: return ui_foldericon(16);
         case 4: return ui_fileicon(16);
         case 5: return "file";
+        case 6: return (void*)(intptr_t)123;
+        case 7: return "edit me";
+        case 8: return "edit me too";
     }
     return NULL;
 }
 
 UiBool table_getstyle(UiList *list, void *elm, int row, int col, void *userdata, UiTextStyle *style) {
+    if(col > 6) {
+        return FALSE;
+    }
     if(row < 2 && col != -1) {
         style->text_style = UI_TEXT_STYLE_BOLD;
         if(col == 2) {
@@ -563,6 +570,11 @@
     ui_linkbutton_value_set(doc->link, label, uri);
 }
 
+static UiBool list_save(UiList *list, int row, int col, UiCellValue *value, void *userdata) {
+    printf("list new value at [%d, %d]: %s\n", row, col, value->string);
+    return FALSE;
+}
+
 void application_startup(UiEvent *event, void *data) {
     // global list
     UiContext *global = ui_global_context();
@@ -573,7 +585,7 @@
     
     
     
-    UiObject *obj = ui_sidebar_window("Test", NULL);
+    UiObject *obj = ui_splitview_window("Test", TRUE);
     
     MyDocument *doc = create_doc();
     ui_attach_document(obj->ctx, doc);
@@ -585,6 +597,10 @@
                 .onactivate = action_sourcelist_activate);
     }
     
+    ui_right_panel(obj, .margin = 10, .spacing = 10) {
+        ui_button(obj, .label = "Test");
+    }
+    
     ui_tabview(obj, .spacing=10, .margin=10, .tabview = UI_TABVIEW_NAVIGATION_SIDE, .varname="tabview", .fill = TRUE) {
         ui_tab(obj, "Tab 0") {
             ui_vbox(obj, .fill = FALSE, .margin = 15, .spacing = 15) {
@@ -607,7 +623,10 @@
                 ui_label(obj, .label = "Label Col 2", .style = UI_LABEL_STYLE_TITLE, .align = UI_ALIGN_RIGHT);
                 ui_newline(obj);
 
-                ui_spinner(obj, .step = 5);
+                ui_spinbox(obj, .step = 5);
+                ui_newline(obj);
+                
+                ui_switch(obj, .varname = "switch");
                 ui_newline(obj);
 
                 ui_progressbar(obj, .colspan = 2, .varname = "progress");
@@ -645,10 +664,10 @@
             }
         }
         ui_tab(obj, "Tab 1") {
-            UiModel *model = ui_model(obj->ctx, UI_ICON_TEXT, "col1", UI_INTEGER, "col2", UI_ICON, "col3", UI_ICON_TEXT, "col4", UI_INTEGER, "col5", -1);
+            UiModel *model = ui_model(obj->ctx, UI_ICON_TEXT, "col1", UI_INTEGER, "col2", UI_ICON, "col3", UI_ICON_TEXT, "col4", UI_INTEGER, "col5", UI_STRING_EDITABLE, "edit6", UI_STRING_EDITABLE, "edit7", -1);
             model->columnsize[0] = -1;
             ui_table(obj, .model = model, .list = doc->list2, .colspan = 2, .fill = TRUE, .contextmenu = menubuilder, .multiselection = TRUE, .fill = TRUE,
-                    .getvalue = table_getvalue, .getstyle = table_getstyle,
+                    .getvalue = table_getvalue, .getstyle = table_getstyle, .onsave = list_save,
                     .onactivate = action_table_activate, .onactivatedata = "activate",
                     .onselection = action_table_activate, .onselectiondata = "selection");
             ui_hbox(obj, .fill = FALSE) {
@@ -710,7 +729,7 @@
         }
         
         ui_tab(obj, "Tab 8") {
-            ui_hsplitpane(obj, .initial_position = 100, .fill = TRUE) {
+            ui_hsplitpane(obj, .initial_position = 100, .position_property = "hsplitpane.position", .fill = TRUE) {
                 ui_button(obj, .label = "Button 1");
                 ui_button(obj, .label = "Button 2");
             }
@@ -1035,7 +1054,7 @@
         
         ui_button(obj, .label = "Button Y");
         ui_checkbox(obj, .label = "Checkbox");
-        ui_spinner(obj, .digits = 2);
+        ui_spinbox(obj, .digits = 2);
         ui_newline(obj);
         
         ui_hbox(obj, .colspan = 3) {
--- a/ui/common/args.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/common/args.c	Thu Oct 02 14:49:27 2025 +0200
@@ -320,13 +320,16 @@
     args->onclickdata = onclickdata;
 }
 
-void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *groups) {
-    // TODO
+void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 void ui_toolbar_item_args_free(UiToolbarItemArgs *args) {
     free((void*)args->label);
     free((void*)args->stockid);
     free((void*)args->icon);
+    free((void*)args->groups);
     free(args);
 }
 
@@ -369,8 +372,10 @@
 }
 
 
-void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *groups) {
-    // TODO
+void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args,int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 
@@ -379,6 +384,7 @@
     free((void*)args->stockid);
     free((void*)args->icon);
     free((void*)args->varname);
+    free((void*)args->groups);
     free(args);
 }
 
@@ -424,7 +430,7 @@
 }
 
 void ui_container_args_set_fill(UiContainerArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 void ui_container_args_set_hexpand(UiContainerArgs *args, UiBool value) {
@@ -529,7 +535,7 @@
 
 
 void ui_frame_args_set_fill(UiFrameArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -662,7 +668,7 @@
 
 
 void ui_splitpane_args_set_fill(UiSplitPaneArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -735,6 +741,9 @@
     args->initial_position = pos;
 }
 
+void ui_splitpane_args_set_position_property(UiSplitPaneArgs *args, const char *propname) {
+    args->position_property = strdup(propname);
+}
 
 void ui_splitpane_args_set_varname(UiSplitPaneArgs *args, const char *varname) {
     args->varname = strdup(varname);
@@ -754,6 +763,7 @@
     free((void*)args->name);
     free((void*)args->style_class);
     free((void*)args->varname);
+    free((void*)args->position_property);
     free(args);
 }
 
@@ -767,7 +777,7 @@
 }
 
 void ui_tabview_args_set_fill(UiTabViewArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 void ui_tabview_args_set_hexpand(UiTabViewArgs *args, UiBool value) {
@@ -876,7 +886,7 @@
 
 
 void ui_widget_args_set_fill(UiWidgetArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -942,7 +952,7 @@
 
 
 void ui_label_args_set_fill(UiLabelArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1031,7 +1041,7 @@
 
 
 void ui_progressbar_args_set_fill(UiProgressbarArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1112,7 +1122,7 @@
 }
 
 void ui_progress_spinner_args_set_fill(UiProgressbarSpinnerArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 void ui_progress_spinner_args_set_hexpand(UiProgressbarSpinnerArgs *args, UiBool value) {
@@ -1177,7 +1187,7 @@
 
 
 void ui_button_args_set_fill(UiButtonArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1253,8 +1263,10 @@
     args->onclickdata = onclickdata;
 }
 
-void ui_button_args_set_groups(UiButtonArgs *args, int *groups){
-    // TODO
+void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_button_args_free(UiButtonArgs *args) {
@@ -1279,7 +1291,7 @@
 
 
 void ui_toggle_args_set_fill(UiToggleArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1367,8 +1379,10 @@
     args->enable_group = group;
 }
 
-void ui_toggle_args_set_groups(UiToggleArgs *args, int *groups){
-    // TODO
+void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_toggle_args_free(UiToggleArgs *args) {
@@ -1393,7 +1407,7 @@
 
 
 void ui_linkbutton_args_set_fill(UiLinkButtonArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1473,8 +1487,10 @@
     args->value = value;
 }
 
-void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *groups){
-    // TODO
+void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_linkbutton_args_free(UiLinkButtonArgs *args) {
@@ -1497,7 +1513,7 @@
 }
 
 void ui_list_args_set_fill(UiListArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 void ui_list_args_set_hexpand(UiListArgs *args, UiBool value) {
@@ -1617,6 +1633,14 @@
     args->ondropdata = userdata;
 }
 
+void ui_list_args_set_onsave(UiListArgs *args, ui_list_savefunc onsave) {
+    args->onsave = onsave;
+}
+
+void ui_list_args_set_onsavedata(UiListArgs *args, void *userdata) {
+    args->onsavedata = userdata;
+}
+
 void ui_list_args_set_multiselection(UiListArgs *args, UiBool multiselection) {
     args->multiselection = multiselection;
 }
@@ -1625,8 +1649,10 @@
     args->contextmenu = menubuilder;
 }
 
-void ui_list_args_set_groups(UiListArgs *args, int *groups) {
-    // TODO
+void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_list_args_free(UiListArgs *args) {
@@ -1639,6 +1665,7 @@
         }
         free(args->static_elements);
     }
+    free((void*)args->groups);
     free(args);
 }
 
@@ -1654,7 +1681,7 @@
 
 
 void ui_sourcelist_args_set_fill(UiSourceListArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1755,6 +1782,7 @@
     free((void*)args->style_class);
     free((void*)args->varname);
     free((void*)args->sublists);
+    free((void*)args->groups);
     free(args);
 }
 
@@ -1769,7 +1797,7 @@
 
 
 void ui_textarea_args_set_fill(UiTextAreaArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1834,8 +1862,10 @@
     args->value = value;
 }
 
-void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *groups){
-    // TODO
+void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_textarea_args_free(UiTextAreaArgs *args) {
@@ -1858,7 +1888,7 @@
 
 
 void ui_textfield_args_set_fill(UiTextFieldArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -1932,8 +1962,10 @@
     args->value = value;
 }
 
-void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *groups){
-    // TODO
+void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_textfield_args_free(UiTextFieldArgs *args) {
@@ -1945,6 +1977,109 @@
 }
 
 
+/* ------------------------- UiSpinBoxArgs ----------------------------*/
+
+UiSpinBoxArgs* ui_spinbox_args_new(void) {
+    UiSpinBoxArgs *args = malloc(sizeof(UiSpinBoxArgs));
+    memset(args, 0, sizeof(UiSpinBoxArgs));
+    return args;
+}
+
+void ui_spinbox_args_set_fill(UiSpinBoxArgs *args, UiBool fill) {
+    args->fill = fill;
+}
+
+void ui_spinbox_args_set_hexpand(UiSpinBoxArgs *args, UiBool value) {
+    args->hexpand = value;
+}
+
+void ui_spinbox_args_set_vexpand(UiSpinBoxArgs *args, UiBool value) {
+    args->vexpand = value;
+}
+
+void ui_spinbox_args_set_hfill(UiSpinBoxArgs *args, UiBool value) {
+    args->hfill = value;
+}
+
+void ui_spinbox_args_set_vfill(UiSpinBoxArgs *args, UiBool value) {
+    args->vfill = value;
+}
+
+void ui_spinbox_args_set_override_defaults(UiSpinBoxArgs *args, UiBool value) {
+    args->override_defaults = value;
+}
+
+void ui_spinbox_args_set_colspan(UiSpinBoxArgs *args, int colspan) {
+    args->colspan = colspan;
+}
+
+void ui_spinbox_args_set_rowspan(UiSpinBoxArgs *args, int rowspan) {
+    args->rowspan = rowspan;
+}
+
+void ui_spinbox_args_set_name(UiSpinBoxArgs *args, const char *name) {
+    args->name = strdup(name);
+}
+
+void ui_spinbox_args_set_style_class(UiSpinBoxArgs *args, const char *classname) {
+    args->style_class = strdup(classname);
+}
+
+void ui_spinbox_args_set_onchange(UiSpinBoxArgs *args, ui_callback callback) {
+    args->onchange = callback;
+}
+
+void ui_spinbox_args_set_onchangedata(UiSpinBoxArgs *args, void *onchangedata) {
+    args->onchangedata = onchangedata;
+}
+
+void ui_spinbox_args_set_min(UiSpinBoxArgs *args, double min) {
+    args->min = min;
+}
+
+void ui_spinbox_args_set_max(UiSpinBoxArgs *args, double max) {
+    args->max = max;
+}
+
+void ui_spinbox_args_set_step(UiSpinBoxArgs *args, double step) {
+    args->step = step;
+}
+
+void ui_spinbox_args_set_digits(UiSpinBoxArgs *args, int digits) {
+    args->digits;
+}
+
+void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varname) {
+    args->varname = strdup(varname);
+}
+
+void ui_spinbox_args_set_intvalue(UiSpinBoxArgs *args, UiInteger *value) {
+    args->intvalue = value;
+}
+
+void ui_spinbox_args_set_doublevalue(UiSpinBoxArgs *args, UiDouble *value) {
+    args->doublevalue = value;
+}
+
+void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value) {
+    args->rangevalue = value;
+}
+
+void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
+}
+
+void ui_spinbox_args_free(UiSpinBoxArgs *args) {
+    free((void*)args->name);
+    free((void*)args->style_class);
+    free((void*)args->varname);
+    free((void*)args->groups);
+    free(args);
+}
+
+
 /* ------------------------- UiWebviewArgs ----------------------------*/
 
 UiWebviewArgs* ui_webview_args_new(void) {
@@ -1955,7 +2090,7 @@
 
 
 void ui_webview_args_set_fill(UiWebviewArgs *args, UiBool fill) {
-    args->fill = fill ? UI_ON : UI_OFF;
+    args->fill = fill;
 }
 
 
@@ -2011,8 +2146,10 @@
     args->value = value;
 }
 
-void ui_webview_args_set_groups(UiWebviewArgs *args, int *groups){
-    // TODO
+void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates) {
+    args->groups = calloc(numstates+1, sizeof(int));
+    memcpy((void*)args->groups, states, numstates * sizeof(int));
+    ((int*)args->groups)[numstates] = -1;
 }
 
 void ui_webview_args_free(UiWebviewArgs *args) {
--- a/ui/common/args.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/common/args.h	Thu Oct 02 14:49:27 2025 +0200
@@ -33,6 +33,7 @@
 #include "../ui/container.h"
 #include "../ui/display.h"
 #include "../ui/button.h"
+#include "../ui/entry.h"
 #include "../ui/menu.h"
 #include "../ui/toolbar.h"
 #include "../ui/tree.h"
@@ -108,7 +109,7 @@
 UIEXPORT void ui_toolbar_item_args_set_icon(UiToolbarItemArgs *args, const char *icon);
 UIEXPORT void ui_toolbar_item_args_set_onclick(UiToolbarItemArgs *args, ui_callback callback);
 UIEXPORT void ui_toolbar_item_args_set_onclickdata(UiToolbarItemArgs *args, void *onclickdata);
-UIEXPORT void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *groups);
+UIEXPORT void ui_toolbar_item_args_set_groups(UiToolbarItemArgs *args, int *states, int numstates);
 UIEXPORT void ui_toolbar_item_args_free(UiToolbarItemArgs *args);
 
 UIEXPORT UiToolbarToggleItemArgs* ui_toolbar_toggleitem_args_new(void);
@@ -118,7 +119,7 @@
 UIEXPORT void ui_toolbar_toggleitem_args_set_varname(UiToolbarToggleItemArgs *args, const char *varname);
 UIEXPORT void ui_toolbar_toggleitem_args_set_onchange(UiToolbarToggleItemArgs *args, ui_callback callback);
 UIEXPORT void ui_toolbar_toggleitem_args_set_onchangedata(UiToolbarToggleItemArgs *args, void *onchangedata);
-UIEXPORT void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *groups);
+UIEXPORT void ui_toolbar_toggleitem_args_set_groups(UiToolbarToggleItemArgs *args, int *states, int numstates);
 UIEXPORT void ui_toolbar_toggleitem_args_free(UiToolbarToggleItemArgs *args);
 
 UIEXPORT UiToolbarMenuArgs* ui_toolbar_menu_args_new(void);
@@ -190,6 +191,7 @@
 UIEXPORT void ui_splitpane_args_set_columnspacing(UiSplitPaneArgs *args, int value);
 UIEXPORT void ui_splitpane_args_set_rowspacing(UiSplitPaneArgs *args, int value);
 UIEXPORT void ui_splitpane_args_set_initial_position(UiSplitPaneArgs *args, int pos);
+UIEXPORT void ui_splitpane_args_set_position_property(UiSplitPaneArgs *args, const char *propname);
 UIEXPORT void ui_splitpane_args_set_varname(UiSplitPaneArgs *args, const char *varname);
 UIEXPORT void ui_splitpane_args_set_value(UiSplitPaneArgs *args, UiInteger *value);
 UIEXPORT void ui_splitpane_args_set_max_panes(UiSplitPaneArgs *args, int max);
@@ -298,7 +300,7 @@
 UIEXPORT void ui_button_args_set_labeltype(UiButtonArgs *args, int labeltype);
 UIEXPORT void ui_button_args_set_onclick(UiButtonArgs *args, ui_callback callback);
 UIEXPORT void ui_button_args_set_onclickdata(UiButtonArgs *args, void *onclickdata);
-UIEXPORT void ui_button_args_set_groups(UiButtonArgs *args, int *groups);
+UIEXPORT void ui_button_args_set_groups(UiButtonArgs *args, int *states, int numstates);
 UIEXPORT void ui_button_args_free(UiButtonArgs *args);
 
 UIEXPORT UiToggleArgs* ui_toggle_args_new(void);
@@ -321,7 +323,7 @@
 UIEXPORT void ui_toggle_args_set_varname(UiToggleArgs *args, const char *varname);
 UIEXPORT void ui_toggle_args_set_value(UiToggleArgs *args, UiInteger *value);
 UIEXPORT void ui_toggle_args_set_enablegroup(UiToggleArgs *args, int group);
-UIEXPORT void ui_toggle_args_set_groups(UiToggleArgs *args, int *groups);
+UIEXPORT void ui_toggle_args_set_groups(UiToggleArgs *args, int *states, int numstates);
 UIEXPORT void ui_toggle_args_free(UiToggleArgs *args);
 
 UIEXPORT UiLinkButtonArgs* ui_linkbutton_args_new(void);
@@ -343,7 +345,7 @@
 UIEXPORT void ui_linkbutton_args_set_onclickdata(UiLinkButtonArgs *args, void *userdata);
 UIEXPORT void ui_linkbutton_args_set_nofollow(UiLinkButtonArgs *args, UiBool value);
 UIEXPORT void ui_linkbutton_args_set_type(UiLinkButtonArgs *args, UiLinkType type);
-UIEXPORT void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *groups);
+UIEXPORT void ui_linkbutton_args_set_groups(UiLinkButtonArgs *args, int *states, int numstates);
 UIEXPORT void ui_linkbutton_args_free(UiLinkButtonArgs *args);
 
 UIEXPORT UiListArgs* ui_list_args_new(void);
@@ -376,9 +378,11 @@
 UIEXPORT void ui_list_args_set_ondragcompletedata(UiListArgs *args, void *userdata);
 UIEXPORT void ui_list_args_set_ondrop(UiListArgs *args, ui_callback callback);
 UIEXPORT void ui_list_args_set_ondropdata(UiListArgs *args, void *userdata);
+UIEXPORT void ui_list_args_set_onsave(UiListArgs *args, ui_list_savefunc onsave);
+UIEXPORT void ui_list_args_set_onsavedata(UiListArgs *args, void *userdata);
 UIEXPORT void ui_list_args_set_multiselection(UiListArgs *args, UiBool multiselection);
 UIEXPORT void ui_list_args_set_contextmenu(UiListArgs *args, UiMenuBuilder *menubuilder);
-UIEXPORT void ui_list_args_set_groups(UiListArgs *args, int *groups);
+UIEXPORT void ui_list_args_set_groups(UiListArgs *args, int *states, int numstates);
 UIEXPORT void ui_list_args_free(UiListArgs *args);
 
 UIEXPORT UiSourceListArgs* ui_sourcelist_args_new(void);
@@ -419,7 +423,7 @@
 UIEXPORT void ui_textarea_args_set_onchangedata(UiTextAreaArgs *args, void *onchangedata);
 UIEXPORT void ui_textarea_args_set_varname(UiTextAreaArgs *args, const char *varname);
 UIEXPORT void ui_textarea_args_set_value(UiTextAreaArgs *args, UiText *value);
-UIEXPORT void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *groups);
+UIEXPORT void ui_textarea_args_set_groups(UiTextAreaArgs *args, int *states, int numstates);
 UIEXPORT void ui_textarea_args_free(UiTextAreaArgs *args);
 
 UIEXPORT UiTextFieldArgs* ui_textfield_args_new(void);
@@ -439,9 +443,33 @@
 UIEXPORT void ui_textfield_args_set_onactivatedata(UiTextFieldArgs *args, void *onactivatedata);
 UIEXPORT void ui_textfield_args_set_varname(UiTextFieldArgs *args, const char *varname);
 UIEXPORT void ui_textfield_args_set_value(UiTextFieldArgs *args, UiString *value);
-UIEXPORT void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *groups);
+UIEXPORT void ui_textfield_args_set_groups(UiTextFieldArgs *args, int *states, int numstates);
 UIEXPORT void ui_textfield_args_free(UiTextFieldArgs *args);
 
+UIEXPORT UiSpinBoxArgs* ui_spinbox_args_new(void);
+UIEXPORT void ui_spinbox_args_set_fill(UiSpinBoxArgs *args, UiBool fill);
+UIEXPORT void ui_spinbox_args_set_hexpand(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_vexpand(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_hfill(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_vfill(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_override_defaults(UiSpinBoxArgs *args, UiBool value);
+UIEXPORT void ui_spinbox_args_set_colspan(UiSpinBoxArgs *args, int colspan);
+UIEXPORT void ui_spinbox_args_set_rowspan(UiSpinBoxArgs *args, int rowspan);
+UIEXPORT void ui_spinbox_args_set_name(UiSpinBoxArgs *args, const char *name);
+UIEXPORT void ui_spinbox_args_set_style_class(UiSpinBoxArgs *args, const char *classname);
+UIEXPORT void ui_spinbox_args_set_onchange(UiSpinBoxArgs *args, ui_callback callback);
+UIEXPORT void ui_spinbox_args_set_onchangedata(UiSpinBoxArgs *args, void *onchangedata);
+UIEXPORT void ui_spinbox_args_set_min(UiSpinBoxArgs *args, double min);
+UIEXPORT void ui_spinbox_args_set_max(UiSpinBoxArgs *args, double max);
+UIEXPORT void ui_spinbox_args_set_step(UiSpinBoxArgs *args, double step);
+UIEXPORT void ui_spinbox_args_set_digits(UiSpinBoxArgs *args, int digits);
+UIEXPORT void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varname);
+UIEXPORT void ui_spinbox_args_set_intvalue(UiSpinBoxArgs *args, UiInteger *value);
+UIEXPORT void ui_spinbox_args_set_doublevalue(UiSpinBoxArgs *args, UiDouble *value);
+UIEXPORT void ui_spinbox_args_set_rangevalue(UiSpinBoxArgs *args, UiRange *value);
+UIEXPORT void ui_spinbox_args_set_groups(UiSpinBoxArgs *args, int *states, int numstates);
+UIEXPORT void ui_spinbox_args_free(UiSpinBoxArgs *args);
+
 UIEXPORT UiWebviewArgs* ui_webview_args_new(void);
 UIEXPORT void ui_webview_args_set_fill(UiWebviewArgs *args, UiBool fill);
 UIEXPORT void ui_webview_args_set_hexpand(UiWebviewArgs *args, UiBool value);
@@ -455,7 +483,7 @@
 UIEXPORT void ui_webview_args_set_style_class(UiWebviewArgs *args, const char *classname);
 UIEXPORT void ui_webview_args_set_varname(UiWebviewArgs *args, const char *varname);
 UIEXPORT void ui_webview_args_set_value(UiWebviewArgs *args, UiGeneric *value);
-UIEXPORT void ui_webview_args_set_groups(UiWebviewArgs *args, int *groups);
+UIEXPORT void ui_webview_args_set_groups(UiWebviewArgs *args, int *states, int numstates);
 UIEXPORT void ui_webview_args_free(UiWebviewArgs *args);
 
 #ifdef __cplusplus
--- a/ui/common/context.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/common/context.c	Thu Oct 02 14:49:27 2025 +0200
@@ -542,6 +542,9 @@
 }
 
 void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...) {
+    if(enable == NULL) {
+        enable = (ui_enablefunc)ui_set_enabled;
+    }
     // get groups
     CxList *groups = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), 16);
     va_list ap;
@@ -557,6 +560,22 @@
     cxListFree(groups);
 }
 
+void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, int *groups, int ngroups) {
+    if(enable == NULL) {
+        enable = (ui_enablefunc)ui_set_enabled;
+    }
+    CxList *ls = cxArrayListCreate(cxDefaultAllocator, NULL, sizeof(int), ngroups);
+    for(int i=0;i<ngroups;i++) {
+        cxListAdd(ls, groups+i);
+    }
+    uic_add_group_widget(ctx, widget, enable, ls);
+    cxListFree(ls);
+}
+
+void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, int *states, int nstates) {
+    ui_widget_set_groups2(ctx, widget, (ui_enablefunc)ui_set_visible, states, nstates);
+}
+
 size_t uic_group_array_size(const int *groups) {
     int i;
     for(i=0;groups[i] >= 0;i++) { }
--- a/ui/common/properties.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/common/properties.c	Thu Oct 02 14:49:27 2025 +0200
@@ -73,7 +73,7 @@
 #define UI_ENV_HOME "USERPROFILE"
 #endif
 
-char* ui_configfile(char *name) {
+char* ui_configfile(const char *name) {
     const char *appname = ui_appname();
     if(!appname) {
         return NULL;
@@ -128,6 +128,7 @@
 
 void uic_load_app_properties() {
     application_properties = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 128);
+    application_properties->collection.simple_destructor = free;
     
     if(!ui_appname()) {
         // applications without name cannot load app properties
@@ -202,7 +203,11 @@
 }
 
 void ui_set_property(const char *name, const char *value) {
-    cxMapPut(application_properties, name, (char*)value);
+    if(value) {
+        cxMapPut(application_properties, name, strdup(value));
+    } else {
+        cxMapRemove(application_properties, name);
+    }
 }
 
 const char* ui_set_default_property(const char *name, const char *value) {
@@ -241,12 +246,14 @@
 
 #ifndef UI_COCOA
 
-void ui_locales_dir(char *path) {
-    locales_dir = path;
+void ui_locales_dir(const char *path) {
+    free(locales_dir);
+    locales_dir = path ? strdup(path) : NULL;
 }
 
-void ui_pixmaps_dir(char *path) {
-    pixmaps_dir = path;
+void ui_pixmaps_dir(const char *path) {
+    free(pixmaps_dir);
+    pixmaps_dir = path ? strdup(path) : NULL;
 }
 
 char* uic_get_image_path(const char *imgfilename) {
@@ -257,7 +264,7 @@
     }
 }
 
-void ui_load_lang(char *locale) {
+void ui_load_lang(const char *locale) {
     if(!locale) {
         locale = "en_EN";
     }
@@ -319,12 +326,12 @@
     return 0;
 }
 
-char* uistr(char *name) {
+char* uistr(const char *name) {
     char *value = uistr_n(name);
     return value ? value : "missing string";
 }
 
-char* uistr_n(char *name) {
+char* uistr_n(const char *name) {
     if(!language) {
         return NULL;
     }
--- a/ui/common/types.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/common/types.c	Thu Oct 02 14:49:27 2025 +0200
@@ -417,7 +417,7 @@
         return s->get ? s->get(s) : s->value.ptr;
     }
     else {
-        return 0;
+        return NULL;
     }
 }
 
@@ -447,6 +447,69 @@
         return s->get ? s->get(s) : s->value.ptr;
     }
     else {
+        return NULL;
+    }
+}
+
+void ui_range_set(UiRange *r, double value) {
+    if (r) {
+        if (r->set) {
+            r->set(r, value);
+        } else {
+            r->value = value;
+        }
+    }
+}
+
+void ui_range_set_range(UiRange *r, double min, double max) {
+    if (r) {
+        if (r->setrange) {
+            r->setrange(r, min, max);
+        } else {
+            r->min = min;
+            r->max = max;
+        }
+    }
+}
+
+void ui_range_set_extent(UiRange *r, double extent) {
+    if (r) {
+        if (r->setextent) {
+            r->setextent(r, extent);
+        } else {
+            r->extent = extent;
+        }
+    }
+}
+
+double ui_range_get(UiRange *r) {
+    if (r) {
+        return r->get ? r->get(r) : r->value;
+    } else {
+        return 0;
+    }
+}
+
+double ui_range_get_min(UiRange *r) {
+    if (r) {
+        return r->min;
+    } else {
+        return 0;
+    }
+}
+
+double ui_range_get_max(UiRange *r) {
+    if (r) {
+        return r->max;
+    } else {
+        return 0;
+    }
+}
+
+double ui_range_get_extent(UiRange *r) {
+    if (r) {
+        return r->extent;
+    } else {
         return 0;
     }
 }
--- a/ui/common/wrapper.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/common/wrapper.c	Thu Oct 02 14:49:27 2025 +0200
@@ -299,3 +299,22 @@
 void ui_textstyle_enable_color(UiTextStyle *style, UiBool enable) {
     style->fg_set = enable;
 }
+
+
+/* ---------------------------- UiCellValue ---------------------------- */
+
+UiBool ui_cell_value_is_string(UiCellValue *value) {
+   return value->type == UI_STRING_EDITABLE;
+}
+
+UiBool ui_cell_value_is_int(UiCellValue *value) {
+    return FALSE; // TODO
+}
+
+const char* ui_cell_value_get_string(UiCellValue *value) {
+    return value->string;
+}
+
+int64_t ui_cell_value_get_int(UiCellValue *value) {
+    return value->i;
+}
--- a/ui/common/wrapper.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/common/wrapper.h	Thu Oct 02 14:49:27 2025 +0200
@@ -89,6 +89,11 @@
 UIEXPORT void ui_textstyle_set_color(UiTextStyle *style, int r, int g, int b);
 UIEXPORT void ui_textstyle_enable_color(UiTextStyle *style, UiBool enable);
 
+UIEXPORT UiBool ui_cell_value_is_string(UiCellValue *value);
+UIEXPORT UiBool ui_cell_value_is_int(UiCellValue *value);
+UIEXPORT const char* ui_cell_value_get_string(UiCellValue *value);
+UIEXPORT int64_t ui_cell_value_get_int(UiCellValue *value);
+
 
 #ifdef __cplusplus
 }
--- a/ui/gtk/button.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/button.c	Thu Oct 02 14:49:27 2025 +0200
@@ -382,13 +382,98 @@
 }
 #endif
 
+
+#if GTK_MAJOR_VERSION >= 3
+
+static void switch_changed(
+        GObject *gobject,
+        GParamSpec *pspec,
+        UiVarEventData *event)
+{
+    GtkSwitch *sw = GTK_SWITCH (gobject);
+    gboolean active = gtk_switch_get_active (sw);
+    
+    UiEvent e;
+    e.obj = event->obj;
+    e.document = e.obj->ctx->document;
+    e.window = e.obj->window;
+    e.eventdata = NULL;
+    e.eventdatatype = 0;
+    e.set = ui_get_setop();
+    
+    if(event->callback) {
+        event->callback(&e, event->userdata);
+    }
+    if(event->var) {
+        UiInteger *i = event->var->value;
+        ui_notify_evt(i->observers, &e);
+    }
+}
+
 UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
-#ifdef UI_GTK3
-    return NULL; // TODO
+    UiObject* current = uic_current_obj(obj);
+    GtkWidget *widget = gtk_switch_new();
+    ui_set_name_and_style(widget, args->name, args->style_class);
+    ui_set_widget_groups(obj->ctx, widget, args->groups);
+    
+    UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_INTEGER);
+    if(var) {
+        UiInteger *value = var->value;
+        value->obj = widget;
+        value->get = ui_switch_get;
+        value->set = ui_switch_set;
+        
+        if(value->value) {
+            gtk_switch_set_active(GTK_SWITCH(widget), TRUE);
+        }
+        
+        
+    }
+    
+    UiVarEventData *event = malloc(sizeof(UiVarEventData));
+    event->obj = obj;
+    event->callback = args->onchange;
+    event->userdata = args->onchangedata;
+    event->var = var;
+    event->observers = NULL;
+    
+    g_signal_connect(
+            widget,
+            "notify::active",
+            G_CALLBACK(switch_changed),
+            event);
+    
+    g_signal_connect(
+            widget,
+            "destroy",
+            G_CALLBACK(ui_destroy_vardata),
+            event);
+    
+    UI_APPLY_LAYOUT2(current, args);
+    current->container->add(current->container, widget);
+    
+    return widget;
+}
+
+int64_t ui_switch_get(UiInteger *value) {
+    GtkSwitch *sw = GTK_SWITCH((GtkWidget*)value->obj);
+    value->value = gtk_switch_get_active(sw);
+    return value->value;
+}
+
+void ui_switch_set(UiInteger *value, int64_t i) {
+    GtkSwitch *sw = GTK_SWITCH((GtkWidget*)value->obj);
+    value->value = i;
+    gtk_switch_set_active(sw, i);
+}
+    
 #else
+
+UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs *args) {
     return ui_checkbox_create(obj, args);
+}
+
 #endif
-}
 
 #if GTK_MAJOR_VERSION >= 4
 #define RADIOBUTTON_NEW(group, label) gtk_check_button_new_with_label(label)
--- a/ui/gtk/button.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/button.h	Thu Oct 02 14:49:27 2025 +0200
@@ -96,6 +96,9 @@
 
 void ui_radio_obs(GtkToggleButton *widget, UiVarEventData *event);
 
+int64_t ui_switch_get(UiInteger *value);
+void ui_switch_set(UiInteger *value, int64_t i);
+
 int64_t ui_radiobutton_get(UiInteger *value);
 void ui_radiobutton_set(UiInteger *value, int64_t i);
 
--- a/ui/gtk/container.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/container.c	Thu Oct 02 14:49:27 2025 +0200
@@ -28,6 +28,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <limits.h>
 
 #include "container.h"
@@ -37,6 +38,8 @@
 #include "../common/context.h"
 #include "../common/object.h"
 
+#include "../ui/properties.h"
+
 
 void ui_container_begin_close(UiObject *obj) {
     UiContainer *ct = uic_get_current_container(obj);
@@ -1050,6 +1053,34 @@
 }
 #endif
 
+/* ------------------------ Split Window Panels ------------------------ */
+
+static UIWIDGET splitwindow_panel(UiObject *obj, GtkWidget *pbox, UiSidebarArgs *args) {
+    if(!pbox) {
+        fprintf(stderr, "Error: window is not a splitview window\n");
+        return NULL;
+    }
+    
+    GtkWidget *box = ui_gtk_vbox_new(args->spacing);
+    ui_box_set_margin(box, args->margin);
+    BOX_ADD_EXPAND(pbox, box);
+    
+    UiObject *newobj = uic_object_new(obj, box);
+    newobj->container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
+    uic_obj_add(obj, newobj);
+    
+    return box;
+}
+
+UIWIDGET ui_left_panel_create(UiObject *obj, UiSidebarArgs *args) {
+    return splitwindow_panel(obj, g_object_get_data(G_OBJECT(obj->widget), "ui_left_panel"), args);
+}
+
+UIWIDGET ui_right_panel_create(UiObject *obj, UiSidebarArgs *args) {
+    return splitwindow_panel(obj, g_object_get_data(G_OBJECT(obj->widget), "ui_right_panel"), args);
+}
+
+
 /* -------------------- Splitpane -------------------- */
 
 static GtkWidget* create_paned(UiOrientation orientation) {
@@ -1067,7 +1098,13 @@
     return NULL;
 }
 
-
+static void save_pane_pos(GtkWidget *widget, char *property_name) {
+    int pos = gtk_paned_get_position(GTK_PANED(widget));
+    char buf[32];
+    snprintf(buf, 32, "%d", pos);
+    ui_set_property(property_name, buf);
+    free(property_name);
+}
 
 static UIWIDGET splitpane_create(UiObject *obj, UiOrientation orientation, UiSplitPaneArgs *args) {
     UiObject* current = uic_current_obj(obj);
@@ -1079,12 +1116,40 @@
     
     int max = args->max_panes == 0 ? 2 : args->max_panes;
     
+    if(args->position_property) {
+        const char *pos_str = ui_get_property(args->position_property);
+        if(pos_str) {
+            char *end;
+            long pos = strtol(pos_str, &end, 10);
+            if(*end == '\0') {
+                args->initial_position = (int)pos;
+            }
+        }
+        
+        g_signal_connect(
+                pane0,
+                "destroy",
+                G_CALLBACK(save_pane_pos),
+                strdup(args->position_property));
+    }
+    
     UiObject *newobj = uic_object_new(obj, pane0);
     newobj->container = ui_splitpane_container(obj, pane0, orientation, max, args->initial_position);
     uic_obj_add(obj, newobj);
     
     g_object_set_data(G_OBJECT(pane0), "ui_splitpane", newobj->container);
     
+    UiVar *var = uic_widget_var(obj->ctx, current->ctx, args->value, args->varname, UI_VAR_INTEGER);
+    if(var) {
+        UiSplitPaneContainer *s = (UiSplitPaneContainer*)newobj->container;
+        UiInteger *i = var->value;
+        s->initial_position = i->value;
+        
+        i->obj = s;
+        i->get = ui_splitpane_get;
+        i->set = ui_splitpane_set;
+    }
+    
     return pane0;
 }
 
@@ -1140,6 +1205,18 @@
     }
 }
 
+int64_t ui_splitpane_get(UiInteger *i) {
+    UiSplitPaneContainer *s = i->obj;
+    i->value = gtk_paned_get_position(GTK_PANED(s->container.widget));
+    return i->value;
+}
+
+void ui_splitpane_set(UiInteger *i, int64_t value) {
+    UiSplitPaneContainer *s = i->obj;
+    i->value = value;
+    gtk_paned_set_position(GTK_PANED(s->container.widget), (int)value);
+}
+
 UIEXPORT void ui_splitpane_set_visible(UIWIDGET splitpane, int child_index, UiBool visible) {
     UiSplitPaneContainer *ct = g_object_get_data(G_OBJECT(splitpane), "ui_splitpane");
     if(!ct) {
--- a/ui/gtk/container.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/container.h	Thu Oct 02 14:49:27 2025 +0200
@@ -197,6 +197,8 @@
 UiContainer* ui_splitpane_container(UiObject *obj, GtkWidget *pane, UiOrientation orientation, int max, int init);
 void ui_splitpane_container_add(UiContainer *ct, GtkWidget *widget);
 
+int64_t ui_splitpane_get(UiInteger *i);
+void ui_splitpane_set(UiInteger *i, int64_t value);
 
 UiGtkTabView* ui_widget_get_tabview_data(UIWIDGET tabview);
 
--- a/ui/gtk/entry.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/entry.c	Thu Oct 02 14:49:27 2025 +0200
@@ -35,28 +35,38 @@
 #include "entry.h"
 
 
-UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
-    double min = 0;
-    double max = 1000;
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
+    double min = args->min;
+    double max = args->max != 0 ? args->max : 1000;
     
     UiObject* current = uic_current_obj(obj);
     
     UiVar *var = NULL;
+    UiVarType vartype = 0;
     if(args->varname) {
         var = uic_get_var(obj->ctx, args->varname);
+        if(var) {
+            vartype = var->type;
+        } else {
+            var = uic_widget_var(obj->ctx, current->ctx, args->rangevalue, args->varname, UI_VAR_RANGE);
+            vartype = UI_VAR_RANGE;
+        }
     }
     
     if(!var) {
         if(args->intvalue) {
             var = uic_widget_var(obj->ctx, current->ctx, args->intvalue, NULL, UI_VAR_INTEGER);
+            vartype = UI_VAR_INTEGER;
         } else if(args->doublevalue) {
             var = uic_widget_var(obj->ctx, current->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE);
+            vartype = UI_VAR_DOUBLE;
         } else if(args->rangevalue) {
             var = uic_widget_var(obj->ctx, current->ctx, args->rangevalue, NULL, UI_VAR_RANGE);
+            vartype = UI_VAR_RANGE;
         }
     }
     
-    if(var && var->type == UI_VAR_RANGE) {
+    if(vartype == UI_VAR_RANGE) {
         UiRange *r = var->value;
         min = r->min;
         max = r->max;
@@ -76,7 +86,7 @@
     UiObserver **obs = NULL;
     if(var) {
         double value = 0;
-        switch(var->type) {
+        switch(vartype) {
             default: break;
             case UI_VAR_INTEGER: {
                 UiInteger *i = var->value;
--- a/ui/gtk/list.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/list.c	Thu Oct 02 14:49:27 2025 +0200
@@ -95,9 +95,8 @@
     tableview->current_row = -1;
     tableview->getstyle = args->getstyle;
     tableview->getstyledata = args->getstyledata;
-#if GTK_CHECK_VERSION(4, 10, 0)
-    tableview->default_attributes = pango_attr_list_new();
-#endif
+    tableview->onsave = args->onsave;
+    tableview->onsavedata = args->onsavedata;
     
     if(args->getvalue2) {
         tableview->getvalue = args->getvalue2;
@@ -146,6 +145,58 @@
 
 /* END GObject wrapper for generic pointers */
 
+typedef struct UiCellEntry {
+    GtkEntry *entry;
+    UiListView *listview;
+    char *previous_value;
+    int row;
+    int col;
+} UiCellEntry;
+
+static void cell_save_value(UiCellEntry *data, int restore) {
+    if(data->listview && data->listview->onsave) {
+        UiVar *var = data->listview->var;
+        UiList *list = var ? var->value : NULL;
+        const char *str = ENTRY_GET_TEXT(data->entry);
+        UiCellValue value;
+        value.string = str;
+        value.type = UI_STRING_EDITABLE;
+        if(data->listview->onsave(list, data->row, data->col, &value, data->listview->onsavedata)) {
+            free(data->previous_value);
+            data->previous_value = strdup(str);
+        } else if(restore) {
+            ENTRY_SET_TEXT(data->entry, data->previous_value);
+        }
+    }
+}
+
+static void cell_entry_leave_focus(
+        GtkEventControllerFocus *self,
+        UiCellEntry *data)
+{
+    // TODO: use a different singal to track focus
+    //       we only want to call cell_save_value, when another entry is selected,
+    //       not when the window loses focus or something like that
+    cell_save_value(data, TRUE);
+}
+
+static void cell_entry_destroy(GtkWidget *object, UiCellEntry *data) {
+    free(data->previous_value);
+    free(data);
+}
+
+static void cell_entry_unmap(GtkWidget *w, UiCellEntry *data) {
+    const char *text = ENTRY_GET_TEXT(w);
+    cell_save_value(data, FALSE);
+}
+
+static void cell_entry_activate(
+        GtkEntry *self,
+        UiCellEntry *data)
+{
+    cell_save_value(data, TRUE);
+}
+
 static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
     UiColData *col = userdata;
     UiModel *model = col->listview->model;
@@ -162,6 +213,38 @@
     } else if(type == UI_ICON) {
         GtkWidget *image = gtk_image_new();
         gtk_list_item_set_child(item, image);
+    } else if(type == UI_STRING_EDITABLE) {
+        GtkWidget *textfield = gtk_entry_new();
+        gtk_widget_add_css_class(textfield, "ui-table-entry");
+        gtk_list_item_set_child(item, textfield);
+        
+        UiCellEntry *entry_data = malloc(sizeof(UiCellEntry));
+        entry_data->entry = GTK_ENTRY(textfield);
+        entry_data->listview = NULL;
+        entry_data->previous_value = NULL;
+        entry_data->col = 0;
+        entry_data->row = 0;
+        g_object_set_data(G_OBJECT(textfield), "ui_entry_data", entry_data);
+        
+        g_signal_connect(
+                textfield,
+                "destroy",
+                G_CALLBACK(cell_entry_destroy),
+                entry_data);
+        g_signal_connect(
+                textfield,
+                "activate",
+                G_CALLBACK(cell_entry_activate),
+                entry_data);
+        g_signal_connect(
+                textfield,
+                "unmap",
+                G_CALLBACK(cell_entry_unmap),
+                entry_data);
+        
+        GtkEventController *focus_controller = gtk_event_controller_focus_new();
+        g_signal_connect(focus_controller, "leave", G_CALLBACK(cell_entry_leave_focus), entry_data);
+        gtk_widget_add_controller(textfield, focus_controller);
     } else {
         GtkWidget *label = gtk_label_new(NULL);
         gtk_label_set_xalign(GTK_LABEL(label), 0);
@@ -296,6 +379,17 @@
             }
             break;
         }
+        case UI_STRING_EDITABLE: {
+            UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data");
+            if(entry) {
+                entry->listview = col->listview;
+                entry->row = obj->i;
+                entry->col = col->data_column;
+                entry->previous_value = strdup(data);
+            }
+            ENTRY_SET_TEXT(child, data);
+            break;
+        }
     }
     
     if(attributes != listview->current_row_attributes) {
@@ -315,6 +409,15 @@
             cxMapRemove(listview->bound_rows, row_key);
         }
     } // else: should not happen
+    
+    GtkWidget *child = gtk_list_item_get_child(item);
+    UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data");
+    if(entry) {
+        cell_save_value(entry, FALSE);
+        entry->listview = NULL;
+        free(entry->previous_value);
+        entry->previous_value = NULL;
+    }
 }
     
 
@@ -1908,7 +2011,6 @@
     }
 #if GTK_CHECK_VERSION(4, 10, 0)
     free(v->columns);
-    pango_attr_list_unref(v->default_attributes);
     pango_attr_list_unref(v->current_row_attributes);
     cxMapFree(v->bound_rows);
 #endif
@@ -2182,7 +2284,7 @@
     }
 }
 
-static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
+static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
     GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
     if(item->icon) {
         GtkWidget *icon = ICON_IMAGE(item->icon);
@@ -2194,7 +2296,6 @@
     if(item->badge) {
         
     }
-    GtkWidget *row = gtk_list_box_row_new();
     LISTBOX_ROW_SET_CHILD(row, hbox);
     
     // signals 
@@ -2245,8 +2346,36 @@
                 event
                 );
     }
+}
+
+static void update_sublist_item(UiListBox *listbox, UiListBoxSubList *sublist, int index) {
+    GtkListBoxRow *row = gtk_list_box_get_row_at_index(listbox->listbox, sublist->startpos + index);
+    if(!row) {
+        return;
+    }
+    UiList *list = sublist->var->value;
+    if(!list) {
+        return;
+    }
     
-    return row;
+    void *elm = list->get(list, index);
+    UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
+    if(listbox->getvalue) {
+        listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata);
+    } else {
+        item.label = strdup(elm);
+    }
+    
+    LISTBOX_ROW_REMOVE_CHILD(row);
+    
+    listbox_fill_row(listbox, GTK_WIDGET(row), sublist, &item, index);
+    
+     // cleanup
+    free(item.label);
+    free(item.icon);
+    free(item.button_label);
+    free(item.button_icon);
+    free(item.badge);
 }
 
 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) {
@@ -2293,7 +2422,8 @@
         }
         
         // create listbox item
-        GtkWidget *row = create_listbox_row(listbox, sublist, &item, (int)index);
+        GtkWidget *row = gtk_list_box_row_new();
+        listbox_fill_row(listbox, row, sublist, &item, (int)index);
         if(index == 0) {
             // first row in the sublist, set ui_listbox data to the row
             // which is then used by the headerfunc
@@ -2327,14 +2457,17 @@
 
 void ui_listbox_list_update(UiList *list, int i) {
     UiListBoxSubList *sublist = list->obj;
-    ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos);
-    size_t pos = 0;
-    CxIterator it = cxListIterator(sublist->listbox->sublists);
-    cx_foreach(UiListBoxSubList *, ls, it) {
-        ls->startpos = pos;
-        pos += sublist->numitems;
+    if(i < 0) {
+        ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos);
+        size_t pos = 0;
+        CxIterator it = cxListIterator(sublist->listbox->sublists);
+        cx_foreach(UiListBoxSubList *, ls, it) {
+            ls->startpos = pos;
+            pos += ls->numitems;
+        }
+    } else {
+        update_sublist_item(sublist->listbox, sublist, i);
     }
-    
 }
 
 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
--- a/ui/gtk/list.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/list.h	Thu Oct 02 14:49:27 2025 +0200
@@ -67,7 +67,6 @@
     GtkSelectionModel *selectionmodel;
     UiColData         *columns;
     int               numcolumns;
-    PangoAttrList     *default_attributes; // TODO: remove
     PangoAttrList     *current_row_attributes;
 #else
     int               style_offset;
@@ -82,6 +81,8 @@
     void              *ondragcompletedata;
     ui_callback       ondrop;
     void              *ondropdata;
+    ui_list_savefunc  onsave;
+    void              *onsavedata;
     UiListSelection   selection;
     
 } UiListView;
--- a/ui/gtk/toolkit.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/toolkit.c	Thu Oct 02 14:49:27 2025 +0200
@@ -407,6 +407,11 @@
 ".ui-nopadding {"
 "  padding: 0;"
 "}\n"
+".ui-table-entry {"
+"  border: none;"
+"  box-shadow: none;"
+"  background: transparent;"
+"}\n"
 ;
 
 #elif GTK_MAJOR_VERSION == 3
--- a/ui/gtk/toolkit.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/toolkit.h	Thu Oct 02 14:49:27 2025 +0200
@@ -74,6 +74,7 @@
 #define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon)
 #define LISTBOX_REMOVE(listbox, row) gtk_list_box_remove(GTK_LIST_BOX(listbox), row)
 #define LISTBOX_ROW_SET_CHILD(row, child) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), child)
+#define LISTBOX_ROW_REMOVE_CHILD(row) gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), NULL)
 #define PANED_SET_CHILD1(paned, child) gtk_paned_set_start_child(GTK_PANED(paned), child)
 #define PANED_SET_CHILD2(paned, child) gtk_paned_set_end_child(GTK_PANED(paned), child)
 #else
@@ -96,6 +97,7 @@
 #define ICON_IMAGE(icon) gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_BUTTON)
 #define LISTBOX_REMOVE(listbox, row) gtk_container_remove(GTK_CONTAINER(listbox), row)
 #define LISTBOX_ROW_SET_CHILD(row, child) gtk_container_add(GTK_CONTAINER(row), child)
+#define LISTBOX_ROW_REMOVE_CHILD(row) gtk_container_remove(GTK_CONTAINER(row), gtk_bin_get_child(GTK_BIN(row)))
 #define PANED_SET_CHILD1(paned, child) gtk_paned_pack1(GTK_PANED(paned), child, TRUE, TRUE)
 #define PANED_SET_CHILD2(paned, child) gtk_paned_pack2(GTK_PANED(paned), child, TRUE, TRUE)
 #endif
--- a/ui/gtk/window.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/gtk/window.c	Thu Oct 02 14:49:27 2025 +0200
@@ -77,6 +77,36 @@
 }
 
 static gboolean ui_window_close_request(UiObject *obj) {
+    if(obj->widget) {
+        void *appwindow = g_object_get_data(G_OBJECT(obj->widget), "ui.appwindow");
+        if(appwindow) {
+            int width = 0;
+            int height = 0;
+#if GTK_CHECK_VERSION(4, 10, 0)
+            graphene_rect_t bounds;
+            if(gtk_widget_compute_bounds(obj->widget, obj->widget, &bounds)) {
+                width = bounds.size.width;
+                height = bounds.size.height;
+            }
+#elif GTK_CHECK_VERSION(4, 0, 0)
+            GtkAllocation alloc;
+            gtk_widget_get_allocation(GTK_WIDGET(obj->widget), &alloc);
+            width = alloc.width;
+            height = alloc.height;
+#else
+            gtk_window_get_size(GTK_WINDOW(obj->widget), &width, &height);
+#endif
+            if(width > 0 && height > 0) {
+                char width_str[32];
+                char height_str[32];
+                snprintf(width_str, 32, "%d", width);
+                snprintf(height_str, 32, "%d", height);
+                ui_set_property("ui.window.width", width_str);
+                ui_set_property("ui.window.height", height_str);
+            }
+        }
+    }
+    
     uic_context_prepare_close(obj->ctx);
     obj->ref--;
     if(obj->ref > 0) {
@@ -101,7 +131,7 @@
 }
 #endif
 
-static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool simple) {
+static UiObject* create_window(const char *title, void *window_data, UiBool sidebar, UiBool splitview, UiBool simple) {
     UiObject *obj = uic_object_new_toplevel();
    
 #ifdef UI_LIBADWAITA
@@ -122,19 +152,28 @@
         gtk_window_set_title(GTK_WINDOW(obj->widget), title);
     }
     
-    const char *width = ui_get_property("ui.window.width");
-    const char *height = ui_get_property("ui.window.height");
-    if(width && height) {
-        gtk_window_set_default_size(
+    if(!simple) {
+        g_object_set_data(G_OBJECT(obj->widget), "ui.appwindow", obj);
+    }
+    
+    int window_width = window_default_width;
+    int window_height = window_default_height;
+    if(!simple) {
+        const char *width = ui_get_property("ui.window.width");
+        const char *height = ui_get_property("ui.window.height");
+        if(width && height) {
+            int w = atoi(width);
+            int h = atoi(height);
+            if(w > 0 && h > 0) {
+                window_width = w;
+                window_height = h;
+            }
+        }
+    }
+    gtk_window_set_default_size(
                 GTK_WINDOW(obj->widget),
-                atoi(width),
-                atoi(height));
-    } else {
-        gtk_window_set_default_size(
-                GTK_WINDOW(obj->widget),
-                window_default_width + sidebar*250,
-                window_default_height);
-    }
+                window_width,
+                window_height);
     
     obj->destroy = ui_window_widget_destroy;
     g_signal_connect(
@@ -161,50 +200,73 @@
     GtkWidget *toolbar_view = adw_toolbar_view_new();
     adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(toolbar_view), vbox);
     
+    GtkWidget *headerbar_sidebar = NULL;
+    GtkWidget *headerbar_main = adw_header_bar_new();
+    GtkWidget *headerbar_right = NULL;
+    
+    GtkWidget *content = toolbar_view;
+    if(splitview) {
+        content = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
+        
+        GtkWidget *right_panel = adw_toolbar_view_new();
+        GtkWidget *right_vbox = ui_gtk_vbox_new(0);
+        adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(right_panel), right_vbox);
+        
+        headerbar_right = adw_header_bar_new();
+        adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_right), FALSE);
+        adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(right_panel), headerbar_right);
+        
+        adw_header_bar_set_show_end_title_buttons(ADW_HEADER_BAR(headerbar_main), FALSE);
+        
+        gtk_paned_set_start_child(GTK_PANED(content), toolbar_view);
+        gtk_paned_set_end_child(GTK_PANED(content), right_panel);
+        
+        g_object_set_data(G_OBJECT(obj->widget), "ui_window_splitview", content);
+        g_object_set_data(G_OBJECT(obj->widget), "ui_left_panel", vbox);
+        g_object_set_data(G_OBJECT(obj->widget), "ui_right_panel", right_vbox);
+    }
+    
     GtkWidget *content_box = ui_gtk_vbox_new(0);
     BOX_ADD_EXPAND(GTK_BOX(vbox), content_box);
     
-    GtkWidget *sidebar_headerbar = NULL; 
     if(sidebar) {
         GtkWidget *splitview = adw_overlay_split_view_new();
         adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), splitview);
         
         GtkWidget *sidebar_toolbar_view = adw_toolbar_view_new();
         adw_overlay_split_view_set_sidebar(ADW_OVERLAY_SPLIT_VIEW(splitview), sidebar_toolbar_view);
-        sidebar_headerbar = adw_header_bar_new();
-        adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), sidebar_headerbar);
+        headerbar_sidebar = adw_header_bar_new();
+        adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(sidebar_toolbar_view), headerbar_sidebar);
         
-        adw_overlay_split_view_set_content(ADW_OVERLAY_SPLIT_VIEW(splitview), toolbar_view);
+        adw_overlay_split_view_set_content(ADW_OVERLAY_SPLIT_VIEW(splitview), content);
         
         g_object_set_data(G_OBJECT(obj->widget), "ui_sidebar", sidebar_toolbar_view);
     } else {
-        adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), toolbar_view);
+        adw_application_window_set_content(ADW_APPLICATION_WINDOW(obj->widget), content);
     }
     
-    GtkWidget *headerbar = adw_header_bar_new();
-    
     const char *show_title = ui_get_property("ui.gtk.window.showtitle");
     if(show_title) {
         if(!strcmp(show_title, "main") && sidebar) {
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(sidebar_headerbar), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
         } else if(!strcmp(show_title, "sidebar")) {
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
         } else if(!strcmp(show_title, "false")) {
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(sidebar_headerbar), FALSE);
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
         } else {
             fprintf(stderr, "Unknown value '%s' for property ui.gtk.window.showtitle\n", show_title);
-            adw_header_bar_set_show_title(ADW_HEADER_BAR(sidebar_headerbar), FALSE);
+            adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_sidebar), FALSE);
         }
     } else {
-        adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar), FALSE);
+        adw_header_bar_set_show_title(ADW_HEADER_BAR(headerbar_main), FALSE);
     }
     
-    adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar);
-    g_object_set_data(G_OBJECT(obj->widget), "ui_headerbar", headerbar);
+    adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), headerbar_main);
+    g_object_set_data(G_OBJECT(obj->widget), "ui_headerbar", headerbar_main);
     
     if(!simple) {
-        ui_fill_headerbar(obj, headerbar);
+        ui_fill_headerbar(obj, headerbar_main);
     }
 #elif GTK_MAJOR_VERSION >= 4
     GtkWidget *content_box = ui_gtk_vbox_new(0);
@@ -286,15 +348,19 @@
 
 
 UiObject* ui_window(const char *title, void *window_data) {
-    return create_window(title, window_data, FALSE, FALSE);
+    return create_window(title, window_data, FALSE, FALSE, FALSE);
 }
 
 UiObject *ui_sidebar_window(const char *title, void *window_data) {
-    return create_window(title, window_data, TRUE, FALSE);
+    return create_window(title, window_data, TRUE, FALSE, FALSE);
+}
+
+UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar) {
+    return create_window(title, NULL, sidebar, TRUE, FALSE);
 }
 
 UiObject* ui_simple_window(const char *title, void *window_data) {
-    return create_window(title, window_data, FALSE, TRUE);
+    return create_window(title, window_data, FALSE, FALSE, TRUE);
 }
 
 void ui_window_size(UiObject *obj, int width, int height) {
@@ -304,6 +370,11 @@
                 height);
 }
 
+void ui_window_default_size(UiObject *obj, int width, int height) {
+    window_default_width = width;
+    window_default_height = height;
+}
+
 #ifdef UI_LIBADWAITA
 
 static void dialog_response(AdwAlertDialog *self, gchar *response, UiEventData *data) {
--- a/ui/motif/Grid.c	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/motif/Grid.c	Thu Oct 02 14:49:27 2025 +0200
@@ -299,31 +299,32 @@
 WidgetClass gridClass = (WidgetClass)&gridClassRec;
 
 
-void grid_class_initialize(Widget request, Widget new, ArgList args, Cardinal *num_args) {
+void grid_class_initialize(void) {
     
 }
 void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args) {
-    MyWidget mn = (MyWidget)new;
+    Grid mn = (Grid)new;
     
     mn->mywidget.max_col = 0;
     mn->mywidget.max_row = 0;
     
 }
-void grid_realize(MyWidget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) {
-    XtMakeResizeRequest((Widget)w, 400, 400, NULL, NULL);
+void grid_realize(Widget w,XtValueMask *valueMask,XSetWindowAttributes *attributes) {
+    Grid grid = (Grid)w;
+    XtMakeResizeRequest(w, 400, 400, NULL, NULL);
     (coreClassRec.core_class.realize)((Widget)w, valueMask, attributes); 
-    grid_place_children(w);
+    grid_place_children(grid);
 }
 
 
-void grid_destroy(MyWidget widget) {
+void grid_destroy(Grid widget) {
     
 }
-void grid_resize(MyWidget widget) {
+void grid_resize(Grid widget) {
     grid_place_children(widget);
 }
 
-void grid_expose(MyWidget widget, XEvent *event, Region region) {
+void grid_expose(Grid widget, XEvent *event, Region region) {
     
 }
 
@@ -336,11 +337,11 @@
     
 }
 
-void grid_getfocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) {
+void grid_getfocus(Widget myw, XEvent *event, String *params, Cardinal *nparam) {
     
 }
 
-void grid_loosefocus(MyWidget myw, XEvent *event, String *params, Cardinal *nparam) {
+void grid_loosefocus(Widget myw, XEvent *event, String *params, Cardinal *nparam) {
     
 }
 
@@ -358,7 +359,7 @@
         widget->core.height = request->height;
         constraints->grid.pref_height = request->height;
     }
-    grid_place_children((MyWidget)XtParent(widget));
+    grid_place_children((Grid)XtParent(widget));
     return XtGeometryYes;
 }
 
@@ -368,7 +369,7 @@
 
 Boolean ConstraintSetValues(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args) {
     GridConstraintRec *constraints = neww->core.constraints;
-    MyWidget grid = (MyWidget)XtParent(neww);
+    Grid grid = (Grid)XtParent(neww);
     if(constraints->grid.x > grid->mywidget.max_col) {
         grid->mywidget.max_col = constraints->grid.x;
     }
@@ -387,7 +388,7 @@
 {
     GridConstraintRec *constraints = neww->core.constraints;
     
-    MyWidget grid = (MyWidget)XtParent(neww);
+    Grid grid = (Grid)XtParent(neww);
     if(constraints->grid.x > grid->mywidget.max_col) {
         grid->mywidget.max_col = constraints->grid.x;
     }
@@ -398,7 +399,7 @@
     constraints->grid.pref_height = neww->core.height;
 }
 
-void grid_place_children(MyWidget w) {
+void grid_place_children(Grid w) {
     int ncols = w->mywidget.max_col+1;
     int nrows = w->mywidget.max_row+1;
     GridDef *cols = calloc(ncols, sizeof(GridDef));
--- a/ui/motif/Grid.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/motif/Grid.h	Thu Oct 02 14:49:27 2025 +0200
@@ -125,23 +125,23 @@
     GridContraintPart grid;
 } GridConstraintRec;
 
-typedef GridRec* MyWidget;
+typedef GridRec* Grid;
 
 extern WidgetClass gridClass;
 
-void grid_class_initialize();
-void grid_initialize();
-void grid_realize();
-void grid_destroy();
-void grid_resize();
-void grid_expose();
-Boolean grid_set_values();
+void grid_class_initialize(void);
+void grid_initialize(Widget request, Widget new, ArgList args, Cardinal num_args);
+void grid_realize(Widget w,XtValueMask *valueMask,XSetWindowAttributes *attributes);
+void grid_destroy(Grid widget);
+void grid_resize(Grid widget);
+void grid_expose(Grid widget, XEvent *event, Region region);
+Boolean grid_set_values(Widget old, Widget request, Widget neww, ArgList args, Cardinal *num_args);
 Boolean grid_acceptfocus(Widget , Time*);
 
-void grid_place_children(MyWidget w);
+void grid_place_children(Grid w);
 
-void grid_getfocus();
-void grid_loosefocus();
+void grid_getfocus(Widget myw, XEvent *event, String *params, Cardinal *nparam);
+void grid_loosefocus(Widget myw, XEvent *event, String *params, Cardinal *nparam);
 
 void grid_constraint_init(
     Widget	request,
--- a/ui/qt/entry.cpp	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/qt/entry.cpp	Thu Oct 02 14:49:27 2025 +0200
@@ -36,14 +36,19 @@
 
 
 
-UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args) {
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) {
     UiContainerPrivate *ctn = ui_obj_container(obj);
     UI_APPLY_LAYOUT(ctn->layout, args);
     
+    double min = args->min;
+    double max = args->max != 0 ? args->max : 100000;
+    
     bool use_double = false;
     UiVar *var = NULL;
+    UiVarType vartype = UI_VAR_SPECIAL;
     if(args->varname) {
         var = uic_get_var(obj->ctx, args->varname);
+        vartype = var->type;
         if(var->type == UI_VAR_DOUBLE) {
             use_double = true;
         } else if(var->type == UI_VAR_RANGE) {
@@ -57,11 +62,14 @@
     if(!var) {
         if(args->intvalue) {
             var = uic_widget_var(obj->ctx, obj->ctx, args->intvalue, NULL, UI_VAR_INTEGER);
+            vartype = UI_VAR_INTEGER;
         } else if(args->doublevalue) {
             var = uic_widget_var(obj->ctx, obj->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE);
+            vartype = UI_VAR_DOUBLE;
             use_double = true;
         } else if(args->rangevalue) {
             var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, NULL, UI_VAR_RANGE);
+            vartype = UI_VAR_RANGE;
             use_double = true;
         } else {
             if(args->digits > 0) {
@@ -70,6 +78,12 @@
         }
     }
     
+    if(vartype == UI_VAR_RANGE) {
+        UiRange *r = (UiRange*)var->value;
+        min = r->min;
+        max = r->max;
+    }
+    
     QAbstractSpinBox *widget = nullptr;
     if(use_double) {
         QDoubleSpinBox *spinbox = new QDoubleSpinBox();
@@ -77,17 +91,21 @@
         if(args->step != 0) {
             spinbox->setSingleStep(args->step);
         }
+        spinbox->setMinimum(min);
+        spinbox->setMaximum(max);
         widget = spinbox;
     } else {
         QSpinBox *spinbox = new QSpinBox();
         if(args->step != 0) {
             spinbox->setSingleStep(args->step);
         }
+        spinbox->setMinimum((int)min);
+        spinbox->setMaximum((int)max);
         widget = spinbox;
     }
     
     if(var) {
-        if(var->type == UI_VAR_INTEGER) {
+        if(vartype == UI_VAR_INTEGER) {
             UiInteger *value = (UiInteger*)var->value;
             value->obj = widget;
             if(value->value != 0) {
@@ -96,7 +114,7 @@
             }
             value->get = ui_spinbox_int_get;
             value->set = ui_spinbox_int_set;
-        } else if(var->type == UI_VAR_DOUBLE) {
+        } else if(vartype == UI_VAR_DOUBLE) {
             UiDouble *value = (UiDouble*)var->value;
             value->obj = widget;
             if(value->value != 0) {
@@ -105,7 +123,7 @@
             }
             value->get = ui_spinbox_double_get;
             value->set = ui_spinbox_double_set;
-        } else if(var->type == UI_VAR_RANGE) {
+        } else if(vartype == UI_VAR_RANGE) {
             UiRange *value = (UiRange*)var->value;
             value->obj = widget;
             QDoubleSpinBox *spinbox = (QDoubleSpinBox*)widget;
--- a/ui/ui/container.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/ui/container.h	Thu Oct 02 14:49:27 2025 +0200
@@ -174,6 +174,7 @@
     int rowspacing;
     
     int initial_position;
+    const char *position_property;
     UiInteger *value;
     const char* varname;
     int max_panes;
@@ -243,6 +244,8 @@
 #define ui_tabview(obj, ...) for(ui_tabview_create(obj, &(UiTabViewArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_headerbar(obj, ...) for(ui_headerbar_create(obj, &(UiHeaderbarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_sidebar(obj, ...) for(ui_sidebar_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_left_panel(obj, ...) for(ui_left_panel_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_right_panel(ob, ...) for(ui_right_panel_create(obj, &(UiSidebarArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 
 #define ui_vbox0(obj) for(ui_vbox_create(obj, &(UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_hbox0(obj) for(ui_hbox_create(obj, &(UiContainerArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
@@ -253,6 +256,9 @@
 #define ui_tabview0(obj) for(ui_tabview_create(obj, &(UiTabViewArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_headerbar0(obj) for(ui_headerbar_create(obj, &(UiHeaderbarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_sidebar0(obj) for(ui_sidebar_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_left_panel0(obj) for(ui_left_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+#define ui_right_panel0(obj) for(ui_right_panel_create(obj, &(UiSidebarArgs){ 0 });ui_container_finish(obj);ui_container_begin_close(obj))
+
 
 #define ui_vbox_w(obj, w, ...) for(w = ui_vbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
 #define ui_hbox_w(obj, w, ...) for(w = ui_hbox_create(obj, &(UiContainerArgs){ __VA_ARGS__ });ui_container_finish(obj);ui_container_begin_close(obj))
@@ -298,6 +304,8 @@
 UIEXPORT void ui_headerbar_end_create(UiObject *obj);
 
 UIEXPORT UIWIDGET ui_sidebar_create(UiObject *obj, UiSidebarArgs *args);
+UIEXPORT UIWIDGET ui_left_panel_create(UiObject *obj, UiSidebarArgs *args);
+UIEXPORT UIWIDGET ui_right_panel_create(UiObject *obj, UiSidebarArgs *args);
 
 UIEXPORT UIWIDGET ui_itemlist_create(UiObject *obj, UiItemListContainerArgs *args);
 
--- a/ui/ui/entry.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/ui/entry.h	Thu Oct 02 14:49:27 2025 +0200
@@ -36,7 +36,7 @@
 #endif
 
 
-typedef struct UiSpinnerArgs {
+typedef struct UiSpinBoxArgs {
     UiBool fill;
     UiBool hexpand;
     UiBool vexpand;
@@ -50,6 +50,8 @@
 
     double step;
     int digits;
+    double min;
+    double max;
     UiInteger *intvalue;
     UiDouble* doublevalue;
     UiRange *rangevalue;
@@ -58,13 +60,13 @@
     void* onchangedata;
     
     const int *groups;
-} UiSpinnerArgs;
+} UiSpinBoxArgs;
 
 
     
-UIWIDGET ui_spinner_create(UiObject *obj, UiSpinnerArgs *args);
+UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args);
 
-#define ui_spinner(obj, ...) ui_spinner_create(obj, &(UiSpinnerArgs){ __VA_ARGS__ } )
+#define ui_spinbox(obj, ...) ui_spinbox_create(obj, &(UiSpinBoxArgs){ __VA_ARGS__ } )
 
 void ui_spinner_setrange(UIWIDGET spinner, double min, double max);
 void ui_spinner_setdigits(UIWIDGET spinner, int digits);
--- a/ui/ui/properties.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/ui/properties.h	Thu Oct 02 14:49:27 2025 +0200
@@ -41,14 +41,14 @@
 
 int ui_properties_store(void);
 
-void ui_locales_dir(char *path);
-void ui_pixmaps_dir(char *path);
+void ui_locales_dir(const char *path);
+void ui_pixmaps_dir(const char *path);
 
-void ui_load_lang(char *locale);
+void ui_load_lang(const char *locale);
 void ui_load_lang_def(char *locale, char *default_locale);
     
-char* uistr(char *name);
-char* uistr_n(char *name);
+char* uistr(const char *name);
+char* uistr_n(const char *name);
 
 #ifdef	__cplusplus
 }
--- a/ui/ui/toolkit.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/ui/toolkit.h	Thu Oct 02 14:49:27 2025 +0200
@@ -568,6 +568,8 @@
 UIEXPORT void ui_detach_document(UiContext *ctx, void *document);
 
 UIEXPORT void ui_widget_set_groups(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, ...);
+UIEXPORT void ui_widget_set_groups2(UiContext *ctx, UIWIDGET widget, ui_enablefunc enable, int *groups, int ngroups);
+UIEXPORT void ui_widget_set_visibility_states(UiContext *ctx, UIWIDGET widget, int *states, int nstates);
 
 UIEXPORT void ui_set_group(UiContext *ctx, int group);
 UIEXPORT void ui_unset_group(UiContext *ctx, int group);
@@ -611,6 +613,13 @@
 UIEXPORT char* ui_string_get(UiString *s);
 UIEXPORT void ui_text_set(UiText *s, const char* value);
 UIEXPORT char* ui_text_get(UiText *s);
+UIEXPORT void ui_range_set(UiRange *r, double value);
+UIEXPORT void ui_range_set_range(UiRange *r, double min, double max);
+UIEXPORT void ui_range_set_extent(UiRange *r, double extent);
+UIEXPORT double ui_range_get(UiRange *r);
+UIEXPORT double ui_range_get_min(UiRange *r);
+UIEXPORT double ui_range_get_max(UiRange *r);
+UIEXPORT double ui_range_get_extent(UiRange *r);
 UIEXPORT void ui_generic_set_image(UiGeneric *g, void *img);
 UIEXPORT void* ui_generic_get_image(UiGeneric *g);
 
@@ -672,7 +681,7 @@
 
 
 UIEXPORT char* ui_getappdir(void);
-UIEXPORT char* ui_configfile(char *name);
+UIEXPORT char* ui_configfile(const char *name);
 
 UIEXPORT UiCondVar* ui_condvar_create(void);
 UIEXPORT void ui_condvar_wait(UiCondVar *var);
--- a/ui/ui/tree.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/ui/tree.h	Thu Oct 02 14:49:27 2025 +0200
@@ -51,9 +51,20 @@
     UI_INTEGER,
     UI_ICON,
     UI_ICON_TEXT,
-    UI_ICON_TEXT_FREE
+    UI_ICON_TEXT_FREE,
+    UI_STRING_EDITABLE
 } UiModelType;
 
+typedef struct UiCellValue {
+    union {
+        const char *string;
+        int64_t i;
+    };
+    UiModelType type;
+} UiCellValue;
+
+typedef UiBool (*ui_list_savefunc)(UiList *list, int row, int col, UiCellValue *value, void *userdata);
+
 struct UiModel {
     /*
      * number of columns
@@ -134,6 +145,8 @@
     void* ondropdata;
     UiBool multiselection;
     UiMenuBuilder *contextmenu;
+    ui_list_savefunc onsave;
+    void *onsavedata;
     
     const int *groups;
 };
--- a/ui/ui/window.h	Thu Oct 02 14:49:17 2025 +0200
+++ b/ui/ui/window.h	Thu Oct 02 14:49:27 2025 +0200
@@ -74,6 +74,7 @@
 
 UIEXPORT UiObject *ui_window(const char *title, void *window_data);
 UIEXPORT UiObject *ui_sidebar_window(const char *title, void *window_data);
+UIEXPORT UiObject *ui_splitview_window(const char *title, UiBool sidebar);
 UIEXPORT UiObject *ui_simple_window(const char *title, void *window_data);
 UIEXPORT UiObject *ui_dialog_window_create(UiObject *parent, UiDialogWindowArgs *args);
 
@@ -81,6 +82,7 @@
 #define ui_dialog_window0(parent) ui_dialog_window_create(parent, &(UiDialogWindowArgs){ 0 });
 
 UIEXPORT void ui_window_size(UiObject *obj, int width, int height);
+UIEXPORT void ui_window_default_size(UiObject *obj, int width, int height);
 
 #define ui_dialog(parent, ...) ui_dialog_create(parent, &(UiDialogArgs){ __VA_ARGS__ } )
 

mercurial