Events
Declaring events
Events declare typed callback slots that the parent binds at instantiation:
tape
component Button {
prop label: *u8;
event on_click();
event on_hover(x: i64, y: i64);
}Events can have up to 8 parameters.
Firing events
Use the fire keyword inside a component method to invoke the bound handler:
tape
component Button {
prop label: *u8;
event on_click();
fn handle_pointer(ev: *ui.PointerEvent) {
if (ev.kind == ui.PointerKind.Up) {
fire on_click();
}
}
}If no handler is bound, fire is a no-op (null-guarded function pointer call).
Binding handlers (parent side)
The parent binds a function to the event by name:
tape
fn save_file() {
io.println("saving...");
}
component Toolbar {
view {
widgets.Button { label: "Save"; on_click: save_file; }
}
}The handler must match the event’s parameter types exactly.
Passing data through events
Declare parameters in the event signature to carry data to the parent:
tape
component TodoItem {
prop index: i64;
event on_delete(index: i64);
fn handle_click() {
fire on_delete(index);
}
}Parent side:
tape
fn delete_item(index: i64) { /* ... */ }
TodoItem { index: 3; on_delete: delete_item; }Auto-setters as event handlers
Generated set_<var> methods are commonly bound to change events:
tape
component SearchBar {
var query: *u8 = "";
view {
widgets.TextInput { value: query; on_change: set_query; }
}
}Design: no closures
Events are single function pointers — not closures, not listener lists. This means:
- No hidden heap allocations
- No capture analysis
- No multi-subscriber patterns (use explicit fan-out if needed)
- Predictable, zero-overhead dispatch (null check + indirect call)
Last modified: