╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ main_wasm.zig ────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯
130 // follow a cross-page link 131 if (ui.crossPageLink(root_focus, gid, session.data)) |route| { 132 const url = try route.toUrl(&page_arena); 133 _navigate(url.ptr, @intCast(url.len)); 134 return; 135 } 136 // follow a link to bytes the server serves directly 137 if (ui.rawLink(root_focus, gid)) |url| { 138 _navigate(url.ptr, @intCast(url.len)); 139 return; 140 } 141 } 142 } 143 144 try root_ptr.input(allocator, key, root_focus); 145 const focused_after = root_focus.grandchild_id; 146 if (key != .enter and focused_before == focused_after) return; 147 const focus_id = focused_after orelse return; 148 if (ui.inPageLink(root_focus, focus_id, session.data)) |route| session.data.current_page = route; 149 } 150 151 fn onMouseClick(focus_id: usize) !void { 152 const root_ptr = if (root) |*root_value| root_value else return error.NotStarted; 153 // follow a cross-page link 154 if (ui.crossPageLink(root_ptr.getFocus(), focus_id, session.data)) |route| { 155 const url = try route.toUrl(&page_arena); 156 _navigate(url.ptr, @intCast(url.len)); 157 return; 158 } 159 // the grid suppresses the anchor's own navigation, so follow it here 160 if (ui.rawLink(root_ptr.getFocus(), focus_id)) |url| { 161 _navigate(url.ptr, @intCast(url.len)); 162 return; 163 } 164 if (ui.currentPageLink(root_ptr.getFocus(), focus_id, session.data)) return; 165 try setFocus(focus_id); 166 // an in-page link selects within the page, and a list row's view may open 167 // its detail on the press. other clickables only focus: a form control's 168 // border reaches here too, and the press must not fire its form. 169 const root_focus = root_ptr.getFocus(); 170 if (ui.inPageLink(root_focus, focus_id, session.data) != null) { 171 const rect = (root_focus.children.get(focus_id) orelse return).rect; 172 try root_ptr.input(allocator, .{ .mouse = .{ .x = @intCast(rect.x), .y = @intCast(rect.y), .button = .left } }, root_focus); 173 } 174 } 175 176 fn setFocus(focus_id: usize) !void { 177 const root_ptr = if (root) |*root_value| root_value else return error.NotStarted; 178 const root_focus = root_ptr.getFocus(); 179 root_focus.setFocus(focus_id); 180 if (ui.inPageLink(root_focus, focus_id, session.data)) |route| session.data.current_page = route; 181 } 182 183 fn consoleLog(arg: []const u8) void { 184 _consoleLog(arg.ptr, @intCast(arg.len)); 185 } 186 187 extern fn _consoleLog(arg: [*]const u8, len: u32) void; 188 extern fn _setHtml(arg: [*]const u8, len: u32) void; 189 extern fn _replaceState(arg: [*]const u8, len: u32) void; 190 extern fn _focusInput(focus_id: u32) void; 191 extern fn _navigate(arg: [*]const u8, len: u32) void; 192 extern fn _scrollToFocus(focus_id: u32) void; 193 /// js calls this first to get a wasm pointer it can write the page json into. 194 export fn _alloc(len: u32) ?[*]u8 { 195 const slice = allocator.alloc(u8, len) catch return null; 196 return slice.ptr; 197 } 198 199 export fn _init(json_ptr: [*]u8, json_len: u32, min_height: u32, max_width: u32) void { 200 const json = json_ptr[0..json_len]; 201 defer allocator.free(json); 202 init(json, min_height, max_width) catch |err| { 203 var buf: [256]u8 = undefined; 204 const str = std.fmt.bufPrint(&buf, "init: {}", .{err}) catch unreachable; 205 consoleLog(str); 206 }; 207 } 208 209 export fn _tick(min_height: u32, max_width: u32) bool { 210 tick(min_height, max_width) catch |err| { 211 var buf: [256]u8 = undefined; 212 const str = std.fmt.bufPrint(&buf, "tick: {}", .{err}) catch unreachable; 213 consoleLog(str); 214 return false; 215 }; 216 return true; 217 } 218 219 export fn _onKeyDown(key_code: u32) void { 220 onKeyDown(key_code) catch |err| { 221 var buf: [256]u8 = undefined; 222 const str = std.fmt.bufPrint(&buf, "onKeyDown: {}", .{err}) catch unreachable; 223 consoleLog(str); 224 }; 225 } 226 227 export fn _onMouseClick(focus_id: u32) void { 228 onMouseClick(focus_id) catch |err| { 229 var buf: [256]u8 = undefined; 230 const str = std.fmt.bufPrint(&buf, "onMouseClick: {}", .{err}) catch unreachable; 231 consoleLog(str); 232 }; 233 } 234 235 export fn _setFocus(focus_id: u32) void { 236 setFocus(focus_id) catch |err| { 237 var buf: [256]u8 = undefined; 238 const str = std.fmt.bufPrint(&buf, "setFocus: {}", .{err}) catch unreachable; 239 consoleLog(str); 240 }; 241 } 242 243 // js calls this when the value of a native <input> changes; we replace 244 // the matching TextInput's content with the supplied utf-8 bytes. 245 export fn _setTextInputValue(focus_id: u32, value_ptr: [*]const u8, value_len: u32) void { 246 setTextInputValue(focus_id, value_ptr[0..value_len]) catch |err| { 247 var buf: [256]u8 = undefined; 248 const str = std.fmt.bufPrint(&buf, "setTextInputValue: {}", .{err}) catch unreachable; 249 consoleLog(str); 250 }; 251 } 252 253 fn setTextInputValue(focus_id: u32, bytes: []const u8) !void { 254 const root_ptr = if (root) |*r| r else return error.NotStarted; 255 const root_focus = root_ptr.getFocus(); 256 const child = root_focus.children.get(focus_id) orelse return error.UnknownFocusId; 257 switch (child.focus.kind) { 258 .text_input, .text_input_password, .text_area => {}, 259 else => return error.NotATextInput, 260 } 261 const ti = session.text_inputs.get(focus_id) orelse return error.UnknownFocusId; 262 try ti.setContent(allocator, bytes); 263 } 264