╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Issues.zig ───────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
546 547 fn focusTags(self: *View, root_focus: *Focus) !void { 548 const tf = self.tagFlow() orelse return; 549 if (tf.text_boxes.items.len == 0) return; 550 const cid = tf.focus.child_id orelse tf.text_boxes.items[0].getFocus().id; 551 const index = tf.indexOfFocusId(cid) orelse 0; 552 self.focusTag(tf, root_focus, index); 553 } 554 555 fn focusDescription(self: *View, root_focus: *Focus) !void { 556 const inner = self.detailInner(); 557 if (inner.children.count() == 0) return; 558 root_focus.setFocus(inner.children.keys()[0]); 559 } 560 561 // enter the detail pane. an empty pane (no issues) can't be entered. 562 fn focusDetail(self: *View, root_focus: *Focus) !void { 563 if (self.detailInner().children.count() == 0) return; 564 root_focus.setFocus(self.detailOuter().getFocus().id); 565 } 566 567 // return to the list. 568 fn focusList(self: *View, root_focus: *Focus) !void { 569 root_focus.setFocus(self.listScroll().getFocus().id); 570 } 571 572 pub fn clearGrid(self: *View) void { 573 self.box.clearGrid(); 574 } 575 576 pub fn getGrid(self: View) ?Grid { 577 return self.box.getGrid(); 578 } 579 580 pub fn getFocus(self: *View) *Focus { 581 return self.box.getFocus(); 582 } 583 584 // for the parent's "scroll up at the top jumps to the header" check. in the 585 // detail pane that means the description is focused and can't scroll up any 586 // further; up from the tags moves to the description instead. when the list 587 // holds focus, report its selected row directly (window-navigation rows 588 // included). 589 pub fn getSelectedIndex(self: *View) ?usize { 590 if (self.detailActive()) { 591 if (self.tagsFocused()) return 1; 592 return if (self.detailScroll().y == 0) 0 else 1; 593 } 594 const lb = self.listBox(); 595 const cid = lb.getFocus().child_id orelse return null; 596 return lb.children.getIndex(cid); 597 } 598 }; 599 600 // the "a:" navigation link for the issues page filtered to the url-encoded 601 // `tag` and rooted at issue `id` within `identity` ("owner/name"). 602 fn issuesLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, tag: []const u8, id: []const u8) ![]const u8 { 603 const route = ui.RoutablePage.repoIssuesRoute(identity, tag, id) orelse return error.RouteTooLong; 604 const url = try route.urlAlloc(page_arena); 605 return std.fmt.allocPrint(page_arena.allocator(), "a:{s}", .{url}); 606 } 607 608 // the in-page "ai:" anchor for selecting issue `id` in `identity`'s list; the 609 // href is only followed with js off. 610 fn issueRowLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, id: []const u8) ![]const u8 { 611 const route = ui.RoutablePage.repoIssuesRoute(identity, "", id) orelse return error.RouteTooLong; 612 const url = try route.urlAlloc(page_arena); 613 return std.fmt.allocPrint(page_arena.allocator(), "ai:{s}", .{url}); 614 } 615 616 // the "a:" link to the issues list filtered to `tag` (raw; encoded here). 617 fn tagLink(page_arena: *std.heap.ArenaAllocator, identity: []const u8, tag: []const u8) ![]const u8 { 618 const encoded = try ui.urlEncodeRef(page_arena.allocator(), tag); 619 const route = ui.RoutablePage.repoIssuesRoute(identity, encoded, "") orelse return error.RouteTooLong; 620 const url = try route.urlAlloc(page_arena); 621 return std.fmt.allocPrint(page_arena.allocator(), "a:{s}", .{url}); 622 } 623