╭ changed ────────╮ │src/main_wasm.zig│ ╰─────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │163 } │ │164 if (ui.currentPageLink(root_ptr.getFocus(), focus_id, session.data)) return; │ │165 try setFocus(focus_id); │ │166 + // an in-page link selects within the page, and a list row's view may open │ │167 + // its detail on the press. other clickables only focus: a form control's │ │168 + // border reaches here too, and the press must not fire its form. │ │169 + const root_focus = root_ptr.getFocus(); │ │170 + if (ui.inPageLink(root_focus, focus_id, session.data) != null) { │ │171 + const rect = (root_focus.children.get(focus_id) orelse return).rect; │ │172 + try root_ptr.input(allocator, .{ .mouse = .{ .x = @intCast(rect.x), .y = @intCast(rect.y), .action = .{ .press = .left } } }, root_focus);│ │173 + } │ │174 } │ │175 │ │176 fn setFocus(focus_id: usize) !void { │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──────────────╮ │src/ui/Repo/Commits.zig│ ╰───────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────╮ │720 _ = ui.widget.moveInSelectedRow(inner, root_focus, true);│ │721 } │ │722 } else { │ │723 - try self.listInput(key, root_focus); │ │723 + try self.listInput(allocator, key, root_focus); │ │724 } │ │725 } │ │726 │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │736 try self.session.navigate((route orelse return).withSearch(text) orelse return); │ │737 } │ │738 │ │739 - fn listInput(self: *View, key: Key, root_focus: *Focus) !void { │ │739 + fn listInput(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void {│ │740 // up/down (and the scroll wheel) move the selection a row; page up/down │ │741 // jump a fixed amount. right/Enter cross into the detail pane. Enter/clicks │ │742 // on the "next" row become navigation in the host before reaching here. │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │751 if (pageRoute(self.data, next)) |route| try self.session.navigate(route); │ │752 }, │ │753 .arrow_right => self.focusDetail(root_focus), │ │754 + // when the window is too narrow to lay out the detail pane beside │ │755 + // the list, a click on a row opens it like enter. the row was just │ │756 + // selected, so the detail is swapped to it ahead of the build. │ │757 + .mouse => |mouse| if (self.contentBox().children.values()[detail_index].rect == null and ui.widget.clickOnSelectedRow(self.listBox(), root_focus, mouse)) {│ │758 + try self.refreshDetail(allocator); │ │759 + self.focusDetail(root_focus); │ │760 + }, │ │761 else => {}, │ │762 } │ │763 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────────╮ │src/ui/Repo/Files.zig│ ╰─────────────────────╯ ╭──────────────────────────────────────────────────────────────────╮ │757 if (self.detailActive()) { │ │758 try self.detailInput(allocator, key, root_focus);│ │759 } else { │ │760 - try self.listInput(key, root_focus); │ │760 + try self.listInput(allocator, key, root_focus); │ │761 } │ │762 } │ │763 │ ╰──────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────╮ │769 try self.session.navigate(route.withFind(term) orelse return); │ │770 } │ │771 │ │772 - fn listInput(self: *View, key: Key, root_focus: *Focus) !void { │ │772 + fn listInput(self: *View, allocator: std.mem.Allocator, key: Key, root_focus: *Focus) !void {│ │773 // up/down (and the scroll wheel) move the selection; page up/down jump a │ │774 // fixed amount. right/Enter cross into the detail pane. Enter/clicks on a │ │775 // directory (or "..") row are turned into navigation by the host │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │780 } │ │781 switch (key) { │ │782 .enter, .arrow_right => try self.focusDetail(root_focus), │ │783 + // when the window is too narrow to lay out the detail pane beside │ │784 + // the list, a click on a row opens it like enter. the row was just │ │785 + // selected, so the detail is swapped to it ahead of the build. │ │786 + .mouse => |mouse| if (self.contentBox().children.values()[detail_index].rect == null and ui.widget.clickOnSelectedRow(self.listBox(), root_focus, mouse)) {│ │787 + try self.refreshDetail(allocator); │ │788 + try self.focusDetail(root_focus); │ │789 + }, │ │790 else => {}, │ │791 } │ │792 } │ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ────────╮ │src/ui/widget.zig│ ╰─────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │122 } │ │123 }; │ │124 │ │125 +// whether `mouse` is a left click on the selected row of `list`. the host │ │126 +// selects a clicked row before forwarding the press. │ │127 +pub fn clickOnSelectedRow(list: *wgt.Box(Widget), root_focus: *Focus, mouse: xitui.input.Mouse) bool {│ │128 + const cid = list.getFocus().child_id orelse return false; │ │129 + return inp.leftClickOn(root_focus, cid, mouse); │ │130 +} │ │131 + │ │132 pub const back_button_width = 3; │ │133 const back_button_height = 3; │ │134 const back_kind = "back"; │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ───────────────╮ │src/ui/widget/thread.zig│ ╰────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2267 const lead: usize = if (win.prev_id != null) 1 else 0; │ │2268 if (idx < lead or idx - lead >= win.items.len) return null; │ │2269 return idx - lead; │ │2270 + } │ │2271 + │ │2272 + // swap the detail pane to the selected thread when it changes │ │2273 + fn refreshDetail(self: *This, allocator: std.mem.Allocator, index: usize) !void { │ │2274 + const selected = self.selectedThreadIndex(index) orelse return; │ │2275 + const entry = self.window(index).items[selected]; │ │2276 + const changed = if (self.detail(index).entry) |current| !std.mem.eql(u8, current.id, entry.id) else true; │ │2277 + if (changed) try self.detail(index).setEntry(allocator, entry); │ │2278 } │ │2279 │ │2280 pub fn build(self: *This, allocator: std.mem.Allocator, constraint: layout.Constraint, root_focus: *Focus) !void {│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │2287 } │ │2288 │ │2289 if (self.selectedSplitIndex()) |i| { │ │2282 - // swap the detail pane to the selected thread when it changes. │ │2283 - if (self.selectedThreadIndex(i)) |selected| { │ │2284 - const entry = self.window(i).items[selected]; │ │2285 - const changed = if (self.detail(i).entry) |current| !std.mem.eql(u8, current.id, entry.id) else true;│ │2286 - if (changed) { │ │2287 - try self.detail(i).setEntry(allocator, entry); │ │2288 - } │ │2289 - } │ │2290 + try self.refreshDetail(allocator, i); │ │2291 │ │2292 // the selected list row shows a border (the focused TextBox │ │2293 // upgrades it to a double border itself); the rest stay borderless. │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────╮ │next →│ ╰──────╯