/* * el_win32.c — Win32 backend for the el native widget system. * * This file implements the Windows Win32 widget layer that el_seed.c calls * through to when EL_TARGET_WIN32 is defined. * * Architecture: * el program (el code) * → __widget_* C builtins in el_seed.c * → el_win32_* C functions declared here * → HWND/Win32 API / GDI * * Widget handles: every widget (window, view, control) is assigned an int64_t * slot index into _el_widgets[]. The el program holds these as opaque Int values. * Slot 0 is never valid (reserved; handle -1 = invalid). * * Threading: Win32 UI must run on the main thread. el is single-threaded for * native UI, so all calls are direct. The message loop runs on the main thread * and never returns. * * Callback mechanism: when a widget fires an event (button click, text change), * the bridge looks up the registered callback function name, then calls * GetProcAddress(GetModuleHandle(NULL), fn_name)(widget_handle, event_data) * This resolves the El function symbol at runtime, matching el_seed.c's * dlsym(RTLD_DEFAULT, fn_name) pattern on POSIX. * * Layout: VStack and HStack containers maintain a child HWND list and reflow * children on WM_SIZE using SetWindowPos. ZStack uses absolute positioning. * * Subclassing: SetWindowSubclass (comctl32) is used to intercept WM_COMMAND * and WM_NOTIFY on container windows so button clicks and edit changes route * back to el callbacks. * * String encoding: el strings are UTF-8 (const char*). All Win32 text APIs * use wide strings (WCHAR*). Conversion via MultiByteToWideChar / * WideCharToMultiByte throughout. * * Compile: * cl /DEL_TARGET_WIN32 el_win32.c /c /Fo:el_win32.obj * (link with: comctl32.lib user32.lib gdi32.lib) * or with clang-cl: * clang-cl /DEL_TARGET_WIN32 -c el_win32.c -o el_win32.obj */ #ifdef EL_TARGET_WIN32 #define WIN32_LEAN_AND_MEAN #ifndef UNICODE #define UNICODE #endif #ifndef _UNICODE #define _UNICODE #endif #include #include #include #include #include #include #include "el_runtime.h" /* ── Compile-time linkage ─────────────────────────────────────────────────── */ #pragma comment(lib, "comctl32.lib") #pragma comment(lib, "user32.lib") #pragma comment(lib, "gdi32.lib") /* ── Custom window messages ───────────────────────────────────────────────── */ /* WM_USER+1: posted to a container to trigger a layout reflow pass. */ #define WM_EL_REFLOW (WM_USER + 1) /* ── Widget type enum ─────────────────────────────────────────────────────── */ #define EL_WIN32_MAX_WIDGETS 4096 #define EL_WIN32_MAX_CHILDREN 256 typedef enum { EL_WIDGET_FREE = 0, EL_WIDGET_WINDOW = 1, EL_WIDGET_VSTACK = 2, EL_WIDGET_HSTACK = 3, EL_WIDGET_ZSTACK = 4, EL_WIDGET_SCROLL = 5, EL_WIDGET_LABEL = 6, EL_WIDGET_BUTTON = 7, EL_WIDGET_TEXTFIELD = 8, EL_WIDGET_TEXTAREA = 9, EL_WIDGET_IMAGE = 10, } ElWidgetKind; /* Per-slot data. */ typedef struct { ElWidgetKind kind; HWND hwnd; /* primary window handle; NULL if free */ char* cb_click; /* El function name for click / submit events */ char* cb_change; /* El function name for value-change events */ /* Layout data (used by VSTACK / HSTACK / ZSTACK / WINDOW containers) */ int spacing; /* pixels between children */ int pad_top; int pad_right; int pad_bottom; int pad_left; int flex; /* flex weight; 0 = fixed size */ /* Fixed size overrides (0 = not set) */ int fixed_w; int fixed_h; /* Child list for layout containers */ int64_t children[EL_WIN32_MAX_CHILDREN]; int child_count; /* Color state (stored for WM_CTLCOLOR* replies) */ COLORREF fg_color; COLORREF bg_color; HBRUSH bg_brush; int has_fg; int has_bg; /* Font */ HFONT font; /* NULL = default system font */ /* Image: HBITMAP loaded for SS_BITMAP static */ HBITMAP hbmp; } ElWidget; static ElWidget _el_widgets[EL_WIN32_MAX_WIDGETS]; static int _el_widget_count = 0; /* ── Window class names ───────────────────────────────────────────────────── */ static const WCHAR* EL_WNDCLASS_VSTACK = L"ElVStack"; static const WCHAR* EL_WNDCLASS_HSTACK = L"ElHStack"; static const WCHAR* EL_WNDCLASS_ZSTACK = L"ElZStack"; static const WCHAR* EL_WNDCLASS_SCROLL = L"ElScroll"; static const WCHAR* EL_WNDCLASS_WINDOW = L"ElWindow"; /* ── Forward declarations ─────────────────────────────────────────────────── */ static void el_win32_reflow(int64_t container_handle); static LRESULT CALLBACK el_container_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); static LRESULT CALLBACK el_toplevel_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); /* ── String helpers ───────────────────────────────────────────────────────── */ /* UTF-8 → WCHAR*. Caller must free(). Returns NULL on failure. */ static WCHAR* el_utf8_to_wide(const char* s) { if (!s) s = ""; int n = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0); if (n <= 0) return _wcsdup(L""); WCHAR* buf = (WCHAR*)malloc((size_t)n * sizeof(WCHAR)); if (!buf) return _wcsdup(L""); MultiByteToWideChar(CP_UTF8, 0, s, -1, buf, n); return buf; } /* WCHAR* → UTF-8 char*. Caller must free(). Returns NULL on failure. */ static char* el_wide_to_utf8(const WCHAR* ws) { if (!ws) return strdup(""); int n = WideCharToMultiByte(CP_UTF8, 0, ws, -1, NULL, 0, NULL, NULL); if (n <= 0) return strdup(""); char* buf = (char*)malloc((size_t)n); if (!buf) return strdup(""); WideCharToMultiByte(CP_UTF8, 0, ws, -1, buf, n, NULL, NULL); return buf; } /* ── Slot management ──────────────────────────────────────────────────────── */ static int64_t el_widget_alloc(ElWidgetKind kind, HWND hwnd) { for (int i = 1; i < EL_WIN32_MAX_WIDGETS; i++) { if (_el_widgets[i].kind == EL_WIDGET_FREE) { ElWidget* w = &_el_widgets[i]; memset(w, 0, sizeof(ElWidget)); w->kind = kind; w->hwnd = hwnd; if (i > _el_widget_count) _el_widget_count = i; return (int64_t)i; } } return -1; } static ElWidget* el_widget_get(int64_t handle) { if (handle <= 0 || handle >= EL_WIN32_MAX_WIDGETS) return NULL; if (_el_widgets[handle].kind == EL_WIDGET_FREE) return NULL; return &_el_widgets[handle]; } static void el_widget_free_slot(int64_t handle) { ElWidget* w = el_widget_get(handle); if (!w) return; free(w->cb_click); w->cb_click = NULL; free(w->cb_change); w->cb_change = NULL; if (w->bg_brush) { DeleteObject(w->bg_brush); w->bg_brush = NULL; } if (w->font) { DeleteObject(w->font); w->font = NULL; } if (w->hbmp) { DeleteObject(w->hbmp); w->hbmp = NULL; } w->hwnd = NULL; w->kind = EL_WIDGET_FREE; } /* Map HWND → widget slot (O(n) linear scan; table is small). */ static int64_t el_hwnd_to_slot(HWND hwnd) { for (int i = 1; i <= _el_widget_count; i++) { if (_el_widgets[i].kind != EL_WIDGET_FREE && _el_widgets[i].hwnd == hwnd) return (int64_t)i; } return -1; } /* ── El callback invocation ──────────────────────────────────────────────── */ /* * Invoke an El callback by symbol name. El functions have the signature: * fn handler(handle: Int, data: String) -> Void * compiled to: void fn_name(el_val_t handle, el_val_t data) */ typedef void (*ElCb2)(int64_t handle, int64_t data); static void el_win32_invoke_cb(const char* fn_name, int64_t handle, const char* data) { if (!fn_name || !*fn_name) return; FARPROC proc = GetProcAddress(GetModuleHandleW(NULL), fn_name); if (!proc) return; ElCb2 fn = (ElCb2)(void*)proc; fn(handle, (int64_t)(uintptr_t)(data ? data : "")); } /* ── Layout reflow ────────────────────────────────────────────────────────── */ /* * Reflow the direct children of a container widget. * * For VSTACK: stack children top-to-bottom, respecting padding and spacing. * Children with flex > 0 divide the leftover height proportionally. * Children with fixed_h > 0 use that height; otherwise QueryWidget. * * For HSTACK: same logic, left-to-right. * * For WINDOW: its content area is the client rect; children stacked vertically * (WINDOW acts like an outer VSTACK for direct child attachment purposes). * * For ZSTACK: children fill the entire client rect (absolute; no reflow). * * For SCROLL: the single child fills the client rect width; height is its * natural height (scroll bar handles overflow). */ static void el_win32_reflow(int64_t handle) { ElWidget* pw = el_widget_get(handle); if (!pw || !pw->hwnd) return; RECT cr; GetClientRect(pw->hwnd, &cr); int total_w = cr.right - cr.left; int total_h = cr.bottom - cr.top; int pt = pw->pad_top; int pr = pw->pad_right; int pb = pw->pad_bottom; int pl = pw->pad_left; int sp = pw->spacing; int n = pw->child_count; if (n == 0) return; if (pw->kind == EL_WIDGET_ZSTACK) { /* All children fill the client rect. */ int cw = total_w - pl - pr; int ch = total_h - pt - pb; if (cw < 0) cw = 0; if (ch < 0) ch = 0; for (int i = 0; i < n; i++) { ElWidget* cw_ = el_widget_get(pw->children[i]); if (!cw_ || !cw_->hwnd) continue; SetWindowPos(cw_->hwnd, NULL, pl, pt, cw, ch, SWP_NOZORDER | SWP_NOACTIVATE); } return; } if (pw->kind == EL_WIDGET_SCROLL) { /* Single child fills width; natural height. */ if (n < 1) return; ElWidget* cw_ = el_widget_get(pw->children[0]); if (!cw_ || !cw_->hwnd) return; int cw = total_w - pl - pr; if (cw < 0) cw = 0; /* Measure natural height. */ RECT cr2; GetWindowRect(cw_->hwnd, &cr2); int ch = cr2.bottom - cr2.top; if (cw_->fixed_h > 0) ch = cw_->fixed_h; SetWindowPos(cw_->hwnd, NULL, pl, pt, cw, ch, SWP_NOZORDER | SWP_NOACTIVATE); /* Update scroll range. */ SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_RANGE | SIF_PAGE; si.nMin = 0; si.nMax = ch + pt + pb; si.nPage = (UINT)total_h; SetScrollInfo(pw->hwnd, SB_VERT, &si, TRUE); return; } /* VSTACK, HSTACK, WINDOW */ int is_vert = (pw->kind == EL_WIDGET_VSTACK || pw->kind == EL_WIDGET_WINDOW); /* First pass: determine fixed sizes and total flex weight. */ int total_flex = 0; int fixed_used = 0; /* pixels consumed by fixed children + spacing */ /* Per-child resolved size in the main axis. */ int* sizes = (int*)malloc((size_t)n * sizeof(int)); if (!sizes) return; for (int i = 0; i < n; i++) { ElWidget* cw_ = el_widget_get(pw->children[i]); sizes[i] = 0; if (!cw_ || !cw_->hwnd) continue; int fixed = is_vert ? cw_->fixed_h : cw_->fixed_w; if (fixed > 0) { sizes[i] = fixed; fixed_used += fixed; } else if (cw_->flex > 0) { total_flex += cw_->flex; } else { /* No flex, no fixed: measure current window size as default. */ RECT wr; GetWindowRect(cw_->hwnd, &wr); int nat = is_vert ? (wr.bottom - wr.top) : (wr.right - wr.left); if (nat < 1) nat = is_vert ? 24 : 80; /* reasonable fallback */ sizes[i] = nat; fixed_used += nat; } if (i < n - 1) fixed_used += sp; /* spacing between children */ } /* Available space for flex children. */ int avail = (is_vert ? (total_h - pt - pb) : (total_w - pl - pr)) - fixed_used; if (avail < 0) avail = 0; /* Second pass: assign flex sizes. */ if (total_flex > 0) { for (int i = 0; i < n; i++) { ElWidget* cw_ = el_widget_get(pw->children[i]); if (!cw_ || !cw_->hwnd) continue; if (cw_->flex > 0 && sizes[i] == 0) { sizes[i] = (avail * cw_->flex) / total_flex; } } } /* Third pass: position children. */ int cursor = is_vert ? pt : pl; for (int i = 0; i < n; i++) { ElWidget* cw_ = el_widget_get(pw->children[i]); if (!cw_ || !cw_->hwnd) continue; int x, y, cw, ch; if (is_vert) { x = pl; y = cursor; cw = (cw_->fixed_w > 0) ? cw_->fixed_w : (total_w - pl - pr); ch = sizes[i]; if (cw < 0) cw = 0; if (ch < 0) ch = 0; cursor += ch + sp; } else { x = cursor; y = pt; cw = sizes[i]; ch = (cw_->fixed_h > 0) ? cw_->fixed_h : (total_h - pt - pb); if (cw < 0) cw = 0; if (ch < 0) ch = 0; cursor += cw + sp; } SetWindowPos(cw_->hwnd, NULL, x, y, cw, ch, SWP_NOZORDER | SWP_NOACTIVATE); /* Recursively reflow child containers. */ if (cw_->kind == EL_WIDGET_VSTACK || cw_->kind == EL_WIDGET_HSTACK || cw_->kind == EL_WIDGET_ZSTACK || cw_->kind == EL_WIDGET_SCROLL || cw_->kind == EL_WIDGET_WINDOW) { el_win32_reflow(pw->children[i]); } } free(sizes); } /* ── Container WndProc ────────────────────────────────────────────────────── */ /* * Used for VSTACK, HSTACK, ZSTACK, SCROLL containers. * Handles WM_SIZE (reflow), WM_CTLCOLOR* (color theming), WM_COMMAND (events), * WM_VSCROLL (scroll container). */ static LRESULT CALLBACK el_container_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { int64_t slot = el_hwnd_to_slot(hwnd); ElWidget* w = (slot >= 0) ? el_widget_get(slot) : NULL; switch (msg) { case WM_SIZE: if (w) el_win32_reflow(slot); return 0; case WM_EL_REFLOW: if (w) el_win32_reflow(slot); return 0; case WM_VSCROLL: { if (!w || w->kind != EL_WIDGET_SCROLL) break; SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_ALL; GetScrollInfo(hwnd, SB_VERT, &si); int pos = si.nPos; switch (LOWORD(wp)) { case SB_LINEUP: pos -= 20; break; case SB_LINEDOWN: pos += 20; break; case SB_PAGEUP: pos -= si.nPage; break; case SB_PAGEDOWN: pos += si.nPage; break; case SB_THUMBTRACK: pos = si.nTrackPos; break; case SB_TOP: pos = si.nMin; break; case SB_BOTTOM: pos = si.nMax; break; } if (pos < si.nMin) pos = si.nMin; if (pos > si.nMax - (int)si.nPage) pos = si.nMax - (int)si.nPage; si.fMask = SIF_POS; si.nPos = pos; SetScrollInfo(hwnd, SB_VERT, &si, TRUE); /* Scroll child. */ if (w->child_count > 0) { ElWidget* cw_ = el_widget_get(w->children[0]); if (cw_ && cw_->hwnd) { RECT cr; GetClientRect(hwnd, &cr); SetWindowPos(cw_->hwnd, NULL, 0, -pos, cr.right - cr.left, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); } } return 0; } case WM_COMMAND: { /* Route click / change notifications to el callbacks. */ HWND ctrl = (HWND)lp; if (!ctrl) break; int64_t cs = el_hwnd_to_slot(ctrl); ElWidget* cw = (cs >= 0) ? el_widget_get(cs) : NULL; if (!cw) break; WORD notif = HIWORD(wp); if (cw->kind == EL_WIDGET_BUTTON && notif == BN_CLICKED) { el_win32_invoke_cb(cw->cb_click, cs, ""); } else if (cw->kind == EL_WIDGET_TEXTFIELD) { if (notif == EN_CHANGE) { el_win32_invoke_cb(cw->cb_change, cs, ""); } } else if (cw->kind == EL_WIDGET_TEXTAREA) { if (notif == EN_CHANGE) { el_win32_invoke_cb(cw->cb_change, cs, ""); } } /* Propagate to parent so top-level window also sees it. */ HWND parent = GetParent(hwnd); if (parent) SendMessageW(parent, msg, wp, lp); return 0; } case WM_CTLCOLORSTATIC: case WM_CTLCOLOREDIT: case WM_CTLCOLORBTN: { HWND ctrl = (HWND)lp; int64_t cs = el_hwnd_to_slot(ctrl); ElWidget* cw = (cs >= 0) ? el_widget_get(cs) : NULL; if (cw) { HDC hdc = (HDC)wp; if (cw->has_fg) SetTextColor(hdc, cw->fg_color); if (cw->has_bg) { SetBkColor(hdc, cw->bg_color); if (cw->bg_brush) return (LRESULT)cw->bg_brush; } } break; } case WM_ERASEBKGND: { if (w && w->has_bg && w->bg_brush) { HDC hdc = (HDC)wp; RECT rc; GetClientRect(hwnd, &rc); FillRect(hdc, &rc, w->bg_brush); return 1; } break; } case WM_DESTROY: return 0; } return DefWindowProcW(hwnd, msg, wp, lp); } /* ── Top-level window WndProc ────────────────────────────────────────────── */ static LRESULT CALLBACK el_toplevel_wndproc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) { int64_t slot = el_hwnd_to_slot(hwnd); ElWidget* w = (slot >= 0) ? el_widget_get(slot) : NULL; switch (msg) { case WM_SIZE: if (w) el_win32_reflow(slot); return 0; case WM_EL_REFLOW: if (w) el_win32_reflow(slot); return 0; case WM_COMMAND: { /* Bubble COMMAND messages from child controls. */ HWND ctrl = (HWND)lp; if (!ctrl) break; int64_t cs = el_hwnd_to_slot(ctrl); ElWidget* cw = (cs >= 0) ? el_widget_get(cs) : NULL; if (!cw) { /* Not a direct slot child — pass to DefWindowProc. */ break; } WORD notif = HIWORD(wp); if (cw->kind == EL_WIDGET_BUTTON && notif == BN_CLICKED) { el_win32_invoke_cb(cw->cb_click, cs, ""); } else if ((cw->kind == EL_WIDGET_TEXTFIELD || cw->kind == EL_WIDGET_TEXTAREA) && notif == EN_CHANGE) { el_win32_invoke_cb(cw->cb_change, cs, ""); } return 0; } case WM_CTLCOLORSTATIC: case WM_CTLCOLOREDIT: case WM_CTLCOLORBTN: { HWND ctrl = (HWND)lp; int64_t cs = el_hwnd_to_slot(ctrl); ElWidget* cw = (cs >= 0) ? el_widget_get(cs) : NULL; if (cw) { HDC hdc = (HDC)wp; if (cw->has_fg) SetTextColor(hdc, cw->fg_color); if (cw->has_bg) { SetBkColor(hdc, cw->bg_color); if (cw->bg_brush) return (LRESULT)cw->bg_brush; } } break; } case WM_ERASEBKGND: { if (w && w->has_bg && w->bg_brush) { HDC hdc = (HDC)wp; RECT rc; GetClientRect(hwnd, &rc); FillRect(hdc, &rc, w->bg_brush); return 1; } break; } case WM_GETMINMAXINFO: { /* Stored in the window's GWLP_USERDATA as packed two ints: min_w | min_h. */ LONG_PTR ud = GetWindowLongPtrW(hwnd, GWLP_USERDATA); if (ud) { int min_w = (int)(ud & 0xFFFF); int min_h = (int)((ud >> 16) & 0xFFFF); MINMAXINFO* mm = (MINMAXINFO*)lp; if (min_w > 0) mm->ptMinTrackSize.x = min_w; if (min_h > 0) mm->ptMinTrackSize.y = min_h; } return 0; } case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProcW(hwnd, msg, wp, lp); } /* ── Subclass proc for EDIT controls (Enter key → on_submit) ────────────── */ static LRESULT CALLBACK el_edit_subclass_proc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, UINT_PTR uid, DWORD_PTR ref_data) { (void)uid; (void)ref_data; if (msg == WM_KEYDOWN && wp == VK_RETURN) { int64_t slot = el_hwnd_to_slot(hwnd); ElWidget* w = (slot >= 0) ? el_widget_get(slot) : NULL; if (w && w->cb_click) { /* Gather current text as data. */ int len = GetWindowTextLengthW(hwnd); WCHAR* wbuf = (WCHAR*)malloc((size_t)(len + 2) * sizeof(WCHAR)); if (wbuf) { GetWindowTextW(hwnd, wbuf, len + 1); char* utf8 = el_wide_to_utf8(wbuf); free(wbuf); el_win32_invoke_cb(w->cb_click, slot, utf8); free(utf8); } return 0; /* suppress default beep */ } } return DefSubclassProc(hwnd, msg, wp, lp); } /* ── Window-class registration ────────────────────────────────────────────── */ static int _el_classes_registered = 0; static void el_win32_register_classes(void) { if (_el_classes_registered) return; _el_classes_registered = 1; /* Initialize common controls (needed for SetWindowSubclass). */ INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(icc); icc.dwICC = ICC_WIN95_CLASSES | ICC_STANDARD_CLASSES; InitCommonControlsEx(&icc); HINSTANCE hinst = GetModuleHandleW(NULL); /* Container class (VStack, HStack, ZStack, Scroll) */ WNDCLASSW wc; memset(&wc, 0, sizeof(wc)); wc.lpfnWndProc = el_container_wndproc; wc.hInstance = hinst; wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = EL_WNDCLASS_VSTACK; RegisterClassW(&wc); wc.lpszClassName = EL_WNDCLASS_HSTACK; RegisterClassW(&wc); wc.lpszClassName = EL_WNDCLASS_ZSTACK; RegisterClassW(&wc); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpszClassName = EL_WNDCLASS_SCROLL; RegisterClassW(&wc); /* Top-level window class */ memset(&wc, 0, sizeof(wc)); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = el_toplevel_wndproc; wc.hInstance = hinst; wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = EL_WNDCLASS_WINDOW; RegisterClassW(&wc); } /* ── Public C API ─────────────────────────────────────────────────────────── */ /* * el_win32_init — one-time initialisation. Must be called before any other * el_win32_* function. */ void el_win32_init(void) { static int done = 0; if (done) return; done = 1; el_win32_register_classes(); } /* * el_win32_run_loop — enter the Win32 message loop. Never returns. * Must be called from the main thread after all windows are shown. */ void el_win32_run_loop(void) { MSG msg; while (GetMessageW(&msg, NULL, 0, 0) > 0) { TranslateMessage(&msg); DispatchMessageW(&msg); } } /* ── Window ───────────────────────────────────────────────────────────────── */ int64_t el_win32_window_create(const char* title, int width, int height, int min_width, int min_height) { el_win32_register_classes(); HINSTANCE hinst = GetModuleHandleW(NULL); WCHAR* wtitle = el_utf8_to_wide(title ? title : ""); DWORD style = WS_OVERLAPPEDWINDOW; HWND hwnd = CreateWindowW( EL_WNDCLASS_WINDOW, wtitle, style, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, hinst, NULL); free(wtitle); if (!hwnd) return -1; /* Pack min_width / min_height into GWLP_USERDATA (low 16 = min_w, next 16 = min_h). */ LONG_PTR ud = ((LONG_PTR)(min_height & 0xFFFF) << 16) | (LONG_PTR)(min_width & 0xFFFF); SetWindowLongPtrW(hwnd, GWLP_USERDATA, ud); int64_t handle = el_widget_alloc(EL_WIDGET_WINDOW, hwnd); if (handle < 0) { DestroyWindow(hwnd); return -1; } ElWidget* w = el_widget_get(handle); w->spacing = 0; return handle; } void el_win32_window_show(int64_t handle) { ElWidget* w = el_widget_get(handle); if (!w || w->kind != EL_WIDGET_WINDOW || !w->hwnd) return; ShowWindow(w->hwnd, SW_SHOW); UpdateWindow(w->hwnd); } void el_win32_window_set_title(int64_t handle, const char* title) { ElWidget* w = el_widget_get(handle); if (!w || w->kind != EL_WIDGET_WINDOW || !w->hwnd) return; WCHAR* wt = el_utf8_to_wide(title ? title : ""); SetWindowTextW(w->hwnd, wt); free(wt); } /* ── Layout container factories ───────────────────────────────────────────── */ static int64_t el_win32_make_container(const WCHAR* class_name, ElWidgetKind kind, int spacing) { el_win32_register_classes(); HWND hwnd = CreateWindowW( class_name, L"", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, 0, 0, 0, 0, HWND_MESSAGE, /* temporary parent; reparented on add_child */ NULL, GetModuleHandleW(NULL), NULL); if (!hwnd) return -1; int64_t handle = el_widget_alloc(kind, hwnd); if (handle < 0) { DestroyWindow(hwnd); return -1; } ElWidget* w = el_widget_get(handle); w->spacing = spacing; return handle; } int64_t el_win32_vstack_create(int spacing) { return el_win32_make_container(EL_WNDCLASS_VSTACK, EL_WIDGET_VSTACK, spacing); } int64_t el_win32_hstack_create(int spacing) { return el_win32_make_container(EL_WNDCLASS_HSTACK, EL_WIDGET_HSTACK, spacing); } int64_t el_win32_zstack_create(void) { return el_win32_make_container(EL_WNDCLASS_ZSTACK, EL_WIDGET_ZSTACK, 0); } int64_t el_win32_scroll_create(void) { HWND hwnd = CreateWindowW( EL_WNDCLASS_SCROLL, L"", WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_VSCROLL, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL); if (!hwnd) return -1; int64_t handle = el_widget_alloc(EL_WIDGET_SCROLL, hwnd); if (handle < 0) { DestroyWindow(hwnd); return -1; } return handle; } /* ── Widget factories ─────────────────────────────────────────────────────── */ int64_t el_win32_label_create(const char* text) { el_win32_register_classes(); WCHAR* wt = el_utf8_to_wide(text ? text : ""); HWND hwnd = CreateWindowW( L"STATIC", wt, WS_CHILD | WS_VISIBLE | SS_LEFT, 0, 0, 0, 24, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL); free(wt); if (!hwnd) return -1; return el_widget_alloc(EL_WIDGET_LABEL, hwnd); } int64_t el_win32_button_create(const char* label) { el_win32_register_classes(); WCHAR* wl = el_utf8_to_wide(label ? label : ""); HWND hwnd = CreateWindowW( L"BUTTON", wl, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 0, 0, 0, 32, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL); free(wl); if (!hwnd) return -1; return el_widget_alloc(EL_WIDGET_BUTTON, hwnd); } int64_t el_win32_text_field_create(const char* placeholder) { el_win32_register_classes(); HWND hwnd = CreateWindowW( L"EDIT", L"", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL | ES_LEFT, 0, 0, 0, 26, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL); if (!hwnd) return -1; /* Set placeholder / cue banner (Windows Vista+). */ if (placeholder && *placeholder) { WCHAR* wp = el_utf8_to_wide(placeholder); SendMessageW(hwnd, EM_SETCUEBANNER, TRUE, (LPARAM)wp); free(wp); } int64_t handle = el_widget_alloc(EL_WIDGET_TEXTFIELD, hwnd); if (handle < 0) { DestroyWindow(hwnd); return -1; } /* Subclass to intercept VK_RETURN for on_submit. */ SetWindowSubclass(hwnd, el_edit_subclass_proc, (UINT_PTR)handle, 0); return handle; } int64_t el_win32_text_area_create(const char* placeholder) { el_win32_register_classes(); HWND hwnd = CreateWindowW( L"EDIT", L"", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL, 0, 0, 0, 80, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL); if (!hwnd) return -1; /* EDIT multiline does not support EM_SETCUEBANNER, so no placeholder. */ (void)placeholder; int64_t handle = el_widget_alloc(EL_WIDGET_TEXTAREA, hwnd); if (handle < 0) { DestroyWindow(hwnd); return -1; } return handle; } int64_t el_win32_image_create(const char* path) { el_win32_register_classes(); HWND hwnd = CreateWindowW( L"STATIC", L"", WS_CHILD | WS_VISIBLE | SS_BITMAP | SS_REALSIZECONTROL, 0, 0, 0, 0, HWND_MESSAGE, NULL, GetModuleHandleW(NULL), NULL); if (!hwnd) return -1; int64_t handle = el_widget_alloc(EL_WIDGET_IMAGE, hwnd); if (handle < 0) { DestroyWindow(hwnd); return -1; } ElWidget* w = el_widget_get(handle); if (path && *path) { WCHAR* wpath = el_utf8_to_wide(path); HBITMAP hbmp = (HBITMAP)LoadImageW(NULL, wpath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE); free(wpath); if (hbmp) { w->hbmp = hbmp; SendMessageW(hwnd, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hbmp); } } return handle; } /* ── Widget property setters ─────────────────────────────────────────────── */ void el_win32_widget_set_text(int64_t handle, const char* text) { ElWidget* w = el_widget_get(handle); if (!w || !w->hwnd) return; WCHAR* wt = el_utf8_to_wide(text ? text : ""); SetWindowTextW(w->hwnd, wt); free(wt); } const char* el_win32_widget_get_text(int64_t handle) { ElWidget* w = el_widget_get(handle); if (!w || !w->hwnd) return strdup(""); int len = GetWindowTextLengthW(w->hwnd); if (len <= 0) return strdup(""); WCHAR* wbuf = (WCHAR*)malloc((size_t)(len + 2) * sizeof(WCHAR)); if (!wbuf) return strdup(""); GetWindowTextW(w->hwnd, wbuf, len + 1); char* result = el_wide_to_utf8(wbuf); free(wbuf); return result; /* caller must free() */ } void el_win32_widget_set_color(int64_t handle, float r, float g, float b, float a) { (void)a; /* Win32 classic does not support per-control alpha */ ElWidget* w = el_widget_get(handle); if (!w || !w->hwnd) return; w->fg_color = RGB((int)(r * 255.0f), (int)(g * 255.0f), (int)(b * 255.0f)); w->has_fg = 1; InvalidateRect(w->hwnd, NULL, TRUE); } void el_win32_widget_set_bg_color(int64_t handle, float r, float g, float b, float a) { (void)a; ElWidget* w = el_widget_get(handle); if (!w || !w->hwnd) return; w->bg_color = RGB((int)(r * 255.0f), (int)(g * 255.0f), (int)(b * 255.0f)); w->has_bg = 1; if (w->bg_brush) DeleteObject(w->bg_brush); w->bg_brush = CreateSolidBrush(w->bg_color); InvalidateRect(w->hwnd, NULL, TRUE); } void el_win32_widget_set_font(int64_t handle, const char* family, int size, int bold) { ElWidget* w = el_widget_get(handle); if (!w || !w->hwnd) return; LOGFONTW lf; memset(&lf, 0, sizeof(lf)); lf.lfHeight = -MulDiv(size > 0 ? size : 12, GetDeviceCaps(GetDC(NULL), LOGPIXELSY), 72); lf.lfWeight = bold ? FW_BOLD : FW_NORMAL; lf.lfCharSet = DEFAULT_CHARSET; if (family && *family && strcmp(family, "system") != 0) { WCHAR* wf = el_utf8_to_wide(family); wcsncpy(lf.lfFaceName, wf, LF_FACESIZE - 1); free(wf); } /* family == NULL or "system" → lfFaceName stays zeroed → system font */ HFONT hfont = CreateFontIndirectW(&lf); if (!hfont) return; if (w->font) DeleteObject(w->font); w->font = hfont; SendMessageW(w->hwnd, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE, 0)); } void el_win32_widget_set_padding(int64_t handle, int top, int right, int bottom, int left) { ElWidget* w = el_widget_get(handle); if (!w) return; w->pad_top = top; w->pad_right = right; w->pad_bottom = bottom; w->pad_left = left; /* Trigger reflow if already parented. */ if (w->hwnd) PostMessageW(w->hwnd, WM_EL_REFLOW, 0, 0); } void el_win32_widget_set_width(int64_t handle, int width) { ElWidget* w = el_widget_get(handle); if (!w) return; w->fixed_w = width; if (w->hwnd) { RECT r; GetWindowRect(w->hwnd, &r); int h = r.bottom - r.top; SetWindowPos(w->hwnd, NULL, 0, 0, width, h, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } } void el_win32_widget_set_height(int64_t handle, int height) { ElWidget* w = el_widget_get(handle); if (!w) return; w->fixed_h = height; if (w->hwnd) { RECT r; GetWindowRect(w->hwnd, &r); int cw = r.right - r.left; SetWindowPos(w->hwnd, NULL, 0, 0, cw, height, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); } } void el_win32_widget_set_flex(int64_t handle, int flex) { ElWidget* w = el_widget_get(handle); if (!w) return; w->flex = flex; /* Let parent reflow absorb the change. */ } void el_win32_widget_set_corner_radius(int64_t handle, int radius) { /* * Classic Win32 does not have native per-control corner radii. * If radius > 0 we apply a rounded rectangular window region so the * control at least clips to a rounded rect. This is approximate — it * does not anti-alias and may look boxy on high-DPI displays. */ ElWidget* w = el_widget_get(handle); if (!w || !w->hwnd || radius <= 0) return; RECT r; GetWindowRect(w->hwnd, &r); int ww = r.right - r.left; int wh = r.bottom - r.top; if (ww <= 0 || wh <= 0) return; HRGN rgn = CreateRoundRectRgn(0, 0, ww, wh, radius * 2, radius * 2); SetWindowRgn(w->hwnd, rgn, TRUE); /* Note: SetWindowRgn takes ownership of rgn; do NOT DeleteObject it. */ } void el_win32_widget_set_disabled(int64_t handle, int disabled) { ElWidget* w = el_widget_get(handle); if (!w || !w->hwnd) return; EnableWindow(w->hwnd, disabled ? FALSE : TRUE); } void el_win32_widget_set_hidden(int64_t handle, int hidden) { ElWidget* w = el_widget_get(handle); if (!w || !w->hwnd) return; ShowWindow(w->hwnd, hidden ? SW_HIDE : SW_SHOW); } /* ── Child management ─────────────────────────────────────────────────────── */ /* * el_win32_widget_add_child — attach child widget to parent. * * - WINDOW / VSTACK / HSTACK / ZSTACK: SetParent child into container. * - SCROLL: first child becomes the scrolled content. * * After reparenting, triggers a layout reflow of the parent. */ void el_win32_widget_add_child(int64_t parent, int64_t child) { ElWidget* pw = el_widget_get(parent); ElWidget* cw = el_widget_get(child); if (!pw || !cw || !pw->hwnd || !cw->hwnd) return; if (cw->kind == EL_WIDGET_WINDOW) return; /* cannot add window as child */ if (pw->child_count >= EL_WIN32_MAX_CHILDREN) return; /* Reparent the child HWND into the container. */ SetParent(cw->hwnd, pw->hwnd); /* Make sure the WS_CHILD style is set and WS_POPUP is cleared. */ LONG_PTR style = GetWindowLongPtrW(cw->hwnd, GWL_STYLE); style |= WS_CHILD; style &= ~WS_POPUP; SetWindowLongPtrW(cw->hwnd, GWL_STYLE, style); ShowWindow(cw->hwnd, SW_SHOW); pw->children[pw->child_count++] = child; /* Reflow parent so the new child gets positioned. */ el_win32_reflow(parent); } void el_win32_widget_remove_child(int64_t parent, int64_t child) { ElWidget* pw = el_widget_get(parent); ElWidget* cw = el_widget_get(child); if (!cw || !cw->hwnd) return; /* Detach HWND from parent. */ SetParent(cw->hwnd, NULL); /* Remove from parent's child list. */ if (pw) { for (int i = 0; i < pw->child_count; i++) { if (pw->children[i] == child) { memmove(&pw->children[i], &pw->children[i + 1], (size_t)(pw->child_count - i - 1) * sizeof(int64_t)); pw->child_count--; break; } } el_win32_reflow(parent); } } /* ── Event registration ───────────────────────────────────────────────────── */ void el_win32_widget_on_click(int64_t handle, const char* fn_name) { ElWidget* w = el_widget_get(handle); if (!w) return; free(w->cb_click); w->cb_click = (fn_name && *fn_name) ? strdup(fn_name) : NULL; } void el_win32_widget_on_change(int64_t handle, const char* fn_name) { ElWidget* w = el_widget_get(handle); if (!w) return; free(w->cb_change); w->cb_change = (fn_name && *fn_name) ? strdup(fn_name) : NULL; } void el_win32_widget_on_submit(int64_t handle, const char* fn_name) { /* For text fields, submit (Enter key) is stored in cb_click, same as AppKit. */ el_win32_widget_on_click(handle, fn_name); } /* ── Widget destroy ───────────────────────────────────────────────────────── */ void el_win32_widget_destroy(int64_t handle) { ElWidget* w = el_widget_get(handle); if (!w || !w->hwnd) return; DestroyWindow(w->hwnd); el_widget_free_slot(handle); } /* ── Manifest reader ──────────────────────────────────────────────────────── */ /* * Same parser as the macOS implementation in el_seed.c. * Reads the app{} block from a manifest.el file: * * app { * window_title "My App" * window_width 1200 * window_height 800 * min_width 600 * min_height 400 * } * * Returns JSON: {"title":"...","width":N,"height":N,"min_width":N,"min_height":N} * Returns "{}" on failure. * * NOTE: el_seed.c already defines __manifest_read for both EL_TARGET_MACOS and * EL_TARGET_WIN32 using the same parser body. We re-implement it here because * el_seed.c's implementation is inside #ifdef EL_TARGET_MACOS; the Win32 * version lives here, symmetric with the macOS one. */ static void w32_manifest_skip_ws(const char** p) { while (**p && (unsigned char)**p <= ' ') (*p)++; } static char* w32_manifest_read_string(const char** p) { w32_manifest_skip_ws(p); if (**p != '"') return NULL; (*p)++; const char* start = *p; while (**p && **p != '"') { if (**p == '\\') (*p)++; (*p)++; } size_t len = (size_t)(*p - start); if (**p == '"') (*p)++; char* out = (char*)malloc(len + 1); if (!out) return NULL; memcpy(out, start, len); out[len] = '\0'; return out; } static int64_t w32_manifest_read_int(const char** p) { w32_manifest_skip_ws(p); int64_t result = 0; int neg = 0; if (**p == '-') { neg = 1; (*p)++; } while (**p >= '0' && **p <= '9') { result = result * 10 + (**p - '0'); (*p)++; } return neg ? -result : result; } static const char* w32_manifest_find_app_block(const char* src) { while (*src) { if (src[0] == '/' && src[1] == '/') { while (*src && *src != '\n') src++; continue; } if (strncmp(src, "app", 3) == 0 && (src[3] == ' ' || src[3] == '\t' || src[3] == '\n' || src[3] == '{')) { src += 3; while (*src && *src != '{') src++; if (*src == '{') return src + 1; return NULL; } src++; } return NULL; } /* el_val_t helpers — mirror of what el_seed.c provides. */ #ifndef EL_CSTR #define EL_CSTR(v) ((const char*)(uintptr_t)(v)) #endif #ifndef EL_STR #define EL_STR(p) ((el_val_t)(uintptr_t)(p)) #endif el_val_t el_win32_manifest_read(const char* path) { if (!path || !*path) return EL_STR(strdup("{}")); FILE* f = fopen(path, "r"); if (!f) return EL_STR(strdup("{}")); fseek(f, 0, SEEK_END); long sz = ftell(f); rewind(f); if (sz <= 0) { fclose(f); return EL_STR(strdup("{}")); } char* src = (char*)malloc((size_t)sz + 1); if (!src) { fclose(f); return EL_STR(strdup("{}")); } size_t got = fread(src, 1, (size_t)sz, f); src[got] = '\0'; fclose(f); const char* app_start = w32_manifest_find_app_block(src); if (!app_start) { free(src); return EL_STR(strdup("{}")); } char* title = NULL; int64_t width = 1200, height = 800; int64_t min_w = 600, min_h = 400; const char* cur = app_start; while (*cur && *cur != '}') { while (*cur && (unsigned char)*cur <= ' ') cur++; if (*cur == '}') break; if (cur[0] == '/' && cur[1] == '/') { while (*cur && *cur != '\n') cur++; continue; } const char* key_start = cur; while (*cur && (unsigned char)*cur > ' ' && *cur != '{' && *cur != '}') cur++; size_t key_len = (size_t)(cur - key_start); if (key_len == 0) { cur++; continue; } while (*cur == ' ' || *cur == '\t') cur++; if (key_len == 12 && strncmp(key_start, "window_title", 12) == 0) { free(title); title = w32_manifest_read_string(&cur); } else if (key_len == 12 && strncmp(key_start, "window_width", 12) == 0) { width = w32_manifest_read_int(&cur); } else if (key_len == 13 && strncmp(key_start, "window_height", 13) == 0) { height = w32_manifest_read_int(&cur); } else if (key_len == 9 && strncmp(key_start, "min_width", 9) == 0) { min_w = w32_manifest_read_int(&cur); } else if (key_len == 10 && strncmp(key_start, "min_height", 10) == 0) { min_h = w32_manifest_read_int(&cur); } else { while (*cur && *cur != '\n' && *cur != '}') cur++; } } free(src); const char* t = title ? title : "App"; size_t buf_sz = strlen(t) * 6 + 128; char* buf = (char*)malloc(buf_sz); if (!buf) { free(title); return EL_STR(strdup("{}")); } snprintf(buf, buf_sz, "{\"title\":\"%s\",\"width\":%lld,\"height\":%lld,\"min_width\":%lld,\"min_height\":%lld}", t, (long long)width, (long long)height, (long long)min_w, (long long)min_h); free(title); return EL_STR(buf); } /* ── el_seed.c __* builtins for EL_TARGET_WIN32 ──────────────────────────── */ /* * These are the thin el_val_t wrappers that el_seed.c (with EL_TARGET_WIN32) * will call. They mirror the EL_TARGET_MACOS section in el_seed.c exactly. * * el_seed.c includes this file's declarations via el_native_target.h and * links el_win32.o. The __* symbol definitions below are compiled into * el_win32.c itself (not el_seed.c) when EL_TARGET_WIN32 is defined, keeping * el_seed.c platform-agnostic. */ /* Helper: extract el string pointer. */ static inline const char* _w32_cstr(el_val_t v) { return (const char*)(uintptr_t)v; } static inline el_val_t _w32_str(const char* p) { return (el_val_t)(uintptr_t)p; } /* Bit-cast helper for float args (el_val_t carries IEEE 754 doubles). */ static inline double _w32_to_float(el_val_t v) { union { double d; int64_t i; } u; u.i = (int64_t)v; return u.d; } void __native_init(void) { el_win32_init(); } void __native_run_loop(void) { el_win32_run_loop(); } el_val_t __window_create(el_val_t title, el_val_t width, el_val_t height, el_val_t min_width, el_val_t min_height) { return (el_val_t)el_win32_window_create( _w32_cstr(title), (int)(int64_t)width, (int)(int64_t)height, (int)(int64_t)min_width, (int)(int64_t)min_height); } void __window_show(el_val_t h) { el_win32_window_show((int64_t)h); } void __window_set_title(el_val_t h, el_val_t t) { el_win32_window_set_title((int64_t)h, _w32_cstr(t)); } el_val_t __vstack_create(el_val_t spacing) { return (el_val_t)el_win32_vstack_create((int)(int64_t)spacing); } el_val_t __hstack_create(el_val_t spacing) { return (el_val_t)el_win32_hstack_create((int)(int64_t)spacing); } el_val_t __zstack_create(void) { return (el_val_t)el_win32_zstack_create(); } el_val_t __scroll_create(void) { return (el_val_t)el_win32_scroll_create(); } el_val_t __label_create(el_val_t text) { return (el_val_t)el_win32_label_create(_w32_cstr(text)); } el_val_t __button_create(el_val_t label) { return (el_val_t)el_win32_button_create(_w32_cstr(label)); } el_val_t __text_field_create(el_val_t placeholder) { return (el_val_t)el_win32_text_field_create(_w32_cstr(placeholder)); } el_val_t __text_area_create(el_val_t placeholder) { return (el_val_t)el_win32_text_area_create(_w32_cstr(placeholder)); } el_val_t __image_create(el_val_t path) { return (el_val_t)el_win32_image_create(_w32_cstr(path)); } void __widget_set_text(el_val_t h, el_val_t t) { el_win32_widget_set_text((int64_t)h, _w32_cstr(t)); } el_val_t __widget_get_text(el_val_t h) { const char* s = el_win32_widget_get_text((int64_t)h); /* el_win32_widget_get_text returns a malloc'd string; caller treats as * short-lived (same contract as el_appkit version). */ return s ? _w32_str(s) : _w32_str(strdup("")); } void __widget_set_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_win32_widget_set_color((int64_t)h, (float)_w32_to_float(r), (float)_w32_to_float(g), (float)_w32_to_float(b), (float)_w32_to_float(a)); } void __widget_set_bg_color(el_val_t h, el_val_t r, el_val_t g, el_val_t b, el_val_t a) { el_win32_widget_set_bg_color((int64_t)h, (float)_w32_to_float(r), (float)_w32_to_float(g), (float)_w32_to_float(b), (float)_w32_to_float(a)); } void __widget_set_font(el_val_t h, el_val_t family, el_val_t size, el_val_t bold) { el_win32_widget_set_font((int64_t)h, _w32_cstr(family), (int)(int64_t)size, (int)(int64_t)bold); } void __widget_set_padding(el_val_t h, el_val_t top, el_val_t right, el_val_t bottom, el_val_t left) { el_win32_widget_set_padding((int64_t)h, (int)(int64_t)top, (int)(int64_t)right, (int)(int64_t)bottom, (int)(int64_t)left); } void __widget_set_width(el_val_t h, el_val_t w) { el_win32_widget_set_width((int64_t)h, (int)(int64_t)w); } void __widget_set_height(el_val_t h, el_val_t ht) { el_win32_widget_set_height((int64_t)h, (int)(int64_t)ht); } void __widget_set_flex(el_val_t h, el_val_t f) { el_win32_widget_set_flex((int64_t)h, (int)(int64_t)f); } void __widget_set_corner_radius(el_val_t h, el_val_t r) { el_win32_widget_set_corner_radius((int64_t)h, (int)(int64_t)r); } void __widget_set_disabled(el_val_t h, el_val_t d) { el_win32_widget_set_disabled((int64_t)h, (int)(int64_t)d); } void __widget_set_hidden(el_val_t h, el_val_t hid) { el_win32_widget_set_hidden((int64_t)h, (int)(int64_t)hid); } void __widget_add_child(el_val_t p, el_val_t c) { el_win32_widget_add_child((int64_t)p, (int64_t)c); } void __widget_remove_child(el_val_t p, el_val_t c) { el_win32_widget_remove_child((int64_t)p, (int64_t)c); } void __widget_destroy(el_val_t h) { el_win32_widget_destroy((int64_t)h); } void __widget_on_click(el_val_t h, el_val_t fn_name) { el_win32_widget_on_click((int64_t)h, _w32_cstr(fn_name)); } void __widget_on_change(el_val_t h, el_val_t fn_name) { el_win32_widget_on_change((int64_t)h, _w32_cstr(fn_name)); } void __widget_on_submit(el_val_t h, el_val_t fn_name) { el_win32_widget_on_submit((int64_t)h, _w32_cstr(fn_name)); } el_val_t __manifest_read(el_val_t path) { return el_win32_manifest_read(_w32_cstr(path)); } #endif /* EL_TARGET_WIN32 */