╭ changed ─╮ │src/ui.zig│ ╰──────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭────────────────────────────────────────────────────────────────────────────────────╮ │26 var last_grid = try Grid.init(allocator, last_size); │ │27 defer last_grid.deinit(); │ │28 │ │29 - while (!term.quit.load(.monotonic)) { │ │29 + while (!terminal.shouldQuit()) { │ │30 const grid_changed = try terminal.render(&root, &last_grid, &last_size);│ │31 │ │32 // process any inputs. │ ╰────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────╮ │41 // there may be an animation that requires more looping. │ │42 var blocking = !grid_changed; │ │43 while (try terminal.readKey(io, blocking)) |key| { │ │44 - switch (key) { │ │45 - .codepoint => |cp| if (cp == 'q') return else try root.input(key, root.getFocus()),│ │46 - .mouse => |mouse| { │ │47 - if (mouse.action == .press and mouse.action.press == .left) { │ │48 - const root_focus = root.getFocus(); │ │49 - var iter = root_focus.children.iterator(); │ │50 - while (iter.next()) |entry| { │ │51 - const child = entry.value_ptr.*; │ │52 - if (!child.focus.focusable) continue; │ │53 - const r = child.rect; │ │54 - if (mouse.x >= r.x and mouse.y >= r.y and │ │55 - mouse.x < r.x + r.size.width and mouse.y < r.y + r.size.height) │ │56 - { │ │57 - try root_focus.setFocus(entry.key_ptr.*); │ │58 - break; │ │59 - } │ │60 - } │ │61 - } else { │ │62 - try root.input(key, root.getFocus()); │ │63 - } │ │64 - }, │ │65 - else => try root.input(key, root.getFocus()), │ │66 - } │ │44 + try inputKey(&root, key, &terminal); │ │45 blocking = false; │ │46 } │ │47 │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │52 } │ │53 } │ │54 │ │55 +// route a single decoded key into the widget tree, handling 'q'-to-quit and │ │56 +// mouse-click focus traversal along the way. shared between the tty event │ │57 +// loop (ui.run) and the SSH session event loop in serve_ssh_tui.zig. │ │58 +// `terminal` is whatever terminal type the caller is driving — it must │ │59 +// expose requestQuit(). │ │60 +pub fn inputKey(root: *Widget, key: inp.Key, terminal: anytype) !void { │ │61 + switch (key) { │ │62 + .codepoint => |cp| if (cp == 'q') terminal.requestQuit() else try root.input(key, root.getFocus()),│ │63 + .mouse => |mouse| { │ │64 + if (mouse.action == .press and mouse.action.press == .left) { │ │65 + const root_focus = root.getFocus(); │ │66 + var iter = root_focus.children.iterator(); │ │67 + while (iter.next()) |entry| { │ │68 + const child = entry.value_ptr.*; │ │69 + if (!child.focus.focusable) continue; │ │70 + const r = child.rect; │ │71 + if (mouse.x >= r.x and mouse.y >= r.y and │ │72 + mouse.x < r.x + r.size.width and mouse.y < r.y + r.size.height) │ │73 + { │ │74 + try root_focus.setFocus(entry.key_ptr.*); │ │75 + break; │ │76 + } │ │77 + } │ │78 + } else { │ │79 + try root.input(key, root.getFocus()); │ │80 + } │ │81 + }, │ │82 + else => try root.input(key, root.getFocus()), │ │83 + } │ │84 +} │ │85 + │ │86 pub fn initRoot(allocator: std.mem.Allocator, page: *const Page) !Widget { │ │87 var root: Widget = switch (page.*) { │ │88 .users_and_repos => |*p| .{ .users_and_repos = try UsersAndRepos.View.init(allocator, p) }, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯