╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ User.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
430 431 pub fn build(self: *ReposView, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { 432 self.clearGrid(); 433 // clear the incoming min height so the list scrolls within what's left 434 try self.box.build(allocator, .{ 435 .min_size = .{ .width = null, .height = null }, 436 .max_size = constraint.max_size, 437 }, root_focus); 438 } 439 440 pub fn input(self: *ReposView, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void { 441 const direction = inp.vertDirection(key); 442 if (self.headerActive()) { 443 const search_box = self.searchBox(); 444 if (direction == .down) return root_focus.setFocus(self.listScroll().getFocus().id); 445 if (key == .enter) { 446 const text = try search_box.text(allocator); 447 defer allocator.free(text); 448 return self.submit(text); 449 } 450 return search_box.input(allocator, key, root_focus); 451 } 452 // up from the first row reaches the search box 453 if (direction == .up and self.listScroll().atTop()) return self.focusHeader(root_focus); 454 try self.listScroll().input(allocator, key, root_focus); 455 } 456 457 // the repos narrowed to `text` from the first window. an empty box on an 458 // unsearched page has nothing to clear. 459 fn submit(self: *ReposView, text: []const u8) !void { 460 if (text.len == 0 and self.data.repos_search == null) return; 461 const route = reposRoute(self.data.user.name) orelse return; 462 try self.session.navigate(route.withSearch(text) orelse return); 463 } 464 465 fn searchBox(self: *ReposView) *ui.widget.SearchBox { 466 return &self.box.children.values()[header_index].widget.box.children.values()[0].widget.search_box; 467 } 468 469 fn listScroll(self: *ReposView) *ui.widget.FlowBox.Scroll { 470 return &self.box.children.values()[list_index].widget.flow_box_scroll; 471 } 472 473 fn headerActive(self: *ReposView) bool { 474 return self.box.getFocus().child_id == self.box.children.keys()[header_index]; 475 } 476 477 pub fn clearGrid(self: *ReposView) void { 478 self.box.clearGrid(); 479 } 480 481 pub fn getGrid(self: ReposView) ?Grid { 482 return self.box.getGrid(); 483 } 484 485 pub fn getFocus(self: *ReposView) *Focus { 486 return self.box.getFocus(); 487 } 488 489 // up leaves the tab from the search box 490 pub fn atTop(self: *ReposView) bool { 491 return self.headerActive(); 492 } 493 494 pub fn focusHeader(self: *ReposView, root_focus: *Focus) void { 495 root_focus.setFocus(self.searchBox().getFocus().id); 496 } 497 }; 498 499 // the first window of `name`'s repos 500 fn reposRoute(name: []const u8) ?ui.RoutablePage { 501 return .{ .user_repos = .{ .name = ui.RoutablePage.Array(evt.User.name_max_len).from(name) orelse return null } }; 502 } 503