feat(runtime): native platform backends and UI vessels onto main
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
// App.el — native-hello root component.
|
||||
//
|
||||
// Demonstrates the el-native widget system:
|
||||
// - Window created from manifest.el
|
||||
// - VStack layout
|
||||
// - Label, Button, TextField
|
||||
// - Click and change event callbacks
|
||||
// - Style token application
|
||||
//
|
||||
// This is the manual API that the el-ui-compiler's native codegen target will
|
||||
// eventually emit. For now it shows exactly what apps (and the compiler) need
|
||||
// to produce.
|
||||
|
||||
import "../../vessels/el-native/src/main.el"
|
||||
|
||||
// ── State ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
// Global state — holds the widget handles so callbacks can reference them.
|
||||
// In a real component system, these would be scoped to the component instance.
|
||||
|
||||
let g_window: Int = -1
|
||||
let g_label: Int = -1
|
||||
let g_input: Int = -1
|
||||
let g_button: Int = -1
|
||||
let g_counter: Int = 0
|
||||
let g_counter_lbl: Int = -1
|
||||
|
||||
// ── Callbacks ─────────────────────────────────────────────────────────────────
|
||||
//
|
||||
// Callbacks must be top-level functions with the signature:
|
||||
// fn name(widget: Int, data: String) -> Void
|
||||
// The C bridge resolves them by symbol name via dlsym.
|
||||
|
||||
fn on_greet_click(widget: Int, data: String) -> Void {
|
||||
let name: String = widget_get_text(g_input)
|
||||
let greeting: String = if str_len(name) > 0 {
|
||||
"Hello, " + name + "!"
|
||||
} else {
|
||||
"Hello, World!"
|
||||
}
|
||||
widget_set_text(g_label, greeting)
|
||||
}
|
||||
|
||||
fn on_counter_click(widget: Int, data: String) -> Void {
|
||||
let g_counter = g_counter + 1
|
||||
widget_set_text(g_counter_lbl, "Clicks: " + int_to_str(g_counter))
|
||||
}
|
||||
|
||||
fn on_name_change(widget: Int, data: String) -> Void {
|
||||
// Enable the greet button only when there is text in the field.
|
||||
// (data is the current text value)
|
||||
let has_text: Bool = str_len(data) > 0
|
||||
widget_set_disabled(g_button, !has_text)
|
||||
}
|
||||
|
||||
// ── App build ─────────────────────────────────────────────────────────────────
|
||||
|
||||
fn app_build(window: Int) -> Void {
|
||||
// Root vertical stack — full-window content.
|
||||
let root: Int = vstack(0)
|
||||
widget_set_padding_all(root, 24)
|
||||
widget_set_bg_color_hex(root, "#0f172a")
|
||||
widget_set_flex(root, 1)
|
||||
|
||||
// Title label.
|
||||
let title: Int = label("el-native — native widget demo")
|
||||
style_label_heading(title)
|
||||
widget_add_child(root, title)
|
||||
|
||||
// Spacer (empty label acts as a gap).
|
||||
let gap1: Int = label("")
|
||||
widget_set_height(gap1, 16)
|
||||
widget_add_child(root, gap1)
|
||||
|
||||
// Subtitle.
|
||||
let subtitle: Int = label("AppKit controls from el code, no ObjC in the app layer.")
|
||||
style_label_muted(subtitle)
|
||||
widget_add_child(root, subtitle)
|
||||
|
||||
// Divider gap.
|
||||
let gap2: Int = label("")
|
||||
widget_set_height(gap2, 24)
|
||||
widget_add_child(root, gap2)
|
||||
|
||||
// Name input row.
|
||||
let input_row: Int = hstack(8)
|
||||
let name_label: Int = label("Name:")
|
||||
style_label_body(name_label)
|
||||
widget_set_width(name_label, 60)
|
||||
|
||||
let input: Int = text_field("Enter your name…")
|
||||
style_label_body(input)
|
||||
widget_set_flex(input, 1)
|
||||
widget_on_change(input, "on_name_change")
|
||||
let g_input = input // stash handle
|
||||
|
||||
let greet_btn: Int = button("Greet")
|
||||
style_button_primary(greet_btn)
|
||||
widget_set_disabled(greet_btn, true) // disabled until name is typed
|
||||
widget_on_click(greet_btn, "on_greet_click")
|
||||
let g_button = greet_btn
|
||||
|
||||
widget_add_child(input_row, name_label)
|
||||
widget_add_child(input_row, input)
|
||||
widget_add_child(input_row, greet_btn)
|
||||
widget_add_child(root, input_row)
|
||||
|
||||
// Greeting output label.
|
||||
let gap3: Int = label("")
|
||||
widget_set_height(gap3, 12)
|
||||
widget_add_child(root, gap3)
|
||||
|
||||
let greeting: Int = label("Waiting for name…")
|
||||
style_label_body(greeting)
|
||||
widget_set_color_hex(greeting, "#60a5fa")
|
||||
widget_set_font(greeting, "system", 16, true)
|
||||
let g_label = greeting
|
||||
widget_add_child(root, greeting)
|
||||
|
||||
// Counter row.
|
||||
let gap4: Int = label("")
|
||||
widget_set_height(gap4, 24)
|
||||
widget_add_child(root, gap4)
|
||||
|
||||
let counter_row: Int = hstack(12)
|
||||
|
||||
let counter_lbl: Int = label("Clicks: 0")
|
||||
style_label_body(counter_lbl)
|
||||
let g_counter_lbl = counter_lbl
|
||||
|
||||
let counter_btn: Int = button("Click me")
|
||||
style_button_primary(counter_btn)
|
||||
widget_on_click(counter_btn, "on_counter_click")
|
||||
|
||||
widget_add_child(counter_row, counter_lbl)
|
||||
widget_add_child(counter_row, counter_btn)
|
||||
widget_add_child(root, counter_row)
|
||||
|
||||
// Attach root to window.
|
||||
widget_add_child(window, root)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// main.el — Entry point for native-hello.
|
||||
//
|
||||
// Boot sequence:
|
||||
// 1. Initialize the native widget system.
|
||||
// 2. Read the manifest.el app{} block for window config.
|
||||
// 3. Create the window.
|
||||
// 4. Build the widget tree (App.el: app_build).
|
||||
// 5. Show the window.
|
||||
// 6. Start the run loop (never returns).
|
||||
|
||||
import "App.el"
|
||||
|
||||
// ── Boot ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
native_init()
|
||||
|
||||
// Determine the manifest path relative to where we are invoked.
|
||||
// When running from the example directory: manifest.el is at ./manifest.el.
|
||||
// When invoked from elsewhere: pass EL_MANIFEST env var.
|
||||
let manifest_env: String = env("EL_MANIFEST")
|
||||
let manifest_path: String = if str_len(manifest_env) > 0 {
|
||||
manifest_env
|
||||
} else {
|
||||
"manifest.el"
|
||||
}
|
||||
|
||||
// Create window from manifest.
|
||||
let win: Int = window_from_manifest(manifest_path)
|
||||
let g_window = win
|
||||
|
||||
if win < 0 {
|
||||
println("Error: failed to create window. Is EL_TARGET_MACOS defined?")
|
||||
exit_program(1)
|
||||
}
|
||||
|
||||
// Build the widget tree.
|
||||
app_build(win)
|
||||
|
||||
// Show the window.
|
||||
window_show(win)
|
||||
|
||||
// Hand control to AppKit. Never returns.
|
||||
native_run_loop()
|
||||
Reference in New Issue
Block a user