╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭──────────╮ │← previous│ ╰──────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────╮ │1874 return true; │ │1875 } │ │1876 return false; │ │1877 + } │ │1878 + │ │1879 + fn focusAdjacentVisible(self: *View, index: usize, root_focus: *Focus, down: bool) bool {│ │1880 + const inner = self.detailInner(index); │ │1881 + var next = self.focusedDetailChild(index, root_focus) orelse return false; │ │1882 + while (true) { │ │1883 + if (down) { │ │1884 + next += 1; │ │1885 + if (next >= inner.children.count()) return false; │ │1886 + } else { │ │1887 + if (next == 0) return false; │ │1888 + next -= 1; │ │1889 + } │ │1890 + if (self.focusVisibleChild(index, next, root_focus, !down)) return true; │ │1891 + } │ │1892 } │ │1893 │ │1894 fn focusedDetailChild(self: *View, index: usize, root_focus: *Focus) ?usize { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1903 │ │1904 fn detailChildFocusable(self: *View, index: usize, child_index: usize) bool { │ │1905 const child = &self.detailInner(index).children.values()[child_index].widget; │ │1869 - return switch (child.*) { │ │1870 - .repo_comment => true, │ │1871 - .box => |*box| for (box.children.values()) |*item| { │ │1872 - if (item.widget.getFocus().focusable) break true; │ │1873 - } else false, │ │1874 - else => child.getFocus().focusable, │ │1875 - }; │ │1906 + const focus = child.getFocus(); │ │1907 + if (focus.focusable) return true; │ │1908 + for (focus.children.values()) |item| { │ │1909 + if (item.focus.focusable) return true; │ │1910 + } │ │1911 + return false; │ │1912 } │ │1913 │ │1914 fn focusDetailChild(self: *View, index: usize, child_index: usize, last: bool, root_focus: *Focus) void {│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1931 }, │ │1932 else => root_focus.setFocus(target), │ │1933 } │ │1898 - self.detailScroll(index).scrollToRect(rect); │ │1934 + const sc = self.detailScroll(index); │ │1935 + if (sc.grid) |viewport| { │ │1936 + const viewport_height = viewport.size.height - sc.bar_h; │ │1937 + if (rect.size.height > viewport_height) { │ │1938 + const bottom_aligned = rect.y + @as(isize, @intCast(rect.size.height - viewport_height));│ │1939 + sc.y = std.math.clamp(sc.y, rect.y, bottom_aligned); │ │1940 + sc.clampToContent(); │ │1941 + return; │ │1942 + } │ │1943 + } │ │1944 + sc.scrollToRect(rect); │ │1945 } │ │1946 │ │1901 - fn moveCommentVertical(self: *View, index: usize, root_focus: *Focus, down: bool) bool { │ │1947 + fn moveDetailVertical(self: *View, index: usize, root_focus: *Focus, down: bool) bool { │ │1948 const inner = self.detailInner(index); │ │1949 const current = self.focusedDetailChild(index, root_focus) orelse return false; │ │1950 if (inner.children.values()[current].widget == .repo_comment) { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2397 switch (key) { │ │2398 .arrow_left => if (cur > 0) self.focusTag(index, tf, root_focus, cur - 1) else try self.focusList(index, root_focus), │ │2399 .arrow_right => if (cur + 1 < count) self.focusTag(index, tf, root_focus, cur + 1), │ │2354 - .arrow_up => if (tf.rowStep(cur, false)) |i| self.focusTag(index, tf, root_focus, i) else self.focusAuthor(index, root_focus), │ │2355 - .arrow_down => if (tf.rowStep(cur, true)) |i| self.focusTag(index, tf, root_focus, i) else try self.focusDescription(index, root_focus),│ │2400 + .arrow_up => if (tf.rowStep(cur, false)) |i| { │ │2401 + self.focusTag(index, tf, root_focus, i); │ │2402 + } else { │ │2403 + _ = self.moveDetailVertical(index, root_focus, false); │ │2404 + }, │ │2405 + .arrow_down => if (tf.rowStep(cur, true)) |i| { │ │2406 + self.focusTag(index, tf, root_focus, i); │ │2407 + } else { │ │2408 + _ = self.moveDetailVertical(index, root_focus, true); │ │2409 + }, │ │2410 .home => self.focusTag(index, tf, root_focus, 0), │ │2411 .end => self.focusTag(index, tf, root_focus, count - 1), │ │2412 else => {}, │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────╮ │2474 return inner.children.getIndex(cid) == title_child_index; │ │2475 } │ │2476 │ │2423 - fn authorPresent(self: *View, index: usize) bool { │ │2424 - return self.author_id[index] != null; │ │2425 - } │ │2426 - │ │2477 fn authorFocused(self: *View, index: usize) bool { │ │2478 const inner = self.detailInner(index); │ │2479 const cid = inner.getFocus().child_id orelse return false;│ ╰────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────╮ │2483 fn descriptionFocused(self: *View, index: usize) bool { │ │2484 const cid = self.detailInner(index).getFocus().child_id orelse return false;│ │2485 return cid == self.description_id[index]; │ │2436 - } │ │2437 - │ │2438 - // the author sits right under the title at the top of the pane, so │ │2439 - // focusing it scrolls there. │ │2440 - fn focusAuthor(self: *View, index: usize, root_focus: *Focus) void { │ │2441 - const id = self.author_id[index] orelse return; │ │2442 - root_focus.setFocus(id); │ │2443 - self.detailScroll(index).y = 0; │ │2444 - } │ │2445 - │ │2446 - // the title sits at the top of the pane, so focusing it scrolls there. │ │2447 - fn focusTitle(self: *View, index: usize, root_focus: *Focus) void { │ │2448 - const inner = self.detailInner(index); │ │2449 - if (inner.children.count() == 0) return; │ │2450 - root_focus.setFocus(inner.children.keys()[title_child_index]); │ │2451 - self.detailScroll(index).y = 0; │ │2486 } │ │2487 │ │2454 - fn focusTags(self: *View, index: usize, root_focus: *Focus) !void { │ │2455 - const tf = self.tagFlow(index) orelse return; │ │2456 - if (tf.text_boxes.items.len == 0) return; │ │2457 - const cid = tf.focus.child_id orelse tf.text_boxes.items[0].getFocus().id; │ │2458 - const item = tf.indexOfFocusId(cid) orelse 0; │ │2459 - self.focusTag(index, tf, root_focus, item); │ │2460 - } │ │2461 - │ │2462 - fn focusDescription(self: *View, index: usize, root_focus: *Focus) !void { │ │2463 - if (self.description_id[index]) |id| root_focus.setFocus(id); │ │2464 - } │ │2465 - │ │2488 // return to the tool row's last-focused button (the header when the │ │2489 // description page shows no row). │ │2490 fn focusToolRow(self: *View, index: usize, root_focus: *Focus) void { │ ╰──────────────────────────────────────────────────────────────────────────────────────────╯