Fri, 10 Oct 2025 09:06:06 +0200
merge
--- a/.hgignore Fri Oct 10 09:05:11 2025 +0200 +++ b/.hgignore Fri Oct 10 09:06:06 2025 +0200 @@ -11,3 +11,4 @@ relre:.qmake.stash$ relre:^.idea/.* relre:^compile_commands.json$ +relre:^application/app.res$
--- a/application/Makefile Fri Oct 10 09:05:11 2025 +0200 +++ b/application/Makefile Fri Oct 10 09:06:06 2025 +0200 @@ -39,8 +39,10 @@ all: $(APP_BIN) -$(APP_BIN): $(OBJ) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)uitk$(LIB_EXT) - $(LD) -o $(APP_BIN) $(OBJ) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)uitk$(LIB_EXT) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)ucx$(LIB_EXT) $(LDFLAGS) $(TK_LDFLAGS) +include $(SYS_MAKEFILE) + +$(APP_BIN): $(OBJ) $(RES_FILE) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)uitk$(LIB_EXT) + $(LD) -o $(APP_BIN) $(OBJ) $(RES_FILE) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)uitk$(LIB_EXT) $(BUILD_ROOT)/build/$(BUILD_LIB_DIR)/$(LIB_PREFIX)ucx$(LIB_EXT) $(LDFLAGS) $(TK_LDFLAGS) ../build/application/%$(OBJ_EXT): %.c $(CC) $(CFLAGS) $(TK_CFLAGS) -o $@ -c $<
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/Makefile.win32 Fri Oct 10 09:06:06 2025 +0200 @@ -0,0 +1,5 @@ +RES_FILE = app.res + +$(RES_FILE): app.rc app.manifest + llvm-rc app.rc +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/app.manifest Fri Oct 10 09:06:06 2025 +0200 @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> + <dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="*" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> + </dependency> +</assembly>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/application/app.rc Fri Oct 10 09:06:06 2025 +0200 @@ -0,0 +1,1 @@ +1 24 "app.manifest"
--- a/application/main.c Fri Oct 10 09:05:11 2025 +0200 +++ b/application/main.c Fri Oct 10 09:06:06 2025 +0200 @@ -470,6 +470,7 @@ case 6: return (void*)(intptr_t)123; case 7: return "edit me"; case 8: return "edit me too"; + case 9: return (void*)(intptr_t)1; } return NULL; } @@ -672,7 +673,7 @@ } } 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", UI_STRING_EDITABLE, "edit6", UI_STRING_EDITABLE, "edit7", -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", UI_BOOL_EDITABLE, "Check", -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, .onsave = list_save, @@ -1075,6 +1076,17 @@ ui_radiobutton(obj, .label = "Radio 3", .varname = "radio"); ui_radiobutton(obj, .label = "Radio 4", .varname = "radio"); } + ui_newline(obj); + + ui_hbox(obj, .colspan = 3) { + ui_button(obj, .label = "Margin Test 1", .margin_top = 10); + ui_button(obj, .label = "Margin Test 2", .margin_top = 20); + UIWIDGET w = ui_button(obj, .label = "Margin Test 3", .margin = 30); + //ui_set_visible(w, FALSE); + ui_button(obj, .label = "Margin Test 4", .margin_top = 40); + ui_button(obj, .label = "Margin Test 5", .margin_top = 50); + ui_button(obj, .label = "Margin Test 6", .margin_top = 60); + } } ui_show(obj); } @@ -1123,9 +1135,18 @@ #ifdef UI_WIN32 +static void action_button(UiEvent *event, void *data) { + printf("button clicked\n"); +} + void application_startup(UiEvent *event, void *data) { UiObject *obj = ui_window("Test w32", NULL); - + ui_button(obj, .label = "Test", .hfill = TRUE, .hexpand = TRUE, .colspan = 3, .margin = 10); + ui_button(obj, .label = "Test 2-1", .margin_left = 10); + ui_button(obj, .label = "Test 2-2", .hfill = TRUE, .hexpand = TRUE, .margin_left = 20); + ui_button(obj, .label = "Test 2-3", .margin_left = 30); + ui_button(obj, .label = "Test 3XX", .colspan = 3, .fill = TRUE, .onclick = action_button); + ui_show(obj); } int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
--- a/configure Fri Oct 10 09:05:11 2025 +0200 +++ b/configure Fri Oct 10 09:06:06 2025 +0200 @@ -675,6 +675,36 @@ done while true do + if notisplatform "unix"; then + break + fi + while true + do + + cat >> "$TEMP_DIR/make.mk" << __EOF__ +SYS_MAKEFILE = Makefile.unix +__EOF__ + break + done + break +done +while true +do + if notisplatform "windows"; then + break + fi + while true + do + + cat >> "$TEMP_DIR/make.mk" << __EOF__ +SYS_MAKEFILE = Makefile.win32 +__EOF__ + break + done + break +done +while true +do if notisplatform "macos"; then break fi
--- a/make/project.xml Fri Oct 10 09:05:11 2025 +0200 +++ b/make/project.xml Fri Oct 10 09:06:06 2025 +0200 @@ -5,6 +5,13 @@ <make>LD = \$(CC)</make> </dependency> + <dependency platform="unix"> + <make>SYS_MAKEFILE = Makefile.unix</make> + </dependency> + <dependency platform="windows"> + <make>SYS_MAKEFILE = Makefile.win32</make> + </dependency> + <dependency name="libadwaita"> <pkgconfig>libadwaita-1</pkgconfig> <cflags>-DUI_GTK4 -DUI_LIBADWAITA</cflags>
--- a/ui/common/args.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/common/args.c Fri Oct 10 09:06:06 2025 +0200 @@ -907,11 +907,11 @@ } void ui_tabview_args_set_margin_top(UiTabViewArgs *args, int value) { - args->margin_top; + args->margin_top = value; } void ui_tabview_args_set_margin_bottom(UiTabViewArgs *args, int value) { - args->margin_bottom; + args->margin_bottom = value; } void ui_tabview_args_set_padding(UiTabViewArgs *args, int value) { @@ -2367,7 +2367,7 @@ } void ui_spinbox_args_set_digits(UiSpinBoxArgs *args, int digits) { - args->digits; + args->digits = digits; } void ui_spinbox_args_set_varname(UiSpinBoxArgs *args, const char *varname) {
--- a/ui/common/container.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/common/container.c Fri Oct 10 09:06:06 2025 +0200 @@ -75,3 +75,13 @@ } } } + +void uic_layout_setup_margin(UiLayout *layout) { + int margin = layout->margin; + if(margin > 0) { + layout->margin_left = margin; + layout->margin_right = margin; + layout->margin_top = margin; + layout->margin_bottom = margin; + } +}
--- a/ui/common/container.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/common/container.h Fri Oct 10 09:06:06 2025 +0200 @@ -46,6 +46,11 @@ UiBool def_hfill, UiBool def_vfill); +/* + * adjusts margin_* if margin > 0 + */ +void uic_layout_setup_margin(UiLayout *layout); + #ifdef __cplusplus } #endif
--- a/ui/common/context.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/common/context.c Fri Oct 10 09:06:06 2025 +0200 @@ -109,7 +109,7 @@ while(var_ctx) { CxMapIterator i = cxMapIterator(var_ctx->vars); cx_foreach(CxMapEntry*, entry, i) { - printf("attach %.*s\n", (int)entry->key->len, entry->key->data); + printf("attach %.*s\n", (int)entry->key->len, (char*)entry->key->data); UiVar *var = entry->value; UiVar *docvar = cxMapGet(doc_ctx->vars, *entry->key); if(docvar) {
--- a/ui/common/toolbar.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/common/toolbar.c Fri Oct 10 09:06:06 2025 +0200 @@ -55,6 +55,7 @@ newargs.label = nl_strdup(args->label); newargs.stockid = nl_strdup(args->stockid); newargs.icon = nl_strdup(args->icon); + newargs.tooltip = nl_strdup(args->tooltip); newargs.onclick = args->onclick; newargs.onclickdata = args->onclickdata; newargs.groups = uic_copy_groups(args->groups, ngroups); @@ -74,6 +75,7 @@ newargs.label = nl_strdup(args->label); newargs.stockid = nl_strdup(args->stockid); newargs.icon = nl_strdup(args->icon); + newargs.tooltip = nl_strdup(args->tooltip); newargs.varname = nl_strdup(args->varname); newargs.onchange = args->onchange; newargs.onchangedata = args->onchangedata; @@ -93,6 +95,7 @@ newargs.label = nl_strdup(args->label); newargs.stockid = nl_strdup(args->stockid); newargs.icon = nl_strdup(args->icon); + newargs.tooltip = nl_strdup(args->tooltip); return newargs; }
--- a/ui/gtk/button.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/gtk/button.c Fri Oct 10 09:06:06 2025 +0200 @@ -60,6 +60,7 @@ UiObject *obj, const char *label, const char *icon, + const char *tooltip, ui_callback onclick, void *userdata, int event_value, @@ -67,6 +68,9 @@ { GtkWidget *button = gtk_button_new_with_label(label); ui_button_set_icon_name(button, icon); + if(tooltip) { + gtk_widget_set_tooltip_text(button, tooltip); + } if(onclick) { UiEventData *event = malloc(sizeof(UiEventData)); @@ -100,7 +104,7 @@ } UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) { - GtkWidget *button = ui_create_button(obj, args->label, args->icon, args->onclick, args->onclickdata, 0, FALSE); + GtkWidget *button = ui_create_button(obj, args->label, args->icon, NULL/*tooltip*/, args->onclick, args->onclickdata, 0, FALSE); ui_set_name_and_style(button, args->name, args->style_class); ui_set_widget_groups(obj->ctx, button, args->groups); UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end; @@ -181,6 +185,7 @@ GtkWidget *togglebutton, const char *label, const char *icon, + const char *tooltip, const char *varname, UiInteger *value, ui_callback onchange, @@ -191,6 +196,9 @@ gtk_button_set_label(GTK_BUTTON(togglebutton), label); } ui_button_set_icon_name(togglebutton, icon); + if(tooltip) { + gtk_widget_set_tooltip_text(togglebutton, tooltip); + } ui_bind_togglebutton( obj, @@ -295,6 +303,7 @@ widget, args->label, args->icon, + NULL, // tooltip args->varname, args->value, args->onchange,
--- a/ui/gtk/button.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/gtk/button.h Fri Oct 10 09:06:06 2025 +0200 @@ -55,6 +55,7 @@ UiObject *obj, const char *label, const char *icon, + const char *tooltip, ui_callback onclick, void *userdata, int event_value, @@ -65,6 +66,7 @@ GtkWidget *togglebutton, const char *label, const char *icon, + const char *tooltip, const char *varname, UiInteger *value, ui_callback onchange,
--- a/ui/gtk/container.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/gtk/container.c Fri Oct 10 09:06:06 2025 +0200 @@ -1022,7 +1022,7 @@ GtkWidget *sidebar_vbox = g_object_get_data(G_OBJECT(obj->widget), "ui_sidebar"); GtkWidget *box = ui_gtk_vbox_new(args->spacing); - ui_gtk_set_margin(box, args->margin, args->margin-left, args->margin_right, args->margin_top, args->margin_bottom); + ui_gtk_set_margin(box, args->margin, args->margin_left, args->margin_right, args->margin_top, args->margin_bottom); BOX_ADD_EXPAND(sidebar_vbox, box); UiContainerX *container = ui_box_container(obj, box, UI_CONTAINER_VBOX);
--- a/ui/gtk/headerbar.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/gtk/headerbar.c Fri Oct 10 09:06:06 2025 +0200 @@ -163,7 +163,7 @@ UiObject *obj, enum UiToolbarPos pos) { - GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.onclick, item->args.onclickdata, 0, FALSE); + GtkWidget *button = ui_create_button(obj, item->args.label, item->args.icon, item->args.tooltip, item->args.onclick, item->args.onclickdata, 0, FALSE); ui_set_widget_groups(obj->ctx, button, item->args.groups); WIDGET_ADD_CSS_CLASS(button, "flat"); headerbar_add(headerbar, box, button, pos); @@ -179,7 +179,7 @@ GtkWidget *button = gtk_toggle_button_new(); ui_set_widget_groups(obj->ctx, button, item->args.groups); WIDGET_ADD_CSS_CLASS(button, "flat"); - ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0); + ui_setup_togglebutton(obj, button, item->args.label, item->args.icon, NULL/*tooltip*/, item->args.varname, NULL, item->args.onchange, item->args.onchangedata, 0); headerbar_add(headerbar, box, button, pos); }
--- a/ui/gtk/list.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/gtk/list.c Fri Oct 10 09:06:06 2025 +0200 @@ -245,7 +245,10 @@ 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 { + } else if(type == UI_BOOL_EDITABLE) { + GtkWidget *checkbox = gtk_check_button_new(); + gtk_list_item_set_child(item, checkbox); + }else { GtkWidget *label = gtk_label_new(NULL); gtk_label_set_xalign(GTK_LABEL(label), 0); gtk_list_item_set_child(item, label); @@ -390,6 +393,11 @@ ENTRY_SET_TEXT(child, data); break; } + case UI_BOOL_EDITABLE: { + intptr_t i = (intptr_t)data; + gtk_check_button_set_active(GTK_CHECK_BUTTON(child), (gboolean)i); + break; + } } if(attributes != listview->current_row_attributes) { @@ -417,6 +425,8 @@ entry->listview = NULL; free(entry->previous_value); entry->previous_value = NULL; + } else if(GTK_IS_CHECK_BUTTON(child)) { + } } @@ -1509,7 +1519,7 @@ UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { GtkWidget *combobox = gtk_combo_box_new(); if(args->width > 0) { - gtk_widget_set_size_request(scroll_area, args->width, -1); + gtk_widget_set_size_request(combobox, args->width, -1); } ui_set_name_and_style(combobox, args->name, args->style_class);
--- a/ui/gtk/toolbar.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/gtk/toolbar.c Fri Oct 10 09:06:06 2025 +0200 @@ -138,6 +138,9 @@ } else { button = gtk_tool_button_new(NULL, item->args.label); } + if(item->args.tooltip) { + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip); + } gtk_tool_item_set_homogeneous(button, FALSE); if(item->args.icon) { @@ -192,6 +195,9 @@ set_toolbutton_icon(button, item->args.icon); } } + if(item->args.tooltip) { + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip); + } ui_set_widget_ngroups(obj->ctx, GTK_WIDGET(button), item->args.groups, item->ngroups); UiVar* var = uic_widget_var(obj->ctx, obj->ctx, NULL, item->args.varname, UI_VAR_INTEGER); @@ -297,6 +303,9 @@ if(item->args.icon) { set_toolbutton_icon(button, item->args.icon); } + if(item->args.tooltip) { + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(button), item->args.tooltip); + } gtk_tool_item_set_is_important(button, TRUE); gtk_toolbar_insert(tb, button, -1);
--- a/ui/gtk/window.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/gtk/window.c Fri Oct 10 09:06:06 2025 +0200 @@ -945,7 +945,7 @@ } if(args->lbutton1) { - GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, args->onclick, args->onclickdata, 1, args->default_button == 1); + GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 1, args->default_button == 1); gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button); if(args->default_button == 1) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); @@ -953,7 +953,7 @@ } } if(args->lbutton2) { - GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, args->onclick, args->onclickdata, 2, args->default_button == 2); + GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 2, args->default_button == 2); gtk_header_bar_pack_start(GTK_HEADER_BAR(headerbar), button); if(args->default_button == 2) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); @@ -962,7 +962,7 @@ } if(args->rbutton4) { - GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, args->onclick, args->onclickdata, 4, args->default_button == 4); + GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 4, args->default_button == 4); gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button); if(args->default_button == 4) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); @@ -970,7 +970,7 @@ } } if(args->rbutton3) { - GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, args->onclick, args->onclickdata, 3, args->default_button == 3); + GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 3, args->default_button == 3); gtk_header_bar_pack_end(GTK_HEADER_BAR(headerbar), button); if(args->default_button == 3) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); @@ -991,7 +991,7 @@ gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); if(args->lbutton1) { - GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, args->onclick, args->onclickdata, 1, args->default_button == 1); + GtkWidget *button = ui_create_button(obj, args->lbutton1, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 1, args->default_button == 1); gtk_grid_attach(GTK_GRID(grid), button, 0, 0, 1, 1); if(args->default_button == 1) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); @@ -999,7 +999,7 @@ } } if(args->lbutton2) { - GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, args->onclick, args->onclickdata, 2, args->default_button == 2); + GtkWidget *button = ui_create_button(obj, args->lbutton2, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 2, args->default_button == 2); gtk_grid_attach(GTK_GRID(grid), button, 1, 0, 1, 1); if(args->default_button == 2) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); @@ -1010,7 +1010,7 @@ gtk_widget_set_hexpand(space, TRUE); gtk_grid_attach(GTK_GRID(grid), space, 2, 0, 1, 1); if(args->rbutton3) { - GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, args->onclick, args->onclickdata, 3, args->default_button == 3); + GtkWidget *button = ui_create_button(obj, args->rbutton3, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 3, args->default_button == 3); gtk_grid_attach(GTK_GRID(grid), button, 3, 0, 1, 1); if(args->default_button == 3) { WIDGET_ADD_CSS_CLASS(button, "suggested-action"); @@ -1018,7 +1018,7 @@ } } if(args->rbutton4) { - GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, args->onclick, args->onclickdata, 4, args->default_button == 4); + GtkWidget *button = ui_create_button(obj, args->rbutton4, NULL, NULL/*tooltip*/, args->onclick, args->onclickdata, 4, args->default_button == 4); gtk_grid_attach(GTK_GRID(grid), button, 4, 0, 1, 1); if(args->default_button == 4) { WIDGET_ADD_CSS_CLASS(button, "suggested-action");
--- a/ui/qt/Makefile Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/qt/Makefile Fri Oct 10 09:06:06 2025 +0200 @@ -35,11 +35,10 @@ $(UI_LIB): $(QT_MAKEFILE) $(OBJ) $(UI_LIB) FORCE $(MAKE) -f $(QT_MAKEFILE) - $(AR) $(ARFLAGS) $(OBJ) $(UI_LIB) $(OBJ) + $(AR) $(ARFLAGS) $(UI_LIB) $(OBJ) -$(UI_SHLIB): $(QT_MAKEFILE) $(OBJ) $(UI_LIB_SH) FORCE - $(MAKE) -f $(QT_MAKEFILE) - $(CXX) -o $(UI_SHLIB) $(LDFLAGS) $(SHLIB_LDFLAGS) $(TK_LDFLAGS) $(OBJ) -L../build/lib -lucx - +$(UI_SHLIB): $(QT_MAKEFILE) $(OBJ) $(UI_LIB_SH) $(UI_LIB) FORCE + $(CXX) -o $(UI_SHLIB) $(LDFLAGS) $(SHLIB_LDFLAGS) $(TK_LDFLAGS) $(OBJ) ../build/ui/qt/*.o -L../build/lib -lucx + FORCE:
--- a/ui/qt/container.cpp Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/qt/container.cpp Fri Oct 10 09:06:06 2025 +0200 @@ -42,7 +42,7 @@ delete ct; } -void ui_container_add(UiObject *obj, UiContainerPrivate *ct) { +void ui_obj_add_container(UiObject *obj, UiContainerPrivate *ct) { UiContainerX *container = (UiContainerX*)ui_malloc(obj->ctx, sizeof(UiContainerX)); container->close = 0; container->container = ct; @@ -53,10 +53,27 @@ uic_object_push_container(obj, container); } +/* ------------------------ margin helper ------------------------ */ + +QWidget* ui_widget_set_margin(QWidget *w, int left, int right, int top, int bottom) { + if((left | right | top | bottom) == 0) { + return w; + } + QWidget* wrapper = new QWidget; + QVBoxLayout* inner = new QVBoxLayout(wrapper); + inner->setContentsMargins(left, top, right, bottom); + inner->addWidget(w); + + // TODO: handle widget visibility changes + + return wrapper; +} + /* -------------------- UiBoxContainer -------------------- */ UiBoxContainer::UiBoxContainer(QBoxLayout* box) { this->box = box; + this->direction = box->direction(); box->setContentsMargins(QMargins(0,0,0,0)); box->setSpacing(0); } @@ -68,7 +85,12 @@ fprintf(stderr, "UiError: container has 2 filled widgets"); } + uic_layout_setup_margin(&layout); + widget = ui_widget_set_margin(widget, layout.margin_left, layout.margin_right, layout.margin_top, layout.margin_bottom); box->addWidget(widget); + if(direction == Qt::LeftToRight) { + box->setAlignment(widget, Qt::AlignTop); + } if(!hasStretchedWidget) { QSpacerItem *newspace = new QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); @@ -92,7 +114,7 @@ widget->setLayout(box); ctn->add(widget, layout); - ui_container_add(obj, new UiBoxContainer(box)); + ui_obj_add_container(obj, new UiBoxContainer(box)); return widget; } @@ -137,6 +159,7 @@ } uic_layout_setup_expand_fill(&layout, def_hexpand, def_vexpand, def_hfill, def_vfill); + uic_layout_setup_margin(&layout); if(layout.hexpand) { col_expanding = true; @@ -163,6 +186,7 @@ int colspan = layout.colspan > 0 ? layout.colspan : 1; int rowspan = layout.rowspan > 0 ? layout.rowspan : 1; + widget = ui_widget_set_margin(widget, layout.margin_left, layout.margin_right, layout.margin_top, layout.margin_bottom); grid->addWidget(widget, y, x, rowspan, colspan, alignment); if(x > max_x) { @@ -201,7 +225,7 @@ widget->setLayout(grid); ctn->add(widget, layout); - ui_container_add(obj, new UiGridContainer( + ui_obj_add_container(obj, new UiGridContainer( grid, args->margin, args->columnspacing, @@ -230,7 +254,7 @@ widget->setLayout(box); dock->setWidget(widget); - ui_container_add(obj, new UiBoxContainer(box)); + ui_obj_add_container(obj, new UiBoxContainer(box)); return dock; }
--- a/ui/qt/container.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/qt/container.h Fri Oct 10 09:06:06 2025 +0200 @@ -52,9 +52,10 @@ class UiBoxContainer : public UiContainerPrivate { public: - QBoxLayout *box; - bool hasStretchedWidget = false; - QSpacerItem *space; + QBoxLayout *box; + bool hasStretchedWidget = false; + QSpacerItem *space; + QBoxLayout::Direction direction; UiBoxContainer(QBoxLayout *box); @@ -89,7 +90,9 @@ virtual void end(); }; -void ui_container_add(UiObject *obj, UiContainerPrivate *ct); +void ui_obj_add_container(UiObject *obj, UiContainerPrivate *ct); + +QWidget* ui_widget_set_margin(QWidget *w, int left, int right, int top, int bottom); #endif /* CONTAINER_H */
--- a/ui/qt/qt5.pro Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/qt/qt5.pro Fri Oct 10 09:06:06 2025 +0200 @@ -26,7 +26,7 @@ # POSSIBILITY OF SUCH DAMAGE. # -TARGET = uitk_qt +TARGET = uitk TEMPLATE = lib CONFIG += staticlib warn_off debug DESTDIR = ../build/lib
--- a/ui/qt/window.cpp Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/qt/window.cpp Fri Oct 10 09:06:06 2025 +0200 @@ -62,7 +62,7 @@ QWidget *boxWidget = new QWidget(); boxWidget->setLayout(box); window->setCentralWidget(boxWidget); - ui_container_add(obj, new UiBoxContainer(box)); + ui_obj_add_container(obj, new UiBoxContainer(box)); if(sidebar) { QDockWidget *dock = new QDockWidget(); window->addDockWidget(Qt::LeftDockWidgetArea, dock);
--- a/ui/ui/toolbar.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/ui/toolbar.h Fri Oct 10 09:06:06 2025 +0200 @@ -37,9 +37,10 @@ #endif typedef struct UiToolbarItemArgs { - const char* label; - const char* stockid; - const char* icon; + const char *label; + const char *stockid; + const char *icon; + const char *tooltip; ui_callback onclick; void* onclickdata; @@ -48,21 +49,23 @@ } UiToolbarItemArgs; typedef struct UiToolbarToggleItemArgs { - const char* label; - const char* stockid; - const char* icon; + const char *label; + const char *stockid; + const char *icon; + const char *tooltip; - const char* varname; + const char *varname; ui_callback onchange; - void* onchangedata; + void *onchangedata; const int *groups; } UiToolbarToggleItemArgs; typedef struct UiToolbarMenuArgs { - const char* label; - const char* stockid; - const char* icon; + const char *label; + const char *stockid; + const char *icon; + const char *tooltip; } UiToolbarMenuArgs; enum UiToolbarPos {
--- a/ui/ui/toolkit.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/ui/toolkit.h Fri Oct 10 09:06:06 2025 +0200 @@ -88,13 +88,7 @@ #elif UI_WIN32 -#include <Windows.h> - -#define UIEXPORT __declspec(dllexport) - -typedef struct W32Widget { - HWND hwnd; -} W32Widget; +#include "win32.h" #define UIWIDGET W32Widget* #define UIWINDOW void*
--- a/ui/ui/tree.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/ui/tree.h Fri Oct 10 09:06:06 2025 +0200 @@ -52,13 +52,15 @@ UI_ICON, UI_ICON_TEXT, UI_ICON_TEXT_FREE, - UI_STRING_EDITABLE + UI_STRING_EDITABLE, + UI_BOOL_EDITABLE } UiModelType; typedef struct UiCellValue { union { const char *string; int64_t i; + UiBool b; }; UiModelType type; } UiCellValue;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/ui/win32.h Fri Oct 10 09:06:06 2025 +0200 @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UI_WIN32_H +#define UI_WIN32_H + +#include <Windows.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define UIEXPORT __declspec(dllexport) + +typedef struct W32WidgetClass W32WidgetClass; +typedef struct W32Widget W32Widget; +typedef struct W32Size W32Size; + +struct W32Size { + int width; + int height; +}; + +struct W32WidgetClass { + void (*eventproc)(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + void (*show)(W32Widget *widget, BOOLEAN show); + void (*enable)(W32Widget *widget, BOOLEAN enable); + W32Size (*get_preferred_size)(W32Widget *widget); + void (*destroy)(W32Widget *widget); +}; + +struct W32Widget { + W32WidgetClass *wclass; + HWND hwnd; + void *userdata; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* UI_WIN32_H */
--- a/ui/win32/button.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/button.c Fri Oct 10 09:06:06 2025 +0200 @@ -27,7 +27,67 @@ */ #include "button.h" +#include "widget.h" + +#include <stdio.h> +#include <stdlib.h> + +static W32WidgetClass button_widget_class = { + .eventproc = ui_button_eventproc, + .enable = w32_widget_default_enable, + .show = w32_widget_default_show, + .get_preferred_size = ui_button_get_preferred_size, + .destroy = w32_widget_default_destroy +}; UIWIDGET ui_button_create(UiObject *obj, UiButtonArgs *args) { - return NULL; + HINSTANCE hInstance = GetModuleHandle(NULL); + UiContainerPrivate *container = ui_obj_container(obj); + HWND parent = ui_container_get_parent(container); + UiLayout layout = UI_ARGS2LAYOUT(args); + + HWND hwnd = CreateWindow( + "BUTTON", + args->label, + WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, + 0, 0, 100, 30, + parent, + (HMENU)0, + hInstance, + NULL); + ui_win32_set_ui_font(hwnd); + + W32Widget *widget = w32_widget_create(&button_widget_class, hwnd, sizeof(UiWidget)); + ui_container_add(container, widget, &layout); + + UiWidget *btn = (UiWidget*)widget; + btn->obj = obj; + btn->callback = args->onclick; + btn->callbackdata = args->onclickdata; + + return widget; } + +W32Size ui_button_get_preferred_size(W32Widget *widget) { + W32Size size; + size.width = 100; + size.height = 30; + return size; +} + +void ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + UiWidget *w = (UiWidget*)widget; + + UiEvent e; + e.obj = w->obj; + e.document = e.obj->ctx->document; + e.window = e.obj->window; + e.eventdata = NULL; + e.eventdatatype = 0; + e.intval = 0; + e.set = ui_get_setop(); + + if (w->callback) { + w->callback(&e, w->callbackdata); + } +}
--- a/ui/win32/button.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/button.h Fri Oct 10 09:06:06 2025 +0200 @@ -32,4 +32,8 @@ #include "../ui/button.h" #include "container.h" +W32Size ui_button_get_preferred_size(W32Widget *widget); + +void ui_button_eventproc(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + #endif //BUTTON_H
--- a/ui/win32/container.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/container.c Fri Oct 10 09:06:06 2025 +0200 @@ -27,6 +27,30 @@ */ #include "container.h" +#include "grid.h" + +#include "../common/context.h" +#include "../common/container.h" + +UiContainerPrivate* ui_obj_container(UiObject *obj) { + return (UiContainerPrivate*)obj->container_end; +} + +HWND ui_container_get_parent(UiContainerPrivate *ctn) { + return ctn->parent ? ctn->parent(ctn) : ctn->hwnd; +} + +void ui_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout) { + UiLayout layout2 = *layout; + if (layout2.margin > 0) { + layout2.margin_left = layout2.margin; + layout2.margin_right = layout2.margin; + layout2.margin_top = layout2.margin; + layout2.margin_bottom = layout2.margin; + } + ctn->add(ctn, widget, &layout2); + ctn->container.newline = FALSE; +} /* ---------------------------- Box Container ---------------------------- */ @@ -45,4 +69,73 @@ return box_create(obj, args, UI_BOX_HORIZONTAL); } +UiContainerX* ui_box_container_create(UiObject *obj, HWND hwnd, UiBoxOrientation orientation, short spacing, GridEdgeInsets padding) { + UiBoxContainer *container = cxZalloc(obj->ctx->allocator, sizeof(UiBoxContainer)); + container->container.hwnd = hwnd; + container->container.add = ui_box_container_add; + container->layout = ui_grid_layout_create(obj->ctx->allocator, spacing, spacing); + container->layout->padding = padding; + container->orientation = orientation; + return (UiContainerX*)container; +} +void ui_box_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout) { + UiBoxContainer *box = (UiBoxContainer*)ctn; + GridLayoutInfo gridLayout = (GridLayoutInfo) { + .margin = (GridEdgeInsets) { layout->margin_top, layout->margin_bottom, layout->margin_left, layout->margin_right }, + }; + if (box->orientation == UI_BOX_HORIZONTAL) { + gridLayout.vexpand = TRUE; + gridLayout.vfill = TRUE; + gridLayout.hexpand = layout->fill; + gridLayout.hfill = layout->fill; + } else { + gridLayout.hexpand = TRUE; + gridLayout.hfill = TRUE; + gridLayout.vexpand = layout->fill; + gridLayout.vfill = layout->fill; + } + ui_grid_add_widget(box->layout, box->x, box->y, widget, &gridLayout); + if (box->orientation == UI_BOX_HORIZONTAL) { + box->x++; + } else { + box->y++; + } +} + +/* ---------------------------- Grid Container ---------------------------- */ + +UIWIDGET ui_grid_create(UiObject *obj, UiContainerArgs *args) { + return NULL; +} + +UiContainerX* ui_grid_container_create(UiObject *obj, HWND hwnd, short columnspacing, short rowspacing, GridEdgeInsets padding) { + UiGridLayoutContainer *container = cxZalloc(obj->ctx->allocator, sizeof(UiGridLayoutContainer)); + container->container.hwnd = hwnd; + container->container.add = ui_grid_container_add; + container->layout = ui_grid_layout_create(obj->ctx->allocator, columnspacing, rowspacing); + container->layout->padding = padding; + return (UiContainerX*)container; +} + +void ui_grid_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout) { + UiGridLayoutContainer *grid = (UiGridLayoutContainer*)ctn; + if (ctn->container.newline) { + grid->y++; + grid->x = 0; + } + + uic_layout_setup_expand_fill(layout, grid->def_hexpand, grid->def_vexpand, grid->def_hfill, grid->def_vfill); + GridLayoutInfo gridLayout = (GridLayoutInfo) { + .margin = (GridEdgeInsets) { layout->margin_top, layout->margin_bottom, layout->margin_left, layout->margin_right }, + .colspan = layout->colspan, + .rowspan = layout->rowspan, + .hexpand = layout->hexpand, + .vexpand = layout->vexpand, + .hfill = layout->hfill, + .vfill = layout->vfill, + }; + ui_grid_add_widget(grid->layout, grid->x, grid->y, widget, &gridLayout); + + grid->x++; +}
--- a/ui/win32/container.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/container.h Fri Oct 10 09:06:06 2025 +0200 @@ -1,5 +1,5 @@ /* -* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2025 Olaf Wintermann. All rights reserved. * @@ -27,36 +27,19 @@ */ #ifndef CONTAINER_H - -#include "../ui/container.h" - #define CONTAINER_H -#define UI_APPLY_LAYOUT(layout, args) \ - layout.fill = args->fill; \ - layout.hexpand = args->hexpand; \ - layout.vexpand = args->vexpand; \ - layout.hfill = args->hfill; \ - layout.vfill = args->vfill; \ - layout.override_defaults = args->override_defaults; \ - layout.colspan = args->colspan; \ - layout.rowspan = args->rowspan - -typedef struct UiLayout UiLayout; +#include "../ui/container.h" +#include "toolkit.h" +#include "grid.h" -struct UiLayout { - UiBool fill; - UiBool newline; - char *label; - UiBool hexpand; - UiBool vexpand; - UiBool hfill; - UiBool vfill; - UiBool override_defaults; - int width; - int colspan; - int rowspan; -}; +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct UiContainerPrivate UiContainerPrivate; +typedef struct UiGridLayoutContainer UiGridLayoutContainer; +typedef struct UiBoxContainer UiBoxContainer; enum UiBoxOrientation { UI_BOX_VERTICAL = 0, @@ -70,8 +53,6 @@ }; typedef enum UiContainerType UiContainerType; -typedef struct UiContainerPrivate UiContainerPrivate; - typedef struct UiRect { int x; int y; @@ -82,10 +63,48 @@ struct UiContainerPrivate { UiContainerX container; - void (*prepare)(UiContainerPrivate*, UiRect*); - void (*add)(UiContainerPrivate*, UiRect*, W32Widget*); + HWND (*parent)(UiContainerPrivate*); + void (*add)(UiContainerPrivate*, W32Widget*, UiLayout*); UiContainerType type; - UiLayout layout; + HWND hwnd; +}; + +struct UiBoxContainer { + UiContainerPrivate container; + UiGridLayout *layout; + UiBoxOrientation orientation; + int x; + int y; }; +struct UiGridLayoutContainer { + UiContainerPrivate container; + UiGridLayout *layout; + int x; + int y; + UiBool def_hexpand; + UiBool def_vexpand; + UiBool def_hfill; + UiBool def_vfill; +}; + +UiContainerPrivate* ui_obj_container(UiObject *obj); +HWND ui_container_get_parent(UiContainerPrivate *ctn); +void ui_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout); + +UiContainerX* ui_box_container_create(UiObject *obj, HWND hwnd, UiBoxOrientation orientation, short spacing, GridEdgeInsets padding); +void ui_box_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout); + +UiContainerX* ui_grid_container_create( + UiObject *obj, + HWND hwnd, + short columnspacing, + short rowspacing, + GridEdgeInsets padding); +void ui_grid_container_add(UiContainerPrivate *ctn, W32Widget *widget, UiLayout *layout); + +#ifdef __cplusplus +} +#endif + #endif //CONTAINER_H
--- a/ui/win32/grid.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/grid.c Fri Oct 10 09:06:06 2025 +0200 @@ -31,11 +31,12 @@ #include "../../ucx/cx/array_list.h" #include "../common/context.h" -UiGridLayout* ui_grid_container(UiObject *obj, HWND control, short padding, short columnspacing, short rowspacing) { - UiGridLayout *grid = cxZalloc(obj->ctx->allocator, sizeof(UiGridLayout)); - grid->hwnd = control; - grid->widgets = cxArrayListCreate(obj->ctx->allocator, NULL, sizeof(GridElm), 32); - grid->padding = padding; +#include <stdio.h> +#include <stdlib.h> + +UiGridLayout* ui_grid_layout_create(const CxAllocator *a, short columnspacing, short rowspacing) { + UiGridLayout *grid = cxZalloc(a, sizeof(UiGridLayout)); + grid->widgets = cxArrayListCreate(a, NULL, sizeof(GridElm), 32); grid->columnspacing = columnspacing; grid->rowspacing = rowspacing; return grid; @@ -48,14 +49,249 @@ W32Widget *widget, GridLayoutInfo *layout) { + // add the widget GridElm elm; elm.widget = widget; - elm.x = x; - elm.y = y; + elm.gridx = x; + elm.gridy = y; elm.layout = *layout; - cxListAdd(grid->widgets, elm); + cxListAdd(grid->widgets, &elm); + + // adjust max col/row count + if (x > grid->max_column) { + grid->max_column = x; + } + if (y > grid->max_row) { + grid->max_row = y; + } } -void ui_grid_layout(UiGridLayout *grid) { - // TODO +void ui_grid_layout(UiGridLayout *grid, int width, int height) { + int ncols = grid->max_column+1; + int nrows = grid->max_row+1; + + GridDef *cols = calloc(ncols, sizeof(GridDef)); + GridDef *rows = calloc(nrows, sizeof(GridDef)); + + int colspacing = grid->columnspacing; + int rowspacing = grid->rowspacing; + + int span_max = 1; + for(int r=0;r<2;r++) { + CxIterator i = cxListIterator(grid->widgets); + cx_foreach(GridElm *, elm, i) { + int x = elm->gridx; + int y = elm->gridy; + GridDef *col = &cols[x]; + GridDef *row = &rows[y]; + + W32Size size = w32_widget_get_preferred_size(elm->widget); + elm->layout.preferred_width = size.width; + elm->layout.preferred_height = size.height; + + int elm_width = size.width + elm->layout.margin.left + elm->layout.margin.right; + if(elm_width > cols[elm->gridx].preferred_size && elm->layout.colspan <= 1 && span_max == 1) { + cols[elm->gridx].preferred_size = elm_width; + } + int elm_height = size.height + elm->layout.margin.top + elm->layout.margin.bottom; + if(elm_height > rows[elm->gridy].preferred_size && elm->layout.rowspan <= 1 && span_max == 1) { + rows[elm->gridy].preferred_size = elm_height; + } + + if(elm->layout.rowspan > span_max || elm->layout.colspan > span_max) { + continue; + } + + int end_col = x+elm->layout.colspan; + if(end_col > ncols) { + end_col = ncols; + } + int end_row = y+elm->layout.rowspan; + if(end_row > nrows) { + end_row = nrows; + } + + // are all columns in the span > preferred_width? + if(elm->layout.colspan > 1) { + int span_width = 0; + GridDef *last_col = col; + for(int c=x;c<end_col;c++) { + span_width += cols[c].size; + last_col = &cols[c]; + } + if(span_width < elm->layout.preferred_width) { + last_col->size += elm->layout.preferred_width - span_width; + } + } + + // are all rows in the span > preferred_height? + if(elm->layout.rowspan > 1) { + int span_height = 0; + GridDef *last_row = row; + for(int c=x;c<end_row;c++) { + span_height += rows[c].size; + last_row = &rows[c]; + } + if(span_height < elm->layout.preferred_height) { + last_row->size += elm->layout.preferred_height - span_height; + } + } + + if(elm->layout.hexpand) { + if(elm->layout.colspan > 1) { + // check if any column in the span is expanding + // if not, make the last column expanding + GridDef *last_col = col; + for(int c=x;c<end_col;c++) { + last_col = &cols[c]; + if(last_col->expand) { + break; + } + } + last_col->expand = TRUE; + } else { + col->expand = TRUE; + } + } + if(elm->layout.vexpand) { + if(elm->layout.rowspan > 1) { + // same as colspan + GridDef *last_row = row; + for(int c=x;c<nrows;c++) { + last_row = &rows[c]; + if(last_row->expand) { + break; + } + } + last_row->expand = TRUE; + } else { + row->expand = TRUE; + } + } + } + span_max = 50000; // not sure if this is unreasonable low or high + } + + int col_ext = 0; + int row_ext = 0; + + int preferred_width = 0; + int preferred_height = 0; + for(int j=0;j<ncols;j++) { + preferred_width += cols[j].preferred_size; + if(cols[j].expand) { + col_ext++; + } + } + for(int j=0;j<nrows;j++) { + preferred_height += rows[j].preferred_size; + if(rows[j].expand) { + row_ext++; + } + } + if(ncols > 0) { + preferred_width += (ncols-1) * colspacing; + } + if(nrows > 0) { + preferred_height += (nrows-1) * rowspacing; + } + + grid->preferred_width = preferred_width; + grid->preferred_height = preferred_height; + + int hremaining = width - preferred_width; + int vremaining = height - preferred_height; + int hext = col_ext > 0 ? hremaining/col_ext : 0; + int vext = row_ext > 0 ? vremaining/row_ext : 0; + + for(int j=0;j<ncols;j++) { + GridDef *col = &cols[j]; + if(col->expand) { + col->size = col->preferred_size + hext; + } else { + col->size = col->preferred_size; + } + } + for(int j=0;j<nrows;j++) { + GridDef *row = &rows[j]; + if(row->expand) { + row->size = row->preferred_size + vext; + } else { + row->size = row->preferred_size; + } + } + + int pos = 0; + for(int j=0;j<ncols;j++) { + cols[j].pos = pos; + pos += cols[j].size + colspacing; + } + pos = 0; + for(int j=0;j<nrows;j++) { + rows[j].pos = pos; + pos += rows[j].size + rowspacing; + } + + CxIterator i = cxListIterator(grid->widgets); + cx_foreach(GridElm *, elm, i) { + GridDef *col = &cols[elm->gridx]; + GridDef *row = &rows[elm->gridy]; + + int child_width = 0; + int child_height = 0; + int child_x = 0; + int child_y = 0; + if(elm->layout.hfill) { + if(elm->layout.colspan > 1) { + int cwidth = 0; + int end_col = elm->gridx + elm->layout.colspan; + if(end_col > ncols) { + end_col = ncols; + } + int real_span = 0; + for(int c=elm->gridx;c<end_col;c++) { + cwidth += cols[c].size; + real_span++; + } + if(real_span > 0) { + cwidth += (real_span-1) * colspacing; + } + child_width = cwidth; + } else { + child_width = col->size; + } + } else { + child_width = elm->layout.preferred_width; + } + child_width -= elm->layout.margin.left + elm->layout.margin.right; + + if(elm->layout.vfill) { + if(elm->layout.rowspan > 1) { + int rheight = 0; + int end_row = elm->gridy + elm->layout.rowspan; + if(end_row > nrows) { + end_row = nrows; + } + int real_span = 0; + for(int r=elm->gridy;r<end_row;r++) { + rheight += rows[r].size; + real_span++; + } + if(real_span > 0) { + rheight += (real_span-1) * rowspacing; + } + child_height = rheight; + } + child_height = row->size; + } else { + child_height = elm->layout.preferred_height; + } + + child_x = col->pos + elm->layout.margin.left; + child_y = row->pos + elm->layout.margin.top; + SetWindowPos(elm->widget->hwnd, NULL, child_x, child_y, child_width, child_height, SWP_NOZORDER); + } + + free(cols); + free(rows); }
--- a/ui/win32/grid.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/grid.h Fri Oct 10 09:06:06 2025 +0200 @@ -28,47 +28,64 @@ #ifndef GRID_H #define GRID_H -#include "container.h" +#include "../ui/win32.h" #include <stdbool.h> #include <cx/array_list.h> -typedef struct GridElm { - W32Widget *widget; - short x; - short y; - GridLayoutInfo layout; -} GridElm; +#include "win32.h" + +#define INSETS_ZERO (GridEdgeInsets){0} +typedef struct GridEdgeInsets { + short top; + short bottom; + short left; + short right; +} GridEdgeInsets; typedef struct GridLayoutInfo { - short margin_left; - short margin_right; - short margin_top; - short margin_bottom; + GridEdgeInsets margin; short colspan; short rowspan; - short preferred_width; - short preferred_height; + int preferred_width; + int preferred_height; bool hexpand; bool vexpand; bool hfill; bool vfill; } GridLayoutInfo; +typedef struct GridElm { + W32Widget *widget; + short gridx; + short gridy; + GridLayoutInfo layout; +} GridElm; + typedef struct UiGridLayout { - HWND hwnd; - - short padding; + GridEdgeInsets padding; short columnspacing; short rowspacing; + int preferred_width; + int preferred_height; + /* * list element type: GridElm */ CxList *widgets; + int max_column; + int max_row; } UiGridLayout; -UiGridLayout* ui_grid_container(UiObject *obj, HWND control, short padding, short columnspacing, short rowspacing); +typedef struct GridDef { + int size; + int pos; + int preferred_size; + BOOLEAN expand; +} GridDef; + +UiGridLayout* ui_grid_layout_create(const CxAllocator *a, short columnspacing, short rowspacing); void ui_grid_add_widget( UiGridLayout *grid, @@ -77,6 +94,6 @@ W32Widget *widget, GridLayoutInfo *layout); -void ui_grid_layout(UiGridLayout *grid); +void ui_grid_layout(UiGridLayout *grid, int width, int height); #endif //GRID_H
--- a/ui/win32/objs.mk Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/objs.mk Fri Oct 10 09:06:06 2025 +0200 @@ -30,10 +30,13 @@ WIN32_OBJPRE = $(OBJ_DIR)$(WIN32_SRC_DIR) WIN32OBJ = toolkit.obj +WIN32OBJ += win32.obj +WIN32OBJ += widget.obj WIN32OBJ += window.obj WIN32OBJ += image.obj WIN32OBJ += container.obj WIN32OBJ += button.obj +WIN32OBJ += grid.obj TOOLKITOBJS += $(WIN32OBJ:%=$(WIN32_OBJPRE)%) TOOLKITSOURCE += $(WIN32OBJ:%.obj=win32/%.c)
--- a/ui/win32/toolkit.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/toolkit.c Fri Oct 10 09:06:06 2025 +0200 @@ -36,9 +36,13 @@ #include "../common/document.h" #include "../common/properties.h" +#include "../ui/widget.h" + #include <stdio.h> #include <stdlib.h> +#include <commctrl.h> + static const char *application_name; static ui_callback startup_func; @@ -48,16 +52,36 @@ static ui_callback exit_func; void *exit_data; +static HFONT ui_font = NULL; + void ui_init(const char *appname, int argc, char **argv) { application_name = appname; uic_init_global_context(); - uic_docmgr_init(); uic_menu_init(); uic_toolbar_init(); uic_load_app_properties(); ui_window_init(); + + INITCOMMONCONTROLSEX icex = { sizeof(icex), ICC_WIN95_CLASSES }; + InitCommonControlsEx(&icex); + + SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + + NONCLIENTMETRICS ncm = { sizeof(ncm) }; + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, FALSE); + ui_font = CreateFontIndirect(&ncm.lfMessageFont); +} + +HFONT ui_win32_get_font(void) { + return ui_font; +} + +void ui_win32_set_ui_font(HWND control) { + if (ui_font) { + SendMessage(control, WM_SETFONT, (WPARAM)ui_font, TRUE); + } } const char* ui_appname() { @@ -96,3 +120,7 @@ } uic_store_app_properties(); } + +void ui_show(UiObject *obj) { + ui_set_visible(obj->widget, TRUE); +} \ No newline at end of file
--- a/ui/win32/toolkit.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/toolkit.h Fri Oct 10 09:06:06 2025 +0200 @@ -34,10 +34,27 @@ #include "../common/context.h" #include "../common/object.h" +#include "win32.h" + #ifdef __cplusplus extern "C" { #endif +/* + * widget struct that can be used for most primitive widgets, + * like buttons, checkboxes + */ +typedef struct UiWidget { + W32Widget widget; + UiObject *obj; + UiVar *var; + ui_callback callback; + void *callbackdata; + int64_t intvalue; +} UiWidget; + +HFONT ui_win32_get_font(void); +void ui_win32_set_ui_font(HWND control); #ifdef __cplusplus
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/win32/widget.c Fri Oct 10 09:06:06 2025 +0200 @@ -0,0 +1,43 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "widget.h" + +void ui_set_enabled(UIWIDGET widget, UiBool enable) { + W32Widget *w = (W32Widget *)widget; + if (w->wclass->enable) { + w->wclass->enable(w, enable); + } +} + +void ui_set_visible(UIWIDGET widget, UiBool visible) { + W32Widget *w = (W32Widget *)widget; + if (w->wclass->show) { + w->wclass->show(w, visible); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/win32/widget.h Fri Oct 10 09:06:06 2025 +0200 @@ -0,0 +1,44 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WIDGET_H +#define WIDGET_H + +#include "toolkit.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif //WIDGET_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/win32/win32.c Fri Oct 10 09:06:06 2025 +0200 @@ -0,0 +1,67 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdlib.h> + +#include "win32.h" + +W32Widget* w32_widget_new(W32WidgetClass *wclass, HWND hwnd) { + return w32_widget_create(wclass, hwnd, sizeof(W32Widget)); +} + +void* w32_widget_create(W32WidgetClass *wclass, HWND hwnd, size_t obj_size) { + W32Widget *w = calloc(obj_size, 1); + w->wclass = wclass; + w->hwnd = hwnd; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)w); + return w; +} + +W32Size w32_widget_get_preferred_size(W32Widget *w) { + if (w->wclass->get_preferred_size) { + return w->wclass->get_preferred_size(w); + } + return (W32Size){0,0}; +} + +void w32_widget_default_destroy(W32Widget *w) { + free(w); +} + +void w32_widget_default_show(W32Widget *w, BOOLEAN show) { + ShowWindow(w->hwnd, show ? SW_SHOW : SW_HIDE); +} + +void w32_widget_default_enable(W32Widget *w, BOOLEAN enable) { + // TODO +} + +W32Size w32_widget_default_get_preferred_size(W32Widget *widget) { + return (W32Size){0,0}; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ui/win32/win32.h Fri Oct 10 09:06:06 2025 +0200 @@ -0,0 +1,61 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2025 Olaf Wintermann. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UI_TK_WIN32_H +#define UI_TK_WIN32_H + +#include "../ui/win32.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Creates a standard W32Widget object for an HWND handle and stores the widget object as + * GWLP_USERDATA in the window. + */ +W32Widget* w32_widget_new(W32WidgetClass *wclass, HWND hwnd); + +/* + * Same as w32_widget_new, but uses obj_size for allocation, allowing to create objects + * derived from W32Widget. + */ +void* w32_widget_create(W32WidgetClass *wclass, HWND hwnd, size_t obj_size); + +W32Size w32_widget_get_preferred_size(W32Widget *w); + +void w32_widget_default_destroy(W32Widget *w); +void w32_widget_default_show(W32Widget *w, BOOLEAN show); +void w32_widget_default_enable(W32Widget *w, BOOLEAN enable); +W32Size w32_widget_default_get_preferred_size(W32Widget *widget); + +#ifdef __cplusplus +} +#endif + +#endif //UI_TK_WIN32_H
--- a/ui/win32/window.c Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/window.c Fri Oct 10 09:06:06 2025 +0200 @@ -27,7 +27,9 @@ */ #include "window.h" -#include "Windows.h" +#include <Windows.h> + +#include "container.h" #include "../common/object.h" @@ -36,18 +38,38 @@ #include <stdio.h> #include <stdlib.h> +#include "win32.h" + +static W32WidgetClass w32_toplevel_widget_class = { + .eventproc = ui_window_widget_event, + .show = ui_window_widget_show, + .enable = NULL, + .get_preferred_size = NULL, + .destroy = w32_widget_default_destroy +}; static HINSTANCE hInstance; static const char *mainWindowClass = "UiMainWindow"; LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + W32Widget *widget = (W32Widget*)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (widget && widget->wclass->eventproc) { + widget->wclass->eventproc(widget, hwnd, uMsg, wParam, lParam); + } switch(uMsg) { - case WM_DESTROY: - PostQuitMessage(0); - break; - default: - return DefWindowProc(hwnd, uMsg, wParam, lParam); + case WM_DESTROY: { + PostQuitMessage(0); + break; + } + case WM_COMMAND: { + HWND hwndCtrl = (HWND)lParam; + W32Widget *cmdWidget = (W32Widget*)GetWindowLongPtr(hwndCtrl, GWLP_USERDATA); + if (cmdWidget && cmdWidget->wclass->eventproc) { + cmdWidget->wclass->eventproc(cmdWidget, hwnd, uMsg, wParam, lParam); + } + } + default: return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 0; } @@ -60,7 +82,7 @@ wc.hInstance = hInstance; wc.lpszClassName = mainWindowClass; wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); if(!RegisterClassExA(&wc)) { MessageBox(NULL, "RegisterClassEx failed", "Error", MB_ICONERROR); @@ -71,12 +93,12 @@ static UiObject* create_window(const char *title, void *window_data, bool simple) { UiObject *obj = uic_object_new_toplevel(); obj->window = window_data; - + HWND hwnd = CreateWindowExA( 0, "UiMainWindow", title, - WS_OVERLAPPEDWINDOW | WS_VISIBLE, + WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 800, @@ -85,10 +107,18 @@ NULL, hInstance, NULL); - - ShowWindow(hwnd, SW_SHOWNORMAL); + UpdateWindow(hwnd); - + + UiContainerX *container = ui_box_container_create(obj, hwnd, UI_BOX_VERTICAL, 0, INSETS_ZERO); + uic_object_push_container(obj, container); + + UiWindow *widget = w32_widget_create(&w32_toplevel_widget_class, hwnd, sizeof(UiWindow)); + widget->obj = obj; + widget->container = (UiBoxContainer *)container; + obj->widget = (W32Widget*)widget; + obj->ref = 1; + return obj; } @@ -96,3 +126,19 @@ return create_window(title, window_data, FALSE); } + +void ui_window_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + UiWindow *window = (UiWindow*)widget; + switch (uMsg) { + case WM_SIZE: { + int width = LOWORD(lParam); + int height = HIWORD(lParam); + ui_grid_layout(window->container->layout, width, height); + break; + } + } +} + +void ui_window_widget_show(W32Widget *w, BOOLEAN show) { + ShowWindow(w->hwnd, show ? SW_SHOWNORMAL : SW_HIDE); +}
--- a/ui/win32/window.h Fri Oct 10 09:05:11 2025 +0200 +++ b/ui/win32/window.h Fri Oct 10 09:06:06 2025 +0200 @@ -35,14 +35,23 @@ #include "../common/object.h" #include "toolkit.h" - +#include "container.h" #ifdef __cplusplus extern "C" { #endif +typedef struct UiWindow { + W32Widget widget; + UiObject *obj; + UiBoxContainer *container; +} UiWindow; + void ui_window_init(void); +void ui_window_widget_event(W32Widget *widget, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +void ui_window_widget_show(W32Widget *w, BOOLEAN show); + #ifdef __cplusplus } #endif