main :: { // imagine a game loop while(running) { render_ui(build_menu); } } build_menu :: (ctx: ViewContext) -> View { // use ctx to allocate some state at the for Menu ViewContext state : MenuState = ctx.state(MenuState); // named args HStack(ctx, children = .[ Button(ctx, label = "Up", onTap = ctx.callback(goUp, state), ), ScrollView(ctx, LazyVStack(ctx, builder = ctx.callback(build_menu_entry, state), ), ) ], ); } build_menu_entry :: (ctx: *ViewContext, index: s32, state: *MenuState) -> View { entry := state.entries[index]; is_selected := index == state.selected_index; icon := if entry.is_dir then "[D]" else " "; Button(ctx, label = concat(icon, " ", entry.name), on_tap = ctx.callback(menu_go, state, index), ); } ViewContext :: struct { //TBD } MenuState :: struct { current_path: string; entries: List(MenuEntry); error_message: string; } MenuEntry :: struct { name: string; is_dir: bool; } menu_go_up :: (state: *MenuState) { parent := fs.path.dirname(state.current_path) else return; // this frees the current path & copies parent to be owned by MenuState state.current_path = parent; menu_refresh(state); } menu_go :: (state: *MenuState, s32 index) { entry := state.entries[index] else return; state.current_path := concat(state.current_path, "/", entry.name); menu_refresh(state); } menu_refresh :: (state: *MenuState) { // this could retain the capacity state.entries.clear(); // this would basically create a copy of the empty string :( state.error_message = ""; // ... multi return params vs Generic Result to deal with exceptions // ... nullable dir := io.Dir.open(state.current_path); if !dir { state.error_message = "failed to open"; return; }; defer dir.close(); for iter.iterate() { entries.append(.{it.name, it.kind == .Directory}); } } HStackState :: struct { spacing: f32 = 8; alignment: VerticalAlignment = .center; padding: f32 = 0; background: ?Color; corner_radius: f32 = 0, } HStack :: (ctx: ViewContext, children: []View) -> View { data := ctx.alloc(HStackState); data.* = .{}; }