// el-native — El native widget system, macOS AppKit target. // // This vessel wraps the __widget_* C builtins (added to el_seed.c under // EL_TARGET_MACOS) in clean El-level functions. Applications import this // vessel and use these functions directly. // // The el-ui-compiler's native codegen target emits calls to these functions // when it processes component templates. // // Layer hierarchy: // Application .el // → el-native functions (this file) // → __widget_* / __window_* C builtins // → el_appkit.m ObjC bridge // → AppKit NSWindow / NSView / NSButton / etc. // // Widget handles are opaque Int values. -1 means invalid/null. // Pass handles between functions freely — they are small integers. // // Color convention: r/g/b/a are Float values 0.0–1.0. // Use color_from_hex() or color_from_rgba() to build color arguments. // ── Bootstrap ──────────────────────────────────────────────────────────────── // init — initialize the native widget system. Call once before creating widgets. fn native_init() -> Void { __native_init() } // run_loop — start the platform run loop. Never returns. // Call this as the final step of your application entry point. fn native_run_loop() -> Void { __native_run_loop() } // ── Manifest ───────────────────────────────────────────────────────────────── // manifest_read — parse the app{} block from a manifest.el file. // Returns a JSON string: {"title":"...","width":N,"height":N,"min_width":N,"min_height":N} fn manifest_read(path: String) -> String { __manifest_read(path) } fn manifest_title(m: String) -> String { json_get_string(m, "title") } fn manifest_width(m: String) -> Int { json_get_int(m, "width") } fn manifest_height(m: String) -> Int { json_get_int(m, "height") } fn manifest_min_width(m: String) -> Int { json_get_int(m, "min_width") } fn manifest_min_height(m: String) -> Int { json_get_int(m, "min_height") } // ── Window ─────────────────────────────────────────────────────────────────── // window_create — create a native window. Returns handle (Int). // Reads sizing from manifest. Most apps should call window_from_manifest(). fn window_create(title: String, width: Int, height: Int, min_width: Int, min_height: Int) -> Int { __window_create(title, width, height, min_width, min_height) } // window_from_manifest — create a window using settings from manifest.el. fn window_from_manifest(manifest_path: String) -> Int { let m: String = manifest_read(manifest_path) let title: String = manifest_title(m) let w: Int = manifest_width(m) let h: Int = manifest_height(m) let mw: Int = manifest_min_width(m) let mh: Int = manifest_min_height(m) let safe_w: Int = if w > 0 { w } else { 1200 } let safe_h: Int = if h > 0 { h } else { 800 } let safe_mw: Int = if mw > 0 { mw } else { 600 } let safe_mh: Int = if mh > 0 { mh } else { 400 } let safe_t: String = if str_len(title) > 0 { title } else { "App" } window_create(safe_t, safe_w, safe_h, safe_mw, safe_mh) } fn window_show(handle: Int) -> Void { __window_show(handle) } fn window_set_title(handle: Int, title: String) -> Void { __window_set_title(handle, title) } // ── Layout containers ───────────────────────────────────────────────────────── // vstack — vertical stack. Children are laid out top-to-bottom. fn vstack(spacing: Int) -> Int { __vstack_create(spacing) } // vstack_tight — 0 spacing vertical stack. fn vstack_tight() -> Int { __vstack_create(0) } // hstack — horizontal stack. Children are laid out left-to-right. fn hstack(spacing: Int) -> Int { __hstack_create(spacing) } // zstack — children are layered on top of each other (Z axis). fn zstack() -> Int { __zstack_create() } // scroll — scrollable container. Add a single child (usually a vstack). fn scroll() -> Int { __scroll_create() } // ── Widgets ────────────────────────────────────────────────────────────────── // label — static text display. fn label(text: String) -> Int { __label_create(text) } // button — tappable button. Register handler with widget_on_click(). fn button(label: String) -> Int { __button_create(label) } // text_field — single-line text input. fn text_field(placeholder: String) -> Int { __text_field_create(placeholder) } // text_area — multi-line text input. fn text_area(placeholder: String) -> Int { __text_area_create(placeholder) } // image — display an image by filesystem path or system image name. fn image(path_or_name: String) -> Int { __image_create(path_or_name) } // ── Widget properties ───────────────────────────────────────────────────────── fn widget_set_text(handle: Int, text: String) -> Void { __widget_set_text(handle, text) } fn widget_get_text(handle: Int) -> String { __widget_get_text(handle) } // Color setters. r/g/b/a are Float 0.0–1.0. fn widget_set_color(handle: Int, r: Float, g: Float, b: Float, a: Float) -> Void { __widget_set_color(handle, r, g, b, a) } fn widget_set_bg_color(handle: Int, r: Float, g: Float, b: Float, a: Float) -> Void { __widget_set_bg_color(handle, r, g, b, a) } // widget_set_font — set font family, size (pt), and bold flag. // Use "system" or "" for the platform default font. fn widget_set_font(handle: Int, family: String, size: Int, bold: Bool) -> Void { let bold_int: Int = if bold { 1 } else { 0 } __widget_set_font(handle, family, size, bold_int) } // widget_set_padding — set padding on all four sides (pixels). fn widget_set_padding(handle: Int, top: Int, right: Int, bottom: Int, left: Int) -> Void { __widget_set_padding(handle, top, right, bottom, left) } // widget_set_padding_all — uniform padding on all sides. fn widget_set_padding_all(handle: Int, p: Int) -> Void { __widget_set_padding(handle, p, p, p, p) } // widget_set_padding_xy — horizontal and vertical padding. fn widget_set_padding_xy(handle: Int, px: Int, py: Int) -> Void { __widget_set_padding(handle, py, px, py, px) } fn widget_set_width(handle: Int, width: Int) -> Void { __widget_set_width(handle, width) } fn widget_set_height(handle: Int, height: Int) -> Void { __widget_set_height(handle, height) } // widget_set_flex — flex > 0 means expand to fill available space. fn widget_set_flex(handle: Int, flex: Int) -> Void { __widget_set_flex(handle, flex) } fn widget_set_corner_radius(handle: Int, radius: Int) -> Void { __widget_set_corner_radius(handle, radius) } fn widget_set_disabled(handle: Int, disabled: Bool) -> Void { let d: Int = if disabled { 1 } else { 0 } __widget_set_disabled(handle, d) } fn widget_set_hidden(handle: Int, hidden: Bool) -> Void { let h: Int = if hidden { 1 } else { 0 } __widget_set_hidden(handle, h) } // ── Layout ─────────────────────────────────────────────────────────────────── fn widget_add_child(parent: Int, child: Int) -> Void { __widget_add_child(parent, child) } fn widget_remove_child(parent: Int, child: Int) -> Void { __widget_remove_child(parent, child) } fn widget_destroy(handle: Int) -> Void { __widget_destroy(handle) } // ── Events ─────────────────────────────────────────────────────────────────── // widget_on_click — register an El function to call when widget is clicked. // fn_name: the compiled El symbol name of the handler function. // Handler signature: fn my_handler(widget: Int, data: String) -> Void fn widget_on_click(handle: Int, fn_name: String) -> Void { __widget_on_click(handle, fn_name) } // widget_on_change — called when text field value changes. fn widget_on_change(handle: Int, fn_name: String) -> Void { __widget_on_change(handle, fn_name) } // widget_on_submit — called when text field receives Enter (submit). fn widget_on_submit(handle: Int, fn_name: String) -> Void { __widget_on_submit(handle, fn_name) } // widget_set_data — attach an arbitrary data string to a widget. // The data string is passed as the `data` argument when the widget's click // callback fires. Use this to associate a session ID (or any identifier) // with a clickable row so the callback knows which item was selected. fn widget_set_data(handle: Int, data: String) -> Void { __widget_set_data(handle, data) } // ── Color helpers ───────────────────────────────────────────────────────────── // // Colors are passed as Float components (0.0–1.0). These helpers convert from // common formats. // hex_channel — parse two hex digits as a byte 0..255. fn hex_channel(s: String, offset: Int) -> Int { let hi: String = str_slice(s, offset, offset + 1) let lo: String = str_slice(s, offset + 1, offset + 2) let h: Int = hex_nibble(hi) let l: Int = hex_nibble(lo) h * 16 + l } fn hex_nibble(c: String) -> Int { if str_eq(c, "0") { return 0 } if str_eq(c, "1") { return 1 } if str_eq(c, "2") { return 2 } if str_eq(c, "3") { return 3 } if str_eq(c, "4") { return 4 } if str_eq(c, "5") { return 5 } if str_eq(c, "6") { return 6 } if str_eq(c, "7") { return 7 } if str_eq(c, "8") { return 8 } if str_eq(c, "9") { return 9 } if str_eq(c, "a") { return 10 } if str_eq(c, "b") { return 11 } if str_eq(c, "c") { return 12 } if str_eq(c, "d") { return 13 } if str_eq(c, "e") { return 14 } if str_eq(c, "f") { return 15 } if str_eq(c, "A") { return 10 } if str_eq(c, "B") { return 11 } if str_eq(c, "C") { return 12 } if str_eq(c, "D") { return 13 } if str_eq(c, "E") { return 14 } if str_eq(c, "F") { return 15 } 0 } // color_hex_r/g/b/a — extract RGBA Float components from a hex color string. // Accepts "#RRGGBB" or "#RRGGBBAA". fn color_hex_r(hex: String) -> Float { let s: String = if str_starts_with(hex, "#") { str_slice(hex, 1, str_len(hex)) } else { hex } let v: Int = hex_channel(s, 0) float_div(int_to_float(v), 255.0) } fn color_hex_g(hex: String) -> Float { let s: String = if str_starts_with(hex, "#") { str_slice(hex, 1, str_len(hex)) } else { hex } let v: Int = hex_channel(s, 2) float_div(int_to_float(v), 255.0) } fn color_hex_b(hex: String) -> Float { let s: String = if str_starts_with(hex, "#") { str_slice(hex, 1, str_len(hex)) } else { hex } let v: Int = hex_channel(s, 4) float_div(int_to_float(v), 255.0) } fn color_hex_a(hex: String) -> Float { let s: String = if str_starts_with(hex, "#") { str_slice(hex, 1, str_len(hex)) } else { hex } if str_len(s) < 8 { return 1.0 } let v: Int = hex_channel(s, 6) float_div(int_to_float(v), 255.0) } // widget_set_color_hex — convenience: set foreground color from a hex string. fn widget_set_color_hex(handle: Int, hex: String) -> Void { widget_set_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex)) } // widget_set_bg_color_hex — convenience: set background color from a hex string. fn widget_set_bg_color_hex(handle: Int, hex: String) -> Void { widget_set_bg_color(handle, color_hex_r(hex), color_hex_g(hex), color_hex_b(hex), color_hex_a(hex)) } // ── Style tokens → native ───────────────────────────────────────────────────── // // Map el-style semantic tokens (from el-style vessel) to native widget properties. // This is the bridge between the design system and platform rendering. // // Token colors match el-style's color_scheme_dark() by default. // Applications can override by calling the individual setters directly. let TOKEN_PRIMARY: String = "#60a5fa" let TOKEN_ON_PRIMARY: String = "#0f172a" let TOKEN_BACKGROUND: String = "#0f172a" let TOKEN_ON_BG: String = "#f8fafc" let TOKEN_SURFACE: String = "#1e293b" let TOKEN_ON_SURFACE: String = "#f8fafc" let TOKEN_OUTLINE: String = "#475569" let TOKEN_ERROR: String = "#f87171" // Apply standard surface styling to a container. fn style_surface(handle: Int) -> Void { widget_set_bg_color_hex(handle, TOKEN_SURFACE) widget_set_corner_radius(handle, 8) } // Apply primary button style. fn style_button_primary(handle: Int) -> Void { widget_set_bg_color_hex(handle, TOKEN_PRIMARY) widget_set_color_hex(handle, TOKEN_ON_PRIMARY) widget_set_corner_radius(handle, 8) widget_set_padding_xy(handle, 16, 8) } // Apply body text style to a label. fn style_label_body(handle: Int) -> Void { widget_set_color_hex(handle, TOKEN_ON_BG) widget_set_font(handle, "system", 14, false) } // Apply heading style to a label. fn style_label_heading(handle: Int) -> Void { widget_set_color_hex(handle, TOKEN_ON_BG) widget_set_font(handle, "system", 20, true) } // Apply muted/secondary text style. fn style_label_muted(handle: Int) -> Void { widget_set_color_hex(handle, TOKEN_OUTLINE) widget_set_font(handle, "system", 12, false) }