╭ changed ─────────────╮ │src/ui/Repo/Issues.zig│ ╰──────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │768 } │ │769 │ │770 row.getFocus().child_id = row.children.keys()[button_in_row_index]; │ │771 + } │ │772 + │ │773 + // the issue's title as a focusable word-wrapped text box. │ │774 + { │ │775 + var tb = try wgt.TextBox(ui.Widget).init(allocator, entry.issue.title, .{ .border_style = .single, .rounded_corners = true, .wrap_kind = .word });│ │776 + errdefer tb.deinit(allocator); │ │777 + tb.getFocus().focusable = true; │ │778 + try inner.children.put(allocator, tb.getFocus().id, .{ .widget = .{ .text_box = tb }, .rect = null, .min_size = null }); │ │779 } │ │780 │ │781 // the issue's tags, each linking to this status's list filtered to │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────╮ │806 break :blk tb.getFocus().id; │ │807 }; │ │808 │ │801 - // select the description by default │ │802 - inner.getFocus().child_id = self.description_id[index]; │ │809 + // select the title by default │ │810 + inner.getFocus().child_id = inner.children.keys()[title_child_index]; │ │811 │ │812 // reset the scroll to the top for the newly-shown issue: directly on the│ │813 // terminal (the wasm offset), and via a version bump on the web (so the │ ╰──────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │887 fn detailInput(self: *View, allocator: std.mem.Allocator, index: usize, key: Key, root_focus: *Focus) !void {│ │888 if (self.statusButtonFocused(index)) { │ │889 try self.statusButtonInput(allocator, index, key, root_focus); │ │890 + } else if (self.titleFocused(index)) { │ │891 + try self.titleInput(index, key, root_focus); │ │892 } else if (self.tagsFocused(index)) { │ │893 try self.tagsInput(index, key, root_focus); │ │894 } else { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │902 switch (key) { │ │903 .arrow_left => try self.focusList(index, root_focus), │ │904 .arrow_up => self.focusHeader(root_focus), │ │895 - .arrow_down => if (self.tagFlow(index) != null) try self.focusTags(index, root_focus) else try self.focusDescription(index, root_focus),│ │905 + .arrow_down => self.focusTitle(index, root_focus), │ │906 .enter => try self.toggleIssueStatus(allocator, index), │ │907 .mouse => |mouse| switch (mouse.action) { │ │908 .scroll => |dir| { │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │912 }, │ │913 else => if (self.statusButton(index)) |button| { │ │914 if (inp.leftClickOn(root_focus, button.getFocus().id, mouse)) try self.toggleIssueStatus(allocator, index); │ │915 + }, │ │916 + }, │ │917 + else => {}, │ │918 + } │ │919 + } │ │920 + │ │921 + fn titleInput(self: *View, index: usize, key: Key, root_focus: *Focus) !void { │ │922 + const sc = self.detailScroll(index); │ │923 + switch (key) { │ │924 + .arrow_left => try self.focusList(index, root_focus), │ │925 + .arrow_up => self.focusStatusButton(index, root_focus), │ │926 + .arrow_down => if (self.tagFlow(index) != null) try self.focusTags(index, root_focus) else try self.focusDescription(index, root_focus),│ │927 + .mouse => |mouse| switch (mouse.action) { │ │928 + .scroll => |dir| { │ │929 + sc.y += if (dir == .up) @as(isize, -1) else 1; │ │930 + sc.clampToContent(); │ │931 }, │ │932 + else => {}, │ │933 }, │ │934 else => {}, │ │935 } │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │946 sc.y -= 1; │ │947 sc.clampToContent(); │ │948 if (sc.y == before) { │ │922 - if (self.tagFlow(index) != null) try self.focusTags(index, root_focus) else self.focusStatusButton(index, root_focus);│ │949 + if (self.tagFlow(index) != null) try self.focusTags(index, root_focus) else self.focusTitle(index, root_focus); │ │950 } │ │951 return; │ │952 }, │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │1109 switch (key) { │ │1110 .arrow_left => if (cur > 0) self.focusTag(index, tf, root_focus, cur - 1) else try self.focusList(index, root_focus), │ │1111 .arrow_right => if (cur + 1 < count) self.focusTag(index, tf, root_focus, cur + 1), │ │1085 - .arrow_up => if (tf.rowStep(cur, false)) |i| self.focusTag(index, tf, root_focus, i) else self.focusStatusButton(index, root_focus), │ │1112 + .arrow_up => if (tf.rowStep(cur, false)) |i| self.focusTag(index, tf, root_focus, i) else self.focusTitle(index, root_focus), │ │1113 .arrow_down => if (tf.rowStep(cur, true)) |i| self.focusTag(index, tf, root_focus, i) else try self.focusDescription(index, root_focus),│ │1114 .home => self.focusTag(index, tf, root_focus, 0), │ │1115 .end => self.focusTag(index, tf, root_focus, count - 1), │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────╮ │1125 } │ │1126 │ │1127 const button_in_row_index: usize = 1; │ │1101 - const tags_child_index: usize = 0; │ │1128 + const title_child_index: usize = 0; │ │1129 + const tags_child_index: usize = 1; │ │1130 │ │1131 fn tagFlow(self: *View, index: usize) ?*ui.TagFlow {│ │1132 const inner = self.detailInner(index); │ ╰──────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │1105 - if (inner.children.count() == 0) return null; │ │1133 + if (inner.children.count() <= tags_child_index) return null; │ │1134 return switch (inner.children.values()[tags_child_index].widget) {│ │1135 .tag_flow => |*tf| tf, │ │1136 else => null, │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────╮ │1168 self.detailScroll(index).scrollToRect(rect); │ │1169 } │ │1170 } │ │1171 + } │ │1172 + │ │1173 + fn titleFocused(self: *View, index: usize) bool { │ │1174 + const inner = self.detailInner(index); │ │1175 + const cid = inner.getFocus().child_id orelse return false; │ │1176 + return inner.children.getIndex(cid) == title_child_index; │ │1177 + } │ │1178 + │ │1179 + // the title sits at the top of the pane, so focusing it scrolls there.│ │1180 + fn focusTitle(self: *View, index: usize, root_focus: *Focus) void { │ │1181 + const inner = self.detailInner(index); │ │1182 + if (inner.children.count() == 0) return; │ │1183 + root_focus.setFocus(inner.children.keys()[title_child_index]); │ │1184 + self.detailScroll(index).y = 0; │ │1185 } │ │1186 │ │1187 fn focusTags(self: *View, index: usize, root_focus: *Focus) !void { │ ╰─────────────────────────────────────────────────────────────────────────────────╯