Thu, 02 Oct 2025 14:49:27 +0200
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__ } )