╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ Comments.zig ─────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
388 } 389 390 pub fn deinit(self: *View, allocator: std.mem.Allocator) void { 391 self.scroll.deinit(allocator); 392 } 393 394 pub fn build(self: *View, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void { 395 self.clearGrid(); 396 self.session.data.current_page = ui.RoutablePage.repoCommentsRoute(self.data.identity, self.data.thread_id, self.data.selected.id, self.data.replies.start) orelse self.session.data.current_page; 397 try self.scroll.build(allocator, constraint, root_focus); 398 } 399 400 pub fn input(self: *View, allocator: std.mem.Allocator, raw_key: Key, root_focus: *Focus) !void { 401 _ = allocator; 402 const key: Key = if (raw_key == .mouse and raw_key.mouse.action == .scroll) 403 (if (raw_key.mouse.action.scroll == .up) .arrow_up else .arrow_down) 404 else 405 raw_key; 406 switch (key) { 407 .arrow_up => { 408 if (!self.moveVertical(root_focus, false)) self.scroll.y -= 1; 409 }, 410 .arrow_down => { 411 if (!self.moveVertical(root_focus, true)) self.scroll.y += 1; 412 }, 413 .arrow_left => _ = self.moveHorizontal(root_focus, false), 414 .arrow_right => _ = self.moveHorizontal(root_focus, true), 415 .page_up => self.scroll.y -= 10, 416 .page_down => self.scroll.y += 10, 417 .home => self.scroll.y = 0, 418 .end => self.scroll.y = std.math.maxInt(isize), 419 else => {}, 420 } 421 self.scroll.clampToContent(); 422 } 423 424 fn content(self: *View) *wgt.Box(ui.Widget) { 425 return &self.scroll.child.box; 426 } 427 428 fn focusedChild(self: *View, root_focus: *Focus) ?usize { 429 const box = self.content(); 430 var id = root_focus.grandchild_id orelse return null; 431 while (box.getFocus().children.get(id)) |child| { 432 if (child.parent_id == box.getFocus().id) return box.children.getIndex(id); 433 id = child.parent_id; 434 } 435 return null; 436 } 437 438 fn childFocusable(self: *View, child_index: usize) bool { 439 const child = &self.content().children.values()[child_index].widget; 440 return switch (child.*) { 441 .repo_comment => true, 442 .box => |*box| for (box.children.values()) |*item| { 443 if (item.widget.getFocus().focusable) break true; 444 } else false, 445 else => child.getFocus().focusable, 446 }; 447 } 448 449 fn focusChild(self: *View, child_index: usize, last: bool, root_focus: *Focus) void { 450 const box = self.content(); 451 const child = &box.children.values()[child_index]; 452 var target = box.children.keys()[child_index]; 453 var rect = child.rect orelse return; 454 switch (child.widget) { 455 .repo_comment => |*comment| { 456 if (comment.rowRect(last)) |row_rect| { 457 rect.x += row_rect.x; 458 rect.y += row_rect.y; 459 rect.size = row_rect.size; 460 } 461 if (last) comment.focusBody(root_focus) else comment.focusMetadata(root_focus); 462 }, 463 .box => |*row| if (row.children.count() > 0) { 464 target = row.children.keys()[if (last) row.children.count() - 1 else 0]; 465 root_focus.setFocus(target); 466 }, 467 else => root_focus.setFocus(target), 468 } 469 self.scroll.scrollToRect(rect); 470 } 471 472 fn moveVertical(self: *View, root_focus: *Focus, down: bool) bool { 473 const box = self.content(); 474 const current = self.focusedChild(root_focus) orelse return false; 475 if (box.children.values()[current].widget == .repo_comment) { 476 const comment = &box.children.values()[current].widget.repo_comment; 477 if (down and !comment.bodyFocused()) { 478 self.focusChild(current, true, root_focus); 479 return true; 480 } 481 if (!down and comment.bodyFocused()) { 482 self.focusChild(current, false, root_focus); 483 return true; 484 } 485 } 486 487 var next = current; 488 while (true) { 489 if (down) { 490 next += 1; 491 if (next >= box.children.count()) return false; 492 } else { 493 if (next == 0) return false; 494 next -= 1; 495 } 496 if (!self.childFocusable(next)) continue; 497 self.focusChild(next, !down, root_focus); 498 return true; 499 } 500 } 501 502 fn moveHorizontal(self: *View, root_focus: *Focus, right: bool) bool { 503 const box = self.content(); 504 const current = self.focusedChild(root_focus) orelse return false; 505 const child = &box.children.values()[current]; 506 if (child.widget == .repo_comment) { 507 const moved = child.widget.repo_comment.moveHorizontal(root_focus, right); 508 if (moved) if (child.rect) |rect| self.scroll.scrollToRect(rect); 509 return moved; 510 } 511 const row = switch (child.widget) { 512 .box => |*row| row, 513 else => return false, 514 }; 515 const selected = row.getFocus().child_id orelse return false; 516 const selected_index = row.children.getIndex(selected) orelse return false; 517 const target = if (right) selected_index + 1 else selected_index -| 1; 518 if (target == selected_index or target >= row.children.count()) return false; 519 root_focus.setFocus(row.children.keys()[target]); 520 if (child.rect) |rect| self.scroll.scrollToRect(rect); 521 return true; 522 } 523 524 pub fn clearGrid(self: *View) void { 525 self.scroll.clearGrid(); 526 } 527 528 pub fn getGrid(self: View) ?Grid { 529 return self.scroll.getGrid(); 530 } 531 532 pub fn getFocus(self: *View) *Focus { 533 return self.scroll.getFocus(); 534 } 535 536 pub fn getSelectedIndex(self: *View) ?usize { 537 const box = self.content(); 538 const selected = box.getFocus().child_id orelse return null; 539 for (box.children.keys(), 0..) |id, index| { 540 if (!self.childFocusable(index)) continue; 541 if (id != selected) return 1; 542 return switch (box.children.values()[index].widget) { 543 .repo_comment => |*comment| if (comment.bodyFocused()) 1 else 0, 544 else => 0, 545 }; 546 } 547 return null; 548 } 549 }; 550