╭ Home.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
1 const std = @import("std"); 2 const ui = @import("../ui.zig"); 3 const xit = @import("xit"); 4 const xitui = xit.xitui; 5 const wgt = xitui.widget; 6 const layout = xitui.layout; 7 const inp = xitui.input; 8 const Grid = xitui.grid.Grid; 9 const Focus = xitui.focus.Focus; 10 const evt = @import("../event.zig"); 11 12 pub const Users = @import("./Home/Users.zig"); 13 pub const Repos = @import("./Home/Repos.zig"); 14 pub const Header = @import("./Home/Header.zig"); 15 pub const Settings = @import("./Settings.zig"); 16 pub const Auth = @import("./Auth.zig"); 17 18 header: Header, 19 users: Users, 20 repos: Repos, 21 settings: Settings, 22 auth: Auth, 23 24 const Self = @This(); 25 26 pub fn init( 27 arena: *std.heap.ArenaAllocator, 28 haxy_moment: evt.AdminDB.HashMap(.read_only), 29 ) !Self { 30 return .{ 31 .header = try Header.init(arena), 32 .users = try Users.init(arena, haxy_moment), 33 .repos = try Repos.init(arena, haxy_moment), 34 .settings = Settings.init(), 35 .auth = Auth.init(), 36 }; 37 } 38 39 pub const View = struct { 40 box: wgt.Box(ui.Widget), 41 data: *const Self, 42 session: *ui.Session, 43 44 const header_index: usize = 0; 45 const stack_index: usize = 1; 46 47 pub fn init(allocator: std.mem.Allocator, data: *const Self, session: *ui.Session) !View { 48 var box = wgt.Box(ui.Widget).init(.{ .border_style = null, .rounded_corners = true, .direction = .vert }); 49 errdefer box.deinit(allocator); 50 51 // build the header first so we can grab the users-tab focus id and 52 // hand it to login/logout 53 var users_tab_id: usize = undefined; 54 { 55 var header_view = try Header.View.init(allocator, &data.header, session); 56 errdefer header_view.deinit(allocator); 57 users_tab_id = header_view.tab_ids[0]; 58 try box.children.put(allocator, header_view.getFocus().id, .{ .widget = .{ .home_header = header_view }, .rect = null, .min_size = null }); 59 } 60 61 { 62 var stack = wgt.Stack(ui.Widget).init(); 63 errdefer stack.deinit(allocator); 64 65 { 66 var users_view = try Users.View.init(allocator, &data.users); 67 errdefer users_view.deinit(allocator); 68 try stack.children.put(allocator, users_view.getFocus().id, .{ .home_users = users_view }); 69 } 70 71 { 72 var repos_view = try Repos.View.init(allocator, &data.repos); 73 errdefer repos_view.deinit(allocator); 74 try stack.children.put(allocator, repos_view.getFocus().id, .{ .home_repos = repos_view }); 75 } 76 77 { 78 var settings_view = try Settings.View.init(allocator, &data.settings, session); 79 errdefer settings_view.deinit(allocator); 80 try stack.children.put(allocator, settings_view.getFocus().id, .{ .settings = settings_view }); 81 } 82 83 { 84 var auth_view = try Auth.View.init(allocator, &data.auth, session, users_tab_id); 85 errdefer auth_view.deinit(allocator); 86 try stack.children.put(allocator, auth_view.getFocus().id, .{ .auth = auth_view }); 87 } 88 89 try box.children.put(allocator, stack.getFocus().id, .{ .widget = .{ .stack = stack }, .rect = null, .min_size = null }); 90 } 91 92 var self = View{ 93 .box = box, 94 .data = data, 95 .session = session, 96 }; 97 self.getFocus().child_id = box.children.keys()[header_index]; 98 return self; 99 } 100 101 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { 102 self.box.deinit(allocator); 103 } 104 105 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { 106 self.clearGrid(); 107 const header = &self.box.children.values()[header_index].widget.home_header; 108 const stack = &self.box.children.values()[stack_index].widget.stack; 109 110 // each header tab maps 1:1 to a stack child by position 111 if (header.getSelectedIndex()) |index| { 112 stack.getFocus().child_id = stack.children.keys()[index]; 113 self.session.data.current_page = switch (index) { 114 0 => .home_users, 115 1 => .home_repos, 116 2 => .settings, 117 3 => .auth, 118 else => .home_users, 119 }; 120 } 121 try self.box.build(allocator, constraint, root_focus); 122 } 123 124 pub fn input(self: *View, allocator: std.mem.Allocator, key: inp.Key, root_focus: *Focus) !void { 125 if (self.getFocus().child_id) |child_id| { 126 if (self.box.children.getIndex(child_id)) |current_index| { 127 const child = &self.box.children.values()[current_index].widget; 128 var index = current_index; 129 130 const Direction = enum { up, down, none }; 131 const direction: Direction = switch (key) { 132 .arrow_up => .up, 133 .arrow_down => .down, 134 .mouse => |mouse| if (mouse.action == .scroll) 135 (if (mouse.action.scroll == .up) .up else .down) 136 else 137 .none, 138 else => .none, 139 }; 140 141 switch (direction) { 142 .up => { 143 switch (child.*) { 144 .home_header => { 145 try child.input(allocator, key, root_focus); 146 }, 147 .stack => { 148 if (child.stack.getSelected()) |selected_widget| { 149 const at_top = switch (selected_widget.*) { 150 .home_users => |*v| v.getSelectedIndex() == 0, 151 .home_repos => |*v| v.getSelectedIndex() == 0, 152 .settings => |*v| v.getSelectedIndex() == 0, 153 .auth => |*v| v.getSelectedIndex() == 0, 154 else => false, 155 }; 156 if (at_top) { 157 index = header_index; 158 } else { 159 try child.input(allocator, key, root_focus); 160 } 161 } 162 }, 163 else => {}, 164 } 165 }, 166 .down => { 167 switch (child.*) { 168 .home_header => { 169 index = stack_index; 170 }, 171 .stack => { 172 try child.input(allocator, key, root_focus); 173 }, 174 else => {}, 175 } 176 }, 177 .none => { 178 try child.input(allocator, key, root_focus); 179 }, 180 } 181 182 if (index != current_index) { 183 try root_focus.setFocus(self.box.children.keys()[index]); 184 } 185 } 186 } 187 } 188 189 pub fn clearGrid(self: *View) void { 190 self.box.clearGrid(); 191 } 192 193 pub fn getGrid(self: View) ?Grid { 194 return self.box.getGrid(); 195 } 196 197 pub fn getFocus(self: *View) *Focus { 198 return self.box.getFocus(); 199 } 200 }; 201